mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
950b72c743 | ||
|
|
6eccfbbf46 | ||
|
|
ed5c64e0d9 | ||
|
|
0dd184c446 | ||
|
|
6676d31d3f | ||
|
|
82c5a09241 | ||
|
|
ac39f413d8 | ||
|
|
d6cd5d1e3c | ||
|
|
5a86c2e725 | ||
|
|
e29a510f45 | ||
|
|
41a2789e18 | ||
|
|
2e5f8bd434 | ||
|
|
e041a40a23 | ||
|
|
74be8f3ea2 | ||
|
|
cda8a74eb7 | ||
|
|
fb98398ca9 | ||
|
|
2dbb90a064 | ||
|
|
b4f29f7515 | ||
|
|
731f022dc8 | ||
|
|
a02495cf80 | ||
|
|
5a0cef9259 | ||
|
|
bb95ed284d | ||
|
|
ecc8ffe17b | ||
|
|
205e577694 | ||
|
|
be8445eea8 | ||
|
|
c4e2066dbd | ||
|
|
92639b93a6 | ||
|
|
5ce246c716 | ||
|
|
ec5add398c | ||
|
|
5c39d8df69 | ||
|
|
e3149358a5 | ||
|
|
7b1d6c438a | ||
|
|
a7fde8727b | ||
|
|
a6ac51b99a | ||
|
|
f1e99b36f5 | ||
|
|
9de912de3f | ||
|
|
0de335e076 | ||
|
|
8a8a01c642 | ||
|
|
8307d4400c | ||
|
|
b175eceb09 | ||
|
|
4e258d3e63 | ||
|
|
423d051124 | ||
|
|
ab202e8d72 | ||
|
|
27aa443a15 | ||
|
|
ef32f11004 | ||
|
|
06ad5dd293 | ||
|
|
3f9b75604d | ||
|
|
499c9448d0 | ||
|
|
5746767387 | ||
|
|
40e97b02be | ||
|
|
af54c4092a | ||
|
|
5b1ccdc191 | ||
|
|
d0c1a01c1b | ||
|
|
679a413f11 | ||
|
|
c4d8838a2b | ||
|
|
a09c3cf13e | ||
|
|
61d5a132c9 | ||
|
|
177f8bd063 | ||
|
|
8d44d792c3 | ||
|
|
55bf42a80c | ||
|
|
792d2f906e | ||
|
|
963314ffd6 | ||
|
|
1e59800718 | ||
|
|
c2485f2d81 | ||
|
|
5043960623 | ||
|
|
8e0df9050c | ||
|
|
9aad24da55 | ||
|
|
722814e357 | ||
|
|
cecdd5d2ab | ||
|
|
d51f88c9e4 | ||
|
|
416943bf28 | ||
|
|
b7e1c5213f | ||
|
|
e653af7164 | ||
|
|
5aacef9712 | ||
|
|
21ce018a32 | ||
|
|
ed7ea43dd9 | ||
|
|
e500c730cf | ||
|
|
3844612625 | ||
|
|
187964a039 | ||
|
|
a1b5484811 | ||
|
|
e9befdebbf | ||
|
|
b288f0131b | ||
|
|
ce4192375d | ||
|
|
327ddb7d47 | ||
|
|
3e422ce0ca | ||
|
|
2040bb545b | ||
|
|
ac7dcfda10 | ||
|
|
b9bb59d49d | ||
|
|
cc24ac14eb | ||
|
|
e6e2f3699c | ||
|
|
7d0ecab570 | ||
|
|
c073b7faef | ||
|
|
b194c21bba | ||
|
|
5d5ff82408 | ||
|
|
92593d511a | ||
|
|
9b083cb11c | ||
|
|
1760637539 | ||
|
|
500eeb6fd3 | ||
|
|
2ad88ceae9 | ||
|
|
2a0d3c7054 | ||
|
|
59ebab654e | ||
|
|
69760b4d05 | ||
|
|
d758273ceb | ||
|
|
510c66fb75 | ||
|
|
15245e9018 | ||
|
|
a1aa3f8385 | ||
|
|
e503f5e0a0 | ||
|
|
4b0a509a91 | ||
|
|
e7358da9a2 | ||
|
|
9d6f48ec1d | ||
|
|
1f9f630b27 | ||
|
|
2bc63e3aba | ||
|
|
b578ca2e4d | ||
|
|
76609cd38a | ||
|
|
378bc94d7e | ||
|
|
f15414b656 | ||
|
|
e9daedec84 | ||
|
|
6ac1d25fd0 | ||
|
|
a99fb67233 | ||
|
|
7eebe6d38a | ||
|
|
f308da5273 | ||
|
|
6e99de6d3c | ||
|
|
cb26c8cc80 | ||
|
|
5444942f15 | ||
|
|
633bff2f19 | ||
|
|
8a1da56b96 | ||
|
|
832b056a30 | ||
|
|
b79f13b2aa | ||
|
|
56f468b356 | ||
|
|
67bbac3621 | ||
|
|
5e1808d515 | ||
|
|
3356187617 | ||
|
|
71c7e4935e | ||
|
|
cd48ecaa1a | ||
|
|
124fbd96d8 | ||
|
|
5c1a544122 | ||
|
|
2735b0275d | ||
|
|
493f425e77 | ||
|
|
41c328fe57 | ||
|
|
fea286d914 | ||
|
|
8cb278d11a | ||
|
|
a4a4a2dabd | ||
|
|
18efd760c5 | ||
|
|
d941b89ddd | ||
|
|
56ed601df3 | ||
|
|
50f206ad84 |
No files matched your search
+59
-1
@@ -31,7 +31,7 @@ jobs:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: pyenv global 3.5.2
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 PRINT_PARALLEL_OUTPUTS=1 ASSERT_STATUS_CHECKED=1 LIB_MODE=shared OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" GTEST_THROW_ON_FAILURE=0 GTEST_OUTPUT="xml:/tmp/test-results/" make V=1 -j32 all check_some | .circleci/cat_ignore_eagain
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 PRINT_PARALLEL_OUTPUTS=1 ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 LIB_MODE=shared OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" GTEST_THROW_ON_FAILURE=0 GTEST_OUTPUT="xml:/tmp/test-results/" make V=1 -j32 all check_some | .circleci/cat_ignore_eagain
|
||||
- store_test_results:
|
||||
path: /tmp/test-results
|
||||
|
||||
@@ -88,6 +88,7 @@ jobs:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "APT::Acquire::Retries \"10\";" | sudo tee -a /etc/apt/apt.conf.d/80-retries # llvm.org unreliable
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y clang-10 libgflags-dev
|
||||
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 all | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out
|
||||
|
||||
@@ -100,6 +101,7 @@ jobs:
|
||||
- run: pyenv global 3.5.2
|
||||
- run: echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "APT::Acquire::Retries \"10\";" | sudo tee -a /etc/apt/apt.conf.d/80-retries # llvm.org unreliable
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y clang-10 libgflags-dev
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 PRINT_PARALLEL_OUTPUTS=1 GTEST_THROW_ON_FAILURE=0 GTEST_OUTPUT="xml:/tmp/test-results/" make V=1 -j32 check | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out
|
||||
- store_test_results:
|
||||
@@ -114,6 +116,7 @@ jobs:
|
||||
- run: pyenv global 3.5.2
|
||||
- run: echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "APT::Acquire::Retries \"10\";" | sudo tee -a /etc/apt/apt.conf.d/80-retries # llvm.org unreliable
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y clang-10 libgflags-dev
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 COMPILE_WITH_TSAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 PRINT_PARALLEL_OUTPUTS=1 EXCLUDE_TESTS_REGEX="TransactionStressTest|SnapshotConcurrentAccess|SeqAdvanceConcurrent|DeadlockStress|MultiThreadedDBTest.MultiThreaded|WriteUnpreparedStressTest.ReadYourOwnWriteStress|DBAsBaseDB/TransactionStressTest|FlushCloseWALFiles|BackgroundPurgeCFDropTest" GTEST_THROW_ON_FAILURE=0 GTEST_OUTPUT="xml:/tmp/test-results/" make V=1 -j32 check | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out. Exclude FlushCloseWALFiles and BackgroundPurgeCFDropTest for occasional failures.
|
||||
- store_test_results:
|
||||
@@ -128,6 +131,7 @@ jobs:
|
||||
- run: pyenv global 3.5.2
|
||||
- run: echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "APT::Acquire::Retries \"10\";" | sudo tee -a /etc/apt/apt.conf.d/80-retries # llvm.org unreliable
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y clang-10 libgflags-dev
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 PRINT_PARALLEL_OUTPUTS=1 make V=1 -j32 ubsan_check | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out
|
||||
|
||||
@@ -140,6 +144,7 @@ jobs:
|
||||
- run: pyenv global 3.5.2
|
||||
- run: echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" | sudo tee -a /etc/apt/sources.list
|
||||
- run: echo "APT::Acquire::Retries \"10\";" | sudo tee -a /etc/apt/apt.conf.d/80-retries # llvm.org unreliable
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y clang-10 libgflags-dev clang-tools-10
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 PRINT_PARALLEL_OUTPUTS=1 make V=1 -j32 analyze | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out. For unknown, reason passing "clang++-10" as CLANG_ANALYZER doesn't work, and we need a full path.
|
||||
|
||||
@@ -160,6 +165,16 @@ jobs:
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
- run: TEST_TMPDIR=/dev/shm && make V=1 -j16 unity_test | .circleci/cat_ignore_eagain
|
||||
|
||||
build-linux-gcc-4-8:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run: pyenv global 3.5.2
|
||||
- run: sudo apt-get update -y && sudo apt-get install gcc-4.8 g++-4.8 libgflags-dev
|
||||
- run: CC=gcc-4.8 CXX=g++-4.8 V=1 SKIP_LINK=1 make -j4 all | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
|
||||
build-windows:
|
||||
executor: windows-2xlarge
|
||||
parameters:
|
||||
@@ -252,6 +267,26 @@ jobs:
|
||||
command: |
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4 | ../.circleci/cat_ignore_eagain
|
||||
|
||||
build-linux-non-shm:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
parameters:
|
||||
start_test:
|
||||
default: ""
|
||||
type: string
|
||||
end_test:
|
||||
default: ""
|
||||
type: string
|
||||
steps:
|
||||
- checkout # checkout the code in the project directory
|
||||
- run: pyenv global 3.5.2
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
- run:
|
||||
name: "Build and unit tests on non-shm"
|
||||
command: |
|
||||
TMPD=/tmp/rocksdb_test_tmp ROCKSDBTESTS_START=<<parameters.start_test>> ROCKSDBTESTS_END=<<parameters.end_test>> make V=1 J=32 -j32 check_some | .circleci/cat_ignore_eagain
|
||||
|
||||
workflows:
|
||||
build-linux:
|
||||
jobs:
|
||||
@@ -315,3 +350,26 @@ workflows:
|
||||
build-examples:
|
||||
jobs:
|
||||
- build-examples
|
||||
build-linux-non-shm-1:
|
||||
jobs:
|
||||
- build-linux-non-shm:
|
||||
start_test: ""
|
||||
end_test: "db_tailing_iter_test" # make sure unique in src.mk
|
||||
build-linux-non-shm-2:
|
||||
jobs:
|
||||
- build-linux-non-shm:
|
||||
start_test: "db_tailing_iter_test" # make sure unique in src.mk
|
||||
end_test: "db_test2" # make sure unique in src.mk
|
||||
build-linux-non-shm-3:
|
||||
jobs:
|
||||
- build-linux-non-shm:
|
||||
start_test: "db_test2" # make sure unique in src.mk
|
||||
end_test: "arena_test" # make sure unique in src.mk
|
||||
build-linux-non-shm-4:
|
||||
jobs:
|
||||
- build-linux-non-shm:
|
||||
start_test: "compact_on_deletion_collector_test" # make sure unique in src.mk
|
||||
end_test: ""
|
||||
build-linux-gcc-4-8:
|
||||
jobs:
|
||||
- build-linux-gcc-4-8
|
||||
@@ -1,4 +1,5 @@
|
||||
make_config.mk
|
||||
rocksdb.pc
|
||||
|
||||
*.a
|
||||
*.arc
|
||||
@@ -52,6 +53,7 @@ db_test2
|
||||
trace_analyzer
|
||||
trace_analyzer_test
|
||||
block_cache_trace_analyzer
|
||||
io_tracer_parser
|
||||
.DS_Store
|
||||
.vs
|
||||
.vscode
|
||||
|
||||
+47
-47
@@ -87,134 +87,134 @@ matrix:
|
||||
- os: linux
|
||||
compiler: clang
|
||||
# Exclude all but most unique cmake variants for pull requests, but build all in branches
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: amd64
|
||||
env: JOB_NAME=cmake
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: amd64
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
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
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=platform_dependent
|
||||
# NB: the cmake build is a partial java test
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os : osx
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : osx
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : osx
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : osx
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=status_checked
|
||||
@@ -224,33 +224,33 @@ install:
|
||||
PATH=$PATH:/usr/local/opt/ccache/libexec;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-gcc8 ]; then
|
||||
sudo apt-get install -y g++-8;
|
||||
sudo apt-get install -y g++-8 || exit $?;
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-gcc9 ] || [ "${JOB_NAME}" == cmake-gcc9-c++20 ]; then
|
||||
sudo apt-get install -y g++-9;
|
||||
sudo apt-get install -y g++-9 || exit $?;
|
||||
CC=gcc-9 && CXX=g++-9;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-mingw ]; then
|
||||
sudo apt-get install -y mingw-w64 ;
|
||||
sudo apt-get install -y mingw-w64 || exit $?;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == make-gcc4.8 ]; then
|
||||
sudo apt-get install -y g++-4.8 ;
|
||||
sudo apt-get install -y g++-4.8 || exit $?;
|
||||
CC=gcc-4.8 && CXX=g++-4.8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
sudo snap install cmake --beta --classic;
|
||||
sudo apt-get install snapd && sudo snap install cmake --beta --classic || exit $?;
|
||||
export PATH=/snap/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
|
||||
brew tap AdoptOpenJDK/openjdk || exit $?
|
||||
brew cask install adoptopenjdk8 || exit $?
|
||||
export JAVA_HOME=$(/usr/libexec/java_home)
|
||||
else
|
||||
sudo apt-get install -y openjdk-8-jdk
|
||||
sudo apt-get install -y openjdk-8-jdk || exit $?
|
||||
export PATH=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)/bin:$PATH
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)
|
||||
fi
|
||||
@@ -269,19 +269,19 @@ script:
|
||||
- if [ `command -v ccache` ]; then ccache -C; fi
|
||||
- case $TEST_GROUP in
|
||||
platform_dependent)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_END=db_block_cache_test make -j4 all_but_some_tests check_some
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=only make -j4 all_but_some_tests check_some
|
||||
;;
|
||||
1)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=db_block_cache_test ROCKSDBTESTS_END=db_iter_test make -j4 check_some
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_END=backupable_db_test make -j4 check_some
|
||||
;;
|
||||
2)
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" V=1 make -j4 tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" V=1 ROCKSDBTESTS_START=db_iter_test ROCKSDBTESTS_END=options_file_test make -j4 check_some
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" V=1 make -j4 tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=backupable_db_test ROCKSDBTESTS_END=db_universal_compaction_test make -j4 check_some
|
||||
;;
|
||||
3)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_START=options_file_test ROCKSDBTESTS_END=write_prepared_transaction_test make -j4 check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=db_universal_compaction_test ROCKSDBTESTS_END=table_properties_collector_test make -j4 check_some
|
||||
;;
|
||||
4)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=write_prepared_transaction_test make -j4 check_some
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=table_properties_collector_test make -j4 check_some
|
||||
;;
|
||||
esac
|
||||
- case $JOB_NAME in
|
||||
|
||||
+35
-5
@@ -254,12 +254,30 @@ endif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX "force building with AVX, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX2 "force building with AVX2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
# MSVC does not need a separate compiler flag to enable SSE4.2; if nmmintrin.h
|
||||
# is available, it is available by default.
|
||||
if(FORCE_SSE42 AND NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2 -mpclmul")
|
||||
endif()
|
||||
if(MSVC)
|
||||
if(FORCE_AVX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX")
|
||||
endif()
|
||||
# MSVC automatically enables BMI / lzcnt with AVX2.
|
||||
if(FORCE_AVX2)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
endif()
|
||||
else()
|
||||
if(FORCE_AVX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx")
|
||||
endif()
|
||||
if(FORCE_AVX2)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2 -mbmi -mlzcnt")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
@@ -546,6 +564,7 @@ set(SOURCES
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_file_addition.cc
|
||||
db/blob/blob_file_builder.cc
|
||||
db/blob/blob_file_garbage.cc
|
||||
db/blob/blob_file_meta.cc
|
||||
db/blob/blob_log_format.cc
|
||||
@@ -607,6 +626,7 @@ set(SOURCES
|
||||
db/version_edit.cc
|
||||
db/version_edit_handler.cc
|
||||
db/version_set.cc
|
||||
db/wal_edit.cc
|
||||
db/wal_manager.cc
|
||||
db/write_batch.cc
|
||||
db/write_batch_base.cc
|
||||
@@ -617,6 +637,7 @@ set(SOURCES
|
||||
env/env_encryption.cc
|
||||
env/env_hdfs.cc
|
||||
env/file_system.cc
|
||||
env/file_system_tracer.cc
|
||||
env/mock_env.cc
|
||||
file/delete_scheduler.cc
|
||||
file/file_prefetch_buffer.cc
|
||||
@@ -650,6 +671,7 @@ set(SOURCES
|
||||
monitoring/perf_level.cc
|
||||
monitoring/persistent_stats_history.cc
|
||||
monitoring/statistics.cc
|
||||
monitoring/stats_dump_scheduler.cc
|
||||
monitoring/thread_status_impl.cc
|
||||
monitoring/thread_status_updater.cc
|
||||
monitoring/thread_status_util.cc
|
||||
@@ -719,6 +741,7 @@ set(SOURCES
|
||||
tools/trace_analyzer_tool.cc
|
||||
trace_replay/trace_replay.cc
|
||||
trace_replay/block_cache_tracer.cc
|
||||
trace_replay/io_tracer.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
@@ -775,6 +798,8 @@ set(SOURCES
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
utilities/transactions/lock/lock_tracker.cc
|
||||
utilities/transactions/lock/point_lock_tracker.cc
|
||||
utilities/transactions/optimistic_transaction_db_impl.cc
|
||||
utilities/transactions/optimistic_transaction.cc
|
||||
utilities/transactions/pessimistic_transaction.cc
|
||||
@@ -992,10 +1017,11 @@ option(WITH_ALL_TESTS "Build all test, rather than a small subset" ON)
|
||||
if(WITH_TESTS OR WITH_BENCHMARK_TOOLS)
|
||||
add_subdirectory(third-party/gtest-1.8.1/fused-src/gtest)
|
||||
add_library(testharness STATIC
|
||||
test_util/mock_time_env.cc
|
||||
test_util/testharness.cc)
|
||||
target_link_libraries(testharness gtest)
|
||||
endif()
|
||||
|
||||
|
||||
if(WITH_TESTS)
|
||||
set(TESTS
|
||||
db/db_basic_test.cc
|
||||
@@ -1006,6 +1032,7 @@ if(WITH_TESTS)
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
db/blob/blob_file_builder_test.cc
|
||||
db/blob/blob_file_garbage_test.cc
|
||||
db/blob/db_blob_index_test.cc
|
||||
db/column_family_test.cc
|
||||
@@ -1076,6 +1103,7 @@ if(WITH_TESTS)
|
||||
db/version_edit_test.cc
|
||||
db/version_set_test.cc
|
||||
db/wal_manager_test.cc
|
||||
db/wal_edit_test.cc
|
||||
db/write_batch_test.cc
|
||||
db/write_callback_test.cc
|
||||
db/write_controller_test.cc
|
||||
@@ -1083,6 +1111,7 @@ if(WITH_TESTS)
|
||||
env/io_posix_test.cc
|
||||
env/mock_env_test.cc
|
||||
file/delete_scheduler_test.cc
|
||||
file/prefetch_test.cc
|
||||
file/random_access_file_reader_test.cc
|
||||
logging/auto_roll_logger_test.cc
|
||||
logging/env_logger_test.cc
|
||||
@@ -1095,6 +1124,7 @@ if(WITH_TESTS)
|
||||
monitoring/histogram_test.cc
|
||||
monitoring/iostats_context_test.cc
|
||||
monitoring/statistics_test.cc
|
||||
monitoring/stats_dump_scheduler_test.cc
|
||||
monitoring/stats_history_test.cc
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
@@ -1112,6 +1142,7 @@ if(WITH_TESTS)
|
||||
table/table_test.cc
|
||||
table/block_fetcher_test.cc
|
||||
test_util/testutil_test.cc
|
||||
trace_replay/io_tracer_test.cc
|
||||
tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
@@ -1200,7 +1231,7 @@ if(WITH_TESTS)
|
||||
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 ${GFLAGS_LIB} ${ROCKSDB_LIB})
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} testharness gtest ${THIRDPARTY_LIBS} ${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})
|
||||
@@ -1224,9 +1255,8 @@ if(WITH_TESTS)
|
||||
|
||||
if(ROCKSDB_LIB_FOR_C)
|
||||
set(C_TESTS db/c_test.c)
|
||||
# C executables must link to a shared object
|
||||
add_executable(c_test db/c_test.c)
|
||||
target_link_libraries(c_test ${ROCKSDB_SHARED_LIB} testharness)
|
||||
target_link_libraries(c_test ${ROCKSDB_LIB_FOR_C} testharness)
|
||||
add_test(NAME c_test COMMAND c_test${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check c_test)
|
||||
endif()
|
||||
@@ -1237,7 +1267,7 @@ if(WITH_BENCHMARK_TOOLS)
|
||||
tools/db_bench.cc
|
||||
tools/db_bench_tool.cc)
|
||||
target_link_libraries(db_bench
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
${ROCKSDB_LIB} ${THIRDPARTY_LIBS})
|
||||
|
||||
add_executable(cache_bench
|
||||
cache/cache_bench.cc)
|
||||
|
||||
+53
-3
@@ -1,5 +1,54 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 6.13.2 (10/13/2020)
|
||||
### Bug Fixes
|
||||
* Fix false positive flush/compaction `Status::Corruption` failure when `paranoid_file_checks == true` and range tombstones were written to the compaction output files.
|
||||
|
||||
## 6.13.1 (10/12/2020)
|
||||
### Bug Fixes
|
||||
* Since 6.12, memtable lookup should report unrecognized value_type as corruption (#7121).
|
||||
* Fixed a bug in the following combination of features: indexes with user keys (`format_version >= 3`), indexes are partitioned (`index_type == kTwoLevelIndexSearch`), and some index partitions are pinned in memory (`BlockBasedTableOptions::pin_l0_filter_and_index_blocks_in_cache`). The bug could cause keys to be truncated when read from the index leading to wrong read results or other unexpected behavior.
|
||||
* Fixed a bug when indexes are partitioned (`index_type == kTwoLevelIndexSearch`), some index partitions are pinned in memory (`BlockBasedTableOptions::pin_l0_filter_and_index_blocks_in_cache`), and partitions reads could be mixed between block cache and directly from the file (e.g., with `enable_index_compression == 1` and `mmap_read == 1`, partitions that were stored uncompressed due to poor compression ratio would be read directly from the file via mmap, while partitions that were stored compressed would be read from block cache). The bug could cause index partitions to be mistakenly considered empty during reads leading to wrong read results.
|
||||
|
||||
## 6.13 (09/24/2020)
|
||||
### Bug fixes
|
||||
* Fix a performance regression introduced in 6.4 that makes a upper bound check for every Next() even if keys are within a data block that is within the upper bound.
|
||||
* Fix a possible corruption to the LSM state (overlapping files within a level) when a `CompactRange()` for refitting levels (`CompactRangeOptions::change_level == true`) and another manual compaction are executed in parallel.
|
||||
* Sanitize `recycle_log_file_num` to zero when the user attempts to enable it in combination with `WALRecoveryMode::kTolerateCorruptedTailRecords`. Previously the two features were allowed together, which compromised the user's configured crash-recovery guarantees.
|
||||
* Fix a bug where a level refitting in CompactRange() might race with an automatic compaction that puts the data to the target level of the refitting. The bug has been there for years.
|
||||
* Fixed a bug in version 6.12 in which BackupEngine::CreateNewBackup could fail intermittently with non-OK status when backing up a read-write DB configured with a DBOptions::file_checksum_gen_factory.
|
||||
* Fix useless no-op compactions scheduled upon snapshot release when options.disable-auto-compactions = true.
|
||||
* Fix a bug when max_write_buffer_size_to_maintain is set, immutable flushed memtable destruction is delayed until the next super version is installed. A memtable is not added to delete list because of its reference hold by super version and super version doesn't switch because of empt delete list. So memory usage keeps on increasing beyond write_buffer_size + max_write_buffer_size_to_maintain.
|
||||
* Avoid converting MERGES to PUTS when allow_ingest_behind is true.
|
||||
* Fix compression dictionary sampling together with `SstFileWriter`. Previously, the dictionary would be trained/finalized immediately with zero samples. Now, the whole `SstFileWriter` file is buffered in memory and then sampled.
|
||||
* Fix a bug with `avoid_unnecessary_blocking_io=1` and creating backups (BackupEngine::CreateNewBackup) or checkpoints (Checkpoint::Create). With this setting and WAL enabled, these operations could randomly fail with non-OK status.
|
||||
* Fix a bug in which bottommost compaction continues to advance the underlying InternalIterator to skip tombstones even after shutdown.
|
||||
|
||||
### New Features
|
||||
* A new field `std::string requested_checksum_func_name` is added to `FileChecksumGenContext`, which enables the checksum factory to create generators for a suite of different functions.
|
||||
* Added a new subcommand, `ldb unsafe_remove_sst_file`, which removes a lost or corrupt SST file from a DB's metadata. This command involves data loss and must not be used on a live DB.
|
||||
|
||||
### Performance Improvements
|
||||
* Reduce thread number for multiple DB instances by re-using one global thread for statistics dumping and persisting.
|
||||
* Reduce write-amp in heavy write bursts in `kCompactionStyleLevel` compaction style with `level_compaction_dynamic_level_bytes` set.
|
||||
* BackupEngine incremental backups no longer read DB table files that are already saved to a shared part of the backup directory, unless `share_files_with_checksum` is used with `kLegacyCrc32cAndFileSize` naming (discouraged).
|
||||
* For `share_files_with_checksum`, we are confident there is no regression (vs. pre-6.12) in detecting DB or backup corruption at backup creation time, mostly because the old design did not leverage this extra checksum computation for detecting inconsistencies at backup creation time.
|
||||
* For `share_table_files` without "checksum" (not recommended), there is a regression in detecting fundamentally unsafe use of the option, greatly mitigated by file size checking (under "Behavior Changes"). Almost no reason to use `share_files_with_checksum=false` should remain.
|
||||
* `DB::VerifyChecksum` and `BackupEngine::VerifyBackup` with checksum checking are still able to catch corruptions that `CreateNewBackup` does not.
|
||||
|
||||
### Public API Change
|
||||
* Expose kTypeDeleteWithTimestamp in EntryType and update GetEntryType() accordingly.
|
||||
* Added file_checksum and file_checksum_func_name to TableFileCreationInfo, which can pass the table file checksum information through the OnTableFileCreated callback during flush and compaction.
|
||||
* A warning is added to `DB::DeleteFile()` API describing its known problems and deprecation plan.
|
||||
* Add a new stats level, i.e. StatsLevel::kExceptTickers (PR7329) to exclude tickers even if application passes a non-null Statistics object.
|
||||
* Added a new status code IOStatus::IOFenced() for the Env/FileSystem to indicate that writes from this instance are fenced off. Like any other background error, this error is returned to the user in Put/Merge/Delete/Flush calls and can be checked using Status::IsIOFenced().
|
||||
|
||||
### Behavior Changes
|
||||
* File abstraction `FSRandomAccessFile.Prefetch()` default return status is changed from `OK` to `NotSupported`. If the user inherited file doesn't implement prefetch, RocksDB will create internal prefetch buffer to improve read performance.
|
||||
* When retryable IO error happens during Flush (manifest write error is excluded) and WAL is disabled, originally it is mapped to kHardError. Now,it is mapped to soft error. So DB will not stall the writes unless the memtable is full. At the same time, when auto resume is triggered to recover the retryable IO error during Flush, SwitchMemtable is not called to avoid generating to many small immutable memtables. If WAL is enabled, no behavior changes.
|
||||
* When considering whether a table file is already backed up in a shared part of backup directory, BackupEngine would already query the sizes of source (DB) and pre-existing destination (backup) files. BackupEngine now uses these file sizes to detect corruption, as at least one of (a) old backup, (b) backup in progress, or (c) current DB is corrupt if there's a size mismatch.
|
||||
|
||||
### Others
|
||||
* Error in prefetching partitioned index blocks will not be swallowed. It will fail the query and return the IOError users.
|
||||
|
||||
## 6.12 (2020-07-28)
|
||||
### Public API Change
|
||||
@@ -26,11 +75,12 @@
|
||||
* Make compaction report InternalKey corruption while iterating over the input.
|
||||
* Fix a bug which may cause MultiGet to be slow because it may read more data than requested, but this won't affect correctness. The bug was introduced in 6.10 release.
|
||||
* Fail recovery and report once hitting a physical log record checksum mismatch, while reading MANIFEST. RocksDB should not continue processing the MANIFEST any further.
|
||||
* Fixed a bug in size-amp-triggered and periodic-triggered universal compaction, where the compression settings for the first input level were used rather than the compression settings for the output (bottom) level.
|
||||
|
||||
### 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.
|
||||
* `BackupTableNameOption BackupableDBOptions::share_files_with_checksum_naming` is added, where `BackupTableNameOption` is an `enum` type with two enumerators `kChecksumAndFileSize` and `kOptionalChecksumAndDbSessionId`. By default, `BackupableDBOptions::share_files_with_checksum_naming` is set to `kOptionalChecksumAndDbSessionId`. In the default case, backup table filenames generated by this version of RocksDB are of the form either `<file_number>_<crc32c>_<db_session_id>.sst` or `<file_number>_<db_session_id>.sst` as opposed to `<file_number>_<crc32c>_<file_size>.sst`. Specifically, table filenames are of the form `<file_number>_<crc32c>_<db_session_id>.sst` if `DBOptions::file_checksum_gen_factory` is set to `GetFileChecksumGenCrc32cFactory()`. Futhermore, the checksum value `<crc32c>` appeared in the filenames is hexadecimal-encoded, instead of being decimal-encoded `uint32_t` value. If `DBOptions::file_checksum_gen_factory` is `nullptr`, the table filenames are of the form `<file_number>_<db_session_id>.sst`. The new default behavior fixes the backup file name collision problem, which might be possible at large scale, but the option `kChecksumAndFileSize` is added to allow use of old naming in case it is needed. Moreover, for table files generated prior to this version of RocksDB, using `kOptionalChecksumAndDbSessionId` will fall back on `kChecksumAndFileSize`. In these cases, the checksum value `<crc32c>` in the filenames `<file_number>_<crc32c>_<file_size>.sst` is decimal-encoded `uint32_t` value as before. This default behavior change is not an upgrade issue, because previous versions of RocksDB can read, restore, and delete backups using new names, and it's OK for a backup directory to use a mixture of table file naming schemes. Note that `share_files_with_checksum_naming` comes into effect only when both `share_files_with_checksum` and `share_table_files` are true.
|
||||
* `BackupableDBOptions::share_files_with_checksum_naming` is added with new default behavior for naming backup files with `share_files_with_checksum`, to address performance and backup integrity issues. See API comments for details.
|
||||
* Added auto resume function to automatically recover the DB from background Retryable IO Error. When retryable IOError happens during flush and WAL write, the error is mapped to Hard Error and DB will be in read mode. When retryable IO Error happens during compaction, the error will be mapped to Soft Error. DB is still in write/read mode. Autoresume function will create a thread for a DB to call DB->ResumeImpl() to try the recover for Retryable IO Error during flush and WAL write. Compaction will be rescheduled by itself if retryable IO Error happens. Auto resume may also cause other Retryable IO Error during the recovery, so the recovery will fail. Retry the auto resume may solve the issue, so we use max_bgerror_resume_count to decide how many resume cycles will be tried in total. If it is <=0, auto resume retryable IO Error is disabled. Default is INT_MAX, which will lead to a infinit auto resume. bgerror_resume_retry_interval decides the time interval between two auto resumes.
|
||||
* Option `max_subcompactions` can be set dynamically using DB::SetDBOptions().
|
||||
* Added experimental ColumnFamilyOptions::sst_partitioner_factory to define determine the partitioning of sst files. This helps compaction to split the files on interesting boundaries (key prefixes) to make propagation of sst files less write amplifying (covering the whole key space).
|
||||
@@ -38,7 +88,7 @@
|
||||
### Performance Improvements
|
||||
* Eliminate key copies for internal comparisons while accessing ingested block-based tables.
|
||||
* Reduce key comparisons during random access in all block-based tables.
|
||||
* BackupEngine avoids unnecessary repeated checksum computation for backing up a table file to the `shared_checksum` directory when using `kOptionalChecksumAndDbSessionId`, except on SST files generated before this version of RocksDB, which fall back on using `kChecksumAndFileSize`.
|
||||
* BackupEngine avoids unnecessary repeated checksum computation for backing up a table file to the `shared_checksum` directory when using `share_files_with_checksum_naming = kUseDbSessionId` (new default), except on SST files generated before this version of RocksDB, which fall back on using `kLegacyCrc32cAndFileSize`.
|
||||
|
||||
## 6.11 (6/12/2020)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -131,11 +131,14 @@ endif
|
||||
# Figure out optimize level.
|
||||
ifneq ($(DEBUG_LEVEL), 2)
|
||||
ifeq ($(LITE), 0)
|
||||
OPT += -O2
|
||||
OPTIMIZE_LEVEL ?= -O2
|
||||
else
|
||||
OPT += -Os
|
||||
OPTIMIZE_LEVEL ?= -Os
|
||||
endif
|
||||
endif
|
||||
# `OPTIMIZE_LEVEL` is empty when the user does not set it and `DEBUG_LEVEL=2`.
|
||||
# In that case, the compiler default (`-O0` for gcc and clang) will be used.
|
||||
OPT += $(OPTIMIZE_LEVEL)
|
||||
|
||||
# compile with -O2 if debug level is not 2
|
||||
ifneq ($(DEBUG_LEVEL), 2)
|
||||
@@ -262,7 +265,7 @@ dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
include make_config.mk
|
||||
|
||||
export JAVAC_ARGS
|
||||
CLEAN_FILES += make_config.mk
|
||||
CLEAN_FILES += make_config.mk rocksdb.pc
|
||||
|
||||
ifeq ($(V), 1)
|
||||
$(info $(shell uname -a))
|
||||
@@ -407,6 +410,10 @@ ifdef TEST_CACHE_LINE_SIZE
|
||||
PLATFORM_CCFLAGS += -DTEST_CACHE_LINE_SIZE=$(TEST_CACHE_LINE_SIZE)
|
||||
PLATFORM_CXXFLAGS += -DTEST_CACHE_LINE_SIZE=$(TEST_CACHE_LINE_SIZE)
|
||||
endif
|
||||
ifdef TEST_UINT128_COMPAT
|
||||
PLATFORM_CCFLAGS += -DTEST_UINT128_COMPAT=1
|
||||
PLATFORM_CXXFLAGS += -DTEST_UINT128_COMPAT=1
|
||||
endif
|
||||
|
||||
# This (the first rule) must depend on "all".
|
||||
default: all
|
||||
@@ -414,6 +421,11 @@ default: all
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wunused-parameter
|
||||
|
||||
ifdef USE_CLANG
|
||||
# Used by some teams in Facebook
|
||||
WARNING_FLAGS += -Wshift-sign-overflow
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
WARNING_FLAGS += -Wno-unused-lambda-capture
|
||||
endif
|
||||
@@ -560,7 +572,10 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
TESTS_PASSING_ASC = \
|
||||
arena_test \
|
||||
autovector_test \
|
||||
cache_test \
|
||||
lru_cache_test \
|
||||
blob_file_addition_test \
|
||||
blob_file_builder_test \
|
||||
blob_file_garbage_test \
|
||||
bloom_test \
|
||||
cassandra_format_test \
|
||||
@@ -571,6 +586,7 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
crc32c_test \
|
||||
dbformat_test \
|
||||
defer_test \
|
||||
filename_test \
|
||||
dynamic_bloom_test \
|
||||
env_basic_test \
|
||||
env_test \
|
||||
@@ -596,12 +612,18 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
repeatable_thread_test \
|
||||
skiplist_test \
|
||||
slice_test \
|
||||
sst_dump_test \
|
||||
statistics_test \
|
||||
thread_local_test \
|
||||
env_timed_test \
|
||||
filelock_test \
|
||||
timer_queue_test \
|
||||
timer_test \
|
||||
util_merge_operators_test \
|
||||
block_cache_trace_analyzer_test \
|
||||
block_cache_tracer_test \
|
||||
cache_simulator_test \
|
||||
sim_cache_test \
|
||||
version_edit_test \
|
||||
work_queue_test \
|
||||
write_controller_test \
|
||||
@@ -611,7 +633,7 @@ TESTS_PASSING_ASC += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
|
||||
# Enable building all unit tests, but use check_some to run only tests
|
||||
# known to pass ASC
|
||||
# known to pass ASC (ASSERT_STATUS_CHECKED)
|
||||
SUBSET := $(TESTS_PASSING_ASC)
|
||||
# Alternate: only build unit tests known to pass ASC, and run them
|
||||
# with make check
|
||||
@@ -620,6 +642,38 @@ endif
|
||||
else
|
||||
SUBSET := $(TESTS)
|
||||
endif
|
||||
# Not necessarily well thought out or up-to-date, but matches old list
|
||||
TESTS_PLATFORM_DEPENDENT := \
|
||||
db_basic_test \
|
||||
db_with_timestamp_basic_test \
|
||||
db_encryption_test \
|
||||
db_test2 \
|
||||
external_sst_file_basic_test \
|
||||
auto_roll_logger_test \
|
||||
bloom_test \
|
||||
dynamic_bloom_test \
|
||||
c_test \
|
||||
checkpoint_test \
|
||||
crc32c_test \
|
||||
coding_test \
|
||||
inlineskiplist_test \
|
||||
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 \
|
||||
|
||||
# Sort SUBSET for filtering, except db_test is special (expensive) so
|
||||
# is placed first (out-of-order)
|
||||
SUBSET := $(filter db_test, $(SUBSET)) $(sort $(filter-out db_test, $(SUBSET)))
|
||||
|
||||
ifdef ROCKSDBTESTS_START
|
||||
SUBSET := $(shell echo $(SUBSET) | sed 's/^.*$(ROCKSDBTESTS_START)/$(ROCKSDBTESTS_START)/')
|
||||
endif
|
||||
@@ -628,6 +682,12 @@ ifdef ROCKSDBTESTS_END
|
||||
SUBSET := $(shell echo $(SUBSET) | sed 's/$(ROCKSDBTESTS_END).*//')
|
||||
endif
|
||||
|
||||
ifeq ($(ROCKSDBTESTS_PLATFORM_DEPENDENT), only)
|
||||
SUBSET := $(filter $(TESTS_PLATFORM_DEPENDENT), $(SUBSET))
|
||||
else ifeq ($(ROCKSDBTESTS_PLATFORM_DEPENDENT), exclude)
|
||||
SUBSET := $(filter-out $(TESTS_PLATFORM_DEPENDENT), $(SUBSET))
|
||||
endif
|
||||
|
||||
# bench_tool_analyer main is in bench_tool_analyzer_tool, or this would be simpler...
|
||||
TOOLS = $(patsubst %.cc, %, $(notdir $(patsubst %_tool.cc, %.cc, $(TOOLS_MAIN_SOURCES))))
|
||||
|
||||
@@ -718,7 +778,7 @@ endif # PLATFORM_SHARED_EXT
|
||||
|
||||
.PHONY: blackbox_crash_test check clean coverage crash_test ldb_tests package \
|
||||
release tags tags0 valgrind_check whitebox_crash_test format static_lib shared_lib all \
|
||||
dbg rocksdbjavastatic rocksdbjava install install-static install-shared uninstall \
|
||||
dbg rocksdbjavastatic rocksdbjava gen-pc 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 \
|
||||
@@ -1525,6 +1585,9 @@ compact_on_deletion_collector_test: $(OBJ_DIR)/utilities/table_properties_collec
|
||||
wal_manager_test: $(OBJ_DIR)/db/wal_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
wal_edit_test: $(OBJ_DIR)/db/wal_edit_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
dbformat_test: $(OBJ_DIR)/db/dbformat_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1792,52 +1855,82 @@ defer_test: $(OBJ_DIR)/util/defer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
blob_file_addition_test: $(OBJ_DIR)/db/blob/blob_file_addition_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_file_builder_test: $(OBJ_DIR)/db/blob/blob_file_builder_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_file_garbage_test: $(OBJ_DIR)/db/blob/blob_file_garbage_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
timer_test: $(OBJ_DIR)/util/timer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
stats_dump_scheduler_test: $(OBJ_DIR)/monitoring/stats_dump_scheduler_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
testutil_test: $(OBJ_DIR)/test_util/testutil_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
io_tracer_test: $(OBJ_DIR)/trace_replay/io_tracer_test.o $(OBJ_DIR)/trace_replay/io_tracer.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
prefetch_test: $(OBJ_DIR)/file/prefetch_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
PREFIX ?= /usr/local
|
||||
LIBDIR ?= $(PREFIX)/lib
|
||||
INSTALL_LIBDIR = $(DESTDIR)$(LIBDIR)
|
||||
|
||||
uninstall:
|
||||
rm -rf $(INSTALL_PATH)/include/rocksdb \
|
||||
$(INSTALL_PATH)/lib/$(LIBRARY) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED4) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED3) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED2) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED1)
|
||||
rm -rf $(DESTDIR)$(PREFIX)/include/rocksdb \
|
||||
$(INSTALL_LIBDIR)/$(LIBRARY) \
|
||||
$(INSTALL_LIBDIR)/$(SHARED4) \
|
||||
$(INSTALL_LIBDIR)/$(SHARED3) \
|
||||
$(INSTALL_LIBDIR)/$(SHARED2) \
|
||||
$(INSTALL_LIBDIR)/$(SHARED1) \
|
||||
$(INSTALL_LIBDIR)/pkgconfig/rocksdb.pc
|
||||
|
||||
install-headers:
|
||||
install -d $(INSTALL_PATH)/lib
|
||||
install-headers: gen-pc
|
||||
install -d $(INSTALL_LIBDIR)
|
||||
install -d $(INSTALL_LIBDIR)/pkgconfig
|
||||
for header_dir in `$(FIND) "include/rocksdb" -type d`; do \
|
||||
install -d $(INSTALL_PATH)/$$header_dir; \
|
||||
install -d $(DESTDIR)/$(PREFIX)/$$header_dir; \
|
||||
done
|
||||
for header in `$(FIND) "include/rocksdb" -type f -name *.h`; do \
|
||||
install -C -m 644 $$header $(INSTALL_PATH)/$$header; \
|
||||
install -C -m 644 $$header $(DESTDIR)/$(PREFIX)/$$header; \
|
||||
done
|
||||
install -C -m 644 rocksdb.pc $(INSTALL_LIBDIR)/pkgconfig/rocksdb.pc
|
||||
|
||||
install-static: install-headers $(LIBRARY)
|
||||
install -C -m 755 $(LIBRARY) $(INSTALL_PATH)/lib
|
||||
install -d $(INSTALL_LIBDIR)
|
||||
install -C -m 755 $(LIBRARY) $(INSTALL_LIBDIR)
|
||||
|
||||
install-shared: install-headers $(SHARED4)
|
||||
install -C -m 755 $(SHARED4) $(INSTALL_PATH)/lib && \
|
||||
ln -fs $(SHARED4) $(INSTALL_PATH)/lib/$(SHARED3) && \
|
||||
ln -fs $(SHARED4) $(INSTALL_PATH)/lib/$(SHARED2) && \
|
||||
ln -fs $(SHARED4) $(INSTALL_PATH)/lib/$(SHARED1)
|
||||
install -d $(INSTALL_LIBDIR)
|
||||
install -C -m 755 $(SHARED4) $(INSTALL_LIBDIR)
|
||||
ln -fs $(SHARED4) $(INSTALL_LIBDIR)/$(SHARED3)
|
||||
ln -fs $(SHARED4) $(INSTALL_LIBDIR)/$(SHARED2)
|
||||
ln -fs $(SHARED4) $(INSTALL_LIBDIR)/$(SHARED1)
|
||||
|
||||
# install static by default + install shared if it exists
|
||||
install: install-static
|
||||
[ -e $(SHARED4) ] && $(MAKE) install-shared || :
|
||||
|
||||
# Generate the pkg-config file
|
||||
gen-pc:
|
||||
-echo 'prefix=$(PREFIX)' > rocksdb.pc
|
||||
-echo 'exec_prefix=$${prefix}' >> rocksdb.pc
|
||||
-echo 'includedir=$${prefix}/include' >> rocksdb.pc
|
||||
-echo 'libdir=$(LIBDIR)' >> rocksdb.pc
|
||||
-echo '' >> rocksdb.pc
|
||||
-echo 'Name: rocksdb' >> rocksdb.pc
|
||||
-echo 'Description: An embeddable persistent key-value store for fast storage' >> rocksdb.pc
|
||||
-echo Version: $(shell ./build_tools/version.sh full) >> rocksdb.pc
|
||||
-echo 'Libs: -L$${libdir} $(EXEC_LDFLAGS) -lrocksdb' >> rocksdb.pc
|
||||
-echo 'Libs.private: $(PLATFORM_LDFLAGS)' >> rocksdb.pc
|
||||
-echo 'Cflags: -I$${includedir} $(PLATFORM_CXXFLAGS)' >> rocksdb.pc
|
||||
|
||||
#-------------------------------------------------
|
||||
|
||||
|
||||
@@ -1874,10 +1967,11 @@ ifneq (,$(filter ppc% arm64 aarch64 sparc64, $(MACHINE)))
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
|
||||
endif
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux$(ARCH)$(JNI_LIBC_POSTFIX).jar
|
||||
ROCKSDB_JAR_ALL = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-javadoc.jar
|
||||
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-sources.jar
|
||||
ROCKSDB_JAVA_VERSION ?= $(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux$(ARCH)$(JNI_LIBC_POSTFIX).jar
|
||||
ROCKSDB_JAR_ALL = rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar
|
||||
ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar
|
||||
ROCKSDB_SOURCES_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar
|
||||
SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.2.11
|
||||
@@ -1899,7 +1993,7 @@ CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
ROCKSDBJNILIB = librocksdbjni-osx.jnilib
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-osx.jar
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-osx.jar
|
||||
SHA256_CMD = openssl sha256 -r
|
||||
ifneq ("$(wildcard $(JAVA_HOME)/include/darwin)","")
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include -I $(JAVA_HOME)/include/darwin
|
||||
@@ -1910,7 +2004,7 @@ endif
|
||||
ifeq ($(PLATFORM), OS_FREEBSD)
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/freebsd
|
||||
ROCKSDBJNILIB = librocksdbjni-freebsd$(ARCH).so
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-freebsd$(ARCH).jar
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-freebsd$(ARCH).jar
|
||||
endif
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ROCKSDBJNILIB = librocksdbjni-solaris$(ARCH).so
|
||||
@@ -1927,7 +2021,7 @@ endif
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/openbsd
|
||||
ROCKSDBJNILIB = librocksdbjni-openbsd$(ARCH).so
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-openbsd$(ARCH).jar
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-openbsd$(ARCH).jar
|
||||
endif
|
||||
|
||||
libz.a:
|
||||
@@ -2027,17 +2121,22 @@ rocksdbjavastatic: $(LIB_OBJECTS) $(JAVA_COMPRESSIONS)
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
cd java/target/apidocs;jar -cf ../$(ROCKSDB_JAVADOCS_JAR) *
|
||||
cd java/src/main/java;jar -cf ../../../target/$(ROCKSDB_SOURCES_JAR) org
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
openssl sha1 java/target/$(ROCKSDB_JAVADOCS_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAVADOCS_JAR).sha1
|
||||
openssl sha1 java/target/$(ROCKSDB_SOURCES_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_SOURCES_JAR).sha1
|
||||
|
||||
rocksdbjavastaticrelease: rocksdbjavastatic
|
||||
cd java/crossbuild && (vagrant destroy -f || true) && vagrant up linux32 && vagrant halt linux32 && vagrant up linux64 && vagrant halt linux64 && vagrant up linux64-musl && vagrant halt linux64-musl
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR_ALL) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR_ALL).sha1
|
||||
|
||||
rocksdbjavastaticreleasedocker: rocksdbjavastatic rocksdbjavastaticdockerx86 rocksdbjavastaticdockerx86_64 rocksdbjavastaticdockerx86musl rocksdbjavastaticdockerx86_64musl
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR_ALL) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR_ALL).sha1
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
mkdir -p java/target
|
||||
@@ -2075,29 +2174,25 @@ rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentr
|
||||
|
||||
rocksdbjavastaticpublishdocker: rocksdbjavastaticreleasedocker rocksdbjavastaticpublishcentral
|
||||
|
||||
rocksdbjavastaticpublishcentral:
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-javadoc.jar -Dclassifier=javadoc
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-sources.jar -Dclassifier=sources
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux64.jar -Dclassifier=linux64
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux32.jar -Dclassifier=linux32
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux64-musl.jar -Dclassifier=linux64-musl
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux32-musl.jar -Dclassifier=linux32-musl
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-osx.jar -Dclassifier=osx
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-win64.jar -Dclassifier=win64
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
rocksdbjavastaticpublishcentral: rocksdbjavageneratepom
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar -Dclassifier=javadoc
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar -Dclassifier=sources
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux64.jar -Dclassifier=linux64
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux32.jar -Dclassifier=linux32
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux64-musl.jar -Dclassifier=linux64-musl
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux32-musl.jar -Dclassifier=linux32-musl
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-osx.jar -Dclassifier=osx
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-win64.jar -Dclassifier=win64
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar
|
||||
|
||||
rocksdbjavageneratepom:
|
||||
cd java;cat pom.xml.template | sed 's/\$${ROCKSDB_JAVA_VERSION}/$(ROCKSDB_JAVA_VERSION)/' > pom.xml
|
||||
|
||||
# A version of each $(LIBOBJECTS) compiled with -fPIC
|
||||
|
||||
jl/%.o: %.cc
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) -fPIC -c $< -o $@ $(COVERAGEFLAGS)
|
||||
|
||||
jl/crc32c_ppc.o: util/crc32c_ppc.c
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
jl/crc32c_ppc_asm.o: util/crc32c_ppc_asm.S
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
|
||||
rocksdbjava: $(LIB_OBJECTS)
|
||||
$(AM_V_GEN)cd java;$(MAKE) javalib;
|
||||
$(AM_V_at)rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
@@ -2105,6 +2200,7 @@ rocksdbjava: $(LIB_OBJECTS)
|
||||
$(AM_V_at)cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
$(AM_V_at)cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
$(AM_V_at)cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
$(AM_V_at)openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
|
||||
jclean:
|
||||
cd java;$(MAKE) clean;
|
||||
@@ -2159,7 +2255,7 @@ ifeq ($(HAVE_POWER8),1)
|
||||
$(OBJ_DIR)/util/crc32c_ppc.o: util/crc32c_ppc.c
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
+$(OBJ_DIR)/util/crc32c_ppc_asm.o: util/crc32c_ppc_asm.S
|
||||
$(OBJ_DIR)/util/crc32c_ppc_asm.o: util/crc32c_ppc_asm.S
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
endif
|
||||
$(OBJ_DIR)/%.o: %.cc
|
||||
@@ -2205,7 +2301,7 @@ $(OBJ_DIR)/%.c.d: %.c
|
||||
@$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) \
|
||||
-MM -MT'$@' -MT'$(<:.c=.o)' "$<" -o '$@'
|
||||
|
||||
+$(OBJ_DIR)/%.S.d: %.S
|
||||
$(OBJ_DIR)/%.S.d: %.S
|
||||
@$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) \
|
||||
-MM -MT'$@' -MT'$(<:.S=.o)' "$<" -o '$@'
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ cpp_library(
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_builder.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/blob/blob_log_format.cc",
|
||||
@@ -183,6 +184,7 @@ cpp_library(
|
||||
"db/version_edit.cc",
|
||||
"db/version_edit_handler.cc",
|
||||
"db/version_set.cc",
|
||||
"db/wal_edit.cc",
|
||||
"db/wal_manager.cc",
|
||||
"db/write_batch.cc",
|
||||
"db/write_batch_base.cc",
|
||||
@@ -230,6 +232,7 @@ cpp_library(
|
||||
"monitoring/perf_level.cc",
|
||||
"monitoring/persistent_stats_history.cc",
|
||||
"monitoring/statistics.cc",
|
||||
"monitoring/stats_dump_scheduler.cc",
|
||||
"monitoring/thread_status_impl.cc",
|
||||
"monitoring/thread_status_updater.cc",
|
||||
"monitoring/thread_status_updater_debug.cc",
|
||||
@@ -357,6 +360,8 @@ cpp_library(
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/transactions/lock/lock_tracker.cc",
|
||||
"utilities/transactions/lock/point_lock_tracker.cc",
|
||||
"utilities/transactions/optimistic_transaction.cc",
|
||||
"utilities/transactions/optimistic_transaction_db_impl.cc",
|
||||
"utilities/transactions/pessimistic_transaction.cc",
|
||||
@@ -382,6 +387,284 @@ cpp_library(
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_whole_archive_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_builder.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",
|
||||
"db/compacted_db_impl.cc",
|
||||
"db/compaction/compaction.cc",
|
||||
"db/compaction/compaction_iterator.cc",
|
||||
"db/compaction/compaction_job.cc",
|
||||
"db/compaction/compaction_picker.cc",
|
||||
"db/compaction/compaction_picker_fifo.cc",
|
||||
"db/compaction/compaction_picker_level.cc",
|
||||
"db/compaction/compaction_picker_universal.cc",
|
||||
"db/compaction/sst_partitioner.cc",
|
||||
"db/convenience.cc",
|
||||
"db/db_filesnapshot.cc",
|
||||
"db/db_impl/db_impl.cc",
|
||||
"db/db_impl/db_impl_compaction_flush.cc",
|
||||
"db/db_impl/db_impl_debug.cc",
|
||||
"db/db_impl/db_impl_experimental.cc",
|
||||
"db/db_impl/db_impl_files.cc",
|
||||
"db/db_impl/db_impl_open.cc",
|
||||
"db/db_impl/db_impl_readonly.cc",
|
||||
"db/db_impl/db_impl_secondary.cc",
|
||||
"db/db_impl/db_impl_write.cc",
|
||||
"db/db_info_dumper.cc",
|
||||
"db/db_iter.cc",
|
||||
"db/dbformat.cc",
|
||||
"db/error_handler.cc",
|
||||
"db/event_helpers.cc",
|
||||
"db/experimental.cc",
|
||||
"db/external_sst_file_ingestion_job.cc",
|
||||
"db/file_indexer.cc",
|
||||
"db/flush_job.cc",
|
||||
"db/flush_scheduler.cc",
|
||||
"db/forward_iterator.cc",
|
||||
"db/import_column_family_job.cc",
|
||||
"db/internal_stats.cc",
|
||||
"db/log_reader.cc",
|
||||
"db/log_writer.cc",
|
||||
"db/logs_with_prep_tracker.cc",
|
||||
"db/malloc_stats.cc",
|
||||
"db/memtable.cc",
|
||||
"db/memtable_list.cc",
|
||||
"db/merge_helper.cc",
|
||||
"db/merge_operator.cc",
|
||||
"db/range_del_aggregator.cc",
|
||||
"db/range_tombstone_fragmenter.cc",
|
||||
"db/repair.cc",
|
||||
"db/snapshot_impl.cc",
|
||||
"db/table_cache.cc",
|
||||
"db/table_properties_collector.cc",
|
||||
"db/transaction_log_impl.cc",
|
||||
"db/trim_history_scheduler.cc",
|
||||
"db/version_builder.cc",
|
||||
"db/version_edit.cc",
|
||||
"db/version_edit_handler.cc",
|
||||
"db/version_set.cc",
|
||||
"db/wal_edit.cc",
|
||||
"db/wal_manager.cc",
|
||||
"db/write_batch.cc",
|
||||
"db/write_batch_base.cc",
|
||||
"db/write_controller.cc",
|
||||
"db/write_thread.cc",
|
||||
"env/env.cc",
|
||||
"env/env_chroot.cc",
|
||||
"env/env_encryption.cc",
|
||||
"env/env_hdfs.cc",
|
||||
"env/env_posix.cc",
|
||||
"env/file_system.cc",
|
||||
"env/file_system_tracer.cc",
|
||||
"env/fs_posix.cc",
|
||||
"env/io_posix.cc",
|
||||
"env/mock_env.cc",
|
||||
"file/delete_scheduler.cc",
|
||||
"file/file_prefetch_buffer.cc",
|
||||
"file/file_util.cc",
|
||||
"file/filename.cc",
|
||||
"file/random_access_file_reader.cc",
|
||||
"file/read_write_util.cc",
|
||||
"file/readahead_raf.cc",
|
||||
"file/sequence_file_reader.cc",
|
||||
"file/sst_file_manager_impl.cc",
|
||||
"file/writable_file_writer.cc",
|
||||
"logging/auto_roll_logger.cc",
|
||||
"logging/event_logger.cc",
|
||||
"logging/log_buffer.cc",
|
||||
"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",
|
||||
"memtable/skiplistrep.cc",
|
||||
"memtable/vectorrep.cc",
|
||||
"memtable/write_buffer_manager.cc",
|
||||
"monitoring/histogram.cc",
|
||||
"monitoring/histogram_windowing.cc",
|
||||
"monitoring/in_memory_stats_history.cc",
|
||||
"monitoring/instrumented_mutex.cc",
|
||||
"monitoring/iostats_context.cc",
|
||||
"monitoring/perf_context.cc",
|
||||
"monitoring/perf_level.cc",
|
||||
"monitoring/persistent_stats_history.cc",
|
||||
"monitoring/statistics.cc",
|
||||
"monitoring/stats_dump_scheduler.cc",
|
||||
"monitoring/thread_status_impl.cc",
|
||||
"monitoring/thread_status_updater.cc",
|
||||
"monitoring/thread_status_updater_debug.cc",
|
||||
"monitoring/thread_status_util.cc",
|
||||
"monitoring/thread_status_util_debug.cc",
|
||||
"options/cf_options.cc",
|
||||
"options/db_options.cc",
|
||||
"options/options.cc",
|
||||
"options/options_helper.cc",
|
||||
"options/options_parser.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",
|
||||
"table/block_based/filter_block_reader_common.cc",
|
||||
"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",
|
||||
"table/cuckoo/cuckoo_table_factory.cc",
|
||||
"table/cuckoo/cuckoo_table_reader.cc",
|
||||
"table/format.cc",
|
||||
"table/get_context.cc",
|
||||
"table/iterator.cc",
|
||||
"table/merging_iterator.cc",
|
||||
"table/meta_blocks.cc",
|
||||
"table/persistent_cache_helper.cc",
|
||||
"table/plain/plain_table_bloom.cc",
|
||||
"table/plain/plain_table_builder.cc",
|
||||
"table/plain/plain_table_factory.cc",
|
||||
"table/plain/plain_table_index.cc",
|
||||
"table/plain/plain_table_key_coding.cc",
|
||||
"table/plain/plain_table_reader.cc",
|
||||
"table/sst_file_dumper.cc",
|
||||
"table/sst_file_reader.cc",
|
||||
"table/sst_file_writer.cc",
|
||||
"table/table_properties.cc",
|
||||
"table/two_level_iterator.cc",
|
||||
"test_util/sync_point.cc",
|
||||
"test_util/sync_point_impl.cc",
|
||||
"test_util/transaction_test_util.cc",
|
||||
"tools/dump/db_dump_tool.cc",
|
||||
"tools/ldb_cmd.cc",
|
||||
"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",
|
||||
"util/compaction_job_stats_impl.cc",
|
||||
"util/comparator.cc",
|
||||
"util/compression_context_cache.cc",
|
||||
"util/concurrent_task_limiter_impl.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
"util/file_checksum_helper.cc",
|
||||
"util/hash.cc",
|
||||
"util/murmurhash.cc",
|
||||
"util/random.cc",
|
||||
"util/rate_limiter.cc",
|
||||
"util/slice.cc",
|
||||
"util/status.cc",
|
||||
"util/string_util.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/xxhash.cc",
|
||||
"utilities/backupable/backupable_db.cc",
|
||||
"utilities/blob_db/blob_compaction_filter.cc",
|
||||
"utilities/blob_db/blob_db.cc",
|
||||
"utilities/blob_db/blob_db_impl.cc",
|
||||
"utilities/blob_db/blob_db_impl_filesnapshot.cc",
|
||||
"utilities/blob_db/blob_dump_tool.cc",
|
||||
"utilities/blob_db/blob_file.cc",
|
||||
"utilities/cassandra/cassandra_compaction_filter.cc",
|
||||
"utilities/cassandra/format.cc",
|
||||
"utilities/cassandra/merge_operator.cc",
|
||||
"utilities/checkpoint/checkpoint_impl.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
"utilities/debug.cc",
|
||||
"utilities/env_mirror.cc",
|
||||
"utilities/env_timed.cc",
|
||||
"utilities/fault_injection_env.cc",
|
||||
"utilities/fault_injection_fs.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators/bytesxor.cc",
|
||||
"utilities/merge_operators/max.cc",
|
||||
"utilities/merge_operators/put.cc",
|
||||
"utilities/merge_operators/sortlist.cc",
|
||||
"utilities/merge_operators/string_append/stringappend.cc",
|
||||
"utilities/merge_operators/string_append/stringappend2.cc",
|
||||
"utilities/merge_operators/uint64add.cc",
|
||||
"utilities/object_registry.cc",
|
||||
"utilities/option_change_migration/option_change_migration.cc",
|
||||
"utilities/options/options_util.cc",
|
||||
"utilities/persistent_cache/block_cache_tier.cc",
|
||||
"utilities/persistent_cache/block_cache_tier_file.cc",
|
||||
"utilities/persistent_cache/block_cache_tier_metadata.cc",
|
||||
"utilities/persistent_cache/persistent_cache_tier.cc",
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/simulator_cache/cache_simulator.cc",
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/transactions/lock/lock_tracker.cc",
|
||||
"utilities/transactions/lock/point_lock_tracker.cc",
|
||||
"utilities/transactions/optimistic_transaction.cc",
|
||||
"utilities/transactions/optimistic_transaction_db_impl.cc",
|
||||
"utilities/transactions/pessimistic_transaction.cc",
|
||||
"utilities/transactions/pessimistic_transaction_db.cc",
|
||||
"utilities/transactions/snapshot_checker.cc",
|
||||
"utilities/transactions/transaction_base.cc",
|
||||
"utilities/transactions/transaction_db_mutex_impl.cc",
|
||||
"utilities/transactions/transaction_lock_mgr.cc",
|
||||
"utilities/transactions/transaction_util.cc",
|
||||
"utilities/transactions/write_prepared_txn.cc",
|
||||
"utilities/transactions/write_prepared_txn_db.cc",
|
||||
"utilities/transactions/write_unprepared_txn.cc",
|
||||
"utilities/transactions/write_unprepared_txn_db.cc",
|
||||
"utilities/ttl/db_ttl_impl.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
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 = [],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = True,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -389,6 +672,7 @@ cpp_library(
|
||||
srcs = [
|
||||
"db/db_test_util.cc",
|
||||
"table/mock_table.cc",
|
||||
"test_util/mock_time_env.cc",
|
||||
"test_util/testharness.cc",
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer.cc",
|
||||
@@ -405,6 +689,7 @@ cpp_library(
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
],
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -423,6 +708,7 @@ cpp_library(
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -483,6 +769,7 @@ cpp_library(
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
# [test_name, test_src, test_type, extra_deps, extra_compiler_flags]
|
||||
@@ -529,6 +816,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_file_builder_test",
|
||||
"db/blob/blob_file_builder_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_file_garbage_test",
|
||||
"db/blob/blob_file_garbage_test.cc",
|
||||
@@ -1355,6 +1649,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"prefetch_test",
|
||||
"file/prefetch_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"prefix_test",
|
||||
"db/prefix_test.cc",
|
||||
@@ -1467,6 +1768,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"stats_dump_scheduler_test",
|
||||
"monitoring/stats_dump_scheduler_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"stats_history_test",
|
||||
"monitoring/stats_history_test.cc",
|
||||
@@ -1660,7 +1968,7 @@ ROCKS_TESTS = [
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS + extra_compiler_flags,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
|
||||
@@ -136,6 +136,15 @@ def generate_targets(repo_path, deps_map):
|
||||
"rocksdb_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"])
|
||||
# rocksdb_whole_archive_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_whole_archive_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"],
|
||||
deps=None,
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
link_whole=True)
|
||||
# rocksdb_test_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_test_lib",
|
||||
|
||||
@@ -39,7 +39,7 @@ class TARGETSBuilder(object):
|
||||
self.targets_file.close()
|
||||
|
||||
def add_library(self, name, srcs, deps=None, headers=None,
|
||||
extra_external_deps=""):
|
||||
extra_external_deps="", link_whole=False):
|
||||
headers_attr_prefix = ""
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
@@ -52,7 +52,8 @@ class TARGETSBuilder(object):
|
||||
headers_attr_prefix=headers_attr_prefix,
|
||||
headers=headers,
|
||||
deps=pretty_list(deps),
|
||||
extra_external_deps=extra_external_deps))
|
||||
extra_external_deps=extra_external_deps,
|
||||
link_whole=link_whole))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None):
|
||||
|
||||
@@ -134,6 +134,7 @@ cpp_library(
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [{deps}],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS{extra_external_deps},
|
||||
link_whole = {link_whole},
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -187,7 +188,7 @@ ROCKS_TESTS = [
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS + extra_compiler_flags,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
|
||||
@@ -60,8 +60,16 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_5xx" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007" ]; then
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_PLATFORM009" ]; then
|
||||
source "$PWD/build_tools/fbcode_config_platform009.sh"
|
||||
elif [ -z "$USE_CLANG" ]; then
|
||||
# Still use platform007 for gcc by default for build break on
|
||||
# some hosts.
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
else
|
||||
source "$PWD/build_tools/fbcode_config_platform009.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -657,11 +665,18 @@ if test "$TRY_SSE_ETC"; then
|
||||
# It doesn't even really check that your current CPU is compatible.
|
||||
#
|
||||
# SSE4.2 available since nehalem, ca. 2008-2010
|
||||
# Includes POPCNT for BitsSetToOne, BitParity
|
||||
TRY_SSE42="-msse4.2"
|
||||
# PCLMUL available since westmere, ca. 2010-2011
|
||||
TRY_PCLMUL="-mpclmul"
|
||||
# AVX2 available since haswell, ca. 2013-2015
|
||||
TRY_AVX2="-mavx2"
|
||||
# BMI available since haswell, ca. 2013-2015
|
||||
# Primarily for TZCNT for CountTrailingZeroBits
|
||||
TRY_BMI="-mbmi"
|
||||
# LZCNT available since haswell, ca. 2013-2015
|
||||
# For FloorLog2
|
||||
TRY_LZCNT="-mlzcnt"
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_SSE42 -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
@@ -710,6 +725,34 @@ elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use AVX2 intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_BMI -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argv;
|
||||
return (int)_tzcnt_u64((uint64_t)argc);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_BMI -DHAVE_BMI"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use BMI intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_LZCNT -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argv;
|
||||
return (int)_lzcnt_u64((uint64_t)argc);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_LZCNT -DHAVE_LZCNT"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use LZCNT intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
int main() {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/1795efe5f06778c15a92c8f9a2aba5dc496d9d4d/9.x/centos7-native/3bed279
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/7318eaac22659b6ff2fe43918e4b69fd0772a8a7/9.0.0/platform009/651ee30
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/4959b39cfbe5965a37c861c4c327fa7c5c759b87/9.x/platform009/9202ce7
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/45ce3375cdc77ecb2520bbf8f0ecddd3f98efd7a/2.30/platform009/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/be4de3205e029101b18aa8103daa696c2bef3b19/1.1.3/platform009/7f3b187
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/3c160ac5c67e257501e24c6c1d00ad5e01d73db6/1.2.8/platform009/7f3b187
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/73a237ac5bc0a5f5d67b39b8d253cfebaab88684/1.0.6/platform009/7f3b187
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ec6573523b0ce55ef6373a4801189027cf07bb2c/1.9.1/platform009/7f3b187
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/64c58a207d2495e83abc57a500a956df09b79a7c/1.4.x/platform009/ba86d1f
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/824d0a8a5abb5b121afd1b35fc3896407ea50092/2.2.0/platform009/7f3b187
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/d9aef9feb850b168a68736420f217b01cce11a89/master/platform009/c305944
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/0af65f71e23a67bf65dc91b11f95caa39325c432/2.0.11/platform009/7f3b187
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/02486dac347645d31dce116f44e1de3177315be2/1.4/platform009/5191652
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/2e0ec671e550bfca347300bf3f789d9c0fff24ad/2018_U5/platform009/7f3b187
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/70dbd9cfee63a25611417d09433a86d7711b3990/20200729/platform009/7f3b187
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/32b8a2407b634df3f8f948ba373fc4acc6a18296/fb/platform009/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/08634589372fa5f237bfd374e8c644a8364e78c1/2.32/platform009/ba86d1f/
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/6ae525939ad02e5e676855082fbbc7828dbafeac/3.15.0/platform009/7f3b187
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/162efd9561a3d21f6869f4814011e9cf1b3ff4dc/5.3.4/platform009/a6271c4
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform009.sh"
|
||||
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/9.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# location of LIBURING
|
||||
LIBURING_INCLUDE=" -isystem $LIBURING_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing.a"
|
||||
else
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DLIBURING"
|
||||
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE $LIBURING_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
AR="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
AR="$CLANG_BIN/llvm-ar"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/9.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/9.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42 -DROCKSDB_IOURING_PRESENT"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS $LIBURING_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform009/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform009/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS $LIBURING_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
@@ -103,31 +103,26 @@ function main() {
|
||||
gem_install fpm
|
||||
|
||||
make static_lib
|
||||
make install INSTALL_PATH=package
|
||||
|
||||
cd package
|
||||
|
||||
LIB_DIR=lib
|
||||
if [[ -z "$ARCH" ]]; then
|
||||
ARCH=$(getconf LONG_BIT)
|
||||
fi
|
||||
if [[ ("$FPM_OUTPUT" = "rpm") && ($ARCH -eq 64) ]]; then
|
||||
mv lib lib64
|
||||
LIB_DIR=lib64
|
||||
LIBDIR=/usr/lib
|
||||
if [[ $FPM_OUTPUT = "rpm" ]]; then
|
||||
LIBDIR=$(rpm --eval '%_libdir')
|
||||
fi
|
||||
|
||||
rm -rf package
|
||||
make install DESTDIR=package PREFIX=/usr LIBDIR=$LIBDIR
|
||||
|
||||
fpm \
|
||||
-s dir \
|
||||
-t $FPM_OUTPUT \
|
||||
-C package \
|
||||
-n rocksdb \
|
||||
-v $1 \
|
||||
--prefix /usr \
|
||||
--url http://rocksdb.org/ \
|
||||
-m rocksdb@fb.com \
|
||||
--license BSD \
|
||||
--vendor Facebook \
|
||||
--description "RocksDB is an embeddable persistent key-value store for fast storage." \
|
||||
include $LIB_DIR
|
||||
usr
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
|
||||
@@ -1101,6 +1101,7 @@ run_regression()
|
||||
strip librocksdb.a
|
||||
send_size_to_ods static_lib_stripped $(stat --printf="%s" librocksdb.a)
|
||||
|
||||
make clean
|
||||
make -j$(nproc) shared_lib
|
||||
send_size_to_ods shared_lib $(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
strip `readlink -f librocksdb.so`
|
||||
@@ -1113,6 +1114,7 @@ run_regression()
|
||||
strip librocksdb.a
|
||||
send_size_to_ods static_lib_lite_stripped $(stat --printf="%s" librocksdb.a)
|
||||
|
||||
make clean
|
||||
make LITE=1 -j$(nproc) shared_lib
|
||||
send_size_to_ods shared_lib_lite $(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
strip `readlink -f librocksdb.so`
|
||||
|
||||
Vendored
+19
-17
@@ -117,8 +117,8 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
|
||||
void Insert(std::shared_ptr<Cache> cache, int key, int value,
|
||||
int charge = 1) {
|
||||
cache->Insert(EncodeKey(key), EncodeValue(value), charge,
|
||||
&CacheTest::Deleter);
|
||||
EXPECT_OK(cache->Insert(EncodeKey(key), EncodeValue(value), charge,
|
||||
&CacheTest::Deleter));
|
||||
}
|
||||
|
||||
void Erase(std::shared_ptr<Cache> cache, int key) {
|
||||
@@ -167,9 +167,10 @@ TEST_P(CacheTest, UsageTest) {
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
std::string key(i, 'a');
|
||||
auto kv_size = key.size() + 5;
|
||||
cache->Insert(key, reinterpret_cast<void*>(value), kv_size, dumbDeleter);
|
||||
precise_cache->Insert(key, reinterpret_cast<void*>(value), kv_size,
|
||||
dumbDeleter);
|
||||
ASSERT_OK(cache->Insert(key, reinterpret_cast<void*>(value), kv_size,
|
||||
dumbDeleter));
|
||||
ASSERT_OK(precise_cache->Insert(key, reinterpret_cast<void*>(value),
|
||||
kv_size, dumbDeleter));
|
||||
usage += kv_size;
|
||||
ASSERT_EQ(usage, cache->GetUsage());
|
||||
ASSERT_LT(usage, precise_cache->GetUsage());
|
||||
@@ -183,10 +184,10 @@ TEST_P(CacheTest, UsageTest) {
|
||||
// make sure the cache will be overloaded
|
||||
for (uint64_t i = 1; i < kCapacity; ++i) {
|
||||
auto key = ToString(i);
|
||||
cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter);
|
||||
precise_cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter);
|
||||
ASSERT_OK(cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter));
|
||||
ASSERT_OK(precise_cache->Insert(key, reinterpret_cast<void*>(value),
|
||||
key.size() + 5, dumbDeleter));
|
||||
}
|
||||
|
||||
// the usage should be close to the capacity
|
||||
@@ -215,11 +216,12 @@ TEST_P(CacheTest, PinnedUsageTest) {
|
||||
auto kv_size = key.size() + 5;
|
||||
Cache::Handle* handle;
|
||||
Cache::Handle* handle_in_precise_cache;
|
||||
cache->Insert(key, reinterpret_cast<void*>(value), kv_size, dumbDeleter,
|
||||
&handle);
|
||||
ASSERT_OK(cache->Insert(key, reinterpret_cast<void*>(value), kv_size,
|
||||
dumbDeleter, &handle));
|
||||
assert(handle);
|
||||
precise_cache->Insert(key, reinterpret_cast<void*>(value), kv_size,
|
||||
dumbDeleter, &handle_in_precise_cache);
|
||||
ASSERT_OK(precise_cache->Insert(key, reinterpret_cast<void*>(value),
|
||||
kv_size, dumbDeleter,
|
||||
&handle_in_precise_cache));
|
||||
assert(handle_in_precise_cache);
|
||||
pinned_usage += kv_size;
|
||||
ASSERT_EQ(pinned_usage, cache->GetPinnedUsage());
|
||||
@@ -254,10 +256,10 @@ TEST_P(CacheTest, PinnedUsageTest) {
|
||||
// check that overloading the cache does not change the pinned usage
|
||||
for (uint64_t i = 1; i < 2 * kCapacity; ++i) {
|
||||
auto key = ToString(i);
|
||||
cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter);
|
||||
precise_cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter);
|
||||
ASSERT_OK(cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter));
|
||||
ASSERT_OK(precise_cache->Insert(key, reinterpret_cast<void*>(value),
|
||||
key.size() + 5, dumbDeleter));
|
||||
}
|
||||
ASSERT_EQ(pinned_usage, cache->GetPinnedUsage());
|
||||
ASSERT_EQ(precise_cache_pinned_usage, precise_cache->GetPinnedUsage());
|
||||
|
||||
Vendored
+3
-2
@@ -37,8 +37,9 @@ class LRUCacheTest : public testing::Test {
|
||||
|
||||
void Insert(const std::string& key,
|
||||
Cache::Priority priority = Cache::Priority::LOW) {
|
||||
cache_->Insert(key, 0 /*hash*/, nullptr /*value*/, 1 /*charge*/,
|
||||
nullptr /*deleter*/, nullptr /*handle*/, priority);
|
||||
EXPECT_OK(cache_->Insert(key, 0 /*hash*/, nullptr /*value*/, 1 /*charge*/,
|
||||
nullptr /*deleter*/, nullptr /*handle*/,
|
||||
priority));
|
||||
}
|
||||
|
||||
void Insert(char key, Cache::Priority priority = Cache::Priority::LOW) {
|
||||
|
||||
@@ -45,6 +45,7 @@ void ArenaWrappedDBIter::Init(Env* env, const ReadOptions& read_options,
|
||||
true, max_sequential_skip_in_iteration,
|
||||
read_callback, db_impl, cfd, allow_blob);
|
||||
sv_number_ = version_number;
|
||||
read_options_ = read_options;
|
||||
allow_refresh_ = allow_refresh;
|
||||
}
|
||||
|
||||
@@ -98,8 +99,7 @@ ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
db_impl, cfd, allow_blob, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(read_options, db_impl, cfd, read_callback,
|
||||
allow_blob);
|
||||
iter->StoreRefreshInfo(db_impl, cfd, read_callback, allow_blob);
|
||||
}
|
||||
|
||||
return iter;
|
||||
|
||||
@@ -41,6 +41,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
virtual ReadRangeDelAggregator* GetRangeDelAggregator() {
|
||||
return db_iter_->GetRangeDelAggregator();
|
||||
}
|
||||
const ReadOptions& GetReadOptions() { return read_options_; }
|
||||
|
||||
// Set the internal iterator wrapped inside the DB Iterator. Usually it is
|
||||
// a merging iterator.
|
||||
@@ -79,10 +80,8 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
|
||||
// Store some parameters so we can refresh the iterator at a later point
|
||||
// with these same params
|
||||
void StoreRefreshInfo(const ReadOptions& read_options, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, ReadCallback* read_callback,
|
||||
bool allow_blob) {
|
||||
read_options_ = read_options;
|
||||
void StoreRefreshInfo(DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
ReadCallback* read_callback, bool allow_blob) {
|
||||
db_impl_ = db_impl;
|
||||
cfd_ = cfd;
|
||||
read_callback_ = read_callback;
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
// 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_builder.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "db/blob/blob_file_addition.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/blob/blob_log_writer.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/read_write_util.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/compression.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobFileBuilder::BlobFileBuilder(
|
||||
VersionSet* versions, Env* env, FileSystem* fs,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const MutableCFOptions* mutable_cf_options, const FileOptions* file_options,
|
||||
int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: BlobFileBuilder([versions]() { return versions->NewFileNumber(); }, env,
|
||||
fs, immutable_cf_options, mutable_cf_options,
|
||||
file_options, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
blob_file_additions) {}
|
||||
|
||||
BlobFileBuilder::BlobFileBuilder(
|
||||
std::function<uint64_t()> file_number_generator, Env* env, FileSystem* fs,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const MutableCFOptions* mutable_cf_options, const FileOptions* file_options,
|
||||
int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: file_number_generator_(std::move(file_number_generator)),
|
||||
env_(env),
|
||||
fs_(fs),
|
||||
immutable_cf_options_(immutable_cf_options),
|
||||
min_blob_size_(mutable_cf_options->min_blob_size),
|
||||
blob_file_size_(mutable_cf_options->blob_file_size),
|
||||
blob_compression_type_(mutable_cf_options->blob_compression_type),
|
||||
file_options_(file_options),
|
||||
job_id_(job_id),
|
||||
column_family_id_(column_family_id),
|
||||
column_family_name_(column_family_name),
|
||||
io_priority_(io_priority),
|
||||
write_hint_(write_hint),
|
||||
blob_file_additions_(blob_file_additions),
|
||||
blob_count_(0),
|
||||
blob_bytes_(0) {
|
||||
assert(file_number_generator_);
|
||||
assert(env_);
|
||||
assert(fs_);
|
||||
assert(immutable_cf_options_);
|
||||
assert(file_options_);
|
||||
assert(blob_file_additions_);
|
||||
}
|
||||
|
||||
BlobFileBuilder::~BlobFileBuilder() = default;
|
||||
|
||||
Status BlobFileBuilder::Add(const Slice& key, const Slice& value,
|
||||
std::string* blob_index) {
|
||||
assert(blob_index);
|
||||
assert(blob_index->empty());
|
||||
|
||||
if (value.size() < min_blob_size_) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
{
|
||||
const Status s = OpenBlobFileIfNeeded();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
Slice blob = value;
|
||||
std::string compressed_blob;
|
||||
|
||||
{
|
||||
const Status s = CompressBlobIfNeeded(&blob, &compressed_blob);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t blob_file_number = 0;
|
||||
uint64_t blob_offset = 0;
|
||||
|
||||
{
|
||||
const Status s =
|
||||
WriteBlobToFile(key, blob, &blob_file_number, &blob_offset);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Status s = CloseBlobFileIfNeeded();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
BlobIndex::EncodeBlob(blob_index, blob_file_number, blob_offset, blob.size(),
|
||||
blob_compression_type_);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status BlobFileBuilder::Finish() {
|
||||
if (!IsBlobFileOpen()) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return CloseBlobFile();
|
||||
}
|
||||
|
||||
bool BlobFileBuilder::IsBlobFileOpen() const { return !!writer_; }
|
||||
|
||||
Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
if (IsBlobFileOpen()) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
assert(!blob_count_);
|
||||
assert(!blob_bytes_);
|
||||
|
||||
assert(file_number_generator_);
|
||||
const uint64_t blob_file_number = file_number_generator_();
|
||||
|
||||
assert(immutable_cf_options_);
|
||||
assert(!immutable_cf_options_->cf_paths.empty());
|
||||
const std::string blob_file_path = BlobFileName(
|
||||
immutable_cf_options_->cf_paths.front().path, blob_file_number);
|
||||
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
|
||||
{
|
||||
TEST_SYNC_POINT("BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile");
|
||||
|
||||
assert(file_options_);
|
||||
const Status s =
|
||||
NewWritableFile(fs_, blob_file_path, &file, *file_options_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
assert(file);
|
||||
file->SetIOPriority(io_priority_);
|
||||
file->SetWriteLifeTimeHint(write_hint_);
|
||||
|
||||
Statistics* const statistics = immutable_cf_options_->statistics;
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), blob_file_path, *file_options_, env_,
|
||||
nullptr /*IOTracer*/, statistics, immutable_cf_options_->listeners,
|
||||
immutable_cf_options_->file_checksum_gen_factory));
|
||||
|
||||
std::unique_ptr<BlobLogWriter> blob_log_writer(
|
||||
new BlobLogWriter(std::move(file_writer), env_, statistics,
|
||||
blob_file_number, immutable_cf_options_->use_fsync));
|
||||
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
|
||||
BlobLogHeader header(column_family_id_, blob_compression_type_, has_ttl,
|
||||
expiration_range);
|
||||
|
||||
{
|
||||
TEST_SYNC_POINT("BlobFileBuilder::OpenBlobFileIfNeeded:WriteHeader");
|
||||
|
||||
const Status s = blob_log_writer->WriteHeader(header);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
writer_ = std::move(blob_log_writer);
|
||||
|
||||
assert(IsBlobFileOpen());
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
Slice* blob, std::string* compressed_blob) const {
|
||||
assert(blob);
|
||||
assert(compressed_blob);
|
||||
assert(compressed_blob->empty());
|
||||
|
||||
if (blob_compression_type_ == kNoCompression) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(blob_compression_type_);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
blob_compression_type_, sample_for_compression);
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
if (!CompressData(*blob, info, compression_format_version, compressed_blob)) {
|
||||
return Status::Corruption("Error compressing blob");
|
||||
}
|
||||
|
||||
*blob = Slice(*compressed_blob);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status BlobFileBuilder::WriteBlobToFile(const Slice& key, const Slice& blob,
|
||||
uint64_t* blob_file_number,
|
||||
uint64_t* blob_offset) {
|
||||
assert(IsBlobFileOpen());
|
||||
assert(blob_file_number);
|
||||
assert(blob_offset);
|
||||
|
||||
uint64_t key_offset = 0;
|
||||
|
||||
TEST_SYNC_POINT("BlobFileBuilder::WriteBlobToFile:AddRecord");
|
||||
|
||||
const Status s = writer_->AddRecord(key, blob, &key_offset, blob_offset);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
*blob_file_number = writer_->get_log_number();
|
||||
|
||||
++blob_count_;
|
||||
blob_bytes_ += BlobLogRecord::kHeaderSize + key.size() + blob.size();
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status BlobFileBuilder::CloseBlobFile() {
|
||||
assert(IsBlobFileOpen());
|
||||
|
||||
BlobLogFooter footer;
|
||||
footer.blob_count = blob_count_;
|
||||
|
||||
std::string checksum_method;
|
||||
std::string checksum_value;
|
||||
|
||||
TEST_SYNC_POINT("BlobFileBuilder::WriteBlobToFile:AppendFooter");
|
||||
|
||||
const Status s =
|
||||
writer_->AppendFooter(footer, &checksum_method, &checksum_value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
const uint64_t blob_file_number = writer_->get_log_number();
|
||||
|
||||
assert(blob_file_additions_);
|
||||
blob_file_additions_->emplace_back(blob_file_number, blob_count_, blob_bytes_,
|
||||
std::move(checksum_method),
|
||||
std::move(checksum_value));
|
||||
|
||||
assert(immutable_cf_options_);
|
||||
ROCKS_LOG_INFO(immutable_cf_options_->info_log,
|
||||
"[%s] [JOB %d] Generated blob file #%" PRIu64 ": %" PRIu64
|
||||
" total blobs, %" PRIu64 " total bytes",
|
||||
column_family_name_.c_str(), job_id_, blob_file_number,
|
||||
blob_count_, blob_bytes_);
|
||||
|
||||
writer_.reset();
|
||||
blob_count_ = 0;
|
||||
blob_bytes_ = 0;
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status BlobFileBuilder::CloseBlobFileIfNeeded() {
|
||||
assert(IsBlobFileOpen());
|
||||
|
||||
const WritableFileWriter* const file_writer = writer_->file();
|
||||
assert(file_writer);
|
||||
|
||||
if (file_writer->GetFileSize() < blob_file_size_) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return CloseBlobFile();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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 <cinttypes>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/compression_type.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class VersionSet;
|
||||
class FileSystem;
|
||||
struct ImmutableCFOptions;
|
||||
struct MutableCFOptions;
|
||||
struct FileOptions;
|
||||
class BlobFileAddition;
|
||||
class Status;
|
||||
class Slice;
|
||||
class BlobLogWriter;
|
||||
|
||||
class BlobFileBuilder {
|
||||
public:
|
||||
BlobFileBuilder(VersionSet* versions, Env* env, FileSystem* fs,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const MutableCFOptions* mutable_cf_options,
|
||||
const FileOptions* file_options, int job_id,
|
||||
uint32_t column_family_id,
|
||||
const std::string& column_family_name,
|
||||
Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
std::vector<BlobFileAddition>* blob_file_additions);
|
||||
|
||||
BlobFileBuilder(std::function<uint64_t()> file_number_generator, Env* env,
|
||||
FileSystem* fs,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const MutableCFOptions* mutable_cf_options,
|
||||
const FileOptions* file_options, int job_id,
|
||||
uint32_t column_family_id,
|
||||
const std::string& column_family_name,
|
||||
Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
std::vector<BlobFileAddition>* blob_file_additions);
|
||||
|
||||
BlobFileBuilder(const BlobFileBuilder&) = delete;
|
||||
BlobFileBuilder& operator=(const BlobFileBuilder&) = delete;
|
||||
|
||||
~BlobFileBuilder();
|
||||
|
||||
Status Add(const Slice& key, const Slice& value, std::string* blob_index);
|
||||
Status Finish();
|
||||
|
||||
private:
|
||||
bool IsBlobFileOpen() const;
|
||||
Status OpenBlobFileIfNeeded();
|
||||
Status CompressBlobIfNeeded(Slice* blob, std::string* compressed_blob) const;
|
||||
Status WriteBlobToFile(const Slice& key, const Slice& blob,
|
||||
uint64_t* blob_file_number, uint64_t* blob_offset);
|
||||
Status CloseBlobFile();
|
||||
Status CloseBlobFileIfNeeded();
|
||||
|
||||
std::function<uint64_t()> file_number_generator_;
|
||||
Env* env_;
|
||||
FileSystem* fs_;
|
||||
const ImmutableCFOptions* immutable_cf_options_;
|
||||
uint64_t min_blob_size_;
|
||||
uint64_t blob_file_size_;
|
||||
CompressionType blob_compression_type_;
|
||||
const FileOptions* file_options_;
|
||||
int job_id_;
|
||||
uint32_t column_family_id_;
|
||||
std::string column_family_name_;
|
||||
Env::IOPriority io_priority_;
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
std::vector<BlobFileAddition>* blob_file_additions_;
|
||||
std::unique_ptr<BlobLogWriter> writer_;
|
||||
uint64_t blob_count_;
|
||||
uint64_t blob_bytes_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,594 @@
|
||||
// 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_builder.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_file_addition.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/blob/blob_log_reader.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_checksum.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/compression.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class TestFileNumberGenerator {
|
||||
public:
|
||||
uint64_t operator()() { return ++next_file_number_; }
|
||||
|
||||
private:
|
||||
uint64_t next_file_number_ = 1;
|
||||
};
|
||||
|
||||
class BlobFileBuilderTest : public testing::Test {
|
||||
protected:
|
||||
BlobFileBuilderTest() : mock_env_(Env::Default()), fs_(&mock_env_) {}
|
||||
|
||||
void VerifyBlobFile(const ImmutableCFOptions& immutable_cf_options,
|
||||
uint64_t blob_file_number, uint32_t column_family_id,
|
||||
CompressionType blob_compression_type,
|
||||
const std::vector<std::pair<std::string, std::string>>&
|
||||
expected_key_value_pairs,
|
||||
const std::vector<std::string>& blob_indexes) {
|
||||
assert(expected_key_value_pairs.size() == blob_indexes.size());
|
||||
|
||||
const std::string blob_file_path = BlobFileName(
|
||||
immutable_cf_options.cf_paths.front().path, blob_file_number);
|
||||
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
ASSERT_OK(
|
||||
fs_.NewRandomAccessFile(blob_file_path, file_options_, &file, dbg));
|
||||
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), blob_file_path,
|
||||
&mock_env_));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
BlobLogReader blob_log_reader(std::move(file_reader), &mock_env_,
|
||||
statistics);
|
||||
|
||||
BlobLogHeader header;
|
||||
ASSERT_OK(blob_log_reader.ReadHeader(&header));
|
||||
ASSERT_EQ(header.version, kVersion1);
|
||||
ASSERT_EQ(header.column_family_id, column_family_id);
|
||||
ASSERT_EQ(header.compression, blob_compression_type);
|
||||
ASSERT_FALSE(header.has_ttl);
|
||||
ASSERT_EQ(header.expiration_range, ExpirationRange());
|
||||
|
||||
for (size_t i = 0; i < expected_key_value_pairs.size(); ++i) {
|
||||
BlobLogRecord record;
|
||||
uint64_t blob_offset = 0;
|
||||
|
||||
ASSERT_OK(blob_log_reader.ReadRecord(
|
||||
&record, BlobLogReader::kReadHeaderKeyBlob, &blob_offset));
|
||||
|
||||
// Check the contents of the blob file
|
||||
const auto& expected_key_value = expected_key_value_pairs[i];
|
||||
const auto& key = expected_key_value.first;
|
||||
const auto& value = expected_key_value.second;
|
||||
|
||||
ASSERT_EQ(record.key_size, key.size());
|
||||
ASSERT_EQ(record.value_size, value.size());
|
||||
ASSERT_EQ(record.expiration, 0);
|
||||
ASSERT_EQ(record.key, key);
|
||||
ASSERT_EQ(record.value, value);
|
||||
|
||||
// Make sure the blob reference returned by the builder points to the
|
||||
// right place
|
||||
BlobIndex blob_index;
|
||||
ASSERT_OK(blob_index.DecodeFrom(blob_indexes[i]));
|
||||
ASSERT_FALSE(blob_index.IsInlined());
|
||||
ASSERT_FALSE(blob_index.HasTTL());
|
||||
ASSERT_EQ(blob_index.file_number(), blob_file_number);
|
||||
ASSERT_EQ(blob_index.offset(), blob_offset);
|
||||
ASSERT_EQ(blob_index.size(), value.size());
|
||||
}
|
||||
|
||||
BlobLogFooter footer;
|
||||
ASSERT_OK(blob_log_reader.ReadFooter(&footer));
|
||||
ASSERT_EQ(footer.blob_count, expected_key_value_pairs.size());
|
||||
ASSERT_EQ(footer.expiration_range, ExpirationRange());
|
||||
}
|
||||
|
||||
MockEnv mock_env_;
|
||||
LegacyFileSystemWrapper fs_;
|
||||
FileOptions file_options_;
|
||||
};
|
||||
|
||||
TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
// Build a single blob file
|
||||
constexpr size_t number_of_blobs = 10;
|
||||
constexpr size_t key_size = 1;
|
||||
constexpr size_t value_size = 4;
|
||||
constexpr size_t value_offset = 1234;
|
||||
|
||||
Options options;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_,
|
||||
"BlobFileBuilderTest_BuildAndCheckOneFile"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, &fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, &blob_file_additions);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
|
||||
number_of_blobs);
|
||||
std::vector<std::string> blob_indexes(number_of_blobs);
|
||||
|
||||
for (size_t i = 0; i < number_of_blobs; ++i) {
|
||||
auto& expected_key_value = expected_key_value_pairs[i];
|
||||
|
||||
auto& key = expected_key_value.first;
|
||||
key = std::to_string(i);
|
||||
assert(key.size() == key_size);
|
||||
|
||||
auto& value = expected_key_value.second;
|
||||
value = std::to_string(i + value_offset);
|
||||
assert(value.size() == value_size);
|
||||
|
||||
auto& blob_index = blob_indexes[i];
|
||||
|
||||
ASSERT_OK(builder.Add(key, value, &blob_index));
|
||||
ASSERT_FALSE(blob_index.empty());
|
||||
}
|
||||
|
||||
ASSERT_OK(builder.Finish());
|
||||
|
||||
// Check the metadata generated
|
||||
ASSERT_EQ(blob_file_additions.size(), 1);
|
||||
|
||||
const auto& blob_file_addition = blob_file_additions[0];
|
||||
|
||||
constexpr uint64_t blob_file_number = 2;
|
||||
|
||||
ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), number_of_blobs);
|
||||
ASSERT_EQ(
|
||||
blob_file_addition.GetTotalBlobBytes(),
|
||||
number_of_blobs * (BlobLogRecord::kHeaderSize + key_size + value_size));
|
||||
|
||||
// Verify the contents of the new blob file as well as the blob references
|
||||
VerifyBlobFile(immutable_cf_options, blob_file_number, column_family_id,
|
||||
kNoCompression, expected_key_value_pairs, blob_indexes);
|
||||
}
|
||||
|
||||
TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
// Build multiple blob files: file size limit is set to the size of a single
|
||||
// value, so each blob ends up in a file of its own
|
||||
constexpr size_t number_of_blobs = 10;
|
||||
constexpr size_t key_size = 1;
|
||||
constexpr size_t value_size = 10;
|
||||
constexpr size_t value_offset = 1234567890;
|
||||
|
||||
Options options;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_,
|
||||
"BlobFileBuilderTest_BuildAndCheckMultipleFiles"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = value_size;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, &fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, &blob_file_additions);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
|
||||
number_of_blobs);
|
||||
std::vector<std::string> blob_indexes(number_of_blobs);
|
||||
|
||||
for (size_t i = 0; i < number_of_blobs; ++i) {
|
||||
auto& expected_key_value = expected_key_value_pairs[i];
|
||||
|
||||
auto& key = expected_key_value.first;
|
||||
key = std::to_string(i);
|
||||
assert(key.size() == key_size);
|
||||
|
||||
auto& value = expected_key_value.second;
|
||||
value = std::to_string(i + value_offset);
|
||||
assert(value.size() == value_size);
|
||||
|
||||
auto& blob_index = blob_indexes[i];
|
||||
|
||||
ASSERT_OK(builder.Add(key, value, &blob_index));
|
||||
ASSERT_FALSE(blob_index.empty());
|
||||
}
|
||||
|
||||
ASSERT_OK(builder.Finish());
|
||||
|
||||
// Check the metadata generated
|
||||
ASSERT_EQ(blob_file_additions.size(), number_of_blobs);
|
||||
|
||||
for (size_t i = 0; i < number_of_blobs; ++i) {
|
||||
const auto& blob_file_addition = blob_file_additions[i];
|
||||
|
||||
ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), i + 2);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 1);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(),
|
||||
BlobLogRecord::kHeaderSize + key_size + value_size);
|
||||
}
|
||||
|
||||
// Verify the contents of the new blob files as well as the blob references
|
||||
for (size_t i = 0; i < number_of_blobs; ++i) {
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pair{
|
||||
expected_key_value_pairs[i]};
|
||||
std::vector<std::string> blob_index{blob_indexes[i]};
|
||||
|
||||
VerifyBlobFile(immutable_cf_options, i + 2, column_family_id,
|
||||
kNoCompression, expected_key_value_pair, blob_index);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BlobFileBuilderTest, InlinedValues) {
|
||||
// All values are below the min_blob_size threshold; no blob files get written
|
||||
constexpr size_t number_of_blobs = 10;
|
||||
constexpr size_t key_size = 1;
|
||||
constexpr size_t value_size = 10;
|
||||
constexpr size_t value_offset = 1234567890;
|
||||
|
||||
Options options;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileBuilderTest_InlinedValues"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 1024;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, &fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, &blob_file_additions);
|
||||
|
||||
for (size_t i = 0; i < number_of_blobs; ++i) {
|
||||
const std::string key = std::to_string(i);
|
||||
assert(key.size() == key_size);
|
||||
|
||||
const std::string value = std::to_string(i + value_offset);
|
||||
assert(value.size() == value_size);
|
||||
|
||||
std::string blob_index;
|
||||
ASSERT_OK(builder.Add(key, value, &blob_index));
|
||||
ASSERT_TRUE(blob_index.empty());
|
||||
}
|
||||
|
||||
ASSERT_OK(builder.Finish());
|
||||
|
||||
// Check the metadata generated
|
||||
ASSERT_TRUE(blob_file_additions.empty());
|
||||
}
|
||||
|
||||
TEST_F(BlobFileBuilderTest, Compression) {
|
||||
// Build a blob file with a compressed blob
|
||||
if (!Snappy_Supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr size_t key_size = 1;
|
||||
constexpr size_t value_size = 100;
|
||||
|
||||
Options options;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileBuilderTest_Compression"), 0);
|
||||
options.enable_blob_files = true;
|
||||
options.blob_compression_type = kSnappyCompression;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, &fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, &blob_file_additions);
|
||||
|
||||
const std::string key("1");
|
||||
const std::string uncompressed_value(value_size, 'x');
|
||||
|
||||
std::string blob_index;
|
||||
|
||||
ASSERT_OK(builder.Add(key, uncompressed_value, &blob_index));
|
||||
ASSERT_FALSE(blob_index.empty());
|
||||
|
||||
ASSERT_OK(builder.Finish());
|
||||
|
||||
// Check the metadata generated
|
||||
ASSERT_EQ(blob_file_additions.size(), 1);
|
||||
|
||||
const auto& blob_file_addition = blob_file_additions[0];
|
||||
|
||||
constexpr uint64_t blob_file_number = 2;
|
||||
|
||||
ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 1);
|
||||
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(kSnappyCompression);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
kSnappyCompression, sample_for_compression);
|
||||
|
||||
std::string compressed_value;
|
||||
ASSERT_TRUE(Snappy_Compress(info, uncompressed_value.data(),
|
||||
uncompressed_value.size(), &compressed_value));
|
||||
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(),
|
||||
BlobLogRecord::kHeaderSize + key_size + compressed_value.size());
|
||||
|
||||
// Verify the contents of the new blob file as well as the blob reference
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs{
|
||||
{key, compressed_value}};
|
||||
std::vector<std::string> blob_indexes{blob_index};
|
||||
|
||||
VerifyBlobFile(immutable_cf_options, blob_file_number, column_family_id,
|
||||
kSnappyCompression, expected_key_value_pairs, blob_indexes);
|
||||
}
|
||||
|
||||
TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
// Simulate an error during compression
|
||||
if (!Snappy_Supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Options options;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileBuilderTest_CompressionError"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.blob_compression_type = kSnappyCompression;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, &fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, &blob_file_additions);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack("CompressData:TamperWithReturnValue",
|
||||
[](void* arg) {
|
||||
bool* ret = static_cast<bool*>(arg);
|
||||
*ret = false;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr char key[] = "1";
|
||||
constexpr char value[] = "deadbeef";
|
||||
|
||||
std::string blob_index;
|
||||
|
||||
ASSERT_TRUE(builder.Add(key, value, &blob_index).IsCorruption());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
// Build a blob file with checksum
|
||||
|
||||
class DummyFileChecksumGenerator : public FileChecksumGenerator {
|
||||
public:
|
||||
void Update(const char* /* data */, size_t /* n */) override {}
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
std::string GetChecksum() const override { return std::string("dummy"); }
|
||||
|
||||
const char* Name() const override { return "DummyFileChecksum"; }
|
||||
};
|
||||
|
||||
class DummyFileChecksumGenFactory : public FileChecksumGenFactory {
|
||||
public:
|
||||
std::unique_ptr<FileChecksumGenerator> CreateFileChecksumGenerator(
|
||||
const FileChecksumGenContext& /* context */) override {
|
||||
return std::unique_ptr<FileChecksumGenerator>(
|
||||
new DummyFileChecksumGenerator);
|
||||
}
|
||||
|
||||
const char* Name() const override { return "DummyFileChecksumGenFactory"; }
|
||||
};
|
||||
|
||||
Options options;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileBuilderTest_Checksum"), 0);
|
||||
options.enable_blob_files = true;
|
||||
options.file_checksum_gen_factory =
|
||||
std::make_shared<DummyFileChecksumGenFactory>();
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, &fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, &blob_file_additions);
|
||||
|
||||
const std::string key("1");
|
||||
const std::string value("deadbeef");
|
||||
|
||||
std::string blob_index;
|
||||
|
||||
ASSERT_OK(builder.Add(key, value, &blob_index));
|
||||
ASSERT_FALSE(blob_index.empty());
|
||||
|
||||
ASSERT_OK(builder.Finish());
|
||||
|
||||
// Check the metadata generated
|
||||
ASSERT_EQ(blob_file_additions.size(), 1);
|
||||
|
||||
const auto& blob_file_addition = blob_file_additions[0];
|
||||
|
||||
constexpr uint64_t blob_file_number = 2;
|
||||
|
||||
ASSERT_EQ(blob_file_addition.GetBlobFileNumber(), blob_file_number);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobCount(), 1);
|
||||
ASSERT_EQ(blob_file_addition.GetTotalBlobBytes(),
|
||||
BlobLogRecord::kHeaderSize + key.size() + value.size());
|
||||
ASSERT_EQ(blob_file_addition.GetChecksumMethod(), "DummyFileChecksum");
|
||||
ASSERT_EQ(blob_file_addition.GetChecksumValue(), "dummy");
|
||||
|
||||
// Verify the contents of the new blob file as well as the blob reference
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs{
|
||||
{key, value}};
|
||||
std::vector<std::string> blob_indexes{blob_index};
|
||||
|
||||
VerifyBlobFile(immutable_cf_options, blob_file_number, column_family_id,
|
||||
kNoCompression, expected_key_value_pairs, blob_indexes);
|
||||
}
|
||||
|
||||
class BlobFileBuilderIOErrorTest
|
||||
: public testing::Test,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
protected:
|
||||
BlobFileBuilderIOErrorTest()
|
||||
: mock_env_(Env::Default()),
|
||||
fault_injection_env_(&mock_env_),
|
||||
fs_(&fault_injection_env_),
|
||||
sync_point_(GetParam()) {}
|
||||
|
||||
MockEnv mock_env_;
|
||||
FaultInjectionTestEnv fault_injection_env_;
|
||||
LegacyFileSystemWrapper fs_;
|
||||
FileOptions file_options_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
BlobFileBuilderTest, BlobFileBuilderIOErrorTest,
|
||||
::testing::ValuesIn(std::vector<std::string>{
|
||||
"BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile",
|
||||
"BlobFileBuilder::OpenBlobFileIfNeeded:WriteHeader",
|
||||
"BlobFileBuilder::WriteBlobToFile:AddRecord",
|
||||
"BlobFileBuilder::WriteBlobToFile:AppendFooter"}));
|
||||
|
||||
TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
// Simulate an I/O error during the specified step of Add()
|
||||
// Note: blob_file_size will be set to value_size in order for the first blob
|
||||
// to trigger close
|
||||
constexpr size_t value_size = 8;
|
||||
|
||||
Options options;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&fault_injection_env_,
|
||||
"BlobFileBuilderIOErrorTest_IOError"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = value_size;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
constexpr uint32_t column_family_id = 123;
|
||||
constexpr char column_family_name[] = "foobar";
|
||||
constexpr Env::IOPriority io_priority = Env::IO_HIGH;
|
||||
constexpr Env::WriteLifeTimeHint write_hint = Env::WLTH_MEDIUM;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), &fault_injection_env_,
|
||||
&fs_, &immutable_cf_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
&blob_file_additions);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr char key[] = "1";
|
||||
constexpr char value[] = "deadbeef";
|
||||
|
||||
std::string blob_index;
|
||||
|
||||
ASSERT_TRUE(builder.Add(key, value, &blob_index).IsIOError());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@@ -176,4 +175,3 @@ class BlobIndex {
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -3,7 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
|
||||
@@ -144,4 +143,3 @@ Status BlobLogRecord::CheckBlobCRC() const {
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -126,4 +124,3 @@ struct BlobLogRecord {
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -3,7 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/blob/blob_log_reader.h"
|
||||
|
||||
@@ -25,6 +24,8 @@ BlobLogReader::BlobLogReader(
|
||||
next_byte_(0) {}
|
||||
|
||||
Status BlobLogReader::ReadSlice(uint64_t size, Slice* slice, char* buf) {
|
||||
assert(file_);
|
||||
|
||||
StopWatch read_sw(env_, statistics_, BLOB_DB_BLOB_FILE_READ_MICROS);
|
||||
Status s = file_->Read(IOOptions(), next_byte_, static_cast<size_t>(size),
|
||||
slice, buf, nullptr);
|
||||
@@ -40,8 +41,11 @@ Status BlobLogReader::ReadSlice(uint64_t size, Slice* slice, char* buf) {
|
||||
}
|
||||
|
||||
Status BlobLogReader::ReadHeader(BlobLogHeader* header) {
|
||||
assert(file_.get() != nullptr);
|
||||
assert(next_byte_ == 0);
|
||||
|
||||
static_assert(BlobLogHeader::kSize <= sizeof(header_buf_),
|
||||
"Buffer is smaller than BlobLogHeader::kSize");
|
||||
|
||||
Status s = ReadSlice(BlobLogHeader::kSize, &buffer_, header_buf_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -56,6 +60,9 @@ Status BlobLogReader::ReadHeader(BlobLogHeader* header) {
|
||||
|
||||
Status BlobLogReader::ReadRecord(BlobLogRecord* record, ReadLevel level,
|
||||
uint64_t* blob_offset) {
|
||||
static_assert(BlobLogRecord::kHeaderSize <= sizeof(header_buf_),
|
||||
"Buffer is smaller than BlobLogRecord::kHeaderSize");
|
||||
|
||||
Status s = ReadSlice(BlobLogRecord::kHeaderSize, &buffer_, header_buf_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -101,5 +108,20 @@ Status BlobLogReader::ReadRecord(BlobLogRecord* record, ReadLevel level,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobLogReader::ReadFooter(BlobLogFooter* footer) {
|
||||
static_assert(BlobLogFooter::kSize <= sizeof(header_buf_),
|
||||
"Buffer is smaller than BlobLogFooter::kSize");
|
||||
|
||||
Status s = ReadSlice(BlobLogFooter::kSize, &buffer_, header_buf_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (buffer_.size() != BlobLogFooter::kSize) {
|
||||
return Status::Corruption("EOF reached before file footer");
|
||||
}
|
||||
|
||||
return footer->DecodeFrom(buffer_);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -5,8 +5,6 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -58,6 +56,8 @@ class BlobLogReader {
|
||||
Status ReadRecord(BlobLogRecord* record, ReadLevel level = kReadHeader,
|
||||
uint64_t* blob_offset = nullptr);
|
||||
|
||||
Status ReadFooter(BlobLogFooter* footer);
|
||||
|
||||
void ResetNextByte() { next_byte_ = 0; }
|
||||
|
||||
uint64_t GetNextByte() const { return next_byte_; }
|
||||
@@ -77,4 +77,3 @@ class BlobLogReader {
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -2,7 +2,6 @@
|
||||
// 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).
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/blob/blob_log_writer.h"
|
||||
|
||||
@@ -30,6 +29,8 @@ BlobLogWriter::BlobLogWriter(std::unique_ptr<WritableFileWriter>&& dest,
|
||||
use_fsync_(use_fs),
|
||||
last_elem_type_(kEtNone) {}
|
||||
|
||||
BlobLogWriter::~BlobLogWriter() = default;
|
||||
|
||||
Status BlobLogWriter::Sync() {
|
||||
TEST_SYNC_POINT("BlobLogWriter::Sync");
|
||||
|
||||
@@ -56,7 +57,9 @@ Status BlobLogWriter::WriteHeader(BlobLogHeader& header) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobLogWriter::AppendFooter(BlobLogFooter& footer) {
|
||||
Status BlobLogWriter::AppendFooter(BlobLogFooter& footer,
|
||||
std::string* checksum_method,
|
||||
std::string* checksum_value) {
|
||||
assert(block_offset_ != 0);
|
||||
assert(last_elem_type_ == kEtFileHdr || last_elem_type_ == kEtRecord);
|
||||
|
||||
@@ -66,10 +69,34 @@ Status BlobLogWriter::AppendFooter(BlobLogFooter& footer) {
|
||||
Status s = dest_->Append(Slice(str));
|
||||
if (s.ok()) {
|
||||
block_offset_ += str.size();
|
||||
|
||||
s = Sync();
|
||||
|
||||
if (s.ok()) {
|
||||
s = dest_->Close();
|
||||
|
||||
if (s.ok()) {
|
||||
assert(!!checksum_method == !!checksum_value);
|
||||
|
||||
if (checksum_method) {
|
||||
assert(checksum_method->empty());
|
||||
|
||||
std::string method = dest_->GetFileChecksumFuncName();
|
||||
if (method != kUnknownFileChecksumFuncName) {
|
||||
*checksum_method = std::move(method);
|
||||
}
|
||||
}
|
||||
if (checksum_value) {
|
||||
assert(checksum_value->empty());
|
||||
|
||||
std::string value = dest_->GetFileChecksum();
|
||||
if (value != kUnknownFileChecksum) {
|
||||
*checksum_value = std::move(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dest_.reset();
|
||||
}
|
||||
|
||||
@@ -139,4 +166,3 @@ Status BlobLogWriter::EmitPhysicalRecord(const std::string& headerbuf,
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -4,8 +4,6 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -41,7 +39,7 @@ class BlobLogWriter {
|
||||
BlobLogWriter(const BlobLogWriter&) = delete;
|
||||
BlobLogWriter& operator=(const BlobLogWriter&) = delete;
|
||||
|
||||
~BlobLogWriter() = default;
|
||||
~BlobLogWriter();
|
||||
|
||||
static void ConstructBlobHeader(std::string* buf, const Slice& key,
|
||||
const Slice& val, uint64_t expiration);
|
||||
@@ -56,7 +54,8 @@ class BlobLogWriter {
|
||||
const Slice& val, uint64_t* key_offset,
|
||||
uint64_t* blob_offset);
|
||||
|
||||
Status AppendFooter(BlobLogFooter& footer);
|
||||
Status AppendFooter(BlobLogFooter& footer, std::string* checksum_method,
|
||||
std::string* checksum_value);
|
||||
|
||||
Status WriteHeader(BlobLogHeader& header);
|
||||
|
||||
@@ -82,4 +81,3 @@ class BlobLogWriter {
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -41,7 +41,8 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
Tier::kImmutableMemtables,
|
||||
Tier::kL0SstFile, Tier::kLnSstFile};
|
||||
|
||||
DBBlobIndexTest() : DBTestBase("/db_blob_index_test") {}
|
||||
DBBlobIndexTest()
|
||||
: DBTestBase("/db_blob_index_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
ColumnFamilyHandle* cfh() { return dbfull()->DefaultColumnFamily(); }
|
||||
|
||||
|
||||
+18
-7
@@ -83,7 +83,8 @@ Status BuildTable(
|
||||
uint64_t sample_for_compression, const CompressionOptions& compression_opts,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
TableFileCreationReason reason, IOStatus* io_status,
|
||||
EventLogger* event_logger, int job_id, const Env::IOPriority io_priority,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, 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,
|
||||
@@ -106,6 +107,8 @@ Status BuildTable(
|
||||
|
||||
std::string fname = TableFileName(ioptions.cf_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
std::string file_checksum = kUnknownFileChecksum;
|
||||
std::string file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
#ifndef ROCKSDB_LITE
|
||||
EventHelpers::NotifyTableFileCreationStarted(
|
||||
ioptions.listeners, dbname, column_family_name, fname, job_id, reason);
|
||||
@@ -133,15 +136,17 @@ Status BuildTable(
|
||||
if (!s.ok()) {
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger, ioptions.listeners, dbname, column_family_name, fname,
|
||||
job_id, meta->fd, kInvalidBlobFileNumber, tp, reason, s);
|
||||
job_id, meta->fd, kInvalidBlobFileNumber, tp, reason, s,
|
||||
file_checksum, file_checksum_func_name);
|
||||
return s;
|
||||
}
|
||||
file->SetIOPriority(io_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
|
||||
file_writer.reset(new WritableFileWriter(
|
||||
std::move(file), fname, file_options, env, ioptions.statistics,
|
||||
ioptions.listeners, ioptions.file_checksum_gen_factory));
|
||||
std::move(file), fname, file_options, env, io_tracer,
|
||||
ioptions.statistics, ioptions.listeners,
|
||||
ioptions.file_checksum_gen_factory));
|
||||
|
||||
builder = NewTableBuilder(
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
@@ -221,10 +226,12 @@ Status BuildTable(
|
||||
delete builder;
|
||||
|
||||
// Finish and check for file errors
|
||||
TEST_SYNC_POINT("BuildTable:BeforeSyncTable");
|
||||
if (s.ok() && !empty) {
|
||||
StopWatch sw(env, ioptions.statistics, TABLE_SYNC_MICROS);
|
||||
*io_status = file_writer->Sync(ioptions.use_fsync);
|
||||
}
|
||||
TEST_SYNC_POINT("BuildTable:BeforeCloseTableFile");
|
||||
if (s.ok() && io_status->ok() && !empty) {
|
||||
*io_status = file_writer->Close();
|
||||
}
|
||||
@@ -232,6 +239,8 @@ Status BuildTable(
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = file_writer->GetFileChecksum();
|
||||
meta->file_checksum_func_name = file_writer->GetFileChecksumFuncName();
|
||||
file_checksum = meta->file_checksum;
|
||||
file_checksum_func_name = meta->file_checksum_func_name;
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -247,8 +256,9 @@ Status BuildTable(
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// we will regrad this verification as user reads since the goal is
|
||||
// to cache it here for further user reads
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
ReadOptions(), file_options, internal_comparator, *meta,
|
||||
read_options, file_options, internal_comparator, *meta,
|
||||
nullptr /* range_del_agg */,
|
||||
mutable_cf_options.prefix_extractor.get(), nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
@@ -270,7 +280,7 @@ Status BuildTable(
|
||||
}
|
||||
s = it->status();
|
||||
if (s.ok() && check_hash != paranoid_hash) {
|
||||
s = Status::Corruption("Paraniod checksums do not match");
|
||||
s = Status::Corruption("Paranoid checksums do not match");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,7 +301,8 @@ Status BuildTable(
|
||||
// Output to event logger and fire events.
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger, ioptions.listeners, dbname, column_family_name, fname,
|
||||
job_id, meta->fd, meta->oldest_blob_file_number, tp, reason, s);
|
||||
job_id, meta->fd, meta->oldest_blob_file_number, tp, reason, s,
|
||||
file_checksum, file_checksum_func_name);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
+2
-1
@@ -79,7 +79,8 @@ extern Status BuildTable(
|
||||
const uint64_t sample_for_compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
IOStatus* io_status, EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
IOStatus* io_status, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
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,
|
||||
|
||||
@@ -707,6 +707,41 @@ rocksdb_t* rocksdb_open_column_families(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_column_families_with_ttl(
|
||||
const rocksdb_options_t* db_options, const char* name,
|
||||
int num_column_families, const char* const* column_family_names,
|
||||
const rocksdb_options_t* const* column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles, const int* ttls,
|
||||
char** errptr) {
|
||||
std::vector<int32_t> ttls_vec;
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
ttls_vec.push_back(ttls[i]);
|
||||
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::DBWithTTL* db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
if (SaveError(errptr, ROCKSDB_NAMESPACE::DBWithTTL::Open(
|
||||
DBOptions(db_options->rep), std::string(name),
|
||||
column_families, &handles, &db, ttls_vec))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < handles.size(); i++) {
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
const rocksdb_options_t* db_options, const char* name,
|
||||
int num_column_families, const char* const* column_family_names,
|
||||
@@ -805,6 +840,18 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family(
|
||||
return handle;
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_create_column_family_with_ttl(
|
||||
rocksdb_t* db, const rocksdb_options_t* column_family_options,
|
||||
const char* column_family_name, int ttl, char** errptr) {
|
||||
ROCKSDB_NAMESPACE::DBWithTTL* db_with_ttl =
|
||||
static_cast<ROCKSDB_NAMESPACE::DBWithTTL*>(db->rep);
|
||||
rocksdb_column_family_handle_t* handle = new rocksdb_column_family_handle_t;
|
||||
SaveError(errptr, db_with_ttl->CreateColumnFamilyWithTtl(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep), ttl));
|
||||
return handle;
|
||||
}
|
||||
|
||||
void rocksdb_drop_column_family(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* handle,
|
||||
@@ -3612,11 +3659,20 @@ void rocksdb_readoptions_set_verify_checksums(
|
||||
opt->rep.verify_checksums = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_verify_checksums(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.verify_checksums;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_fill_cache(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.fill_cache = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_fill_cache(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.fill_cache;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_snapshot(
|
||||
rocksdb_readoptions_t* opt,
|
||||
const rocksdb_snapshot_t* snap) {
|
||||
@@ -3653,11 +3709,19 @@ void rocksdb_readoptions_set_read_tier(
|
||||
opt->rep.read_tier = static_cast<ROCKSDB_NAMESPACE::ReadTier>(v);
|
||||
}
|
||||
|
||||
int rocksdb_readoptions_get_read_tier(rocksdb_readoptions_t* opt) {
|
||||
return static_cast<int>(opt->rep.read_tier);
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_tailing(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.tailing = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_tailing(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.tailing;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_managed(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.managed = v;
|
||||
@@ -3668,37 +3732,70 @@ void rocksdb_readoptions_set_readahead_size(
|
||||
opt->rep.readahead_size = v;
|
||||
}
|
||||
|
||||
size_t rocksdb_readoptions_get_readahead_size(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.readahead_size;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_prefix_same_as_start(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.prefix_same_as_start = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_prefix_same_as_start(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.prefix_same_as_start;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_pin_data(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.pin_data = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_pin_data(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.pin_data;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_total_order_seek(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.total_order_seek = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_total_order_seek(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.total_order_seek;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_max_skippable_internal_keys(
|
||||
rocksdb_readoptions_t* opt,
|
||||
uint64_t v) {
|
||||
opt->rep.max_skippable_internal_keys = v;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_readoptions_get_max_skippable_internal_keys(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.max_skippable_internal_keys;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_background_purge_on_iterator_cleanup(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.background_purge_on_iterator_cleanup = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_background_purge_on_iterator_cleanup(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.background_purge_on_iterator_cleanup;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_ignore_range_deletions(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.ignore_range_deletions = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_readoptions_get_ignore_range_deletions(
|
||||
rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.ignore_range_deletions;
|
||||
}
|
||||
|
||||
rocksdb_writeoptions_t* rocksdb_writeoptions_create() {
|
||||
return new rocksdb_writeoptions_t;
|
||||
}
|
||||
@@ -3712,33 +3809,61 @@ void rocksdb_writeoptions_set_sync(
|
||||
opt->rep.sync = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_writeoptions_get_sync(rocksdb_writeoptions_t* opt) {
|
||||
return opt->rep.sync;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_disable_WAL(rocksdb_writeoptions_t* opt, int disable) {
|
||||
opt->rep.disableWAL = disable;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_writeoptions_get_disable_WAL(
|
||||
rocksdb_writeoptions_t* opt) {
|
||||
return opt->rep.disableWAL;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_set_ignore_missing_column_families(
|
||||
rocksdb_writeoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.ignore_missing_column_families = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_writeoptions_get_ignore_missing_column_families(
|
||||
rocksdb_writeoptions_t* opt) {
|
||||
return opt->rep.ignore_missing_column_families;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_set_no_slowdown(
|
||||
rocksdb_writeoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.no_slowdown = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_writeoptions_get_no_slowdown(
|
||||
rocksdb_writeoptions_t* opt) {
|
||||
return opt->rep.no_slowdown;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_set_low_pri(
|
||||
rocksdb_writeoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.low_pri = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_writeoptions_get_low_pri(rocksdb_writeoptions_t* opt) {
|
||||
return opt->rep.low_pri;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_set_memtable_insert_hint_per_batch(
|
||||
rocksdb_writeoptions_t* opt, unsigned char v) {
|
||||
opt->rep.memtable_insert_hint_per_batch = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_writeoptions_get_memtable_insert_hint_per_batch(
|
||||
rocksdb_writeoptions_t* opt) {
|
||||
return opt->rep.memtable_insert_hint_per_batch;
|
||||
}
|
||||
|
||||
rocksdb_compactoptions_t* rocksdb_compactoptions_create() {
|
||||
return new rocksdb_compactoptions_t;
|
||||
}
|
||||
@@ -3752,21 +3877,40 @@ void rocksdb_compactoptions_set_bottommost_level_compaction(
|
||||
opt->rep.bottommost_level_compaction = static_cast<BottommostLevelCompaction>(v);
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compactoptions_get_bottommost_level_compaction(
|
||||
rocksdb_compactoptions_t* opt) {
|
||||
return static_cast<unsigned char>(opt->rep.bottommost_level_compaction);
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_exclusive_manual_compaction(
|
||||
rocksdb_compactoptions_t* opt, unsigned char v) {
|
||||
opt->rep.exclusive_manual_compaction = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compactoptions_get_exclusive_manual_compaction(
|
||||
rocksdb_compactoptions_t* opt) {
|
||||
return opt->rep.exclusive_manual_compaction;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_change_level(rocksdb_compactoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.change_level = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_compactoptions_get_change_level(
|
||||
rocksdb_compactoptions_t* opt) {
|
||||
return opt->rep.change_level;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_target_level(rocksdb_compactoptions_t* opt,
|
||||
int n) {
|
||||
opt->rep.target_level = n;
|
||||
}
|
||||
|
||||
int rocksdb_compactoptions_get_target_level(rocksdb_compactoptions_t* opt) {
|
||||
return opt->rep.target_level;
|
||||
}
|
||||
|
||||
rocksdb_flushoptions_t* rocksdb_flushoptions_create() {
|
||||
return new rocksdb_flushoptions_t;
|
||||
}
|
||||
@@ -3780,6 +3924,10 @@ void rocksdb_flushoptions_set_wait(
|
||||
opt->rep.wait = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_flushoptions_get_wait(rocksdb_flushoptions_t* opt) {
|
||||
return opt->rep.wait;
|
||||
}
|
||||
|
||||
rocksdb_cache_t* rocksdb_cache_create_lru(size_t capacity) {
|
||||
rocksdb_cache_t* c = new rocksdb_cache_t;
|
||||
c->rep = NewLRUCache(capacity);
|
||||
@@ -3794,6 +3942,10 @@ void rocksdb_cache_set_capacity(rocksdb_cache_t* cache, size_t capacity) {
|
||||
cache->rep->SetCapacity(capacity);
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_capacity(rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetCapacity();
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_usage(rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetUsage();
|
||||
}
|
||||
@@ -3831,10 +3983,20 @@ void rocksdb_env_set_background_threads(rocksdb_env_t* env, int n) {
|
||||
env->rep->SetBackgroundThreads(n);
|
||||
}
|
||||
|
||||
void rocksdb_env_set_bottom_priority_background_threads(rocksdb_env_t* env,
|
||||
int n) {
|
||||
env->rep->SetBackgroundThreads(n, Env::BOTTOM);
|
||||
}
|
||||
|
||||
void rocksdb_env_set_high_priority_background_threads(rocksdb_env_t* env, int n) {
|
||||
env->rep->SetBackgroundThreads(n, Env::HIGH);
|
||||
}
|
||||
|
||||
void rocksdb_env_set_low_priority_background_threads(rocksdb_env_t* env,
|
||||
int n) {
|
||||
env->rep->SetBackgroundThreads(n, Env::LOW);
|
||||
}
|
||||
|
||||
void rocksdb_env_join_all_threads(rocksdb_env_t* env) {
|
||||
env->rep->WaitForJoin();
|
||||
}
|
||||
|
||||
+116
@@ -2235,6 +2235,122 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_destroy(o);
|
||||
}
|
||||
|
||||
StartPhase("read_options");
|
||||
{
|
||||
rocksdb_readoptions_t* ro;
|
||||
ro = rocksdb_readoptions_create();
|
||||
|
||||
rocksdb_readoptions_set_verify_checksums(ro, 1);
|
||||
CheckCondition(1 == rocksdb_readoptions_get_verify_checksums(ro));
|
||||
|
||||
rocksdb_readoptions_set_fill_cache(ro, 1);
|
||||
CheckCondition(1 == rocksdb_readoptions_get_fill_cache(ro));
|
||||
|
||||
rocksdb_readoptions_set_read_tier(ro, 2);
|
||||
CheckCondition(2 == rocksdb_readoptions_get_read_tier(ro));
|
||||
|
||||
rocksdb_readoptions_set_tailing(ro, 1);
|
||||
CheckCondition(1 == rocksdb_readoptions_get_tailing(ro));
|
||||
|
||||
rocksdb_readoptions_set_readahead_size(ro, 100);
|
||||
CheckCondition(100 == rocksdb_readoptions_get_readahead_size(ro));
|
||||
|
||||
rocksdb_readoptions_set_prefix_same_as_start(ro, 1);
|
||||
CheckCondition(1 == rocksdb_readoptions_get_prefix_same_as_start(ro));
|
||||
|
||||
rocksdb_readoptions_set_pin_data(ro, 1);
|
||||
CheckCondition(1 == rocksdb_readoptions_get_pin_data(ro));
|
||||
|
||||
rocksdb_readoptions_set_total_order_seek(ro, 1);
|
||||
CheckCondition(1 == rocksdb_readoptions_get_total_order_seek(ro));
|
||||
|
||||
rocksdb_readoptions_set_max_skippable_internal_keys(ro, 200);
|
||||
CheckCondition(200 ==
|
||||
rocksdb_readoptions_get_max_skippable_internal_keys(ro));
|
||||
|
||||
rocksdb_readoptions_set_background_purge_on_iterator_cleanup(ro, 1);
|
||||
CheckCondition(
|
||||
1 == rocksdb_readoptions_get_background_purge_on_iterator_cleanup(ro));
|
||||
|
||||
rocksdb_readoptions_set_ignore_range_deletions(ro, 1);
|
||||
CheckCondition(1 == rocksdb_readoptions_get_ignore_range_deletions(ro));
|
||||
|
||||
rocksdb_readoptions_destroy(ro);
|
||||
}
|
||||
|
||||
StartPhase("write_options");
|
||||
{
|
||||
rocksdb_writeoptions_t* wo;
|
||||
wo = rocksdb_writeoptions_create();
|
||||
|
||||
rocksdb_writeoptions_set_sync(wo, 1);
|
||||
CheckCondition(1 == rocksdb_writeoptions_get_sync(wo));
|
||||
|
||||
rocksdb_writeoptions_disable_WAL(wo, 1);
|
||||
CheckCondition(1 == rocksdb_writeoptions_get_disable_WAL(wo));
|
||||
|
||||
rocksdb_writeoptions_set_ignore_missing_column_families(wo, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_writeoptions_get_ignore_missing_column_families(wo));
|
||||
|
||||
rocksdb_writeoptions_set_no_slowdown(wo, 1);
|
||||
CheckCondition(1 == rocksdb_writeoptions_get_no_slowdown(wo));
|
||||
|
||||
rocksdb_writeoptions_set_low_pri(wo, 1);
|
||||
CheckCondition(1 == rocksdb_writeoptions_get_low_pri(wo));
|
||||
|
||||
rocksdb_writeoptions_set_memtable_insert_hint_per_batch(wo, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_writeoptions_get_memtable_insert_hint_per_batch(wo));
|
||||
|
||||
rocksdb_writeoptions_destroy(wo);
|
||||
}
|
||||
|
||||
StartPhase("compact_options");
|
||||
{
|
||||
rocksdb_compactoptions_t* co;
|
||||
co = rocksdb_compactoptions_create();
|
||||
|
||||
rocksdb_compactoptions_set_exclusive_manual_compaction(co, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_compactoptions_get_exclusive_manual_compaction(co));
|
||||
|
||||
rocksdb_compactoptions_set_bottommost_level_compaction(co, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_compactoptions_get_bottommost_level_compaction(co));
|
||||
|
||||
rocksdb_compactoptions_set_change_level(co, 1);
|
||||
CheckCondition(1 == rocksdb_compactoptions_get_change_level(co));
|
||||
|
||||
rocksdb_compactoptions_set_target_level(co, 1);
|
||||
CheckCondition(1 == rocksdb_compactoptions_get_target_level(co));
|
||||
|
||||
rocksdb_compactoptions_destroy(co);
|
||||
}
|
||||
|
||||
StartPhase("flush_options");
|
||||
{
|
||||
rocksdb_flushoptions_t* fo;
|
||||
fo = rocksdb_flushoptions_create();
|
||||
|
||||
rocksdb_flushoptions_set_wait(fo, 1);
|
||||
CheckCondition(1 == rocksdb_flushoptions_get_wait(fo));
|
||||
|
||||
rocksdb_flushoptions_destroy(fo);
|
||||
}
|
||||
|
||||
StartPhase("cache_options");
|
||||
{
|
||||
rocksdb_cache_t* co;
|
||||
co = rocksdb_cache_create_lru(100);
|
||||
CheckCondition(100 == rocksdb_cache_get_capacity(co));
|
||||
|
||||
rocksdb_cache_set_capacity(co, 200);
|
||||
CheckCondition(200 == rocksdb_cache_get_capacity(co));
|
||||
|
||||
rocksdb_cache_destroy(co);
|
||||
}
|
||||
|
||||
StartPhase("iterate_upper_bound");
|
||||
{
|
||||
// Create new empty database
|
||||
|
||||
+22
-7
@@ -12,6 +12,7 @@
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -148,6 +149,16 @@ Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
|
||||
"should be nonzero if we're using zstd's dictionary generator.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!CompressionTypeSupported(cf_options.blob_compression_type)) {
|
||||
std::ostringstream oss;
|
||||
oss << "The specified blob compression type "
|
||||
<< CompressionTypeToString(cf_options.blob_compression_type)
|
||||
<< " is not available.";
|
||||
|
||||
return Status::InvalidArgument(oss.str());
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -488,7 +499,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
Cache* _table_cache, WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options,
|
||||
const FileOptions& file_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer)
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(_dummy_versions),
|
||||
@@ -546,7 +558,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
internal_stats_.reset(
|
||||
new InternalStats(ioptions_.num_levels, db_options.env, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, file_options, _table_cache,
|
||||
block_cache_tracer));
|
||||
block_cache_tracer, io_tracer));
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
new LevelCompactionPicker(ioptions_, &internal_comparator_));
|
||||
@@ -1043,7 +1055,8 @@ void ColumnFamilyData::CreateNewMemtable(
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::NeedsCompaction() const {
|
||||
return compaction_picker_->NeedsCompaction(current_->storage_info());
|
||||
return !mutable_cf_options_.disable_auto_compactions &&
|
||||
compaction_picker_->NeedsCompaction(current_->storage_info());
|
||||
}
|
||||
|
||||
Compaction* ColumnFamilyData::PickCompaction(
|
||||
@@ -1403,12 +1416,13 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
Cache* table_cache,
|
||||
WriteBufferManager* _write_buffer_manager,
|
||||
WriteController* _write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer)
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: max_column_family_(0),
|
||||
dummy_cfd_(new ColumnFamilyData(
|
||||
ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr,
|
||||
nullptr, ColumnFamilyOptions(), *db_options, file_options, nullptr,
|
||||
block_cache_tracer)),
|
||||
block_cache_tracer, io_tracer)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
@@ -1416,7 +1430,8 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
table_cache_(table_cache),
|
||||
write_buffer_manager_(_write_buffer_manager),
|
||||
write_controller_(_write_controller),
|
||||
block_cache_tracer_(block_cache_tracer) {
|
||||
block_cache_tracer_(block_cache_tracer),
|
||||
io_tracer_(io_tracer) {
|
||||
// initialize linked list
|
||||
dummy_cfd_->prev_ = dummy_cfd_;
|
||||
dummy_cfd_->next_ = dummy_cfd_;
|
||||
@@ -1482,7 +1497,7 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
*db_options_, file_options_, this, block_cache_tracer_);
|
||||
*db_options_, file_options_, this, block_cache_tracer_, io_tracer_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
|
||||
+5
-2
@@ -518,7 +518,8 @@ class ColumnFamilyData {
|
||||
const ImmutableDBOptions& db_options,
|
||||
const FileOptions& file_options,
|
||||
ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer);
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer);
|
||||
|
||||
std::vector<std::string> GetDbPaths() const;
|
||||
|
||||
@@ -651,7 +652,8 @@ class ColumnFamilySet {
|
||||
const FileOptions& file_options, Cache* table_cache,
|
||||
WriteBufferManager* _write_buffer_manager,
|
||||
WriteController* _write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer);
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer);
|
||||
~ColumnFamilySet();
|
||||
|
||||
ColumnFamilyData* GetDefault() const;
|
||||
@@ -715,6 +717,7 @@ class ColumnFamilySet {
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
WriteController* write_controller_;
|
||||
BlockCacheTracer* const block_cache_tracer_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
|
||||
@@ -2427,7 +2427,7 @@ TEST_P(ColumnFamilyTest, FlushAndDropRaceCondition) {
|
||||
// Make sure the task is sleeping. Otherwise, it might start to execute
|
||||
// after sleeping_task.WaitUntilDone() and cause TSAN warning.
|
||||
sleeping_task.WaitUntilSleeping();
|
||||
|
||||
|
||||
// 1MB should create ~10 files for each CF
|
||||
int kKeysNum = 10000;
|
||||
PutRandomData(1, kKeysNum, 100);
|
||||
|
||||
@@ -149,7 +149,7 @@ Status CompactedDBImpl::Open(const Options& options,
|
||||
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
||||
Status s = db->Init(options);
|
||||
if (s.ok()) {
|
||||
db->StartTimedTasks();
|
||||
db->StartStatsDumpScheduler();
|
||||
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
|
||||
@@ -40,7 +40,7 @@ CompactionIterator::CompactionIterator(
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
const std::atomic<bool>* manual_compaction_paused,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::shared_ptr<Logger> info_log)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
@@ -62,7 +62,7 @@ CompactionIterator::CompactionIterator(
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
const std::atomic<bool>* manual_compaction_paused,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::shared_ptr<Logger> info_log)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
@@ -86,8 +86,10 @@ CompactionIterator::CompactionIterator(
|
||||
info_log_(info_log) {
|
||||
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
||||
assert(snapshots_ != nullptr);
|
||||
bottommost_level_ =
|
||||
compaction_ == nullptr ? false : compaction_->bottommost_level();
|
||||
bottommost_level_ = compaction_ == nullptr
|
||||
? false
|
||||
: compaction_->bottommost_level() &&
|
||||
!compaction_->allow_ingest_behind();
|
||||
if (compaction_ != nullptr) {
|
||||
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
||||
}
|
||||
@@ -142,8 +144,7 @@ void CompactionIterator::Next() {
|
||||
if (merge_out_iter_.Valid()) {
|
||||
key_ = merge_out_iter_.key();
|
||||
value_ = merge_out_iter_.value();
|
||||
bool valid_key __attribute__((__unused__));
|
||||
valid_key = ParseInternalKey(key_, &ikey_);
|
||||
bool valid_key = ParseInternalKey(key_, &ikey_);
|
||||
// MergeUntil stops when it encounters a corrupt key and does not
|
||||
// include them in the result, so we expect the keys here to be valid.
|
||||
assert(valid_key);
|
||||
@@ -350,8 +351,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// If there are no snapshots, then this kv affect visibility at tip.
|
||||
// Otherwise, search though all existing snapshots to find the earliest
|
||||
// snapshot that is affected by this kv.
|
||||
SequenceNumber last_sequence __attribute__((__unused__));
|
||||
last_sequence = current_user_key_sequence_;
|
||||
SequenceNumber last_sequence = current_user_key_sequence_;
|
||||
current_user_key_sequence_ = ikey_.sequence;
|
||||
SequenceNumber last_snapshot = current_user_key_snapshot_;
|
||||
SequenceNumber prev_snapshot = 0; // 0 means no previous snapshot
|
||||
@@ -563,11 +563,14 @@ void CompactionIterator::NextFromInput() {
|
||||
// Handle the case where we have a delete key at the bottom most level
|
||||
// We can skip outputting the key iff there are no subsequent puts for this
|
||||
// key
|
||||
assert(!compaction_ || compaction_->KeyNotExistsBeyondOutputLevel(
|
||||
ikey_.user_key, &level_ptrs_));
|
||||
ParsedInternalKey next_ikey;
|
||||
input_->Next();
|
||||
// Skip over all versions of this key that happen to occur in the same snapshot
|
||||
// range as the delete
|
||||
while (input_->Valid() && ParseInternalKey(input_->key(), &next_ikey) &&
|
||||
while (!IsPausingManualCompaction() && !IsShuttingDown() &&
|
||||
input_->Valid() && ParseInternalKey(input_->key(), &next_ikey) &&
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key) &&
|
||||
(prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot))) {
|
||||
@@ -604,8 +607,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// These will be correctly set below.
|
||||
key_ = merge_out_iter_.key();
|
||||
value_ = merge_out_iter_.value();
|
||||
bool valid_key __attribute__((__unused__));
|
||||
valid_key = ParseInternalKey(key_, &ikey_);
|
||||
bool valid_key = ParseInternalKey(key_, &ikey_);
|
||||
// MergeUntil stops when it encounters a corrupt key and does not
|
||||
// include them in the result, so we expect the keys here to valid.
|
||||
assert(valid_key);
|
||||
|
||||
@@ -59,34 +59,34 @@ class CompactionIterator {
|
||||
const Compaction* compaction_;
|
||||
};
|
||||
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr);
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr);
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
@@ -166,7 +166,7 @@ class CompactionIterator {
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<bool>* manual_compaction_paused_;
|
||||
const std::atomic<int>* manual_compaction_paused_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
@@ -235,7 +235,7 @@ class CompactionIterator {
|
||||
bool IsPausingManualCompaction() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
return manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed);
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed) > 0;
|
||||
}
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -169,13 +169,15 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
Slice GetLargestUserKey() const override {
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
bool allow_ingest_behind() const override { return false; }
|
||||
bool allow_ingest_behind() const override { return is_allow_ingest_behind; }
|
||||
|
||||
bool preserve_deletes() const override { return false; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
bool is_bottommost_level = false;
|
||||
|
||||
bool is_allow_ingest_behind = false;
|
||||
};
|
||||
|
||||
// A simplifed snapshot checker which assumes each snapshot has a global
|
||||
@@ -237,6 +239,7 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
if (filter || bottommost_level) {
|
||||
compaction_proxy_ = new FakeCompaction();
|
||||
compaction_proxy_->is_bottommost_level = bottommost_level;
|
||||
compaction_proxy_->is_allow_ingest_behind = AllowIngestBehind();
|
||||
compaction.reset(compaction_proxy_);
|
||||
}
|
||||
bool use_snapshot_checker = UseSnapshotChecker() || GetParam();
|
||||
@@ -266,6 +269,8 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
|
||||
virtual bool UseSnapshotChecker() const { return false; }
|
||||
|
||||
virtual bool AllowIngestBehind() const { return false; }
|
||||
|
||||
void RunTest(
|
||||
const std::vector<std::string>& input_keys,
|
||||
const std::vector<std::string>& input_values,
|
||||
@@ -697,6 +702,18 @@ TEST_P(CompactionIteratorTest, RemoveSingleDeletionAtBottomLevel) {
|
||||
nullptr /*compaction_filter*/, true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, ConvertToPutAtBottom) {
|
||||
std::shared_ptr<MergeOperator> merge_op =
|
||||
MergeOperators::CreateStringAppendOperator();
|
||||
RunTest({test::KeyStr("a", 4, kTypeMerge), test::KeyStr("a", 3, kTypeMerge),
|
||||
test::KeyStr("a", 2, kTypeMerge), test::KeyStr("b", 1, kTypeValue)},
|
||||
{"a4", "a3", "a2", "b1"},
|
||||
{test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 0, kTypeValue)},
|
||||
{"a2,a3,a4", "b1"}, kMaxSequenceNumber /*last_committed_seq*/,
|
||||
merge_op.get(), nullptr /*compaction_filter*/,
|
||||
true /*bottomost_level*/);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionIteratorTestInstance, CompactionIteratorTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
@@ -968,6 +985,45 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_FullMerge) {
|
||||
compaction_filter.get());
|
||||
}
|
||||
|
||||
// Tests how CompactionIterator work together with AllowIngestBehind.
|
||||
class CompactionIteratorWithAllowIngestBehindTest
|
||||
: public CompactionIteratorTest {
|
||||
public:
|
||||
bool AllowIngestBehind() const override { return true; }
|
||||
};
|
||||
|
||||
// When allow_ingest_behind is set, compaction iterator is not targeting
|
||||
// the bottommost level since there is no guarantee there won't be further
|
||||
// data ingested under the compaction output in future.
|
||||
TEST_P(CompactionIteratorWithAllowIngestBehindTest, NoConvertToPutAtBottom) {
|
||||
std::shared_ptr<MergeOperator> merge_op =
|
||||
MergeOperators::CreateStringAppendOperator();
|
||||
RunTest({test::KeyStr("a", 4, kTypeMerge), test::KeyStr("a", 3, kTypeMerge),
|
||||
test::KeyStr("a", 2, kTypeMerge), test::KeyStr("b", 1, kTypeValue)},
|
||||
{"a4", "a3", "a2", "b1"},
|
||||
{test::KeyStr("a", 4, kTypeMerge), test::KeyStr("b", 1, kTypeValue)},
|
||||
{"a2,a3,a4", "b1"}, kMaxSequenceNumber /*last_committed_seq*/,
|
||||
merge_op.get(), nullptr /*compaction_filter*/,
|
||||
true /*bottomost_level*/);
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorWithAllowIngestBehindTest,
|
||||
MergeToPutIfEncounteredPutAtBottom) {
|
||||
std::shared_ptr<MergeOperator> merge_op =
|
||||
MergeOperators::CreateStringAppendOperator();
|
||||
RunTest({test::KeyStr("a", 4, kTypeMerge), test::KeyStr("a", 3, kTypeMerge),
|
||||
test::KeyStr("a", 2, kTypeValue), test::KeyStr("b", 1, kTypeValue)},
|
||||
{"a4", "a3", "a2", "b1"},
|
||||
{test::KeyStr("a", 4, kTypeValue), test::KeyStr("b", 1, kTypeValue)},
|
||||
{"a2,a3,a4", "b1"}, kMaxSequenceNumber /*last_committed_seq*/,
|
||||
merge_op.get(), nullptr /*compaction_filter*/,
|
||||
true /*bottomost_level*/);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionIteratorWithAllowIngestBehindTestInstance,
|
||||
CompactionIteratorWithAllowIngestBehindTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -134,6 +134,7 @@ struct CompactionJob::SubcompactionState {
|
||||
std::vector<Output> outputs;
|
||||
std::unique_ptr<WritableFileWriter> outfile;
|
||||
std::unique_ptr<TableBuilder> builder;
|
||||
|
||||
Output* current_output() {
|
||||
if (outputs.empty()) {
|
||||
// This subcompaction's output could be empty if compaction was aborted
|
||||
@@ -147,13 +148,13 @@ struct CompactionJob::SubcompactionState {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t current_output_file_size;
|
||||
uint64_t current_output_file_size = 0;
|
||||
|
||||
// State during the subcompaction
|
||||
uint64_t total_bytes;
|
||||
uint64_t num_output_records;
|
||||
uint64_t total_bytes = 0;
|
||||
uint64_t num_output_records = 0;
|
||||
CompactionJobStats compaction_job_stats;
|
||||
uint64_t approx_size;
|
||||
uint64_t approx_size = 0;
|
||||
// An index that used to speed up ShouldStopBefore().
|
||||
size_t grandparent_index = 0;
|
||||
// The number of bytes overlapping between the current output and
|
||||
@@ -162,50 +163,11 @@ struct CompactionJob::SubcompactionState {
|
||||
// A flag determine whether the key has been seen in ShouldStopBefore()
|
||||
bool seen_key = false;
|
||||
|
||||
SubcompactionState(Compaction* c, Slice* _start, Slice* _end,
|
||||
uint64_t size = 0)
|
||||
: compaction(c),
|
||||
start(_start),
|
||||
end(_end),
|
||||
outfile(nullptr),
|
||||
builder(nullptr),
|
||||
current_output_file_size(0),
|
||||
total_bytes(0),
|
||||
num_output_records(0),
|
||||
approx_size(size),
|
||||
grandparent_index(0),
|
||||
overlapped_bytes(0),
|
||||
seen_key(false) {
|
||||
SubcompactionState(Compaction* c, Slice* _start, Slice* _end, uint64_t size)
|
||||
: compaction(c), start(_start), end(_end), approx_size(size) {
|
||||
assert(compaction != nullptr);
|
||||
}
|
||||
|
||||
SubcompactionState(SubcompactionState&& o) { *this = std::move(o); }
|
||||
|
||||
SubcompactionState& operator=(SubcompactionState&& o) {
|
||||
compaction = std::move(o.compaction);
|
||||
start = std::move(o.start);
|
||||
end = std::move(o.end);
|
||||
status = std::move(o.status);
|
||||
io_status = std::move(o.io_status);
|
||||
outputs = std::move(o.outputs);
|
||||
outfile = std::move(o.outfile);
|
||||
builder = std::move(o.builder);
|
||||
current_output_file_size = std::move(o.current_output_file_size);
|
||||
total_bytes = std::move(o.total_bytes);
|
||||
num_output_records = std::move(o.num_output_records);
|
||||
compaction_job_stats = std::move(o.compaction_job_stats);
|
||||
approx_size = std::move(o.approx_size);
|
||||
grandparent_index = std::move(o.grandparent_index);
|
||||
overlapped_bytes = std::move(o.overlapped_bytes);
|
||||
seen_key = std::move(o.seen_key);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Because member std::unique_ptrs do not have these.
|
||||
SubcompactionState(const SubcompactionState&) = delete;
|
||||
|
||||
SubcompactionState& operator=(const SubcompactionState&) = delete;
|
||||
|
||||
// Adds the key and value to the builder
|
||||
// If paranoid is true, adds the key-value to the paranoid hash
|
||||
void AddToBuilder(const Slice& key, const Slice& value, bool paranoid) {
|
||||
@@ -328,8 +290,9 @@ CompactionJob::CompactionJob(
|
||||
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,
|
||||
const std::string& db_id, const std::string& db_session_id)
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* 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),
|
||||
@@ -340,7 +303,8 @@ CompactionJob::CompactionJob(
|
||||
db_options_(db_options),
|
||||
file_options_(file_options),
|
||||
env_(db_options.env),
|
||||
fs_(db_options.fs.get()),
|
||||
io_tracer_(io_tracer),
|
||||
fs_(db_options.fs, io_tracer),
|
||||
file_options_for_read_(
|
||||
fs_->OptimizeForCompactionTableRead(file_options, db_options_)),
|
||||
versions_(versions),
|
||||
@@ -449,7 +413,11 @@ void CompactionJob::Prepare() {
|
||||
RecordInHistogram(stats_, NUM_SUBCOMPACTIONS_SCHEDULED,
|
||||
compact_->sub_compact_states.size());
|
||||
} else {
|
||||
compact_->sub_compact_states.emplace_back(c, nullptr, nullptr);
|
||||
constexpr Slice* start = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
constexpr uint64_t size = 0;
|
||||
|
||||
compact_->sub_compact_states.emplace_back(c, start, end, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,8 +644,9 @@ Status CompactionJob::Run() {
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// we will regard this verification as user reads since the goal is
|
||||
// to cache it here for further user reads
|
||||
ReadOptions read_options;
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
ReadOptions(), file_options_, cfd->internal_comparator(),
|
||||
read_options, file_options_, cfd->internal_comparator(),
|
||||
files_output[file_idx]->meta, /*range_del_agg=*/nullptr,
|
||||
prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr,
|
||||
@@ -701,7 +670,7 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
s = iter->status();
|
||||
if (s.ok() && hash != files_output[file_idx]->paranoid_hash) {
|
||||
s = Status::Corruption("Paraniod checksums do not match");
|
||||
s = Status::Corruption("Paranoid checksums do not match");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,11 +846,20 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
CompactionRangeDelAggregator range_del_agg(&cfd->internal_comparator(),
|
||||
existing_snapshots_);
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
read_options.fill_cache = false;
|
||||
// Compaction iterators shouldn't be confined to a single prefix.
|
||||
// Compactions use Seek() for
|
||||
// (a) concurrent compactions,
|
||||
// (b) CompactionFilter::Decision::kRemoveAndSkipUntil.
|
||||
read_options.total_order_seek = true;
|
||||
|
||||
// Although the v2 aggregator is what the level iterator(s) know about,
|
||||
// the AddTombstones calls will be propagated down to the v1 aggregator.
|
||||
std::unique_ptr<InternalIterator> input(versions_->MakeInputIterator(
|
||||
sub_compact->compaction, &range_del_agg, file_options_for_read_));
|
||||
std::unique_ptr<InternalIterator> input(
|
||||
versions_->MakeInputIterator(read_options, sub_compact->compaction,
|
||||
&range_del_agg, file_options_for_read_));
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -918,7 +896,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(manual_compaction_paused_)));
|
||||
const_cast<std::atomic<int>*>(manual_compaction_paused_)));
|
||||
|
||||
Slice* start = sub_compact->start;
|
||||
Slice* end = sub_compact->end;
|
||||
@@ -1012,7 +990,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:2",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(manual_compaction_paused_)));
|
||||
const_cast<std::atomic<int>*>(manual_compaction_paused_)));
|
||||
if (partitioner.get()) {
|
||||
last_key_for_partitioner.assign(c_iter->user_key().data_,
|
||||
c_iter->user_key().size_);
|
||||
@@ -1079,7 +1057,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
if ((status.ok() || status.IsColumnFamilyDropped()) &&
|
||||
(manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed))) {
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed) > 0)) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
if (status.ok()) {
|
||||
@@ -1187,6 +1165,8 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
const Comparator* ucmp = cfd->user_comparator();
|
||||
std::string file_checksum = kUnknownFileChecksum;
|
||||
std::string file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
|
||||
// Check for iterator errors
|
||||
Status s = input_status;
|
||||
@@ -1288,8 +1268,8 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
auto kv = tombstone.Serialize();
|
||||
assert(lower_bound == nullptr ||
|
||||
ucmp->Compare(*lower_bound, kv.second) < 0);
|
||||
sub_compact->AddToBuilder(kv.first.Encode(), kv.second,
|
||||
paranoid_file_checks_);
|
||||
// Range tombstone is not supported by output validator yet.
|
||||
sub_compact->builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey smallest_candidate = std::move(kv.first);
|
||||
if (lower_bound != nullptr &&
|
||||
ucmp->Compare(smallest_candidate.user_key(), *lower_bound) <= 0) {
|
||||
@@ -1386,6 +1366,8 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
meta->file_checksum = sub_compact->outfile->GetFileChecksum();
|
||||
meta->file_checksum_func_name =
|
||||
sub_compact->outfile->GetFileChecksumFuncName();
|
||||
file_checksum = meta->file_checksum;
|
||||
file_checksum_func_name = meta->file_checksum_func_name;
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = io_s;
|
||||
@@ -1442,7 +1424,8 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(), fname,
|
||||
job_id_, output_fd, oldest_blob_file_number, tp,
|
||||
TableFileCreationReason::kCompaction, s);
|
||||
TableFileCreationReason::kCompaction, s, file_checksum,
|
||||
file_checksum_func_name);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Report new file to SstFileManagerImpl
|
||||
@@ -1509,10 +1492,22 @@ Status CompactionJob::InstallCompactionResults(
|
||||
|
||||
void CompactionJob::RecordCompactionIOStats() {
|
||||
RecordTick(stats_, COMPACT_READ_BYTES, IOSTATS(bytes_read));
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, IOSTATS(bytes_written));
|
||||
CompactionReason compaction_reason =
|
||||
compact_->compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kFilesMarkedForCompaction) {
|
||||
RecordTick(stats_, COMPACT_READ_BYTES_MARKED, IOSTATS(bytes_read));
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES_MARKED, IOSTATS(bytes_written));
|
||||
} else if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
RecordTick(stats_, COMPACT_READ_BYTES_PERIODIC, IOSTATS(bytes_read));
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES_PERIODIC, IOSTATS(bytes_written));
|
||||
} else if (compaction_reason == CompactionReason::kTtl) {
|
||||
RecordTick(stats_, COMPACT_READ_BYTES_TTL, IOSTATS(bytes_read));
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES_TTL, IOSTATS(bytes_written));
|
||||
}
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_BYTES_READ, IOSTATS(bytes_read));
|
||||
IOSTATS_RESET(bytes_read);
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, IOSTATS(bytes_written));
|
||||
ThreadStatusUtil::IncreaseThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_BYTES_WRITTEN, IOSTATS(bytes_written));
|
||||
IOSTATS_RESET(bytes_written);
|
||||
@@ -1542,7 +1537,8 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
&syncpoint_arg);
|
||||
#endif
|
||||
Status s;
|
||||
IOStatus io_s = NewWritableFile(fs_, fname, &writable_file, file_options_);
|
||||
IOStatus io_s =
|
||||
NewWritableFile(fs_.get(), fname, &writable_file, file_options_);
|
||||
s = io_s;
|
||||
if (sub_compact->io_status.ok()) {
|
||||
sub_compact->io_status = io_s;
|
||||
@@ -1558,7 +1554,8 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(),
|
||||
fname, job_id_, FileDescriptor(), kInvalidBlobFileNumber,
|
||||
TableProperties(), TableFileCreationReason::kCompaction, s);
|
||||
TableProperties(), TableFileCreationReason::kCompaction, s,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1596,10 +1593,10 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->compaction->OutputFilePreallocationSize()));
|
||||
const auto& listeners =
|
||||
sub_compact->compaction->immutable_cf_options()->listeners;
|
||||
sub_compact->outfile.reset(
|
||||
new WritableFileWriter(std::move(writable_file), fname, file_options_,
|
||||
env_, db_options_.statistics.get(), listeners,
|
||||
db_options_.file_checksum_gen_factory.get()));
|
||||
sub_compact->outfile.reset(new WritableFileWriter(
|
||||
std::move(writable_file), fname, file_options_, env_, io_tracer_,
|
||||
db_options_.statistics.get(), listeners,
|
||||
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
|
||||
|
||||
@@ -62,25 +62,23 @@ class VersionSet;
|
||||
// if needed.
|
||||
class CompactionJob {
|
||||
public:
|
||||
CompactionJob(int job_id, Compaction* compaction,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
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,
|
||||
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 = nullptr,
|
||||
const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
CompactionJob(
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, 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,
|
||||
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::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "");
|
||||
|
||||
~CompactionJob();
|
||||
|
||||
@@ -160,12 +158,13 @@ class CompactionJob {
|
||||
const FileOptions file_options_;
|
||||
|
||||
Env* env_;
|
||||
FileSystem* fs_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
FileSystemPtr fs_;
|
||||
// env_option optimized for compaction table reads
|
||||
FileOptions file_options_for_read_;
|
||||
VersionSet* versions_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<bool>* manual_compaction_paused_;
|
||||
const std::atomic<int>* manual_compaction_paused_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
LogBuffer* log_buffer_;
|
||||
FSDirectory* db_directory_;
|
||||
|
||||
@@ -79,10 +79,10 @@ class CompactionJobTest : public testing::Test {
|
||||
mutable_db_options_(),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_manager_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_,
|
||||
/*block_cache_tracer=*/nullptr)),
|
||||
versions_(new VersionSet(
|
||||
dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr)),
|
||||
shutting_down_(false),
|
||||
preserve_deletes_seqnum_(0),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
@@ -249,10 +249,10 @@ class CompactionJobTest : public testing::Test {
|
||||
void NewDB() {
|
||||
DestroyDB(dbname_, Options());
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
versions_.reset(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_,
|
||||
/*block_cache_tracer=*/nullptr));
|
||||
versions_.reset(
|
||||
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr));
|
||||
compaction_job_stats_.Reset();
|
||||
SetIdentityFile(env_, dbname_);
|
||||
|
||||
@@ -334,7 +334,7 @@ class CompactionJobTest : public testing::Test {
|
||||
nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, table_cache_,
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_,
|
||||
Env::Priority::USER);
|
||||
Env::Priority::USER, nullptr /* IOTracer */);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
compaction_job.Prepare();
|
||||
|
||||
@@ -639,6 +639,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
compact_range_options.max_subcompactions, /* grandparents */ {},
|
||||
/* is manual */ true);
|
||||
RegisterCompaction(c);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
@@ -2016,6 +2016,171 @@ TEST_F(CompactionPickerTest, UniversalMarkedCompactionStartOutputOverlap) {
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedL0NoOverlap) {
|
||||
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 and should result in a full compaction
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
|
||||
// Mark file number 4 for compaction
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 201, 250);
|
||||
Add(0, 3U, "301", "350", 4 * kFileSize, 0, 101, 150);
|
||||
Add(0, 6U, "501", "750", 8 * kFileSize, 0, 50, 100);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
ASSERT_TRUE(file_map_[4].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[5].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[3].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[6].first->being_compacted);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedL0WithOverlap) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a file is being compacted, and a
|
||||
// delete triggered compaction is then scheduled. The latter should stop
|
||||
// at the first file being compacted
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
|
||||
// Mark file number 4 for compaction
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 201, 250);
|
||||
Add(0, 3U, "301", "350", 4 * kFileSize, 0, 101, 150);
|
||||
Add(0, 6U, "501", "750", 8 * kFileSize, 0, 50, 100);
|
||||
UpdateVersionStorageInfo();
|
||||
file_map_[3].first->being_compacted = true;
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_TRUE(file_map_[4].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[5].first->being_compacted);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedL0Overlap2) {
|
||||
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(1, kCompactionStyleUniversal);
|
||||
|
||||
// Mark file number 4 for compaction
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 260, 300);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 201, 250, 0, true);
|
||||
Add(0, 3U, "301", "350", 4 * kFileSize, 0, 101, 150);
|
||||
Add(0, 6U, "501", "750", 8 * kFileSize, 0, 50, 100);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(3U, compaction->num_input_files(0));
|
||||
ASSERT_TRUE(file_map_[5].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[3].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[6].first->being_compacted);
|
||||
|
||||
AddVersionStorage();
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", kFileSize, 0, 401, 450);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction2);
|
||||
ASSERT_EQ(3U, compaction->num_input_files(0));
|
||||
ASSERT_TRUE(file_map_[1].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[2].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[4].first->being_compacted);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedManualCompaction) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
const int kNumLevels = 7;
|
||||
|
||||
// This test makes sure the `files_marked_for_compaction_` is updated after
|
||||
// creating manual compaction.
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
NewVersionStorage(kNumLevels, kCompactionStyleUniversal);
|
||||
|
||||
// Add 3 files marked for compaction
|
||||
Add(0, 3U, "301", "350", 4 * kFileSize, 0, 101, 150, 0, true);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 201, 250, 0, true);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// All 3 files are marked for compaction
|
||||
ASSERT_EQ(3U, vstorage_->FilesMarkedForCompaction().size());
|
||||
|
||||
bool manual_conflict = false;
|
||||
InternalKey* manual_end = NULL;
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.CompactRange(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
ColumnFamilyData::kCompactAllLevels, 6, CompactRangeOptions(), NULL,
|
||||
NULL, &manual_end, &manual_conflict, port::kMaxUint64));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
|
||||
ASSERT_EQ(CompactionReason::kManualCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(kNumLevels - 1, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(3U, compaction->num_input_files(0));
|
||||
ASSERT_TRUE(file_map_[3].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[4].first->being_compacted);
|
||||
ASSERT_TRUE(file_map_[5].first->being_compacted);
|
||||
|
||||
// After creating the manual compaction, all files should be cleared from
|
||||
// `FilesMarkedForCompaction`. So they won't be picked by others.
|
||||
ASSERT_EQ(0U, vstorage_->FilesMarkedForCompaction().size());
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -841,34 +841,48 @@ 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
|
||||
// has the same goals.
|
||||
bool compact = false;
|
||||
int start_index = -1;
|
||||
|
||||
start_level_inputs.level = 0;
|
||||
start_level_inputs.files.clear();
|
||||
output_level = 0;
|
||||
for (FileMetaData* f : vstorage_->LevelFiles(0)) {
|
||||
// Find the first file marked for compaction. Ignore the last file
|
||||
for (size_t loop = 0; loop + 1 < sorted_runs_.size(); loop++) {
|
||||
SortedRun* sr = &sorted_runs_[loop];
|
||||
if (sr->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
FileMetaData* f = vstorage_->LevelFiles(0)[loop];
|
||||
if (f->marked_for_compaction) {
|
||||
compact = true;
|
||||
}
|
||||
if (compact) {
|
||||
start_level_inputs.files.push_back(f);
|
||||
start_index =
|
||||
static_cast<int>(loop); // Consider this as the first candidate.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start_index < 0) {
|
||||
// Either no file marked, or they're already being compacted
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t loop = start_index + 1; loop < sorted_runs_.size(); loop++) {
|
||||
SortedRun* sr = &sorted_runs_[loop];
|
||||
if (sr->being_compacted) {
|
||||
break;
|
||||
}
|
||||
|
||||
FileMetaData* f = vstorage_->LevelFiles(0)[loop];
|
||||
start_level_inputs.files.push_back(f);
|
||||
}
|
||||
if (start_level_inputs.size() <= 1) {
|
||||
// If only the last file in L0 is marked for compaction, ignore it
|
||||
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;
|
||||
|
||||
@@ -1019,9 +1033,9 @@ Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_, start_level,
|
||||
1, true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, start_level,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1, true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
|
||||
+107
-4
@@ -69,6 +69,7 @@ class CorruptionTest : public testing::Test {
|
||||
|
||||
~CorruptionTest() override {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
DestroyDB(dbname_, Options());
|
||||
}
|
||||
|
||||
@@ -104,7 +105,7 @@ class CorruptionTest : public testing::Test {
|
||||
ASSERT_OK(::ROCKSDB_NAMESPACE::RepairDB(dbname_, options_));
|
||||
}
|
||||
|
||||
void Build(int n, int flush_every = 0) {
|
||||
void Build(int n, int start, int flush_every) {
|
||||
std::string key_space, value_space;
|
||||
WriteBatch batch;
|
||||
for (int i = 0; i < n; i++) {
|
||||
@@ -113,13 +114,15 @@ class CorruptionTest : public testing::Test {
|
||||
dbi->TEST_FlushMemTable();
|
||||
}
|
||||
//if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
|
||||
Slice key = Key(i, &key_space);
|
||||
Slice key = Key(i + start, &key_space);
|
||||
batch.Clear();
|
||||
batch.Put(key, Value(i, &value_space));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
}
|
||||
}
|
||||
|
||||
void Build(int n, int flush_every = 0) { Build(n, 0, flush_every); }
|
||||
|
||||
void Check(int min_expected, int max_expected) {
|
||||
uint64_t next_expected = 0;
|
||||
uint64_t missed = 0;
|
||||
@@ -565,19 +568,21 @@ static const auto& corruption_modes = {mock::MockTableFactory::kCorruptNone,
|
||||
mock::MockTableFactory::kCorruptKey,
|
||||
mock::MockTableFactory::kCorruptValue};
|
||||
|
||||
TEST_F(CorruptionTest, ParaniodFileChecksOnFlush) {
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
Options options;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
Status s;
|
||||
for (const auto& mode : corruption_modes) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
s = DestroyDB(dbname_, options);
|
||||
std::shared_ptr<mock::MockTableFactory> mock =
|
||||
std::make_shared<mock::MockTableFactory>();
|
||||
options.table_factory = mock;
|
||||
mock->SetCorruptionMode(mode);
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
assert(db_ != nullptr);
|
||||
Build(10);
|
||||
s = db_->Flush(FlushOptions());
|
||||
if (mode == mock::MockTableFactory::kCorruptNone) {
|
||||
@@ -588,18 +593,20 @@ TEST_F(CorruptionTest, ParaniodFileChecksOnFlush) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, ParaniodFileChecksOnCompact) {
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
Options options;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
Status s;
|
||||
for (const auto& mode : corruption_modes) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
s = DestroyDB(dbname_, options);
|
||||
std::shared_ptr<mock::MockTableFactory> mock =
|
||||
std::make_shared<mock::MockTableFactory>();
|
||||
options.table_factory = mock;
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
assert(db_ != nullptr);
|
||||
Build(100, 2);
|
||||
// ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
@@ -614,6 +621,102 @@ TEST_F(CorruptionTest, ParaniodFileChecksOnCompact) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
Options options;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
std::string start, end;
|
||||
assert(db_ != nullptr);
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(3, &start), Key(7, &end)));
|
||||
auto snap = db_->GetSnapshot();
|
||||
ASSERT_NE(snap, nullptr);
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(8, &start), Key(9, &end)));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(2, &start), Key(5, &end)));
|
||||
Build(10);
|
||||
if (do_flush) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
} else {
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
}
|
||||
db_->ReleaseSnapshot(snap);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
Options options;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
assert(db_ != nullptr);
|
||||
Build(10, 0, 0);
|
||||
std::string start, end;
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(5, &start), Key(15, &end)));
|
||||
auto snap = db_->GetSnapshot();
|
||||
ASSERT_NE(snap, nullptr);
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(8, &start), Key(9, &end)));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(12, &start), Key(17, &end)));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(2, &start), Key(4, &end)));
|
||||
Build(10, 10, 0);
|
||||
if (do_flush) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
} else {
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
}
|
||||
db_->ReleaseSnapshot(snap);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
Options options;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
assert(db_ != nullptr);
|
||||
std::string start, end;
|
||||
Build(10);
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(3, &start), Key(7, &end)));
|
||||
auto snap = db_->GetSnapshot();
|
||||
ASSERT_NE(snap, nullptr);
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(6, &start), Key(8, &end)));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(2, &start), Key(5, &end)));
|
||||
if (do_flush) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
} else {
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
|
||||
}
|
||||
db_->ReleaseSnapshot(snap);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+298
-139
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -28,7 +29,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBBasicTest : public DBTestBase {
|
||||
public:
|
||||
DBBasicTest() : DBTestBase("/db_basic_test") {}
|
||||
DBBasicTest() : DBTestBase("/db_basic_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTest, OpenWhenOpen) {
|
||||
@@ -62,6 +63,13 @@ TEST_F(DBBasicTest, UniqueSession) {
|
||||
|
||||
ASSERT_EQ(sid2, sid4);
|
||||
|
||||
// Expected compact format for session ids (see notes in implementation)
|
||||
std::regex expected("[0-9A-Z]{20}");
|
||||
const std::string match("match");
|
||||
EXPECT_EQ(match, std::regex_replace(sid1, expected, match));
|
||||
EXPECT_EQ(match, std::regex_replace(sid2, expected, match));
|
||||
EXPECT_EQ(match, std::regex_replace(sid3, expected, match));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
@@ -101,19 +109,35 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
Close();
|
||||
|
||||
auto verify_one_iter = [&](Iterator* iter) {
|
||||
int count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
++count;
|
||||
}
|
||||
// Always expect two keys: "foo" and "bar"
|
||||
ASSERT_EQ(count, 2);
|
||||
};
|
||||
|
||||
auto verify_all_iters = [&]() {
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
verify_one_iter(iter);
|
||||
delete iter;
|
||||
|
||||
std::vector<Iterator*> iters;
|
||||
ASSERT_OK(db_->NewIterators(ReadOptions(),
|
||||
{dbfull()->DefaultColumnFamily()}, &iters));
|
||||
ASSERT_EQ(static_cast<uint64_t>(1), iters.size());
|
||||
verify_one_iter(iters[0]);
|
||||
delete iters[0];
|
||||
};
|
||||
|
||||
auto options = CurrentOptions();
|
||||
assert(options.env == env_);
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
int count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
++count;
|
||||
}
|
||||
ASSERT_EQ(count, 2);
|
||||
delete iter;
|
||||
verify_all_iters();
|
||||
Close();
|
||||
|
||||
// Reopen and flush memtable.
|
||||
@@ -124,6 +148,7 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
verify_all_iters();
|
||||
ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
|
||||
}
|
||||
|
||||
@@ -575,6 +600,7 @@ TEST_F(DBBasicTest, IdentityAcrossRestarts2) {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTest, Snapshot) {
|
||||
env_->SetMockSleep();
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
do {
|
||||
@@ -590,7 +616,7 @@ TEST_F(DBBasicTest, Snapshot) {
|
||||
Put(0, "foo", "0v2");
|
||||
Put(1, "foo", "1v2");
|
||||
|
||||
env_->addon_time_.fetch_add(1);
|
||||
env_->MockSleepForSeconds(1);
|
||||
|
||||
const Snapshot* s2 = db_->GetSnapshot();
|
||||
ASSERT_EQ(2U, GetNumSnapshots());
|
||||
@@ -646,60 +672,77 @@ TEST_F(DBBasicTest, Snapshot) {
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBBasicTest, CompactBetweenSnapshots) {
|
||||
class DBBasicMultiConfigs : public DBBasicTest,
|
||||
public ::testing::WithParamInterface<int> {
|
||||
public:
|
||||
DBBasicMultiConfigs() { option_config_ = GetParam(); }
|
||||
|
||||
static std::vector<int> GenerateOptionConfigs() {
|
||||
std::vector<int> option_configs;
|
||||
for (int option_config = kDefault; option_config < kEnd; ++option_config) {
|
||||
if (!ShouldSkipOptions(option_config, kSkipFIFOCompaction)) {
|
||||
option_configs.push_back(option_config);
|
||||
}
|
||||
}
|
||||
return option_configs;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicMultiConfigs, CompactBetweenSnapshots) {
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
do {
|
||||
Options options = CurrentOptions(options_override);
|
||||
options.disable_auto_compactions = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Random rnd(301);
|
||||
FillLevels("a", "z", 1);
|
||||
Options options = CurrentOptions(options_override);
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Random rnd(301);
|
||||
FillLevels("a", "z", 1);
|
||||
|
||||
Put(1, "foo", "first");
|
||||
const Snapshot* snapshot1 = db_->GetSnapshot();
|
||||
Put(1, "foo", "second");
|
||||
Put(1, "foo", "third");
|
||||
Put(1, "foo", "fourth");
|
||||
const Snapshot* snapshot2 = db_->GetSnapshot();
|
||||
Put(1, "foo", "fifth");
|
||||
Put(1, "foo", "sixth");
|
||||
Put(1, "foo", "first");
|
||||
const Snapshot* snapshot1 = db_->GetSnapshot();
|
||||
Put(1, "foo", "second");
|
||||
Put(1, "foo", "third");
|
||||
Put(1, "foo", "fourth");
|
||||
const Snapshot* snapshot2 = db_->GetSnapshot();
|
||||
Put(1, "foo", "fifth");
|
||||
Put(1, "foo", "sixth");
|
||||
|
||||
// All entries (including duplicates) exist
|
||||
// before any compaction or flush is triggered.
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1),
|
||||
"[ sixth, fifth, fourth, third, second, first ]");
|
||||
ASSERT_EQ("sixth", Get(1, "foo"));
|
||||
ASSERT_EQ("fourth", Get(1, "foo", snapshot2));
|
||||
ASSERT_EQ("first", Get(1, "foo", snapshot1));
|
||||
// All entries (including duplicates) exist
|
||||
// before any compaction or flush is triggered.
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1),
|
||||
"[ sixth, fifth, fourth, third, second, first ]");
|
||||
ASSERT_EQ("sixth", Get(1, "foo"));
|
||||
ASSERT_EQ("fourth", Get(1, "foo", snapshot2));
|
||||
ASSERT_EQ("first", Get(1, "foo", snapshot1));
|
||||
|
||||
// After a flush, "second", "third" and "fifth" should
|
||||
// be removed
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth, fourth, first ]");
|
||||
// After a flush, "second", "third" and "fifth" should
|
||||
// be removed
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth, fourth, first ]");
|
||||
|
||||
// after we release the snapshot1, only two values left
|
||||
db_->ReleaseSnapshot(snapshot1);
|
||||
FillLevels("a", "z", 1);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
|
||||
nullptr);
|
||||
// after we release the snapshot1, only two values left
|
||||
db_->ReleaseSnapshot(snapshot1);
|
||||
FillLevels("a", "z", 1);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr);
|
||||
|
||||
// We have only one valid snapshot snapshot2. Since snapshot1 is
|
||||
// not valid anymore, "first" should be removed by a compaction.
|
||||
ASSERT_EQ("sixth", Get(1, "foo"));
|
||||
ASSERT_EQ("fourth", Get(1, "foo", snapshot2));
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth, fourth ]");
|
||||
// We have only one valid snapshot snapshot2. Since snapshot1 is
|
||||
// not valid anymore, "first" should be removed by a compaction.
|
||||
ASSERT_EQ("sixth", Get(1, "foo"));
|
||||
ASSERT_EQ("fourth", Get(1, "foo", snapshot2));
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth, fourth ]");
|
||||
|
||||
// after we release the snapshot2, only one value should be left
|
||||
db_->ReleaseSnapshot(snapshot2);
|
||||
FillLevels("a", "z", 1);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
|
||||
nullptr);
|
||||
ASSERT_EQ("sixth", Get(1, "foo"));
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth ]");
|
||||
} while (ChangeOptions(kSkipFIFOCompaction));
|
||||
// after we release the snapshot2, only one value should be left
|
||||
db_->ReleaseSnapshot(snapshot2);
|
||||
FillLevels("a", "z", 1);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr);
|
||||
ASSERT_EQ("sixth", Get(1, "foo"));
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth ]");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBBasicMultiConfigs, DBBasicMultiConfigs,
|
||||
::testing::ValuesIn(DBBasicMultiConfigs::GenerateOptionConfigs()));
|
||||
|
||||
TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
Options options = CurrentOptions();
|
||||
Close();
|
||||
@@ -2356,7 +2399,7 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
DBBasicTestMultiGet(std::string test_dir, int num_cfs, bool compressed_cache,
|
||||
bool uncompressed_cache, bool _compression_enabled,
|
||||
bool _fill_cache, uint32_t compression_parallel_threads)
|
||||
: DBTestBase(test_dir) {
|
||||
: DBTestBase(test_dir, /*env_do_fsync=*/true) {
|
||||
compression_enabled_ = _compression_enabled;
|
||||
fill_cache_ = _fill_cache;
|
||||
|
||||
@@ -2812,12 +2855,11 @@ class DeadlineFS;
|
||||
|
||||
class DeadlineRandomAccessFile : public FSRandomAccessFileWrapper {
|
||||
public:
|
||||
DeadlineRandomAccessFile(DeadlineFS& fs, SpecialEnv* env,
|
||||
DeadlineRandomAccessFile(DeadlineFS& fs,
|
||||
std::unique_ptr<FSRandomAccessFile>& file)
|
||||
: FSRandomAccessFileWrapper(file.get()),
|
||||
fs_(fs),
|
||||
file_(std::move(file)),
|
||||
env_(env) {}
|
||||
file_(std::move(file)) {}
|
||||
|
||||
IOStatus Read(uint64_t offset, size_t len, const IOOptions& opts,
|
||||
Slice* result, char* scratch,
|
||||
@@ -2829,18 +2871,22 @@ class DeadlineRandomAccessFile : public FSRandomAccessFileWrapper {
|
||||
private:
|
||||
DeadlineFS& fs_;
|
||||
std::unique_ptr<FSRandomAccessFile> file_;
|
||||
SpecialEnv* env_;
|
||||
};
|
||||
|
||||
class DeadlineFS : public FileSystemWrapper {
|
||||
public:
|
||||
explicit DeadlineFS(SpecialEnv* env)
|
||||
// The error_on_delay parameter specifies whether a IOStatus::TimedOut()
|
||||
// status should be returned after delaying the IO to exceed the timeout,
|
||||
// or to simply delay but return success anyway. The latter mimics the
|
||||
// behavior of PosixFileSystem, which does not enforce any timeout
|
||||
explicit DeadlineFS(SpecialEnv* env, bool error_on_delay)
|
||||
: FileSystemWrapper(FileSystem::Default()),
|
||||
delay_idx_(0),
|
||||
deadline_(std::chrono::microseconds::zero()),
|
||||
io_timeout_(std::chrono::microseconds::zero()),
|
||||
env_(env),
|
||||
timedout_(false),
|
||||
ignore_deadline_(false) {}
|
||||
ignore_deadline_(false),
|
||||
error_on_delay_(error_on_delay) {}
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
@@ -2850,100 +2896,111 @@ class DeadlineFS : public FileSystemWrapper {
|
||||
IOStatus s;
|
||||
|
||||
s = target()->NewRandomAccessFile(fname, opts, &file, dbg);
|
||||
result->reset(new DeadlineRandomAccessFile(*this, env_, file));
|
||||
result->reset(new DeadlineRandomAccessFile(*this, file));
|
||||
|
||||
int delay;
|
||||
const std::chrono::microseconds deadline = GetDeadline();
|
||||
if (deadline.count()) {
|
||||
AssertDeadline(deadline, opts.io_options);
|
||||
const std::chrono::microseconds io_timeout = GetIOTimeout();
|
||||
if (deadline.count() || io_timeout.count()) {
|
||||
AssertDeadline(deadline, io_timeout, opts.io_options);
|
||||
}
|
||||
if (ShouldDelay(&delay, &s)) {
|
||||
env_->SleepForMicroseconds(delay);
|
||||
}
|
||||
return s;
|
||||
return ShouldDelay(opts.io_options);
|
||||
}
|
||||
|
||||
// Set a vector of {IO counter, delay in microseconds, return status} tuples
|
||||
// that control when to inject a delay and duration of the delay
|
||||
void SetDelaySequence(
|
||||
const std::chrono::microseconds deadline,
|
||||
const std::vector<std::tuple<int, int, IOStatus>>&& seq) {
|
||||
int total_delay = 0;
|
||||
for (auto& seq_iter : seq) {
|
||||
// Ensure no individual delay is > 500ms
|
||||
ASSERT_LT(std::get<1>(seq_iter), 500000);
|
||||
total_delay += std::get<1>(seq_iter);
|
||||
}
|
||||
// ASSERT total delay is < 1s. This is mainly to keep the test from
|
||||
// timing out in CI test frameworks
|
||||
ASSERT_LT(total_delay, 1000000);
|
||||
delay_seq_ = seq;
|
||||
delay_idx_ = 0;
|
||||
void SetDelayTrigger(const std::chrono::microseconds deadline,
|
||||
const std::chrono::microseconds io_timeout,
|
||||
const int trigger) {
|
||||
delay_trigger_ = trigger;
|
||||
io_count_ = 0;
|
||||
deadline_ = deadline;
|
||||
io_timeout_ = io_timeout;
|
||||
timedout_ = false;
|
||||
}
|
||||
|
||||
// Increment the IO counter and return a delay in microseconds
|
||||
bool ShouldDelay(int* delay, IOStatus* s) {
|
||||
if (!ignore_deadline_ && delay_idx_ < delay_seq_.size() &&
|
||||
std::get<0>(delay_seq_[delay_idx_]) == io_count_++) {
|
||||
*delay = std::get<1>(delay_seq_[delay_idx_]);
|
||||
*s = std::get<2>(delay_seq_[delay_idx_]);
|
||||
delay_idx_++;
|
||||
timedout_ = true;
|
||||
return true;
|
||||
IOStatus ShouldDelay(const IOOptions& opts) {
|
||||
if (!deadline_.count() && !io_timeout_.count()) {
|
||||
return IOStatus::OK();
|
||||
}
|
||||
*s = IOStatus::OK();
|
||||
return false;
|
||||
if (!ignore_deadline_ && delay_trigger_ == io_count_++) {
|
||||
env_->SleepForMicroseconds(static_cast<int>(opts.timeout.count() + 1));
|
||||
timedout_ = true;
|
||||
if (error_on_delay_) {
|
||||
return IOStatus::TimedOut();
|
||||
}
|
||||
}
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
const std::chrono::microseconds GetDeadline() {
|
||||
return ignore_deadline_ ? std::chrono::microseconds::zero() : deadline_;
|
||||
}
|
||||
|
||||
const std::chrono::microseconds GetIOTimeout() {
|
||||
return ignore_deadline_ ? std::chrono::microseconds::zero() : io_timeout_;
|
||||
}
|
||||
|
||||
bool TimedOut() { return timedout_; }
|
||||
|
||||
void IgnoreDeadline(bool ignore) { ignore_deadline_ = ignore; }
|
||||
|
||||
void AssertDeadline(const std::chrono::microseconds deadline,
|
||||
const std::chrono::microseconds io_timeout,
|
||||
const IOOptions& opts) const {
|
||||
// Give a leeway of +- 10us as it can take some time for the Get/
|
||||
// MultiGet call to reach here, in order to avoid false alarms
|
||||
std::chrono::microseconds now =
|
||||
std::chrono::microseconds(env_->NowMicros());
|
||||
if (deadline - now != opts.timeout) {
|
||||
ASSERT_EQ(deadline - now, opts.timeout);
|
||||
std::chrono::microseconds timeout;
|
||||
if (deadline.count()) {
|
||||
timeout = deadline - now;
|
||||
if (io_timeout.count()) {
|
||||
timeout = std::min(timeout, io_timeout);
|
||||
}
|
||||
} else {
|
||||
timeout = io_timeout;
|
||||
}
|
||||
if (opts.timeout != timeout) {
|
||||
ASSERT_EQ(timeout, opts.timeout);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::tuple<int, int, IOStatus>> delay_seq_;
|
||||
size_t delay_idx_;
|
||||
// The number of IOs to trigger the delay after
|
||||
int delay_trigger_;
|
||||
// Current IO count
|
||||
int io_count_;
|
||||
// ReadOptions deadline for the Get/MultiGet/Iterator
|
||||
std::chrono::microseconds deadline_;
|
||||
// ReadOptions io_timeout for the Get/MultiGet/Iterator
|
||||
std::chrono::microseconds io_timeout_;
|
||||
SpecialEnv* env_;
|
||||
// Flag to indicate whether we injected a delay
|
||||
bool timedout_;
|
||||
// Temporarily ignore deadlines/timeouts
|
||||
bool ignore_deadline_;
|
||||
// Return IOStatus::TimedOut() or IOStatus::OK()
|
||||
bool error_on_delay_;
|
||||
};
|
||||
|
||||
IOStatus DeadlineRandomAccessFile::Read(uint64_t offset, size_t len,
|
||||
const IOOptions& opts, Slice* result,
|
||||
char* scratch,
|
||||
IODebugContext* dbg) const {
|
||||
int delay;
|
||||
const std::chrono::microseconds deadline = fs_.GetDeadline();
|
||||
const std::chrono::microseconds io_timeout = fs_.GetIOTimeout();
|
||||
IOStatus s;
|
||||
if (deadline.count()) {
|
||||
fs_.AssertDeadline(deadline, opts);
|
||||
}
|
||||
if (fs_.ShouldDelay(&delay, &s)) {
|
||||
env_->SleepForMicroseconds(delay);
|
||||
if (deadline.count() || io_timeout.count()) {
|
||||
fs_.AssertDeadline(deadline, io_timeout, opts);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = FSRandomAccessFileWrapper::Read(offset, len, opts, result, scratch,
|
||||
dbg);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = fs_.ShouldDelay(opts);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -2951,23 +3008,23 @@ IOStatus DeadlineRandomAccessFile::MultiRead(FSReadRequest* reqs,
|
||||
size_t num_reqs,
|
||||
const IOOptions& options,
|
||||
IODebugContext* dbg) {
|
||||
int delay;
|
||||
const std::chrono::microseconds deadline = fs_.GetDeadline();
|
||||
const std::chrono::microseconds io_timeout = fs_.GetIOTimeout();
|
||||
IOStatus s;
|
||||
if (deadline.count()) {
|
||||
fs_.AssertDeadline(deadline, options);
|
||||
}
|
||||
if (fs_.ShouldDelay(&delay, &s)) {
|
||||
env_->SleepForMicroseconds(delay);
|
||||
if (deadline.count() || io_timeout.count()) {
|
||||
fs_.AssertDeadline(deadline, io_timeout, options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = FSRandomAccessFileWrapper::MultiRead(reqs, num_reqs, options, dbg);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = fs_.ShouldDelay(options);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// A test class for intercepting random reads and injecting artificial
|
||||
// delays. Used for testing the deadline/timeout feature
|
||||
// delays. Used for testing the MultiGet deadline feature
|
||||
class DBBasicTestMultiGetDeadline : public DBBasicTestMultiGet {
|
||||
public:
|
||||
DBBasicTestMultiGetDeadline()
|
||||
@@ -2983,23 +3040,25 @@ class DBBasicTestMultiGetDeadline : public DBBasicTestMultiGet {
|
||||
if (i < num_ok) {
|
||||
EXPECT_OK(statuses[i]);
|
||||
} else {
|
||||
EXPECT_EQ(statuses[i], Status::TimedOut());
|
||||
if (statuses[i] != Status::TimedOut()) {
|
||||
EXPECT_EQ(statuses[i], Status::TimedOut());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
std::shared_ptr<DeadlineFS> fs = std::make_shared<DeadlineFS>(env_);
|
||||
std::shared_ptr<DeadlineFS> fs = std::make_shared<DeadlineFS>(env_, false);
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = cache;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.env = env.get();
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
ReopenWithColumnFamilies(GetCFNames(), options);
|
||||
|
||||
// Test the non-batched version of MultiGet with multiple column
|
||||
@@ -3020,9 +3079,8 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
|
||||
ReadOptions ro;
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
// Delay the first IO by 200ms
|
||||
fs->SetDelaySequence(
|
||||
ro.deadline, {std::tuple<int, int, IOStatus>{0, 20000, IOStatus::OK()}});
|
||||
// Delay the first IO
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 0);
|
||||
|
||||
std::vector<Status> statuses = dbfull()->MultiGet(ro, cfs, keys, &values);
|
||||
// The first key is successful because we check after the lookup, but
|
||||
@@ -3047,8 +3105,7 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
keys[i] = Slice(key_str[i].data(), key_str[i].size());
|
||||
}
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(
|
||||
ro.deadline, {std::tuple<int, int, IOStatus>{1, 20000, IOStatus::OK()}});
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 1);
|
||||
statuses = dbfull()->MultiGet(ro, cfs, keys, &values);
|
||||
CheckStatus(statuses, 3);
|
||||
|
||||
@@ -3062,8 +3119,7 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(
|
||||
ro.deadline, {std::tuple<int, int, IOStatus>{0, 20000, IOStatus::OK()}});
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 0);
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 2);
|
||||
@@ -3078,8 +3134,7 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(
|
||||
ro.deadline, {std::tuple<int, int, IOStatus>{2, 20000, IOStatus::OK()}});
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 2);
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 6);
|
||||
@@ -3093,8 +3148,7 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(
|
||||
ro.deadline, {std::tuple<int, int, IOStatus>{3, 20000, IOStatus::OK()}});
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 3);
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 8);
|
||||
@@ -3120,8 +3174,7 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(
|
||||
ro.deadline, {std::tuple<int, int, IOStatus>{1, 20000, IOStatus::OK()}});
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 1);
|
||||
dbfull()->MultiGet(ro, handles_[0], keys.size(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 64);
|
||||
@@ -3155,13 +3208,18 @@ TEST_F(DBBasicTest, ManifestWriteFailure) {
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, PointLookupDeadline) {
|
||||
std::shared_ptr<DeadlineFS> fs = std::make_shared<DeadlineFS>(env_);
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
|
||||
// A test class for intercepting random reads and injecting artificial
|
||||
// delays. Used for testing the deadline/timeout feature
|
||||
class DBBasicTestDeadline
|
||||
: public DBBasicTest,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {};
|
||||
|
||||
TEST_P(DBBasicTestDeadline, PointLookupDeadline) {
|
||||
std::shared_ptr<DeadlineFS> fs = std::make_shared<DeadlineFS>(env_, true);
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
|
||||
bool set_deadline = std::get<0>(GetParam());
|
||||
bool set_timeout = std::get<1>(GetParam());
|
||||
|
||||
// Since we call SetTimeElapseOnlySleep, Close() later on may not work
|
||||
// properly for the DB that's opened by the DBTestBase constructor.
|
||||
Close();
|
||||
for (int option_config = kDefault; option_config < kEnd; ++option_config) {
|
||||
if (ShouldSkipOptions(option_config, kSkipPlainTable | kSkipMmapReads)) {
|
||||
continue;
|
||||
@@ -3174,7 +3232,6 @@ TEST_F(DBBasicTest, PointLookupDeadline) {
|
||||
options.env = env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
Cache* block_cache = nullptr;
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
// Fileter block reads currently don't cause the request to get
|
||||
// aborted on a read timeout, so its possible those block reads
|
||||
// may get issued even if the deadline is past
|
||||
@@ -3198,6 +3255,7 @@ TEST_F(DBBasicTest, PointLookupDeadline) {
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
Reopen(options);
|
||||
|
||||
if (options.table_factory &&
|
||||
@@ -3224,10 +3282,13 @@ TEST_F(DBBasicTest, PointLookupDeadline) {
|
||||
// and cause the Get() to fail.
|
||||
while (timedout) {
|
||||
ReadOptions ro;
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(
|
||||
ro.deadline, {std::tuple<int, int, IOStatus>{
|
||||
io_deadline_trigger, 20000, IOStatus::TimedOut()}});
|
||||
if (set_deadline) {
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
}
|
||||
if (set_timeout) {
|
||||
ro.io_timeout = std::chrono::microseconds{5000};
|
||||
}
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, io_deadline_trigger);
|
||||
|
||||
block_cache->SetCapacity(0);
|
||||
block_cache->SetCapacity(1048576);
|
||||
@@ -3243,11 +3304,109 @@ TEST_F(DBBasicTest, PointLookupDeadline) {
|
||||
io_deadline_trigger++;
|
||||
}
|
||||
// Reset the delay sequence in order to avoid false alarms during Reopen
|
||||
fs->SetDelaySequence(std::chrono::microseconds::zero(), {});
|
||||
fs->SetDelayTrigger(std::chrono::microseconds::zero(),
|
||||
std::chrono::microseconds::zero(), 0);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBBasicTestDeadline, IteratorDeadline) {
|
||||
std::shared_ptr<DeadlineFS> fs = std::make_shared<DeadlineFS>(env_, true);
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
|
||||
bool set_deadline = std::get<0>(GetParam());
|
||||
bool set_timeout = std::get<1>(GetParam());
|
||||
|
||||
for (int option_config = kDefault; option_config < kEnd; ++option_config) {
|
||||
if (ShouldSkipOptions(option_config, kSkipPlainTable | kSkipMmapReads)) {
|
||||
continue;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
if (options.use_direct_reads) {
|
||||
continue;
|
||||
}
|
||||
options.env = env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
Cache* block_cache = nullptr;
|
||||
// DB open will create table readers unless we reduce the table cache
|
||||
// capacity.
|
||||
// SanitizeOptions will set max_open_files to minimum of 20. Table cache
|
||||
// is allocated with max_open_files - 10 as capacity. So override
|
||||
// max_open_files to 11 so table cache capacity will become 1. This will
|
||||
// prevent file open during DB open and force the file to be opened
|
||||
// during MultiGet
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
|
||||
int* max_open_files = (int*)arg;
|
||||
*max_open_files = 11;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
Reopen(options);
|
||||
|
||||
if (options.table_factory &&
|
||||
!strcmp(options.table_factory->Name(),
|
||||
BlockBasedTableFactory::kName.c_str())) {
|
||||
BlockBasedTableFactory* bbtf =
|
||||
static_cast<BlockBasedTableFactory*>(options.table_factory.get());
|
||||
block_cache = bbtf->table_options().block_cache.get();
|
||||
}
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 400; ++i) {
|
||||
std::string key = "k" + ToString(i);
|
||||
Put(key, rnd.RandomString(100));
|
||||
}
|
||||
Flush();
|
||||
|
||||
bool timedout = true;
|
||||
// A timeout will be forced when the IO counter reaches this value
|
||||
int io_deadline_trigger = 0;
|
||||
// Keep incrementing io_deadline_trigger and call Get() until there is an
|
||||
// iteration that doesn't cause a timeout. This ensures that we cover
|
||||
// all file reads in the point lookup path that can potentially timeout
|
||||
while (timedout) {
|
||||
ReadOptions ro;
|
||||
if (set_deadline) {
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
}
|
||||
if (set_timeout) {
|
||||
ro.io_timeout = std::chrono::microseconds{5000};
|
||||
}
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, io_deadline_trigger);
|
||||
|
||||
block_cache->SetCapacity(0);
|
||||
block_cache->SetCapacity(1048576);
|
||||
|
||||
Iterator* iter = dbfull()->NewIterator(ro);
|
||||
int count = 0;
|
||||
iter->Seek("k50");
|
||||
while (iter->Valid() && count++ < 100) {
|
||||
iter->Next();
|
||||
}
|
||||
if (fs->TimedOut()) {
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_EQ(iter->status(), Status::TimedOut());
|
||||
} else {
|
||||
timedout = false;
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
delete iter;
|
||||
io_deadline_trigger++;
|
||||
}
|
||||
// Reset the delay sequence in order to avoid false alarms during Reopen
|
||||
fs->SetDelayTrigger(std::chrono::microseconds::zero(),
|
||||
std::chrono::microseconds::zero(), 0);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
// Param 0: If true, set read_options.deadline
|
||||
// Param 1: If true, set read_options.io_timeout
|
||||
INSTANTIATE_TEST_CASE_P(DBBasicTestDeadline, DBBasicTestDeadline,
|
||||
::testing::Values(std::make_tuple(true, false),
|
||||
std::make_tuple(false, true),
|
||||
std::make_tuple(true, true)));
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
@@ -34,7 +34,8 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
const size_t kNumBlocks = 10;
|
||||
const size_t kValueSize = 100;
|
||||
|
||||
DBBlockCacheTest() : DBTestBase("/db_block_cache_test") {}
|
||||
DBBlockCacheTest()
|
||||
: DBTestBase("/db_block_cache_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
BlockBasedTableOptions GetTableOptions() {
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
@@ -23,7 +23,8 @@ using BFP = BloomFilterPolicy;
|
||||
|
||||
class DBBloomFilterTest : public DBTestBase {
|
||||
public:
|
||||
DBBloomFilterTest() : DBTestBase("/db_bloom_filter_test") {}
|
||||
DBBloomFilterTest()
|
||||
: DBTestBase("/db_bloom_filter_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
class DBBloomFilterTestWithParam : public DBTestBase,
|
||||
@@ -36,7 +37,8 @@ class DBBloomFilterTestWithParam : public DBTestBase,
|
||||
uint32_t format_version_;
|
||||
|
||||
public:
|
||||
DBBloomFilterTestWithParam() : DBTestBase("/db_bloom_filter_tests") {}
|
||||
DBBloomFilterTestWithParam()
|
||||
: DBTestBase("/db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
|
||||
~DBBloomFilterTestWithParam() override {}
|
||||
|
||||
@@ -1039,7 +1041,7 @@ class DBBloomFilterTestVaryPrefixAndFormatVer
|
||||
|
||||
public:
|
||||
DBBloomFilterTestVaryPrefixAndFormatVer()
|
||||
: DBTestBase("/db_bloom_filter_tests") {}
|
||||
: DBTestBase("/db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
|
||||
~DBBloomFilterTestVaryPrefixAndFormatVer() override {}
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ static std::string NEW_VALUE = "NewValue";
|
||||
|
||||
class DBTestCompactionFilter : public DBTestBase {
|
||||
public:
|
||||
DBTestCompactionFilter() : DBTestBase("/db_compaction_filter_test") {}
|
||||
DBTestCompactionFilter()
|
||||
: DBTestBase("/db_compaction_filter_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
// Param variant of DBTestBase::ChangeCompactOptions
|
||||
@@ -126,22 +127,6 @@ class SkipEvenFilter : public CompactionFilter {
|
||||
const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "DelayFilter"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
};
|
||||
|
||||
class ConditionalFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ConditionalFilter(const std::string* filtered_value)
|
||||
@@ -248,20 +233,6 @@ class SkipEvenFilterFactory : public CompactionFilterFactory {
|
||||
const char* Name() const override { return "SkipEvenFilterFactory"; }
|
||||
};
|
||||
|
||||
class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
const char* Name() const override { return "DelayFilterFactory"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
};
|
||||
|
||||
class ConditionalFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit ConditionalFilterFactory(const Slice& filtered_value)
|
||||
@@ -336,8 +307,9 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber, handles_[1]));
|
||||
read_options, &arena, &range_del_agg, kMaxSequenceNumber, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
@@ -426,8 +398,9 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber, handles_[1]));
|
||||
read_options, &arena, &range_del_agg, kMaxSequenceNumber, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
@@ -644,8 +617,9 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* snapshots */);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber));
|
||||
read_options, &arena, &range_del_agg, kMaxSequenceNumber));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
|
||||
+285
-33
@@ -26,14 +26,16 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBCompactionTest : public DBTestBase {
|
||||
public:
|
||||
DBCompactionTest() : DBTestBase("/db_compaction_test") {}
|
||||
DBCompactionTest()
|
||||
: DBTestBase("/db_compaction_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
class DBCompactionTestWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<uint32_t, bool>> {
|
||||
public:
|
||||
DBCompactionTestWithParam() : DBTestBase("/db_compaction_test") {
|
||||
DBCompactionTestWithParam()
|
||||
: DBTestBase("/db_compaction_test", /*env_do_fsync=*/true) {
|
||||
max_subcompactions_ = std::get<0>(GetParam());
|
||||
exclusive_manual_compaction_ = std::get<1>(GetParam());
|
||||
}
|
||||
@@ -52,6 +54,16 @@ class DBCompactionDirectIOTest : public DBCompactionTest,
|
||||
DBCompactionDirectIOTest() : DBCompactionTest() {}
|
||||
};
|
||||
|
||||
// Param = true : target level is non-empty
|
||||
// Param = false: level between target level and source level
|
||||
// is not empty.
|
||||
class ChangeLevelConflictsWithAuto
|
||||
: public DBCompactionTest,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
ChangeLevelConflictsWithAuto() : DBCompactionTest() {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
class FlushedFileCollector : public EventListener {
|
||||
@@ -3588,6 +3600,76 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, NoCompactBottomLevelFilesWithDeletions) {
|
||||
// bottom-level files may contain deletions due to snapshots protecting the
|
||||
// deleted keys. Once the snapshot is released, we should see files with many
|
||||
// such deletions undergo single-file compactions. But when disabling auto
|
||||
// compactions, it shouldn't be triggered which may causing too many
|
||||
// background jobs.
|
||||
const int kNumKeysPerFile = 1024;
|
||||
const int kNumLevelFiles = 4;
|
||||
const int kValueSize = 128;
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_file_num_compaction_trigger = kNumLevelFiles;
|
||||
// inflate it a bit to account for key/metadata overhead
|
||||
options.target_file_size_base = 120 * kNumKeysPerFile * kValueSize / 100;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
|
||||
}
|
||||
if (i == kNumLevelFiles - 1) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
// delete every other key after grabbing a snapshot, so these deletions
|
||||
// and the keys they cover can't be dropped until after the snapshot is
|
||||
// released.
|
||||
for (int j = 0; j < kNumLevelFiles * kNumKeysPerFile; j += 2) {
|
||||
ASSERT_OK(Delete(Key(j)));
|
||||
}
|
||||
}
|
||||
Flush();
|
||||
if (i < kNumLevelFiles - 1) {
|
||||
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
}
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr);
|
||||
ASSERT_EQ(kNumLevelFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
std::vector<LiveFileMetaData> pre_release_metadata, post_release_metadata;
|
||||
db_->GetLiveFilesMetaData(&pre_release_metadata);
|
||||
// just need to bump seqnum so ReleaseSnapshot knows the newest key in the SST
|
||||
// files does not need to be preserved in case of a future snapshot.
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
|
||||
// release snapshot and no compaction should be triggered.
|
||||
std::atomic<int> num_compactions{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:Start",
|
||||
[&](void* /*arg*/) { num_compactions.fetch_add(1); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, num_compactions);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
db_->GetLiveFilesMetaData(&post_release_metadata);
|
||||
ASSERT_EQ(pre_release_metadata.size(), post_release_metadata.size());
|
||||
for (size_t i = 0; i < pre_release_metadata.size(); ++i) {
|
||||
const auto& pre_file = pre_release_metadata[i];
|
||||
const auto& post_file = post_release_metadata[i];
|
||||
ASSERT_EQ(1, pre_file.level);
|
||||
ASSERT_EQ(1, post_file.level);
|
||||
// each file is same as before with deletion markers/deleted keys.
|
||||
ASSERT_EQ(post_file.size, pre_file.size);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
const int kNumKeysPerFile = 32;
|
||||
const int kNumLevelFiles = 2;
|
||||
@@ -3597,10 +3679,11 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
options.compression = kNoCompression;
|
||||
options.ttl = 24 * 60 * 60; // 24 hours
|
||||
options.max_open_files = -1;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -3627,7 +3710,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ("0,2,0,2", FilesPerLevel());
|
||||
|
||||
env_->addon_time_.fetch_add(36 * 60 * 60); // 36 hours
|
||||
env_->MockSleepForSeconds(36 * 60 * 60); // 36 hours
|
||||
ASSERT_EQ("0,2,0,2", FilesPerLevel());
|
||||
|
||||
// Just do a simple write + flush so that the Ttl expired files get
|
||||
@@ -3647,7 +3730,8 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
|
||||
// Test dynamically changing ttl.
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
@@ -3675,7 +3759,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
|
||||
// Move time forward by 12 hours, and make sure that compaction still doesn't
|
||||
// trigger as ttl is set to 24 hours.
|
||||
env_->addon_time_.fetch_add(12 * 60 * 60);
|
||||
env_->MockSleepForSeconds(12 * 60 * 60);
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -3698,6 +3782,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
env_->SetMockSleep();
|
||||
const int kValueSize = 100;
|
||||
|
||||
for (bool if_restart : {false, true}) {
|
||||
@@ -3728,10 +3813,10 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
}
|
||||
});
|
||||
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int ttl_compactions = 0;
|
||||
@@ -3759,7 +3844,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
&level_to_files);
|
||||
uint64_t oldest_time = level_to_files[0][0].oldest_ancester_time;
|
||||
// Add 1 hour and do another flush.
|
||||
env_->addon_time_.fetch_add(1 * 60 * 60);
|
||||
env_->MockSleepForSeconds(1 * 60 * 60);
|
||||
for (int i = 101; i <= 200; ++i) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
|
||||
}
|
||||
@@ -3767,13 +3852,13 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
MoveFilesToLevel(6);
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
|
||||
env_->addon_time_.fetch_add(1 * 60 * 60);
|
||||
env_->MockSleepForSeconds(1 * 60 * 60);
|
||||
// Add two L4 files with key ranges: [1 .. 50], [51 .. 150].
|
||||
for (int i = 1; i <= 50; ++i) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
|
||||
}
|
||||
Flush();
|
||||
env_->addon_time_.fetch_add(1 * 60 * 60);
|
||||
env_->MockSleepForSeconds(1 * 60 * 60);
|
||||
for (int i = 51; i <= 150; ++i) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
|
||||
}
|
||||
@@ -3781,7 +3866,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
MoveFilesToLevel(4);
|
||||
ASSERT_EQ("0,0,0,0,2,0,2", FilesPerLevel());
|
||||
|
||||
env_->addon_time_.fetch_add(1 * 60 * 60);
|
||||
env_->MockSleepForSeconds(1 * 60 * 60);
|
||||
// Add one L1 file with key range: [26, 75].
|
||||
for (int i = 26; i <= 75; ++i) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
|
||||
@@ -3811,7 +3896,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
// 4. A TTL compaction happens between L5 and L6 files. Ouptut in L6.
|
||||
|
||||
// Add 25 hours and do a write
|
||||
env_->addon_time_.fetch_add(25 * 60 * 60);
|
||||
env_->MockSleepForSeconds(25 * 60 * 60);
|
||||
|
||||
ASSERT_OK(Put(Key(1), "1"));
|
||||
if (if_restart) {
|
||||
@@ -3827,7 +3912,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
&level_to_files);
|
||||
ASSERT_EQ(oldest_time, level_to_files[6][0].oldest_ancester_time);
|
||||
|
||||
env_->addon_time_.fetch_add(25 * 60 * 60);
|
||||
env_->MockSleepForSeconds(25 * 60 * 60);
|
||||
ASSERT_OK(Put(Key(2), "1"));
|
||||
if (if_restart) {
|
||||
Reopen(options);
|
||||
@@ -3844,6 +3929,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
env_->SetMockSleep();
|
||||
const int kNumKeysPerFile = 32;
|
||||
const int kNumLevelFiles = 2;
|
||||
const int kValueSize = 100;
|
||||
@@ -3875,10 +3961,10 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
}
|
||||
});
|
||||
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int periodic_compactions = 0;
|
||||
@@ -3906,7 +3992,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
ASSERT_EQ(0, periodic_compactions);
|
||||
|
||||
// Add 50 hours and do a write
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
env_->MockSleepForSeconds(50 * 60 * 60);
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -3919,7 +4005,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
ASSERT_EQ("0,3", FilesPerLevel());
|
||||
|
||||
// Add another 50 hours and do another write
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
env_->MockSleepForSeconds(50 * 60 * 60);
|
||||
ASSERT_OK(Put("b", "2"));
|
||||
if (if_restart) {
|
||||
Reopen(options);
|
||||
@@ -3933,7 +4019,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
ASSERT_EQ(5, periodic_compactions);
|
||||
|
||||
// Add another 50 hours and do another write
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
env_->MockSleepForSeconds(50 * 60 * 60);
|
||||
ASSERT_OK(Put("c", "3"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -3957,10 +4043,11 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
const int kValueSize = 100;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int periodic_compactions = 0;
|
||||
@@ -4008,7 +4095,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
|
||||
set_file_creation_time_to_zero = false;
|
||||
// Forward the clock by 2 days.
|
||||
env_->addon_time_.fetch_add(2 * 24 * 60 * 60);
|
||||
env_->MockSleepForSeconds(2 * 24 * 60 * 60);
|
||||
options.periodic_compaction_seconds = 1 * 24 * 60 * 60; // 1 day
|
||||
|
||||
Reopen(options);
|
||||
@@ -4029,10 +4116,11 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
options.ttl = 10 * 60 * 60; // 10 hours
|
||||
options.periodic_compaction_seconds = 48 * 60 * 60; // 2 days
|
||||
options.max_open_files = -1; // needed for both periodic and ttl compactions
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int periodic_compactions = 0;
|
||||
@@ -4066,7 +4154,7 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
ASSERT_EQ(0, ttl_compactions);
|
||||
|
||||
// Add some time greater than periodic_compaction_time.
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
env_->MockSleepForSeconds(50 * 60 * 60);
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -4076,7 +4164,7 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
ASSERT_EQ(0, ttl_compactions);
|
||||
|
||||
// Add a little more time than ttl
|
||||
env_->addon_time_.fetch_add(11 * 60 * 60);
|
||||
env_->MockSleepForSeconds(11 * 60 * 60);
|
||||
ASSERT_OK(Put("b", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -4088,7 +4176,7 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
ASSERT_EQ(3, ttl_compactions);
|
||||
|
||||
// Add some time greater than periodic_compaction_time.
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
env_->MockSleepForSeconds(50 * 60 * 60);
|
||||
ASSERT_OK(Put("c", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -4121,9 +4209,10 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
|
||||
|
||||
Options options = CurrentOptions();
|
||||
TestCompactionFilter test_compaction_filter;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
env_->addon_time_.store(0);
|
||||
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
enum CompactionFilterType {
|
||||
kUseCompactionFilter,
|
||||
@@ -4174,7 +4263,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
|
||||
ASSERT_EQ(0, periodic_compactions);
|
||||
|
||||
// Add 31 days and do a write
|
||||
env_->addon_time_.fetch_add(31 * 24 * 60 * 60);
|
||||
env_->MockSleepForSeconds(31 * 24 * 60 * 60);
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
@@ -4808,7 +4897,8 @@ INSTANTIATE_TEST_CASE_P(DBCompactionDirectIOTest, DBCompactionDirectIOTest,
|
||||
class CompactionPriTest : public DBTestBase,
|
||||
public testing::WithParamInterface<uint32_t> {
|
||||
public:
|
||||
CompactionPriTest() : DBTestBase("/compaction_pri_test") {
|
||||
CompactionPriTest()
|
||||
: DBTestBase("/compaction_pri_test", /*env_do_fsync=*/true) {
|
||||
compaction_pri_ = GetParam();
|
||||
}
|
||||
|
||||
@@ -5435,7 +5525,169 @@ TEST_F(DBCompactionTest, UpdateUniversalSubCompactionTest) {
|
||||
ASSERT_TRUE(has_compaction);
|
||||
}
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
TEST_P(ChangeLevelConflictsWithAuto, TestConflict) {
|
||||
// A `CompactRange()` may race with an automatic compaction, we'll need
|
||||
// to make sure it doesn't corrupte the data.
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("bar", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
|
||||
{
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
}
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel(0));
|
||||
|
||||
// Run a qury to refitting to level 1 while another thread writing to
|
||||
// the same level.
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
// The first two dependencies ensure the foreground creates an L0 file
|
||||
// between the background compaction's L0->L1 and its L1->L2.
|
||||
{
|
||||
"DBImpl::CompactRange:BeforeRefit:1",
|
||||
"AutoCompactionFinished1",
|
||||
},
|
||||
{
|
||||
"AutoCompactionFinished2",
|
||||
"DBImpl::CompactRange:BeforeRefit:2",
|
||||
},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::thread auto_comp([&] {
|
||||
TEST_SYNC_POINT("AutoCompactionFinished1");
|
||||
ASSERT_OK(Put("bar", "v2"));
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("bar", "v3"));
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
TEST_SYNC_POINT("AutoCompactionFinished2");
|
||||
});
|
||||
|
||||
{
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = GetParam() ? 1 : 0;
|
||||
// This should return non-OK, but it's more important for the test to
|
||||
// make sure that the DB is not corrupted.
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
}
|
||||
auto_comp.join();
|
||||
// Refitting didn't happen.
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Write something to DB just make sure that consistency check didn't
|
||||
// fail and make the DB readable.
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ChangeLevelConflictsWithAuto,
|
||||
ChangeLevelConflictsWithAuto, testing::Bool());
|
||||
|
||||
TEST_F(DBCompactionTest, ChangeLevelCompactRangeConflictsWithManual) {
|
||||
// A `CompactRange()` with `change_level == true` needs to execute its final
|
||||
// step, `ReFitLevel()`, in isolation. Previously there was a bug where
|
||||
// refitting could target the same level as an ongoing manual compaction,
|
||||
// leading to overlapping files in that level.
|
||||
//
|
||||
// This test ensures that case is not possible by verifying any manual
|
||||
// compaction issued during the `ReFitLevel()` phase fails with
|
||||
// `Status::Incomplete`.
|
||||
Options options = CurrentOptions();
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.num_levels = 3;
|
||||
Reopen(options);
|
||||
|
||||
// Setup an LSM with three levels populated.
|
||||
Random rnd(301);
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
{
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
}
|
||||
ASSERT_EQ("0,0,2", FilesPerLevel(0));
|
||||
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("1,1,2", FilesPerLevel(0));
|
||||
|
||||
// The background thread will refit L2->L1 while the
|
||||
// foreground thread will try to simultaneously compact L0->L1.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
// The first two dependencies ensure the foreground creates an L0 file
|
||||
// between the background compaction's L0->L1 and its L1->L2.
|
||||
{
|
||||
"DBImpl::RunManualCompaction()::1",
|
||||
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
|
||||
"PutFG",
|
||||
},
|
||||
{
|
||||
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
|
||||
"FlushedFG",
|
||||
"DBImpl::RunManualCompaction()::2",
|
||||
},
|
||||
// The next two dependencies ensure the foreground invokes
|
||||
// `CompactRange()` while the background is refitting. The
|
||||
// foreground's `CompactRange()` is guaranteed to attempt an L0->L1
|
||||
// as we set it up with an empty memtable and a new L0 file.
|
||||
{
|
||||
"DBImpl::CompactRange:PreRefitLevel",
|
||||
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
|
||||
"CompactFG",
|
||||
},
|
||||
{
|
||||
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
|
||||
"CompactedFG",
|
||||
"DBImpl::CompactRange:PostRefitLevel",
|
||||
},
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread refit_level_thread([&] {
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 1;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:PutFG");
|
||||
// Make sure we have something new to compact in the foreground.
|
||||
// Note key 1 is carefully chosen as it ensures the file we create here
|
||||
// overlaps with one of the files being refitted L2->L1 in the background.
|
||||
// If we chose key 0, the file created here would not overlap.
|
||||
ASSERT_OK(Put(Key(1), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:FlushedFG");
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:CompactFG");
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
.IsIncomplete());
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
|
||||
"CompactedFG");
|
||||
refit_level_thread.join();
|
||||
}
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class DBTestDynamicLevel : public DBTestBase {
|
||||
public:
|
||||
DBTestDynamicLevel() : DBTestBase("/db_dynamic_level_test") {}
|
||||
DBTestDynamicLevel()
|
||||
: DBTestBase("/db_dynamic_level_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBEncryptionTest : public DBTestBase {
|
||||
public:
|
||||
DBEncryptionTest() : DBTestBase("/db_encryption_test") {}
|
||||
DBEncryptionTest()
|
||||
: DBTestBase("/db_encryption_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
@@ -116,7 +116,7 @@ Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// long as deletions are disabled (so the below loop must terminate).
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while (disable_delete_obsolete_files_ > 0 &&
|
||||
pending_purge_obsolete_files_ > 0) {
|
||||
(pending_purge_obsolete_files_ > 0 || bg_purge_scheduled_ > 0)) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBFlushTest : public DBTestBase {
|
||||
public:
|
||||
DBFlushTest() : DBTestBase("/db_flush_test") {}
|
||||
DBFlushTest() : DBTestBase("/db_flush_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
class DBFlushDirectIOTest : public DBFlushTest,
|
||||
|
||||
+143
-121
@@ -65,6 +65,7 @@
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/persistent_stats_history.h"
|
||||
#include "monitoring/stats_dump_scheduler.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -150,8 +151,9 @@ 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_.env->GetFileSystem()),
|
||||
io_tracer_(std::make_shared<IOTracer>()),
|
||||
immutable_db_options_(initial_db_options_),
|
||||
fs_(immutable_db_options_.fs, io_tracer_),
|
||||
mutable_db_options_(initial_db_options_),
|
||||
stats_(immutable_db_options_.statistics.get()),
|
||||
mutex_(stats_, env_, DB_MUTEX_WAIT_MICROS,
|
||||
@@ -196,13 +198,17 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
unable_to_release_oldest_log_(false),
|
||||
num_running_ingest_file_(0),
|
||||
#ifndef ROCKSDB_LITE
|
||||
wal_manager_(immutable_db_options_, file_options_, seq_per_batch),
|
||||
wal_manager_(immutable_db_options_, file_options_, io_tracer_,
|
||||
seq_per_batch),
|
||||
#endif // ROCKSDB_LITE
|
||||
event_logger_(immutable_db_options_.info_log.get()),
|
||||
bg_work_paused_(0),
|
||||
bg_compaction_paused_(0),
|
||||
refitting_level_(false),
|
||||
opened_successfully_(false),
|
||||
#ifndef ROCKSDB_LITE
|
||||
stats_dump_scheduler_(nullptr),
|
||||
#endif // ROCKSDB_LITE
|
||||
two_write_queues_(options.two_write_queues),
|
||||
manual_wal_flush_(options.manual_wal_flush),
|
||||
// last_sequencee_ is always maintained by the main queue that also writes
|
||||
@@ -229,7 +235,8 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
// !batch_per_trx_ implies seq_per_batch_ because it is only unset for
|
||||
// WriteUnprepared, which should use seq_per_batch_.
|
||||
assert(batch_per_txn_ || seq_per_batch_);
|
||||
env_->GetAbsolutePath(dbname, &db_absolute_path_);
|
||||
// TODO: Check for an error here
|
||||
env_->GetAbsolutePath(dbname, &db_absolute_path_).PermitUncheckedError();
|
||||
|
||||
// Reserve ten files or so for other uses and give the rest to TableCache.
|
||||
// Give a large number for setting of "infinite" open files.
|
||||
@@ -244,7 +251,8 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
|
||||
versions_.reset(new VersionSet(dbname_, &immutable_db_options_, file_options_,
|
||||
table_cache_.get(), write_buffer_manager_,
|
||||
&write_controller_, &block_cache_tracer_));
|
||||
&write_controller_, &block_cache_tracer_,
|
||||
io_tracer_));
|
||||
column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
|
||||
@@ -294,7 +302,7 @@ Status DBImpl::Resume() {
|
||||
// 4. Schedule compactions if needed for all the CFs. This is needed as the
|
||||
// flush in the prior step might have been a no-op for some CFs, which
|
||||
// means a new super version wouldn't have been installed
|
||||
Status DBImpl::ResumeImpl() {
|
||||
Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
mutex_.AssertHeld();
|
||||
WaitForBackgroundWork();
|
||||
|
||||
@@ -356,7 +364,7 @@ Status DBImpl::ResumeImpl() {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
mutex_.Unlock();
|
||||
s = AtomicFlushMemTables(cfds, flush_opts, FlushReason::kErrorRecovery);
|
||||
s = AtomicFlushMemTables(cfds, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -365,7 +373,7 @@ Status DBImpl::ResumeImpl() {
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
s = FlushMemTable(cfd, flush_opts, FlushReason::kErrorRecovery);
|
||||
s = FlushMemTable(cfd, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!s.ok()) {
|
||||
@@ -437,14 +445,12 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Shutdown: canceling all background work");
|
||||
|
||||
if (thread_dump_stats_ != nullptr) {
|
||||
thread_dump_stats_->cancel();
|
||||
thread_dump_stats_.reset();
|
||||
}
|
||||
if (thread_persist_stats_ != nullptr) {
|
||||
thread_persist_stats_->cancel();
|
||||
thread_persist_stats_.reset();
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (stats_dump_scheduler_ != nullptr) {
|
||||
stats_dump_scheduler_->Unregister(this);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (!shutting_down_.load(std::memory_order_acquire) &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed) &&
|
||||
@@ -496,7 +502,7 @@ Status DBImpl::CloseHelper() {
|
||||
env_->UnSchedule(this, Env::Priority::BOTTOM);
|
||||
int compactions_unscheduled = env_->UnSchedule(this, Env::Priority::LOW);
|
||||
int flushes_unscheduled = env_->UnSchedule(this, Env::Priority::HIGH);
|
||||
Status ret;
|
||||
Status ret = Status::OK();
|
||||
mutex_.Lock();
|
||||
bg_bottom_compaction_scheduled_ -= bottom_compactions_unscheduled;
|
||||
bg_compaction_scheduled_ -= compactions_unscheduled;
|
||||
@@ -608,7 +614,8 @@ Status DBImpl::CloseHelper() {
|
||||
versions_.reset();
|
||||
mutex_.Unlock();
|
||||
if (db_lock_ != nullptr) {
|
||||
env_->UnlockFile(db_lock_);
|
||||
// TODO: Check for unlock error
|
||||
env_->UnlockFile(db_lock_).PermitUncheckedError();
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Shutdown complete");
|
||||
@@ -627,7 +634,7 @@ Status DBImpl::CloseHelper() {
|
||||
|
||||
if (immutable_db_options_.info_log && own_info_log_) {
|
||||
Status s = immutable_db_options_.info_log->Close();
|
||||
if (ret.ok()) {
|
||||
if (!s.ok() && ret.ok()) {
|
||||
ret = s;
|
||||
}
|
||||
}
|
||||
@@ -646,7 +653,7 @@ Status DBImpl::CloseImpl() { return CloseHelper(); }
|
||||
DBImpl::~DBImpl() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
CloseHelper();
|
||||
CloseHelper().PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,36 +684,19 @@ void DBImpl::PrintStatistics() {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::StartTimedTasks() {
|
||||
unsigned int stats_dump_period_sec = 0;
|
||||
unsigned int stats_persist_period_sec = 0;
|
||||
void DBImpl::StartStatsDumpScheduler() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
stats_dump_period_sec = mutable_db_options_.stats_dump_period_sec;
|
||||
if (stats_dump_period_sec > 0) {
|
||||
if (!thread_dump_stats_) {
|
||||
// In case of many `DB::Open()` in rapid succession we can have all
|
||||
// threads dumping at once, which causes severe lock contention in
|
||||
// jemalloc. Ensure successive `DB::Open()`s are staggered by at least
|
||||
// one second in the common case.
|
||||
static std::atomic<uint64_t> stats_dump_threads_started(0);
|
||||
thread_dump_stats_.reset(new ROCKSDB_NAMESPACE::RepeatableThread(
|
||||
[this]() { DBImpl::DumpStats(); }, "dump_st", env_,
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond,
|
||||
stats_dump_threads_started.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_dump_period_sec) *
|
||||
kMicrosInSecond));
|
||||
}
|
||||
}
|
||||
stats_persist_period_sec = mutable_db_options_.stats_persist_period_sec;
|
||||
if (stats_persist_period_sec > 0) {
|
||||
if (!thread_persist_stats_) {
|
||||
thread_persist_stats_.reset(new ROCKSDB_NAMESPACE::RepeatableThread(
|
||||
[this]() { DBImpl::PersistStats(); }, "pst_st", env_,
|
||||
static_cast<uint64_t>(stats_persist_period_sec) * kMicrosInSecond));
|
||||
}
|
||||
}
|
||||
stats_dump_scheduler_ = StatsDumpScheduler::Default();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::StartStatsDumpScheduler:Init",
|
||||
&stats_dump_scheduler_);
|
||||
}
|
||||
|
||||
stats_dump_scheduler_->Register(this,
|
||||
mutable_db_options_.stats_dump_period_sec,
|
||||
mutable_db_options_.stats_persist_period_sec);
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
// esitmate the total size of stats_history_
|
||||
@@ -732,7 +722,9 @@ void DBImpl::PersistStats() {
|
||||
if (shutdown_initiated_) {
|
||||
return;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::PersistStats:StartRunning");
|
||||
uint64_t now_seconds = env_->NowMicros() / kMicrosInSecond;
|
||||
|
||||
Statistics* statistics = immutable_db_options_.statistics.get();
|
||||
if (!statistics) {
|
||||
return;
|
||||
@@ -823,6 +815,7 @@ void DBImpl::PersistStats() {
|
||||
" bytes, slice count: %" ROCKSDB_PRIszt,
|
||||
stats_history_size, stats_history_.size());
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::PersistStats:End");
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -877,6 +870,7 @@ void DBImpl::DumpStats() {
|
||||
if (shutdown_initiated_) {
|
||||
return;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:StartRunning");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
default_cf_internal_stats_->GetStringProperty(
|
||||
@@ -1084,45 +1078,22 @@ Status DBImpl::SetDBOptions(
|
||||
}
|
||||
|
||||
if (new_options.stats_dump_period_sec !=
|
||||
mutable_db_options_.stats_dump_period_sec) {
|
||||
if (thread_dump_stats_) {
|
||||
mutable_db_options_.stats_dump_period_sec ||
|
||||
new_options.stats_persist_period_sec !=
|
||||
mutable_db_options_.stats_persist_period_sec) {
|
||||
if (stats_dump_scheduler_) {
|
||||
mutex_.Unlock();
|
||||
thread_dump_stats_->cancel();
|
||||
stats_dump_scheduler_->Unregister(this);
|
||||
mutex_.Lock();
|
||||
}
|
||||
if (new_options.stats_dump_period_sec > 0) {
|
||||
// In case many DBs have `stats_dump_period_sec` enabled in rapid
|
||||
// succession, we can have all threads dumping at once, which causes
|
||||
// severe lock contention in jemalloc. Ensure successive enabling of
|
||||
// `stats_dump_period_sec` are staggered by at least one second in the
|
||||
// common case.
|
||||
static std::atomic<uint64_t> stats_dump_threads_started(0);
|
||||
thread_dump_stats_.reset(new ROCKSDB_NAMESPACE::RepeatableThread(
|
||||
[this]() { DBImpl::DumpStats(); }, "dump_st", env_,
|
||||
static_cast<uint64_t>(new_options.stats_dump_period_sec) *
|
||||
kMicrosInSecond,
|
||||
stats_dump_threads_started.fetch_add(1) %
|
||||
static_cast<uint64_t>(new_options.stats_dump_period_sec) *
|
||||
kMicrosInSecond));
|
||||
} else {
|
||||
thread_dump_stats_.reset();
|
||||
}
|
||||
}
|
||||
if (new_options.stats_persist_period_sec !=
|
||||
mutable_db_options_.stats_persist_period_sec) {
|
||||
if (thread_persist_stats_) {
|
||||
if (new_options.stats_dump_period_sec > 0 ||
|
||||
new_options.stats_persist_period_sec > 0) {
|
||||
mutex_.Unlock();
|
||||
thread_persist_stats_->cancel();
|
||||
stats_dump_scheduler_->Register(this,
|
||||
new_options.stats_dump_period_sec,
|
||||
new_options.stats_persist_period_sec);
|
||||
mutex_.Lock();
|
||||
}
|
||||
if (new_options.stats_persist_period_sec > 0) {
|
||||
thread_persist_stats_.reset(new ROCKSDB_NAMESPACE::RepeatableThread(
|
||||
[this]() { DBImpl::PersistStats(); }, "pst_st", env_,
|
||||
static_cast<uint64_t>(new_options.stats_persist_period_sec) *
|
||||
kMicrosInSecond));
|
||||
} else {
|
||||
thread_persist_stats_.reset();
|
||||
}
|
||||
}
|
||||
write_controller_.set_max_delayed_write_rate(
|
||||
new_options.delayed_write_rate);
|
||||
@@ -1364,9 +1335,12 @@ bool DBImpl::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) {
|
||||
}
|
||||
}
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family, bool allow_unprepared_value) {
|
||||
InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family,
|
||||
bool allow_unprepared_value) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
cfd = default_cf_handle_->cfd();
|
||||
@@ -1378,9 +1352,8 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
mutex_.Lock();
|
||||
SuperVersion* super_version = cfd->GetSuperVersion()->Ref();
|
||||
mutex_.Unlock();
|
||||
ReadOptions roptions;
|
||||
return NewInternalIterator(roptions, cfd, super_version, arena, range_del_agg,
|
||||
sequence, allow_unprepared_value);
|
||||
return NewInternalIterator(read_options, cfd, super_version, arena,
|
||||
range_del_agg, sequence, allow_unprepared_value);
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePurge() {
|
||||
@@ -1578,19 +1551,32 @@ Status DBImpl::Get(const ReadOptions& read_options,
|
||||
return s;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class GetWithTimestampReadCallback : public ReadCallback {
|
||||
public:
|
||||
explicit GetWithTimestampReadCallback(SequenceNumber seq)
|
||||
: ReadCallback(seq) {}
|
||||
bool IsVisibleFullCheck(SequenceNumber seq) override {
|
||||
return seq <= max_visible_seq_;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
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();
|
||||
const Comparator* ucmp = get_impl_options.column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
GetWithTimestampReadCallback read_cb(0); // Will call Refresh
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (ts_sz > 0) {
|
||||
assert(read_options.timestamp);
|
||||
assert(read_options.timestamp->size() == ucmp->timestamp_size());
|
||||
assert(read_options.timestamp->size() == ts_sz);
|
||||
} else {
|
||||
assert(!read_options.timestamp);
|
||||
}
|
||||
@@ -1656,6 +1642,12 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
snapshot = get_impl_options.callback->max_visible_seq();
|
||||
}
|
||||
}
|
||||
// If timestamp is used, we use read callback to ensure <key,t,s> is returned
|
||||
// only if t <= read_opts.timestamp and s <= snapshot.
|
||||
if (ts_sz > 0 && !get_impl_options.callback) {
|
||||
read_cb.Refresh(snapshot);
|
||||
get_impl_options.callback = &read_cb;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:3");
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:4");
|
||||
|
||||
@@ -1673,9 +1665,6 @@ 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
|
||||
@@ -2705,10 +2694,6 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
return NewErrorIterator(
|
||||
Status::NotSupported("Managed iterator is not supported anymore."));
|
||||
}
|
||||
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(
|
||||
@@ -2829,10 +2814,10 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
sv->version_number, read_callback, this, cfd, allow_blob,
|
||||
read_options.snapshot != nullptr ? false : allow_refresh);
|
||||
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot,
|
||||
/* allow_unprepared_value */ true);
|
||||
InternalIterator* internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
|
||||
return db_iter;
|
||||
@@ -3042,6 +3027,20 @@ FileSystem* DBImpl::GetFileSystem() const {
|
||||
return immutable_db_options_.fs.get();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status DBImpl::StartIOTrace(Env* env, const TraceOptions& trace_options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) {
|
||||
return io_tracer_->StartIOTrace(env, trace_options, std::move(trace_writer));
|
||||
}
|
||||
|
||||
Status DBImpl::EndIOTrace() {
|
||||
io_tracer_->EndIOTrace();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Options DBImpl::GetOptions(ColumnFamilyHandle* column_family) const {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
@@ -3691,13 +3690,29 @@ Status DBImpl::GetDbSessionId(std::string& session_id) const {
|
||||
}
|
||||
|
||||
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();
|
||||
// GenerateUniqueId() generates an identifier that has a negligible
|
||||
// probability of being duplicated, ~128 bits of entropy
|
||||
std::string uuid = env_->GenerateUniqueId();
|
||||
|
||||
// Hash and reformat that down to a more compact format, 20 characters
|
||||
// in base-36 ([0-9A-Z]), which is ~103 bits of entropy, which is enough
|
||||
// to expect no collisions across a billion servers each opening DBs
|
||||
// a million times (~2^50). Benefits vs. raw unique id:
|
||||
// * Save ~ dozen bytes per SST file
|
||||
// * Shorter shared backup file names (some platforms have low limits)
|
||||
// * Visually distinct from DB id format
|
||||
uint64_t a = NPHash64(uuid.data(), uuid.size(), 1234U);
|
||||
uint64_t b = NPHash64(uuid.data(), uuid.size(), 5678U);
|
||||
db_session_id_.resize(20);
|
||||
static const char* const base36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
size_t i = 0;
|
||||
for (; i < 10U; ++i, a /= 36U) {
|
||||
db_session_id_[i] = base36[a % 36];
|
||||
}
|
||||
for (; i < 20U; ++i, b /= 36U) {
|
||||
db_session_id_[i] = base36[b % 36];
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::SetDbSessionId", &db_session_id_);
|
||||
}
|
||||
|
||||
// Default implementation -- returns not supported status
|
||||
@@ -3772,7 +3787,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
// log file and prevents cleanup and directory removal
|
||||
soptions.info_log.reset();
|
||||
// Ignore error in case directory does not exist
|
||||
env->GetChildren(dbname, &filenames);
|
||||
env->GetChildren(dbname, &filenames).PermitUncheckedError();
|
||||
|
||||
FileLock* lock;
|
||||
const std::string lockname = LockFileName(dbname);
|
||||
@@ -3794,7 +3809,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
} else {
|
||||
del = env->DeleteFile(path_to_delete);
|
||||
}
|
||||
if (result.ok() && !del.ok()) {
|
||||
if (!del.ok() && result.ok()) {
|
||||
result = del;
|
||||
}
|
||||
}
|
||||
@@ -3817,12 +3832,13 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
std::string table_path = path + "/" + fname;
|
||||
Status del = DeleteDBFile(&soptions, table_path, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/false);
|
||||
if (result.ok() && !del.ok()) {
|
||||
if (!del.ok() && result.ok()) {
|
||||
result = del;
|
||||
}
|
||||
}
|
||||
}
|
||||
env->DeleteDir(path);
|
||||
// TODO: Should we return an error if we cannot delete the directory?
|
||||
env->DeleteDir(path).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3845,12 +3861,13 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
Status del =
|
||||
DeleteDBFile(&soptions, archivedir + "/" + file, archivedir,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
|
||||
if (result.ok() && !del.ok()) {
|
||||
if (!del.ok() && result.ok()) {
|
||||
result = del;
|
||||
}
|
||||
}
|
||||
}
|
||||
env->DeleteDir(archivedir);
|
||||
// Ignore error in case dir contains other files
|
||||
env->DeleteDir(archivedir).PermitUncheckedError();
|
||||
}
|
||||
|
||||
// Delete log files in the WAL dir
|
||||
@@ -3861,22 +3878,26 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
DeleteDBFile(&soptions, LogFileName(soptions.wal_dir, number),
|
||||
soptions.wal_dir, /*force_bg=*/false,
|
||||
/*force_fg=*/!wal_in_db_path);
|
||||
if (result.ok() && !del.ok()) {
|
||||
if (!del.ok() && result.ok()) {
|
||||
result = del;
|
||||
}
|
||||
}
|
||||
}
|
||||
env->DeleteDir(soptions.wal_dir);
|
||||
// Ignore error in case dir contains other files
|
||||
env->DeleteDir(soptions.wal_dir).PermitUncheckedError();
|
||||
}
|
||||
|
||||
env->UnlockFile(lock); // Ignore error since state is already gone
|
||||
env->DeleteFile(lockname);
|
||||
// Ignore error since state is already gone
|
||||
env->UnlockFile(lock).PermitUncheckedError();
|
||||
env->DeleteFile(lockname).PermitUncheckedError();
|
||||
|
||||
// sst_file_manager holds a ref to the logger. Make sure the logger is
|
||||
// gone before trying to remove the directory.
|
||||
soptions.sst_file_manager.reset();
|
||||
|
||||
env->DeleteDir(dbname); // Ignore error in case dir contains other files
|
||||
// Ignore error in case dir contains other files
|
||||
env->DeleteDir(dbname).PermitUncheckedError();
|
||||
;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -3918,7 +3939,7 @@ Status DBImpl::WriteOptionsFile(bool need_mutex_lock,
|
||||
std::string file_name =
|
||||
TempOptionsFileName(GetName(), versions_->NewFileNumber());
|
||||
Status s = PersistRocksDBOptions(db_options, cf_names, cf_opts, file_name,
|
||||
GetFileSystem());
|
||||
fs_.get());
|
||||
|
||||
if (s.ok()) {
|
||||
s = RenameTempFileToOptionsFile(file_name);
|
||||
@@ -4011,7 +4032,8 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
|
||||
}
|
||||
|
||||
if (0 == disable_delete_obsolete_files_) {
|
||||
DeleteObsoleteOptionsFiles();
|
||||
// TODO: Should we check for errors here?
|
||||
DeleteObsoleteOptionsFiles().PermitUncheckedError();
|
||||
}
|
||||
return s;
|
||||
#else
|
||||
@@ -4265,7 +4287,7 @@ Status DBImpl::IngestExternalFiles(
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(arg.column_family)->cfd();
|
||||
ingestion_jobs.emplace_back(
|
||||
env_, versions_.get(), cfd, immutable_db_options_, file_options_,
|
||||
&snapshots_, arg.options, &directories_, &event_logger_);
|
||||
&snapshots_, arg.options, &directories_, &event_logger_, io_tracer_);
|
||||
}
|
||||
std::vector<std::pair<bool, Status>> exec_results;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
@@ -4539,7 +4561,7 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
auto cfd = cfh->cfd();
|
||||
ImportColumnFamilyJob import_job(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, file_options_,
|
||||
import_options, metadata.files);
|
||||
import_options, metadata.files, io_tracer_);
|
||||
|
||||
SuperVersionContext dummy_sv_ctx(/* create_superversion */ true);
|
||||
VersionEdit dummy_edit;
|
||||
|
||||
+57
-25
@@ -55,6 +55,7 @@
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "trace_replay/block_cache_tracer.h"
|
||||
#include "trace_replay/io_tracer.h"
|
||||
#include "trace_replay/trace_replay.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/hash.h"
|
||||
@@ -69,6 +70,10 @@ class ArenaWrappedDBIter;
|
||||
class InMemoryStatsHistoryIterator;
|
||||
class MemTable;
|
||||
class PersistentStatsHistoryIterator;
|
||||
class StatsDumpScheduler;
|
||||
#ifndef NDEBUG
|
||||
class StatsDumpTestScheduler;
|
||||
#endif // !NDEBUG
|
||||
class TableCache;
|
||||
class TaskLimiterToken;
|
||||
class Version;
|
||||
@@ -445,6 +450,13 @@ class DBImpl : public DB {
|
||||
using DB::EndBlockCacheTrace;
|
||||
Status EndBlockCacheTrace() override;
|
||||
|
||||
using DB::StartIOTrace;
|
||||
Status StartIOTrace(Env* env, const TraceOptions& options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) override;
|
||||
|
||||
using DB::EndIOTrace;
|
||||
Status EndIOTrace() override;
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
virtual Status GetPropertiesOfAllTables(
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -594,8 +606,10 @@ class DBImpl : public DB {
|
||||
// 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.
|
||||
// @param read_options Must outlive the returned iterator.
|
||||
InternalIterator* NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
const ReadOptions& read_options, Arena* arena,
|
||||
RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool allow_unprepared_value = false);
|
||||
|
||||
@@ -721,10 +735,14 @@ class DBImpl : public DB {
|
||||
|
||||
const WriteController& write_controller() { return write_controller_; }
|
||||
|
||||
InternalIterator* NewInternalIterator(
|
||||
const ReadOptions&, ColumnFamilyData* cfd, SuperVersion* super_version,
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
bool allow_unprepared_value);
|
||||
// @param read_options Must outlive the returned iterator.
|
||||
InternalIterator* NewInternalIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version,
|
||||
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
|
||||
@@ -974,9 +992,7 @@ class DBImpl : public DB {
|
||||
int TEST_BGCompactionsAllowed() const;
|
||||
int TEST_BGFlushesAllowed() const;
|
||||
size_t TEST_GetWalPreallocateBlockSize(uint64_t write_buffer_size) const;
|
||||
void TEST_WaitForDumpStatsRun(std::function<void()> callback) const;
|
||||
void TEST_WaitForPersistStatsRun(std::function<void()> callback) const;
|
||||
bool TEST_IsPersistentStatsEnabled() const;
|
||||
void TEST_WaitForStatsDumpRun(std::function<void()> callback) const;
|
||||
size_t TEST_EstimateInMemoryStatsHistorySize() const;
|
||||
|
||||
VersionSet* TEST_GetVersionSet() const { return versions_.get(); }
|
||||
@@ -984,8 +1000,19 @@ class DBImpl : public DB {
|
||||
const std::unordered_set<uint64_t>& TEST_GetFilesGrabbedForPurge() const {
|
||||
return files_grabbed_for_purge_;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
StatsDumpTestScheduler* TEST_GetStatsDumpScheduler() const;
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#endif // NDEBUG
|
||||
|
||||
// persist stats to column family "_persistent_stats"
|
||||
void PersistStats();
|
||||
|
||||
// dump rocksdb.stats to LOG
|
||||
void DumpStats();
|
||||
|
||||
protected:
|
||||
const std::string dbname_;
|
||||
std::string db_id_;
|
||||
@@ -997,8 +1024,9 @@ class DBImpl : public DB {
|
||||
bool own_info_log_;
|
||||
const DBOptions initial_db_options_;
|
||||
Env* const env_;
|
||||
std::shared_ptr<FileSystem> fs_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
const ImmutableDBOptions immutable_db_options_;
|
||||
FileSystemPtr fs_;
|
||||
MutableDBOptions mutable_db_options_;
|
||||
Statistics* stats_;
|
||||
std::unordered_map<std::string, RecoveredTransaction*>
|
||||
@@ -1351,7 +1379,7 @@ class DBImpl : public DB {
|
||||
// Required: DB mutex held
|
||||
Status PersistentStatsProcessFormatVersion();
|
||||
|
||||
Status ResumeImpl();
|
||||
Status ResumeImpl(DBRecoverContext context);
|
||||
|
||||
void MaybeIgnoreError(Status* s) const;
|
||||
|
||||
@@ -1539,6 +1567,10 @@ class DBImpl : public DB {
|
||||
uint64_t* log_used,
|
||||
SequenceNumber* last_sequence, size_t seq_inc);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
// Caller must hold mutex_.
|
||||
void WriteStatusCheckOnLocked(const Status& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
void WriteStatusCheck(const Status& status);
|
||||
|
||||
@@ -1620,18 +1652,12 @@ class DBImpl : public DB {
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Schedule background tasks
|
||||
void StartTimedTasks();
|
||||
void StartStatsDumpScheduler();
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
size_t EstimateInMemoryStatsHistorySize() const;
|
||||
|
||||
// persist stats to column family "_persistent_stats"
|
||||
void PersistStats();
|
||||
|
||||
// dump rocksdb.stats to LOG
|
||||
void DumpStats();
|
||||
|
||||
// Return the minimum empty level that could hold the total data in the
|
||||
// input level. Return the input level, if such level could not be found.
|
||||
int FindMinimumEmptyLevelFitting(ColumnFamilyData* cfd,
|
||||
@@ -1814,7 +1840,14 @@ class DBImpl : public DB {
|
||||
InstrumentedMutex log_write_mutex_;
|
||||
|
||||
std::atomic<bool> shutting_down_;
|
||||
std::atomic<bool> manual_compaction_paused_;
|
||||
|
||||
// If zero, manual compactions are allowed to proceed. If non-zero, manual
|
||||
// compactions may still be running, but will quickly fail with
|
||||
// `Status::Incomplete`. The value indicates how many threads have paused
|
||||
// manual compactions. It is accessed in read mode outside the DB mutex in
|
||||
// compaction code paths.
|
||||
std::atomic<int> manual_compaction_paused_;
|
||||
|
||||
// This condition variable is signaled on these conditions:
|
||||
// * whenever bg_compaction_scheduled_ goes down to 0
|
||||
// * if AnyManualCompaction, whenever a compaction finishes, even if it hasn't
|
||||
@@ -2077,13 +2110,12 @@ class DBImpl : public DB {
|
||||
// Only to be set during initialization
|
||||
std::unique_ptr<PreReleaseCallback> recoverable_state_pre_release_callback_;
|
||||
|
||||
// handle for scheduling stats dumping at fixed intervals
|
||||
// REQUIRES: mutex locked
|
||||
std::unique_ptr<ROCKSDB_NAMESPACE::RepeatableThread> thread_dump_stats_;
|
||||
|
||||
// handle for scheduling stats snapshoting at fixed intervals
|
||||
// REQUIRES: mutex locked
|
||||
std::unique_ptr<ROCKSDB_NAMESPACE::RepeatableThread> thread_persist_stats_;
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Scheduler to run DumpStats() and PersistStats(). Currently, it always use
|
||||
// a global instance from StatsDumpScheduler::Default(). Only in unittest, it
|
||||
// can be overrided by StatsDumpTestSchduler.
|
||||
StatsDumpScheduler* stats_dump_scheduler_;
|
||||
#endif
|
||||
|
||||
// When set, we use a separate queue for writes that don't write to memtable.
|
||||
// In 2PC these are the writes at Prepare phase.
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
// 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 "db/db_impl/db_impl.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
@@ -126,7 +125,12 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
// "number < current_log_number".
|
||||
MarkLogsSynced(current_log_number - 1, true, io_s);
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
if (total_log_size_ > 0) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return io_s;
|
||||
}
|
||||
@@ -155,7 +159,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
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,
|
||||
db_id_, db_session_id_);
|
||||
io_tracer_, db_id_, db_session_id_);
|
||||
FileMetaData file_meta;
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforePickMemtables");
|
||||
@@ -168,7 +172,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
IOStatus io_s = IOStatus::OK();
|
||||
if (logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1) {
|
||||
// If there are more than one column families, we need to make sure that
|
||||
@@ -179,6 +183,10 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// SyncClosedLogs() may unlock and re-lock the db_mutex.
|
||||
io_s = SyncClosedLogs(job_context);
|
||||
s = io_s;
|
||||
if (!io_s.ok() && !io_s.IsShutdownInProgress() &&
|
||||
!io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Skip");
|
||||
}
|
||||
@@ -218,14 +226,22 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// CURRENT file. With current code, it's just difficult to tell. So just
|
||||
// be pessimistic and try write to a new MANIFEST.
|
||||
// TODO: distinguish between MANIFEST write and CURRENT renaming
|
||||
auto err_reason = versions_->io_status().ok()
|
||||
? BackgroundErrorReason::kFlush
|
||||
: BackgroundErrorReason::kManifestWrite;
|
||||
error_handler_.SetBGError(io_s, err_reason);
|
||||
if (!versions_->io_status().ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kManifestWrite);
|
||||
} else if (total_log_size_ > 0) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
} else {
|
||||
// If we got here, then we decided not to care about the i_os status (either
|
||||
// from never needing it or ignoring the flush job status
|
||||
io_s.PermitUncheckedError();
|
||||
}
|
||||
if (s.ok()) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -356,7 +372,7 @@ 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, db_id_, db_session_id_));
|
||||
thread_pri, io_tracer_, db_id_, db_session_id_));
|
||||
jobs.back()->PickMemTable();
|
||||
}
|
||||
|
||||
@@ -590,10 +606,14 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// CURRENT file. With current code, it's just difficult to tell. So just
|
||||
// be pessimistic and try write to a new MANIFEST.
|
||||
// TODO: distinguish between MANIFEST write and CURRENT renaming
|
||||
auto err_reason = versions_->io_status().ok()
|
||||
? BackgroundErrorReason::kFlush
|
||||
: BackgroundErrorReason::kManifestWrite;
|
||||
error_handler_.SetBGError(io_s, err_reason);
|
||||
if (!versions_->io_status().ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kManifestWrite);
|
||||
} else if (total_log_size_ > 0) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
@@ -846,13 +866,20 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
}
|
||||
|
||||
if (options.change_level) {
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:BeforeRefit:1");
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:BeforeRefit:2");
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[RefitLevel] waiting for background threads to stop");
|
||||
DisableManualCompaction();
|
||||
s = PauseBackgroundWork();
|
||||
if (s.ok()) {
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:PreRefitLevel");
|
||||
s = ReFitLevel(cfd, final_output_level, options.target_level);
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:PostRefitLevel");
|
||||
ContinueBackgroundWork();
|
||||
}
|
||||
ContinueBackgroundWork();
|
||||
EnableManualCompaction();
|
||||
}
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
@@ -959,7 +986,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
if (manual_compaction_paused_.load(std::memory_order_acquire)) {
|
||||
if (manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
|
||||
@@ -1051,8 +1078,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_,
|
||||
db_id_, db_session_id_);
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
&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
|
||||
@@ -1180,7 +1207,7 @@ void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
return;
|
||||
}
|
||||
if (c->is_manual_compaction() &&
|
||||
manual_compaction_paused_.load(std::memory_order_acquire)) {
|
||||
manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
return;
|
||||
}
|
||||
Version* current = cfd->current();
|
||||
@@ -1190,43 +1217,7 @@ void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
TEST_SYNC_POINT("DBImpl::NotifyOnCompactionBegin::UnlockMutex");
|
||||
{
|
||||
CompactionJobInfo info{};
|
||||
info.cf_name = cfd->GetName();
|
||||
info.status = st;
|
||||
info.thread_id = env_->GetThreadID();
|
||||
info.job_id = job_id;
|
||||
info.base_input_level = c->start_level();
|
||||
info.output_level = c->output_level();
|
||||
info.stats = job_stats;
|
||||
info.table_properties = c->GetOutputTableProperties();
|
||||
info.compaction_reason = c->compaction_reason();
|
||||
info.compression = c->output_compression();
|
||||
for (size_t i = 0; i < c->num_input_levels(); ++i) {
|
||||
for (const auto fmd : *c->inputs(i)) {
|
||||
const FileDescriptor& desc = fmd->fd;
|
||||
const uint64_t file_number = desc.GetNumber();
|
||||
auto fn = TableFileName(c->immutable_cf_options()->cf_paths,
|
||||
file_number, desc.GetPathId());
|
||||
info.input_files.push_back(fn);
|
||||
info.input_file_infos.push_back(CompactionFileInfo{
|
||||
static_cast<int>(i), file_number, fmd->oldest_blob_file_number});
|
||||
if (info.table_properties.count(fn) == 0) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
auto s = current->GetTableProperties(&tp, fmd, &fn);
|
||||
if (s.ok()) {
|
||||
info.table_properties[fn] = tp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& newf : c->edit()->GetNewFiles()) {
|
||||
const FileMetaData& meta = newf.second;
|
||||
const FileDescriptor& desc = meta.fd;
|
||||
const uint64_t file_number = desc.GetNumber();
|
||||
info.output_files.push_back(TableFileName(
|
||||
c->immutable_cf_options()->cf_paths, file_number, desc.GetPathId()));
|
||||
info.output_file_infos.push_back(CompactionFileInfo{
|
||||
newf.first, file_number, meta.oldest_blob_file_number});
|
||||
}
|
||||
BuildCompactionJobInfo(cfd, c, st, job_stats, job_id, current, &info);
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionBegin(this, info);
|
||||
}
|
||||
@@ -1254,7 +1245,7 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
return;
|
||||
}
|
||||
if (c->is_manual_compaction() &&
|
||||
manual_compaction_paused_.load(std::memory_order_acquire)) {
|
||||
manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
return;
|
||||
}
|
||||
Version* current = cfd->current();
|
||||
@@ -1313,20 +1304,29 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
}
|
||||
|
||||
auto* vstorage = cfd->current()->storage_info();
|
||||
if (to_level > level) {
|
||||
if (level == 0) {
|
||||
return Status::NotSupported(
|
||||
"Cannot change from level 0 to other levels.");
|
||||
}
|
||||
// Check levels are empty for a trivial move
|
||||
for (int l = level + 1; l <= to_level; l++) {
|
||||
if (vstorage->NumLevelFiles(l) > 0) {
|
||||
if (to_level != level) {
|
||||
if (to_level > level) {
|
||||
if (level == 0) {
|
||||
return Status::NotSupported(
|
||||
"Levels between source and target are not empty for a move.");
|
||||
"Cannot change from level 0 to other levels.");
|
||||
}
|
||||
// Check levels are empty for a trivial move
|
||||
for (int l = level + 1; l <= to_level; l++) {
|
||||
if (vstorage->NumLevelFiles(l) > 0) {
|
||||
return Status::NotSupported(
|
||||
"Levels between source and target are not empty for a move.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// to_level < level
|
||||
// Check levels are empty for a trivial move
|
||||
for (int l = to_level; l < level; l++) {
|
||||
if (vstorage->NumLevelFiles(l) > 0) {
|
||||
return Status::NotSupported(
|
||||
"Levels between source and target are not empty for a move.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (to_level != level) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] Before refitting:\n%s", cfd->GetName().c_str(),
|
||||
cfd->current()->DebugString().data());
|
||||
@@ -1615,6 +1615,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
FlushRequest flush_req;
|
||||
{
|
||||
WriteContext context;
|
||||
@@ -1631,7 +1632,11 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
WaitForPendingWrites();
|
||||
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load()) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
if (flush_reason != FlushReason::kErrorRecoveryRetryFlush) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
} else {
|
||||
assert(cfd->imm()->NumNotFlushed() > 0);
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
@@ -1639,7 +1644,8 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
if (immutable_db_options_.persist_stats_to_disk) {
|
||||
if (immutable_db_options_.persist_stats_to_disk &&
|
||||
flush_reason != FlushReason::kErrorRecoveryRetryFlush) {
|
||||
ColumnFamilyData* cfd_stats =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(
|
||||
kPersistentStatsColumnFamilyName);
|
||||
@@ -1668,7 +1674,6 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && !flush_req.empty()) {
|
||||
for (auto& elem : flush_req) {
|
||||
ColumnFamilyData* loop_cfd = elem.first;
|
||||
@@ -1704,8 +1709,10 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
cfds.push_back(iter.first);
|
||||
flush_memtable_ids.push_back(&(iter.second));
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery));
|
||||
s = WaitForFlushMemTables(
|
||||
cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery ||
|
||||
flush_reason == FlushReason::kErrorRecoveryRetryFlush));
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
for (auto* tmp_cfd : cfds) {
|
||||
tmp_cfd->UnrefAndTryDelete();
|
||||
@@ -1763,7 +1770,8 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
}
|
||||
for (auto cfd : cfds) {
|
||||
if (cfd->mem()->IsEmpty() && cached_recoverable_state_empty_.load()) {
|
||||
if ((cfd->mem()->IsEmpty() && cached_recoverable_state_empty_.load()) ||
|
||||
flush_reason == FlushReason::kErrorRecoveryRetryFlush) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
@@ -1806,8 +1814,10 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
for (auto& iter : flush_req) {
|
||||
flush_memtable_ids.push_back(&(iter.second));
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery));
|
||||
s = WaitForFlushMemTables(
|
||||
cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery ||
|
||||
flush_reason == FlushReason::kErrorRecoveryRetryFlush));
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
for (auto* cfd : cfds) {
|
||||
cfd->UnrefAndTryDelete();
|
||||
@@ -1917,6 +1927,13 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
if (!error_handler_.GetRecoveryError().ok()) {
|
||||
break;
|
||||
}
|
||||
// If BGWorkStopped, which indicate that there is a BG error and
|
||||
// 1) soft error but requires no BG work, 2) no in auto_recovery_
|
||||
if (!resuming_from_bg_err && error_handler_.IsBGWorkStopped() &&
|
||||
error_handler_.GetBGError().severity() < Status::Severity::kHardError) {
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
// Number of column families that have been dropped.
|
||||
int num_dropped = 0;
|
||||
// Number of column families that have finished flush.
|
||||
@@ -1965,11 +1982,21 @@ Status DBImpl::EnableAutoCompaction(
|
||||
}
|
||||
|
||||
void DBImpl::DisableManualCompaction() {
|
||||
manual_compaction_paused_.store(true, std::memory_order_release);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
manual_compaction_paused_.fetch_add(1, std::memory_order_release);
|
||||
// Wait for any pending manual compactions to finish (typically through
|
||||
// failing with `Status::Incomplete`) prior to returning. This way we are
|
||||
// guaranteed no pending manual compaction will commit while manual
|
||||
// compactions are "disabled".
|
||||
while (HasPendingManualCompaction()) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::EnableManualCompaction() {
|
||||
manual_compaction_paused_.store(false, std::memory_order_release);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
assert(manual_compaction_paused_ > 0);
|
||||
manual_compaction_paused_.fetch_sub(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
@@ -2528,7 +2555,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
status = Status::ShutdownInProgress();
|
||||
} else if (is_manual &&
|
||||
manual_compaction_paused_.load(std::memory_order_acquire)) {
|
||||
manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
} else {
|
||||
@@ -2570,12 +2597,13 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
if (!c) {
|
||||
m->done = true;
|
||||
m->manual_end = nullptr;
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%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(true).c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString(true).c_str() : "(end)"));
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%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(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(
|
||||
@@ -2846,7 +2874,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
earliest_write_conflict_snapshot, 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, thread_pri,
|
||||
&compaction_job_stats, thread_pri, io_tracer_,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr, db_id_,
|
||||
db_session_id_);
|
||||
compaction_job.Prepare();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "monitoring/stats_dump_scheduler.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
@@ -271,21 +272,18 @@ size_t DBImpl::TEST_GetWalPreallocateBlockSize(
|
||||
return GetWalPreallocateBlockSize(write_buffer_size);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_WaitForDumpStatsRun(std::function<void()> callback) const {
|
||||
if (thread_dump_stats_ != nullptr) {
|
||||
thread_dump_stats_->TEST_WaitForRun(callback);
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DBImpl::TEST_WaitForStatsDumpRun(std::function<void()> callback) const {
|
||||
if (stats_dump_scheduler_ != nullptr) {
|
||||
static_cast<StatsDumpTestScheduler*>(stats_dump_scheduler_)
|
||||
->TEST_WaitForRun(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::TEST_WaitForPersistStatsRun(std::function<void()> callback) const {
|
||||
if (thread_persist_stats_ != nullptr) {
|
||||
thread_persist_stats_->TEST_WaitForRun(callback);
|
||||
}
|
||||
}
|
||||
|
||||
bool DBImpl::TEST_IsPersistentStatsEnabled() const {
|
||||
return thread_persist_stats_ && thread_persist_stats_->IsRunning();
|
||||
StatsDumpTestScheduler* DBImpl::TEST_GetStatsDumpScheduler() const {
|
||||
return static_cast<StatsDumpTestScheduler*>(stats_dump_scheduler_);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
size_t DBImpl::TEST_EstimateInMemoryStatsHistorySize() const {
|
||||
return EstimateInMemoryStatsHistorySize();
|
||||
|
||||
@@ -447,7 +447,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
// Close WALs before trying to delete them.
|
||||
for (const auto w : state.logs_to_free) {
|
||||
// TODO: maybe check the return value of Close.
|
||||
w->Close();
|
||||
auto s = w->Close();
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
bool own_files = OwnTablesAndLogs();
|
||||
@@ -566,7 +567,6 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
if (!own_files) {
|
||||
continue;
|
||||
}
|
||||
Status file_deletion_status;
|
||||
if (schedule_only) {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
SchedulePendingPurge(fname, dir_to_sync, type, number, state.job_id);
|
||||
|
||||
+48
-27
@@ -6,17 +6,17 @@
|
||||
// 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 "db/db_impl/db_impl.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "file/read_write_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "monitoring/persistent_stats_history.h"
|
||||
#include "monitoring/stats_dump_scheduler.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
@@ -90,12 +90,22 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
}
|
||||
|
||||
if (result.recycle_log_file_num &&
|
||||
(result.wal_recovery_mode == WALRecoveryMode::kPointInTimeRecovery ||
|
||||
(result.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords ||
|
||||
result.wal_recovery_mode == WALRecoveryMode::kPointInTimeRecovery ||
|
||||
result.wal_recovery_mode == WALRecoveryMode::kAbsoluteConsistency)) {
|
||||
// kPointInTimeRecovery is inconsistent with recycle log file feature since
|
||||
// we define the "end" of the log as the first corrupt record we encounter.
|
||||
// kAbsoluteConsistency doesn't make sense because even a clean
|
||||
// shutdown leaves old junk at the end of the log file.
|
||||
// - kTolerateCorruptedTailRecords is inconsistent with recycle log file
|
||||
// feature. WAL recycling expects recovery success upon encountering a
|
||||
// corrupt record at the point where new data ends and recycled data
|
||||
// remains at the tail. However, `kTolerateCorruptedTailRecords` must fail
|
||||
// upon encountering any such corrupt record, as it cannot differentiate
|
||||
// between this and a real corruption, which would cause committed updates
|
||||
// to be truncated -- a violation of the recovery guarantee.
|
||||
// - kPointInTimeRecovery and kAbsoluteConsistency are incompatible with
|
||||
// recycle log file feature temporarily due to a bug found introducing a
|
||||
// hole in the recovered data
|
||||
// (https://github.com/facebook/rocksdb/pull/7252#issuecomment-673766236).
|
||||
// Besides this bug, we believe the features are fundamentally compatible.
|
||||
result.recycle_log_file_num = 0;
|
||||
}
|
||||
|
||||
@@ -137,13 +147,13 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
// DeleteScheduler::CleanupDirectory on the same dir later, it will be
|
||||
// safe
|
||||
std::vector<std::string> filenames;
|
||||
result.env->GetChildren(result.wal_dir, &filenames);
|
||||
result.env->GetChildren(result.wal_dir, &filenames).PermitUncheckedError();
|
||||
for (std::string& filename : filenames) {
|
||||
if (filename.find(".log.trash", filename.length() -
|
||||
std::string(".log.trash").length()) !=
|
||||
std::string::npos) {
|
||||
std::string trash_file = result.wal_dir + "/" + filename;
|
||||
result.env->DeleteFile(trash_file);
|
||||
result.env->DeleteFile(trash_file).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,8 +290,8 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
file->SetPreallocationBlockSize(
|
||||
immutable_db_options_.manifest_preallocation_size);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), manifest, file_options, env_, nullptr /* stats */,
|
||||
immutable_db_options_.listeners));
|
||||
std::move(file), manifest, file_options, env_, io_tracer_,
|
||||
nullptr /* stats */, immutable_db_options_.listeners));
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
new_db.EncodeTo(&record);
|
||||
@@ -851,7 +861,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
}
|
||||
}
|
||||
file_reader.reset(new SequentialFileReader(
|
||||
std::move(file), fname, immutable_db_options_.log_readahead_size));
|
||||
std::move(file), fname, immutable_db_options_.log_readahead_size,
|
||||
io_tracer_));
|
||||
}
|
||||
|
||||
// Create the log reader.
|
||||
@@ -890,7 +901,11 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
Status::Corruption("log record too small"));
|
||||
continue;
|
||||
}
|
||||
WriteBatchInternal::SetContents(&batch, record);
|
||||
|
||||
status = WriteBatchInternal::SetContents(&batch, record);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
SequenceNumber sequence = WriteBatchInternal::Sequence(&batch);
|
||||
|
||||
if (immutable_db_options_.wal_recovery_mode ==
|
||||
@@ -1281,7 +1296,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
cfd->GetLatestMutableCFOptions()->paranoid_file_checks;
|
||||
|
||||
int64_t _current_time = 0;
|
||||
env_->GetCurrentTime(&_current_time); // ignore error
|
||||
env_->GetCurrentTime(&_current_time)
|
||||
.PermitUncheckedError(); // ignore error
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
meta.oldest_ancester_time = current_time;
|
||||
|
||||
@@ -1314,9 +1330,10 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
mutable_cf_options.sample_for_compression,
|
||||
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, 0 /* oldest_key_time */, write_hint,
|
||||
0 /* file_creation_time */, db_id_, db_session_id_);
|
||||
io_tracer_, &event_logger_, job_id, Env::IO_HIGH,
|
||||
nullptr /* table_properties */, -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]"
|
||||
@@ -1420,9 +1437,9 @@ IOStatus DBImpl::CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
lfile->SetPreallocationBlockSize(preallocate_block_size);
|
||||
|
||||
const auto& listeners = immutable_db_options_.listeners;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), log_fname, opt_file_options,
|
||||
env_, nullptr /* stats */, listeners));
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(lfile), log_fname, opt_file_options, env_, io_tracer_,
|
||||
nullptr /* stats */, listeners));
|
||||
*new_log = new log::Writer(std::move(file_writer), log_file_num,
|
||||
immutable_db_options_.recycle_log_file_num > 0,
|
||||
immutable_db_options_.manual_wal_flush);
|
||||
@@ -1528,7 +1545,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
s = Status::InvalidArgument("Column family not found: ", cf.name);
|
||||
s = Status::InvalidArgument("Column family not found", cf.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1673,20 +1690,24 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
paths.erase(std::unique(paths.begin(), paths.end()), paths.end());
|
||||
for (auto& path : paths) {
|
||||
std::vector<std::string> existing_files;
|
||||
impl->immutable_db_options_.env->GetChildren(path, &existing_files);
|
||||
// TODO: Check for errors here?
|
||||
impl->immutable_db_options_.env->GetChildren(path, &existing_files)
|
||||
.PermitUncheckedError();
|
||||
for (auto& file_name : existing_files) {
|
||||
uint64_t file_number;
|
||||
FileType file_type;
|
||||
std::string file_path = path + "/" + file_name;
|
||||
if (ParseFileName(file_name, &file_number, &file_type) &&
|
||||
file_type == kTableFile) {
|
||||
// TODO: Check for errors from OnAddFile?
|
||||
if (known_file_sizes.count(file_name)) {
|
||||
// We're assuming that each sst file name exists in at most one of
|
||||
// the paths.
|
||||
sfm->OnAddFile(file_path, known_file_sizes.at(file_name),
|
||||
/* compaction */ false);
|
||||
/* compaction */ false)
|
||||
.PermitUncheckedError();
|
||||
} else {
|
||||
sfm->OnAddFile(file_path);
|
||||
sfm->OnAddFile(file_path).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1700,6 +1721,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
sfm->ReserveDiskBuffer(max_write_buffer_size,
|
||||
impl->immutable_db_options_.db_paths[0].path);
|
||||
}
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -1716,9 +1738,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
impl->StartTimedTasks();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
impl->StartStatsDumpScheduler();
|
||||
} else {
|
||||
for (auto* h : *handles) {
|
||||
delete h;
|
||||
}
|
||||
|
||||
@@ -86,10 +86,10 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
read_seq,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq,
|
||||
/* allow_unprepared_value */ true);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
@@ -118,10 +118,10 @@ Status DBImplReadOnly::NewIterators(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options, read_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback);
|
||||
auto* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq,
|
||||
/* allow_unprepared_value */ true);
|
||||
auto* internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
iterators->push_back(db_iter);
|
||||
}
|
||||
@@ -219,7 +219,7 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (cfd == nullptr) {
|
||||
s = Status::InvalidArgument("Column family not found: ", cf.name);
|
||||
s = Status::InvalidArgument("Column family not found", cf.name);
|
||||
break;
|
||||
}
|
||||
handles->push_back(new ColumnFamilyHandleImpl(cfd, impl, &impl->mutex_));
|
||||
|
||||
@@ -153,7 +153,8 @@ Status DBImplSecondary::MaybeInitLogReader(
|
||||
return status;
|
||||
}
|
||||
file_reader.reset(new SequentialFileReader(
|
||||
std::move(file), fname, immutable_db_options_.log_readahead_size));
|
||||
std::move(file), fname, immutable_db_options_.log_readahead_size,
|
||||
io_tracer_));
|
||||
}
|
||||
|
||||
// Create the log reader.
|
||||
@@ -415,10 +416,10 @@ ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
|
||||
snapshot,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot,
|
||||
/* allow_unprepared_value */ true);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
@@ -611,7 +612,7 @@ Status DB::OpenAsSecondary(
|
||||
impl->versions_.reset(new ReactiveVersionSet(
|
||||
dbname, &impl->immutable_db_options_, impl->file_options_,
|
||||
impl->table_cache_.get(), impl->write_buffer_manager_,
|
||||
&impl->write_controller_));
|
||||
&impl->write_controller_, impl->io_tracer_));
|
||||
impl->column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
|
||||
impl->wal_in_db_path_ = IsWalDirSameAsDBPath(&impl->immutable_db_options_);
|
||||
@@ -623,7 +624,7 @@ Status DB::OpenAsSecondary(
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (nullptr == cfd) {
|
||||
s = Status::InvalidArgument("Column family not found: ", cf.name);
|
||||
s = Status::InvalidArgument("Column family not found", cf.name);
|
||||
break;
|
||||
}
|
||||
handles->push_back(new ColumnFamilyHandleImpl(cfd, impl, &impl->mutex_));
|
||||
|
||||
@@ -623,8 +623,6 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
0 /*log_number*/, this, true /*concurrent_memtable_writes*/,
|
||||
seq_per_batch_, sub_batch_cnt, true /*batch_per_txn*/,
|
||||
write_options.memtable_insert_hint_per_batch);
|
||||
|
||||
WriteStatusCheck(w.status);
|
||||
if (write_options.disableWAL) {
|
||||
has_unpersisted_data_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
@@ -639,6 +637,7 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
std::lock_guard<std::mutex> lck(switch_mutex_);
|
||||
switch_cv_.notify_all();
|
||||
}
|
||||
WriteStatusCheck(w.status);
|
||||
|
||||
if (!w.FinalStatus().ok()) {
|
||||
return w.FinalStatus();
|
||||
@@ -689,7 +688,7 @@ Status DBImpl::WriteImplWALOnly(
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
bool need_log_sync = false;
|
||||
status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
WriteStatusCheck(status);
|
||||
WriteStatusCheckOnLocked(status);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
WriteThread::WriteGroup write_group;
|
||||
@@ -830,9 +829,22 @@ Status DBImpl::WriteImplWALOnly(
|
||||
return status;
|
||||
}
|
||||
|
||||
void DBImpl::WriteStatusCheckOnLocked(const Status& status) {
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
// Caller must hold mutex_.
|
||||
assert(!status.IsIOFenced() || !error_handler_.GetBGError().ok());
|
||||
mutex_.AssertHeld();
|
||||
if (immutable_db_options_.paranoid_checks && !status.ok() &&
|
||||
!status.IsBusy() && !status.IsIncomplete()) {
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kWriteCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::WriteStatusCheck(const Status& status) {
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
assert(!status.IsIOFenced() || !error_handler_.GetBGError().ok());
|
||||
if (immutable_db_options_.paranoid_checks && !status.ok() &&
|
||||
!status.IsBusy() && !status.IsIncomplete()) {
|
||||
mutex_.Lock();
|
||||
@@ -844,8 +856,9 @@ 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()) {
|
||||
if ((immutable_db_options_.paranoid_checks && !io_status.ok() &&
|
||||
!io_status.IsBusy() && !io_status.IsIncomplete()) ||
|
||||
io_status.IsIOFenced()) {
|
||||
mutex_.Lock();
|
||||
error_handler_.SetBGError(io_status, BackgroundErrorReason::kWriteCallback);
|
||||
mutex_.Unlock();
|
||||
@@ -1546,11 +1559,14 @@ Status DBImpl::TrimMemtableHistory(WriteContext* context) {
|
||||
}
|
||||
for (auto& cfd : cfds) {
|
||||
autovector<MemTable*> to_delete;
|
||||
cfd->imm()->TrimHistory(&to_delete, cfd->mem()->ApproximateMemoryUsage());
|
||||
bool trimmed = cfd->imm()->TrimHistory(
|
||||
&to_delete, cfd->mem()->ApproximateMemoryUsage());
|
||||
if (!to_delete.empty()) {
|
||||
for (auto m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
if (trimmed) {
|
||||
context->superversion_context.NewSuperVersion();
|
||||
assert(context->superversion_context.new_superversion.get() != nullptr);
|
||||
cfd->InstallSuperVersion(&context->superversion_context, &mutex_);
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBSecondaryTest : public DBTestBase {
|
||||
public:
|
||||
DBSecondaryTest()
|
||||
: DBTestBase("/db_secondary_test"),
|
||||
: DBTestBase("/db_secondary_test", /*env_do_fsync=*/true),
|
||||
secondary_path_(),
|
||||
handles_secondary_(),
|
||||
db_secondary_(nullptr) {
|
||||
|
||||
+24
-13
@@ -53,16 +53,24 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
Header(options.info_log, "IDENTITY file: %s\n", file.c_str());
|
||||
break;
|
||||
case kDescriptorFile:
|
||||
env->GetFileSize(dbname + "/" + file, &file_size);
|
||||
Header(options.info_log, "MANIFEST file: %s size: %" PRIu64 " Bytes\n",
|
||||
file.c_str(), file_size);
|
||||
if (env->GetFileSize(dbname + "/" + file, &file_size).ok()) {
|
||||
Header(options.info_log,
|
||||
"MANIFEST file: %s size: %" PRIu64 " Bytes\n", file.c_str(),
|
||||
file_size);
|
||||
} else {
|
||||
Error(options.info_log, "Error when reading MANIFEST file: %s/%s\n",
|
||||
dbname.c_str(), file.c_str());
|
||||
}
|
||||
break;
|
||||
case kLogFile:
|
||||
env->GetFileSize(dbname + "/" + file, &file_size);
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%" PRIu64, file_size);
|
||||
wal_info.append(file).append(" size: ").
|
||||
append(str).append(" ; ");
|
||||
if (env->GetFileSize(dbname + "/" + file, &file_size).ok()) {
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%" PRIu64, file_size);
|
||||
wal_info.append(file).append(" size: ").append(str).append(" ; ");
|
||||
} else {
|
||||
Error(options.info_log, "Error when reading LOG file: %s/%s\n",
|
||||
dbname.c_str(), file.c_str());
|
||||
}
|
||||
break;
|
||||
case kTableFile:
|
||||
if (++file_num < 10) {
|
||||
@@ -111,11 +119,14 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
for (const std::string& file : files) {
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
if (type == kLogFile) {
|
||||
env->GetFileSize(options.wal_dir + "/" + file, &file_size);
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%" PRIu64, file_size);
|
||||
wal_info.append(file).append(" size: ").
|
||||
append(str).append(" ; ");
|
||||
if (env->GetFileSize(options.wal_dir + "/" + file, &file_size).ok()) {
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%" PRIu64, file_size);
|
||||
wal_info.append(file).append(" size: ").append(str).append(" ; ");
|
||||
} else {
|
||||
Error(options.info_log, "Error when reading LOG file %s/%s\n",
|
||||
options.wal_dir.c_str(), file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBTestInPlaceUpdate : public DBTestBase {
|
||||
public:
|
||||
DBTestInPlaceUpdate() : DBTestBase("/db_inplace_update_test") {}
|
||||
DBTestInPlaceUpdate()
|
||||
: DBTestBase("/db_inplace_update_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBTestInPlaceUpdate, InPlaceUpdate) {
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBIOFailureTest : public DBTestBase {
|
||||
public:
|
||||
DBIOFailureTest() : DBTestBase("/db_io_failure_test") {}
|
||||
DBIOFailureTest()
|
||||
: DBTestBase("/db_io_failure_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -34,7 +35,7 @@ TEST_F(DBIOFailureTest, DropWrites) {
|
||||
// Force out-of-space errors
|
||||
env_->drop_writes_.store(true, std::memory_order_release);
|
||||
env_->sleep_counter_.Reset();
|
||||
env_->no_slowdown_ = true;
|
||||
env_->SetMockSleep();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (option_config_ != kUniversalCompactionMultiLevel &&
|
||||
option_config_ != kUniversalSubcompactions) {
|
||||
|
||||
+31
-12
@@ -223,11 +223,13 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
|
||||
is_key_seqnum_zero_ = (ikey_.sequence == 0);
|
||||
|
||||
assert(iterate_upper_bound_ == nullptr || iter_.MayBeOutOfUpperBound() ||
|
||||
assert(iterate_upper_bound_ == nullptr ||
|
||||
iter_.UpperBoundCheckResult() != IterBoundCheck::kInbound ||
|
||||
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() &&
|
||||
if (iterate_upper_bound_ != nullptr &&
|
||||
iter_.UpperBoundCheckResult() != IterBoundCheck::kInbound &&
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, /*a_has_ts=*/true, *iterate_upper_bound_,
|
||||
/*b_has_ts=*/false) >= 0) {
|
||||
@@ -246,11 +248,10 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
}
|
||||
|
||||
assert(ikey_.user_key.size() >= timestamp_size_);
|
||||
Slice ts;
|
||||
Slice ts = timestamp_size_ > 0 ? ExtractTimestampFromUserKey(
|
||||
ikey_.user_key, timestamp_size_)
|
||||
: Slice();
|
||||
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
|
||||
@@ -285,7 +286,20 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// 2) return ikey only if ikey.seqnum >= start_seqnum_
|
||||
// note that if deletion seqnum is < start_seqnum_ we
|
||||
// just skip it like in normal iterator.
|
||||
if (start_seqnum_ > 0 && ikey_.sequence >= start_seqnum_) {
|
||||
if (start_seqnum_ > 0) {
|
||||
if (ikey_.sequence >= start_seqnum_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
} else {
|
||||
saved_key_.SetUserKey(
|
||||
ikey_.user_key,
|
||||
!pin_thru_lifetime_ ||
|
||||
!iter_.iter()->IsKeyPinned() /* copy */);
|
||||
skipping_saved_key = true;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
}
|
||||
} else if (timestamp_lb_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
@@ -300,10 +314,8 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
case kTypeValue:
|
||||
case kTypeBlobIndex:
|
||||
if (start_seqnum_ > 0) {
|
||||
// we are taking incremental snapshot here
|
||||
// incremental snapshots aren't supported on DB with range deletes
|
||||
assert(ikey_.type != kTypeBlobIndex);
|
||||
if (ikey_.sequence >= start_seqnum_) {
|
||||
assert(ikey_.type != kTypeBlobIndex);
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
@@ -316,6 +328,10 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
!iter_.iter()->IsKeyPinned() /* copy */);
|
||||
skipping_saved_key = true;
|
||||
}
|
||||
} else if (timestamp_lb_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
} else {
|
||||
saved_key_.SetUserKey(
|
||||
ikey_.user_key, !pin_thru_lifetime_ ||
|
||||
@@ -1193,7 +1209,8 @@ void DBIter::Seek(const Slice& target) {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
db_impl_->TraceIteratorSeek(cfd_->GetID(), target);
|
||||
// TODO: What do we do if this returns an error?
|
||||
db_impl_->TraceIteratorSeek(cfd_->GetID(), target).PermitUncheckedError();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -1254,7 +1271,9 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
db_impl_->TraceIteratorSeekForPrev(cfd_->GetID(), target);
|
||||
// TODO: What do we do if this returns an error?
|
||||
db_impl_->TraceIteratorSeekForPrev(cfd_->GetID(), target)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ class DBIter final : public Iterator {
|
||||
bool Valid() const override { return valid_; }
|
||||
Slice key() const override {
|
||||
assert(valid_);
|
||||
if (start_seqnum_ > 0) {
|
||||
if (start_seqnum_ > 0 || timestamp_lb_) {
|
||||
return saved_key_.GetInternalKey();
|
||||
} else {
|
||||
const Slice ukey_and_ts = saved_key_.GetUserKey();
|
||||
|
||||
@@ -34,7 +34,7 @@ class DummyReadCallback : public ReadCallback {
|
||||
class DBIteratorTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBIteratorTest() : DBTestBase("/db_iterator_test") {}
|
||||
DBIteratorTest() : DBTestBase("/db_iterator_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
Iterator* NewIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family = nullptr) {
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBTestXactLogIterator : public DBTestBase {
|
||||
public:
|
||||
DBTestXactLogIterator() : DBTestBase("/db_log_iter_test") {}
|
||||
DBTestXactLogIterator()
|
||||
: DBTestBase("/db_log_iter_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::unique_ptr<TransactionLogIterator> OpenTransactionLogIter(
|
||||
const SequenceNumber seq) {
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBMemTableTest : public DBTestBase {
|
||||
public:
|
||||
DBMemTableTest() : DBTestBase("/db_memtable_test") {}
|
||||
DBMemTableTest() : DBTestBase("/db_memtable_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
class MockMemTableRep : public MemTableRep {
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBMergeOperandTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperandTest() : DBTestBase("/db_merge_operand_test") {}
|
||||
DBMergeOperandTest()
|
||||
: DBTestBase("/db_merge_operand_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
|
||||
@@ -36,7 +36,8 @@ class TestReadCallback : public ReadCallback {
|
||||
// Test merge operator functionality.
|
||||
class DBMergeOperatorTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperatorTest() : DBTestBase("/db_merge_operator_test") {}
|
||||
DBMergeOperatorTest()
|
||||
: DBTestBase("/db_merge_operator_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::string GetWithReadCallback(SnapshotChecker* snapshot_checker,
|
||||
const Slice& key,
|
||||
|
||||
+11
-11
@@ -27,7 +27,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBOptionsTest : public DBTestBase {
|
||||
public:
|
||||
DBOptionsTest() : DBTestBase("/db_options_test") {}
|
||||
DBOptionsTest() : DBTestBase("/db_options_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::unordered_map<std::string, std::string> GetMutableDBOptionsMap(
|
||||
@@ -563,10 +563,9 @@ static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
SpecialEnv env(env_);
|
||||
env.time_elapse_only_sleep_ = true;
|
||||
Options options;
|
||||
options.env = &env;
|
||||
options.env = env_;
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
options.create_if_missing = true;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
@@ -585,10 +584,10 @@ TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
|
||||
assert_candidate_files_empty(dbfull(), true);
|
||||
|
||||
env.addon_time_.store(20);
|
||||
env_->MockSleepForMicroseconds(20);
|
||||
assert_candidate_files_empty(dbfull(), true);
|
||||
|
||||
env.addon_time_.store(21);
|
||||
env_->MockSleepForMicroseconds(1);
|
||||
assert_candidate_files_empty(dbfull(), false);
|
||||
|
||||
Close();
|
||||
@@ -702,11 +701,12 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
// Test dynamically changing ttl.
|
||||
env_->addon_time_.store(0);
|
||||
options.ttl = 1 * 60 * 60; // 1 hour
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
@@ -721,8 +721,7 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Add 61 seconds to the time.
|
||||
env_->addon_time_.fetch_add(61);
|
||||
env_->MockSleepForSeconds(61);
|
||||
|
||||
// No files should be compacted as ttl is set to 1 hour.
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 3600);
|
||||
@@ -736,8 +735,9 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
// Test dynamically changing compaction_options_fifo.max_table_files_size
|
||||
env_->addon_time_.store(0);
|
||||
options.compaction_options_fifo.max_table_files_size = 500 << 10; // 00KB
|
||||
options.ttl = 0;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
+19
-20
@@ -26,7 +26,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBPropertiesTest : public DBTestBase {
|
||||
public:
|
||||
DBPropertiesTest() : DBTestBase("/db_properties_test") {}
|
||||
DBPropertiesTest()
|
||||
: DBTestBase("/db_properties_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -1415,13 +1416,11 @@ TEST_F(DBPropertiesTest, EstimateNumKeysUnderflow) {
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, EstimateOldestKeyTime) {
|
||||
std::unique_ptr<MockTimeEnv> mock_env(new MockTimeEnv(Env::Default()));
|
||||
uint64_t oldest_key_time = 0;
|
||||
Options options;
|
||||
options.env = mock_env.get();
|
||||
Options options = CurrentOptions();
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
|
||||
// "rocksdb.estimate-oldest-key-time" only available to fifo compaction.
|
||||
mock_env->set_current_time(100);
|
||||
for (auto compaction : {kCompactionStyleLevel, kCompactionStyleUniversal,
|
||||
kCompactionStyleNone}) {
|
||||
options.compaction_style = compaction;
|
||||
@@ -1432,60 +1431,60 @@ TEST_F(DBPropertiesTest, EstimateOldestKeyTime) {
|
||||
DB::Properties::kEstimateOldestKeyTime, &oldest_key_time));
|
||||
}
|
||||
|
||||
int64_t mock_start_time;
|
||||
ASSERT_OK(env_->GetCurrentTime(&mock_start_time));
|
||||
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.ttl = 300;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
mock_env->set_current_time(100);
|
||||
env_->MockSleepForSeconds(100);
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
|
||||
&oldest_key_time));
|
||||
ASSERT_EQ(100, oldest_key_time);
|
||||
ASSERT_EQ(100, oldest_key_time - mock_start_time);
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
|
||||
&oldest_key_time));
|
||||
ASSERT_EQ(100, oldest_key_time);
|
||||
ASSERT_EQ(100, oldest_key_time - mock_start_time);
|
||||
|
||||
mock_env->set_current_time(200);
|
||||
env_->MockSleepForSeconds(100); // -> 200
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
|
||||
&oldest_key_time));
|
||||
ASSERT_EQ(100, oldest_key_time);
|
||||
ASSERT_EQ(100, oldest_key_time - mock_start_time);
|
||||
|
||||
mock_env->set_current_time(300);
|
||||
env_->MockSleepForSeconds(100); // -> 300
|
||||
ASSERT_OK(Put("k3", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("3", FilesPerLevel());
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
|
||||
&oldest_key_time));
|
||||
ASSERT_EQ(100, oldest_key_time);
|
||||
ASSERT_EQ(100, oldest_key_time - mock_start_time);
|
||||
|
||||
mock_env->set_current_time(450);
|
||||
env_->MockSleepForSeconds(150); // -> 450
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
|
||||
&oldest_key_time));
|
||||
ASSERT_EQ(200, oldest_key_time);
|
||||
ASSERT_EQ(200, oldest_key_time - mock_start_time);
|
||||
|
||||
mock_env->set_current_time(550);
|
||||
env_->MockSleepForSeconds(100); // -> 550
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
|
||||
&oldest_key_time));
|
||||
ASSERT_EQ(300, oldest_key_time);
|
||||
ASSERT_EQ(300, oldest_key_time - mock_start_time);
|
||||
|
||||
mock_env->set_current_time(650);
|
||||
env_->MockSleepForSeconds(100); // -> 650
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("", FilesPerLevel());
|
||||
ASSERT_FALSE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
|
||||
&oldest_key_time));
|
||||
|
||||
// Close before mock_env destructs.
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBRangeDelTest : public DBTestBase {
|
||||
public:
|
||||
DBRangeDelTest() : DBTestBase("/db_range_del_test") {}
|
||||
DBRangeDelTest() : DBTestBase("/db_range_del_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::string GetNumericStr(int key) {
|
||||
uint64_t uint64_key = static_cast<uint64_t>(key);
|
||||
|
||||
+13
-13
@@ -18,7 +18,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBSSTTest : public DBTestBase {
|
||||
public:
|
||||
DBSSTTest() : DBTestBase("/db_sst_test") {}
|
||||
DBSSTTest() : DBTestBase("/db_sst_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -359,12 +359,11 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
uint64_t* abs_time_us = static_cast<uint64_t*>(arg);
|
||||
uint64_t cur_time = env_->NowMicros();
|
||||
if (*abs_time_us > cur_time) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us - cur_time);
|
||||
env_->MockSleepForMicroseconds(*abs_time_us - cur_time);
|
||||
}
|
||||
|
||||
// Randomly sleep shortly
|
||||
env_->addon_time_.fetch_add(
|
||||
static_cast<uint64_t>(Random::GetTLSInstance()->Uniform(10)));
|
||||
// Plus an additional short, random amount
|
||||
env_->MockSleepForMicroseconds(Random::GetTLSInstance()->Uniform(10));
|
||||
|
||||
// Set wait until time to before (actual) current time to force not
|
||||
// to sleep
|
||||
@@ -374,7 +373,7 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
options.statistics = CreateDBStatistics();
|
||||
@@ -442,8 +441,6 @@ TEST_F(DBSSTTest, RateLimitedWALDelete) {
|
||||
"DeleteScheduler::BackgroundEmptyTrash:Wait",
|
||||
[&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); });
|
||||
|
||||
env_->no_slowdown_ = true;
|
||||
env_->time_elapse_only_sleep_ = true;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
@@ -457,6 +454,7 @@ TEST_F(DBSSTTest, RateLimitedWALDelete) {
|
||||
options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -1032,13 +1030,16 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
// we encode table properties as varint64. Force time to be 0 to work around
|
||||
// it. Should remove the workaround after we propagate the property on
|
||||
// compaction.
|
||||
std::unique_ptr<MockTimeEnv> mock_env(new MockTimeEnv(Env::Default()));
|
||||
mock_env->set_current_time(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:oldest_ancester_time", [&](void* arg) {
|
||||
uint64_t* current_time = static_cast<uint64_t*>(arg);
|
||||
*current_time = 0;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
options.env = mock_env.get();
|
||||
DestroyAndReopen(options);
|
||||
// Generate 5 files in L0
|
||||
for (int i = 0; i < 5; i++) {
|
||||
@@ -1126,8 +1127,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
// Total SST files = 0
|
||||
ASSERT_EQ(total_sst_files_size, 0);
|
||||
|
||||
// Close db before mock_env destruct.
|
||||
Close();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBStatisticsTest : public DBTestBase {
|
||||
public:
|
||||
DBStatisticsTest() : DBTestBase("/db_statistics_test") {}
|
||||
DBStatisticsTest()
|
||||
: DBTestBase("/db_statistics_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBStatisticsTest, CompressionStatsTest) {
|
||||
@@ -141,6 +142,19 @@ TEST_F(DBStatisticsTest, ResetStats) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBStatisticsTest, ExcludeTickers) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
options.statistics->set_stats_level(StatsLevel::kExceptTickers);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BYTES_WRITTEN));
|
||||
options.statistics->set_stats_level(StatsLevel::kExceptHistogramOrTimers);
|
||||
Reopen(options);
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
ASSERT_GT(options.statistics->getTickerCount(BYTES_READ), 0);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -49,7 +49,8 @@ void VerifyTableProperties(DB* db, uint64_t expected_entries_size) {
|
||||
class DBTablePropertiesTest : public DBTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBTablePropertiesTest() : DBTestBase("/db_table_properties_test") {}
|
||||
DBTablePropertiesTest()
|
||||
: DBTestBase("/db_table_properties_test", /*env_do_fsync=*/true) {}
|
||||
TablePropertiesCollection TestGetPropertiesOfTablesInRange(
|
||||
std::vector<Range> ranges, std::size_t* num_properties = nullptr,
|
||||
std::size_t* num_files = nullptr);
|
||||
@@ -294,6 +295,7 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger);
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
opts.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
|
||||
if(GetParam() == "kCompactionStyleUniversal") {
|
||||
@@ -364,6 +366,8 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
ASSERT_LT(0, opts.statistics->getTickerCount(COMPACT_WRITE_BYTES_MARKED));
|
||||
ASSERT_LT(0, opts.statistics->getTickerCount(COMPACT_READ_BYTES_MARKED));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBTestTailingIterator : public DBTestBase {
|
||||
public:
|
||||
DBTestTailingIterator() : DBTestBase("/db_tailing_iterator_test") {}
|
||||
DBTestTailingIterator()
|
||||
: DBTestBase("/db_tailing_iterator_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBTestTailingIterator, TailingIteratorSingle) {
|
||||
|
||||
+87
-44
@@ -71,9 +71,12 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Note that whole DBTest and its child classes disable fsync on files
|
||||
// and directories for speed.
|
||||
// If fsync needs to be covered in a test, put it in other places.
|
||||
class DBTest : public DBTestBase {
|
||||
public:
|
||||
DBTest() : DBTestBase("/db_test") {}
|
||||
DBTest() : DBTestBase("/db_test", /*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
class DBTestWithParam
|
||||
@@ -974,7 +977,7 @@ class DelayFilter : public CompactionFilter {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
db_test->env_->MockSleepForMicroseconds(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1719,6 +1722,7 @@ TEST_F(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, Snapshot) {
|
||||
env_->SetMockSleep();
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
do {
|
||||
@@ -1734,7 +1738,7 @@ TEST_F(DBTest, Snapshot) {
|
||||
Put(0, "foo", "0v2");
|
||||
Put(1, "foo", "1v2");
|
||||
|
||||
env_->addon_time_.fetch_add(1);
|
||||
env_->MockSleepForSeconds(1);
|
||||
|
||||
const Snapshot* s2 = db_->GetSnapshot();
|
||||
ASSERT_EQ(2U, GetNumSnapshots());
|
||||
@@ -1791,7 +1795,6 @@ TEST_F(DBTest, Snapshot) {
|
||||
TEST_F(DBTest, HiddenValuesAreRemoved) {
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
env_->skip_fsync_ = true;
|
||||
do {
|
||||
Options options = CurrentOptions(options_override);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -3146,7 +3149,7 @@ static bool CompareIterators(int step, DB* model, DB* db,
|
||||
fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n",
|
||||
step, EscapeString(miter->key()).c_str(),
|
||||
EscapeString(miter->value()).c_str(),
|
||||
EscapeString(miter->value()).c_str());
|
||||
EscapeString(dbiter->value()).c_str());
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
@@ -3545,13 +3548,14 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
options.arena_block_size = 4096;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
// Test to make sure that all files with expired ttl are deleted on next
|
||||
// manual compaction.
|
||||
{
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
options.compaction_options_fifo.max_table_files_size = 150 << 10; // 150KB
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.ttl = 1 * 60 * 60 ; // 1 hour
|
||||
@@ -3570,10 +3574,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Sleep for 2 hours -- which is much greater than TTL.
|
||||
// Note: Couldn't use SleepForMicroseconds because it takes an int instead
|
||||
// of uint64_t. Hence used addon_time_ directly.
|
||||
// env_->SleepForMicroseconds(2 * 60 * 60 * 1000 * 1000);
|
||||
env_->addon_time_.fetch_add(2 * 60 * 60);
|
||||
env_->MockSleepForSeconds(2 * 60 * 60);
|
||||
|
||||
// Since no flushes and compactions have run, the db should still be in
|
||||
// the same state even after considerable time has passed.
|
||||
@@ -3605,7 +3606,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Sleep for 2 hours -- which is much greater than TTL.
|
||||
env_->addon_time_.fetch_add(2 * 60 * 60);
|
||||
env_->MockSleepForSeconds(2 * 60 * 60);
|
||||
// Just to make sure that we are in the same state even after sleeping.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
@@ -3647,7 +3648,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 3);
|
||||
|
||||
// Sleep for 2 hours -- which is much greater than TTL.
|
||||
env_->addon_time_.fetch_add(2 * 60 * 60);
|
||||
env_->MockSleepForSeconds(2 * 60 * 60);
|
||||
// Just to make sure that we are in the same state even after sleeping.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 3);
|
||||
@@ -3688,7 +3689,7 @@ TEST_F(DBTest, FIFOCompactionWithTTLTest) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 5);
|
||||
|
||||
// Sleep for 2 hours -- which is much greater than TTL.
|
||||
env_->addon_time_.fetch_add(2 * 60 * 60);
|
||||
env_->MockSleepForSeconds(2 * 60 * 60);
|
||||
// Just to make sure that we are in the same state even after sleeping.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 5);
|
||||
@@ -4001,7 +4002,6 @@ TEST_F(DBTest, DynamicMemtableOptions) {
|
||||
const uint64_t k128KB = 1 << 17;
|
||||
const uint64_t k5KB = 5 * 1024;
|
||||
Options options;
|
||||
env_->skip_fsync_ = true;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.compression = kNoCompression;
|
||||
@@ -5148,7 +5148,6 @@ TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
|
||||
TEST_F(DBTest, FileCreationRandomFailure) {
|
||||
Options options;
|
||||
env_->skip_fsync_ = true;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 100000; // Small write buffer
|
||||
@@ -5444,9 +5443,10 @@ class DelayedMergeOperator : public MergeOperator {
|
||||
public:
|
||||
explicit DelayedMergeOperator(DBTest* d) : db_test_(d) {}
|
||||
|
||||
bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
db_test_->env_->addon_time_.fetch_add(1000);
|
||||
db_test_->env_->MockSleepForMicroseconds(1000 *
|
||||
merge_in.operand_list.size());
|
||||
merge_out->new_value = "";
|
||||
return true;
|
||||
}
|
||||
@@ -5454,8 +5454,6 @@ 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);
|
||||
@@ -5464,14 +5462,14 @@ TEST_F(DBTest, MergeTestTime) {
|
||||
|
||||
// Enable time profiling
|
||||
SetPerfLevel(kEnableTime);
|
||||
this->env_->addon_time_.store(0);
|
||||
this->env_->time_elapse_only_sleep_ = true;
|
||||
this->env_->no_slowdown_ = true;
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.merge_operator.reset(new DelayedMergeOperator(this));
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
ASSERT_EQ(TestGetTickerCount(options, MERGE_OPERATION_TOTAL_TIME), 0);
|
||||
db_->Put(WriteOptions(), "foo", one);
|
||||
ASSERT_OK(Flush());
|
||||
@@ -5486,7 +5484,7 @@ TEST_F(DBTest, MergeTestTime) {
|
||||
std::string result;
|
||||
db_->Get(opt, "foo", &result);
|
||||
|
||||
ASSERT_EQ(1000000, TestGetTickerCount(options, MERGE_OPERATION_TOTAL_TIME));
|
||||
ASSERT_EQ(2000000, TestGetTickerCount(options, MERGE_OPERATION_TOTAL_TIME));
|
||||
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
@@ -5497,34 +5495,37 @@ TEST_F(DBTest, MergeTestTime) {
|
||||
}
|
||||
|
||||
ASSERT_EQ(1, count);
|
||||
ASSERT_EQ(2000000, TestGetTickerCount(options, MERGE_OPERATION_TOTAL_TIME));
|
||||
ASSERT_EQ(4000000, TestGetTickerCount(options, MERGE_OPERATION_TOTAL_TIME));
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
ASSERT_GT(TestGetTickerCount(options, FLUSH_WRITE_BYTES), 0);
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
this->env_->time_elapse_only_sleep_ = false;
|
||||
}
|
||||
#endif // OS_WIN
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(DBTestWithParam, MergeCompactionTimeTest) {
|
||||
SetPerfLevel(kEnableTime);
|
||||
env_->skip_fsync_ = true;
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_filter_factory = std::make_shared<KeepFilterFactory>();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.merge_operator.reset(new DelayedMergeOperator(this));
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
constexpr unsigned n = 1000;
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "foo", "TEST"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_NE(TestGetTickerCount(options, MERGE_OPERATION_TOTAL_TIME), 0);
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = exclusive_manual_compaction_;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
ASSERT_EQ(uint64_t{n} * 1000000U,
|
||||
TestGetTickerCount(options, MERGE_OPERATION_TOTAL_TIME));
|
||||
}
|
||||
|
||||
TEST_P(DBTestWithParam, FilterCompactionTimeTest) {
|
||||
@@ -5536,12 +5537,15 @@ TEST_P(DBTestWithParam, FilterCompactionTimeTest) {
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.statistics->set_stats_level(kExceptTimeForMutex);
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
unsigned n = 0;
|
||||
// put some data
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
Put(ToString(table * 100 + i), "val");
|
||||
++n;
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
@@ -5555,7 +5559,8 @@ TEST_P(DBTestWithParam, FilterCompactionTimeTest) {
|
||||
|
||||
Iterator* itr = db_->NewIterator(ReadOptions());
|
||||
itr->SeekToFirst();
|
||||
ASSERT_NE(TestGetTickerCount(options, FILTER_OPERATION_TOTAL_TIME), 0);
|
||||
ASSERT_EQ(uint64_t{n} * 1000000U,
|
||||
TestGetTickerCount(options, FILTER_OPERATION_TOTAL_TIME));
|
||||
delete itr;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -6007,7 +6012,6 @@ TEST_F(DBTest, DelayedWriteRate) {
|
||||
Options options = CurrentOptions();
|
||||
env_->SetBackgroundThreads(1, Env::LOW);
|
||||
options.env = env_;
|
||||
env_->no_slowdown_ = true;
|
||||
options.write_buffer_size = 100000000;
|
||||
options.max_write_buffer_number = 256;
|
||||
options.max_background_compactions = 1;
|
||||
@@ -6018,6 +6022,7 @@ TEST_F(DBTest, DelayedWriteRate) {
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(kEntriesPerMemTable));
|
||||
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Block compactions
|
||||
@@ -6056,12 +6061,9 @@ TEST_F(DBTest, DelayedWriteRate) {
|
||||
kIncSlowdownRatio * kIncSlowdownRatio);
|
||||
}
|
||||
// Estimate the total sleep time fall into the rough range.
|
||||
ASSERT_GT(env_->addon_time_.load(),
|
||||
static_cast<int64_t>(estimated_sleep_time / 2));
|
||||
ASSERT_LT(env_->addon_time_.load(),
|
||||
static_cast<int64_t>(estimated_sleep_time * 2));
|
||||
ASSERT_GT(env_->NowMicros(), estimated_sleep_time / 2);
|
||||
ASSERT_LT(env_->NowMicros(), estimated_sleep_time * 2);
|
||||
|
||||
env_->no_slowdown_ = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
@@ -6575,10 +6577,11 @@ TEST_F(DBTest, CreationTimeOfOldestFile) {
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.max_open_files = -1;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
bool set_file_creation_time_to_zero = true;
|
||||
@@ -6589,7 +6592,7 @@ TEST_F(DBTest, CreationTimeOfOldestFile) {
|
||||
const uint64_t uint_time_1 = static_cast<uint64_t>(time_1);
|
||||
|
||||
// Add 50 hours
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
env_->MockSleepForSeconds(50 * 60 * 60);
|
||||
|
||||
int64_t time_2 = 0;
|
||||
env_->GetCurrentTime(&time_2);
|
||||
@@ -6643,10 +6646,10 @@ TEST_F(DBTest, CreationTimeOfOldestFile) {
|
||||
set_file_creation_time_to_zero = false;
|
||||
options = CurrentOptions();
|
||||
options.max_open_files = -1;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
@@ -6674,6 +6677,46 @@ TEST_F(DBTest, CreationTimeOfOldestFile) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MemoryUsageWithMaxWriteBufferSizeToMaintain) {
|
||||
Options options = CurrentOptions();
|
||||
options.max_write_buffer_size_to_maintain = 10000;
|
||||
options.write_buffer_size = 160000;
|
||||
Reopen(options);
|
||||
Random rnd(301);
|
||||
bool memory_limit_exceeded = false;
|
||||
uint64_t size_all_mem_table = 0;
|
||||
uint64_t cur_active_mem = 0;
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
std::string value = rnd.RandomString(1000);
|
||||
ASSERT_OK(Put("keykey_" + std::to_string(i), value));
|
||||
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
ASSERT_TRUE(db_->GetIntProperty(db_->DefaultColumnFamily(),
|
||||
DB::Properties::kSizeAllMemTables,
|
||||
&size_all_mem_table));
|
||||
ASSERT_TRUE(db_->GetIntProperty(db_->DefaultColumnFamily(),
|
||||
DB::Properties::kCurSizeActiveMemTable,
|
||||
&cur_active_mem));
|
||||
|
||||
// Errors out if memory usage keeps on increasing beyond the limit.
|
||||
// Once memory limit exceeds, memory_limit_exceeded is set and if
|
||||
// size_all_mem_table doesn't drop out in the next write then it errors out
|
||||
// (not expected behaviour). If memory usage drops then
|
||||
// memory_limit_exceeded is set to false.
|
||||
if ((size_all_mem_table > cur_active_mem) &&
|
||||
(cur_active_mem >=
|
||||
static_cast<uint64_t>(options.max_write_buffer_size_to_maintain)) &&
|
||||
(size_all_mem_table > options.max_write_buffer_size_to_maintain +
|
||||
options.write_buffer_size)) {
|
||||
ASSERT_FALSE(memory_limit_exceeded);
|
||||
memory_limit_exceeded = true;
|
||||
} else {
|
||||
memory_limit_exceeded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+71
-24
@@ -23,7 +23,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBTest2 : public DBTestBase {
|
||||
public:
|
||||
DBTest2() : DBTestBase("/db_test2") {}
|
||||
DBTest2() : DBTestBase("/db_test2", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -93,7 +93,8 @@ class TestReadOnlyWithCompressedCache
|
||||
public testing::WithParamInterface<std::tuple<int, bool>> {
|
||||
public:
|
||||
TestReadOnlyWithCompressedCache()
|
||||
: DBTestBase("/test_readonly_with_compressed_cache") {
|
||||
: DBTestBase("/test_readonly_with_compressed_cache",
|
||||
/*env_do_fsync=*/true) {
|
||||
max_open_files_ = std::get<0>(GetParam());
|
||||
use_mmap_ = std::get<1>(GetParam());
|
||||
}
|
||||
@@ -184,7 +185,7 @@ class PrefixFullBloomWithReverseComparator
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
PrefixFullBloomWithReverseComparator()
|
||||
: DBTestBase("/prefix_bloom_reverse") {}
|
||||
: DBTestBase("/prefix_bloom_reverse", /*env_do_fsync=*/true) {}
|
||||
void SetUp() override { if_cache_filter_ = GetParam(); }
|
||||
bool if_cache_filter_;
|
||||
};
|
||||
@@ -324,7 +325,7 @@ class DBTestSharedWriteBufferAcrossCFs
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
DBTestSharedWriteBufferAcrossCFs()
|
||||
: DBTestBase("/db_test_shared_write_buffer") {}
|
||||
: DBTestBase("/db_test_shared_write_buffer", /*env_do_fsync=*/true) {}
|
||||
void SetUp() override {
|
||||
use_old_interface_ = std::get<0>(GetParam());
|
||||
cost_cache_ = std::get<1>(GetParam());
|
||||
@@ -1481,8 +1482,7 @@ TEST_P(CompressionFailuresTest, CompressionFailures) {
|
||||
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::CompressBlockInternal:TamperWithReturnValue",
|
||||
[](void* arg) {
|
||||
"CompressData:TamperWithReturnValue", [](void* arg) {
|
||||
bool* ret = static_cast<bool*>(arg);
|
||||
*ret = false;
|
||||
});
|
||||
@@ -1791,7 +1791,8 @@ class PinL0IndexAndFilterBlocksTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
PinL0IndexAndFilterBlocksTest() : DBTestBase("/db_pin_l0_index_bloom_test") {}
|
||||
PinL0IndexAndFilterBlocksTest()
|
||||
: DBTestBase("/db_pin_l0_index_bloom_test", /*env_do_fsync=*/true) {}
|
||||
void SetUp() override {
|
||||
infinite_max_files_ = std::get<0>(GetParam());
|
||||
disallow_preload_ = std::get<1>(GetParam());
|
||||
@@ -2158,6 +2159,9 @@ TEST_F(DBTest2, TestPerfContextGetCpuTime) {
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
env_->now_cpu_count_.store(0);
|
||||
env_->SetMockSleep();
|
||||
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
// CPU timing is not enabled with kEnableTimeExceptForMutex
|
||||
SetPerfLevel(PerfLevel::kEnableTimeExceptForMutex);
|
||||
@@ -2165,19 +2169,20 @@ TEST_F(DBTest2, TestPerfContextGetCpuTime) {
|
||||
ASSERT_EQ(0, get_perf_context()->get_cpu_nanos);
|
||||
ASSERT_EQ(0, env_->now_cpu_count_.load());
|
||||
|
||||
uint64_t kDummyAddonTime = uint64_t{1000000000000};
|
||||
constexpr uint64_t kDummyAddonSeconds = uint64_t{1000000};
|
||||
constexpr uint64_t kDummyAddonNanos = 1000000000U * kDummyAddonSeconds;
|
||||
|
||||
// Add time to NowNanos() reading.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::FindTable:0",
|
||||
[&](void* /*arg*/) { env_->addon_time_.fetch_add(kDummyAddonTime); });
|
||||
[&](void* /*arg*/) { env_->MockSleepForSeconds(kDummyAddonSeconds); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
SetPerfLevel(PerfLevel::kEnableTimeAndCPUTimeExceptForMutex);
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
ASSERT_GT(env_->now_cpu_count_.load(), 2);
|
||||
ASSERT_LT(get_perf_context()->get_cpu_nanos, kDummyAddonTime);
|
||||
ASSERT_GT(get_perf_context()->find_table_nanos, kDummyAddonTime);
|
||||
ASSERT_LT(get_perf_context()->get_cpu_nanos, kDummyAddonNanos);
|
||||
ASSERT_GT(get_perf_context()->find_table_nanos, kDummyAddonNanos);
|
||||
|
||||
SetPerfLevel(PerfLevel::kDisable);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -2200,6 +2205,9 @@ TEST_F(DBTest2, TestPerfContextIterCpuTime) {
|
||||
std::string last_key = "k" + ToString(kNumEntries - 1);
|
||||
std::string last_value = "v" + ToString(kNumEntries - 1);
|
||||
env_->now_cpu_count_.store(0);
|
||||
env_->SetMockSleep();
|
||||
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
// CPU timing is not enabled with kEnableTimeExceptForMutex
|
||||
SetPerfLevel(PerfLevel::kEnableTimeExceptForMutex);
|
||||
@@ -2227,12 +2235,13 @@ TEST_F(DBTest2, TestPerfContextIterCpuTime) {
|
||||
ASSERT_EQ(0, env_->now_cpu_count_.load());
|
||||
delete iter;
|
||||
|
||||
uint64_t kDummyAddonTime = uint64_t{1000000000000};
|
||||
constexpr uint64_t kDummyAddonSeconds = uint64_t{1000000};
|
||||
constexpr uint64_t kDummyAddonNanos = 1000000000U * kDummyAddonSeconds;
|
||||
|
||||
// Add time to NowNanos() reading.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::FindTable:0",
|
||||
[&](void* /*arg*/) { env_->addon_time_.fetch_add(kDummyAddonTime); });
|
||||
[&](void* /*arg*/) { env_->MockSleepForSeconds(kDummyAddonSeconds); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
SetPerfLevel(PerfLevel::kEnableTimeAndCPUTimeExceptForMutex);
|
||||
@@ -2249,19 +2258,19 @@ TEST_F(DBTest2, TestPerfContextIterCpuTime) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("v0", iter->value().ToString());
|
||||
ASSERT_GT(get_perf_context()->iter_seek_cpu_nanos, 0);
|
||||
ASSERT_LT(get_perf_context()->iter_seek_cpu_nanos, kDummyAddonTime);
|
||||
ASSERT_LT(get_perf_context()->iter_seek_cpu_nanos, kDummyAddonNanos);
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("v1", iter->value().ToString());
|
||||
ASSERT_GT(get_perf_context()->iter_next_cpu_nanos, 0);
|
||||
ASSERT_LT(get_perf_context()->iter_next_cpu_nanos, kDummyAddonTime);
|
||||
ASSERT_LT(get_perf_context()->iter_next_cpu_nanos, kDummyAddonNanos);
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("v0", iter->value().ToString());
|
||||
ASSERT_GT(get_perf_context()->iter_prev_cpu_nanos, 0);
|
||||
ASSERT_LT(get_perf_context()->iter_prev_cpu_nanos, kDummyAddonTime);
|
||||
ASSERT_LT(get_perf_context()->iter_prev_cpu_nanos, kDummyAddonNanos);
|
||||
ASSERT_GE(env_->now_cpu_count_.load(), 12);
|
||||
ASSERT_GT(get_perf_context()->find_table_nanos, kDummyAddonTime);
|
||||
ASSERT_GT(get_perf_context()->find_table_nanos, kDummyAddonNanos);
|
||||
|
||||
SetPerfLevel(PerfLevel::kDisable);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -2753,9 +2762,9 @@ TEST_F(DBTest2, PausingManualCompaction1) {
|
||||
int manual_compactions_paused = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:1", [&](void* arg) {
|
||||
auto paused = reinterpret_cast<std::atomic<bool>*>(arg);
|
||||
ASSERT_FALSE(paused->load(std::memory_order_acquire));
|
||||
paused->store(true, std::memory_order_release);
|
||||
auto paused = static_cast<std::atomic<int>*>(arg);
|
||||
ASSERT_EQ(0, paused->load(std::memory_order_acquire));
|
||||
paused->fetch_add(1, std::memory_order_release);
|
||||
manual_compactions_paused += 1;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -2914,14 +2923,13 @@ TEST_F(DBTest2, PausingManualCompaction4) {
|
||||
int run_manual_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:2", [&](void* arg) {
|
||||
auto paused = reinterpret_cast<std::atomic<bool>*>(arg);
|
||||
ASSERT_FALSE(paused->load(std::memory_order_acquire));
|
||||
paused->store(true, std::memory_order_release);
|
||||
auto paused = static_cast<std::atomic<int>*>(arg);
|
||||
ASSERT_EQ(0, paused->load(std::memory_order_acquire));
|
||||
paused->fetch_add(1, std::memory_order_release);
|
||||
run_manual_compactions++;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
dbfull()->EnableManualCompaction();
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
ASSERT_EQ(run_manual_compactions, 1);
|
||||
@@ -4797,6 +4805,45 @@ TEST_F(DBTest2, BlockBasedTablePrefixIndexSeekForPrev) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PartitionedIndexPrefetchFailure) {
|
||||
Options options = last_options_;
|
||||
options.max_open_files = 20;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
bbto.metadata_block_size = 128;
|
||||
bbto.block_size = 128;
|
||||
bbto.block_cache = NewLRUCache(16777216);
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Force no table cache so every read will preload the SST file.
|
||||
dbfull()->TEST_table_cache()->SetCapacity(0);
|
||||
bbto.block_cache->SetCapacity(0);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(32)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Try different random failures in table open for 300 times.
|
||||
for (int i = 0; i < 300; i++) {
|
||||
env_->num_reads_fails_ = 0;
|
||||
env_->rand_reads_fail_odd_ = 8;
|
||||
|
||||
std::string value;
|
||||
Status s = dbfull()->Get(ReadOptions(), Key(1), &value);
|
||||
if (env_->num_reads_fails_ > 0) {
|
||||
ASSERT_NOK(s);
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
env_->rand_reads_fail_odd_ = 0;
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, ChangePrefixExtractor) {
|
||||
for (bool use_partitioned_filter : {true, false}) {
|
||||
// create a DB with block prefix index
|
||||
|
||||
+51
-12
@@ -19,21 +19,20 @@ namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
int64_t MaybeCurrentTime(Env* env) {
|
||||
int64_t time = 1337346000; // arbitrary fallback default
|
||||
(void)env->GetCurrentTime(&time);
|
||||
env->GetCurrentTime(&time).PermitUncheckedError();
|
||||
return time;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Special Env used to delay background operations
|
||||
|
||||
SpecialEnv::SpecialEnv(Env* base)
|
||||
SpecialEnv::SpecialEnv(Env* base, bool time_elapse_only_sleep)
|
||||
: EnvWrapper(base),
|
||||
maybe_starting_time_(MaybeCurrentTime(base)),
|
||||
rnd_(301),
|
||||
sleep_counter_(this),
|
||||
addon_time_(0),
|
||||
time_elapse_only_sleep_(false),
|
||||
no_slowdown_(false) {
|
||||
time_elapse_only_sleep_(time_elapse_only_sleep),
|
||||
no_slowdown_(time_elapse_only_sleep) {
|
||||
delay_sstable_sync_.store(false, std::memory_order_release);
|
||||
drop_writes_.store(false, std::memory_order_release);
|
||||
no_space_.store(false, std::memory_order_release);
|
||||
@@ -58,7 +57,7 @@ SpecialEnv::SpecialEnv(Env* base)
|
||||
ROT13BlockCipher rot13Cipher_(16);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
DBTestBase::DBTestBase(const std::string path)
|
||||
DBTestBase::DBTestBase(const std::string path, bool env_do_fsync)
|
||||
: mem_env_(nullptr), encrypted_env_(nullptr), option_config_(kDefault) {
|
||||
Env* base_env = Env::Default();
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -85,6 +84,7 @@ DBTestBase::DBTestBase(const std::string path)
|
||||
: (mem_env_ ? mem_env_ : base_env));
|
||||
env_->SetBackgroundThreads(1, Env::LOW);
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
env_->skip_fsync_ = !env_do_fsync;
|
||||
dbname_ = test::PerThreadDBPath(env_, path);
|
||||
alternative_wal_dir_ = dbname_ + "/wal";
|
||||
alternative_db_log_dir_ = dbname_ + "/db_log_dir";
|
||||
@@ -606,6 +606,39 @@ void DBTestBase::ReopenWithColumnFamilies(const std::vector<std::string>& cfs,
|
||||
ASSERT_OK(TryReopenWithColumnFamilies(cfs, options));
|
||||
}
|
||||
|
||||
void DBTestBase::SetTimeElapseOnlySleepOnReopen(DBOptions* options) {
|
||||
time_elapse_only_sleep_on_reopen_ = true;
|
||||
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, which uses InstrumentedCondVar::TimedWaitInternal.
|
||||
// With time_elapse_only_sleep_, this can hang on some platforms (MacOS)
|
||||
// because (a) on some platforms, pthread_cond_timedwait does not appear
|
||||
// to release the lock for other threads to operate if the deadline time
|
||||
// is already passed, and (b) TimedWait calls are currently a bad abstraction
|
||||
// because the deadline parameter is usually computed from Env time,
|
||||
// but is interpreted in real clock time.
|
||||
options->stats_dump_period_sec = 0;
|
||||
options->stats_persist_period_sec = 0;
|
||||
}
|
||||
|
||||
void DBTestBase::MaybeInstallTimeElapseOnlySleep(const DBOptions& options) {
|
||||
if (time_elapse_only_sleep_on_reopen_) {
|
||||
assert(options.env == env_ ||
|
||||
static_cast_with_check<CompositeEnvWrapper>(options.env)
|
||||
->env_target() == env_);
|
||||
assert(options.stats_dump_period_sec == 0);
|
||||
assert(options.stats_persist_period_sec == 0);
|
||||
// We cannot set these before destroying the last DB because they might
|
||||
// cause a deadlock or similar without the appropriate options set in
|
||||
// the DB.
|
||||
env_->time_elapse_only_sleep_ = true;
|
||||
env_->no_slowdown_ = true;
|
||||
} else {
|
||||
// Going back in same test run is not yet supported, so no
|
||||
// reset in this case.
|
||||
}
|
||||
}
|
||||
|
||||
Status DBTestBase::TryReopenWithColumnFamilies(
|
||||
const std::vector<std::string>& cfs, const std::vector<Options>& options) {
|
||||
Close();
|
||||
@@ -616,6 +649,7 @@ Status DBTestBase::TryReopenWithColumnFamilies(
|
||||
}
|
||||
DBOptions db_opts = DBOptions(options[0]);
|
||||
last_options_ = options[0];
|
||||
MaybeInstallTimeElapseOnlySleep(db_opts);
|
||||
return DB::Open(db_opts, dbname_, column_families, &handles_, &db_);
|
||||
}
|
||||
|
||||
@@ -659,6 +693,7 @@ void DBTestBase::Destroy(const Options& options, bool delete_cf_paths) {
|
||||
}
|
||||
|
||||
Status DBTestBase::ReadOnlyReopen(const Options& options) {
|
||||
MaybeInstallTimeElapseOnlySleep(options);
|
||||
return DB::OpenForReadOnly(options, dbname_, &db_);
|
||||
}
|
||||
|
||||
@@ -673,6 +708,7 @@ Status DBTestBase::TryReopen(const Options& options) {
|
||||
// problem, we manually call destructor of table_facotry which eventually
|
||||
// clears the block cache.
|
||||
last_options_ = options;
|
||||
MaybeInstallTimeElapseOnlySleep(options);
|
||||
return DB::Open(options, dbname_, &db_);
|
||||
}
|
||||
|
||||
@@ -907,12 +943,13 @@ std::string DBTestBase::AllEntriesFor(const Slice& user_key, int cf) {
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter;
|
||||
if (cf == 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
iter.set(dbfull()->NewInternalIterator(read_options, &arena, &range_del_agg,
|
||||
kMaxSequenceNumber));
|
||||
} else {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
iter.set(dbfull()->NewInternalIterator(read_options, &arena, &range_del_agg,
|
||||
kMaxSequenceNumber, handles_[cf]));
|
||||
}
|
||||
InternalKey target(user_key, kMaxSequenceNumber, kTypeValue);
|
||||
@@ -1322,12 +1359,13 @@ void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
// This should be defined after range_del_agg so that it destructs the
|
||||
// assigned iterator before it range_del_agg is already destructed.
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter;
|
||||
if (cf != 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
iter.set(dbfull()->NewInternalIterator(read_options, &arena, &range_del_agg,
|
||||
kMaxSequenceNumber, handles_[cf]));
|
||||
} else {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
iter.set(dbfull()->NewInternalIterator(read_options, &arena, &range_del_agg,
|
||||
kMaxSequenceNumber));
|
||||
}
|
||||
iter->SeekToFirst();
|
||||
@@ -1530,8 +1568,9 @@ void DBTestBase::VerifyDBInternal(
|
||||
InternalKeyComparator icmp(last_options_.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
auto iter =
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, kMaxSequenceNumber);
|
||||
ReadOptions read_options;
|
||||
auto iter = dbfull()->NewInternalIterator(read_options, &arena,
|
||||
&range_del_agg, kMaxSequenceNumber);
|
||||
iter->SeekToFirst();
|
||||
for (auto p : true_data) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
+79
-27
@@ -206,7 +206,7 @@ class SpecialSkipListFactory : public MemTableRepFactory {
|
||||
// Special Env used to delay background operations
|
||||
class SpecialEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit SpecialEnv(Env* base);
|
||||
explicit SpecialEnv(Env* base, bool time_elapse_only_sleep = false);
|
||||
|
||||
Status NewWritableFile(const std::string& f, std::unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
@@ -488,12 +488,44 @@ class SpecialEnv : public EnvWrapper {
|
||||
std::atomic<size_t>* bytes_read_;
|
||||
};
|
||||
|
||||
class RandomFailureFile : public RandomAccessFile {
|
||||
public:
|
||||
RandomFailureFile(std::unique_ptr<RandomAccessFile>&& target,
|
||||
std::atomic<uint64_t>* failure_cnt, uint32_t fail_odd)
|
||||
: target_(std::move(target)),
|
||||
fail_cnt_(failure_cnt),
|
||||
fail_odd_(fail_odd) {}
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
if (Random::GetTLSInstance()->OneIn(fail_odd_)) {
|
||||
fail_cnt_->fetch_add(1);
|
||||
return Status::IOError("random error");
|
||||
}
|
||||
return target_->Read(offset, n, result, scratch);
|
||||
}
|
||||
|
||||
virtual Status Prefetch(uint64_t offset, size_t n) override {
|
||||
return target_->Prefetch(offset, n);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<RandomAccessFile> target_;
|
||||
std::atomic<uint64_t>* fail_cnt_;
|
||||
uint32_t fail_odd_;
|
||||
};
|
||||
|
||||
Status s = target()->NewRandomAccessFile(f, r, soptions);
|
||||
random_file_open_counter_++;
|
||||
if (s.ok() && count_random_reads_) {
|
||||
r->reset(new CountingFile(std::move(*r), &random_read_counter_,
|
||||
&random_read_bytes_counter_));
|
||||
if (s.ok()) {
|
||||
if (count_random_reads_) {
|
||||
r->reset(new CountingFile(std::move(*r), &random_read_counter_,
|
||||
&random_read_bytes_counter_));
|
||||
} else if (rand_reads_fail_odd_ > 0) {
|
||||
r->reset(new RandomFailureFile(std::move(*r), &num_reads_fails_,
|
||||
rand_reads_fail_odd_));
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && soptions.compaction_readahead_size > 0) {
|
||||
compaction_readahead_size_ = soptions.compaction_readahead_size;
|
||||
}
|
||||
@@ -529,13 +561,25 @@ class SpecialEnv : public EnvWrapper {
|
||||
virtual void SleepForMicroseconds(int micros) override {
|
||||
sleep_counter_.Increment();
|
||||
if (no_slowdown_ || time_elapse_only_sleep_) {
|
||||
addon_time_.fetch_add(micros);
|
||||
addon_microseconds_.fetch_add(micros);
|
||||
}
|
||||
if (!no_slowdown_) {
|
||||
target()->SleepForMicroseconds(micros);
|
||||
}
|
||||
}
|
||||
|
||||
void MockSleepForMicroseconds(int64_t micros) {
|
||||
sleep_counter_.Increment();
|
||||
assert(no_slowdown_);
|
||||
addon_microseconds_.fetch_add(micros);
|
||||
}
|
||||
|
||||
void MockSleepForSeconds(int64_t seconds) {
|
||||
sleep_counter_.Increment();
|
||||
assert(no_slowdown_);
|
||||
addon_microseconds_.fetch_add(seconds * 1000000);
|
||||
}
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* unix_time) override {
|
||||
Status s;
|
||||
if (time_elapse_only_sleep_) {
|
||||
@@ -544,9 +588,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
s = target()->GetCurrentTime(unix_time);
|
||||
}
|
||||
if (s.ok()) {
|
||||
// FIXME: addon_time_ sometimes used to mean seconds (here) and
|
||||
// sometimes microseconds
|
||||
*unix_time += addon_time_.load();
|
||||
// mock microseconds elapsed to seconds of time
|
||||
*unix_time += addon_microseconds_.load() / 1000000;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -558,12 +601,12 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
virtual uint64_t NowNanos() override {
|
||||
return (time_elapse_only_sleep_ ? 0 : target()->NowNanos()) +
|
||||
addon_time_.load() * 1000;
|
||||
addon_microseconds_.load() * 1000;
|
||||
}
|
||||
|
||||
virtual uint64_t NowMicros() override {
|
||||
return (time_elapse_only_sleep_ ? 0 : target()->NowMicros()) +
|
||||
addon_time_.load();
|
||||
addon_microseconds_.load();
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(const std::string& fname) override {
|
||||
@@ -571,16 +614,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
void SetTimeElapseOnlySleep(Options* options) {
|
||||
time_elapse_only_sleep_ = true;
|
||||
no_slowdown_ = true;
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, which uses InstrumentedCondVar::TimedWaitInternal.
|
||||
// With time_elapse_only_sleep_, this can hang on some platforms.
|
||||
// TODO: why? investigate/fix
|
||||
options->stats_dump_period_sec = 0;
|
||||
options->stats_persist_period_sec = 0;
|
||||
}
|
||||
void SetMockSleep(bool enabled = true) { no_slowdown_ = enabled; }
|
||||
|
||||
Status NewDirectory(const std::string& name,
|
||||
std::unique_ptr<Directory>* result) override {
|
||||
@@ -634,6 +668,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
std::atomic<int> num_open_wal_file_;
|
||||
|
||||
bool count_random_reads_;
|
||||
uint32_t rand_reads_fail_odd_ = 0;
|
||||
std::atomic<uint64_t> num_reads_fails_;
|
||||
anon::AtomicCounter random_read_counter_;
|
||||
std::atomic<size_t> random_read_bytes_counter_;
|
||||
std::atomic<int> random_file_open_counter_;
|
||||
@@ -658,19 +694,23 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
std::function<void()>* table_write_callback_;
|
||||
|
||||
std::atomic<int64_t> addon_time_;
|
||||
|
||||
std::atomic<int> now_cpu_count_;
|
||||
|
||||
std::atomic<int> delete_count_;
|
||||
|
||||
std::atomic<bool> time_elapse_only_sleep_;
|
||||
|
||||
bool no_slowdown_;
|
||||
|
||||
std::atomic<bool> is_wal_sync_thread_safe_{true};
|
||||
|
||||
std::atomic<size_t> compaction_readahead_size_{};
|
||||
|
||||
private: // accessing these directly is prone to error
|
||||
friend class DBTestBase;
|
||||
|
||||
std::atomic<int64_t> addon_microseconds_{0};
|
||||
|
||||
// Do not modify in the env of a running DB (could cause deadlock)
|
||||
std::atomic<bool> time_elapse_only_sleep_;
|
||||
|
||||
bool no_slowdown_;
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -871,7 +911,10 @@ class DBTestBase : public testing::Test {
|
||||
// requires.
|
||||
kSkipMmapReads;
|
||||
|
||||
explicit DBTestBase(const std::string path);
|
||||
// `env_do_fsync` decides whether the special Env would do real
|
||||
// fsync for files and directories. Skipping fsync can speed up
|
||||
// tests, but won't cover the exact fsync logic.
|
||||
DBTestBase(const std::string path, bool env_do_fsync);
|
||||
|
||||
~DBTestBase();
|
||||
|
||||
@@ -1130,6 +1173,15 @@ class DBTestBase : public testing::Test {
|
||||
Tickers ticker_type) {
|
||||
return options.statistics->getAndResetTickerCount(ticker_type);
|
||||
}
|
||||
|
||||
// Note: reverting this setting within the same test run is not yet
|
||||
// supported
|
||||
void SetTimeElapseOnlySleepOnReopen(DBOptions* options);
|
||||
|
||||
private: // Prone to error on direct use
|
||||
void MaybeInstallTimeElapseOnlySleep(const DBOptions& options);
|
||||
|
||||
bool time_elapse_only_sleep_on_reopen_ = false;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -26,8 +26,8 @@ class DBTestUniversalCompactionBase
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<int, bool>> {
|
||||
public:
|
||||
explicit DBTestUniversalCompactionBase(
|
||||
const std::string& path) : DBTestBase(path) {}
|
||||
explicit DBTestUniversalCompactionBase(const std::string& path)
|
||||
: DBTestBase(path, /*env_do_fsync=*/true) {}
|
||||
void SetUp() override {
|
||||
num_levels_ = std::get<0>(GetParam());
|
||||
exclusive_manual_compaction_ = std::get<1>(GetParam());
|
||||
@@ -44,7 +44,8 @@ class DBTestUniversalCompaction : public DBTestUniversalCompactionBase {
|
||||
|
||||
class DBTestUniversalCompaction2 : public DBTestBase {
|
||||
public:
|
||||
DBTestUniversalCompaction2() : DBTestBase("/db_universal_compaction_test2") {}
|
||||
DBTestUniversalCompaction2()
|
||||
: DBTestBase("/db_universal_compaction_test2", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -91,36 +92,6 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
std::atomic_bool expect_full_compaction_;
|
||||
std::atomic_bool expect_manual_compaction_;
|
||||
};
|
||||
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "DelayFilter"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
};
|
||||
|
||||
class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
const char* Name() const override { return "DelayFilterFactory"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Make sure we don't trigger a problem if the trigger condtion is given
|
||||
@@ -2185,9 +2156,11 @@ TEST_F(DBTestUniversalCompaction2, PeriodicCompaction) {
|
||||
opts.compaction_options_universal.max_size_amplification_percent = 200;
|
||||
opts.periodic_compaction_seconds = 48 * 60 * 60; // 2 days
|
||||
opts.num_levels = 5;
|
||||
env_->addon_time_.store(0);
|
||||
env_->SetMockSleep();
|
||||
Reopen(opts);
|
||||
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
int periodic_compactions = 0;
|
||||
int start_level = -1;
|
||||
int output_level = -1;
|
||||
@@ -2210,7 +2183,7 @@ TEST_F(DBTestUniversalCompaction2, PeriodicCompaction) {
|
||||
ASSERT_EQ(0, periodic_compactions);
|
||||
// Move clock forward so that the flushed file would qualify periodic
|
||||
// compaction.
|
||||
env_->addon_time_.store(48 * 60 * 60 + 100);
|
||||
env_->MockSleepForSeconds(48 * 60 * 60 + 100);
|
||||
|
||||
// Another flush would trigger compaction the oldest file.
|
||||
ASSERT_OK(Put("foo", "bar2"));
|
||||
@@ -2232,7 +2205,7 @@ TEST_F(DBTestUniversalCompaction2, PeriodicCompaction) {
|
||||
// After periodic compaction threshold hits, a flush will trigger
|
||||
// a compaction
|
||||
ASSERT_OK(Put("foo", "bar2"));
|
||||
env_->addon_time_.fetch_add(48 * 60 * 60 + 100);
|
||||
env_->MockSleepForSeconds(48 * 60 * 60 + 100);
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(1, periodic_compactions);
|
||||
|
||||
+18
-9
@@ -18,7 +18,8 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class DBWALTestBase : public DBTestBase {
|
||||
protected:
|
||||
explicit DBWALTestBase(const std::string& dir_name) : DBTestBase(dir_name) {}
|
||||
explicit DBWALTestBase(const std::string& dir_name)
|
||||
: DBTestBase(dir_name, /*env_do_fsync=*/true) {}
|
||||
|
||||
#if defined(ROCKSDB_PLATFORM_POSIX)
|
||||
public:
|
||||
@@ -92,7 +93,8 @@ class EnrichedSpecialEnv : public SpecialEnv {
|
||||
|
||||
class DBWALTestWithEnrichedEnv : public DBTestBase {
|
||||
public:
|
||||
DBWALTestWithEnrichedEnv() : DBTestBase("/db_wal_test") {
|
||||
DBWALTestWithEnrichedEnv()
|
||||
: DBTestBase("/db_wal_test", /*env_do_fsync=*/true) {
|
||||
enriched_env_ = new EnrichedSpecialEnv(env_->target());
|
||||
auto options = CurrentOptions();
|
||||
options.env = enriched_env_;
|
||||
@@ -528,7 +530,10 @@ TEST_F(DBWALTest, PreallocateBlock) {
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBWALTest, FullPurgePreservesRecycledLog) {
|
||||
TEST_F(DBWALTest, DISABLED_FullPurgePreservesRecycledLog) {
|
||||
// TODO(ajkr): Disabled until WAL recycling is fixed for
|
||||
// `kPointInTimeRecovery`.
|
||||
|
||||
// For github issue #1303
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -564,7 +569,10 @@ TEST_F(DBWALTest, FullPurgePreservesRecycledLog) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, FullPurgePreservesLogPendingReuse) {
|
||||
TEST_F(DBWALTest, DISABLED_FullPurgePreservesLogPendingReuse) {
|
||||
// TODO(ajkr): Disabled until WAL recycling is fixed for
|
||||
// `kPointInTimeRecovery`.
|
||||
|
||||
// Ensures full purge cannot delete a WAL while it's in the process of being
|
||||
// recycled. In particular, we force the full purge after a file has been
|
||||
// chosen for reuse, but before it has been renamed.
|
||||
@@ -936,12 +944,13 @@ class RecoveryTestHelper {
|
||||
std::unique_ptr<WalManager> wal_manager;
|
||||
WriteController write_controller;
|
||||
|
||||
versions.reset(new VersionSet(test->dbname_, &db_options, env_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller,
|
||||
/*block_cache_tracer=*/nullptr));
|
||||
versions.reset(new VersionSet(
|
||||
test->dbname_, &db_options, env_options, table_cache.get(),
|
||||
&write_buffer_manager, &write_controller,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr));
|
||||
|
||||
wal_manager.reset(new WalManager(db_options, env_options));
|
||||
wal_manager.reset(
|
||||
new WalManager(db_options, env_options, /*io_tracer=*/nullptr));
|
||||
|
||||
std::unique_ptr<log::Writer> current_log_writer;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBBasicTestWithTimestampBase : public DBTestBase {
|
||||
public:
|
||||
explicit DBBasicTestWithTimestampBase(const std::string& dbname)
|
||||
: DBTestBase(dbname) {}
|
||||
: DBTestBase(dbname, /*env_do_fsync=*/true) {}
|
||||
|
||||
protected:
|
||||
static std::string Key1(uint64_t k) {
|
||||
@@ -120,12 +120,15 @@ class DBBasicTestWithTimestampBase : public DBTestBase {
|
||||
}
|
||||
|
||||
void CheckIterUserEntry(const Iterator* it, const Slice& expected_key,
|
||||
ValueType expected_value_type,
|
||||
const Slice& expected_value,
|
||||
const Slice& expected_ts) const {
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_EQ(expected_key, it->key());
|
||||
ASSERT_EQ(expected_value, it->value());
|
||||
if (kTypeValue == expected_value_type) {
|
||||
ASSERT_EQ(expected_value, it->value());
|
||||
}
|
||||
ASSERT_EQ(expected_ts, it->timestamp());
|
||||
}
|
||||
|
||||
@@ -137,10 +140,30 @@ class DBBasicTestWithTimestampBase : public DBTestBase {
|
||||
std::string ukey_and_ts;
|
||||
ukey_and_ts.assign(expected_ukey.data(), expected_ukey.size());
|
||||
ukey_and_ts.append(expected_ts.data(), expected_ts.size());
|
||||
ParsedInternalKey parsed_ikey(ukey_and_ts, expected_seq, expected_val_type);
|
||||
std::string ikey;
|
||||
AppendInternalKey(&ikey, parsed_ikey);
|
||||
ASSERT_EQ(Slice(ikey), it->key());
|
||||
ParsedInternalKey parsed_ikey;
|
||||
ASSERT_TRUE(ParseInternalKey(it->key(), &parsed_ikey));
|
||||
ASSERT_EQ(ukey_and_ts, parsed_ikey.user_key);
|
||||
ASSERT_EQ(expected_val_type, parsed_ikey.type);
|
||||
ASSERT_EQ(expected_seq, parsed_ikey.sequence);
|
||||
if (expected_val_type == kTypeValue) {
|
||||
ASSERT_EQ(expected_value, it->value());
|
||||
}
|
||||
ASSERT_EQ(expected_ts, it->timestamp());
|
||||
}
|
||||
|
||||
void CheckIterEntry(const Iterator* it, const Slice& expected_ukey,
|
||||
ValueType expected_val_type, const Slice& expected_value,
|
||||
const Slice& expected_ts) {
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
std::string ukey_and_ts;
|
||||
ukey_and_ts.assign(expected_ukey.data(), expected_ukey.size());
|
||||
ukey_and_ts.append(expected_ts.data(), expected_ts.size());
|
||||
|
||||
ParsedInternalKey parsed_ikey;
|
||||
ASSERT_TRUE(ParseInternalKey(it->key(), &parsed_ikey));
|
||||
ASSERT_EQ(expected_val_type, parsed_ikey.type);
|
||||
ASSERT_EQ(Slice(ukey_and_ts), parsed_ikey.user_key);
|
||||
if (expected_val_type == kTypeValue) {
|
||||
ASSERT_EQ(expected_value, it->value());
|
||||
}
|
||||
@@ -188,8 +211,8 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
uint64_t key = 0;
|
||||
for (it->Seek(Key1(0)), key = start_keys[i]; it->Valid();
|
||||
it->Next(), ++count, ++key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), "value" + std::to_string(i),
|
||||
write_timestamps[i]);
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
}
|
||||
size_t expected_count = kMaxKey - start_keys[i] + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
@@ -208,8 +231,8 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
it.reset(db_->NewIterator(read_opts));
|
||||
for (it->SeekToFirst(), key = std::max(l, start_keys[i]), count = 0;
|
||||
it->Valid(); it->Next(), ++key, ++count) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), "value" + std::to_string(i),
|
||||
write_timestamps[i]);
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
}
|
||||
ASSERT_EQ(r - std::max(l, start_keys[i]), count);
|
||||
l += (kMaxKey / 100);
|
||||
@@ -220,8 +243,8 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterateLowerTsBound) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
constexpr int kNumKeysPerFile = 128;
|
||||
constexpr uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -255,17 +278,47 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterateLowerTsBound) {
|
||||
int count = 0;
|
||||
uint64_t key = 0;
|
||||
for (it->Seek(Key1(0)), key = 0; it->Valid(); it->Next(), ++count, ++key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), "value" + std::to_string(i),
|
||||
write_timestamps[i]);
|
||||
CheckIterEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
if (i > 0) {
|
||||
it->Next();
|
||||
CheckIterUserEntry(it.get(), Key1(key), "value" + std::to_string(i - 1),
|
||||
write_timestamps[i - 1]);
|
||||
CheckIterEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i - 1),
|
||||
write_timestamps[i - 1]);
|
||||
}
|
||||
}
|
||||
size_t expected_count = kMaxKey + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
}
|
||||
// Delete all keys@ts=5 and check iteration result with start ts set
|
||||
{
|
||||
std::string write_timestamp = Timestamp(5, 0);
|
||||
WriteOptions write_opts;
|
||||
Slice write_ts = write_timestamp;
|
||||
write_opts.timestamp = &write_ts;
|
||||
for (uint64_t key = 0; key < kMaxKey + 1; ++key) {
|
||||
Status s = db_->Delete(write_opts, Key1(key));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
std::string read_timestamp = Timestamp(6, 0);
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamp;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::string read_timestamp_lb = Timestamp(2, 0);
|
||||
Slice read_ts_lb = read_timestamp_lb;
|
||||
read_opts.iter_start_ts = &read_ts_lb;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
int count = 0;
|
||||
uint64_t key = 0;
|
||||
for (it->Seek(Key1(0)), key = 0; it->Valid(); it->Next(), ++count, ++key) {
|
||||
CheckIterEntry(it.get(), Key1(key), kTypeDeletionWithTimestamp, Slice(),
|
||||
write_ts);
|
||||
// Skip key@ts=3 and land on tombstone key@ts=5
|
||||
it->Next();
|
||||
}
|
||||
ASSERT_EQ(kMaxKey + 1, count);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -296,34 +349,61 @@ TEST_F(DBBasicTestWithTimestamp, ForwardIterateStartSeqnum) {
|
||||
for (size_t i = 0; i != write_ts_list.size(); ++i) {
|
||||
Slice write_ts = write_ts_list[i];
|
||||
write_opts.timestamp = &write_ts;
|
||||
uint64_t k = kMinKey;
|
||||
do {
|
||||
Status s = db_->Put(write_opts, Key1(k), "value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
if (k == kMaxKey) {
|
||||
break;
|
||||
for (uint64_t k = kMaxKey; k >= kMinKey; --k) {
|
||||
Status s;
|
||||
if (k % 2) {
|
||||
s = db_->Put(write_opts, Key1(k), "value" + std::to_string(i));
|
||||
} else {
|
||||
s = db_->Delete(write_opts, Key1(k));
|
||||
}
|
||||
++k;
|
||||
} while (k != 0);
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
start_seqs.push_back(db_->GetLatestSequenceNumber());
|
||||
}
|
||||
std::vector<std::string> read_ts_list;
|
||||
for (int t = 0; t != kNumTimestamps - 1; ++t) {
|
||||
read_ts_list.push_back(Timestamp(2 * t + 3, /*do not care*/ 17));
|
||||
}
|
||||
|
||||
ReadOptions read_opts;
|
||||
// Scan with only read_opts.iter_start_seqnum set.
|
||||
for (size_t i = 0; i != read_ts_list.size(); ++i) {
|
||||
Slice read_ts = read_ts_list[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
read_opts.iter_start_seqnum = start_seqs[i];
|
||||
read_opts.iter_start_seqnum = start_seqs[i] + 1;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
SequenceNumber expected_seq = start_seqs[i] + 1;
|
||||
SequenceNumber expected_seq = start_seqs[i] + (kMaxKey - kMinKey) + 1;
|
||||
uint64_t key = kMinKey;
|
||||
for (iter->Seek(Key1(kMinKey)); iter->Valid(); iter->Next()) {
|
||||
CheckIterEntry(iter.get(), Key1(key), expected_seq, kTypeValue,
|
||||
CheckIterEntry(
|
||||
iter.get(), Key1(key), expected_seq,
|
||||
(key % 2) ? kTypeValue : kTypeDeletionWithTimestamp,
|
||||
(key % 2) ? "value" + std::to_string(i + 1) : std::string(),
|
||||
write_ts_list[i + 1]);
|
||||
++key;
|
||||
--expected_seq;
|
||||
}
|
||||
}
|
||||
// Scan with both read_opts.iter_start_seqnum and read_opts.iter_start_ts set.
|
||||
std::vector<std::string> read_ts_lb_list;
|
||||
for (int t = 0; t < kNumTimestamps - 1; ++t) {
|
||||
read_ts_lb_list.push_back(Timestamp(2 * t, /*do not care*/ 17));
|
||||
}
|
||||
for (size_t i = 0; i < read_ts_list.size(); ++i) {
|
||||
Slice read_ts = read_ts_list[i];
|
||||
Slice read_ts_lb = read_ts_lb_list[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
read_opts.iter_start_ts = &read_ts_lb;
|
||||
read_opts.iter_start_seqnum = start_seqs[i] + 1;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
uint64_t key = kMinKey;
|
||||
SequenceNumber expected_seq = start_seqs[i] + (kMaxKey - kMinKey) + 1;
|
||||
for (it->Seek(Key1(kMinKey)); it->Valid(); it->Next()) {
|
||||
CheckIterEntry(it.get(), Key1(key), expected_seq,
|
||||
(key % 2) ? kTypeValue : kTypeDeletionWithTimestamp,
|
||||
"value" + std::to_string(i + 1), write_ts_list[i + 1]);
|
||||
++key;
|
||||
++expected_seq;
|
||||
--expected_seq;
|
||||
}
|
||||
}
|
||||
Close();
|
||||
@@ -357,7 +437,7 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToTargetTimestamp) {
|
||||
read_opts.timestamp = &ts;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
iter->SeekToFirst();
|
||||
CheckIterUserEntry(iter.get(), "foo", "value0", ts_str);
|
||||
CheckIterUserEntry(iter.get(), "foo", kTypeValue, "value0", ts_str);
|
||||
ASSERT_EQ(
|
||||
1, options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
@@ -403,7 +483,7 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToNextUserKey) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
iter->Seek("a");
|
||||
iter->Next();
|
||||
CheckIterUserEntry(iter.get(), "b", "new_value", ts_str);
|
||||
CheckIterUserEntry(iter.get(), "b", kTypeValue, "new_value", ts_str);
|
||||
ASSERT_EQ(
|
||||
1, options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
@@ -508,6 +588,324 @@ TEST_F(DBBasicTestWithTimestamp, CompactDeletionWithTimestampMarkerToBottom) {
|
||||
Close();
|
||||
}
|
||||
|
||||
class DataVisibilityTest : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
DataVisibilityTest() : DBBasicTestWithTimestampBase("data_visibility_test") {}
|
||||
};
|
||||
|
||||
// Application specifies timestamp but not snapshot.
|
||||
// reader writer
|
||||
// ts'=90
|
||||
// ts=100
|
||||
// seq=10
|
||||
// seq'=11
|
||||
// write finishes
|
||||
// GetImpl(ts,seq)
|
||||
// It is OK to return <k, t1, s1> if ts>=t1 AND seq>=s1. If ts>=1t1 but seq<s1,
|
||||
// the key should not be returned.
|
||||
TEST_F(DataVisibilityTest, PointLookupWithoutSnapshot1) {
|
||||
Options options = CurrentOptions();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::GetImpl:3",
|
||||
"DataVisibilityTest::PointLookupWithoutSnapshot1:BeforePut"},
|
||||
{"DataVisibilityTest::PointLookupWithoutSnapshot1:AfterPut",
|
||||
"DBImpl::GetImpl:4"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
port::Thread writer_thread([this]() {
|
||||
std::string write_ts_str = Timestamp(1, 0);
|
||||
Slice write_ts = write_ts_str;
|
||||
WriteOptions write_opts;
|
||||
write_opts.timestamp = &write_ts;
|
||||
TEST_SYNC_POINT(
|
||||
"DataVisibilityTest::PointLookupWithoutSnapshot1:BeforePut");
|
||||
Status s = db_->Put(write_opts, "foo", "value");
|
||||
ASSERT_OK(s);
|
||||
TEST_SYNC_POINT("DataVisibilityTest::PointLookupWithoutSnapshot1:AfterPut");
|
||||
});
|
||||
ReadOptions read_opts;
|
||||
std::string read_ts_str = Timestamp(3, 0);
|
||||
Slice read_ts = read_ts_str;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, "foo", &value);
|
||||
|
||||
writer_thread.join();
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
Close();
|
||||
}
|
||||
|
||||
// Application specifies timestamp but not snapshot.
|
||||
// reader writer
|
||||
// ts'=90
|
||||
// ts=100
|
||||
// seq=10
|
||||
// seq'=11
|
||||
// write finishes
|
||||
// Flush
|
||||
// GetImpl(ts,seq)
|
||||
// It is OK to return <k, t1, s1> if ts>=t1 AND seq>=s1. If ts>=t1 but seq<s1,
|
||||
// the key should not be returned.
|
||||
TEST_F(DataVisibilityTest, PointLookupWithoutSnapshot2) {
|
||||
Options options = CurrentOptions();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::GetImpl:3",
|
||||
"DataVisibilityTest::PointLookupWithoutSnapshot2:BeforePut"},
|
||||
{"DataVisibilityTest::PointLookupWithoutSnapshot2:AfterPut",
|
||||
"DBImpl::GetImpl:4"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
port::Thread writer_thread([this]() {
|
||||
std::string write_ts_str = Timestamp(1, 0);
|
||||
Slice write_ts = write_ts_str;
|
||||
WriteOptions write_opts;
|
||||
write_opts.timestamp = &write_ts;
|
||||
TEST_SYNC_POINT(
|
||||
"DataVisibilityTest::PointLookupWithoutSnapshot2:BeforePut");
|
||||
Status s = db_->Put(write_opts, "foo", "value");
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
write_ts_str = Timestamp(2, 0);
|
||||
write_ts = write_ts_str;
|
||||
write_opts.timestamp = &write_ts;
|
||||
s = db_->Put(write_opts, "bar", "value");
|
||||
ASSERT_OK(s);
|
||||
TEST_SYNC_POINT("DataVisibilityTest::PointLookupWithoutSnapshot2:AfterPut");
|
||||
});
|
||||
ReadOptions read_opts;
|
||||
std::string read_ts_str = Timestamp(3, 0);
|
||||
Slice read_ts = read_ts_str;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, "foo", &value);
|
||||
writer_thread.join();
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
Close();
|
||||
}
|
||||
|
||||
// Application specifies both timestamp and snapshot.
|
||||
// reader writer
|
||||
// seq=10
|
||||
// ts'=90
|
||||
// ts=100
|
||||
// seq'=11
|
||||
// write finishes
|
||||
// GetImpl(ts,seq)
|
||||
// Since application specifies both timestamp and snapshot, application expects
|
||||
// to see data that visible in BOTH timestamp and sequence number. Therefore,
|
||||
// <k, t1, s1> can be returned only if t1<=ts AND s1<=seq.
|
||||
TEST_F(DataVisibilityTest, PointLookupWithSnapshot1) {
|
||||
Options options = CurrentOptions();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DataVisibilityTest::PointLookupWithSnapshot1:AfterTakingSnap",
|
||||
"DataVisibilityTest::PointLookupWithSnapshot1:BeforePut"},
|
||||
{"DataVisibilityTest::PointLookupWithSnapshot1:AfterPut",
|
||||
"DBImpl::GetImpl:1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
port::Thread writer_thread([this]() {
|
||||
std::string write_ts_str = Timestamp(1, 0);
|
||||
Slice write_ts = write_ts_str;
|
||||
WriteOptions write_opts;
|
||||
write_opts.timestamp = &write_ts;
|
||||
TEST_SYNC_POINT("DataVisibilityTest::PointLookupWithSnapshot1:BeforePut");
|
||||
Status s = db_->Put(write_opts, "foo", "value");
|
||||
TEST_SYNC_POINT("DataVisibilityTest::PointLookupWithSnapshot1:AfterPut");
|
||||
ASSERT_OK(s);
|
||||
});
|
||||
ReadOptions read_opts;
|
||||
const Snapshot* snap = db_->GetSnapshot();
|
||||
TEST_SYNC_POINT(
|
||||
"DataVisibilityTest::PointLookupWithSnapshot1:AfterTakingSnap");
|
||||
read_opts.snapshot = snap;
|
||||
std::string read_ts_str = Timestamp(3, 0);
|
||||
Slice read_ts = read_ts_str;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, "foo", &value);
|
||||
writer_thread.join();
|
||||
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
|
||||
db_->ReleaseSnapshot(snap);
|
||||
Close();
|
||||
}
|
||||
|
||||
// Application specifies both timestamp and snapshot.
|
||||
// reader writer
|
||||
// seq=10
|
||||
// ts'=90
|
||||
// ts=100
|
||||
// seq'=11
|
||||
// write finishes
|
||||
// Flush
|
||||
// GetImpl(ts,seq)
|
||||
// Since application specifies both timestamp and snapshot, application expects
|
||||
// to see data that visible in BOTH timestamp and sequence number. Therefore,
|
||||
// <k, t1, s1> can be returned only if t1<=ts AND s1<=seq.
|
||||
TEST_F(DataVisibilityTest, PointLookupWithSnapshot2) {
|
||||
Options options = CurrentOptions();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DataVisibilityTest::PointLookupWithSnapshot2:AfterTakingSnap",
|
||||
"DataVisibilityTest::PointLookupWithSnapshot2:BeforePut"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
port::Thread writer_thread([this]() {
|
||||
std::string write_ts_str = Timestamp(1, 0);
|
||||
Slice write_ts = write_ts_str;
|
||||
WriteOptions write_opts;
|
||||
write_opts.timestamp = &write_ts;
|
||||
TEST_SYNC_POINT("DataVisibilityTest::PointLookupWithSnapshot2:BeforePut");
|
||||
Status s = db_->Put(write_opts, "foo", "value1");
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
write_ts_str = Timestamp(2, 0);
|
||||
write_ts = write_ts_str;
|
||||
write_opts.timestamp = &write_ts;
|
||||
s = db_->Put(write_opts, "bar", "value2");
|
||||
ASSERT_OK(s);
|
||||
});
|
||||
const Snapshot* snap = db_->GetSnapshot();
|
||||
TEST_SYNC_POINT(
|
||||
"DataVisibilityTest::PointLookupWithSnapshot2:AfterTakingSnap");
|
||||
writer_thread.join();
|
||||
std::string read_ts_str = Timestamp(3, 0);
|
||||
Slice read_ts = read_ts_str;
|
||||
ReadOptions read_opts;
|
||||
read_opts.snapshot = snap;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, "foo", &value);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
db_->ReleaseSnapshot(snap);
|
||||
Close();
|
||||
}
|
||||
|
||||
// Application specifies timestamp but not snapshot.
|
||||
// reader writer
|
||||
// ts'=90
|
||||
// ts=100
|
||||
// seq=10
|
||||
// seq'=11
|
||||
// write finishes
|
||||
// scan(ts,seq)
|
||||
// <k, t1, s1> can be seen in scan as long as ts>=t1 AND seq>=s1. If ts>=t1 but
|
||||
// seq<s1, then the key should not be returned.
|
||||
TEST_F(DataVisibilityTest, RangeScanWithoutSnapshot) {
|
||||
Options options = CurrentOptions();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::NewIterator:3",
|
||||
"DataVisibilityTest::RangeScanWithoutSnapshot:BeforePut"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
port::Thread writer_thread([this]() {
|
||||
WriteOptions write_opts;
|
||||
TEST_SYNC_POINT("DataVisibilityTest::RangeScanWithoutSnapshot:BeforePut");
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
std::string write_ts_str = Timestamp(i + 1, 0);
|
||||
Slice write_ts = write_ts_str;
|
||||
write_opts.timestamp = &write_ts;
|
||||
Status s = db_->Put(write_opts, "key" + std::to_string(i),
|
||||
"value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
});
|
||||
std::string read_ts_str = Timestamp(10, 0);
|
||||
Slice read_ts = read_ts_str;
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
read_opts.timestamp = &read_ts;
|
||||
Iterator* it = db_->NewIterator(read_opts);
|
||||
ASSERT_NE(nullptr, it);
|
||||
writer_thread.join();
|
||||
it->SeekToFirst();
|
||||
ASSERT_FALSE(it->Valid());
|
||||
delete it;
|
||||
Close();
|
||||
}
|
||||
|
||||
// Application specifies both timestamp and snapshot.
|
||||
// reader writer
|
||||
// seq=10
|
||||
// ts'=90
|
||||
// ts=100 seq'=11
|
||||
// write finishes
|
||||
// scan(ts,seq)
|
||||
// <k, t1, s1> can be seen by the scan only if t1<=ts AND s1<=seq. If t1<=ts
|
||||
// but s1>seq, then the key should not be returned.
|
||||
TEST_F(DataVisibilityTest, RangeScanWithSnapshot) {
|
||||
Options options = CurrentOptions();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DataVisibilityTest::RangeScanWithSnapshot:AfterTakingSnapshot",
|
||||
"DataVisibilityTest::RangeScanWithSnapshot:BeforePut"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
port::Thread writer_thread([this]() {
|
||||
WriteOptions write_opts;
|
||||
TEST_SYNC_POINT("DataVisibilityTest::RangeScanWithSnapshot:BeforePut");
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
std::string write_ts_str = Timestamp(i + 1, 0);
|
||||
Slice write_ts = write_ts_str;
|
||||
write_opts.timestamp = &write_ts;
|
||||
Status s = db_->Put(write_opts, "key" + std::to_string(i),
|
||||
"value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
});
|
||||
const Snapshot* snap = db_->GetSnapshot();
|
||||
TEST_SYNC_POINT(
|
||||
"DataVisibilityTest::RangeScanWithSnapshot:AfterTakingSnapshot");
|
||||
|
||||
writer_thread.join();
|
||||
|
||||
std::string read_ts_str = Timestamp(10, 0);
|
||||
Slice read_ts = read_ts_str;
|
||||
ReadOptions read_opts;
|
||||
read_opts.snapshot = snap;
|
||||
read_opts.total_order_seek = true;
|
||||
read_opts.timestamp = &read_ts;
|
||||
Iterator* it = db_->NewIterator(read_opts);
|
||||
ASSERT_NE(nullptr, it);
|
||||
it->Seek("key0");
|
||||
ASSERT_FALSE(it->Valid());
|
||||
|
||||
delete it;
|
||||
db_->ReleaseSnapshot(snap);
|
||||
Close();
|
||||
}
|
||||
|
||||
class DBBasicTestWithTimestampCompressionSettings
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
@@ -1049,8 +1447,8 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
|
||||
// Seek to kMaxKey
|
||||
iter->Seek(Key1(kMaxKey));
|
||||
CheckIterUserEntry(iter.get(), Key1(kMaxKey), "value" + std::to_string(i),
|
||||
write_ts_list[i]);
|
||||
CheckIterUserEntry(iter.get(), Key1(kMaxKey), kTypeValue,
|
||||
"value" + std::to_string(i), write_ts_list[i]);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
}
|
||||
@@ -1084,7 +1482,7 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
pe->Transform(saved_prev_key) != pe->Transform(start_key)) {
|
||||
break;
|
||||
}
|
||||
CheckIterUserEntry(it.get(), Key1(expected_key),
|
||||
CheckIterUserEntry(it.get(), Key1(expected_key), kTypeValue,
|
||||
"value" + std::to_string(i), write_ts_list[i]);
|
||||
++count;
|
||||
++expected_key;
|
||||
|
||||
@@ -31,7 +31,7 @@ std::string Timestamp(uint64_t ts) {
|
||||
class TimestampCompatibleCompactionTest : public DBTestBase {
|
||||
public:
|
||||
TimestampCompatibleCompactionTest()
|
||||
: DBTestBase("/ts_compatible_compaction_test") {}
|
||||
: DBTestBase("/ts_compatible_compaction_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::string Get(const std::string& key, uint64_t ts) {
|
||||
ReadOptions read_opts;
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// Test variations of WriteImpl.
|
||||
class DBWriteTest : public DBTestBase, public testing::WithParamInterface<int> {
|
||||
public:
|
||||
DBWriteTest() : DBTestBase("/db_write_test") {}
|
||||
DBWriteTest() : DBTestBase("/db_write_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
Options GetOptions() { return DBTestBase::GetOptions(GetParam()); }
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ EntryType GetEntryType(ValueType value_type) {
|
||||
return kEntryPut;
|
||||
case kTypeDeletion:
|
||||
return kEntryDelete;
|
||||
case kTypeDeletionWithTimestamp:
|
||||
return kEntryDeleteWithTimestamp;
|
||||
case kTypeSingleDeletion:
|
||||
return kEntrySingleDelete;
|
||||
case kTypeMerge:
|
||||
|
||||
@@ -35,7 +35,7 @@ class DeleteFileTest : public DBTestBase {
|
||||
const std::string wal_dir_;
|
||||
|
||||
DeleteFileTest()
|
||||
: DBTestBase("/deletefile_test"),
|
||||
: DBTestBase("/deletefile_test", /*env_do_fsync=*/true),
|
||||
numlevels_(7),
|
||||
wal_dir_(dbname_ + "/wal_files") {}
|
||||
|
||||
|
||||
+121
-3
@@ -32,6 +32,14 @@ std::map<std::tuple<BackgroundErrorReason, Status::Code, Status::SubCode, bool>,
|
||||
Status::Code::kIOError, Status::SubCode::kSpaceLimit,
|
||||
true),
|
||||
Status::Severity::kHardError},
|
||||
{std::make_tuple(BackgroundErrorReason::kCompaction,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
true),
|
||||
Status::Severity::kFatalError},
|
||||
{std::make_tuple(BackgroundErrorReason::kCompaction,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
false),
|
||||
Status::Severity::kFatalError},
|
||||
// Errors during BG flush
|
||||
{std::make_tuple(BackgroundErrorReason::kFlush, Status::Code::kIOError,
|
||||
Status::SubCode::kNoSpace, true),
|
||||
@@ -42,6 +50,12 @@ std::map<std::tuple<BackgroundErrorReason, Status::Code, Status::SubCode, bool>,
|
||||
{std::make_tuple(BackgroundErrorReason::kFlush, Status::Code::kIOError,
|
||||
Status::SubCode::kSpaceLimit, true),
|
||||
Status::Severity::kHardError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlush, Status::Code::kIOError,
|
||||
Status::SubCode::kIOFenced, true),
|
||||
Status::Severity::kFatalError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlush, Status::Code::kIOError,
|
||||
Status::SubCode::kIOFenced, false),
|
||||
Status::Severity::kFatalError},
|
||||
// Errors during Write
|
||||
{std::make_tuple(BackgroundErrorReason::kWriteCallback,
|
||||
Status::Code::kIOError, Status::SubCode::kNoSpace,
|
||||
@@ -51,6 +65,14 @@ std::map<std::tuple<BackgroundErrorReason, Status::Code, Status::SubCode, bool>,
|
||||
Status::Code::kIOError, Status::SubCode::kNoSpace,
|
||||
false),
|
||||
Status::Severity::kHardError},
|
||||
{std::make_tuple(BackgroundErrorReason::kWriteCallback,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
true),
|
||||
Status::Severity::kFatalError},
|
||||
{std::make_tuple(BackgroundErrorReason::kWriteCallback,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
false),
|
||||
Status::Severity::kFatalError},
|
||||
// Errors during MANIFEST write
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWrite,
|
||||
Status::Code::kIOError, Status::SubCode::kNoSpace,
|
||||
@@ -60,6 +82,36 @@ std::map<std::tuple<BackgroundErrorReason, Status::Code, Status::SubCode, bool>,
|
||||
Status::Code::kIOError, Status::SubCode::kNoSpace,
|
||||
false),
|
||||
Status::Severity::kHardError},
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWrite,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
true),
|
||||
Status::Severity::kFatalError},
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWrite,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
false),
|
||||
Status::Severity::kFatalError},
|
||||
// Errors during BG flush with WAL disabled
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kNoSpace,
|
||||
true),
|
||||
Status::Severity::kHardError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kNoSpace,
|
||||
false),
|
||||
Status::Severity::kNoError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kSpaceLimit,
|
||||
true),
|
||||
Status::Severity::kHardError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
true),
|
||||
Status::Severity::kFatalError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
false),
|
||||
Status::Severity::kFatalError},
|
||||
|
||||
};
|
||||
|
||||
std::map<std::tuple<BackgroundErrorReason, Status::Code, bool>,
|
||||
@@ -110,6 +162,19 @@ std::map<std::tuple<BackgroundErrorReason, Status::Code, bool>,
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWrite,
|
||||
Status::Code::kIOError, false),
|
||||
Status::Severity::kFatalError},
|
||||
// Errors during BG flush with WAL disabled
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kCorruption, true),
|
||||
Status::Severity::kUnrecoverableError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kCorruption, false),
|
||||
Status::Severity::kNoError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kIOError, true),
|
||||
Status::Severity::kFatalError},
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kIOError, false),
|
||||
Status::Severity::kNoError},
|
||||
};
|
||||
|
||||
std::map<std::tuple<BackgroundErrorReason, bool>, Status::Severity>
|
||||
@@ -188,6 +253,7 @@ Status ErrorHandler::SetBGError(const Status& bg_err, BackgroundErrorReason reas
|
||||
bool paranoid = db_options_.paranoid_checks;
|
||||
Status::Severity sev = Status::Severity::kFatalError;
|
||||
Status new_bg_err;
|
||||
DBRecoverContext context;
|
||||
bool found = false;
|
||||
|
||||
{
|
||||
@@ -246,6 +312,7 @@ Status ErrorHandler::SetBGError(const Status& bg_err, BackgroundErrorReason reas
|
||||
}
|
||||
}
|
||||
|
||||
recover_context_ = context;
|
||||
if (auto_recovery) {
|
||||
recovery_in_prog_ = true;
|
||||
|
||||
@@ -273,8 +340,10 @@ Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
// Always returns ok
|
||||
db_->DisableFileDeletionsWithLock();
|
||||
}
|
||||
|
||||
Status new_bg_io_err = bg_io_err;
|
||||
Status s;
|
||||
DBRecoverContext context;
|
||||
if (bg_io_err.GetDataLoss()) {
|
||||
// FIrst, data loss is treated as unrecoverable error. So it can directly
|
||||
// overwrite any existing bg_error_.
|
||||
@@ -286,6 +355,7 @@ Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
}
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason, &s,
|
||||
db_mutex_, &auto_recovery);
|
||||
recover_context_ = context;
|
||||
return bg_error_;
|
||||
} else if (bg_io_err.GetRetryable()) {
|
||||
// Second, check if the error is a retryable IO error or not. if it is
|
||||
@@ -302,7 +372,27 @@ Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
if (bg_err.severity() > bg_error_.severity()) {
|
||||
bg_error_ = bg_err;
|
||||
}
|
||||
recover_context_ = context;
|
||||
return bg_error_;
|
||||
} else if (BackgroundErrorReason::kFlushNoWAL == reason) {
|
||||
// When the BG Retryable IO error reason is flush without WAL,
|
||||
// We map it to a soft error. At the same time, all the background work
|
||||
// should be stopped except the BG work from recovery. Therefore, we
|
||||
// set the soft_error_no_bg_work_ to true. At the same time, since DB
|
||||
// continues to receive writes when BG error is soft error, to avoid
|
||||
// to many small memtable being generated during auto resume, the flush
|
||||
// reason is set to kErrorRecoveryRetryFlush.
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kSoftError);
|
||||
if (recovery_in_prog_ && recovery_error_.ok()) {
|
||||
recovery_error_ = bg_err;
|
||||
}
|
||||
if (bg_err.severity() > bg_error_.severity()) {
|
||||
bg_error_ = bg_err;
|
||||
}
|
||||
soft_error_no_bg_work_ = true;
|
||||
context.flush_reason = FlushReason::kErrorRecoveryRetryFlush;
|
||||
recover_context_ = context;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
} else {
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kHardError);
|
||||
if (recovery_in_prog_ && recovery_error_.ok()) {
|
||||
@@ -311,6 +401,7 @@ Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
if (bg_err.severity() > bg_error_.severity()) {
|
||||
bg_error_ = bg_err;
|
||||
}
|
||||
recover_context_ = context;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
}
|
||||
} else {
|
||||
@@ -377,6 +468,7 @@ Status ErrorHandler::ClearBGError() {
|
||||
Status old_bg_error = bg_error_;
|
||||
bg_error_ = Status::OK();
|
||||
recovery_in_prog_ = false;
|
||||
soft_error_no_bg_work_ = false;
|
||||
EventHelpers::NotifyOnErrorRecoveryCompleted(db_options_.listeners,
|
||||
old_bg_error, db_mutex_);
|
||||
}
|
||||
@@ -389,6 +481,7 @@ Status ErrorHandler::ClearBGError() {
|
||||
Status ErrorHandler::RecoverFromBGError(bool is_manual) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
InstrumentedMutexLock l(db_mutex_);
|
||||
bool no_bg_work_original_flag = soft_error_no_bg_work_;
|
||||
if (is_manual) {
|
||||
// If its a manual recovery and there's a background recovery in progress
|
||||
// return busy status
|
||||
@@ -396,9 +489,24 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
|
||||
return Status::Busy();
|
||||
}
|
||||
recovery_in_prog_ = true;
|
||||
|
||||
// In manual resume, we allow the bg work to run. If it is a auto resume,
|
||||
// the bg work should follow this tag.
|
||||
soft_error_no_bg_work_ = false;
|
||||
|
||||
// In manual resume, if the bg error is a soft error and also requires
|
||||
// no bg work, the error must be recovered by call the flush with
|
||||
// flush reason: kErrorRecoveryRetryFlush. In other case, the flush
|
||||
// reason is set to kErrorRecovery.
|
||||
if (no_bg_work_original_flag) {
|
||||
recover_context_.flush_reason = FlushReason::kErrorRecoveryRetryFlush;
|
||||
} else {
|
||||
recover_context_.flush_reason = FlushReason::kErrorRecovery;
|
||||
}
|
||||
}
|
||||
|
||||
if (bg_error_.severity() == Status::Severity::kSoftError) {
|
||||
if (bg_error_.severity() == Status::Severity::kSoftError &&
|
||||
recover_context_.flush_reason == FlushReason::kErrorRecovery) {
|
||||
// Simply clear the background error and return
|
||||
recovery_error_ = Status::OK();
|
||||
return ClearBGError();
|
||||
@@ -408,7 +516,13 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
|
||||
// during the recovery process. While recovering, the only operations that
|
||||
// can generate background errors should be the flush operations
|
||||
recovery_error_ = Status::OK();
|
||||
Status s = db_->ResumeImpl();
|
||||
Status s = db_->ResumeImpl(recover_context_);
|
||||
if (s.ok()) {
|
||||
soft_error_no_bg_work_ = false;
|
||||
} else {
|
||||
soft_error_no_bg_work_ = no_bg_work_original_flag;
|
||||
}
|
||||
|
||||
// For manual recover, shutdown, and fatal error cases, set
|
||||
// recovery_in_prog_ to false. For automatic background recovery, leave it
|
||||
// as is regardless of success or failure as it will be retried
|
||||
@@ -461,6 +575,7 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
if (end_recovery_) {
|
||||
return;
|
||||
}
|
||||
DBRecoverContext context = recover_context_;
|
||||
int resume_count = db_options_.max_bgerror_resume_count;
|
||||
uint64_t wait_interval = db_options_.bgerror_resume_retry_interval;
|
||||
// Recover from the retryable error. Create a separate thread to do it.
|
||||
@@ -472,7 +587,7 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:BeforeResume1");
|
||||
recovery_io_error_ = IOStatus::OK();
|
||||
recovery_error_ = Status::OK();
|
||||
Status s = db_->ResumeImpl();
|
||||
Status s = db_->ResumeImpl(context);
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:AfterResume0");
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:AfterResume1");
|
||||
if (s.IsShutdownInProgress() ||
|
||||
@@ -507,6 +622,9 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
EventHelpers::NotifyOnErrorRecoveryCompleted(db_options_.listeners,
|
||||
old_bg_error, db_mutex_);
|
||||
recovery_in_prog_ = false;
|
||||
if (soft_error_no_bg_work_) {
|
||||
soft_error_no_bg_work_ = false;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:RecoverFail1");
|
||||
|
||||
+27
-3
@@ -14,6 +14,17 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBImpl;
|
||||
|
||||
// This structure is used to store the DB recovery context. The context is
|
||||
// the information that related to the recover actions. For example, it contains
|
||||
// FlushReason, which tells the flush job why this flush is called.
|
||||
struct DBRecoverContext {
|
||||
FlushReason flush_reason;
|
||||
|
||||
DBRecoverContext() : flush_reason(FlushReason::kErrorRecovery) {}
|
||||
|
||||
DBRecoverContext(FlushReason reason) : flush_reason(reason) {}
|
||||
};
|
||||
|
||||
class ErrorHandler {
|
||||
public:
|
||||
ErrorHandler(DBImpl* db, const ImmutableDBOptions& db_options,
|
||||
@@ -28,8 +39,13 @@ class ErrorHandler {
|
||||
recovery_thread_(nullptr),
|
||||
db_mutex_(db_mutex),
|
||||
auto_recovery_(false),
|
||||
recovery_in_prog_(false) {}
|
||||
~ErrorHandler() {}
|
||||
recovery_in_prog_(false),
|
||||
soft_error_no_bg_work_(false) {}
|
||||
~ErrorHandler() {
|
||||
bg_error_.PermitUncheckedError();
|
||||
recovery_error_.PermitUncheckedError();
|
||||
recovery_io_error_.PermitUncheckedError();
|
||||
}
|
||||
|
||||
void EnableAutoRecovery() { auto_recovery_ = true; }
|
||||
|
||||
@@ -55,9 +71,11 @@ class ErrorHandler {
|
||||
bool IsBGWorkStopped() {
|
||||
return !bg_error_.ok() &&
|
||||
(bg_error_.severity() >= Status::Severity::kHardError ||
|
||||
!auto_recovery_);
|
||||
!auto_recovery_ || soft_error_no_bg_work_);
|
||||
}
|
||||
|
||||
bool IsSoftErrorNoBGWork() { return soft_error_no_bg_work_; }
|
||||
|
||||
bool IsRecoveryInProgress() { return recovery_in_prog_; }
|
||||
|
||||
Status RecoverFromBGError(bool is_manual = false);
|
||||
@@ -85,6 +103,12 @@ class ErrorHandler {
|
||||
// A flag indicating whether automatic recovery from errors is enabled
|
||||
bool auto_recovery_;
|
||||
bool recovery_in_prog_;
|
||||
// A flag to indicate that for the soft error, we should not allow any
|
||||
// backrgound work execpt the work is from recovery.
|
||||
bool soft_error_no_bg_work_;
|
||||
|
||||
// Used to store the context for recover, such as flush reason.
|
||||
DBRecoverContext recover_context_;
|
||||
|
||||
Status OverrideNoSpaceError(Status bg_error, bool* auto_recovery);
|
||||
void RecoverFromNoSpace();
|
||||
|
||||
+436
-2
@@ -24,7 +24,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBErrorHandlingFSTest : public DBTestBase {
|
||||
public:
|
||||
DBErrorHandlingFSTest() : DBTestBase("/db_error_handling_fs_test") {}
|
||||
DBErrorHandlingFSTest()
|
||||
: DBTestBase("/db_error_handling_fs_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::string GetManifestNameFromLiveFiles() {
|
||||
std::vector<std::string> live_files;
|
||||
@@ -182,7 +183,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWriteError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeError) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
@@ -246,6 +247,143 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError1) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
WriteOptions wo = WriteOptions();
|
||||
wo.disableWAL = true;
|
||||
Put(Key(1), "val1", wo);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
Put(Key(2), "val2", wo);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
Put(Key(3), "val3", wo);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError2) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
WriteOptions wo = WriteOptions();
|
||||
wo.disableWAL = true;
|
||||
|
||||
Put(Key(1), "val1", wo);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeSyncTable",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
Put(Key(2), "val2", wo);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
Put(Key(3), "val3", wo);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError3) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
WriteOptions wo = WriteOptions();
|
||||
wo.disableWAL = true;
|
||||
|
||||
Put(Key(1), "val1", wo);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeCloseTableFile",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
Put(Key(2), "val2", wo);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
Put(Key(3), "val3", wo);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, ManifestWriteError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
@@ -1212,6 +1350,114 @@ TEST_F(DBErrorHandlingFSTest, MultiDBVariousErrors) {
|
||||
delete def_env;
|
||||
}
|
||||
|
||||
// When Put the KV-pair, the write option is set to disable WAL.
|
||||
// If retryable error happens in this condition, map the bg error
|
||||
// to soft error and trigger auto resume. During auto resume, SwitchMemtable
|
||||
// is disabled to avoid small SST tables. Write can still be applied before
|
||||
// the bg error is cleaned unless the memtable is full.
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover1) {
|
||||
// Activate the FS before the first resume
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 100000; // 0.1 second
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
WriteOptions wo = WriteOptions();
|
||||
wo.disableWAL = true;
|
||||
Put(Key(1), "val1", wo);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"RecoverFromRetryableBGIOError:LoopOut",
|
||||
"FLushWritNoWALRetryableeErrorAutoRecover1:1"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
TEST_SYNC_POINT("FLushWritNoWALRetryableeErrorAutoRecover1:1");
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
Put(Key(2), "val2", wo);
|
||||
s = Flush();
|
||||
// Since auto resume fails, the bg error is not cleand, flush will
|
||||
// return the bg_error set before.
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
|
||||
// call auto resume
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
Put(Key(3), "val3", wo);
|
||||
s = Flush();
|
||||
// After resume is successful, the flush should be ok.
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover2) {
|
||||
// Activate the FS before the first resume
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 100000; // 0.1 second
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
WriteOptions wo = WriteOptions();
|
||||
wo.disableWAL = true;
|
||||
Put(Key(1), "val1", wo);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
Put(Key(2), "val2", wo);
|
||||
s = Flush();
|
||||
// Since auto resume is successful, the bg error is cleaned, flush will
|
||||
// be successful.
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, DISABLED_FLushWritRetryableeErrorAutoRecover1) {
|
||||
// Fail the first resume and make the second resume successful
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
@@ -1961,6 +2207,194 @@ TEST_F(DBErrorHandlingFSTest, WALWriteRetryableErrorAutoRecover2) {
|
||||
Close();
|
||||
}
|
||||
|
||||
class DBErrorHandlingFencingTest : public DBErrorHandlingFSTest,
|
||||
public testing::WithParamInterface<bool> {};
|
||||
|
||||
TEST_P(DBErrorHandlingFencingTest, FLushWriteFenced) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.paranoid_checks = GetParam();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(true);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Put(Key(0), "val");
|
||||
SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) {
|
||||
fault_fs->SetFilesystemActive(false, IOStatus::IOFenced("IO fenced"));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError);
|
||||
ASSERT_TRUE(s.IsIOFenced());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_TRUE(s.IsIOFenced());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBErrorHandlingFencingTest, ManifestWriteFenced) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.paranoid_checks = GetParam();
|
||||
Status s;
|
||||
std::string old_manifest;
|
||||
std::string new_manifest;
|
||||
|
||||
listener->EnableAutoRecovery(true);
|
||||
DestroyAndReopen(options);
|
||||
old_manifest = GetManifestNameFromLiveFiles();
|
||||
|
||||
Put(Key(0), "val");
|
||||
Flush();
|
||||
Put(Key(1), "val");
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest", [&](void*) {
|
||||
fault_fs->SetFilesystemActive(false, IOStatus::IOFenced("IO fenced"));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError);
|
||||
ASSERT_TRUE(s.IsIOFenced());
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_TRUE(s.IsIOFenced());
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBErrorHandlingFencingTest, CompactionWriteFenced) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.paranoid_checks = GetParam();
|
||||
Status s;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Put(Key(0), "va;");
|
||||
Put(Key(2), "va;");
|
||||
s = Flush();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
|
||||
listener->EnableAutoRecovery(true);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BackgroundCallCompaction:0", [&](void*) {
|
||||
fault_fs->SetFilesystemActive(false, IOStatus::IOFenced("IO fenced"));
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put(Key(1), "val");
|
||||
s = Flush();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError);
|
||||
ASSERT_TRUE(s.IsIOFenced());
|
||||
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_TRUE(s.IsIOFenced());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBErrorHandlingFencingTest, WALWriteFenced) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.writable_file_max_buffer_size = 32768;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.paranoid_checks = GetParam();
|
||||
Status s;
|
||||
Random rnd(301);
|
||||
|
||||
listener->EnableAutoRecovery(true);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
{
|
||||
WriteBatch batch;
|
||||
|
||||
for (auto i = 0; i < 100; ++i) {
|
||||
batch.Put(Key(i), rnd.RandomString(1024));
|
||||
}
|
||||
|
||||
WriteOptions wopts;
|
||||
wopts.sync = true;
|
||||
ASSERT_EQ(dbfull()->Write(wopts, &batch), Status::OK());
|
||||
};
|
||||
|
||||
{
|
||||
WriteBatch batch;
|
||||
int write_error = 0;
|
||||
|
||||
for (auto i = 100; i < 199; ++i) {
|
||||
batch.Put(Key(i), rnd.RandomString(1024));
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::Append:BeforePrepareWrite", [&](void*) {
|
||||
write_error++;
|
||||
if (write_error > 2) {
|
||||
fault_fs->SetFilesystemActive(false,
|
||||
IOStatus::IOFenced("IO fenced"));
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
WriteOptions wopts;
|
||||
wopts.sync = true;
|
||||
s = dbfull()->Write(wopts, &batch);
|
||||
ASSERT_TRUE(s.IsIOFenced());
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
{
|
||||
WriteBatch batch;
|
||||
|
||||
for (auto i = 0; i < 100; ++i) {
|
||||
batch.Put(Key(i), rnd.RandomString(1024));
|
||||
}
|
||||
|
||||
WriteOptions wopts;
|
||||
wopts.sync = true;
|
||||
s = dbfull()->Write(wopts, &batch);
|
||||
ASSERT_TRUE(s.IsIOFenced());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBErrorHandlingFSTest, DBErrorHandlingFencingTest,
|
||||
::testing::Bool());
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+7
-2
@@ -71,14 +71,17 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
const std::string& db_name, const std::string& cf_name,
|
||||
const std::string& file_path, int job_id, const FileDescriptor& fd,
|
||||
uint64_t oldest_blob_file_number, const TableProperties& table_properties,
|
||||
TableFileCreationReason reason, const Status& s) {
|
||||
TableFileCreationReason reason, const Status& s,
|
||||
const std::string& file_checksum,
|
||||
const std::string& file_checksum_func_name) {
|
||||
if (s.ok() && event_logger) {
|
||||
JSONWriter jwriter;
|
||||
AppendCurrentTime(&jwriter);
|
||||
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
|
||||
<< "table_file_creation"
|
||||
<< "file_number" << fd.GetNumber() << "file_size"
|
||||
<< fd.GetFileSize();
|
||||
<< fd.GetFileSize() << "file_checksum" << file_checksum
|
||||
<< "file_checksum_func_name" << file_checksum_func_name;
|
||||
|
||||
// table_properties
|
||||
{
|
||||
@@ -154,6 +157,8 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
info.table_properties = table_properties;
|
||||
info.reason = reason;
|
||||
info.status = s;
|
||||
info.file_checksum = file_checksum;
|
||||
info.file_checksum_func_name = file_checksum_func_name;
|
||||
for (auto& listener : listeners) {
|
||||
listener->OnTableFileCreated(info);
|
||||
}
|
||||
|
||||
+3
-1
@@ -35,7 +35,9 @@ class EventHelpers {
|
||||
const std::string& db_name, const std::string& cf_name,
|
||||
const std::string& file_path, int job_id, const FileDescriptor& fd,
|
||||
uint64_t oldest_blob_file_number, const TableProperties& table_properties,
|
||||
TableFileCreationReason reason, const Status& s);
|
||||
TableFileCreationReason reason, const Status& s,
|
||||
const std::string& file_checksum,
|
||||
const std::string& file_checksum_func_name);
|
||||
static void LogAndNotifyTableFileDeletion(
|
||||
EventLogger* event_logger, int job_id,
|
||||
uint64_t file_number, const std::string& file_path,
|
||||
|
||||
Loaded 100 of 337 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user