mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36540d4355 | ||
|
|
fd68cc09a4 | ||
|
|
fc05af3b43 | ||
|
|
814ba4bfc1 | ||
|
|
7575389a25 | ||
|
|
63e7f124b4 | ||
|
|
f444196659 | ||
|
|
9ded0f791c | ||
|
|
b64bc23b63 | ||
|
|
3c9956ef40 | ||
|
|
659fc7c7a7 | ||
|
|
f2732d0586 | ||
|
|
055b21ab11 | ||
|
|
d227276147 | ||
|
|
956f1dfde3 | ||
|
|
28c1c15c29 | ||
|
|
12018136d8 | ||
|
|
bfd00bba9c | ||
|
|
6e57135a65 | ||
|
|
d201e59941 | ||
|
|
f405e55cfa | ||
|
|
4bea83aa44 | ||
|
|
8c7c0a38f1 | ||
|
|
10d02456b6 | ||
|
|
45668a05f5 | ||
|
|
de1e3ff6ea | ||
|
|
395d24f0fa | ||
|
|
daf06f1361 | ||
|
|
b46f5707c4 | ||
|
|
58d55b7f4e | ||
|
|
42a8e583c9 | ||
|
|
9a2d7485f0 | ||
|
|
54cb9c77d9 | ||
|
|
e3e8fbb497 | ||
|
|
0088f77788 | ||
|
|
1a885fe730 | ||
|
|
3a287796e3 | ||
|
|
6e88126dd3 | ||
|
|
4eaa771c01 | ||
|
|
5620efc794 | ||
|
|
ace1721b28 | ||
|
|
1d6dbfb8b7 | ||
|
|
95b41eec6d | ||
|
|
046ac91a7f | ||
|
|
c6b1f6d182 | ||
|
|
f9d45358ca | ||
|
|
76c834e441 | ||
|
|
95d582e0cc | ||
|
|
d11584e42e | ||
|
|
acf77e1bfe | ||
|
|
377eee77f8 | ||
|
|
2b4245559c | ||
|
|
b10c171e58 | ||
|
|
aacf60dda2 | ||
|
|
61ed0de600 | ||
|
|
1d8c54aeaa | ||
|
|
17042a3fb7 | ||
|
|
b9cb7b9644 | ||
|
|
0d68aff3a1 | ||
|
|
fc48af33f5 | ||
|
|
071a146fa0 | ||
|
|
8829ba9fe1 | ||
|
|
4e60663b31 | ||
|
|
36704e9227 | ||
|
|
f2ddb92750 | ||
|
|
2233a2f4c0 | ||
|
|
a31fded253 | ||
|
|
f046a8f617 | ||
|
|
c3bff1c02d | ||
|
|
0bf9079d44 | ||
|
|
46e8c445e7 | ||
|
|
054c00e92d | ||
|
|
96fb7de3bc | ||
|
|
d895eb08b3 | ||
|
|
11d4ac87ea | ||
|
|
3812a77771 | ||
|
|
438fc3d9b7 | ||
|
|
928aca835f | ||
|
|
d82d179a5e | ||
|
|
59f4cbef8c | ||
|
|
1b2b16b38e | ||
|
|
9243f1b668 | ||
|
|
b31f3245f1 | ||
|
|
fc25ac0f3b | ||
|
|
14633148a7 | ||
|
|
28ba896f19 | ||
|
|
f099032131 | ||
|
|
502a1754c4 | ||
|
|
0797616de0 | ||
|
|
1f3e3ead3f | ||
|
|
532c940b79 | ||
|
|
8c01cb79da | ||
|
|
3079a7e7c2 | ||
|
|
5eebfaaa09 | ||
|
|
3ef9092487 | ||
|
|
dee46863ba | ||
|
|
7fe93162c5 | ||
|
|
84711e2f6a | ||
|
|
c057c2e81d | ||
|
|
186344196b | ||
|
|
b60cb55889 | ||
|
|
24e7e7be04 | ||
|
|
51ecdd3e8f | ||
|
|
ef342246dc | ||
|
|
bc95cdd242 | ||
|
|
a29db3048f | ||
|
|
e572ae9f57 | ||
|
|
4b684e96b7 | ||
|
|
800cfae987 |
No files matched your search
@@ -547,7 +547,7 @@ jobs:
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000 --use_io_uring=0' blackbox_crash_test_with_atomic_flush
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
|
||||
- post-steps
|
||||
|
||||
build-linux-crashtest-tiered-storage-bb:
|
||||
@@ -557,7 +557,7 @@ jobs:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "run crashtest"
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800 --use_io_uring=0' blackbox_crash_test_with_tiered_storage
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' blackbox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
@@ -568,7 +568,7 @@ jobs:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "run crashtest"
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800 --use_io_uring=0' whitebox_crash_test_with_tiered_storage
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800' whitebox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000 --use_io_uring=0' blackbox_crash_test_with_atomic_flush
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================= Linux with Sanitizers ===================== #
|
||||
build-linux-clang10-asan:
|
||||
@@ -403,7 +403,7 @@ jobs:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS with Tests ======================== #
|
||||
# ========================= MacOS build only ======================== #
|
||||
build-macos:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
@@ -417,9 +417,10 @@ jobs:
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Run all tests
|
||||
- name: Build
|
||||
run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j16 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS with Tests ======================== #
|
||||
build-macos-cmake:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
@@ -605,4 +606,4 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: maven-site
|
||||
path: "${{ github.workspace }}/java/target/site"
|
||||
path: "${{ github.workspace }}/java/target/site"
|
||||
+2
-2
@@ -166,7 +166,7 @@ else()
|
||||
if(WITH_ZSTD)
|
||||
find_package(zstd REQUIRED)
|
||||
add_definitions(-DZSTD)
|
||||
include_directories(${ZSTD_INCLUDE_DIR})
|
||||
include_directories(${ZSTD_INCLUDE_DIRS})
|
||||
list(APPEND THIRDPARTY_LIBS zstd::zstd)
|
||||
endif()
|
||||
endif()
|
||||
@@ -371,7 +371,7 @@ option(WITH_NUMA "build with NUMA policy support" OFF)
|
||||
if(WITH_NUMA)
|
||||
find_package(NUMA REQUIRED)
|
||||
add_definitions(-DNUMA)
|
||||
include_directories(${NUMA_INCLUDE_DIR})
|
||||
include_directories(${NUMA_INCLUDE_DIRS})
|
||||
list(APPEND THIRDPARTY_LIBS NUMA::NUMA)
|
||||
endif()
|
||||
|
||||
|
||||
+28
-12
@@ -1,25 +1,41 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 8.11.5 (11/26/2025)
|
||||
### Behavior Changes
|
||||
* Backport new value types `kTypeValuePreferredSeqno` and `kTypeColumnFamilyValuePreferredSeqno` introduced in RocksDB 9.1. This fixes a forward compatibility issue to allow reading MANIFEST with entries containing kTypeMaxValid written by RocksDB 9.1+.
|
||||
|
||||
## 8.11.4 (04/09/2024)
|
||||
## 9.0.1 (04/11/2024)
|
||||
### Bug Fixes
|
||||
* Fixed CMake Javadoc build
|
||||
* Fixed CMake Javadoc and source jar builds
|
||||
* Fixed Java `SstFileMetaData` to prevent throwing `java.lang.NoSuchMethodError`
|
||||
|
||||
## 8.11.3 (02/27/2024)
|
||||
* Correct CMake Javadoc and source jar builds
|
||||
## 9.0.0 (02/16/2024)
|
||||
### New Features
|
||||
* Provide support for FSBuffer for point lookups. Also added support for scans and compactions that don't go through prefetching.
|
||||
* *Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
|
||||
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
|
||||
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
|
||||
|
||||
## 8.11.2 (02/16/2024)
|
||||
* Update zlib to 1.3.1 for Java builds
|
||||
### Public API Changes
|
||||
* Allow setting Stderr logger via C API
|
||||
* Declare one Get and one MultiGet variant as pure virtual, and make all the other variants non-overridable. The methods required to be implemented by derived classes of DB allow returning timestamps. It is up to the implementation to check and return an error if timestamps are not supported. The non-batched MultiGet APIs are reimplemented in terms of batched MultiGet, so callers might see a performance improvement.
|
||||
* Exposed mode option to Rate Limiter via c api.
|
||||
* Removed deprecated option `access_hint_on_compaction_start`
|
||||
* Removed deprecated option `ColumnFamilyOptions::check_flush_compaction_key_order`
|
||||
* *Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
|
||||
* Removed deprecated option `ColumnFamilyOptions::level_compaction_dynamic_file_size`
|
||||
* *Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
|
||||
* Remove the force mode for `EnableFileDeletions` API because it is unsafe with no known legitimate use.
|
||||
* Removed deprecated option `ColumnFamilyOptions::ignore_max_compaction_bytes_for_input`
|
||||
* `sst_dump --command=check` now compares the number of records in a table with `num_entries` in table property, and reports corruption if there is a mismatch. API `SstFileDumper::ReadSequential()` is updated to optionally do this verification. (#12322)
|
||||
|
||||
### Behavior Changes
|
||||
* format\_version=6 is the new default setting in BlockBasedTableOptions, for more robust data integrity checking. DBs and SST files written with this setting cannot be read by RocksDB versions before 8.6.0.
|
||||
* Compactions can be scheduled in parallel in an additional scenario: multiple files are marked for compaction within a single column family
|
||||
* For leveled compaction, RocksDB will try to do intra-L0 compaction if the total L0 size is small compared to Lbase (#12214). Users with atomic_flush=true are more likely to see the impact of this change.
|
||||
|
||||
## 8.11.1 (01/25/2024)
|
||||
### Bug Fixes
|
||||
* Fixed a data race in `DBImpl::RenameTempFileToOptionsFile`.
|
||||
* Fix some perf context statistics error in write steps. which include missing write_memtable_time in unordered_write. missing write_memtable_time in PipelineWrite when Writer stat is STATE_PARALLEL_MEMTABLE_WRITER. missing write_delay_time when calling DelayWrite in WriteImplWALOnly function.
|
||||
* Fixed a bug that can, under rare circumstances, cause MultiGet to return an incorrect result for a duplicate key in a MultiGet batch.
|
||||
* Fix a bug where older data of an ingested key can be returned for read when universal compaction is used
|
||||
* Apply appropriate rate limiting and priorities in more places.
|
||||
|
||||
## 8.11.0 (01/19/2024)
|
||||
### New Features
|
||||
|
||||
+8
-3
@@ -169,21 +169,26 @@ most processors made since roughly 2013.
|
||||
|
||||
* Install the dependencies for RocksDB:
|
||||
|
||||
pkg_add gmake gflags snappy bzip2 lz4 zstd git jdk bash findutils gnuwatch
|
||||
`pkg_add gmake gflags snappy bzip2 lz4 zstd git bash findutils gnuwatch`
|
||||
|
||||
* Build RocksDB from source:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
git clone https://github.com/facebook/rocksdb.git
|
||||
cd rocksdb
|
||||
gmake static_lib
|
||||
```
|
||||
|
||||
* Build RocksJava from source (optional):
|
||||
|
||||
* In OpenBSD, JDK depends on XWindows system, so please check that you installed OpenBSD with `xbase` package.
|
||||
* Install dependencies : `pkg_add -v jdk%1.8`
|
||||
```bash
|
||||
cd rocksdb
|
||||
export JAVA_HOME=/usr/local/jdk-1.8.0
|
||||
export PATH=$PATH:/usr/local/jdk-1.8.0/bin
|
||||
gmake rocksdbjava
|
||||
gmake rocksdbjava SHA256_CMD='sha256 -q'
|
||||
```
|
||||
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define an important pre-processing macros: `IOS_CROSS_COMPILE`.
|
||||
|
||||
@@ -539,7 +539,8 @@ endif
|
||||
|
||||
ifdef USE_CLANG
|
||||
# Used by some teams in Facebook
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator -Wimplicit-fallthrough
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator \
|
||||
-Wimplicit-fallthrough -Wreinterpret-base-class -Wundefined-reinterpret-cast
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
|
||||
@@ -163,24 +163,6 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -ldl"
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if test "$ROCKSDB_USE_IO_URING" -ne 0; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
@@ -614,11 +596,29 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if [ "$ROCKSDB_USE_IO_URING" -ne 0 -a "$PLATFORM" = OS_LINUX ]; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
# -Wshorten-64-to-32 breaks compilation on FreeBSD aarch64 and i386
|
||||
if ! { [ "$TARGET_OS" = FreeBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
if ! { [ "$TARGET_OS" = FreeBSD -o "$TARGET_OS" = OpenBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
# Test whether -Wshorten-64-to-32 is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# The file is generated using update_dependencies.sh.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/e40bde78650fa91b8405a857e3f10bf336633fb0/11.x/centos7-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/2043340983c032915adbb6f78903dc855b65aee8/12/platform010/9520e0f
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c00dcc6a3e4125c7e8b248e9a79c14b78ac9e0ca/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/0b9c8e4b060eda62f3bc1c6127bbe1256697569b/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/bc9647f7912b131315827d65cb6189c21f381d05/1.1.3/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/a6f5f3f1d063d2d00cd02fc12f0f05fc3ab3a994/1.2.11/platform010/76ebdda
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/62de5a92e5f23c661c3d4b9f322e04eb14e7a5bd/11.x/centos8-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1f6edd1ff15c99c861afc8f3cd69054cd974dd64/15/platform010/72a2ff8
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/fed6e93d87571fb162734c86636119d45a398963/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/31a346126a1f3b64812c362511cb04cc1bd40855/1.1.8/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0c65c05468b5a38cef1a106a1f526463e120c8dd/1.2.8/platform010/76ebdda
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/09703139cfc376bd8a82642385a0e97726b28287/1.0.6/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/60220d6a5bf7722b9cc239a1368c596619b12060/1.9.1/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/50eace8143eaaea9473deae1f3283e0049e05633/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/5d27e5919771603da06000a027b12f799e58a4f7/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/b62912d333ef33f9760efa6219dbe3fe6abb3b0e/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/6b412770957aa3c8a87e5e0dcd8cc2f45f393bc0/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/52f69816e936e147664ad717eb71a1a0e9dc973a/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/c9cc192099fa84c0dcd0ffeedd44a373ad6e4925/2018_U5/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ff23d17b932725cc1734a14896a8b67c518ba169/1.9.4/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/576397d8b1d9cea7306ad1e454d5e55caaa2ff1c/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/fecac07861cb829f5e60dbeff0503d3272db73c0/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/0bb3f5756788ce26e2e16a1cb2f2af2c59b51abe/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/5b602edd46fda54cdd7ea45f77dbe4061206e174/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/97cac22a149c2e202917e05d44e87e516b68216f/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/53953ebc4e3eda85ad6fc3e429ba146035e97b90/2018_U5/platform010/76ebdda
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/02d9f76aaaba580611cf75e741753c800c7fdc12/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/938dc3f064ef3a48c0446f5b11d788d50b3eb5ee/2.37/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/429a6b3203eb415f1599bd15183659153129188e/3.15.0/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/624a2f8f6c93c3c1df8aa4a6255d8202631a6c80/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/39579e8603b48b3540f8b0633f43adf29acccb8b/2.37/centos8-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/cd9cc656d49ecb53797ce4d055e49fde29fd57ff/3.19.0/platform010/76ebdda
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/363787fa5cac2a8aa20638909210443278fa138e/5.3.4/platform010/9079c97
|
||||
@@ -137,11 +137,11 @@ then
|
||||
# should be relevant for formatting fixes.
|
||||
FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
|
||||
# Get the differences
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1)
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
echo "Checking format of uncommitted changes..."
|
||||
fi
|
||||
|
||||
@@ -149,6 +149,9 @@ if [ -z "$diffs" ]
|
||||
then
|
||||
echo "Nothing needs to be reformatted!"
|
||||
exit 0
|
||||
elif [ $? -ne 1 ]; then
|
||||
# CLANG_FORMAT_DIFF will exit on 1 while there is suggested changes.
|
||||
exit $?
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
|
||||
@@ -75,8 +75,8 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/12/platform010/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos8-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/15/platform010/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
@@ -86,7 +86,7 @@ log_variable CLANG_BASE
|
||||
get_lib_base libgcc 11.x platform010
|
||||
get_lib_base glibc 2.34 platform010
|
||||
get_lib_base snappy LATEST platform010
|
||||
get_lib_base zlib LATEST platform010
|
||||
get_lib_base zlib 1.2.8 platform010
|
||||
get_lib_base bzip2 LATEST platform010
|
||||
get_lib_base lz4 LATEST platform010
|
||||
get_lib_base zstd LATEST platform010
|
||||
@@ -99,7 +99,7 @@ get_lib_base liburing LATEST platform010
|
||||
get_lib_base benchmark LATEST platform010
|
||||
|
||||
get_lib_base kernel-headers fb platform010
|
||||
get_lib_base binutils LATEST centos7-native
|
||||
get_lib_base binutils LATEST centos8-native
|
||||
get_lib_base valgrind LATEST platform010
|
||||
get_lib_base lua 5.3.4 platform010
|
||||
|
||||
|
||||
Vendored
+4
-4
@@ -560,7 +560,7 @@ void BaseClockTable::TrackAndReleaseEvictedEntry(ClockHandle* h) {
|
||||
took_value_ownership =
|
||||
eviction_callback_(ClockCacheShard<FixedHyperClockTable>::ReverseHash(
|
||||
h->GetHash(), &unhashed, hash_seed_),
|
||||
reinterpret_cast<Cache::Handle*>(h),
|
||||
static_cast<Cache::Handle*>(h),
|
||||
h->meta.LoadRelaxed() & ClockHandle::kHitBitMask);
|
||||
}
|
||||
if (!took_value_ownership) {
|
||||
@@ -1428,19 +1428,19 @@ BaseHyperClockCache<Table>::BaseHyperClockCache(
|
||||
|
||||
template <class Table>
|
||||
Cache::ObjectPtr BaseHyperClockCache<Table>::Value(Handle* handle) {
|
||||
return reinterpret_cast<const typename Table::HandleImpl*>(handle)->value;
|
||||
return static_cast<const typename Table::HandleImpl*>(handle)->value;
|
||||
}
|
||||
|
||||
template <class Table>
|
||||
size_t BaseHyperClockCache<Table>::GetCharge(Handle* handle) const {
|
||||
return reinterpret_cast<const typename Table::HandleImpl*>(handle)
|
||||
return static_cast<const typename Table::HandleImpl*>(handle)
|
||||
->GetTotalCharge();
|
||||
}
|
||||
|
||||
template <class Table>
|
||||
const Cache::CacheItemHelper* BaseHyperClockCache<Table>::GetCacheItemHelper(
|
||||
Handle* handle) const {
|
||||
auto h = reinterpret_cast<const typename Table::HandleImpl*>(handle);
|
||||
auto h = static_cast<const typename Table::HandleImpl*>(handle);
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -298,7 +298,7 @@ class ClockCacheTest;
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
struct ClockHandleBasicData {
|
||||
struct ClockHandleBasicData : public Cache::Handle {
|
||||
Cache::ObjectPtr value = nullptr;
|
||||
const Cache::CacheItemHelper* helper = nullptr;
|
||||
// A lossless, reversible hash of the fixed-size (16 byte) cache key. This
|
||||
|
||||
+5
-5
@@ -1062,7 +1062,7 @@ bool CacheUsageWithinBounds(size_t val1, size_t val2, size_t error) {
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
// double explicit casts
|
||||
@@ -1085,7 +1085,7 @@ TEST_P(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
TEST_P(CompressedSecCacheTestWithTiered,
|
||||
CacheReservationManagerMultipleUpdate) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
@@ -1171,7 +1171,7 @@ TEST_P(CompressedSecCacheTestWithTiered, AdmissionPolicy) {
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdate) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
@@ -1235,7 +1235,7 @@ TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdate) {
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdateWithReservation) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(10 << 20));
|
||||
@@ -1329,7 +1329,7 @@ TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdateWithReservation) {
|
||||
|
||||
TEST_P(CompressedSecCacheTestWithTiered, ReservationOverCapacity) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(110 << 20));
|
||||
|
||||
Vendored
+5
-6
@@ -339,8 +339,7 @@ void LRUCacheShard::NotifyEvicted(
|
||||
MemoryAllocator* alloc = table_.GetAllocator();
|
||||
for (LRUHandle* entry : evicted_handles) {
|
||||
if (eviction_callback_ &&
|
||||
eviction_callback_(entry->key(),
|
||||
reinterpret_cast<Cache::Handle*>(entry),
|
||||
eviction_callback_(entry->key(), static_cast<Cache::Handle*>(entry),
|
||||
entry->HasHit())) {
|
||||
// Callback took ownership of obj; just free handle
|
||||
free(entry);
|
||||
@@ -506,7 +505,7 @@ bool LRUCacheShard::Release(LRUHandle* e, bool /*useful*/,
|
||||
// Only call eviction callback if we're sure no one requested erasure
|
||||
// FIXME: disabled because of test churn
|
||||
if (false && was_in_cache && !erase_if_last_ref && eviction_callback_ &&
|
||||
eviction_callback_(e->key(), reinterpret_cast<Cache::Handle*>(e),
|
||||
eviction_callback_(e->key(), static_cast<Cache::Handle*>(e),
|
||||
e->HasHit())) {
|
||||
// Callback took ownership of obj; just free handle
|
||||
free(e);
|
||||
@@ -661,18 +660,18 @@ LRUCache::LRUCache(const LRUCacheOptions& opts) : ShardedCache(opts) {
|
||||
}
|
||||
|
||||
Cache::ObjectPtr LRUCache::Value(Handle* handle) {
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
return h->value;
|
||||
}
|
||||
|
||||
size_t LRUCache::GetCharge(Handle* handle) const {
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
return static_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
GetShard(0).metadata_charge_policy_);
|
||||
}
|
||||
|
||||
const Cache::CacheItemHelper* LRUCache::GetCacheItemHelper(
|
||||
Handle* handle) const {
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -47,7 +47,7 @@ namespace lru_cache {
|
||||
// LRUCacheShard::Lookup.
|
||||
// While refs > 0, public properties like value and deleter must not change.
|
||||
|
||||
struct LRUHandle {
|
||||
struct LRUHandle : public Cache::Handle {
|
||||
Cache::ObjectPtr value;
|
||||
const Cache::CacheItemHelper* helper;
|
||||
LRUHandle* next_hash;
|
||||
|
||||
Vendored
+2
-3
@@ -47,7 +47,7 @@ class LRUCacheTest : public testing::Test {
|
||||
double low_pri_pool_ratio = 1.0,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex) {
|
||||
DeleteCache();
|
||||
cache_ = reinterpret_cast<LRUCacheShard*>(
|
||||
cache_ = static_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard)));
|
||||
new (cache_) LRUCacheShard(capacity, /*strict_capacity_limit=*/false,
|
||||
high_pri_pool_ratio, low_pri_pool_ratio,
|
||||
@@ -392,8 +392,7 @@ class ClockCacheTest : public testing::Test {
|
||||
void NewShard(size_t capacity, bool strict_capacity_limit = true,
|
||||
int eviction_effort_cap = 30) {
|
||||
DeleteShard();
|
||||
shard_ =
|
||||
reinterpret_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
|
||||
shard_ = static_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
|
||||
|
||||
TableOpts opts{1 /*value_size*/, eviction_effort_cap};
|
||||
new (shard_)
|
||||
|
||||
Vendored
+6
-6
@@ -139,7 +139,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
|
||||
explicit ShardedCache(const ShardedCacheOptions& opts)
|
||||
: ShardedCacheBase(opts),
|
||||
shards_(reinterpret_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
shards_(static_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
sizeof(CacheShard) * GetNumShards()))),
|
||||
destroy_shards_in_dtor_(false) {}
|
||||
|
||||
@@ -192,7 +192,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).CreateStandalone(
|
||||
key, hash, obj, helper, charge, allow_uncharged);
|
||||
return reinterpret_cast<Handle*>(result);
|
||||
return static_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper = nullptr,
|
||||
@@ -202,7 +202,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).Lookup(key, hash, helper,
|
||||
create_context, priority, stats);
|
||||
return reinterpret_cast<Handle*>(result);
|
||||
return static_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
void Erase(const Slice& key) override {
|
||||
@@ -212,11 +212,11 @@ class ShardedCache : public ShardedCacheBase {
|
||||
|
||||
bool Release(Handle* handle, bool useful,
|
||||
bool erase_if_last_ref = false) override {
|
||||
auto h = reinterpret_cast<HandleImpl*>(handle);
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Release(h, useful, erase_if_last_ref);
|
||||
}
|
||||
bool Ref(Handle* handle) override {
|
||||
auto h = reinterpret_cast<HandleImpl*>(handle);
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Ref(h);
|
||||
}
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
@@ -259,7 +259,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
} while (remaining_work);
|
||||
}
|
||||
|
||||
virtual void EraseUnRefEntries() override {
|
||||
void EraseUnRefEntries() override {
|
||||
ForEachShard([](CacheShard* cs) { cs->EraseUnRefEntries(); });
|
||||
}
|
||||
|
||||
|
||||
Vendored
+8
-10
@@ -42,27 +42,25 @@ class TieredSecondaryCache : public SecondaryCacheWrapper {
|
||||
|
||||
// This is a no-op as we currently don't allow demotion (i.e
|
||||
// insertion by the upper layer) of evicted blocks.
|
||||
virtual Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Warm up the nvm tier directly
|
||||
virtual Status InsertSaved(
|
||||
const Slice& key, const Slice& saved,
|
||||
CompressionType type = CompressionType::kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
Status InsertSaved(const Slice& key, const Slice& saved,
|
||||
CompressionType type = CompressionType::kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
return nvm_sec_cache_->InsertSaved(key, saved, type, source);
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) override;
|
||||
|
||||
virtual void WaitAll(
|
||||
std::vector<SecondaryCacheResultHandle*> handles) override;
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override;
|
||||
|
||||
private:
|
||||
struct CreateContext : public Cache::CreateContext {
|
||||
|
||||
Vendored
+3
-4
@@ -155,7 +155,7 @@ class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
using BaseCacheInterface<CachePtr>::BaseCacheInterface;
|
||||
struct TypedAsyncLookupHandle : public Cache::AsyncLookupHandle {
|
||||
TypedHandle* Result() {
|
||||
return reinterpret_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
return static_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -169,8 +169,7 @@ class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
}
|
||||
|
||||
inline TypedHandle* Lookup(const Slice& key, Statistics* stats = nullptr) {
|
||||
return reinterpret_cast<TypedHandle*>(
|
||||
this->cache_->BasicLookup(key, stats));
|
||||
return static_cast<TypedHandle*>(this->cache_->BasicLookup(key, stats));
|
||||
}
|
||||
|
||||
inline void StartAsyncLookup(TypedAsyncLookupHandle& async_handle) {
|
||||
@@ -347,7 +346,7 @@ class FullTypedCacheInterface
|
||||
Priority priority = Priority::LOW, Statistics* stats = nullptr,
|
||||
CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier) {
|
||||
if (lowest_used_cache_tier > CacheTier::kVolatileTier) {
|
||||
return reinterpret_cast<TypedHandle*>(this->cache_->Lookup(
|
||||
return static_cast<TypedHandle*>(this->cache_->Lookup(
|
||||
key, GetFullHelper(), create_context, priority, stats));
|
||||
} else {
|
||||
return BasicTypedCacheInterface<TValue, kRole, CachePtr>::Lookup(key,
|
||||
|
||||
@@ -14,7 +14,7 @@ find_library(zstd_LIBRARIES
|
||||
HINTS ${zstd_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES zstd_INCLUDE_DIRS)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES ZSTD_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
zstd_LIBRARIES
|
||||
|
||||
@@ -118,7 +118,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
// Generate the arena wrapped iterator class.
|
||||
// `db_impl` and `cfd` are used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
|
||||
@@ -325,8 +325,7 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
|
||||
auto check_is_blob = [&](bool is_blob) {
|
||||
return [is_blob](Iterator* iterator) {
|
||||
ASSERT_EQ(is_blob,
|
||||
reinterpret_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
|
||||
ASSERT_EQ(is_blob, static_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+10
-11
@@ -83,11 +83,8 @@ Status BuildTable(
|
||||
auto& ioptions = tboptions.ioptions;
|
||||
// Reports the IOStats for flush for every following bytes.
|
||||
const size_t kReportFlushIOStatsEvery = 1048576;
|
||||
OutputValidator output_validator(
|
||||
tboptions.internal_comparator,
|
||||
/*enable_order_check=*/
|
||||
mutable_cf_options.check_flush_compaction_key_order,
|
||||
/*enable_hash=*/paranoid_file_checks);
|
||||
OutputValidator output_validator(tboptions.internal_comparator,
|
||||
/*enable_hash=*/paranoid_file_checks);
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
iter->SeekToFirst();
|
||||
@@ -210,7 +207,7 @@ Status BuildTable(
|
||||
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
|
||||
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool strip_timestamp =
|
||||
const bool logical_strip_timestamp =
|
||||
ts_sz > 0 && !ioptions.persist_user_defined_timestamps;
|
||||
|
||||
std::string key_after_flush_buf;
|
||||
@@ -224,7 +221,7 @@ Status BuildTable(
|
||||
// the in memory version of the key act logically the same as one with a
|
||||
// minimum timestamp. We update the timestamp here so file boundary and
|
||||
// output validator, block builder all see the effect of the stripping.
|
||||
if (strip_timestamp) {
|
||||
if (logical_strip_timestamp) {
|
||||
key_after_flush_buf.clear();
|
||||
ReplaceInternalKeyWithMinTimestamp(&key_after_flush_buf, key, ts_sz);
|
||||
key_after_flush = key_after_flush_buf;
|
||||
@@ -267,9 +264,12 @@ Status BuildTable(
|
||||
Slice last_tombstone_start_user_key{};
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
// TODO(yuzhangyu): handle range deletion for UDT in memtables only.
|
||||
// When user timestamp should not be persisted, we logically strip a
|
||||
// range tombstone's start and end key's timestamp (replace it with min
|
||||
// timestamp) before passing them along to table builder and to update
|
||||
// file boundaries.
|
||||
auto tombstone = range_del_it->Tombstone(logical_strip_timestamp);
|
||||
std::pair<InternalKey, Slice> kv = tombstone.Serialize();
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone_end, tombstone.seq_,
|
||||
@@ -422,7 +422,6 @@ Status BuildTable(
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
OutputValidator file_validator(tboptions.internal_comparator,
|
||||
/*enable_order_check=*/true,
|
||||
/*enable_hash=*/true);
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
// Generate a rolling 64-bit hash of the key and values
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
//
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown.
|
||||
extern Status BuildTable(
|
||||
Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
const FileOptions& file_options, TableCache* table_cache,
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "util/stderr_logger.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using ROCKSDB_NAMESPACE::BackupEngine;
|
||||
@@ -115,6 +116,7 @@ using ROCKSDB_NAMESPACE::Snapshot;
|
||||
using ROCKSDB_NAMESPACE::SstFileMetaData;
|
||||
using ROCKSDB_NAMESPACE::SstFileWriter;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::StderrLogger;
|
||||
using ROCKSDB_NAMESPACE::TablePropertiesCollectorFactory;
|
||||
using ROCKSDB_NAMESPACE::Transaction;
|
||||
using ROCKSDB_NAMESPACE::TransactionDB;
|
||||
@@ -1685,6 +1687,11 @@ void rocksdb_release_snapshot(rocksdb_t* db,
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_snapshot_get_sequence_number(
|
||||
const rocksdb_snapshot_t* snapshot) {
|
||||
return snapshot->rep->GetSequenceNumber();
|
||||
}
|
||||
|
||||
char* rocksdb_property_value(rocksdb_t* db, const char* propname) {
|
||||
std::string tmp;
|
||||
if (db->rep->GetProperty(Slice(propname), &tmp)) {
|
||||
@@ -1872,9 +1879,8 @@ void rocksdb_disable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->DisableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, unsigned char force,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions(force));
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_destroy_db(const rocksdb_options_t* options, const char* name,
|
||||
@@ -2919,6 +2925,23 @@ void rocksdb_options_set_cf_paths(rocksdb_options_t* opt,
|
||||
opt->rep.cf_paths = cf_paths;
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_logger_create_stderr_logger(int log_level,
|
||||
const char* prefix) {
|
||||
rocksdb_logger_t* logger = new rocksdb_logger_t;
|
||||
|
||||
if (prefix) {
|
||||
logger->rep = std::make_shared<StderrLogger>(
|
||||
static_cast<InfoLogLevel>(log_level), prefix);
|
||||
} else {
|
||||
logger->rep =
|
||||
std::make_shared<StderrLogger>(static_cast<InfoLogLevel>(log_level));
|
||||
}
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
void rocksdb_logger_destroy(rocksdb_logger_t* logger) { delete logger; }
|
||||
|
||||
void rocksdb_options_set_env(rocksdb_options_t* opt, rocksdb_env_t* env) {
|
||||
opt->rep.env = (env ? env->rep : nullptr);
|
||||
}
|
||||
@@ -2929,6 +2952,12 @@ void rocksdb_options_set_info_log(rocksdb_options_t* opt, rocksdb_logger_t* l) {
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_options_get_info_log(rocksdb_options_t* opt) {
|
||||
rocksdb_logger_t* info_log = new rocksdb_logger_t;
|
||||
info_log->rep = opt->rep.info_log;
|
||||
return info_log;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_info_log_level(rocksdb_options_t* opt, int v) {
|
||||
opt->rep.info_log_level = static_cast<InfoLogLevel>(v);
|
||||
}
|
||||
@@ -3119,7 +3148,7 @@ void rocksdb_options_set_enable_blob_files(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.enable_blob_files = val;
|
||||
}
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_blob_files;
|
||||
}
|
||||
@@ -3491,35 +3520,6 @@ unsigned char rocksdb_options_get_advise_random_on_open(
|
||||
return opt->rep.advise_random_on_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_access_hint_on_compaction_start(rocksdb_options_t* opt,
|
||||
int v) {
|
||||
switch (v) {
|
||||
case 0:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::NONE;
|
||||
break;
|
||||
case 1:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::NORMAL;
|
||||
break;
|
||||
case 2:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::SEQUENTIAL;
|
||||
break;
|
||||
case 3:
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::WILLNEED;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
int rocksdb_options_get_access_hint_on_compaction_start(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.access_hint_on_compaction_start;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_use_adaptive_mutex(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.use_adaptive_mutex = v;
|
||||
@@ -4005,6 +4005,16 @@ rocksdb_ratelimiter_t* rocksdb_ratelimiter_create_auto_tuned(
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
rocksdb_ratelimiter_t* rocksdb_ratelimiter_create_with_mode(
|
||||
int64_t rate_bytes_per_sec, int64_t refill_period_us, int32_t fairness,
|
||||
int mode, bool auto_tuned) {
|
||||
rocksdb_ratelimiter_t* rate_limiter = new rocksdb_ratelimiter_t;
|
||||
rate_limiter->rep.reset(
|
||||
NewGenericRateLimiter(rate_bytes_per_sec, refill_period_us, fairness,
|
||||
static_cast<RateLimiter::Mode>(mode), auto_tuned));
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
void rocksdb_ratelimiter_destroy(rocksdb_ratelimiter_t* limiter) {
|
||||
delete limiter;
|
||||
}
|
||||
@@ -4601,7 +4611,7 @@ void rocksdb_readoptions_set_io_timeout(rocksdb_readoptions_t* opt,
|
||||
opt->rep.io_timeout = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.io_timeout.count();
|
||||
}
|
||||
@@ -5434,9 +5444,7 @@ uint64_t rocksdb_livefiles_deletions(const rocksdb_livefiles_t* lf, int index) {
|
||||
return lf->rep[index].num_deletions;
|
||||
}
|
||||
|
||||
extern void rocksdb_livefiles_destroy(const rocksdb_livefiles_t* lf) {
|
||||
delete lf;
|
||||
}
|
||||
void rocksdb_livefiles_destroy(const rocksdb_livefiles_t* lf) { delete lf; }
|
||||
|
||||
void rocksdb_get_options_from_string(const rocksdb_options_t* base_options,
|
||||
const char* opts_str,
|
||||
|
||||
+30
-10
@@ -718,6 +718,11 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_ratelimiter(options, rate_limiter);
|
||||
rocksdb_ratelimiter_destroy(rate_limiter);
|
||||
|
||||
rate_limiter = rocksdb_ratelimiter_create_with_mode(1000 * 1024 * 1024,
|
||||
100 * 1000, 10, 0, true);
|
||||
rocksdb_options_set_ratelimiter(options, rate_limiter);
|
||||
rocksdb_ratelimiter_destroy(rate_limiter);
|
||||
|
||||
roptions = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_verify_checksums(roptions, 1);
|
||||
rocksdb_readoptions_set_fill_cache(roptions, 1);
|
||||
@@ -2034,8 +2039,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_advise_random_on_open(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(o));
|
||||
|
||||
rocksdb_options_set_access_hint_on_compaction_start(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_access_hint_on_compaction_start(o));
|
||||
|
||||
rocksdb_options_set_use_adaptive_mutex(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(o));
|
||||
@@ -2231,8 +2234,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(18 == rocksdb_options_get_stats_dump_period_sec(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_stats_persist_period_sec(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(copy));
|
||||
CheckCondition(3 ==
|
||||
rocksdb_options_get_access_hint_on_compaction_start(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(copy));
|
||||
CheckCondition(19 == rocksdb_options_get_bytes_per_sync(copy));
|
||||
CheckCondition(20 == rocksdb_options_get_wal_bytes_per_sync(copy));
|
||||
@@ -2515,11 +2516,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(0 == rocksdb_options_get_advise_random_on_open(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(o));
|
||||
|
||||
rocksdb_options_set_access_hint_on_compaction_start(copy, 2);
|
||||
CheckCondition(2 ==
|
||||
rocksdb_options_get_access_hint_on_compaction_start(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_access_hint_on_compaction_start(o));
|
||||
|
||||
rocksdb_options_set_use_adaptive_mutex(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_use_adaptive_mutex(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(o));
|
||||
@@ -2788,6 +2784,30 @@ int main(int argc, char** argv) {
|
||||
rocksdb_memory_allocator_destroy(allocator);
|
||||
}
|
||||
|
||||
StartPhase("stderr_logger");
|
||||
{
|
||||
rocksdb_options_t* o_no_prefix = rocksdb_options_create();
|
||||
rocksdb_logger_t* no_prefix_logger =
|
||||
rocksdb_logger_create_stderr_logger(3, NULL);
|
||||
rocksdb_options_set_info_log(o_no_prefix, no_prefix_logger);
|
||||
rocksdb_logger_t* no_prefix_info_log =
|
||||
rocksdb_options_get_info_log(o_no_prefix);
|
||||
CheckCondition(no_prefix_info_log != NULL);
|
||||
rocksdb_logger_destroy(no_prefix_logger);
|
||||
rocksdb_logger_destroy(no_prefix_info_log);
|
||||
rocksdb_options_destroy(o_no_prefix);
|
||||
|
||||
rocksdb_options_t* o_prefix = rocksdb_options_create();
|
||||
rocksdb_logger_t* prefix_logger =
|
||||
rocksdb_logger_create_stderr_logger(3, "some prefix");
|
||||
rocksdb_options_set_info_log(o_prefix, prefix_logger);
|
||||
rocksdb_logger_t* prefix_info_log = rocksdb_options_get_info_log(o_prefix);
|
||||
CheckCondition(prefix_info_log != NULL);
|
||||
rocksdb_logger_destroy(prefix_logger);
|
||||
rocksdb_logger_destroy(prefix_info_log);
|
||||
rocksdb_options_destroy(o_prefix);
|
||||
}
|
||||
|
||||
StartPhase("env");
|
||||
{
|
||||
rocksdb_env_t* e;
|
||||
@@ -3694,7 +3714,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
StartPhase("statistics");
|
||||
{
|
||||
const uint32_t BYTES_WRITTEN_TICKER = 40;
|
||||
const uint32_t BYTES_WRITTEN_TICKER = 60;
|
||||
const uint32_t DB_WRITE_HIST = 1;
|
||||
|
||||
rocksdb_statistics_histogram_data_t* hist =
|
||||
|
||||
+26
-7
@@ -96,16 +96,16 @@ const Comparator* ColumnFamilyHandleImpl::GetComparator() const {
|
||||
return cfd()->user_comparator();
|
||||
}
|
||||
|
||||
void GetIntTblPropCollectorFactory(
|
||||
void GetInternalTblPropCollFactory(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
IntTblPropCollectorFactories* int_tbl_prop_collector_factories) {
|
||||
assert(int_tbl_prop_collector_factories);
|
||||
InternalTblPropCollFactories* internal_tbl_prop_coll_factories) {
|
||||
assert(internal_tbl_prop_coll_factories);
|
||||
|
||||
auto& collector_factories = ioptions.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < ioptions.table_properties_collector_factories.size();
|
||||
++i) {
|
||||
assert(collector_factories[i]);
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
internal_tbl_prop_coll_factories->emplace_back(
|
||||
new UserKeyTablePropertiesCollectorFactory(collector_factories[i]));
|
||||
}
|
||||
}
|
||||
@@ -572,7 +572,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
GetIntTblPropCollectorFactory(ioptions_, &int_tbl_prop_collector_factories_);
|
||||
GetInternalTblPropCollFactory(ioptions_, &internal_tbl_prop_coll_factories_);
|
||||
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
@@ -893,6 +893,14 @@ uint64_t GetPendingCompactionBytesForCompactionSpeedup(
|
||||
uint64_t size_threshold = bottommost_files_size / kBottommostSizeDivisor;
|
||||
return std::min(size_threshold, slowdown_threshold);
|
||||
}
|
||||
|
||||
uint64_t GetMarkedFileCountForCompactionSpeedup() {
|
||||
// When just one file is marked, it is not clear that parallel compaction will
|
||||
// help the compaction that the user nicely requested to happen sooner. When
|
||||
// multiple files are marked, however, it is pretty clearly helpful, except
|
||||
// for the rare case in which a single compaction grabs all the marked files.
|
||||
return 2;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
std::pair<WriteStallCondition, WriteStallCause>
|
||||
@@ -1074,6 +1082,16 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"compaction "
|
||||
"bytes %" PRIu64,
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
|
||||
} else if (uint64_t(vstorage->FilesMarkedForCompaction().size()) >=
|
||||
GetMarkedFileCountForCompactionSpeedup()) {
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
ROCKS_LOG_INFO(
|
||||
ioptions_.logger,
|
||||
"[%s] Increasing compaction threads because we have %" PRIu64
|
||||
" files marked for compaction",
|
||||
name_.c_str(),
|
||||
uint64_t(vstorage->FilesMarkedForCompaction().size()));
|
||||
} else {
|
||||
write_controller_token_.reset();
|
||||
}
|
||||
@@ -1161,7 +1179,7 @@ bool ColumnFamilyData::RangeOverlapWithCompaction(
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
const autovector<Range>& ranges, SuperVersion* super_version,
|
||||
const autovector<UserKeyRange>& ranges, SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap) {
|
||||
assert(overlap != nullptr);
|
||||
*overlap = false;
|
||||
@@ -1205,7 +1223,8 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
|
||||
if (status.ok()) {
|
||||
if (memtable_iter->Valid() &&
|
||||
ucmp->Compare(seek_result.user_key, ranges[i].limit) <= 0) {
|
||||
ucmp->CompareWithoutTimestamp(seek_result.user_key,
|
||||
ranges[i].limit) <= 0) {
|
||||
*overlap = true;
|
||||
} else if (range_del_agg.IsRangeOverlapped(ranges[i].start,
|
||||
ranges[i].limit)) {
|
||||
|
||||
+24
-26
@@ -169,10 +169,10 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
|
||||
virtual uint32_t GetID() const override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
virtual const Comparator* GetComparator() const override;
|
||||
uint32_t GetID() const override;
|
||||
const std::string& GetName() const override;
|
||||
Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
const Comparator* GetComparator() const override;
|
||||
|
||||
private:
|
||||
ColumnFamilyData* cfd_;
|
||||
@@ -193,7 +193,7 @@ class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
||||
internal_cfd_(nullptr) {}
|
||||
|
||||
void SetCFD(ColumnFamilyData* _cfd) { internal_cfd_ = _cfd; }
|
||||
virtual ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
|
||||
private:
|
||||
ColumnFamilyData* internal_cfd_;
|
||||
@@ -251,22 +251,21 @@ struct SuperVersion {
|
||||
autovector<MemTable*> to_delete;
|
||||
};
|
||||
|
||||
extern Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
|
||||
extern Status CheckConcurrentWritesSupported(
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options);
|
||||
|
||||
extern Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
extern ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
// Wrap user defined table properties collector factories `from cf_options`
|
||||
// into internal ones in int_tbl_prop_collector_factories. Add a system internal
|
||||
// into internal ones in internal_tbl_prop_coll_factories. Add a system internal
|
||||
// one too.
|
||||
extern void GetIntTblPropCollectorFactory(
|
||||
void GetInternalTblPropCollFactory(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
IntTblPropCollectorFactories* int_tbl_prop_collector_factories);
|
||||
InternalTblPropCollFactories* internal_tbl_prop_coll_factories);
|
||||
|
||||
class ColumnFamilySet;
|
||||
|
||||
@@ -401,7 +400,7 @@ class ColumnFamilyData {
|
||||
// duration of this function.
|
||||
//
|
||||
// Thread-safe
|
||||
Status RangesOverlapWithMemtables(const autovector<Range>& ranges,
|
||||
Status RangesOverlapWithMemtables(const autovector<UserKeyRange>& ranges,
|
||||
SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap);
|
||||
|
||||
@@ -430,8 +429,8 @@ class ColumnFamilyData {
|
||||
return internal_comparator_;
|
||||
}
|
||||
|
||||
const IntTblPropCollectorFactories* int_tbl_prop_collector_factories() const {
|
||||
return &int_tbl_prop_collector_factories_;
|
||||
const InternalTblPropCollFactories* internal_tbl_prop_coll_factories() const {
|
||||
return &internal_tbl_prop_coll_factories_;
|
||||
}
|
||||
|
||||
SuperVersion* GetSuperVersion() { return super_version_; }
|
||||
@@ -574,7 +573,7 @@ class ColumnFamilyData {
|
||||
std::atomic<bool> dropped_; // true if client dropped it
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
IntTblPropCollectorFactories int_tbl_prop_collector_factories_;
|
||||
InternalTblPropCollFactories internal_tbl_prop_coll_factories_;
|
||||
|
||||
const ColumnFamilyOptions initial_cf_options_;
|
||||
const ImmutableOptions ioptions_;
|
||||
@@ -854,17 +853,17 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
// REQUIRES: Seek() called first
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
virtual MemTable* GetMemTable() const override;
|
||||
MemTable* GetMemTable() const override;
|
||||
|
||||
// Returns column family handle for the selected column family
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
virtual ColumnFamilyHandle* GetColumnFamilyHandle() override;
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle() override;
|
||||
|
||||
// Cannot be called while another thread is calling Seek().
|
||||
// REQUIRES: use this function of DBImpl::column_family_memtables_ should be
|
||||
// under a DB mutex OR from a write thread
|
||||
virtual ColumnFamilyData* current() override { return current_; }
|
||||
ColumnFamilyData* current() override { return current_; }
|
||||
|
||||
private:
|
||||
ColumnFamilySet* column_family_set_;
|
||||
@@ -872,12 +871,11 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
ColumnFamilyHandleInternal handle_;
|
||||
};
|
||||
|
||||
extern uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
|
||||
extern const Comparator* GetColumnFamilyUserComparator(
|
||||
const Comparator* GetColumnFamilyUserComparator(
|
||||
ColumnFamilyHandle* column_family);
|
||||
|
||||
extern const ImmutableOptions& GetImmutableOptions(
|
||||
ColumnFamilyHandle* column_family);
|
||||
const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+124
-43
@@ -2604,90 +2604,92 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(450);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(450, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(205);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(205, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(202);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(202, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(198);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(198, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(599);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(599, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(2001);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(2001, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(100);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(100, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2706,7 +2708,7 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2718,14 +2720,14 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(200);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(200, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(0, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2744,7 +2746,7 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(60);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2753,14 +2755,14 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
vstorage->set_l0_delay_trigger_count(70);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(71);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2785,19 +2787,21 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit = 200;
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
@@ -2853,53 +2857,55 @@ TEST_P(ColumnFamilyTest, WriteStallTwoColumnFamilies) {
|
||||
MutableCFOptions mutable_cf_options1 = mutable_cf_options;
|
||||
mutable_cf_options1.soft_pending_compaction_bytes_limit = 500;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600, dbmu);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(70, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(800);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(800, dbmu);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(700);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(700, dbmu);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600, dbmu);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2932,29 +2938,31 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
MutableCFOptions mutable_cf_options1 = mutable_cf_options;
|
||||
mutable_cf_options1.level0_slowdown_writes_trigger = 16;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60, dbmu);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30, dbmu);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70, dbmu);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3, dbmu);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
@@ -2980,10 +2988,13 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
{
|
||||
// No bottommost data, so debt ratio cannot trigger speedup.
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
|
||||
dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
@@ -2996,18 +3007,88 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupForCompactionDebt) {
|
||||
// 1MB debt is way bigger than bottommost data so definitely triggers
|
||||
// speedup.
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */,
|
||||
dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
// Eight bytes is way smaller than bottommost data so definitely does not
|
||||
// trigger speedup.
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(8);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(8, dbmu);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CompactionSpeedupForMarkedFiles) {
|
||||
const int kParallelismLimit = 3;
|
||||
class AlwaysCompactTpc : public TablePropertiesCollector {
|
||||
public:
|
||||
Status Finish(UserCollectedProperties* /* properties */) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
const char* Name() const override { return "AlwaysCompactTpc"; }
|
||||
|
||||
bool NeedCompact() const override { return true; }
|
||||
};
|
||||
|
||||
class AlwaysCompactTpcf : public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context /* context */) override {
|
||||
return new AlwaysCompactTpc();
|
||||
}
|
||||
|
||||
const char* Name() const override { return "AlwaysCompactTpcf"; }
|
||||
};
|
||||
|
||||
column_family_options_.num_levels = 2;
|
||||
column_family_options_.table_properties_collector_factories.emplace_back(
|
||||
std::make_shared<AlwaysCompactTpcf>());
|
||||
db_options_.max_background_compactions = kParallelismLimit;
|
||||
Open();
|
||||
|
||||
// Make a nonempty last level. Only marked files in upper levels count.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
WaitForCompaction();
|
||||
AssertFilesPerLevel("0,1", 0 /* cf */);
|
||||
|
||||
// Block the compaction thread pool so marked files accumulate in L0.
|
||||
test::SleepingBackgroundTask sleeping_tasks[kParallelismLimit];
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_tasks[i], Env::Priority::LOW);
|
||||
sleeping_tasks[i].WaitUntilSleeping();
|
||||
}
|
||||
|
||||
// Zero marked upper-level files. No speedup.
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("0,1", 0 /* cf */);
|
||||
|
||||
// One marked upper-level file. No speedup.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("1,1", 0 /* cf */);
|
||||
|
||||
// Two marked upper-level files. Speedup.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(kParallelismLimit, dbfull()->TEST_BGCompactionsAllowed());
|
||||
AssertFilesPerLevel("2,1", 0 /* cf */);
|
||||
|
||||
for (int i = 0; i < kParallelismLimit; i++) {
|
||||
sleeping_tasks[i].WakeUp();
|
||||
sleeping_tasks[i].WaitUntilDone();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ColumnFamilyTest, CreateAndDestroyOptions) {
|
||||
std::unique_ptr<ColumnFamilyOptions> cfo(new ColumnFamilyOptions());
|
||||
ColumnFamilyHandle* cfh;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "logging/logging.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/sst_partitioner.h"
|
||||
@@ -21,9 +22,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
const uint64_t kRangeTombstoneSentinel =
|
||||
PackSequenceAndType(kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
|
||||
int sstableKeyCompare(const Comparator* uc, const Slice& a, const Slice& b) {
|
||||
auto c = uc->CompareWithoutTimestamp(ExtractUserKey(a), ExtractUserKey(b));
|
||||
if (c != 0) {
|
||||
@@ -352,11 +350,9 @@ Compaction::Compaction(
|
||||
|
||||
// for the non-bottommost levels, it tries to build files match the target
|
||||
// file size, but not guaranteed. It could be 2x the size of the target size.
|
||||
max_output_file_size_ =
|
||||
bottommost_level_ || grandparents_.empty() ||
|
||||
!_immutable_options.level_compaction_dynamic_file_size
|
||||
? target_output_file_size_
|
||||
: 2 * target_output_file_size_;
|
||||
max_output_file_size_ = bottommost_level_ || grandparents_.empty()
|
||||
? target_output_file_size_
|
||||
: 2 * target_output_file_size_;
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i < inputs_.size(); ++i) {
|
||||
|
||||
@@ -595,6 +595,6 @@ struct PerKeyPlacementContext {
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
// Return sum of sizes of all files in `files`.
|
||||
extern uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -450,14 +450,6 @@ void CompactionJob::ReleaseSubcompactionResources() {
|
||||
ShrinkSubcompactionResources(extra_num_subcompaction_threads_reserved_);
|
||||
}
|
||||
|
||||
struct RangeWithSize {
|
||||
Range range;
|
||||
uint64_t size;
|
||||
|
||||
RangeWithSize(const Slice& a, const Slice& b, uint64_t s = 0)
|
||||
: range(a, b), size(s) {}
|
||||
};
|
||||
|
||||
void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// The goal is to find some boundary keys so that we can evenly partition
|
||||
// the compaction input data into max_subcompactions ranges.
|
||||
@@ -760,7 +752,6 @@ Status CompactionJob::Run() {
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
OutputValidator validator(cfd->internal_comparator(),
|
||||
/*_enable_order_check=*/true,
|
||||
/*_enable_hash=*/true);
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
s = validator.Add(iter->key(), iter->value());
|
||||
@@ -831,24 +822,27 @@ Status CompactionJob::Run() {
|
||||
// input keys. So the number of keys it processed is not suitable for
|
||||
// verification here.
|
||||
// TODO: support verification when trim_ts_ is non-empty.
|
||||
if (!(ts_sz > 0 && !trim_ts_.empty()) &&
|
||||
db_options_.compaction_verify_record_count) {
|
||||
if (!(ts_sz > 0 && !trim_ts_.empty())) {
|
||||
assert(compaction_stats_.stats.num_input_records > 0);
|
||||
// TODO: verify the number of range deletion entries.
|
||||
uint64_t expected =
|
||||
compaction_stats_.stats.num_input_records - num_input_range_del;
|
||||
uint64_t actual = compaction_job_stats_->num_input_records;
|
||||
if (expected != actual) {
|
||||
char scratch[2345];
|
||||
compact_->compaction->Summary(scratch, sizeof(scratch));
|
||||
std::string msg =
|
||||
"Total number of input records: " + std::to_string(expected) +
|
||||
", but processed " + std::to_string(actual) + " records.";
|
||||
"Compaction number of input keys does not match "
|
||||
"number of keys processed. Expected " +
|
||||
std::to_string(expected) + " but processed " +
|
||||
std::to_string(actual) + ". Compaction summary: " + scratch;
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log, "[%s] [JOB %d] Compaction %s",
|
||||
db_options_.info_log, "[%s] [JOB %d] Compaction with status: %s",
|
||||
compact_->compaction->column_family_data()->GetName().c_str(),
|
||||
job_context_->job_id, msg.c_str());
|
||||
status = Status::Corruption(
|
||||
"Compaction number of input keys does not match number of keys "
|
||||
"processed.");
|
||||
if (db_options_.compaction_verify_record_count) {
|
||||
status = Status::Corruption(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1285,10 +1279,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
: nullptr);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:1",
|
||||
static_cast<void*>(const_cast<std::atomic<bool>*>(
|
||||
&manual_compaction_canceled_)));
|
||||
|
||||
const std::string* const full_history_ts_low =
|
||||
full_history_ts_low_.empty() ? nullptr : &full_history_ts_low_;
|
||||
@@ -1336,8 +1329,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
Status status;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<Compaction*>(sub_compact->compaction)));
|
||||
static_cast<void*>(const_cast<Compaction*>(sub_compact->compaction)));
|
||||
uint64_t last_cpu_micros = prev_cpu_micros;
|
||||
while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
|
||||
// Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
|
||||
@@ -1368,10 +1360,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
break;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:2",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:2",
|
||||
static_cast<void*>(const_cast<std::atomic<bool>*>(
|
||||
&manual_compaction_canceled_)));
|
||||
c_iter->Next();
|
||||
if (c_iter->status().IsManualCompactionPaused()) {
|
||||
break;
|
||||
@@ -1945,8 +1936,6 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
}
|
||||
|
||||
outputs.AddOutput(std::move(meta), cfd->internal_comparator(),
|
||||
sub_compact->compaction->mutable_cf_options()
|
||||
->check_flush_compaction_key_order,
|
||||
paranoid_file_checks_);
|
||||
}
|
||||
|
||||
@@ -1969,7 +1958,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
TableBuilderOptions tboptions(
|
||||
*cfd->ioptions(), *(sub_compact->compaction->mutable_cf_options()),
|
||||
read_options, write_options, cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(),
|
||||
cfd->internal_tbl_prop_coll_factories(),
|
||||
sub_compact->compaction->output_compression(),
|
||||
sub_compact->compaction->output_compression_opts(), cfd->GetID(),
|
||||
cfd->GetName(), sub_compact->compaction->output_level(),
|
||||
|
||||
@@ -302,7 +302,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
TableBuilderOptions(*cfd_->ioptions(), mutable_cf_options_,
|
||||
read_options, write_options,
|
||||
cfd_->internal_comparator(),
|
||||
cfd_->int_tbl_prop_collector_factories(),
|
||||
cfd_->internal_tbl_prop_coll_factories(),
|
||||
CompressionType::kNoCompression,
|
||||
CompressionOptions(), 0 /* column_family_id */,
|
||||
kDefaultColumnFamilyName, -1 /* level */),
|
||||
@@ -1752,23 +1752,9 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
}
|
||||
}
|
||||
|
||||
class CompactionJobDynamicFileSizeTest
|
||||
: public CompactionJobTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
CompactionJobDynamicFileSizeTest()
|
||||
: CompactionJobTestBase(
|
||||
test::PerThreadDBPath("compaction_job_dynamic_file_size_test"),
|
||||
BytewiseComparator(), [](uint64_t /*ts*/) { return ""; },
|
||||
/*test_io_priority=*/false, TableTypeForTest::kMockTable) {}
|
||||
};
|
||||
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutForMaxCompactionBytes) {
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytes) {
|
||||
// dynamic_file_size option should have no impact on cutting for max
|
||||
// compaction bytes.
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
mutable_cf_options_.target_file_size_base = 80;
|
||||
mutable_cf_options_.max_compaction_bytes = 21;
|
||||
@@ -1842,10 +1828,7 @@ TEST_P(CompactionJobDynamicFileSizeTest, CutForMaxCompactionBytes) {
|
||||
{expected_file1, expected_file2});
|
||||
}
|
||||
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToSkipGrandparentFile) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
|
||||
NewDB();
|
||||
// Make sure the grandparent level file size (10) qualifies skipping.
|
||||
// Currently, it has to be > 1/8 of target file size.
|
||||
@@ -1880,28 +1863,15 @@ TEST_P(CompactionJobDynamicFileSizeTest, CutToSkipGrandparentFile) {
|
||||
mock::MakeMockFile({{KeyStr("x", 4U, kTypeValue), "val"},
|
||||
{KeyStr("z", 6U, kTypeValue), "val3"}});
|
||||
|
||||
auto expected_file_disable_dynamic_file_size =
|
||||
mock::MakeMockFile({{KeyStr("a", 5U, kTypeValue), "val2"},
|
||||
{KeyStr("c", 3U, kTypeValue), "val"},
|
||||
{KeyStr("x", 4U, kTypeValue), "val"},
|
||||
{KeyStr("z", 6U, kTypeValue), "val3"}});
|
||||
|
||||
SetLastSequence(6U);
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundary) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
|
||||
NewDB();
|
||||
|
||||
// MockTable has 1 byte per entry by default and each file is 10 bytes.
|
||||
@@ -1968,40 +1938,15 @@ TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundary) {
|
||||
}
|
||||
expected_file2.emplace_back(KeyStr("s", 4U, kTypeValue), "val");
|
||||
|
||||
mock::KVVector expected_file_disable_dynamic_file_size1;
|
||||
for (char i = 0; i < 10; i++) {
|
||||
expected_file_disable_dynamic_file_size1.emplace_back(
|
||||
KeyStr(std::string(1, ch + i), i + 10, kTypeValue),
|
||||
"val" + std::to_string(i));
|
||||
}
|
||||
|
||||
mock::KVVector expected_file_disable_dynamic_file_size2;
|
||||
for (char i = 10; i < 12; i++) {
|
||||
expected_file_disable_dynamic_file_size2.emplace_back(
|
||||
KeyStr(std::string(1, ch + i), i + 10, kTypeValue),
|
||||
"val" + std::to_string(i));
|
||||
}
|
||||
|
||||
expected_file_disable_dynamic_file_size2.emplace_back(
|
||||
KeyStr("s", 4U, kTypeValue), "val");
|
||||
|
||||
SetLastSequence(22U);
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
auto lvl1_files = cfd_->current()->storage_info()->LevelFiles(1);
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2});
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size1,
|
||||
expected_file_disable_dynamic_file_size2});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
TEST_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
NewDB();
|
||||
|
||||
// MockTable has 1 byte per entry by default and each file is 10 bytes.
|
||||
@@ -2038,13 +1983,9 @@ TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
AddMockFile(file5, 2);
|
||||
|
||||
mock::KVVector expected_file1;
|
||||
mock::KVVector expected_file_disable_dynamic_file_size;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
expected_file1.emplace_back(KeyStr("a", 100 - i, kTypeValue),
|
||||
"val" + std::to_string(100 - i));
|
||||
expected_file_disable_dynamic_file_size.emplace_back(
|
||||
KeyStr("a", 100 - i, kTypeValue), "val" + std::to_string(100 - i));
|
||||
}
|
||||
|
||||
// make sure `b` is cut in a separated file (so internally it's not using
|
||||
@@ -2053,9 +1994,6 @@ TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
auto expected_file2 =
|
||||
mock::MakeMockFile({{KeyStr("b", 90U, kTypeValue), "valb"}});
|
||||
|
||||
expected_file_disable_dynamic_file_size.emplace_back(
|
||||
KeyStr("b", 90U, kTypeValue), "valb");
|
||||
|
||||
SetLastSequence(122U);
|
||||
const std::vector<int> input_levels = {0, 1};
|
||||
auto lvl0_files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -2066,20 +2004,13 @@ TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
for (int i = 80; i <= 100; i++) {
|
||||
snapshots.emplace_back(i);
|
||||
}
|
||||
if (enable_dyanmic_file_size) {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file1, expected_file2}, snapshots);
|
||||
} else {
|
||||
RunCompaction({lvl0_files, lvl1_files}, input_levels,
|
||||
{expected_file_disable_dynamic_file_size}, snapshots);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutForMaxCompactionBytesSameKey) {
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
// dynamic_file_size option should have no impact on cutting for max
|
||||
// compaction bytes.
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
mutable_cf_options_.target_file_size_base = 80;
|
||||
@@ -2136,9 +2067,6 @@ TEST_P(CompactionJobDynamicFileSizeTest, CutForMaxCompactionBytesSameKey) {
|
||||
{expected_file1, expected_file2, expected_file3}, snapshots);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionJobDynamicFileSizeTest,
|
||||
CompactionJobDynamicFileSizeTest, testing::Bool());
|
||||
|
||||
class CompactionJobTimestampTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobTimestampTest()
|
||||
|
||||
@@ -320,7 +320,6 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
being_grandparent_gap_ ? 2 : 3;
|
||||
if (compaction_->immutable_options()->compaction_style ==
|
||||
kCompactionStyleLevel &&
|
||||
compaction_->immutable_options()->level_compaction_dynamic_file_size &&
|
||||
num_grandparent_boundaries_crossed >=
|
||||
num_skippable_boundaries_crossed &&
|
||||
grandparent_overlapped_bytes_ - previous_overlapped_bytes >
|
||||
@@ -342,7 +341,6 @@ bool CompactionOutputs::ShouldStopBefore(const CompactionIterator& c_iter) {
|
||||
// improvement.
|
||||
if (compaction_->immutable_options()->compaction_style ==
|
||||
kCompactionStyleLevel &&
|
||||
compaction_->immutable_options()->level_compaction_dynamic_file_size &&
|
||||
current_output_file_size_ >=
|
||||
((compaction_->target_output_file_size() + 99) / 100) *
|
||||
(50 + std::min(grandparent_boundary_switched_num_ * 5,
|
||||
|
||||
@@ -30,11 +30,9 @@ class CompactionOutputs {
|
||||
// compaction output file
|
||||
struct Output {
|
||||
Output(FileMetaData&& _meta, const InternalKeyComparator& _icmp,
|
||||
bool _enable_order_check, bool _enable_hash, bool _finished,
|
||||
uint64_t precalculated_hash)
|
||||
bool _enable_hash, bool _finished, uint64_t precalculated_hash)
|
||||
: meta(std::move(_meta)),
|
||||
validator(_icmp, _enable_order_check, _enable_hash,
|
||||
precalculated_hash),
|
||||
validator(_icmp, _enable_hash, precalculated_hash),
|
||||
finished(_finished) {}
|
||||
FileMetaData meta;
|
||||
OutputValidator validator;
|
||||
@@ -49,10 +47,10 @@ class CompactionOutputs {
|
||||
|
||||
// Add generated output to the list
|
||||
void AddOutput(FileMetaData&& meta, const InternalKeyComparator& icmp,
|
||||
bool enable_order_check, bool enable_hash,
|
||||
bool finished = false, uint64_t precalculated_hash = 0) {
|
||||
outputs_.emplace_back(std::move(meta), icmp, enable_order_check,
|
||||
enable_hash, finished, precalculated_hash);
|
||||
bool enable_hash, bool finished = false,
|
||||
uint64_t precalculated_hash = 0) {
|
||||
outputs_.emplace_back(std::move(meta), icmp, enable_hash, finished,
|
||||
precalculated_hash);
|
||||
}
|
||||
|
||||
// Set new table builder for the current output
|
||||
|
||||
@@ -456,10 +456,9 @@ bool CompactionPicker::IsRangeInCompaction(VersionStorageInfo* vstorage,
|
||||
// Returns false if files on parent level are currently in compaction, which
|
||||
// means that we can't compact them
|
||||
bool CompactionPicker::SetupOtherInputs(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, CompactionInputFiles* inputs,
|
||||
CompactionInputFiles* output_level_inputs, int* parent_index,
|
||||
int base_index, bool only_expand_towards_right) {
|
||||
const std::string& cf_name, VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs, CompactionInputFiles* output_level_inputs,
|
||||
int* parent_index, int base_index, bool only_expand_towards_right) {
|
||||
assert(!inputs->empty());
|
||||
assert(output_level_inputs->empty());
|
||||
const int input_level = inputs->level;
|
||||
@@ -500,7 +499,6 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
// user key, while excluding other entries for the same user key. This
|
||||
// can happen when one user key spans multiple files.
|
||||
if (!output_level_inputs->empty()) {
|
||||
const uint64_t limit = mutable_cf_options.max_compaction_bytes;
|
||||
const uint64_t output_level_inputs_size =
|
||||
TotalFileSize(output_level_inputs->files);
|
||||
const uint64_t inputs_size = TotalFileSize(inputs->files);
|
||||
@@ -527,8 +525,6 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
try_overlapping_inputs = false;
|
||||
}
|
||||
if (try_overlapping_inputs && expanded_inputs.size() > inputs->size() &&
|
||||
(mutable_cf_options.ignore_max_compaction_bytes_for_input ||
|
||||
output_level_inputs_size + expanded_inputs_size < limit) &&
|
||||
!AreFilesInCompaction(expanded_inputs.files)) {
|
||||
InternalKey new_start, new_limit;
|
||||
GetRange(expanded_inputs, &new_start, &new_limit);
|
||||
@@ -551,8 +547,6 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
base_index, nullptr);
|
||||
expanded_inputs_size = TotalFileSize(expanded_inputs.files);
|
||||
if (expanded_inputs.size() > inputs->size() &&
|
||||
(mutable_cf_options.ignore_max_compaction_bytes_for_input ||
|
||||
output_level_inputs_size + expanded_inputs_size < limit) &&
|
||||
!AreFilesInCompaction(expanded_inputs.files)) {
|
||||
expand_inputs = true;
|
||||
}
|
||||
@@ -812,8 +806,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
output_level_inputs.level = output_level;
|
||||
if (input_level != output_level) {
|
||||
int parent_index = -1;
|
||||
if (!SetupOtherInputs(cf_name, mutable_cf_options, vstorage, &inputs,
|
||||
&output_level_inputs, &parent_index, -1)) {
|
||||
if (!SetupOtherInputs(cf_name, vstorage, &inputs, &output_level_inputs,
|
||||
&parent_index, -1)) {
|
||||
// manual compaction is now multi-threaded, so it can
|
||||
// happen that SetupOtherInputs fails
|
||||
// we handle it higher in RunManualCompaction
|
||||
|
||||
@@ -186,7 +186,6 @@ class CompactionPicker {
|
||||
int penultimate_level) const;
|
||||
|
||||
bool SetupOtherInputs(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs,
|
||||
CompactionInputFiles* output_level_inputs,
|
||||
@@ -277,8 +276,7 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
}
|
||||
|
||||
// Always returns false.
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* /*vstorage*/) const override {
|
||||
bool NeedsCompaction(const VersionStorageInfo* /*vstorage*/) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,26 +18,27 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) override;
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual Compaction* CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
int input_level, int output_level,
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) override;
|
||||
Compaction* CompactRange(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage, int input_level,
|
||||
int output_level,
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore,
|
||||
const std::string& trim_ts) override;
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
virtual int MaxOutputLevel() const override { return 0; }
|
||||
int MaxOutputLevel() const override { return 0; }
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
private:
|
||||
Compaction* PickTTLCompaction(const std::string& cf_name,
|
||||
|
||||
@@ -113,6 +113,15 @@ class LevelCompactionBuilder {
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
// When total L0 size is small compared to Lbase, try to pick intra-L0
|
||||
// compaction starting from the newest L0 file. This helps to prevent
|
||||
// L0->Lbase compaction with large write-amp.
|
||||
//
|
||||
// Returns true iff an intra-L0 compaction is picked.
|
||||
// `start_level_inputs_` and `output_level_` will be updated accordingly if
|
||||
// a compaction is picked.
|
||||
bool PickSizeBasedIntraL0Compaction();
|
||||
|
||||
// Return true if TrivialMove is extended. `start_index` is the index of
|
||||
// the initial file picked, which should already be in `start_level_inputs_`.
|
||||
bool TryExtendNonL0TrivialMove(int start_index,
|
||||
@@ -456,9 +465,8 @@ bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
}
|
||||
if (!is_l0_trivial_move_ &&
|
||||
!compaction_picker_->SetupOtherInputs(
|
||||
cf_name_, mutable_cf_options_, vstorage_, &start_level_inputs_,
|
||||
&output_level_inputs_, &parent_index_, base_index_,
|
||||
round_robin_expanding)) {
|
||||
cf_name_, vstorage_, &start_level_inputs_, &output_level_inputs_,
|
||||
&parent_index_, base_index_, round_robin_expanding)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -776,6 +784,9 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
// being compacted at level 0.
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
if (PickSizeBasedIntraL0Compaction()) {
|
||||
return true;
|
||||
}
|
||||
TEST_SYNC_POINT("LevelCompactionPicker::PickCompactionBySize:0");
|
||||
return false;
|
||||
}
|
||||
@@ -788,6 +799,9 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
if (TryPickL0TrivialMove()) {
|
||||
return true;
|
||||
}
|
||||
if (start_level_ == 0 && PickSizeBasedIntraL0Compaction()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
@@ -892,6 +906,56 @@ bool LevelCompactionBuilder::PickIntraL0Compaction() {
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
&start_level_inputs_);
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickSizeBasedIntraL0Compaction() {
|
||||
assert(start_level_ == 0);
|
||||
int base_level = vstorage_->base_level();
|
||||
if (base_level <= 0) {
|
||||
return false;
|
||||
}
|
||||
const std::vector<FileMetaData*>& l0_files =
|
||||
vstorage_->LevelFiles(/*level=*/0);
|
||||
size_t min_num_file =
|
||||
std::max(2, mutable_cf_options_.level0_file_num_compaction_trigger);
|
||||
if (l0_files.size() < min_num_file) {
|
||||
return false;
|
||||
}
|
||||
uint64_t l0_size = 0;
|
||||
for (const auto& file : l0_files) {
|
||||
l0_size += file->fd.GetFileSize();
|
||||
}
|
||||
const uint64_t min_lbase_size =
|
||||
l0_size * static_cast<uint64_t>(std::max(
|
||||
10.0, mutable_cf_options_.max_bytes_for_level_multiplier));
|
||||
assert(min_lbase_size >= l0_size);
|
||||
const std::vector<FileMetaData*>& lbase_files =
|
||||
vstorage_->LevelFiles(/*level=*/base_level);
|
||||
uint64_t lbase_size = 0;
|
||||
for (const auto& file : lbase_files) {
|
||||
lbase_size += file->fd.GetFileSize();
|
||||
if (lbase_size > min_lbase_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lbase_size <= min_lbase_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.clear();
|
||||
start_level_inputs_.level = 0;
|
||||
for (const auto& file : l0_files) {
|
||||
if (file->being_compacted) {
|
||||
break;
|
||||
}
|
||||
start_level_inputs_.files.push_back(file);
|
||||
}
|
||||
if (start_level_inputs_.files.size() < min_num_file) {
|
||||
start_level_inputs_.clear();
|
||||
return false;
|
||||
}
|
||||
output_level_ = 0;
|
||||
return true /* picked an intra-L0 compaction */;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
|
||||
@@ -20,14 +20,13 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
LevelCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1443,15 +1443,12 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping4) {
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000000;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
mutable_cf_options_.ignore_max_compaction_bytes_for_input = false;
|
||||
|
||||
// file 7 and 8 over lap with the same file, but file 8 is smaller so
|
||||
// it will be picked.
|
||||
// Overlaps with file 26, 27. And the file is compensated so will be
|
||||
// picked up.
|
||||
// Overlaps with file 26, 27, ratio is
|
||||
// (60000000U + 60000000U / 180000000U) = 0.67
|
||||
Add(2, 6U, "150", "167", 60000000U, 0, 100, 100, 180000000U);
|
||||
Add(2, 7U, "168", "169", 60000000U); // Overlaps with file 27
|
||||
Add(2, 8U, "201", "300", 61000000U); // Overlaps with file 28
|
||||
Add(2, 7U, "178", "189", 60000000U); // Overlaps with file 28
|
||||
Add(2, 8U, "401", "500", 61000000U); // Overlaps with file 29
|
||||
|
||||
Add(3, 26U, "160", "165", 60000000U);
|
||||
// Boosted file size in output level is not considered.
|
||||
@@ -1465,7 +1462,7 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping4) {
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
// Picking file 8 because overlapping ratio is the biggest.
|
||||
// Picking file 6 because overlapping ratio is the biggest.
|
||||
ASSERT_EQ(6U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
@@ -2468,20 +2465,23 @@ TEST_F(CompactionPickerTest, IsBottommostLevelTest) {
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesHit) {
|
||||
TEST_F(CompactionPickerTest, IgnoreCompactionLimitWhenAddFileFromInputLevel) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 800000u;
|
||||
mutable_cf_options_.ignore_max_compaction_bytes_for_input = false;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// It can expand because adding file 1 and 3, the compaction size will
|
||||
// exceed mutable_cf_options_.max_bytes_for_level_base.
|
||||
// It pulls in other compaction input file from the input level L1
|
||||
// without pulling in more output level files.
|
||||
// Files 1, 3, 4 will be included in the compaction.
|
||||
// File 6 is excluded since it overlaps with file 7.
|
||||
Add(1, 1U, "100", "150", 300000U);
|
||||
Add(1, 2U, "151", "200", 300001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 300000U, 0, 0);
|
||||
Add(2, 5U, "100", "256", 1U);
|
||||
Add(1, 6U, "325", "400", 300000U, 0, 0);
|
||||
Add(2, 5U, "100", "350", 1U);
|
||||
Add(2, 7U, "375", "425", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -2489,37 +2489,12 @@ TEST_F(CompactionPickerTest, MaxCompactionBytesHit) {
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesNotHit) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 800000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 1000000u;
|
||||
mutable_cf_options_.ignore_max_compaction_bytes_for_input = false;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// and it expands to file 1 and 3 too.
|
||||
Add(1, 1U, "100", "150", 300000U);
|
||||
Add(1, 2U, "151", "200", 300001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 300000U, 0, 0);
|
||||
Add(2, 5U, "000", "251", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(3U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(0, 2)->fd.GetNumber());
|
||||
ASSERT_EQ(4U, compaction->input(0, 3)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
@@ -4268,6 +4243,58 @@ TEST_F(CompactionPickerTest,
|
||||
INSTANTIATE_TEST_CASE_P(PerKeyPlacementCompactionPickerTest,
|
||||
PerKeyPlacementCompactionPickerTest, ::testing::Bool());
|
||||
|
||||
TEST_F(CompactionPickerTest, IntraL0WhenL0IsSmall) {
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
for (const uint64_t lbase_size_multiplier : {1, 10, 11, 40}) {
|
||||
SCOPED_TRACE("lbase_size_multiplier=" +
|
||||
std::to_string(lbase_size_multiplier));
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// When L0 size is <= Lbase size / max_bytes_for_level_multiplier,
|
||||
// intra-L0 compaction is picked. Otherwise, L0->L1
|
||||
// compaction is picked.
|
||||
Add(/*level=*/0, /*file_number=*/1U, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/1000, /*path_id=*/0,
|
||||
/*smallest_seq=*/10, /*largest_seq=*/11,
|
||||
/*compensated_file_size=*/1000);
|
||||
Add(/*level=*/0, /*file_number=*/2U, /*smallest=*/"100",
|
||||
/*largest=*/"100", /*file_size=*/1000, /*path_id=*/0,
|
||||
/*smallest_seq=*/20, /*largest_seq=*/21,
|
||||
/*compensated_file_size=*/1000);
|
||||
Add(/*level=*/0, /*file_number=*/3U, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/1000, /*path_id=*/0,
|
||||
/*smallest_seq=*/30, /*largest_seq=*/31,
|
||||
/*compensated_file_size=*/1000);
|
||||
Add(/*level=*/0, /*file_number=*/4U, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/1000, /*path_id=*/0,
|
||||
/*smallest_seq=*/40, /*largest_seq=*/41,
|
||||
/*compensated_file_size=*/1000);
|
||||
const uint64_t l0_size = 4000;
|
||||
const uint64_t lbase_size = l0_size * lbase_size_multiplier;
|
||||
Add(/*level=*/1, /*file_number=*/5U, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/lbase_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/0, /*largest_seq=*/0,
|
||||
/*compensated_file_size=*/lbase_size);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
LevelCompactionPicker compaction_picker(ioptions_, &icmp_);
|
||||
std::unique_ptr<Compaction> compaction(compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
if (lbase_size_multiplier >
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier) {
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
} else {
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1, compaction->output_level());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -1139,8 +1139,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
// from bottom_start_idx and bottom_end_idx, but for now, we use
|
||||
// SetupOtherInputs() for simplicity.
|
||||
int parent_index = -1; // Create and use bottom_start_idx?
|
||||
if (!picker_->SetupOtherInputs(cf_name_, mutable_cf_options_, vstorage_,
|
||||
&second_last_level_inputs,
|
||||
if (!picker_->SetupOtherInputs(cf_name_, vstorage_, &second_last_level_inputs,
|
||||
&bottom_level_inputs, &parent_index,
|
||||
/*base_index=*/-1)) {
|
||||
return nullptr;
|
||||
@@ -1311,9 +1310,8 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
int parent_index = -1;
|
||||
|
||||
output_level_inputs.level = output_level;
|
||||
if (!picker_->SetupOtherInputs(cf_name_, mutable_cf_options_, vstorage_,
|
||||
&start_level_inputs, &output_level_inputs,
|
||||
&parent_index, -1)) {
|
||||
if (!picker_->SetupOtherInputs(cf_name_, vstorage_, &start_level_inputs,
|
||||
&output_level_inputs, &parent_index, -1)) {
|
||||
return nullptr;
|
||||
}
|
||||
inputs.push_back(start_level_inputs);
|
||||
|
||||
@@ -17,14 +17,13 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
UniversalCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
virtual int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -195,8 +195,8 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
|
||||
auto cfd = compaction->column_family_data();
|
||||
sub_compact->Current().AddOutput(std::move(meta),
|
||||
cfd->internal_comparator(), false, false,
|
||||
true, file.paranoid_hash);
|
||||
cfd->internal_comparator(), false, true,
|
||||
file.paranoid_hash);
|
||||
}
|
||||
sub_compact->compaction_job_stats = compaction_result.stats;
|
||||
sub_compact->Current().SetNumOutputRecords(
|
||||
|
||||
@@ -1338,7 +1338,7 @@ class PrecludeLastLevelTest : public DBTestBase {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::StartPeriodicTaskScheduler:Init", [&](void* arg) {
|
||||
auto periodic_task_scheduler_ptr =
|
||||
reinterpret_cast<PeriodicTaskScheduler*>(arg);
|
||||
static_cast<PeriodicTaskScheduler*>(arg);
|
||||
periodic_task_scheduler_ptr->TEST_OverrideTimer(mock_clock_.get());
|
||||
});
|
||||
mock_clock_->SetCurrentTime(kMockStartTime);
|
||||
@@ -1823,7 +1823,6 @@ TEST_P(PrecludeLastLevelTestWithParms, PeriodicCompactionToPenultimateLevel) {
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.num_levels = kNumLevels;
|
||||
options.ignore_max_compaction_bytes_for_input = false;
|
||||
options.periodic_compaction_seconds = 10000;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
+7
-38
@@ -53,10 +53,9 @@ class ErrorFS : public FileSystemWrapper {
|
||||
num_writable_file_errors_(0) {}
|
||||
const char* Name() const override { return "ErrorEnv"; }
|
||||
|
||||
virtual IOStatus NewWritableFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
IOStatus NewWritableFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
result->reset();
|
||||
if (writable_file_error_) {
|
||||
++num_writable_file_errors_;
|
||||
@@ -823,7 +822,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
Status s;
|
||||
@@ -854,7 +852,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
Status s;
|
||||
for (const auto& mode : corruption_modes) {
|
||||
delete db_;
|
||||
@@ -886,7 +883,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
@@ -923,7 +919,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
@@ -963,7 +958,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
for (bool do_flush : {true, false}) {
|
||||
@@ -1032,7 +1026,6 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
@@ -1047,7 +1040,6 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
|
||||
mock->SetCorruptionMode(mock::MockTableFactory::kCorruptNone);
|
||||
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "true"}}));
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_NOK(
|
||||
@@ -1060,7 +1052,6 @@ TEST_F(CorruptionTest, FlushKeyOrderCheck) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "true"}}));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo1", "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo2", "v1"));
|
||||
@@ -1085,28 +1076,6 @@ TEST_F(CorruptionTest, FlushKeyOrderCheck) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, DisableKeyOrderCheck) {
|
||||
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "false"}}));
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"OutputValidator::Add:order_check",
|
||||
[&](void* /*arg*/) { ASSERT_TRUE(false); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo1", "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo3", "v1"));
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo2", "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo4", "v1"));
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(
|
||||
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
@@ -1133,7 +1102,7 @@ TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
int count{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::VerifyFullFileChecksum:mismatch", [&](void* arg) {
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
ASSERT_NE(s, nullptr);
|
||||
++count;
|
||||
ASSERT_NOK(*s);
|
||||
@@ -1278,7 +1247,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLogSizeAndMaybeTruncate:0", [&](void* arg) {
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
@@ -1460,7 +1429,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::Open::BeforeSyncWAL", [&](void* arg) {
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
@@ -1628,7 +1597,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecoveryWithFlush) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLogSizeAndMaybeTruncate:0", [&](void* arg) {
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
|
||||
@@ -131,7 +131,7 @@ TEST_F(CuckooTableDBTest, Flush) {
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
|
||||
TablePropertiesCollection ptc;
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(1U, ptc.size());
|
||||
ASSERT_EQ(3U, ptc.begin()->second->num_entries);
|
||||
@@ -148,7 +148,7 @@ TEST_F(CuckooTableDBTest, Flush) {
|
||||
ASSERT_OK(Put("key6", "v6"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(2U, ptc.size());
|
||||
auto row = ptc.begin();
|
||||
@@ -166,7 +166,7 @@ TEST_F(CuckooTableDBTest, Flush) {
|
||||
ASSERT_OK(Delete("key5"));
|
||||
ASSERT_OK(Delete("key4"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(3U, ptc.size());
|
||||
row = ptc.begin();
|
||||
@@ -191,7 +191,7 @@ TEST_F(CuckooTableDBTest, FlushWithDuplicateKeys) {
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
|
||||
TablePropertiesCollection ptc;
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(1U, ptc.size());
|
||||
ASSERT_EQ(2U, ptc.begin()->second->num_entries);
|
||||
|
||||
+227
-93
@@ -25,10 +25,60 @@
|
||||
#include "util/random.h"
|
||||
#include "utilities/counted_fs.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
class MyFlushBlockPolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
explicit MyFlushBlockPolicy(const int num_keys_in_block,
|
||||
const BlockBuilder& data_block_builder)
|
||||
: num_keys_in_block_(num_keys_in_block),
|
||||
num_keys_(0),
|
||||
data_block_builder_(data_block_builder) {}
|
||||
|
||||
bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (data_block_builder_.empty()) {
|
||||
// First key in this block
|
||||
num_keys_ = 1;
|
||||
return false;
|
||||
}
|
||||
// Flush every 10 keys
|
||||
if (num_keys_ == num_keys_in_block_) {
|
||||
num_keys_ = 1;
|
||||
return true;
|
||||
}
|
||||
num_keys_++;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int num_keys_in_block_;
|
||||
int num_keys_;
|
||||
const BlockBuilder& data_block_builder_;
|
||||
};
|
||||
|
||||
class MyFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
|
||||
public:
|
||||
explicit MyFlushBlockPolicyFactory(const int num_keys_in_block)
|
||||
: num_keys_in_block_(num_keys_in_block) {}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "MyFlushBlockPolicyFactory";
|
||||
}
|
||||
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new MyFlushBlockPolicy(num_keys_in_block_, data_block_builder);
|
||||
}
|
||||
|
||||
private:
|
||||
const int num_keys_in_block_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static bool enable_io_uring = true;
|
||||
extern "C" bool RocksDbIOUringEnable() { return enable_io_uring; }
|
||||
@@ -1853,7 +1903,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedEmptyLevel) {
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test requires coroutine support");
|
||||
@@ -1865,12 +1915,37 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedEmptyLevel) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(env_->GetFileSystem()));
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fault_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
|
||||
LRUCacheOptions cache_opts;
|
||||
cache_opts.capacity = 1 << 20;
|
||||
|
||||
BlockBasedTableOptions table_opts;
|
||||
table_opts.metadata_cache_options.top_level_index_pinning = PinningTier::kAll;
|
||||
table_opts.metadata_cache_options.partition_pinning = PinningTier::kNone;
|
||||
table_opts.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
table_opts.cache_index_and_filter_blocks = true;
|
||||
table_opts.block_cache = cache_opts.MakeSharedCache();
|
||||
table_opts.flush_block_policy_factory.reset(new MyFlushBlockPolicyFactory(1));
|
||||
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_opts));
|
||||
Reopen(options);
|
||||
int key;
|
||||
|
||||
// Setup the LSM so that the following search bounds are generated for
|
||||
// key 9 for each level -
|
||||
// Level 1 - lb = 0, rb = max
|
||||
// Level 2 - lb = 0, rb = 0
|
||||
// Level 3 - lb = 0, rb = -1
|
||||
// Level 4 - lb = 0, rb = 0
|
||||
|
||||
key = 9;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -1881,6 +1956,9 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedEmptyLevel) {
|
||||
key = 9;
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
const Snapshot* snap = dbfull()->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "val_l2_ext_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
// Leave level 3 empty
|
||||
MoveFilesToLevel(2);
|
||||
@@ -1899,8 +1977,138 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedEmptyLevel) {
|
||||
keys.push_back("key_" + std::to_string(9));
|
||||
keys.push_back("key_" + std::to_string(9));
|
||||
|
||||
int num_reads = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FaultInjectionTestFS::RandomRead", [&](void*) {
|
||||
++num_reads;
|
||||
// Fail on the 2nd read. First read is index partition,
|
||||
// second read is data block in level 1
|
||||
if (num_reads == 2) {
|
||||
fault_fs->SetFilesystemActive(false);
|
||||
} else {
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
size_t capacity = table_opts.block_cache->GetCapacity();
|
||||
table_opts.block_cache->SetCapacity(0);
|
||||
table_opts.block_cache->SetCapacity(capacity);
|
||||
|
||||
values = MultiGet(keys, nullptr, std::get<1>(GetParam()));
|
||||
ASSERT_EQ(values.size(), 2);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
dbfull()->ReleaseSnapshot(snap);
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesNonEmptyLevel) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test requires coroutine support");
|
||||
return;
|
||||
}
|
||||
#endif // USE_COROUTINES
|
||||
// Skip for unbatched MultiGet
|
||||
if (!std::get<0>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test is only for batched MultiGet");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(env_->GetFileSystem()));
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fault_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
|
||||
LRUCacheOptions cache_opts;
|
||||
cache_opts.capacity = 1 << 20;
|
||||
|
||||
BlockBasedTableOptions table_opts;
|
||||
table_opts.metadata_cache_options.top_level_index_pinning = PinningTier::kAll;
|
||||
table_opts.metadata_cache_options.partition_pinning = PinningTier::kNone;
|
||||
table_opts.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
table_opts.cache_index_and_filter_blocks = true;
|
||||
table_opts.block_cache = cache_opts.MakeSharedCache();
|
||||
table_opts.flush_block_policy_factory.reset(new MyFlushBlockPolicyFactory(1));
|
||||
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_opts));
|
||||
Reopen(options);
|
||||
int key;
|
||||
|
||||
// Setup the LSM so that the following search bounds are generated for
|
||||
// key 9 for each level -
|
||||
// Level 1 - lb = 0, rb = max
|
||||
// Level 2 - lb = 0, rb = 0
|
||||
// Level 3 - lb = 0, rb = 1
|
||||
// Level 4 - N/A
|
||||
|
||||
key = 8;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
|
||||
key = 7;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
key = 9;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(3);
|
||||
|
||||
key = 5;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
key = 9;
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "merge1_l2_" + std::to_string(key)));
|
||||
const Snapshot* snap = dbfull()->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "merge2_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
key = 2;
|
||||
ASSERT_OK(Put("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
key = 6;
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(key), "val_l2_" + std::to_string(key)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
keys.push_back("key_" + std::to_string(9));
|
||||
keys.push_back("key_" + std::to_string(9));
|
||||
|
||||
int num_reads = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FaultInjectionTestFS::RandomRead", [&](void*) {
|
||||
++num_reads;
|
||||
// Fail on the 2nd read. First read is index partition,
|
||||
// second read is data block in level 1
|
||||
if (num_reads == 2) {
|
||||
fault_fs->SetFilesystemActive(false);
|
||||
} else {
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
size_t capacity = table_opts.block_cache->GetCapacity();
|
||||
table_opts.block_cache->SetCapacity(0);
|
||||
table_opts.block_cache->SetCapacity(capacity);
|
||||
|
||||
values = MultiGet(keys, nullptr, std::get<1>(GetParam()));
|
||||
ASSERT_EQ(values.size(), 2);
|
||||
ASSERT_EQ(values[0], "Corruption: Not active");
|
||||
ASSERT_EQ(values[1], "val_l2_9,merge1_l2_9,merge2_l2_9");
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
dbfull()->ReleaseSnapshot(snap);
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevelMerge) {
|
||||
@@ -3134,8 +3342,7 @@ TEST_F(DBBasicTest, BestEffortsRecoveryWithVersionBuildingFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -3566,7 +3773,7 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
}
|
||||
table_options.flush_block_policy_factory.reset(
|
||||
new MyFlushBlockPolicyFactory());
|
||||
new MyFlushBlockPolicyFactory(10));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
if (!compression_enabled_) {
|
||||
options.compression = kNoCompression;
|
||||
@@ -3654,46 +3861,6 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
static void TearDownTestCase() {}
|
||||
|
||||
protected:
|
||||
class MyFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
|
||||
public:
|
||||
MyFlushBlockPolicyFactory() {}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "MyFlushBlockPolicyFactory";
|
||||
}
|
||||
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new MyFlushBlockPolicy(data_block_builder);
|
||||
}
|
||||
};
|
||||
|
||||
class MyFlushBlockPolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
explicit MyFlushBlockPolicy(const BlockBuilder& data_block_builder)
|
||||
: num_keys_(0), data_block_builder_(data_block_builder) {}
|
||||
|
||||
bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (data_block_builder_.empty()) {
|
||||
// First key in this block
|
||||
num_keys_ = 1;
|
||||
return false;
|
||||
}
|
||||
// Flush every 10 keys
|
||||
if (num_keys_ == 10) {
|
||||
num_keys_ = 1;
|
||||
return true;
|
||||
}
|
||||
num_keys_++;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int num_keys_;
|
||||
const BlockBuilder& data_block_builder_;
|
||||
};
|
||||
|
||||
class MyBlockCache : public CacheWrapper {
|
||||
public:
|
||||
explicit MyBlockCache(std::shared_ptr<Cache> target)
|
||||
@@ -4110,7 +4277,7 @@ class DeadlineRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
const IOOptions& options, IODebugContext* dbg) override;
|
||||
|
||||
IOStatus ReadAsync(FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb,
|
||||
std::function<void(FSReadRequest&, void*)> cb,
|
||||
void* cb_arg, void** io_handle, IOHandleDeleter* del_fn,
|
||||
IODebugContext* dbg) override;
|
||||
|
||||
@@ -4256,7 +4423,7 @@ IOStatus DeadlineRandomAccessFile::Read(uint64_t offset, size_t len,
|
||||
|
||||
IOStatus DeadlineRandomAccessFile::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
std::function<void(FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn, IODebugContext* dbg) {
|
||||
const std::chrono::microseconds deadline = fs_.GetDeadline();
|
||||
const std::chrono::microseconds io_timeout = fs_.GetIOTimeout();
|
||||
@@ -4337,68 +4504,35 @@ TEST_P(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
ReopenWithColumnFamilies(GetCFNames(), options);
|
||||
|
||||
// Test the non-batched version of MultiGet with multiple column
|
||||
// families
|
||||
// Test batched MultiGet with an IO delay in the first data block read.
|
||||
// Both keys in the first CF should succeed as they're in the same data
|
||||
// block and would form one batch, and we check for deadline between
|
||||
// batches.
|
||||
std::vector<std::string> key_str;
|
||||
size_t i;
|
||||
for (i = 0; i < 5; ++i) {
|
||||
for (i = 0; i < 10; ++i) {
|
||||
key_str.emplace_back(Key(static_cast<int>(i)));
|
||||
}
|
||||
std::vector<ColumnFamilyHandle*> cfs(key_str.size());
|
||||
;
|
||||
std::vector<Slice> keys(key_str.size());
|
||||
std::vector<std::string> values(key_str.size());
|
||||
std::vector<PinnableSlice> pin_values(keys.size());
|
||||
|
||||
for (i = 0; i < key_str.size(); ++i) {
|
||||
cfs[i] = handles_[i];
|
||||
// 2 keys per CF
|
||||
cfs[i] = handles_[i / 2];
|
||||
keys[i] = Slice(key_str[i].data(), key_str[i].size());
|
||||
}
|
||||
|
||||
ReadOptions ro;
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
ro.async_io = GetParam();
|
||||
// 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
|
||||
// subsequent keys fail due to deadline exceeded
|
||||
CheckStatus(statuses, 1);
|
||||
|
||||
// Clear the cache
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
// Test non-batched Multiget with multiple column families and
|
||||
// introducing an IO delay in one of the middle CFs
|
||||
key_str.clear();
|
||||
for (i = 0; i < 10; ++i) {
|
||||
key_str.emplace_back(Key(static_cast<int>(i)));
|
||||
}
|
||||
cfs.resize(key_str.size());
|
||||
keys.resize(key_str.size());
|
||||
values.resize(key_str.size());
|
||||
for (i = 0; i < key_str.size(); ++i) {
|
||||
// 2 keys per CF
|
||||
cfs[i] = handles_[i / 2];
|
||||
keys[i] = Slice(key_str[i].data(), key_str[i].size());
|
||||
}
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 1);
|
||||
statuses = dbfull()->MultiGet(ro, cfs, keys, &values);
|
||||
CheckStatus(statuses, 3);
|
||||
|
||||
// Test batched MultiGet with an IO delay in the first data block read.
|
||||
// Both keys in the first CF should succeed as they're in the same data
|
||||
// block and would form one batch, and we check for deadline between
|
||||
// batches.
|
||||
std::vector<PinnableSlice> pin_values(keys.size());
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelayTrigger(ro.deadline, ro.io_timeout, 0);
|
||||
std::vector<Status> statuses(key_str.size());
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
// The first key is successful because we check after the lookup, but
|
||||
// subsequent keys fail due to deadline exceeded
|
||||
CheckStatus(statuses, 2);
|
||||
|
||||
// Similar to the previous one, but an IO delay in the third CF data block
|
||||
@@ -4476,7 +4610,7 @@ TEST_F(DBBasicTest, ManifestWriteFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::ProcessManifestWrites:AfterSyncManifest", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
ASSERT_OK(*s);
|
||||
// Manually overwrite return status
|
||||
*s = Status::IOError();
|
||||
@@ -4531,7 +4665,7 @@ TEST_F(DBBasicTest, FailOpenIfLoggerCreationFail) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"rocksdb::CreateLoggerFromOptions:AfterGetPath", [&](void* arg) {
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected");
|
||||
});
|
||||
|
||||
@@ -1373,7 +1373,7 @@ class StableCacheKeyTestFS : public FaultInjectionTestFS {
|
||||
SetFailGetUniqueId(true);
|
||||
}
|
||||
|
||||
virtual ~StableCacheKeyTestFS() override {}
|
||||
~StableCacheKeyTestFS() override {}
|
||||
|
||||
IOStatus LinkFile(const std::string&, const std::string&, const IOOptions&,
|
||||
IODebugContext*) override {
|
||||
@@ -1424,7 +1424,7 @@ TEST_P(DBBlockCacheKeyTest, StableCacheKeys) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::BlockBasedTableBuilder:PreSetupBaseCacheKey",
|
||||
[&](void* arg) {
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
TableProperties* props = static_cast<TableProperties*>(arg);
|
||||
props->orig_file_number = 0;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
@@ -3562,6 +3563,277 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter3) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, SstQueryFilter) {
|
||||
using experimental::KeySegmentsExtractor;
|
||||
using experimental::MakeSharedBytewiseMinMaxSQFC;
|
||||
using experimental::SelectKeySegment;
|
||||
using experimental::SstQueryFilterConfigs;
|
||||
using experimental::SstQueryFilterConfigsManager;
|
||||
using KeyCategorySet = KeySegmentsExtractor::KeyCategorySet;
|
||||
|
||||
struct MySegmentExtractor : public KeySegmentsExtractor {
|
||||
char min_first_char;
|
||||
char max_first_char;
|
||||
char delim_char;
|
||||
MySegmentExtractor(char _min_first_char, char _max_first_char,
|
||||
char _delim_char)
|
||||
: min_first_char(_min_first_char),
|
||||
max_first_char(_max_first_char),
|
||||
delim_char(_delim_char) {}
|
||||
|
||||
const char* Name() const override { return "MySegmentExtractor"; }
|
||||
|
||||
std::string GetId() const override {
|
||||
return std::string("MySegmentExtractor+") + min_first_char +
|
||||
max_first_char + delim_char;
|
||||
}
|
||||
|
||||
void Extract(const Slice& key_or_bound, KeyKind /*kind*/,
|
||||
Result* result) const override {
|
||||
size_t len = key_or_bound.size();
|
||||
if (len == 0) {
|
||||
result->category = KeySegmentsExtractor::kReservedLowCategory;
|
||||
} else if (static_cast<unsigned char>(key_or_bound[0]) <
|
||||
static_cast<unsigned char>(min_first_char)) {
|
||||
result->category = KeySegmentsExtractor::kReservedLowCategory;
|
||||
} else if (static_cast<unsigned char>(key_or_bound[0]) >
|
||||
static_cast<unsigned char>(max_first_char)) {
|
||||
result->category = KeySegmentsExtractor::kReservedHighCategory;
|
||||
}
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
if (key_or_bound[i] == delim_char || i + 1 == key_or_bound.size()) {
|
||||
result->segment_ends.push_back(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Use '_' as delimiter, but different spans for default category
|
||||
auto extractor_to_c = std::make_shared<MySegmentExtractor>('a', 'c', '_');
|
||||
auto extractor_to_z = std::make_shared<MySegmentExtractor>('a', 'z', '_');
|
||||
auto extractor_alt = std::make_shared<MySegmentExtractor>('0', '9', '_');
|
||||
|
||||
// Filter on 2nd field, only for default category
|
||||
auto filter1_def = MakeSharedBytewiseMinMaxSQFC(
|
||||
experimental::SelectKeySegment(1),
|
||||
KeyCategorySet{KeySegmentsExtractor::kDefaultCategory});
|
||||
|
||||
// Also filter on 3rd field regardless of category
|
||||
auto filter2_all =
|
||||
MakeSharedBytewiseMinMaxSQFC(experimental::SelectKeySegment(2));
|
||||
|
||||
SstQueryFilterConfigs configs1 = {{filter1_def, filter2_all}, extractor_to_c};
|
||||
SstQueryFilterConfigs configs2 = {{filter1_def, filter2_all}, extractor_to_z};
|
||||
SstQueryFilterConfigs configs3 = {{filter2_all}, extractor_alt};
|
||||
|
||||
SstQueryFilterConfigsManager::Data data = {
|
||||
{42, {{"foo", configs1}}}, {43, {{"foo", configs2}, {"bar", configs3}}}};
|
||||
|
||||
std::shared_ptr<SstQueryFilterConfigsManager> configs_manager;
|
||||
ASSERT_OK(SstQueryFilterConfigsManager::MakeShared(data, &configs_manager));
|
||||
|
||||
// Test manager behaviors
|
||||
auto MakeFactory = [configs_manager](
|
||||
const std::string& configs_name,
|
||||
SstQueryFilterConfigsManager::FilteringVersion ver)
|
||||
-> std::shared_ptr<SstQueryFilterConfigsManager::Factory> {
|
||||
std::shared_ptr<SstQueryFilterConfigsManager::Factory> factory;
|
||||
Status s = configs_manager->MakeSharedFactory(configs_name, ver, &factory);
|
||||
assert(s.ok());
|
||||
return factory;
|
||||
};
|
||||
std::shared_ptr<SstQueryFilterConfigsManager::Factory> factory;
|
||||
|
||||
// Version 0 is always OK, returning empty/not found configs
|
||||
ASSERT_TRUE(MakeFactory("blah", 0)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_TRUE(MakeFactory("foo", 0)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_TRUE(MakeFactory("bar", 0)->GetConfigs().IsEmptyNotFound());
|
||||
|
||||
// We can't be sure about the proper configuration for versions outside the
|
||||
// known range (and reserved version 0).
|
||||
ASSERT_TRUE(configs_manager->MakeSharedFactory("foo", 1, &factory)
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(configs_manager->MakeSharedFactory("foo", 41, &factory)
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(configs_manager->MakeSharedFactory("foo", 44, &factory)
|
||||
.IsInvalidArgument());
|
||||
ASSERT_TRUE(configs_manager->MakeSharedFactory("foo", 500, &factory)
|
||||
.IsInvalidArgument());
|
||||
|
||||
ASSERT_TRUE(MakeFactory("blah", 42)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_TRUE(MakeFactory("blah", 43)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_FALSE(MakeFactory("foo", 42)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_FALSE(MakeFactory("foo", 43)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_TRUE(MakeFactory("bar", 42)->GetConfigs().IsEmptyNotFound());
|
||||
ASSERT_FALSE(MakeFactory("bar", 43)->GetConfigs().IsEmptyNotFound());
|
||||
|
||||
ASSERT_OK(configs_manager->MakeSharedFactory("foo", 42, &factory));
|
||||
ASSERT_EQ(factory->GetConfigsName(), "foo");
|
||||
ASSERT_EQ(factory->GetConfigs().IsEmptyNotFound(), false);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.table_properties_collector_factories.push_back(factory);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// For lower level file
|
||||
ASSERT_OK(Put(" ", "val0"));
|
||||
ASSERT_OK(Put(" _345_678", "val0"));
|
||||
ASSERT_OK(Put("aaa", "val0"));
|
||||
ASSERT_OK(Put("abc_123", "val1"));
|
||||
ASSERT_OK(Put("abc_13", "val2"));
|
||||
ASSERT_OK(Put("abc_156_987", "val3"));
|
||||
ASSERT_OK(Put("bcd_1722", "val4"));
|
||||
ASSERT_OK(Put("xyz_145", "val5"));
|
||||
ASSERT_OK(Put("xyz_167", "val6"));
|
||||
ASSERT_OK(Put("xyz_178", "val7"));
|
||||
ASSERT_OK(Put("zzz", "val0"));
|
||||
ASSERT_OK(Put("~~~", "val0"));
|
||||
ASSERT_OK(Put("~~~_456_789", "val0"));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
ASSERT_EQ(factory->GetFilteringVersion(), 42U);
|
||||
ASSERT_NOK(factory->SetFilteringVersion(41));
|
||||
ASSERT_NOK(factory->SetFilteringVersion(44));
|
||||
ASSERT_EQ(factory->GetFilteringVersion(), 42U);
|
||||
ASSERT_OK(factory->SetFilteringVersion(43));
|
||||
ASSERT_EQ(factory->GetFilteringVersion(), 43U);
|
||||
|
||||
// For higher level file
|
||||
ASSERT_OK(Put(" ", "val0"));
|
||||
ASSERT_OK(Put(" _345_680", "val0"));
|
||||
ASSERT_OK(Put("aaa", "val9"));
|
||||
ASSERT_OK(Put("abc_234", "val1"));
|
||||
ASSERT_OK(Put("abc_245_567", "val2"));
|
||||
ASSERT_OK(Put("abc_25", "val3"));
|
||||
ASSERT_OK(Put("xyz_180", "val4"));
|
||||
ASSERT_OK(Put("xyz_191", "val4"));
|
||||
ASSERT_OK(Put("xyz_260", "val4"));
|
||||
ASSERT_OK(Put("zzz", "val9"));
|
||||
ASSERT_OK(Put("~~~", "val0"));
|
||||
ASSERT_OK(Put("~~~_456_790", "val0"));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
using Keys = std::vector<std::string>;
|
||||
auto RangeQueryKeys =
|
||||
[factory, db = db_](
|
||||
std::string lb, std::string ub,
|
||||
std::shared_ptr<SstQueryFilterConfigsManager::Factory> alt_factory =
|
||||
nullptr) {
|
||||
Slice lb_slice = lb;
|
||||
Slice ub_slice = ub;
|
||||
|
||||
ReadOptions ro;
|
||||
ro.iterate_lower_bound = &lb_slice;
|
||||
ro.iterate_upper_bound = &ub_slice;
|
||||
ro.table_filter = (alt_factory ? alt_factory : factory)
|
||||
->GetTableFilterForRangeQuery(lb_slice, ub_slice);
|
||||
auto it = db->NewIterator(ro);
|
||||
Keys ret;
|
||||
for (it->Seek(lb_slice); it->Valid(); it->Next()) {
|
||||
ret.push_back(it->key().ToString());
|
||||
}
|
||||
EXPECT_OK(it->status());
|
||||
delete it;
|
||||
return ret;
|
||||
};
|
||||
|
||||
// Control 1: range is not filtered but min/max filter is checked
|
||||
// because of common prefix leading up to 2nd segment
|
||||
// TODO/future: statistics for when filter is checked vs. not applicable
|
||||
EXPECT_EQ(RangeQueryKeys("abc_150", "abc_249"),
|
||||
Keys({"abc_156_987", "abc_234", "abc_245_567"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// Test 1: range is filtered to just lowest level, fully containing the
|
||||
// segments in that category
|
||||
EXPECT_EQ(RangeQueryKeys("abc_100", "abc_179"),
|
||||
Keys({"abc_123", "abc_13", "abc_156_987"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 2: range is filtered to just lowest level, partial overlap
|
||||
EXPECT_EQ(RangeQueryKeys("abc_1500_x_y", "abc_16QQ"), Keys({"abc_156_987"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 3: range is filtered to just highest level, fully containing the
|
||||
// segments in that category but would be overlapping the range for the other
|
||||
// file if the filter included all categories
|
||||
EXPECT_EQ(RangeQueryKeys("abc_200", "abc_300"),
|
||||
Keys({"abc_234", "abc_245_567", "abc_25"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 4: range is filtered to just highest level, partial overlap (etc.)
|
||||
EXPECT_EQ(RangeQueryKeys("abc_200", "abc_249"),
|
||||
Keys({"abc_234", "abc_245_567"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 5: range is filtered from both levels, because of category scope
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Control 2: range is not filtered because association between 1st and
|
||||
// 2nd segment is not represented
|
||||
EXPECT_EQ(RangeQueryKeys("abc_170", "abc_190"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// Control 3: range is not filtered because there's no (bloom) filter on
|
||||
// 1st segment (like prefix filtering)
|
||||
EXPECT_EQ(RangeQueryKeys("baa_170", "baa_190"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// Control 4: range is not filtered because difference in segments leading
|
||||
// up to 2nd segment
|
||||
EXPECT_EQ(RangeQueryKeys("abc_500", "abd_501"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// TODO: exclusive upper bound tests
|
||||
|
||||
// ======= Testing 3rd segment (cross-category filter) =======
|
||||
// Control 5: not filtered because of segment range overlap
|
||||
EXPECT_EQ(RangeQueryKeys(" z__700", " z__750"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 2);
|
||||
|
||||
// Test 6: filtered on both levels
|
||||
EXPECT_EQ(RangeQueryKeys(" z__100", " z__300"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Control 6: finding something, with 2nd segment filter helping
|
||||
EXPECT_EQ(RangeQueryKeys("abc_156_9", "abc_156_99"), Keys({"abc_156_987"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
EXPECT_EQ(RangeQueryKeys("abc_245_56", "abc_245_57"), Keys({"abc_245_567"}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 1);
|
||||
|
||||
// Test 6: filtered on both levels, for different segments
|
||||
EXPECT_EQ(RangeQueryKeys("abc_245_900", "abc_245_999"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// ======= Testing extractor read portability =======
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Only modifies how filters are written
|
||||
ASSERT_OK(factory->SetFilteringVersion(0));
|
||||
ASSERT_EQ(factory->GetFilteringVersion(), 0U);
|
||||
ASSERT_EQ(factory->GetConfigs().IsEmptyNotFound(), true);
|
||||
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400"), Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Even a different config name with different extractor can read
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400", MakeFactory("bar", 43)),
|
||||
Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
|
||||
// Or a "not found" config name
|
||||
EXPECT_EQ(RangeQueryKeys("abc_300", "abc_400", MakeFactory("blah", 43)),
|
||||
Keys({}));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, NON_LAST_LEVEL_SEEK_DATA), 0);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
+99
-50
@@ -505,7 +505,7 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::FindTable:0", [&](void* arg) {
|
||||
assert(arg != nullptr);
|
||||
bool no_io = *(reinterpret_cast<bool*>(arg));
|
||||
bool no_io = *(static_cast<bool*>(arg));
|
||||
if (!no_io) {
|
||||
// filter out cases for table properties queries.
|
||||
num_table_cache_lookup++;
|
||||
@@ -681,7 +681,7 @@ TEST_F(DBCompactionTest, CompactRangeBottomPri) {
|
||||
int bottom_pri_count = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"ThreadPoolImpl::Impl::BGThread:BeforeRun", [&](void* arg) {
|
||||
Env::Priority* pri = reinterpret_cast<Env::Priority*>(arg);
|
||||
Env::Priority* pri = static_cast<Env::Priority*>(arg);
|
||||
// First time is low pri pool in the test case.
|
||||
if (low_pri_count == 0 && bottom_pri_count == 0) {
|
||||
ASSERT_EQ(Env::Priority::LOW, *pri);
|
||||
@@ -1870,13 +1870,60 @@ TEST_F(DBCompactionTest, ManualCompactionWithUnorderedWrite) {
|
||||
ASSERT_EQ(Get("foo"), "v2");
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
// Test params:
|
||||
// 1) whether to enable user-defined timestamps.
|
||||
class DBDeleteFileRangeTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBDeleteFileRangeTest()
|
||||
: DBTestBase("db_delete_file_range_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
void SetUp() override { enable_udt_ = GetParam(); }
|
||||
|
||||
protected:
|
||||
void PutKeyValue(const Slice& key, const Slice& value) {
|
||||
if (enable_udt_) {
|
||||
EXPECT_OK(db_->Put(WriteOptions(), key, min_ts_, value));
|
||||
} else {
|
||||
EXPECT_OK(Put(key, value));
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetValue(const std::string& key) {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
if (enable_udt_) {
|
||||
roptions.timestamp = &min_ts_;
|
||||
}
|
||||
Status s = db_->Get(roptions, key, &result);
|
||||
EXPECT_TRUE(s.ok());
|
||||
return result;
|
||||
}
|
||||
|
||||
Status MaybeGetValue(const std::string& key, std::string* result) {
|
||||
ReadOptions roptions;
|
||||
if (enable_udt_) {
|
||||
roptions.timestamp = &min_ts_;
|
||||
}
|
||||
Status s = db_->Get(roptions, key, result);
|
||||
EXPECT_TRUE(s.IsNotFound() || s.ok());
|
||||
return s;
|
||||
}
|
||||
|
||||
bool enable_udt_ = false;
|
||||
Slice min_ts_ = MinU64Ts();
|
||||
};
|
||||
|
||||
TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 4;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
options.max_background_compactions = 3;
|
||||
if (enable_udt_) {
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
@@ -1888,14 +1935,14 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
// file 1 [0 => 100]
|
||||
for (int32_t i = 0; i < 100; i++) {
|
||||
values[i] = rnd.RandomString(value_size);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// file 2 [100 => 300]
|
||||
for (int32_t i = 100; i < 300; i++) {
|
||||
values[i] = rnd.RandomString(value_size);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -1911,7 +1958,7 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
// file 3 [ 0 => 200]
|
||||
for (int32_t i = 0; i < 200; i++) {
|
||||
values[i] = rnd.RandomString(value_size);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -1923,7 +1970,7 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
}
|
||||
values[j] = rnd.RandomString(value_size);
|
||||
ASSERT_OK(Put(Key(j), values[j]));
|
||||
PutKeyValue(Key(j), values[j]);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
@@ -1948,11 +1995,10 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
int32_t deleted_count = 0;
|
||||
for (int32_t i = 0; i < 4300; i++) {
|
||||
if (i < 1000 || i > 2000) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
} else {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
Status s = db_->Get(roptions, Key(i), &result);
|
||||
Status s = MaybeGetValue(Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound() || s.ok());
|
||||
if (s.IsNotFound()) {
|
||||
deleted_count++;
|
||||
@@ -1982,7 +2028,7 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
for (int32_t i = 0; i < 4300; i++) {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
ASSERT_TRUE(db_->Get(roptions, Key(i), &result).IsNotFound());
|
||||
ASSERT_TRUE(MaybeGetValue(Key(i), &result).IsNotFound());
|
||||
deleted_count2++;
|
||||
}
|
||||
ASSERT_GT(deleted_count2, deleted_count);
|
||||
@@ -1990,13 +2036,16 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
ASSERT_GT(old_num_files, new_num_files);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 4;
|
||||
options.max_background_compactions = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
if (enable_udt_) {
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
@@ -2009,7 +2058,7 @@ TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
values[k] = rnd.RandomString(value_size);
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
PutKeyValue(Key(k), values[k]);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -2024,7 +2073,7 @@ TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
for (auto i = 0; i < 10; i += 2) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
PutKeyValue(Key(k), values[k]);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -2050,13 +2099,12 @@ TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
|
||||
// Keys [0, 300) should not exist.
|
||||
for (auto i = 0; i < 300; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 300; i < 1000; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2078,16 +2126,15 @@ TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
|
||||
// Keys [600, 900) should not exist.
|
||||
for (auto i = 600; i < 900; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 300; i < 600; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
}
|
||||
for (auto i = 900; i < 1000; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2098,15 +2145,14 @@ TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
for (auto i = 0; i < 1000; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
// regression test for #2833: groups of files whose user-keys overlap at the
|
||||
// endpoints could be split by `DeleteFilesInRange`. This caused old data to
|
||||
// reappear, either because a new version of the key was removed, or a range
|
||||
@@ -2118,6 +2164,9 @@ TEST_F(DBCompactionTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.target_file_size_base = 1 << 10; // 1KB
|
||||
if (enable_udt_) {
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
}
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// The snapshot prevents key 1 from having its old version dropped. The low
|
||||
@@ -2141,8 +2190,8 @@ TEST_F(DBCompactionTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
std::string vals[kNumL0Files];
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
vals[i] = rnd.RandomString(kValSize);
|
||||
ASSERT_OK(Put(Key(i), vals[i]));
|
||||
ASSERT_OK(Put(Key(i + 1), vals[i]));
|
||||
PutKeyValue(Key(i), vals[i]);
|
||||
PutKeyValue(Key(i + 1), vals[i]);
|
||||
ASSERT_OK(Flush());
|
||||
if (i == 0) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
@@ -2155,11 +2204,14 @@ TEST_F(DBCompactionTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
std::string begin_str = Key(0), end_str = Key(1);
|
||||
Slice begin = begin_str, end = end_str;
|
||||
ASSERT_OK(DeleteFilesInRange(db_, db_->DefaultColumnFamily(), &begin, &end));
|
||||
ASSERT_EQ(vals[1], Get(Key(1)));
|
||||
ASSERT_EQ(vals[1], GetValue(Key(1)));
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBDeleteFileRangeTest, DBDeleteFileRangeTest,
|
||||
::testing::Bool());
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, TrivialMoveToLastLevelWithFiles) {
|
||||
int32_t trivial_move = 0;
|
||||
int32_t non_trivial_move = 0;
|
||||
@@ -4192,7 +4244,7 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
ASSERT_NE(kMaxSequenceNumber, dbfull()->bottommost_files_mark_threshold_);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kBottommostFiles);
|
||||
});
|
||||
@@ -4248,7 +4300,7 @@ TEST_F(DBCompactionTest, DelayCompactBottomLevelFilesWithDeletions) {
|
||||
std::atomic_int compaction_count = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kBottommostFiles);
|
||||
compaction_count++;
|
||||
@@ -4379,7 +4431,7 @@ TEST_F(DBCompactionTest, RoundRobinTtlCompactionNormal) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -4529,7 +4581,7 @@ TEST_F(DBCompactionTest, RoundRobinTtlCompactionUnsortedTime) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -4645,7 +4697,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -4693,7 +4745,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -4720,7 +4772,6 @@ TEST_F(DBCompactionTest, LevelTtlCompactionOutputCuttingIteractingWithOther) {
|
||||
options.env = env_;
|
||||
options.target_file_size_base = 4 << 10;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level_compaction_dynamic_file_size = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
@@ -4821,7 +4872,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
int ttl_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -4969,7 +5020,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
int periodic_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5153,7 +5204,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
bool set_creation_time_to_zero = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5161,7 +5212,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* arg) {
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
TableProperties* props = static_cast<TableProperties*>(arg);
|
||||
if (set_file_creation_time_to_zero) {
|
||||
props->file_creation_time = 0;
|
||||
}
|
||||
@@ -5225,7 +5276,7 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
int ttl_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5408,7 +5459,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
|
||||
int periodic_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -7146,8 +7197,7 @@ TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency0", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
auto p = static_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
@@ -7184,8 +7234,7 @@ TEST_F(DBCompactionTest, ConsistencyFailTest2) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency1", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
auto p = static_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
@@ -7998,7 +8047,7 @@ TEST_F(DBCompactionTest, UpdateLevelSubCompactionTest) {
|
||||
bool has_compaction = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 10);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8022,7 +8071,7 @@ TEST_F(DBCompactionTest, UpdateLevelSubCompactionTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 2);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8050,7 +8099,7 @@ TEST_F(DBCompactionTest, UpdateUniversalSubCompactionTest) {
|
||||
bool has_compaction = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 10);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8073,7 +8122,7 @@ TEST_F(DBCompactionTest, UpdateUniversalSubCompactionTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 2);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -9650,6 +9699,7 @@ TEST_F(DBCompactionTest, BottomPriCompactionCountsTowardConcurrencyLimit) {
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
sleeping_task_low.WaitUntilSleeping();
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
@@ -10198,8 +10248,7 @@ TEST_F(DBCompactionTest, ErrorWhenReadFileHead) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::BeforeReturn",
|
||||
[&count, &error_file](void* pair_ptr) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
int cur = ++count;
|
||||
if (cur == error_file) {
|
||||
IOStatus* io_s = p->second;
|
||||
|
||||
@@ -338,7 +338,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
|
||||
std::set<int> output_levels;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionPicker::CompactRange:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
output_levels.insert(compaction->output_level());
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -121,7 +121,7 @@ Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
|
||||
// DisableFileDeletions / EnableFileDeletions not supported in read-only DB
|
||||
if (deletions_disabled.ok()) {
|
||||
Status s2 = EnableFileDeletions(/*force=*/false);
|
||||
Status s2 = EnableFileDeletions();
|
||||
assert(s2.ok());
|
||||
s2.PermitUncheckedError();
|
||||
} else {
|
||||
|
||||
+6
-6
@@ -272,7 +272,7 @@ TEST_F(DBFlushTest, ScheduleOnlyOneBgThread) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:AfterSchedule:0", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto unscheduled_flushes = *reinterpret_cast<int*>(arg);
|
||||
auto unscheduled_flushes = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(0, unscheduled_flushes);
|
||||
++called;
|
||||
});
|
||||
@@ -1791,7 +1791,7 @@ TEST_F(DBFlushTest, MemPurgeCorrectLogNumberAndSSTFileCreation) {
|
||||
std::atomic<uint64_t> num_memtable_at_first_flush(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:num_memtables", [&](void* arg) {
|
||||
uint64_t* mems_size = reinterpret_cast<uint64_t*>(arg);
|
||||
uint64_t* mems_size = static_cast<uint64_t*>(arg);
|
||||
// atomic_compare_exchange_strong sometimes updates the value
|
||||
// of ZERO (the "expected" object), so we make sure ZERO is indeed...
|
||||
// zero.
|
||||
@@ -2038,7 +2038,7 @@ TEST_F(DBFlushTest, FireOnFlushCompletedAfterCommittedResult) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table", [&listener](void* arg) {
|
||||
// Wait for the second flush finished, out of mutex.
|
||||
auto* mems = reinterpret_cast<autovector<MemTable*>*>(arg);
|
||||
auto* mems = static_cast<autovector<MemTable*>*>(arg);
|
||||
if (mems->front()->GetEarliestSequenceNumber() == listener->seq1 - 1) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:"
|
||||
@@ -2386,7 +2386,7 @@ TEST_F(DBFlushTest, PickRightMemtables) {
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables", [&](void* arg) {
|
||||
auto* job = reinterpret_cast<FlushJob*>(arg);
|
||||
auto* job = static_cast<FlushJob*>(arg);
|
||||
assert(job);
|
||||
const auto& mems = job->GetMemTables();
|
||||
assert(mems.size() == 1);
|
||||
@@ -2634,7 +2634,7 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
// it means atomic flush didn't write the min_log_number_to_keep to MANIFEST.
|
||||
cfs.push_back(kDefaultColumnFamilyName);
|
||||
ASSERT_OK(TryReopenWithColumnFamilies(cfs, options));
|
||||
DBImpl* db_impl = reinterpret_cast<DBImpl*>(db_);
|
||||
DBImpl* db_impl = static_cast<DBImpl*>(db_);
|
||||
ASSERT_TRUE(db_impl->allow_2pc());
|
||||
ASSERT_NE(db_impl->MinLogNumberToKeep(), 0);
|
||||
}
|
||||
@@ -3171,7 +3171,7 @@ TEST_P(DBAtomicFlushTest, BgThreadNoWaitAfterManifestError) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::ProcessManifestWrites:AfterSyncManifest", [&](void* arg) {
|
||||
auto* ptr = reinterpret_cast<IOStatus*>(arg);
|
||||
auto* ptr = static_cast<IOStatus*>(arg);
|
||||
assert(ptr);
|
||||
*ptr = IOStatus::IOError("Injected failure");
|
||||
});
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
extern void MarkKeyMayExist(void* arg);
|
||||
extern bool SaveValue(void* arg, const ParsedInternalKey& parsed_key,
|
||||
const Slice& v, bool hit_and_return);
|
||||
|
||||
CompactedDBImpl::CompactedDBImpl(const DBOptions& options,
|
||||
const std::string& dbname)
|
||||
: DBImpl(options, dbname, /*seq_per_batch*/ false, +/*batch_per_txn*/ true,
|
||||
@@ -37,12 +33,6 @@ size_t CompactedDBImpl::FindFile(const Slice& key) {
|
||||
files_.files);
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
const Slice& key, PinnableSlice* value) {
|
||||
return Get(options, /*column_family*/ nullptr, key, value,
|
||||
/*timestamp*/ nullptr);
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle*, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) {
|
||||
@@ -112,62 +102,59 @@ Status CompactedDBImpl::Get(const ReadOptions& _read_options,
|
||||
return Status::NotFound();
|
||||
}
|
||||
|
||||
std::vector<Status> CompactedDBImpl::MultiGet(
|
||||
const ReadOptions& options, const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values) {
|
||||
return MultiGet(options, keys, values, /*timestamps*/ nullptr);
|
||||
}
|
||||
|
||||
std::vector<Status> CompactedDBImpl::MultiGet(
|
||||
const ReadOptions& _read_options, const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) {
|
||||
void CompactedDBImpl::MultiGet(const ReadOptions& _read_options,
|
||||
size_t num_keys,
|
||||
ColumnFamilyHandle** /*column_families*/,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool /*sorted_input*/) {
|
||||
assert(user_comparator_);
|
||||
size_t num_keys = keys.size();
|
||||
Status s;
|
||||
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
|
||||
_read_options.io_activity != Env::IOActivity::kMultiGet) {
|
||||
Status s = Status::InvalidArgument(
|
||||
s = Status::InvalidArgument(
|
||||
"Can only call MultiGet with `ReadOptions::io_activity` is "
|
||||
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kMultiGet`");
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
|
||||
ReadOptions read_options(_read_options);
|
||||
if (read_options.io_activity == Env::IOActivity::kUnknown) {
|
||||
read_options.io_activity = Env::IOActivity::kMultiGet;
|
||||
if (s.ok()) {
|
||||
if (read_options.io_activity == Env::IOActivity::kUnknown) {
|
||||
read_options.io_activity = Env::IOActivity::kMultiGet;
|
||||
}
|
||||
|
||||
if (read_options.timestamp) {
|
||||
s = FailIfTsMismatchCf(DefaultColumnFamily(), *(read_options.timestamp));
|
||||
if (s.ok()) {
|
||||
if (read_options.timestamp->size() > 0) {
|
||||
s = FailIfReadCollapsedHistory(cfd_, cfd_->GetSuperVersion(),
|
||||
*(read_options.timestamp));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = FailIfCfHasTs(DefaultColumnFamily());
|
||||
}
|
||||
}
|
||||
|
||||
if (read_options.timestamp) {
|
||||
Status s =
|
||||
FailIfTsMismatchCf(DefaultColumnFamily(), *(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
if (read_options.timestamp->size() > 0) {
|
||||
s = FailIfReadCollapsedHistory(cfd_, cfd_->GetSuperVersion(),
|
||||
*(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
return std::vector<Status>(num_keys, s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Status s = FailIfCfHasTs(DefaultColumnFamily());
|
||||
if (!s.ok()) {
|
||||
return std::vector<Status>(num_keys, s);
|
||||
if (!s.ok()) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = s;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
if (timestamps) {
|
||||
for (auto& ts : *timestamps) {
|
||||
ts.clear();
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
timestamps[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
GetWithTimestampReadCallback read_cb(kMaxSequenceNumber);
|
||||
autovector<TableReader*, 16> reader_list;
|
||||
for (const auto& key : keys) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
const Slice& key = keys[i];
|
||||
LookupKey lkey(key, kMaxSequenceNumber, read_options.timestamp);
|
||||
const FdWithKeyRange& f = files_.files[FindFile(lkey.user_key())];
|
||||
if (user_comparator_->CompareWithoutTimestamp(
|
||||
@@ -181,30 +168,26 @@ std::vector<Status> CompactedDBImpl::MultiGet(
|
||||
reader_list.push_back(f.fd.table_reader);
|
||||
}
|
||||
}
|
||||
std::vector<Status> statuses(num_keys, Status::NotFound());
|
||||
values->resize(num_keys);
|
||||
if (timestamps) {
|
||||
timestamps->resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = Status::NotFound();
|
||||
}
|
||||
int idx = 0;
|
||||
for (auto* r : reader_list) {
|
||||
if (r != nullptr) {
|
||||
PinnableSlice pinnable_val;
|
||||
std::string& value = (*values)[idx];
|
||||
PinnableSlice& pinnable_val = values[idx];
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber, read_options.timestamp);
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[idx] : nullptr;
|
||||
std::string* timestamp = timestamps ? ×tamps[idx] : nullptr;
|
||||
GetContext get_context(
|
||||
user_comparator_, nullptr, nullptr, nullptr, GetContext::kNotFound,
|
||||
lkey.user_key(), &pinnable_val, /*columns=*/nullptr,
|
||||
user_comparator_->timestamp_size() > 0 ? timestamp : nullptr, nullptr,
|
||||
nullptr, true, nullptr, nullptr, nullptr, nullptr, &read_cb);
|
||||
Status s =
|
||||
Status status =
|
||||
r->Get(read_options, lkey.internal_key(), &get_context, nullptr);
|
||||
assert(static_cast<size_t>(idx) < statuses.size());
|
||||
if (!s.ok() && !s.IsNotFound()) {
|
||||
statuses[idx] = s;
|
||||
assert(static_cast<size_t>(idx) < num_keys);
|
||||
if (!status.ok() && !status.IsNotFound()) {
|
||||
statuses[idx] = status;
|
||||
} else {
|
||||
value.assign(pinnable_val.data(), pinnable_val.size());
|
||||
if (get_context.State() == GetContext::kFound) {
|
||||
statuses[idx] = Status::OK();
|
||||
}
|
||||
@@ -212,7 +195,6 @@ std::vector<Status> CompactedDBImpl::MultiGet(
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return statuses;
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Init(const Options& options) {
|
||||
|
||||
@@ -26,33 +26,23 @@ class CompactedDBImpl : public DBImpl {
|
||||
|
||||
// Implementations of the DB interface
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) override;
|
||||
|
||||
Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) override;
|
||||
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp) override;
|
||||
|
||||
using DB::MultiGet;
|
||||
// Note that CompactedDBImpl::MultiGet is not the optimized version of
|
||||
// MultiGet to use.
|
||||
// TODO: optimize CompactedDBImpl::MultiGet, see DBImpl::MultiGet for details.
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options, const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override;
|
||||
|
||||
std::vector<Status> MultiGet(const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>&,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) override;
|
||||
void MultiGet(const ReadOptions& options, size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses, const bool sorted_input) override;
|
||||
|
||||
using DBImpl::Put;
|
||||
virtual Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
@@ -65,54 +55,53 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DBImpl::Merge;
|
||||
virtual Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Delete;
|
||||
virtual Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DBImpl::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/,
|
||||
const Slice* /*end*/) override {
|
||||
Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/, const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
virtual Status DisableFileDeletions() override {
|
||||
Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
Status EnableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
return DBImpl::GetLiveFiles(ret, manifest_file_size,
|
||||
false /* flush_memtable */);
|
||||
}
|
||||
using DBImpl::Flush;
|
||||
virtual Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
virtual Status SyncWAL() override {
|
||||
Status SyncWAL() override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
Status IngestExternalFile(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
@@ -120,7 +109,7 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
@@ -130,9 +119,9 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
|
||||
+90
-310
@@ -309,7 +309,7 @@ Status DBImpl::Resume() {
|
||||
|
||||
if (error_handler_.IsRecoveryInProgress()) {
|
||||
// Don't allow a mix of manual and automatic recovery
|
||||
return Status::Busy();
|
||||
return Status::Busy("Recovery in progress");
|
||||
}
|
||||
|
||||
mutex_.Unlock();
|
||||
@@ -1907,7 +1907,7 @@ struct SuperVersionHandle {
|
||||
};
|
||||
|
||||
static void CleanupSuperVersionHandle(void* arg1, void* /*arg2*/) {
|
||||
SuperVersionHandle* sv_handle = reinterpret_cast<SuperVersionHandle*>(arg1);
|
||||
SuperVersionHandle* sv_handle = static_cast<SuperVersionHandle*>(arg1);
|
||||
|
||||
if (sv_handle->super_version->Unref()) {
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
@@ -2020,12 +2020,6 @@ ColumnFamilyHandle* DBImpl::PersistentStatsColumnFamily() const {
|
||||
return persist_stats_cf_handle_;
|
||||
}
|
||||
|
||||
Status DBImpl::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) {
|
||||
return Get(read_options, column_family, key, value, /*timestamp=*/nullptr);
|
||||
}
|
||||
|
||||
Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) {
|
||||
@@ -2269,7 +2263,7 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
snapshot = get_impl_options.callback->max_visible_seq();
|
||||
} else {
|
||||
snapshot =
|
||||
reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)->number_;
|
||||
static_cast<const SnapshotImpl*>(read_options.snapshot)->number_;
|
||||
}
|
||||
} else {
|
||||
// Note that the snapshot is assigned AFTER referencing the super
|
||||
@@ -2497,250 +2491,6 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<Status> DBImpl::MultiGet(
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values) {
|
||||
return MultiGet(read_options, column_family, keys, values,
|
||||
/*timestamps=*/nullptr);
|
||||
}
|
||||
|
||||
std::vector<Status> DBImpl::MultiGet(
|
||||
const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) {
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_MULTIGET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
size_t num_keys = keys.size();
|
||||
assert(column_family.size() == num_keys);
|
||||
std::vector<Status> stat_list(num_keys);
|
||||
|
||||
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
|
||||
_read_options.io_activity != Env::IOActivity::kMultiGet) {
|
||||
Status s = Status::InvalidArgument(
|
||||
"Can only call MultiGet with `ReadOptions::io_activity` is "
|
||||
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kMultiGet`");
|
||||
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
stat_list[i] = s;
|
||||
}
|
||||
return stat_list;
|
||||
}
|
||||
|
||||
ReadOptions read_options(_read_options);
|
||||
if (read_options.io_activity == Env::IOActivity::kUnknown) {
|
||||
read_options.io_activity = Env::IOActivity::kMultiGet;
|
||||
}
|
||||
|
||||
bool should_fail = false;
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
assert(column_family[i]);
|
||||
if (read_options.timestamp) {
|
||||
stat_list[i] =
|
||||
FailIfTsMismatchCf(column_family[i], *(read_options.timestamp));
|
||||
if (!stat_list[i].ok()) {
|
||||
should_fail = true;
|
||||
}
|
||||
} else {
|
||||
stat_list[i] = FailIfCfHasTs(column_family[i]);
|
||||
if (!stat_list[i].ok()) {
|
||||
should_fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (should_fail) {
|
||||
for (auto& s : stat_list) {
|
||||
if (s.ok()) {
|
||||
s = Status::Incomplete(
|
||||
"DB not queried due to invalid argument(s) in the same MultiGet");
|
||||
}
|
||||
}
|
||||
return stat_list;
|
||||
}
|
||||
|
||||
if (tracer_) {
|
||||
// TODO: This mutex should be removed later, to improve performance when
|
||||
// tracing is enabled.
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->MultiGet(column_family, keys).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
|
||||
UnorderedMap<uint32_t, MultiGetColumnFamilyData> multiget_cf_data(
|
||||
column_family.size());
|
||||
for (auto cf : column_family) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(cf);
|
||||
auto cfd = cfh->cfd();
|
||||
if (multiget_cf_data.find(cfd->GetID()) == multiget_cf_data.end()) {
|
||||
multiget_cf_data.emplace(cfd->GetID(),
|
||||
MultiGetColumnFamilyData(cfh, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
std::function<MultiGetColumnFamilyData*(
|
||||
UnorderedMap<uint32_t, MultiGetColumnFamilyData>::iterator&)>
|
||||
iter_deref_lambda =
|
||||
[](UnorderedMap<uint32_t, MultiGetColumnFamilyData>::iterator&
|
||||
cf_iter) { return &cf_iter->second; };
|
||||
|
||||
SequenceNumber consistent_seqnum;
|
||||
bool sv_from_thread_local;
|
||||
Status status =
|
||||
MultiCFSnapshot<UnorderedMap<uint32_t, MultiGetColumnFamilyData>>(
|
||||
read_options, nullptr, iter_deref_lambda, &multiget_cf_data,
|
||||
&consistent_seqnum, &sv_from_thread_local);
|
||||
|
||||
if (!status.ok()) {
|
||||
for (auto& s : stat_list) {
|
||||
if (s.ok()) {
|
||||
s = status;
|
||||
}
|
||||
}
|
||||
return stat_list;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::MultiGet:AfterGetSeqNum1");
|
||||
TEST_SYNC_POINT("DBImpl::MultiGet:AfterGetSeqNum2");
|
||||
|
||||
// Contain a list of merge operations if merge occurs.
|
||||
MergeContext merge_context;
|
||||
|
||||
// Note: this always resizes the values array
|
||||
values->resize(num_keys);
|
||||
if (timestamps) {
|
||||
timestamps->resize(num_keys);
|
||||
}
|
||||
|
||||
// Keep track of bytes that we read for statistics-recording later
|
||||
uint64_t bytes_read = 0;
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
|
||||
// For each of the given keys, apply the entire "get" process as follows:
|
||||
// First look in the memtable, then in the immutable memtable (if any).
|
||||
// s is both in/out. When in, s could either be OK or MergeInProgress.
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t num_found = 0;
|
||||
size_t keys_read;
|
||||
uint64_t curr_value_size = 0;
|
||||
|
||||
GetWithTimestampReadCallback timestamp_read_callback(0);
|
||||
ReadCallback* read_callback = nullptr;
|
||||
if (read_options.timestamp && read_options.timestamp->size() > 0) {
|
||||
timestamp_read_callback.Refresh(consistent_seqnum);
|
||||
read_callback = ×tamp_read_callback;
|
||||
}
|
||||
|
||||
for (keys_read = 0; keys_read < num_keys; ++keys_read) {
|
||||
merge_context.Clear();
|
||||
Status& s = stat_list[keys_read];
|
||||
std::string* value = &(*values)[keys_read];
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[keys_read] : nullptr;
|
||||
|
||||
LookupKey lkey(keys[keys_read], consistent_seqnum, read_options.timestamp);
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(
|
||||
column_family[keys_read]);
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
auto mgd_iter = multiget_cf_data.find(cfh->cfd()->GetID());
|
||||
assert(mgd_iter != multiget_cf_data.end());
|
||||
auto mgd = mgd_iter->second;
|
||||
auto super_version = mgd.super_version;
|
||||
bool skip_memtable =
|
||||
(read_options.read_tier == kPersistedTier &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed));
|
||||
bool done = false;
|
||||
if (!skip_memtable) {
|
||||
if (super_version->mem->Get(
|
||||
lkey, value, /*columns=*/nullptr, timestamp, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options,
|
||||
false /* immutable_memtable */, read_callback)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if (super_version->imm->Get(lkey, value, /*columns=*/nullptr,
|
||||
timestamp, &s, &merge_context,
|
||||
&max_covering_tombstone_seq,
|
||||
read_options, read_callback)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
PinnableSlice pinnable_val;
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
super_version->current->Get(read_options, lkey, &pinnable_val,
|
||||
/*columns=*/nullptr, timestamp, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
&pinned_iters_mgr, /*value_found=*/nullptr,
|
||||
/*key_exists=*/nullptr,
|
||||
/*seq=*/nullptr, read_callback);
|
||||
value->assign(pinnable_val.data(), pinnable_val.size());
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
const auto& merge_threshold = read_options.merge_operand_count_threshold;
|
||||
if (merge_threshold.has_value() &&
|
||||
merge_context.GetNumOperands() > merge_threshold.value()) {
|
||||
s = Status::OkMergeOperandThresholdExceeded();
|
||||
}
|
||||
|
||||
bytes_read += value->size();
|
||||
num_found++;
|
||||
|
||||
curr_value_size += value->size();
|
||||
if (curr_value_size > read_options.value_size_soft_limit) {
|
||||
while (++keys_read < num_keys) {
|
||||
stat_list[keys_read] = Status::Aborted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (read_options.deadline.count() &&
|
||||
immutable_db_options_.clock->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keys_read < num_keys) {
|
||||
// The only reason to break out of the loop is when the deadline is
|
||||
// exceeded
|
||||
assert(immutable_db_options_.clock->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count()));
|
||||
for (++keys_read; keys_read < num_keys; ++keys_read) {
|
||||
stat_list[keys_read] = Status::TimedOut();
|
||||
}
|
||||
}
|
||||
|
||||
// Post processing (decrement reference counts and record statistics)
|
||||
PERF_TIMER_GUARD(get_post_process_time);
|
||||
|
||||
for (auto mgd_iter : multiget_cf_data) {
|
||||
auto mgd = mgd_iter.second;
|
||||
if (sv_from_thread_local) {
|
||||
ReturnAndCleanupSuperVersion(mgd.cfd, mgd.super_version);
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::MultiGet::BeforeLastTryUnRefSV");
|
||||
CleanupSuperVersion(mgd.super_version);
|
||||
}
|
||||
}
|
||||
RecordTick(stats_, NUMBER_MULTIGET_CALLS);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_KEYS_READ, num_keys);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_KEYS_FOUND, num_found);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_BYTES_READ, bytes_read);
|
||||
RecordInHistogram(stats_, BYTES_PER_MULTIGET, bytes_read);
|
||||
PERF_COUNTER_ADD(multiget_read_bytes, bytes_read);
|
||||
PERF_TIMER_STOP(get_post_process_time);
|
||||
|
||||
return stat_list;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Status DBImpl::MultiCFSnapshot(
|
||||
const ReadOptions& read_options, ReadCallback* callback,
|
||||
@@ -2889,7 +2639,8 @@ Status DBImpl::MultiCFSnapshot(
|
||||
}
|
||||
}
|
||||
|
||||
// Keep track of bytes that we read for statistics-recording later
|
||||
TEST_SYNC_POINT("DBImpl::MultiCFSnapshot:AfterGetSeqNum1");
|
||||
TEST_SYNC_POINT("DBImpl::MultiCFSnapshot:AfterGetSeqNum2");
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
*sv_from_thread_local = !last_try;
|
||||
if (!s.ok()) {
|
||||
@@ -2898,14 +2649,6 @@ Status DBImpl::MultiCFSnapshot(
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
MultiGet(read_options, num_keys, column_families, keys, values,
|
||||
/* timestamps */ nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& _read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
@@ -3122,38 +2865,23 @@ void DBImpl::PrepareMultiGetKeys(
|
||||
CompareKeyContext());
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const size_t num_keys,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
Status* statuses, const bool sorted_input) {
|
||||
MultiGet(read_options, column_family, num_keys, keys, values,
|
||||
/* timestamps */ nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const size_t num_keys,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
|
||||
_read_options.io_activity != Env::IOActivity::kMultiGet) {
|
||||
Status s = Status::InvalidArgument(
|
||||
"Can only call MultiGet with `ReadOptions::io_activity` is "
|
||||
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kMultiGet`");
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
if (statuses[i].ok()) {
|
||||
statuses[i] = s;
|
||||
}
|
||||
}
|
||||
return;
|
||||
void DB::MultiGet(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses, const bool sorted_input) {
|
||||
// Use std::array, if possible, to avoid memory allocation overhead
|
||||
if (num_keys > MultiGetContext::MAX_BATCH_SIZE) {
|
||||
std::vector<ColumnFamilyHandle*> column_families(num_keys, column_family);
|
||||
MultiGet(options, num_keys, column_families.data(), keys, values,
|
||||
timestamps, statuses, sorted_input);
|
||||
} else {
|
||||
std::array<ColumnFamilyHandle*, MultiGetContext::MAX_BATCH_SIZE>
|
||||
column_families;
|
||||
std::fill(column_families.begin(), column_families.begin() + num_keys,
|
||||
column_family);
|
||||
MultiGet(options, num_keys, column_families.data(), keys, values,
|
||||
timestamps, statuses, sorted_input);
|
||||
}
|
||||
|
||||
ReadOptions read_options(_read_options);
|
||||
if (read_options.io_activity == Env::IOActivity::kUnknown) {
|
||||
read_options.io_activity = Env::IOActivity::kMultiGet;
|
||||
}
|
||||
MultiGetCommon(read_options, column_family, num_keys, keys, values,
|
||||
/* columns */ nullptr, timestamps, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGetCommon(const ReadOptions& read_options,
|
||||
@@ -4285,7 +4013,7 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
// inplace_update_support enabled.
|
||||
return;
|
||||
}
|
||||
const SnapshotImpl* casted_s = reinterpret_cast<const SnapshotImpl*>(s);
|
||||
const SnapshotImpl* casted_s = static_cast<const SnapshotImpl*>(s);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
snapshots_.Delete(casted_s);
|
||||
@@ -4370,7 +4098,25 @@ Status DBImpl::GetPropertiesOfTablesInRange(ColumnFamilyHandle* column_family,
|
||||
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
auto s = version->GetPropertiesOfTablesInRange(read_options, range, n, props);
|
||||
const Comparator* const ucmp = cfd->user_comparator();
|
||||
assert(ucmp);
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
|
||||
autovector<UserKeyRange> ukey_ranges;
|
||||
std::vector<std::string> keys;
|
||||
ukey_ranges.reserve(n);
|
||||
keys.reserve(2 * n);
|
||||
// Add timestamp if needed
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
auto [start, limit] = MaybeAddTimestampsToRange(
|
||||
&range[i].start, &range[i].limit, ts_sz, &keys.emplace_back(),
|
||||
&keys.emplace_back(), /*exclusive_end=*/false);
|
||||
assert(start.has_value());
|
||||
assert(limit.has_value());
|
||||
ukey_ranges.emplace_back(start.value(), limit.value());
|
||||
}
|
||||
auto s =
|
||||
version->GetPropertiesOfTablesInRange(read_options, ukey_ranges, props);
|
||||
|
||||
// Decrement the ref count
|
||||
mutex_.Lock();
|
||||
@@ -4887,6 +4633,24 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
Status status = Status::OK();
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
const Comparator* ucmp = cfd->user_comparator();
|
||||
assert(ucmp);
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
autovector<UserKeyRangePtr> ukey_ranges;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<Slice> key_slices;
|
||||
ukey_ranges.reserve(n);
|
||||
keys.reserve(2 * n);
|
||||
key_slices.reserve(2 * n);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
auto [start, limit] = MaybeAddTimestampsToRange(
|
||||
ranges[i].start, ranges[i].limit, ts_sz, &keys.emplace_back(),
|
||||
&keys.emplace_back(), !include_end);
|
||||
assert((ranges[i].start != nullptr) == start.has_value());
|
||||
assert((ranges[i].limit != nullptr) == limit.has_value());
|
||||
ukey_ranges.emplace_back(start, limit);
|
||||
}
|
||||
|
||||
VersionEdit edit;
|
||||
std::set<FileMetaData*> deleted_files;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
@@ -4895,8 +4659,9 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
Version* input_version = cfd->current();
|
||||
|
||||
auto* vstorage = input_version->storage_info();
|
||||
for (size_t r = 0; r < n; r++) {
|
||||
auto begin = ranges[r].start, end = ranges[r].limit;
|
||||
for (const auto& range : ukey_ranges) {
|
||||
auto begin = range.start.has_value() ? &range.start.value() : nullptr;
|
||||
auto end = range.limit.has_value() ? &range.limit.value() : nullptr;
|
||||
for (int i = 1; i < cfd->NumberLevels(); i++) {
|
||||
if (vstorage->LevelFiles(i).empty() ||
|
||||
!vstorage->OverlapInLevel(i, begin, end)) {
|
||||
@@ -4930,8 +4695,8 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
continue;
|
||||
}
|
||||
if (!include_end && end != nullptr &&
|
||||
cfd->user_comparator()->Compare(level_file->largest.user_key(),
|
||||
*end) == 0) {
|
||||
(ucmp->CompareWithoutTimestamp(level_file->largest.user_key(),
|
||||
*end) == 0)) {
|
||||
continue;
|
||||
}
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
@@ -5514,16 +5279,23 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
versions_->options_file_number_ = options_file_number;
|
||||
versions_->options_file_size_ = options_file_size;
|
||||
int my_disable_delete_obsolete_files;
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
versions_->options_file_number_ = options_file_number;
|
||||
versions_->options_file_size_ = options_file_size;
|
||||
my_disable_delete_obsolete_files = disable_delete_obsolete_files_;
|
||||
}
|
||||
|
||||
if (!my_disable_delete_obsolete_files) {
|
||||
// TODO: Should we check for errors here?
|
||||
DeleteObsoleteOptionsFiles().PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == disable_delete_obsolete_files_) {
|
||||
// TODO: Should we check for errors here?
|
||||
DeleteObsoleteOptionsFiles().PermitUncheckedError();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -5780,10 +5552,18 @@ Status DBImpl::IngestExternalFiles(
|
||||
}
|
||||
for (const auto& arg : args) {
|
||||
const IngestExternalFileOptions& ingest_opts = arg.options;
|
||||
if (ingest_opts.ingest_behind &&
|
||||
!immutable_db_options_.allow_ingest_behind) {
|
||||
return Status::InvalidArgument(
|
||||
"can't ingest_behind file in DB with allow_ingest_behind=false");
|
||||
if (ingest_opts.ingest_behind) {
|
||||
if (!immutable_db_options_.allow_ingest_behind) {
|
||||
return Status::InvalidArgument(
|
||||
"can't ingest_behind file in DB with allow_ingest_behind=false");
|
||||
}
|
||||
auto ucmp = arg.column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
return Status::NotSupported(
|
||||
"Column family with user-defined "
|
||||
"timestamps enabled doesn't support ingest behind.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+113
-153
@@ -229,16 +229,12 @@ class DBImpl : public DB {
|
||||
const Slice& end_key, const Slice& ts) override;
|
||||
|
||||
using DB::Write;
|
||||
virtual Status Write(const WriteOptions& options,
|
||||
WriteBatch* updates) override;
|
||||
Status Write(const WriteOptions& options, WriteBatch* updates) override;
|
||||
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) override;
|
||||
virtual Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) override;
|
||||
Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) override;
|
||||
|
||||
using DB::GetEntity;
|
||||
Status GetEntity(const ReadOptions& options,
|
||||
@@ -263,17 +259,6 @@ class DBImpl : public DB {
|
||||
}
|
||||
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) override;
|
||||
|
||||
// This MultiGet is a batched version, which may be faster than calling Get
|
||||
// multiple times, especially if the keys have some spatial locality that
|
||||
// enables them to be queried in the same SST files/set of files. The larger
|
||||
@@ -281,19 +266,6 @@ class DBImpl : public DB {
|
||||
// The values and statuses parameters are arrays with number of elements
|
||||
// equal to keys.size(). This allows the storage for those to be alloacted
|
||||
// by the caller on the stack for small batches
|
||||
void MultiGet(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys, PinnableSlice* values,
|
||||
Status* statuses, const bool sorted_input = false) override;
|
||||
void MultiGet(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const size_t num_keys,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
|
||||
void MultiGet(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
void MultiGet(const ReadOptions& _read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
@@ -319,9 +291,9 @@ class DBImpl : public DB {
|
||||
const Slice* keys,
|
||||
PinnableAttributeGroups* results) override;
|
||||
|
||||
virtual Status CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle) override {
|
||||
Status CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle) override {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
return CreateColumnFamily(ReadOptions(), WriteOptions(), cf_options,
|
||||
column_family, handle);
|
||||
@@ -331,7 +303,7 @@ class DBImpl : public DB {
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle);
|
||||
virtual Status CreateColumnFamilies(
|
||||
Status CreateColumnFamilies(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::vector<std::string>& column_family_names,
|
||||
std::vector<ColumnFamilyHandle*>* handles) override {
|
||||
@@ -345,7 +317,7 @@ class DBImpl : public DB {
|
||||
const std::vector<std::string>& column_family_names,
|
||||
std::vector<ColumnFamilyHandle*>* handles);
|
||||
|
||||
virtual Status CreateColumnFamilies(
|
||||
Status CreateColumnFamilies(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles) override {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
@@ -356,8 +328,8 @@ class DBImpl : public DB {
|
||||
const ReadOptions& read_options, const WriteOptions& write_options,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles);
|
||||
virtual Status DropColumnFamily(ColumnFamilyHandle* column_family) override;
|
||||
virtual Status DropColumnFamilies(
|
||||
Status DropColumnFamily(ColumnFamilyHandle* column_family) override;
|
||||
Status DropColumnFamilies(
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
|
||||
// Returns false if key doesn't exist in the database and true if it may.
|
||||
@@ -365,21 +337,20 @@ class DBImpl : public DB {
|
||||
// memory. On return, if value was found, then value_found will be set to true
|
||||
// , otherwise false.
|
||||
using DB::KeyMayExist;
|
||||
virtual bool KeyMayExist(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value, std::string* timestamp,
|
||||
bool* value_found = nullptr) override;
|
||||
bool KeyMayExist(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value, std::string* timestamp,
|
||||
bool* value_found = nullptr) override;
|
||||
|
||||
using DB::NewIterator;
|
||||
virtual Iterator* NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status NewIterators(
|
||||
const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
Iterator* NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
Status NewIterators(const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
|
||||
virtual const Snapshot* GetSnapshot() override;
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
const Snapshot* GetSnapshot() override;
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
// Create a timestamped snapshot. This snapshot can be shared by multiple
|
||||
// readers. If any of them uses it for write conflict checking, then
|
||||
// is_write_conflict_boundary is true. For simplicity, set it to true by
|
||||
@@ -394,35 +365,33 @@ class DBImpl : public DB {
|
||||
timestamped_snapshots) const;
|
||||
|
||||
using DB::GetProperty;
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) override;
|
||||
bool GetProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::string* value) override;
|
||||
using DB::GetMapProperty;
|
||||
virtual bool GetMapProperty(
|
||||
ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override;
|
||||
bool GetMapProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override;
|
||||
using DB::GetIntProperty;
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, uint64_t* value) override;
|
||||
bool GetIntProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
uint64_t* value) override;
|
||||
using DB::GetAggregatedIntProperty;
|
||||
virtual bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* aggregated_value) override;
|
||||
bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* aggregated_value) override;
|
||||
using DB::GetApproximateSizes;
|
||||
virtual Status GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes) override;
|
||||
Status GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes) override;
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override;
|
||||
void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range, uint64_t* const count,
|
||||
uint64_t* const size) override;
|
||||
using DB::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) override;
|
||||
Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice* begin,
|
||||
const Slice* end) override;
|
||||
|
||||
using DB::CompactFiles;
|
||||
virtual Status CompactFiles(
|
||||
Status CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names, const int output_level,
|
||||
@@ -430,55 +399,54 @@ class DBImpl : public DB {
|
||||
std::vector<std::string>* const output_file_names = nullptr,
|
||||
CompactionJobInfo* compaction_job_info = nullptr) override;
|
||||
|
||||
virtual Status PauseBackgroundWork() override;
|
||||
virtual Status ContinueBackgroundWork() override;
|
||||
Status PauseBackgroundWork() override;
|
||||
Status ContinueBackgroundWork() override;
|
||||
|
||||
virtual Status EnableAutoCompaction(
|
||||
Status EnableAutoCompaction(
|
||||
const std::vector<ColumnFamilyHandle*>& column_family_handles) override;
|
||||
|
||||
virtual void EnableManualCompaction() override;
|
||||
virtual void DisableManualCompaction() override;
|
||||
void EnableManualCompaction() override;
|
||||
void DisableManualCompaction() override;
|
||||
|
||||
using DB::SetOptions;
|
||||
Status SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
virtual Status SetDBOptions(
|
||||
Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
using DB::NumberLevels;
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
using DB::MaxMemCompactionLevel;
|
||||
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) override;
|
||||
int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) override;
|
||||
using DB::Level0StopWriteTrigger;
|
||||
virtual int Level0StopWriteTrigger(
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Env* GetEnv() const override;
|
||||
virtual FileSystem* GetFileSystem() const override;
|
||||
int Level0StopWriteTrigger(ColumnFamilyHandle* column_family) override;
|
||||
const std::string& GetName() const override;
|
||||
Env* GetEnv() const override;
|
||||
FileSystem* GetFileSystem() const override;
|
||||
using DB::GetOptions;
|
||||
virtual Options GetOptions(ColumnFamilyHandle* column_family) const override;
|
||||
Options GetOptions(ColumnFamilyHandle* column_family) const override;
|
||||
using DB::GetDBOptions;
|
||||
virtual DBOptions GetDBOptions() const override;
|
||||
DBOptions GetDBOptions() const override;
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status Flush(
|
||||
Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
Status Flush(
|
||||
const FlushOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
virtual Status FlushWAL(bool sync) override {
|
||||
Status FlushWAL(bool sync) override {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
return FlushWAL(WriteOptions(), sync);
|
||||
}
|
||||
|
||||
virtual Status FlushWAL(const WriteOptions& write_options, bool sync);
|
||||
bool WALBufferIsEmpty();
|
||||
virtual Status SyncWAL() override;
|
||||
virtual Status LockWAL() override;
|
||||
virtual Status UnlockWAL() override;
|
||||
Status SyncWAL() override;
|
||||
Status LockWAL() override;
|
||||
Status UnlockWAL() override;
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
SequenceNumber GetLatestSequenceNumber() const override;
|
||||
|
||||
// IncreaseFullHistoryTsLow(ColumnFamilyHandle*, std::string) will acquire
|
||||
// and release db_mutex
|
||||
@@ -490,21 +458,21 @@ class DBImpl : public DB {
|
||||
Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string* ts_low) override;
|
||||
|
||||
virtual Status GetDbIdentity(std::string& identity) const override;
|
||||
Status GetDbIdentity(std::string& identity) const override;
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
|
||||
|
||||
virtual Status GetDbSessionId(std::string& session_id) const override;
|
||||
Status GetDbSessionId(std::string& session_id) const override;
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override;
|
||||
|
||||
ColumnFamilyHandle* PersistentStatsColumnFamily() const;
|
||||
|
||||
virtual Status Close() override;
|
||||
Status Close() override;
|
||||
|
||||
virtual Status DisableFileDeletions() override;
|
||||
Status DisableFileDeletions() override;
|
||||
|
||||
virtual Status EnableFileDeletions(bool force) override;
|
||||
Status EnableFileDeletions() override;
|
||||
|
||||
virtual bool IsFileDeletionsEnabled() const;
|
||||
|
||||
@@ -513,40 +481,35 @@ class DBImpl : public DB {
|
||||
std::unique_ptr<StatsHistoryIterator>* stats_iterator) override;
|
||||
|
||||
using DB::ResetStats;
|
||||
virtual Status ResetStats() override;
|
||||
Status ResetStats() override;
|
||||
// All the returned filenames start with "/"
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true) override;
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& files) override;
|
||||
virtual Status GetCurrentWalFile(
|
||||
std::unique_ptr<LogFile>* current_log_file) override;
|
||||
virtual Status GetCreationTimeOfOldestFile(
|
||||
uint64_t* creation_time) override;
|
||||
Status GetLiveFiles(std::vector<std::string>&, uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true) override;
|
||||
Status GetSortedWalFiles(VectorLogPtr& files) override;
|
||||
Status GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) override;
|
||||
Status GetCreationTimeOfOldestFile(uint64_t* creation_time) override;
|
||||
|
||||
virtual Status GetUpdatesSince(
|
||||
Status GetUpdatesSince(
|
||||
SequenceNumber seq_number, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options =
|
||||
TransactionLogIterator::ReadOptions()) override;
|
||||
virtual Status DeleteFile(std::string name) override;
|
||||
Status DeleteFile(std::string name) override;
|
||||
Status DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end = true);
|
||||
|
||||
virtual void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData>* metadata) override;
|
||||
void GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) override;
|
||||
|
||||
virtual Status GetLiveFilesChecksumInfo(
|
||||
FileChecksumList* checksum_list) override;
|
||||
Status GetLiveFilesChecksumInfo(FileChecksumList* checksum_list) override;
|
||||
|
||||
virtual Status GetLiveFilesStorageInfo(
|
||||
Status GetLiveFilesStorageInfo(
|
||||
const LiveFilesStorageInfoOptions& opts,
|
||||
std::vector<LiveFileStorageInfo>* files) override;
|
||||
|
||||
// Obtains the meta data of the specified column family of the DB.
|
||||
// TODO(yhchiang): output parameter is placed in the end in this codebase.
|
||||
virtual void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
|
||||
void GetAllColumnFamilyMetaData(
|
||||
std::vector<ColumnFamilyMetaData>* metadata) override;
|
||||
@@ -558,32 +521,32 @@ class DBImpl : public DB {
|
||||
int target_level) override;
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override;
|
||||
|
||||
using DB::IngestExternalFiles;
|
||||
virtual Status IngestExternalFiles(
|
||||
Status IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& args) override;
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const std::vector<const ExportImportFilesMetaData*>& metadatas,
|
||||
ColumnFamilyHandle** handle) override;
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key,
|
||||
const Slice& end_key) override;
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key,
|
||||
const Slice& end_key) override;
|
||||
|
||||
using DB::VerifyFileChecksums;
|
||||
Status VerifyFileChecksums(const ReadOptions& read_options) override;
|
||||
|
||||
using DB::VerifyChecksum;
|
||||
virtual Status VerifyChecksum(const ReadOptions& /*read_options*/) override;
|
||||
Status VerifyChecksum(const ReadOptions& /*read_options*/) override;
|
||||
// Verify the checksums of files in db. Currently only tables are checked.
|
||||
//
|
||||
// read_options: controls file I/O behavior, e.g. read ahead size while
|
||||
@@ -604,18 +567,16 @@ class DBImpl : public DB {
|
||||
const ReadOptions& read_options);
|
||||
|
||||
using DB::StartTrace;
|
||||
virtual Status StartTrace(
|
||||
const TraceOptions& options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) override;
|
||||
Status StartTrace(const TraceOptions& options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) override;
|
||||
|
||||
using DB::EndTrace;
|
||||
virtual Status EndTrace() override;
|
||||
Status EndTrace() override;
|
||||
|
||||
using DB::NewDefaultReplayer;
|
||||
virtual Status NewDefaultReplayer(
|
||||
const std::vector<ColumnFamilyHandle*>& handles,
|
||||
std::unique_ptr<TraceReader>&& reader,
|
||||
std::unique_ptr<Replayer>* replayer) override;
|
||||
Status NewDefaultReplayer(const std::vector<ColumnFamilyHandle*>& handles,
|
||||
std::unique_ptr<TraceReader>&& reader,
|
||||
std::unique_ptr<Replayer>* replayer) override;
|
||||
|
||||
using DB::StartBlockCacheTrace;
|
||||
Status StartBlockCacheTrace(
|
||||
@@ -637,14 +598,12 @@ class DBImpl : public DB {
|
||||
Status EndIOTrace() override;
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
virtual Status GetPropertiesOfAllTables(
|
||||
ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) override;
|
||||
virtual Status GetPropertiesOfTablesInRange(
|
||||
Status GetPropertiesOfAllTables(ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) override;
|
||||
Status GetPropertiesOfTablesInRange(
|
||||
ColumnFamilyHandle* column_family, const Range* range, std::size_t n,
|
||||
TablePropertiesCollection* props) override;
|
||||
|
||||
|
||||
// ---- End of implementations of the DB interface ----
|
||||
SystemClock* GetSystemClock() const;
|
||||
|
||||
@@ -1181,6 +1140,8 @@ class DBImpl : public DB {
|
||||
|
||||
void TEST_UnlockMutex();
|
||||
|
||||
InstrumentedMutex* TEST_Mutex() { return &mutex_; }
|
||||
|
||||
void TEST_SignalAllBgCv();
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
@@ -2853,17 +2814,16 @@ class GetWithTimestampReadCallback : public ReadCallback {
|
||||
}
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db, const Options& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
Options SanitizeOptions(const std::string& db, const Options& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
|
||||
extern DBOptions SanitizeOptions(const std::string& db, const DBOptions& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
DBOptions SanitizeOptions(const std::string& db, const DBOptions& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
|
||||
extern CompressionType GetCompressionFlush(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
CompressionType GetCompressionFlush(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Return the earliest log file to keep after the memtable flush is
|
||||
// finalized.
|
||||
@@ -2871,13 +2831,13 @@ extern CompressionType GetCompressionFlush(
|
||||
// `memtables_to_flush`) will be flushed and thus will not depend on any WAL
|
||||
// file.
|
||||
// The function is only applicable to 2pc mode.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker);
|
||||
// For atomic flush.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush,
|
||||
@@ -2885,21 +2845,21 @@ extern uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
|
||||
// In non-2PC mode, WALs with log number < the returned number can be
|
||||
// deleted after the cfd_to_flush column family is flushed successfully.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list);
|
||||
// For atomic flush.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists);
|
||||
|
||||
// `cfd_to_flush` is the column family whose memtable will be flushed and thus
|
||||
// will not depend on any WAL file. nullptr means no memtable is being flushed.
|
||||
// The function is only applicable to 2pc mode.
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<MemTable*>& memtables_to_flush);
|
||||
// For atomic flush.
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush);
|
||||
|
||||
|
||||
@@ -1140,7 +1140,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
// TODO(ajkr): We could also optimize away the flush in certain cases where
|
||||
// one/both sides of the interval are unbounded. But it requires more
|
||||
// changes to RangesOverlapWithMemtables.
|
||||
Range range(*begin, *end);
|
||||
UserKeyRange range(*begin, *end);
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
s = cfd->RangesOverlapWithMemtables(
|
||||
{range}, super_version, immutable_db_options_.allow_data_in_errors,
|
||||
@@ -1419,10 +1419,9 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
|
||||
// Perform CompactFiles
|
||||
TEST_SYNC_POINT("TestCompactFiles::IngestExternalFile2");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"TestCompactFiles:PausingManualCompaction:3",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<int>*>(&manual_compaction_paused_)));
|
||||
TEST_SYNC_POINT_CALLBACK("TestCompactFiles:PausingManualCompaction:3",
|
||||
static_cast<void*>(const_cast<std::atomic<int>*>(
|
||||
&manual_compaction_paused_)));
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto* current = cfd->current();
|
||||
@@ -1695,7 +1694,7 @@ Status DBImpl::PauseBackgroundWork() {
|
||||
Status DBImpl::ContinueBackgroundWork() {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
if (bg_work_paused_ == 0) {
|
||||
return Status::InvalidArgument();
|
||||
return Status::InvalidArgument("Background work already unpaused");
|
||||
}
|
||||
assert(bg_work_paused_ > 0);
|
||||
assert(bg_compaction_paused_ > 0);
|
||||
@@ -3045,8 +3044,8 @@ void DBImpl::SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkFlush(void* arg) {
|
||||
FlushThreadArg fta = *(reinterpret_cast<FlushThreadArg*>(arg));
|
||||
delete reinterpret_cast<FlushThreadArg*>(arg);
|
||||
FlushThreadArg fta = *(static_cast<FlushThreadArg*>(arg));
|
||||
delete static_cast<FlushThreadArg*>(arg);
|
||||
|
||||
IOSTATS_SET_THREAD_POOL_ID(fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush");
|
||||
@@ -3055,8 +3054,8 @@ void DBImpl::BGWorkFlush(void* arg) {
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkCompaction(void* arg) {
|
||||
CompactionArg ca = *(reinterpret_cast<CompactionArg*>(arg));
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
CompactionArg ca = *(static_cast<CompactionArg*>(arg));
|
||||
delete static_cast<CompactionArg*>(arg);
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::LOW);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkCompaction");
|
||||
auto prepicked_compaction =
|
||||
@@ -3080,12 +3079,12 @@ void DBImpl::BGWorkBottomCompaction(void* arg) {
|
||||
void DBImpl::BGWorkPurge(void* db) {
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::HIGH);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkPurge:start");
|
||||
reinterpret_cast<DBImpl*>(db)->BackgroundCallPurge();
|
||||
static_cast<DBImpl*>(db)->BackgroundCallPurge();
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkPurge:end");
|
||||
}
|
||||
|
||||
void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
CompactionArg* ca_ptr = reinterpret_cast<CompactionArg*>(arg);
|
||||
CompactionArg* ca_ptr = static_cast<CompactionArg*>(arg);
|
||||
Env::Priority compaction_pri = ca_ptr->compaction_pri_;
|
||||
if (Env::Priority::BOTTOM == compaction_pri) {
|
||||
// Decrement bg_bottom_compaction_scheduled_ if priority is BOTTOM
|
||||
@@ -3095,7 +3094,7 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
ca_ptr->db->bg_compaction_scheduled_--;
|
||||
}
|
||||
CompactionArg ca = *(ca_ptr);
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
delete static_cast<CompactionArg*>(arg);
|
||||
if (ca.prepicked_compaction != nullptr) {
|
||||
// if it's a manual compaction, set status to ManualCompactionPaused
|
||||
if (ca.prepicked_compaction->manual_compaction_state) {
|
||||
@@ -3115,14 +3114,14 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
|
||||
void DBImpl::UnscheduleFlushCallback(void* arg) {
|
||||
// Decrement bg_flush_scheduled_ in flush callback
|
||||
reinterpret_cast<FlushThreadArg*>(arg)->db_->bg_flush_scheduled_--;
|
||||
Env::Priority flush_pri = reinterpret_cast<FlushThreadArg*>(arg)->thread_pri_;
|
||||
static_cast<FlushThreadArg*>(arg)->db_->bg_flush_scheduled_--;
|
||||
Env::Priority flush_pri = static_cast<FlushThreadArg*>(arg)->thread_pri_;
|
||||
if (Env::Priority::LOW == flush_pri) {
|
||||
TEST_SYNC_POINT("DBImpl::UnscheduleLowFlushCallback");
|
||||
} else if (Env::Priority::HIGH == flush_pri) {
|
||||
TEST_SYNC_POINT("DBImpl::UnscheduleHighFlushCallback");
|
||||
}
|
||||
delete reinterpret_cast<FlushThreadArg*>(arg);
|
||||
delete static_cast<FlushThreadArg*>(arg);
|
||||
TEST_SYNC_POINT("DBImpl::UnscheduleFlushCallback");
|
||||
}
|
||||
|
||||
|
||||
@@ -208,11 +208,11 @@ void DBImpl::TEST_SignalAllBgCv() { bg_cv_.SignalAll(); }
|
||||
void* DBImpl::TEST_BeginWrite() {
|
||||
auto w = new WriteThread::Writer();
|
||||
write_thread_.EnterUnbatched(w, &mutex_);
|
||||
return reinterpret_cast<void*>(w);
|
||||
return static_cast<void*>(w);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_EndWrite(void* w) {
|
||||
auto writer = reinterpret_cast<WriteThread::Writer*>(w);
|
||||
auto writer = static_cast<WriteThread::Writer*>(w);
|
||||
write_thread_.ExitUnbatched(writer);
|
||||
delete writer;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ Status DBImpl::DisableFileDeletions() {
|
||||
if (my_disable_delete_obsolete_files == 1) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Disabled");
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
my_disable_delete_obsolete_files);
|
||||
}
|
||||
@@ -67,17 +67,14 @@ Status DBImpl::DisableFileDeletionsWithLock() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::EnableFileDeletions(bool force) {
|
||||
Status DBImpl::EnableFileDeletions() {
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
int saved_counter; // initialize on all paths
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (force) {
|
||||
// if force, we need to enable file deletions right away
|
||||
disable_delete_obsolete_files_ = 0;
|
||||
} else if (disable_delete_obsolete_files_ > 0) {
|
||||
if (disable_delete_obsolete_files_ > 0) {
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
saved_counter = disable_delete_obsolete_files_;
|
||||
@@ -92,7 +89,7 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
saved_counter);
|
||||
}
|
||||
|
||||
@@ -1682,7 +1682,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
const WriteOptions write_option(Env::IO_HIGH, Env::IOActivity::kDBOpen);
|
||||
TableBuilderOptions tboptions(
|
||||
*cfd->ioptions(), mutable_cf_options, read_option, write_option,
|
||||
cfd->internal_comparator(), cfd->int_tbl_prop_collector_factories(),
|
||||
cfd->internal_comparator(), cfd->internal_tbl_prop_coll_factories(),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
mutable_cf_options.compression_opts, cfd->GetID(), cfd->GetName(),
|
||||
0 /* level */, false /* is_bottommost */,
|
||||
|
||||
@@ -169,8 +169,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& _read_options,
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
SequenceNumber read_seq =
|
||||
read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
|
||||
: latest_snapshot;
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
@@ -216,8 +215,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
SequenceNumber read_seq =
|
||||
read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
|
||||
: latest_snapshot;
|
||||
|
||||
autovector<std::tuple<ColumnFamilyData*, SuperVersion*>> cfd_to_sv;
|
||||
|
||||
@@ -31,18 +31,17 @@ class DBImplReadOnly : public DBImpl {
|
||||
// TODO: Implement ReadOnly MultiGet?
|
||||
|
||||
using DBImpl::NewIterator;
|
||||
virtual Iterator* NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
Iterator* NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
|
||||
virtual Status NewIterators(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
Status NewIterators(const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
|
||||
using DBImpl::Put;
|
||||
virtual Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
@@ -59,37 +58,36 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
using DBImpl::Merge;
|
||||
virtual Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
using DBImpl::Delete;
|
||||
virtual Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
using DBImpl::SingleDelete;
|
||||
virtual Status SingleDelete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
Status SingleDelete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
using DBImpl::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/,
|
||||
const Slice* /*end*/) override {
|
||||
Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/, const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DBImpl::CompactFiles;
|
||||
virtual Status CompactFiles(
|
||||
Status CompactFiles(
|
||||
const CompactionOptions& /*compact_options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*input_file_names*/,
|
||||
@@ -99,33 +97,33 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
virtual Status DisableFileDeletions() override {
|
||||
Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
Status EnableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
return DBImpl::GetLiveFiles(ret, manifest_file_size,
|
||||
false /* flush_memtable */);
|
||||
}
|
||||
|
||||
using DBImpl::Flush;
|
||||
virtual Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DBImpl::SyncWAL;
|
||||
virtual Status SyncWAL() override {
|
||||
Status SyncWAL() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
Status IngestExternalFile(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
@@ -133,7 +131,7 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
@@ -142,7 +140,7 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
@@ -152,9 +150,9 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ class DBImplSecondary : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status EnableFileDeletions(bool /*force*/) override {
|
||||
Status EnableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
|
||||
@@ -819,6 +819,9 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
assert(w.ShouldWriteToMemtable());
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
@@ -828,6 +831,10 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
0 /*log_number*/, this, true /*concurrent_memtable_writes*/,
|
||||
false /*seq_per_batch*/, 0 /*batch_cnt*/, true /*batch_per_txn*/,
|
||||
write_options.memtable_insert_hint_per_batch);
|
||||
|
||||
PERF_TIMER_STOP(write_memtable_time);
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
versions_->SetLastSequence(w.write_group->last_sequence);
|
||||
@@ -860,6 +867,9 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
stats->AddDBStats(InternalStats::kIntStatsNumKeysWritten, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
@@ -871,6 +881,8 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
if (write_options.disableWAL) {
|
||||
has_unpersisted_data_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
}
|
||||
|
||||
size_t pending_cnt = pending_memtable_writes_.fetch_sub(1) - 1;
|
||||
@@ -941,9 +953,13 @@ Status DBImpl::WriteImplWALOnly(
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_delay_time);
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
Status status =
|
||||
DelayWrite(/*num_bytes=*/0ull, *write_thread, write_options);
|
||||
PERF_TIMER_STOP(write_delay_time);
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
if (!status.ok()) {
|
||||
WriteThread::WriteGroup write_group;
|
||||
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
|
||||
|
||||
+20
-7
@@ -98,7 +98,12 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
for (auto& db_path : options.db_paths) {
|
||||
if (dbname.compare(db_path.path) != 0) {
|
||||
s = env->GetChildren(db_path.path, &files);
|
||||
if (!s.ok()) {
|
||||
if (s.IsNotFound() || s.IsPathNotFound()) {
|
||||
Header(options.info_log,
|
||||
"Directory from db_paths/cf_paths does not yet exist: %s\n",
|
||||
db_path.path.c_str());
|
||||
continue;
|
||||
} else if (!s.ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir %s\n",
|
||||
db_path.path.c_str(), s.ToString().c_str());
|
||||
continue;
|
||||
@@ -121,12 +126,18 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
|
||||
// Get wal file in wal_dir
|
||||
const auto& wal_dir = options.GetWalDir(dbname);
|
||||
bool log_wal_info = true;
|
||||
if (!options.IsWalDirSameAsDBPath(dbname)) {
|
||||
s = env->GetChildren(wal_dir, &files);
|
||||
if (!s.ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir %s\n", wal_dir.c_str(),
|
||||
s.ToString().c_str());
|
||||
return;
|
||||
if (s.IsNotFound() || s.IsPathNotFound()) {
|
||||
Header(options.info_log,
|
||||
"Write Ahead Log directory does not yet exist: %s\n",
|
||||
wal_dir.c_str());
|
||||
log_wal_info = false;
|
||||
} else if (!s.ok()) {
|
||||
Error(options.info_log, "Error when reading wal dir %s: %s\n",
|
||||
wal_dir.c_str(), s.ToString().c_str());
|
||||
log_wal_info = false;
|
||||
}
|
||||
wal_info.clear();
|
||||
for (const std::string& file : files) {
|
||||
@@ -146,7 +157,9 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
}
|
||||
}
|
||||
}
|
||||
Header(options.info_log, "Write Ahead Log file in %s: %s\n", wal_dir.c_str(),
|
||||
wal_info.c_str());
|
||||
if (log_wal_info) {
|
||||
Header(options.info_log, "Write Ahead Log file in %s: %s\n",
|
||||
wal_dir.c_str(), wal_info.c_str());
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+10
-8
@@ -428,13 +428,15 @@ class DBIter final : public Iterator {
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified `sequence` number
|
||||
// into appropriate user keys.
|
||||
extern Iterator* NewDBIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const Version* version, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, ReadCallback* read_callback,
|
||||
DBImpl* db_impl = nullptr, ColumnFamilyData* cfd = nullptr,
|
||||
bool expose_blob_index = false);
|
||||
Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr,
|
||||
bool expose_blob_index = false);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+5
-5
@@ -2824,7 +2824,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace4) {
|
||||
// Seek() and before calling Prev()
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"MergeIterator::Prev:BeforePrev", [&](void* arg) {
|
||||
IteratorWrapper* it = reinterpret_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = static_cast<IteratorWrapper*>(arg);
|
||||
if (it->key().starts_with("z")) {
|
||||
internal_iter2_->Add("x", kTypeValue, "7", 16u, true);
|
||||
internal_iter2_->Add("x", kTypeValue, "7", 15u, true);
|
||||
@@ -2875,7 +2875,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace5) {
|
||||
// Seek() and before calling Prev()
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"MergeIterator::Prev:BeforePrev", [&](void* arg) {
|
||||
IteratorWrapper* it = reinterpret_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = static_cast<IteratorWrapper*>(arg);
|
||||
if (it->key().starts_with("z")) {
|
||||
internal_iter2_->Add("x", kTypeValue, "7", 16u, true);
|
||||
internal_iter2_->Add("x", kTypeValue, "7", 15u, true);
|
||||
@@ -2922,7 +2922,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace6) {
|
||||
// Seek() and before calling Prev()
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"MergeIterator::Prev:BeforePrev", [&](void* arg) {
|
||||
IteratorWrapper* it = reinterpret_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = static_cast<IteratorWrapper*>(arg);
|
||||
if (it->key().starts_with("z")) {
|
||||
internal_iter2_->Add("x", kTypeValue, "7", 16u, true);
|
||||
}
|
||||
@@ -2971,7 +2971,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace7) {
|
||||
// Seek() and before calling Prev()
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"MergeIterator::Prev:BeforePrev", [&](void* arg) {
|
||||
IteratorWrapper* it = reinterpret_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = static_cast<IteratorWrapper*>(arg);
|
||||
if (it->key().starts_with("z")) {
|
||||
internal_iter2_->Add("x", kTypeValue, "7", 16u, true);
|
||||
internal_iter2_->Add("x", kTypeValue, "7", 15u, true);
|
||||
@@ -3024,7 +3024,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace8) {
|
||||
// before calling Prev()
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"MergeIterator::Prev:BeforePrev", [&](void* arg) {
|
||||
IteratorWrapper* it = reinterpret_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = static_cast<IteratorWrapper*>(arg);
|
||||
if (it->key().starts_with("z")) {
|
||||
internal_iter2_->Add("x", kTypeValue, "7", 16u, true);
|
||||
internal_iter2_->Add("y", kTypeValue, "7", 17u, true);
|
||||
|
||||
@@ -2525,7 +2525,7 @@ TEST_P(DBIteratorTest, RefreshWithSnapshot) {
|
||||
TEST_P(DBIteratorTest, CreationFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::NewInternalIterator:StatusCallback", [](void* arg) {
|
||||
*(reinterpret_cast<Status*>(arg)) = Status::Corruption("test status");
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("test status");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -3448,8 +3448,7 @@ TEST_F(DBIteratorTest, ErrorWhenReadFile) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::BeforeReturn",
|
||||
[&error_file](void* io_s_ptr) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(io_s_ptr);
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(io_s_ptr);
|
||||
if (p->first->find(error_file) != std::string::npos) {
|
||||
*p->second = IOStatus::IOError();
|
||||
p->second->SetRetryable(true);
|
||||
@@ -3529,8 +3528,7 @@ TEST_F(DBIteratorTest, ErrorWhenReadFile) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::AnyOffset", [&f1](void* pair_ptr) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
if (p->first->find(f1) != std::string::npos) {
|
||||
*p->second = IOStatus::IOError();
|
||||
p->second->SetRetryable(true);
|
||||
|
||||
@@ -437,14 +437,13 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALCorrupted) {
|
||||
// This callback should only be called by the leader thread
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait2", [&](void* arg_leader) {
|
||||
auto* leader = reinterpret_cast<WriteThread::Writer*>(arg_leader);
|
||||
auto* leader = static_cast<WriteThread::Writer*>(arg_leader);
|
||||
ASSERT_EQ(leader->state, WriteThread::STATE_GROUP_LEADER);
|
||||
|
||||
// This callback should only be called by the follower thread
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg_follower) {
|
||||
auto* follower =
|
||||
reinterpret_cast<WriteThread::Writer*>(arg_follower);
|
||||
auto* follower = static_cast<WriteThread::Writer*>(arg_follower);
|
||||
// The leader thread will wait on this bool and hence wait until
|
||||
// this writer joins the write group
|
||||
ASSERT_NE(follower->state, WriteThread::STATE_GROUP_LEADER);
|
||||
@@ -549,14 +548,13 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALWithColumnFamilyCorrupted) {
|
||||
// This callback should only be called by the leader thread
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait2", [&](void* arg_leader) {
|
||||
auto* leader = reinterpret_cast<WriteThread::Writer*>(arg_leader);
|
||||
auto* leader = static_cast<WriteThread::Writer*>(arg_leader);
|
||||
ASSERT_EQ(leader->state, WriteThread::STATE_GROUP_LEADER);
|
||||
|
||||
// This callback should only be called by the follower thread
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg_follower) {
|
||||
auto* follower =
|
||||
reinterpret_cast<WriteThread::Writer*>(arg_follower);
|
||||
auto* follower = static_cast<WriteThread::Writer*>(arg_follower);
|
||||
// The leader thread will wait on this bool and hence wait until
|
||||
// this writer joins the write group
|
||||
ASSERT_NE(follower->state, WriteThread::STATE_GROUP_LEADER);
|
||||
@@ -662,7 +660,7 @@ TEST_F(DbKVChecksumWALToWriteBatchTest, WriteBatchChecksumHandoff) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RecoverLogFiles:BeforeUpdateProtectionInfo:batch",
|
||||
[&](void* batch_ptr) {
|
||||
WriteBatch* batch = reinterpret_cast<WriteBatch*>(batch_ptr);
|
||||
WriteBatch* batch = static_cast<WriteBatch*>(batch_ptr);
|
||||
content.assign(batch->Data().data(), batch->GetDataSize());
|
||||
Slice batch_content = batch->Data();
|
||||
// Corrupt first bit
|
||||
@@ -672,7 +670,7 @@ TEST_F(DbKVChecksumWALToWriteBatchTest, WriteBatchChecksumHandoff) {
|
||||
"DBImpl::RecoverLogFiles:BeforeUpdateProtectionInfo:checksum",
|
||||
[&](void* checksum_ptr) {
|
||||
// Verify that checksum is produced on the batch content
|
||||
uint64_t checksum = *reinterpret_cast<uint64_t*>(checksum_ptr);
|
||||
uint64_t checksum = *static_cast<uint64_t*>(checksum_ptr);
|
||||
ASSERT_EQ(checksum, XXH3_64bits(content.data(), content.size()));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -236,7 +236,7 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorCorruptedLog) {
|
||||
ASSERT_OK(test::TruncateFile(env_, logfile_path,
|
||||
wal_files.front()->SizeFileBytes() / 2));
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/false));
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
|
||||
// Insert a new entry to a new log file
|
||||
ASSERT_OK(Put("key1025", DummyString(10)));
|
||||
|
||||
@@ -121,7 +121,7 @@ class TestPrefixExtractor : public SliceTransform {
|
||||
|
||||
private:
|
||||
const char* separator(const Slice& key) const {
|
||||
return reinterpret_cast<const char*>(memchr(key.data(), '_', key.size()));
|
||||
return static_cast<const char*>(memchr(key.data(), '_', key.size()));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -287,7 +287,7 @@ TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
MockMemTableRep* rep =
|
||||
reinterpret_cast<MockMemTableRepFactory*>(options.memtable_factory.get())
|
||||
static_cast<MockMemTableRepFactory*>(options.memtable_factory.get())
|
||||
->rep();
|
||||
ASSERT_OK(Put("foo_k1", "foo_v1"));
|
||||
ASSERT_EQ(nullptr, rep->last_hint_in());
|
||||
|
||||
@@ -1390,7 +1390,7 @@ TEST_F(DBOptionsTest, ChangeCompression) {
|
||||
bool compacted = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* c = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* c = static_cast<Compaction*>(arg);
|
||||
compression_used = c->output_compression();
|
||||
compression_opt_used = c->output_compression_opts();
|
||||
compacted = true;
|
||||
|
||||
@@ -106,12 +106,18 @@ TEST_F(DBPropertiesTest, Empty) {
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("0", num);
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/false));
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("0", num);
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/true));
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("0", num);
|
||||
// File deletion enabled after `EnableFileDeletions` called as many times
|
||||
// as `DisableFileDeletions`.
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("1", num);
|
||||
@@ -1743,7 +1749,7 @@ TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
ASSERT_EQ(obsolete_sst_size, sst_size);
|
||||
|
||||
// Let the obsolete files be deleted.
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/false));
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kObsoleteSstFilesSize,
|
||||
&obsolete_sst_size));
|
||||
ASSERT_EQ(obsolete_sst_size, 0);
|
||||
|
||||
+64
-27
@@ -1036,9 +1036,6 @@ TEST_F(DBRangeDelTest, CompactionTreatsSplitInputLevelDeletionAtomically) {
|
||||
test::NewSpecialSkipListFactory(2 /* num_entries_flush */));
|
||||
// max file size could be 2x of target file size, so set it to half of that
|
||||
options.target_file_size_base = kValueBytes / 2;
|
||||
// disable dynamic_file_size, as it will cut L1 files into more files (than
|
||||
// kNumFilesPerLevel).
|
||||
options.level_compaction_dynamic_file_size = false;
|
||||
options.max_compaction_bytes = 1500;
|
||||
// i == 0: CompactFiles
|
||||
// i == 1: CompactRange
|
||||
@@ -1107,14 +1104,9 @@ TEST_F(DBRangeDelTest, RangeTombstoneEndKeyAsSstableUpperBound) {
|
||||
options.level0_file_num_compaction_trigger = kNumFilesPerLevel;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(2 /* num_entries_flush */));
|
||||
options.target_file_size_base = kValueBytes;
|
||||
// Compaction can generate files of size at most 2 * target_file_size_base.
|
||||
options.target_file_size_base = kValueBytes / 2;
|
||||
options.disable_auto_compactions = true;
|
||||
// disable it for now, otherwise the L1 files are going be cut before data 1:
|
||||
// L1: [0] [1,4]
|
||||
// L2: [0,0]
|
||||
// because the grandparent file is between [0]->[1] and it's size is more than
|
||||
// 1/8 of target size (4k).
|
||||
options.level_compaction_dynamic_file_size = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -1154,6 +1146,13 @@ TEST_F(DBRangeDelTest, RangeTombstoneEndKeyAsSstableUpperBound) {
|
||||
// [key000000#1,1, key000000#1,1]
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(1));
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
ASSERT_EQ(
|
||||
files[1][0].largest.Encode(),
|
||||
InternalKey(Key(2), kMaxSequenceNumber, kTypeRangeDeletion).Encode());
|
||||
ASSERT_EQ(files[1][1].smallest.Encode(),
|
||||
InternalKey(Key(2), 6, kTypeValue).Encode());
|
||||
|
||||
{
|
||||
// Compact the second sstable in L1:
|
||||
@@ -1172,6 +1171,12 @@ TEST_F(DBRangeDelTest, RangeTombstoneEndKeyAsSstableUpperBound) {
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(2));
|
||||
ASSERT_EQ(value, Get(Key(2)));
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
ASSERT_EQ(files[2][1].smallest.Encode(),
|
||||
InternalKey(Key(2), 6, kTypeValue).Encode());
|
||||
ASSERT_EQ(
|
||||
files[2][1].largest.Encode(),
|
||||
InternalKey(Key(4), kMaxSequenceNumber, kTypeRangeDeletion).Encode());
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1190,6 +1195,20 @@ TEST_F(DBRangeDelTest, RangeTombstoneEndKeyAsSstableUpperBound) {
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, &begin, &begin));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(3, NumTableFilesAtLevel(2));
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
ASSERT_EQ(
|
||||
files[2][0].largest.Encode(),
|
||||
InternalKey(Key(1), kMaxSequenceNumber, kTypeRangeDeletion).Encode());
|
||||
ASSERT_EQ(files[2][1].smallest.Encode(),
|
||||
InternalKey(Key(1), 5, kTypeValue).Encode());
|
||||
ASSERT_EQ(
|
||||
files[2][1].largest.Encode(),
|
||||
InternalKey(Key(2), kMaxSequenceNumber, kTypeRangeDeletion).Encode());
|
||||
ASSERT_EQ(files[2][2].smallest.Encode(),
|
||||
InternalKey(Key(2), 6, kTypeValue).Encode());
|
||||
ASSERT_EQ(
|
||||
files[2][2].largest.Encode(),
|
||||
InternalKey(Key(4), kMaxSequenceNumber, kTypeRangeDeletion).Encode());
|
||||
}
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
@@ -2309,13 +2328,13 @@ TEST_F(DBRangeDelTest, NonOverlappingTombstonAtBoundary) {
|
||||
// Test set up:
|
||||
// L1_0: 1, 3, [4, 7) L1_1: 6, 8, [4, 7)
|
||||
// L2: 5
|
||||
// L1_0's largest key: Key(6)@kMaxSequenceNumber with type kTypeRangeDeletion
|
||||
// Note that [4, 7) is at end of L1_0 and not overlapping with any point key
|
||||
// in L1_0. [4, 7) from L1_0 should cover 5 is sentinel works
|
||||
// in L1_0. [4, 7) from L1_0 should cover 5 if sentinel in LevelIterator works
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.target_file_size_base = 2 * 1024;
|
||||
options.level_compaction_dynamic_file_size = false;
|
||||
options.target_file_size_base = 4 * 1024;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -2335,6 +2354,7 @@ TEST_F(DBRangeDelTest, NonOverlappingTombstonAtBoundary) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1), rnd.RandomString(4 << 10)));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(3), rnd.RandomString(4 << 10)));
|
||||
// Prevent keys being compacted away
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(4),
|
||||
Key(7)));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
@@ -2342,6 +2362,11 @@ TEST_F(DBRangeDelTest, NonOverlappingTombstonAtBoundary) {
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(1));
|
||||
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
InternalKey ik = InternalKey(Key(6), kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
ASSERT_EQ(files[1][0].largest.Encode(), ik.Encode());
|
||||
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
iter->Seek(Key(3));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -2361,6 +2386,7 @@ TEST_F(DBRangeDelTest, NonOverlappingTombstonAtBoundary) {
|
||||
ASSERT_EQ(get_perf_context()->internal_range_del_reseek_count, 1);
|
||||
}
|
||||
delete iter;
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, OlderLevelHasNewerData) {
|
||||
@@ -3126,14 +3152,18 @@ TEST_F(DBRangeDelTest, RangetombesoneCompensateFilesizePersistDuringReopen) {
|
||||
TEST_F(DBRangeDelTest, SingleKeyFile) {
|
||||
// Test for a bug fix where a range tombstone could be added
|
||||
// to an SST file while is not within the file's key range.
|
||||
// Create 3 files in L0 and then L1 where all keys have the same user key
|
||||
// `Key(2)`. The middle file will contain Key(2)@6 and Key(2)@5. Before fix,
|
||||
// the range tombstone [Key(2), Key(5))@2 would be added to this file during
|
||||
// compaction, but it is not in this file's key range.
|
||||
// Create 3 files in L0 and then compact them to L1 where all keys have the
|
||||
// same user key `Key(2)`.
|
||||
// L0_0: Key(2)@5
|
||||
// L0_1: Key(2)@4
|
||||
// L0_2: Key(2)@3, range tombstone [Key(2), Key(5))@2
|
||||
//
|
||||
// After compaction, the first output file contains Key(2)@5 and Key(2)@4.
|
||||
// Before fix, the range tombstone [Key(2), Key(5))@2 would be added to this
|
||||
// file during compaction, but it is not in this file's key range.
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.target_file_size_base = 1 << 10;
|
||||
opts.level_compaction_dynamic_file_size = false;
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
// prevent range tombstone drop
|
||||
@@ -3178,6 +3208,12 @@ TEST_F(DBRangeDelTest, SingleKeyFile) {
|
||||
std::numeric_limits<uint64_t>::max() /*max_file_num_to_ignore*/,
|
||||
"" /*trim_ts*/));
|
||||
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(1));
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
ASSERT_EQ(files[1][0].largest.Encode(),
|
||||
InternalKey(Key(2), 4, kTypeValue).Encode());
|
||||
|
||||
for (const auto s : snapshots) {
|
||||
db_->ReleaseSnapshot(s);
|
||||
}
|
||||
@@ -3248,13 +3284,12 @@ TEST_F(DBRangeDelTest, AddRangeDelsSameLowerAndUpperBound) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.target_file_size_base = 1 << 10;
|
||||
opts.level_compaction_dynamic_file_size = false;
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
Random rnd(301);
|
||||
// Create file at bottommost level so the manual compaction below is
|
||||
// non-bottommost level and goes through code path like compensate range
|
||||
// tombstone size.
|
||||
// non-bottommost level and goes through code path in
|
||||
// versions->ApproximateSize() to calculate compensated range tombstone size
|
||||
ASSERT_OK(Put(Key(1), "v1"));
|
||||
ASSERT_OK(Put(Key(4), "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -3277,6 +3312,12 @@ TEST_F(DBRangeDelTest, AddRangeDelsSameLowerAndUpperBound) {
|
||||
// File 1: Key(1)@1, Key(3)@6, DeleteRange ends at Key(3)@6
|
||||
// File 2: Key(3)@4, Key(4)@7, DeleteRange start from Key(3)@4
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 2);
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
ASSERT_EQ(files[1][0].largest.Encode(),
|
||||
InternalKey(Key(3), 6, kTypeValue).Encode());
|
||||
ASSERT_EQ(files[1][1].smallest.Encode(),
|
||||
InternalKey(Key(3), 4, kTypeValue).Encode());
|
||||
|
||||
// Manually update compaction output file cutting decisions
|
||||
// to cut before range tombstone sentinel Key(3)@4
|
||||
@@ -3325,7 +3366,6 @@ TEST_F(DBRangeDelTest, AddRangeDelsSingleUserKeyTombstoneOnlyFile) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.target_file_size_base = 1 << 10;
|
||||
opts.level_compaction_dynamic_file_size = false;
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -3486,7 +3526,6 @@ TEST_F(DBRangeDelTest, NonBottommostCompactionDropRangetombstone) {
|
||||
TEST_F(DBRangeDelTest, MemtableMaxRangeDeletions) {
|
||||
// Tests option `memtable_max_range_deletions`.
|
||||
Options options = CurrentOptions();
|
||||
options.level_compaction_dynamic_file_size = false;
|
||||
options.memtable_max_range_deletions = 50;
|
||||
options.level0_file_num_compaction_trigger = 5;
|
||||
DestroyAndReopen(options);
|
||||
@@ -3566,8 +3605,7 @@ TEST_F(DBRangeDelTest, RangeDelReseekAfterFileReadError) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::BeforeReturn", [&fname](void* pair_ptr) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
if (p->first->find(fname) != std::string::npos) {
|
||||
*p->second = IOStatus::IOError();
|
||||
p->second->SetRetryable(true);
|
||||
@@ -3627,8 +3665,7 @@ TEST_F(DBRangeDelTest, RangeDelReseekAfterFileReadError) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::AnyOffset", [&fname](void* pair_ptr) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
if (p->first->find(fname) != std::string::npos) {
|
||||
*p->second = IOStatus::IOError();
|
||||
p->second->SetRetryable(true);
|
||||
|
||||
@@ -131,7 +131,7 @@ TEST_F(DBSecondaryTest, FailOpenIfLoggerCreationFail) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"rocksdb::CreateLoggerFromOptions:AfterGetPath", [&](void* arg) {
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected");
|
||||
});
|
||||
@@ -1191,7 +1191,7 @@ TEST_F(DBSecondaryTest, CheckConsistencyWhenOpen) {
|
||||
"DBImplSecondary::CheckConsistency:AfterFirstAttempt", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
called = true;
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
ASSERT_NOK(*s);
|
||||
});
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
@@ -1229,8 +1229,7 @@ TEST_F(DBSecondaryTest, StartFromInconsistent) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options1;
|
||||
@@ -1263,8 +1262,7 @@ TEST_F(DBSecondaryTest, InconsistencyDuringCatchUp) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = db_secondary_->TryCatchUpWithPrimary();
|
||||
|
||||
+1
-1
@@ -1679,7 +1679,7 @@ TEST_F(DBSSTTest, OpenDBWithoutGetFileSizeInvocations) {
|
||||
bool is_get_file_size_called = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MockFileSystem::GetFileSize:CheckFileType", [&](void* arg) {
|
||||
std::string* filename = reinterpret_cast<std::string*>(arg);
|
||||
std::string* filename = static_cast<std::string*>(arg);
|
||||
if (filename->find(".blob") != std::string::npos) {
|
||||
is_get_file_size_called = true;
|
||||
}
|
||||
|
||||
+108
-46
@@ -59,9 +59,6 @@ class DBTablePropertiesTest : public DBTestBase,
|
||||
public:
|
||||
DBTablePropertiesTest()
|
||||
: DBTestBase("db_table_properties_test", /*env_do_fsync=*/false) {}
|
||||
TablePropertiesCollection TestGetPropertiesOfTablesInRange(
|
||||
std::vector<Range> ranges, std::size_t* num_properties = nullptr,
|
||||
std::size_t* num_files = nullptr);
|
||||
};
|
||||
|
||||
TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
@@ -77,8 +74,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
if (table == 3) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WritePropertiesBlock:Meta", [&](void* meta) {
|
||||
*reinterpret_cast<const std::string**>(meta) =
|
||||
&kPropertiesBlockOldName;
|
||||
*static_cast<const std::string**>(meta) = &kPropertiesBlockOldName;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
}
|
||||
@@ -96,7 +92,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
// Part of strategy to prevent pinning table files
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionEditHandler::LoadTables:skip_load_table_files",
|
||||
[&](void* skip_load) { *reinterpret_cast<bool*>(skip_load) = true; });
|
||||
[&](void* skip_load) { *static_cast<bool*>(skip_load) = true; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// 1. Read table properties directly from file
|
||||
@@ -181,9 +177,7 @@ TEST_F(DBTablePropertiesTest, InvalidIgnored) {
|
||||
// Inject properties block data that Block considers invalid
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WritePropertiesBlock:BlockData",
|
||||
[&](void* block_data) {
|
||||
*reinterpret_cast<Slice*>(block_data) = Slice("X");
|
||||
});
|
||||
[&](void* block_data) { *static_cast<Slice*>(block_data) = Slice("X"); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Corrupting the table properties corrupts the unique id.
|
||||
@@ -236,49 +230,109 @@ TEST_F(DBTablePropertiesTest, CreateOnDeletionCollectorFactory) {
|
||||
ASSERT_EQ(0.5, del_factory->GetDeletionRatio());
|
||||
}
|
||||
|
||||
TablePropertiesCollection
|
||||
DBTablePropertiesTest::TestGetPropertiesOfTablesInRange(
|
||||
std::vector<Range> ranges, std::size_t* num_properties,
|
||||
std::size_t* num_files) {
|
||||
// Since we deref zero element in the vector it can not be empty
|
||||
// otherwise we pass an address to some random memory
|
||||
EXPECT_GT(ranges.size(), 0U);
|
||||
// run the query
|
||||
TablePropertiesCollection props;
|
||||
EXPECT_OK(db_->GetPropertiesOfTablesInRange(
|
||||
db_->DefaultColumnFamily(), ranges.data(), ranges.size(), &props));
|
||||
// Test params:
|
||||
// 1) whether to enable user-defined timestamps
|
||||
class DBTablePropertiesInRangeTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBTablePropertiesInRangeTest()
|
||||
: DBTestBase("db_table_properties_in_range_test",
|
||||
/*env_do_fsync=*/false) {}
|
||||
|
||||
// Make sure that we've received properties for those and for those files
|
||||
// only which fall within requested ranges
|
||||
std::vector<LiveFileMetaData> vmd;
|
||||
db_->GetLiveFilesMetaData(&vmd);
|
||||
for (auto& md : vmd) {
|
||||
std::string fn = md.db_path + md.name;
|
||||
bool in_range = false;
|
||||
void SetUp() override { enable_udt_ = GetParam(); }
|
||||
|
||||
protected:
|
||||
void PutKeyValue(const Slice& key, const Slice& value) {
|
||||
if (enable_udt_) {
|
||||
EXPECT_OK(db_->Put(WriteOptions(), key, min_ts_, value));
|
||||
} else {
|
||||
EXPECT_OK(Put(key, value));
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetValue(const std::string& key) {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
if (enable_udt_) {
|
||||
roptions.timestamp = &min_ts_;
|
||||
}
|
||||
Status s = db_->Get(roptions, key, &result);
|
||||
EXPECT_TRUE(s.ok());
|
||||
return result;
|
||||
}
|
||||
|
||||
Status MaybeGetValue(const std::string& key, std::string* result) {
|
||||
ReadOptions roptions;
|
||||
if (enable_udt_) {
|
||||
roptions.timestamp = &min_ts_;
|
||||
}
|
||||
Status s = db_->Get(roptions, key, result);
|
||||
EXPECT_TRUE(s.IsNotFound() || s.ok());
|
||||
return s;
|
||||
}
|
||||
|
||||
TablePropertiesCollection TestGetPropertiesOfTablesInRange(
|
||||
std::vector<Range> ranges, std::size_t* num_properties = nullptr,
|
||||
std::size_t* num_files = nullptr) {
|
||||
// Since we deref zero element in the vector it can not be empty
|
||||
// otherwise we pass an address to some random memory
|
||||
EXPECT_GT(ranges.size(), 0U);
|
||||
// run the query
|
||||
TablePropertiesCollection props;
|
||||
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
|
||||
EXPECT_OK(db_->GetPropertiesOfTablesInRange(default_cf, &ranges[0],
|
||||
ranges.size(), &props));
|
||||
|
||||
const Comparator* ucmp = default_cf->GetComparator();
|
||||
EXPECT_NE(ucmp, nullptr);
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const size_t range_size = ranges.size();
|
||||
autovector<UserKeyRange> ukey_ranges;
|
||||
std::vector<std::string> keys;
|
||||
ukey_ranges.reserve(range_size);
|
||||
keys.reserve(range_size * 2);
|
||||
for (auto& r : ranges) {
|
||||
// smallestkey < limit && largestkey >= start
|
||||
if (r.limit.compare(md.smallestkey) >= 0 &&
|
||||
r.start.compare(md.largestkey) <= 0) {
|
||||
in_range = true;
|
||||
EXPECT_GT(props.count(fn), 0);
|
||||
auto [start, limit] = MaybeAddTimestampsToRange(
|
||||
&r.start, &r.limit, ts_sz, &keys.emplace_back(), &keys.emplace_back(),
|
||||
/*exclusive_end=*/false);
|
||||
EXPECT_TRUE(start.has_value());
|
||||
EXPECT_TRUE(limit.has_value());
|
||||
ukey_ranges.emplace_back(start.value(), limit.value());
|
||||
}
|
||||
// Make sure that we've received properties for those and for those files
|
||||
// only which fall within requested ranges
|
||||
std::vector<LiveFileMetaData> vmd;
|
||||
db_->GetLiveFilesMetaData(&vmd);
|
||||
for (auto& md : vmd) {
|
||||
std::string fn = md.db_path + md.name;
|
||||
bool in_range = false;
|
||||
for (auto& r : ukey_ranges) {
|
||||
if (ucmp->Compare(r.start, md.largestkey) <= 0 &&
|
||||
ucmp->Compare(r.limit, md.smallestkey) >= 0) {
|
||||
in_range = true;
|
||||
EXPECT_GT(props.count(fn), 0);
|
||||
}
|
||||
}
|
||||
if (!in_range) {
|
||||
EXPECT_EQ(props.count(fn), 0);
|
||||
}
|
||||
}
|
||||
if (!in_range) {
|
||||
EXPECT_EQ(props.count(fn), 0);
|
||||
|
||||
if (num_properties) {
|
||||
*num_properties = props.size();
|
||||
}
|
||||
|
||||
if (num_files) {
|
||||
*num_files = vmd.size();
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
if (num_properties) {
|
||||
*num_properties = props.size();
|
||||
}
|
||||
bool enable_udt_ = false;
|
||||
Slice min_ts_ = MinU64Ts();
|
||||
};
|
||||
|
||||
if (num_files) {
|
||||
*num_files = vmd.size();
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
TEST_P(DBTablePropertiesInRangeTest, GetPropertiesOfTablesInRange) {
|
||||
// Fixed random sead
|
||||
Random rnd(301);
|
||||
|
||||
@@ -296,17 +350,21 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
options.hard_pending_compaction_bytes_limit = 16 * 1024;
|
||||
options.num_levels = 8;
|
||||
options.env = env_;
|
||||
bool udt_enabled = GetParam();
|
||||
if (udt_enabled) {
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// build a decent LSM
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), rnd.RandomString(102)));
|
||||
PutKeyValue(test::RandomKey(&rnd, 5), rnd.RandomString(102));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
if (NumTableFilesAtLevel(0) == 0) {
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), rnd.RandomString(102)));
|
||||
PutKeyValue(test::RandomKey(&rnd, 5), rnd.RandomString(102));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
@@ -371,6 +429,10 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBTablePropertiesInRangeTest,
|
||||
DBTablePropertiesInRangeTest,
|
||||
::testing::Values(true, false));
|
||||
|
||||
TEST_F(DBTablePropertiesTest, GetColumnFamilyNameProperty) {
|
||||
std::string kExtraCfName = "pikachu";
|
||||
CreateAndReopenWithCF({kExtraCfName}, CurrentOptions());
|
||||
|
||||
@@ -203,13 +203,13 @@ TEST_P(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
bool file_iters_renewed_copy = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::SeekInternal:Return", [&](void* arg) {
|
||||
ForwardIterator* fiter = reinterpret_cast<ForwardIterator*>(arg);
|
||||
ForwardIterator* fiter = static_cast<ForwardIterator*>(arg);
|
||||
ASSERT_TRUE(!file_iters_deleted ||
|
||||
fiter->TEST_CheckDeletedIters(&deleted_iters, &num_iters));
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::Next:Return", [&](void* arg) {
|
||||
ForwardIterator* fiter = reinterpret_cast<ForwardIterator*>(arg);
|
||||
ForwardIterator* fiter = static_cast<ForwardIterator*>(arg);
|
||||
ASSERT_TRUE(!file_iters_deleted ||
|
||||
fiter->TEST_CheckDeletedIters(&deleted_iters, &num_iters));
|
||||
});
|
||||
|
||||
+55
-66
@@ -2517,7 +2517,7 @@ TEST_F(DBTest, SnapshotFiles) {
|
||||
}
|
||||
|
||||
// release file snapshot
|
||||
ASSERT_OK(dbfull()->EnableFileDeletions(/*force*/ false));
|
||||
ASSERT_OK(dbfull()->EnableFileDeletions());
|
||||
// overwrite one key, this key should not appear in the snapshot
|
||||
std::vector<std::string> extras;
|
||||
for (unsigned int i = 0; i < 1; i++) {
|
||||
@@ -2747,7 +2747,7 @@ struct MTThread {
|
||||
};
|
||||
|
||||
static void MTThreadBody(void* arg) {
|
||||
MTThread* t = reinterpret_cast<MTThread*>(arg);
|
||||
MTThread* t = static_cast<MTThread*>(arg);
|
||||
int id = t->id;
|
||||
DB* db = t->state->test->db_;
|
||||
int counter = 0;
|
||||
@@ -2932,7 +2932,7 @@ struct GCThread {
|
||||
};
|
||||
|
||||
static void GCThreadBody(void* arg) {
|
||||
GCThread* t = reinterpret_cast<GCThread*>(arg);
|
||||
GCThread* t = static_cast<GCThread*>(arg);
|
||||
int id = t->id;
|
||||
DB* db = t->db;
|
||||
WriteOptions wo;
|
||||
@@ -3098,28 +3098,28 @@ class ModelDB : public DB {
|
||||
}
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& /*options*/, ColumnFamilyHandle* /*cf*/,
|
||||
const Slice& key, PinnableSlice* /*value*/) override {
|
||||
const Slice& key, PinnableSlice* /*value*/,
|
||||
std::string* /*timestamp*/) override {
|
||||
return Status::NotSupported(key);
|
||||
}
|
||||
|
||||
using DB::GetMergeOperands;
|
||||
virtual Status GetMergeOperands(
|
||||
const ReadOptions& /*options*/, ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& key, PinnableSlice* /*slice*/,
|
||||
GetMergeOperandsOptions* /*merge_operands_options*/,
|
||||
int* /*number_of_operands*/) override {
|
||||
Status GetMergeOperands(const ReadOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& key, PinnableSlice* /*slice*/,
|
||||
GetMergeOperandsOptions* /*merge_operands_options*/,
|
||||
int* /*number_of_operands*/) override {
|
||||
return Status::NotSupported(key);
|
||||
}
|
||||
|
||||
using DB::MultiGet;
|
||||
std::vector<Status> MultiGet(
|
||||
const ReadOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_family*/,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* /*values*/) override {
|
||||
std::vector<Status> s(keys.size(),
|
||||
Status::NotSupported("Not implemented."));
|
||||
return s;
|
||||
void MultiGet(const ReadOptions& /*options*/, const size_t num_keys,
|
||||
ColumnFamilyHandle** /*column_families*/, const Slice* /*keys*/,
|
||||
PinnableSlice* /*values*/, std::string* /*timestamps*/,
|
||||
Status* statuses, const bool /*sorted_input*/) override {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = Status::NotSupported("Not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
@@ -3137,7 +3137,7 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
@@ -3152,9 +3152,9 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
@@ -3190,7 +3190,7 @@ class ModelDB : public DB {
|
||||
return new ModelIter(saved, true);
|
||||
} else {
|
||||
const KVMap* snapshot_state =
|
||||
&(reinterpret_cast<const ModelSnapshot*>(options.snapshot)->map_);
|
||||
&(static_cast<const ModelSnapshot*>(options.snapshot)->map_);
|
||||
return new ModelIter(snapshot_state, false);
|
||||
}
|
||||
}
|
||||
@@ -3206,7 +3206,7 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override {
|
||||
delete reinterpret_cast<const ModelSnapshot*>(snapshot);
|
||||
delete static_cast<const ModelSnapshot*>(snapshot);
|
||||
}
|
||||
|
||||
Status Write(const WriteOptions& /*options*/, WriteBatch* batch) override {
|
||||
@@ -3308,7 +3308,7 @@ class ModelDB : public DB {
|
||||
|
||||
void DisableManualCompaction() override {}
|
||||
|
||||
virtual Status WaitForCompact(
|
||||
Status WaitForCompact(
|
||||
const WaitForCompactOptions& /* wait_for_compact_options */) override {
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -3354,7 +3354,7 @@ class ModelDB : public DB {
|
||||
|
||||
Status DisableFileDeletions() override { return Status::OK(); }
|
||||
|
||||
Status EnableFileDeletions(bool /*force*/) override { return Status::OK(); }
|
||||
Status EnableFileDeletions() override { return Status::OK(); }
|
||||
|
||||
Status GetLiveFiles(std::vector<std::string>&, uint64_t* /*size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
@@ -3381,8 +3381,7 @@ class ModelDB : public DB {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status GetCreationTimeOfOldestFile(
|
||||
uint64_t* /*creation_time*/) override {
|
||||
Status GetCreationTimeOfOldestFile(uint64_t* /*creation_time*/) override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
@@ -4670,6 +4669,13 @@ void VerifyOperationCount(Env* env, ThreadStatus::OperationType op_type,
|
||||
op_count++;
|
||||
}
|
||||
}
|
||||
if (op_count != expected_count) {
|
||||
for (const auto& thread : thread_list) {
|
||||
fprintf(stderr, "thread id: %" PRIu64 ", thread status: %s\n",
|
||||
thread.thread_id,
|
||||
thread.GetOperationName(thread.operation_type).c_str());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(op_count, expected_count);
|
||||
}
|
||||
} // anonymous namespace
|
||||
@@ -5241,7 +5247,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
std::atomic<int> num_no(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
if (compaction->output_level() == 4) {
|
||||
ASSERT_TRUE(compaction->output_compression() == kLZ4Compression);
|
||||
num_lz4.fetch_add(1);
|
||||
@@ -5249,7 +5255,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:output_compression", [&](void* arg) {
|
||||
auto* compression = reinterpret_cast<CompressionType*>(arg);
|
||||
auto* compression = static_cast<CompressionType*>(arg);
|
||||
ASSERT_TRUE(*compression == kNoCompression);
|
||||
num_no.fetch_add(1);
|
||||
});
|
||||
@@ -5283,7 +5289,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
num_no.store(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
if (compaction->output_level() == 4 && compaction->start_level() == 3) {
|
||||
ASSERT_TRUE(compaction->output_compression() == kZlibCompression);
|
||||
num_zlib.fetch_add(1);
|
||||
@@ -5294,7 +5300,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:output_compression", [&](void* arg) {
|
||||
auto* compression = reinterpret_cast<CompressionType*>(arg);
|
||||
auto* compression = static_cast<CompressionType*>(arg);
|
||||
ASSERT_TRUE(*compression == kNoCompression);
|
||||
num_no.fetch_add(1);
|
||||
});
|
||||
@@ -5807,13 +5813,6 @@ TEST_F(DBTest, DynamicMiscOptions) {
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[1],
|
||||
&mutable_cf_options));
|
||||
ASSERT_TRUE(mutable_cf_options.report_bg_io_stats);
|
||||
ASSERT_TRUE(mutable_cf_options.check_flush_compaction_key_order);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
handles_[1], {{"check_flush_compaction_key_order", "false"}}));
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[1],
|
||||
&mutable_cf_options));
|
||||
ASSERT_FALSE(mutable_cf_options.check_flush_compaction_key_order);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, L0L1L2AndUpHitCounter) {
|
||||
@@ -6193,7 +6192,7 @@ TEST_F(DBTest, SuggestCompactRangeTest) {
|
||||
return "CompactionFilterFactoryGetContext";
|
||||
}
|
||||
static bool IsManual(CompactionFilterFactory* compaction_filter_factory) {
|
||||
return reinterpret_cast<CompactionFilterFactoryGetContext*>(
|
||||
return static_cast<CompactionFilterFactoryGetContext*>(
|
||||
compaction_filter_factory)
|
||||
->saved_context.is_manual_compaction;
|
||||
}
|
||||
@@ -7076,9 +7075,8 @@ TEST_F(DBTest, PinnableSliceAndRowCache) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(Get("foo"), "bar");
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
PinnableSlice pin_slice;
|
||||
@@ -7086,13 +7084,11 @@ TEST_F(DBTest, PinnableSliceAndRowCache) {
|
||||
ASSERT_EQ(pin_slice.ToString(), "bar");
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(), 0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ReusePinnableSlice) {
|
||||
@@ -7105,9 +7101,8 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(Get("foo"), "bar");
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
PinnableSlice pin_slice;
|
||||
@@ -7117,13 +7112,11 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(), 0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
std::vector<Slice> multiget_keys;
|
||||
@@ -7142,13 +7135,11 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(), 0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
std::vector<ColumnFamilyHandle*> multiget_cfs;
|
||||
@@ -7169,13 +7160,11 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(), 0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
@@ -7334,7 +7323,7 @@ TEST_F(DBTest, CreationTimeOfOldestFile) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* arg) {
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
TableProperties* props = static_cast<TableProperties*>(arg);
|
||||
if (set_file_creation_time_to_zero) {
|
||||
if (idx == 0) {
|
||||
props->file_creation_time = 0;
|
||||
|
||||
+8
-10
@@ -2330,7 +2330,7 @@ TEST_F(DBTest2, MaxCompactionBytesTest) {
|
||||
}
|
||||
|
||||
static void UniqueIdCallback(void* arg) {
|
||||
int* result = reinterpret_cast<int*>(arg);
|
||||
int* result = static_cast<int*>(arg);
|
||||
if (*result == -1) {
|
||||
*result = 0;
|
||||
}
|
||||
@@ -4161,7 +4161,7 @@ TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
ASSERT_OK(env_->FileExists(LogFileName(dbname_, log_file->LogNumber())));
|
||||
}
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/false));
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
@@ -4242,7 +4242,7 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
|
||||
TraceExecutionResultHandler() = default;
|
||||
~TraceExecutionResultHandler() override = default;
|
||||
|
||||
virtual Status Handle(const StatusOnlyTraceExecutionResult& result) override {
|
||||
Status Handle(const StatusOnlyTraceExecutionResult& result) override {
|
||||
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
|
||||
return Status::InvalidArgument("Invalid timestamps.");
|
||||
}
|
||||
@@ -4260,8 +4260,7 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status Handle(
|
||||
const SingleValueTraceExecutionResult& result) override {
|
||||
Status Handle(const SingleValueTraceExecutionResult& result) override {
|
||||
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
|
||||
return Status::InvalidArgument("Invalid timestamps.");
|
||||
}
|
||||
@@ -4279,8 +4278,7 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status Handle(
|
||||
const MultiValuesTraceExecutionResult& result) override {
|
||||
Status Handle(const MultiValuesTraceExecutionResult& result) override {
|
||||
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
|
||||
return Status::InvalidArgument("Invalid timestamps.");
|
||||
}
|
||||
@@ -4300,7 +4298,7 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status Handle(const IteratorTraceExecutionResult& result) override {
|
||||
Status Handle(const IteratorTraceExecutionResult& result) override {
|
||||
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
|
||||
return Status::InvalidArgument("Invalid timestamps.");
|
||||
}
|
||||
@@ -6474,7 +6472,7 @@ class RenameCurrentTest : public DBTestBase,
|
||||
void SetupSyncPoints() {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [&](void* arg) {
|
||||
Status* s = reinterpret_cast<Status*>(arg);
|
||||
Status* s = static_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected IO error.");
|
||||
});
|
||||
@@ -7176,7 +7174,7 @@ TEST_F(DBTest2, PointInTimeRecoveryWithIOErrorWhileReadingWal) {
|
||||
"LogReader::ReadMore:AfterReadFile", [&](void* arg) {
|
||||
if (should_inject_error) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*reinterpret_cast<Status*>(arg) = Status::IOError("Injected IOError");
|
||||
*static_cast<Status*>(arg) = Status::IOError("Injected IOError");
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
+20
-20
@@ -438,15 +438,15 @@ class SpecialEnv : public EnvWrapper {
|
||||
: target_(std::move(target)),
|
||||
counter_(counter),
|
||||
bytes_read_(bytes_read) {}
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
counter_->Increment();
|
||||
Status s = target_->Read(offset, n, result, scratch);
|
||||
*bytes_read_ += result->size();
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual Status Prefetch(uint64_t offset, size_t n) override {
|
||||
Status Prefetch(uint64_t offset, size_t n) override {
|
||||
Status s = target_->Prefetch(offset, n);
|
||||
*bytes_read_ += n;
|
||||
return s;
|
||||
@@ -465,8 +465,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
: 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 {
|
||||
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");
|
||||
@@ -474,7 +474,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
return target_->Read(offset, n, result, scratch);
|
||||
}
|
||||
|
||||
virtual Status Prefetch(uint64_t offset, size_t n) override {
|
||||
Status Prefetch(uint64_t offset, size_t n) override {
|
||||
return target_->Prefetch(offset, n);
|
||||
}
|
||||
|
||||
@@ -502,19 +502,19 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual Status NewSequentialFile(const std::string& f,
|
||||
std::unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
Status NewSequentialFile(const std::string& f,
|
||||
std::unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
class CountingFile : public SequentialFile {
|
||||
public:
|
||||
CountingFile(std::unique_ptr<SequentialFile>&& target,
|
||||
anon::AtomicCounter* counter)
|
||||
: target_(std::move(target)), counter_(counter) {}
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
counter_->Increment();
|
||||
return target_->Read(n, result, scratch);
|
||||
}
|
||||
virtual Status Skip(uint64_t n) override { return target_->Skip(n); }
|
||||
Status Skip(uint64_t n) override { return target_->Skip(n); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<SequentialFile> target_;
|
||||
@@ -528,7 +528,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual void SleepForMicroseconds(int micros) override {
|
||||
void SleepForMicroseconds(int micros) override {
|
||||
sleep_counter_.Increment();
|
||||
if (no_slowdown_ || time_elapse_only_sleep_) {
|
||||
addon_microseconds_.fetch_add(micros);
|
||||
@@ -550,7 +550,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
addon_microseconds_.fetch_add(seconds * 1000000);
|
||||
}
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* unix_time) override {
|
||||
Status GetCurrentTime(int64_t* unix_time) override {
|
||||
Status s;
|
||||
if (time_elapse_only_sleep_) {
|
||||
*unix_time = maybe_starting_time_;
|
||||
@@ -564,22 +564,22 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual uint64_t NowCPUNanos() override {
|
||||
uint64_t NowCPUNanos() override {
|
||||
now_cpu_count_.fetch_add(1);
|
||||
return target()->NowCPUNanos();
|
||||
}
|
||||
|
||||
virtual uint64_t NowNanos() override {
|
||||
uint64_t NowNanos() override {
|
||||
return (time_elapse_only_sleep_ ? 0 : target()->NowNanos()) +
|
||||
addon_microseconds_.load() * 1000;
|
||||
}
|
||||
|
||||
virtual uint64_t NowMicros() override {
|
||||
uint64_t NowMicros() override {
|
||||
return (time_elapse_only_sleep_ ? 0 : target()->NowMicros()) +
|
||||
addon_microseconds_.load();
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(const std::string& fname) override {
|
||||
Status DeleteFile(const std::string& fname) override {
|
||||
delete_count_.fetch_add(1);
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
@@ -882,8 +882,8 @@ class FlushCounterListener : public EventListener {
|
||||
// "corrupted", "corrupted_try_merge", or "corrupted_must_merge".
|
||||
class TestPutOperator : public MergeOperator {
|
||||
public:
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
static const std::map<std::string, MergeOperator::OpFailureScope>
|
||||
bad_operand_to_op_failure_scope = {
|
||||
{"corrupted", MergeOperator::OpFailureScope::kDefault},
|
||||
@@ -914,7 +914,7 @@ class TestPutOperator : public MergeOperator {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "TestPutOperator"; }
|
||||
const char* Name() const override { return "TestPutOperator"; }
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -2187,7 +2187,7 @@ TEST_F(DBTestUniversalCompaction2, PeriodicCompaction) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickPeriodicCompaction:Return",
|
||||
[&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kPeriodicCompaction);
|
||||
@@ -2258,7 +2258,7 @@ TEST_F(DBTestUniversalCompaction2, PeriodicCompactionOffpeak) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickPeriodicCompaction:Return",
|
||||
[&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kPeriodicCompaction);
|
||||
|
||||
+3
-3
@@ -1195,7 +1195,7 @@ TEST_F(DBWALTest, DISABLED_FullPurgePreservesLogPendingReuse) {
|
||||
ROCKSDB_NAMESPACE::port::Thread thread([&]() {
|
||||
TEST_SYNC_POINT(
|
||||
"DBWALTest::FullPurgePreservesLogPendingReuse:PreFullPurge");
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/true));
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
TEST_SYNC_POINT(
|
||||
"DBWALTest::FullPurgePreservesLogPendingReuse:PostFullPurge");
|
||||
});
|
||||
@@ -2451,7 +2451,7 @@ TEST_F(DBWALTest, TruncateLastLogAfterRecoverWALEmpty) {
|
||||
"DBImpl::DeleteObsoleteFileImpl::BeforeDeletion"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PosixWritableFile::Close",
|
||||
[](void* arg) { *(reinterpret_cast<size_t*>(arg)) = 0; });
|
||||
[](void* arg) { *(static_cast<size_t*>(arg)) = 0; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
// Preallocate space for the empty log file. This could happen if WAL data
|
||||
// was buffered in memory and the process crashed.
|
||||
@@ -2495,7 +2495,7 @@ TEST_F(DBWALTest, ReadOnlyRecoveryNoTruncate) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"PosixWritableFile::Close", [&](void* arg) {
|
||||
if (!enable_truncate) {
|
||||
*(reinterpret_cast<size_t*>(arg)) = 0;
|
||||
*(static_cast<size_t*>(arg)) = 0;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -2610,10 +2610,10 @@ TEST_F(DataVisibilityTest, MultiGetWithoutSnapshot) {
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::MultiGet:AfterGetSeqNum1",
|
||||
{"DBImpl::MultiCFSnapshot:AfterGetSeqNum1",
|
||||
"DataVisibilityTest::MultiGetWithoutSnapshot:BeforePut"},
|
||||
{"DataVisibilityTest::MultiGetWithoutSnapshot:AfterPut",
|
||||
"DBImpl::MultiGet:AfterGetSeqNum2"},
|
||||
"DBImpl::MultiCFSnapshot:AfterGetSeqNum2"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
port::Thread writer_thread([this]() {
|
||||
@@ -3490,7 +3490,7 @@ TEST_F(UpdateFullHistoryTsLowTest, ConcurrentUpdate) {
|
||||
VersionEdit* version_edit;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::IncreaseFullHistoryTsLowImpl:BeforeEdit",
|
||||
[&](void* arg) { version_edit = reinterpret_cast<VersionEdit*>(arg); });
|
||||
[&](void* arg) { version_edit = static_cast<VersionEdit*>(arg); });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:BeforeWriterWaiting",
|
||||
[&](void* /*arg*/) { version_edit->SetFullHistoryTsLow(higher_ts_low); });
|
||||
@@ -3515,7 +3515,7 @@ class HandleFileBoundariesTest
|
||||
: DBBasicTestWithTimestampBase("/handle_file_boundaries") {}
|
||||
};
|
||||
|
||||
TEST_P(HandleFileBoundariesTest, ConfigurePersistUdt) {
|
||||
TEST_P(HandleFileBoundariesTest, ConfigurePersistUdtWithPut) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
// Write a timestamp that is not the min timestamp to help test the behavior
|
||||
@@ -3539,7 +3539,7 @@ TEST_P(HandleFileBoundariesTest, ConfigurePersistUdt) {
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), largest_ukey_without_ts, write_ts, "val2"));
|
||||
|
||||
// Create a L0 SST file and its record is added to the Manfiest.
|
||||
// Create a L0 SST file and its record is added to the Manifest.
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
|
||||
@@ -3571,6 +3571,61 @@ TEST_P(HandleFileBoundariesTest, ConfigurePersistUdt) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(HandleFileBoundariesTest, ConfigurePersistUdtWithRangeDelete) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
// Write a timestamp that is not the min/max timestamp to help test the
|
||||
// behavior of flag `persist_user_defined_timestamps`.
|
||||
std::string write_ts;
|
||||
std::string min_ts;
|
||||
std::string max_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
PutFixed64(&min_ts, 0);
|
||||
PutFixed64(&max_ts, std::numeric_limits<uint64_t>::max());
|
||||
std::string smallest_ukey_without_ts = "bar";
|
||||
std::string largest_ukey_without_ts = "foo";
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
bool persist_udt = test::ShouldPersistUDT(GetParam());
|
||||
options.persist_user_defined_timestamps = persist_udt;
|
||||
if (!persist_udt) {
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
}
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
smallest_ukey_without_ts, largest_ukey_without_ts,
|
||||
write_ts));
|
||||
|
||||
// Create a L0 SST file and its record is added to the Manifest.
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
|
||||
options.create_if_missing = false;
|
||||
// Reopen the DB and process manifest file.
|
||||
Reopen(options);
|
||||
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
|
||||
&level_to_files);
|
||||
ASSERT_GT(level_to_files.size(), 1);
|
||||
// L0 only has one SST file.
|
||||
ASSERT_EQ(level_to_files[0].size(), 1);
|
||||
auto file_meta = level_to_files[0][0];
|
||||
if (persist_udt) {
|
||||
ASSERT_EQ(smallest_ukey_without_ts + write_ts,
|
||||
file_meta.smallest.user_key());
|
||||
} else {
|
||||
ASSERT_EQ(smallest_ukey_without_ts + min_ts, file_meta.smallest.user_key());
|
||||
}
|
||||
// When right file boundary comes from range deletion, it uses max timestamp
|
||||
// and a range deletion sentinel that uses the max sequence number to mark the
|
||||
// end key exclusive. This is regardless of whether timestamp is persisted.
|
||||
ASSERT_EQ(largest_ukey_without_ts + max_ts, file_meta.largest.user_key());
|
||||
auto largest_footer = ExtractInternalKeyFooter(file_meta.largest.Encode());
|
||||
ASSERT_EQ(largest_footer, kRangeTombstoneSentinel);
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ConfigurePersistUdt, HandleFileBoundariesTest,
|
||||
::testing::Values(
|
||||
@@ -4006,42 +4061,80 @@ TEST_F(DBBasicTestWithTimestamp,
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, DeleteRangeBaiscReadAndIterate) {
|
||||
class DeleteRangeWithTimestampTableOptions
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<BlockBasedTableOptions::IndexType,
|
||||
test::UserDefinedTimestampTestMode>> {
|
||||
public:
|
||||
explicit DeleteRangeWithTimestampTableOptions()
|
||||
: DBBasicTestWithTimestampBase(
|
||||
"delete_range_with_timestamp_table_options") {}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Timestamp, DeleteRangeWithTimestampTableOptions,
|
||||
testing::Combine(
|
||||
testing::Values(
|
||||
BlockBasedTableOptions::IndexType::kBinarySearch,
|
||||
BlockBasedTableOptions::IndexType::kHashSearch,
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch,
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey),
|
||||
testing::Values(
|
||||
test::UserDefinedTimestampTestMode::kNormal,
|
||||
test::UserDefinedTimestampTestMode::kStripUserDefinedTimestamp)));
|
||||
|
||||
TEST_P(DeleteRangeWithTimestampTableOptions, BasicReadAndIterate) {
|
||||
const int kNum = 200, kRangeBegin = 50, kRangeEnd = 150, kNumPerFile = 25;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
options.compression = kNoCompression;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.index_type = GetParam();
|
||||
bbto.index_type = std::get<0>(GetParam());
|
||||
bbto.block_size = 100;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
bool persist_udt = test::ShouldPersistUDT(std::get<1>(GetParam()));
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
options.persist_user_defined_timestamps = persist_udt;
|
||||
// UDT in memtables only not compatible with concurrent memtable writes.
|
||||
options.allow_concurrent_memtable_write = persist_udt;
|
||||
options.memtable_factory.reset(test::NewSpecialSkipListFactory(kNumPerFile));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Write half of the keys before the tombstone and half after the tombstone.
|
||||
// Only covered keys (i.e., within the range and older than the tombstone)
|
||||
// should be deleted.
|
||||
std::string full_history_ts_low;
|
||||
int cutoff_ts = 0;
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, i);
|
||||
if (i == kNum / 2) {
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key1(kRangeBegin), Key1(kRangeEnd),
|
||||
Timestamp(i, 0)));
|
||||
Key1(kRangeBegin), Key1(kRangeEnd), write_ts));
|
||||
}
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key1(i), Timestamp(i, 0),
|
||||
"val" + std::to_string(i)));
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key1(i), write_ts, "val" + std::to_string(i)));
|
||||
if (i == kNum - kNumPerFile) {
|
||||
if (!persist_udt) {
|
||||
// When UDTs are not persisted, mark the timestamps in the Memtables as
|
||||
// all expired so the followed flush can go through.
|
||||
cutoff_ts = i + 1;
|
||||
PutFixed64(&full_history_ts_low, cutoff_ts);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(db_->DefaultColumnFamily(),
|
||||
full_history_ts_low));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
std::string read_ts = Timestamp(kNum, 0);
|
||||
std::string read_ts;
|
||||
PutFixed64(&read_ts, kNum);
|
||||
Slice read_ts_slice = read_ts;
|
||||
read_opts.timestamp = &read_ts_slice;
|
||||
{
|
||||
@@ -4076,33 +4169,43 @@ TEST_P(DBBasicTestWithTimestampTableOptions, DeleteRangeBaiscReadAndIterate) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(-1, expected);
|
||||
|
||||
read_ts = Timestamp(0, 0);
|
||||
read_ts_slice = read_ts;
|
||||
read_opts.timestamp = &read_ts_slice;
|
||||
iter.reset(db_->NewIterator(read_opts));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), Key1(0));
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
// Cannot read below the cutoff timestamp when timestamps are not persisted.
|
||||
if (persist_udt) {
|
||||
read_ts.clear();
|
||||
PutFixed64(&read_ts, 0);
|
||||
read_ts_slice = read_ts;
|
||||
read_opts.timestamp = &read_ts_slice;
|
||||
iter.reset(db_->NewIterator(read_opts));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), Key1(0));
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
}
|
||||
|
||||
read_ts = Timestamp(kNum, 0);
|
||||
read_ts.clear();
|
||||
PutFixed64(&read_ts, kNum);
|
||||
read_ts_slice = read_ts;
|
||||
read_opts.timestamp = &read_ts_slice;
|
||||
std::string value, timestamp;
|
||||
Status s;
|
||||
std::string expected_ts;
|
||||
int int_expected_ts;
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
s = db_->Get(read_opts, Key1(i), &value, ×tamp);
|
||||
if (i >= kRangeBegin && i < kNum / 2) {
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
ASSERT_EQ(timestamp, Timestamp(kNum / 2, 0));
|
||||
int_expected_ts = (persist_udt || kNum / 2 >= cutoff_ts) ? kNum / 2 : 0;
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(value, "val" + std::to_string(i));
|
||||
ASSERT_EQ(timestamp, Timestamp(i, 0));
|
||||
int_expected_ts = (persist_udt || i >= cutoff_ts) ? i : 0;
|
||||
}
|
||||
expected_ts.clear();
|
||||
PutFixed64(&expected_ts, int_expected_ts);
|
||||
ASSERT_EQ(timestamp, expected_ts);
|
||||
}
|
||||
|
||||
size_t batch_size = kNum;
|
||||
@@ -4121,11 +4224,41 @@ TEST_P(DBBasicTestWithTimestampTableOptions, DeleteRangeBaiscReadAndIterate) {
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
if (i >= kRangeBegin && i < kNum / 2) {
|
||||
ASSERT_TRUE(statuses[i].IsNotFound());
|
||||
ASSERT_EQ(timestamps[i], Timestamp(kNum / 2, 0));
|
||||
int_expected_ts = (persist_udt || kNum / 2 >= cutoff_ts) ? kNum / 2 : 0;
|
||||
} else {
|
||||
ASSERT_OK(statuses[i]);
|
||||
ASSERT_EQ(values[i], "val" + std::to_string(i));
|
||||
ASSERT_EQ(timestamps[i], Timestamp(i, 0));
|
||||
int_expected_ts = (persist_udt || i >= cutoff_ts) ? i : 0;
|
||||
}
|
||||
expected_ts.clear();
|
||||
PutFixed64(&expected_ts, int_expected_ts);
|
||||
ASSERT_EQ(timestamps[i], expected_ts);
|
||||
}
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
if (!persist_udt) {
|
||||
// Mark everything expired so manual compaction can go through
|
||||
full_history_ts_low.clear();
|
||||
PutFixed64(&full_history_ts_low, kNum);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(db_->DefaultColumnFamily(),
|
||||
full_history_ts_low));
|
||||
}
|
||||
Slice compaction_ts = full_history_ts_low;
|
||||
cro.full_history_ts_low = &compaction_ts;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
for (int i = kRangeBegin; i < kNum / 2; ++i) {
|
||||
s = db_->Get(read_opts, Key1(i), &value, ×tamp);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
if (persist_udt) {
|
||||
expected_ts.clear();
|
||||
PutFixed64(&expected_ts, kNum / 2);
|
||||
ASSERT_EQ(timestamp, expected_ts);
|
||||
} else {
|
||||
// When timestamps are not persisted, data in SST files all logically have
|
||||
// min timestamp. A compaction to the last level will drop the range
|
||||
// tombstone.
|
||||
ASSERT_TRUE(timestamp.empty());
|
||||
}
|
||||
}
|
||||
Close();
|
||||
@@ -4604,4 +4737,4 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ TEST_F(TimestampCompatibleCompactionTest, UserKeyCrossFileBoundary) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
const auto* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
const auto* compaction = static_cast<Compaction*>(arg);
|
||||
ASSERT_NE(nullptr, compaction);
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(1, compaction->num_input_levels());
|
||||
|
||||
@@ -119,7 +119,7 @@ TEST_P(DBWriteBufferManagerTest, SharedWriteBufferAcrossCFs2) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::WriteStall::Wait", [&](void* arg) {
|
||||
InstrumentedMutexLock lock(&mutex);
|
||||
WriteThread::Writer* w = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
WriteThread::Writer* w = static_cast<WriteThread::Writer*>(arg);
|
||||
w_set.insert(w);
|
||||
// Allow the flush to continue if all writer threads are blocked.
|
||||
if (w_set.size() == (unsigned long)num_writers) {
|
||||
@@ -368,7 +368,7 @@ TEST_P(DBWriteBufferManagerTest, SharedWriteBufferLimitAcrossDB1) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::WriteStall::Wait", [&](void* arg) {
|
||||
WriteThread::Writer* w = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
WriteThread::Writer* w = static_cast<WriteThread::Writer*>(arg);
|
||||
{
|
||||
InstrumentedMutexLock lock(&mutex);
|
||||
w_set.insert(w);
|
||||
@@ -511,7 +511,7 @@ TEST_P(DBWriteBufferManagerTest, MixedSlowDownOptionsSingleDB) {
|
||||
"WriteThread::WriteStall::Wait", [&](void* arg) {
|
||||
{
|
||||
InstrumentedMutexLock lock(&mutex);
|
||||
WriteThread::Writer* w = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
WriteThread::Writer* w = static_cast<WriteThread::Writer*>(arg);
|
||||
w_slowdown_set.insert(w);
|
||||
// Allow the flush continue if all writer threads are blocked.
|
||||
if (w_slowdown_set.size() + (unsigned long)w_no_slowdown.load(
|
||||
@@ -674,7 +674,7 @@ TEST_P(DBWriteBufferManagerTest, MixedSlowDownOptionsMultipleDB) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::WriteStall::Wait", [&](void* arg) {
|
||||
WriteThread::Writer* w = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
WriteThread::Writer* w = static_cast<WriteThread::Writer*>(arg);
|
||||
InstrumentedMutexLock lock(&mutex);
|
||||
w_slowdown_set.insert(w);
|
||||
// Allow the flush continue if all writer threads are blocked.
|
||||
|
||||
+4
-4
@@ -286,7 +286,7 @@ TEST_P(DBWriteTest, IOErrorOnWALWritePropagateToWriteThreadFollower) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::JoinBatchGroup:Wait", [&](void* arg) {
|
||||
ready_count++;
|
||||
auto* w = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
auto* w = static_cast<WriteThread::Writer*>(arg);
|
||||
if (w->state == WriteThread::STATE_GROUP_LEADER) {
|
||||
leader_count++;
|
||||
while (ready_count < kNumThreads) {
|
||||
@@ -384,7 +384,7 @@ TEST_F(DBWriteTestUnparameterized, PipelinedWriteRace) {
|
||||
second_write_in_progress = true;
|
||||
return;
|
||||
}
|
||||
auto* w = reinterpret_cast<WriteThread::Writer*>(arg);
|
||||
auto* w = static_cast<WriteThread::Writer*>(arg);
|
||||
if (w->state == WriteThread::STATE_GROUP_LEADER) {
|
||||
active_writers++;
|
||||
if (leader.load() == nullptr) {
|
||||
@@ -404,7 +404,7 @@ TEST_F(DBWriteTestUnparameterized, PipelinedWriteRace) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::ExitAsBatchGroupLeader:Start", [&](void* arg) {
|
||||
auto* wg = reinterpret_cast<WriteThread::WriteGroup*>(arg);
|
||||
auto* wg = static_cast<WriteThread::WriteGroup*>(arg);
|
||||
if (wg->leader == leader && !finished_WAL_write) {
|
||||
finished_WAL_write = true;
|
||||
while (active_writers.load() < 3) {
|
||||
@@ -416,7 +416,7 @@ TEST_F(DBWriteTestUnparameterized, PipelinedWriteRace) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::ExitAsBatchGroupLeader:AfterCompleteWriters",
|
||||
[&](void* arg) {
|
||||
auto* wg = reinterpret_cast<WriteThread::WriteGroup*>(arg);
|
||||
auto* wg = static_cast<WriteThread::WriteGroup*>(arg);
|
||||
if (wg->leader == leader) {
|
||||
while (!second_write_in_progress.load()) {
|
||||
// wait for the old follower thread to start the next write
|
||||
|
||||
@@ -66,6 +66,13 @@ void AppendInternalKeyWithDifferentTimestamp(std::string* result,
|
||||
PutFixed64(result, PackSequenceAndType(key.sequence, key.type));
|
||||
}
|
||||
|
||||
void AppendUserKeyWithDifferentTimestamp(std::string* result, const Slice& key,
|
||||
const Slice& ts) {
|
||||
assert(key.size() >= ts.size());
|
||||
result->append(key.data(), key.size() - ts.size());
|
||||
result->append(ts.data(), ts.size());
|
||||
}
|
||||
|
||||
void AppendInternalKeyFooter(std::string* result, SequenceNumber s,
|
||||
ValueType t) {
|
||||
PutFixed64(result, PackSequenceAndType(s, t));
|
||||
@@ -110,6 +117,7 @@ void AppendUserKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
void PadInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(ts_sz > 0);
|
||||
assert(key.size() >= kNumInternalBytes);
|
||||
size_t user_key_size = key.size() - kNumInternalBytes;
|
||||
result->reserve(key.size() + ts_sz);
|
||||
result->append(key.data(), user_key_size);
|
||||
@@ -117,6 +125,17 @@ void PadInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
result->append(key.data() + user_key_size, kNumInternalBytes);
|
||||
}
|
||||
|
||||
void PadInternalKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(ts_sz > 0);
|
||||
assert(key.size() >= kNumInternalBytes);
|
||||
size_t user_key_size = key.size() - kNumInternalBytes;
|
||||
result->reserve(key.size() + ts_sz);
|
||||
result->append(key.data(), user_key_size);
|
||||
result->append(std::string(ts_sz, '\xff'));
|
||||
result->append(key.data() + user_key_size, kNumInternalBytes);
|
||||
}
|
||||
|
||||
void StripTimestampFromInternalKey(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(key.size() >= ts_sz + kNumInternalBytes);
|
||||
|
||||
+70
-16
@@ -11,6 +11,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -67,9 +68,7 @@ enum ValueType : unsigned char {
|
||||
kTypeDeletionWithTimestamp = 0x14,
|
||||
kTypeCommitXIDAndTimestamp = 0x15, // WAL only
|
||||
kTypeWideColumnEntity = 0x16,
|
||||
kTypeColumnFamilyWideColumnEntity = 0x17, // WAL only
|
||||
kTypeValuePreferredSeqno = 0x18, // Value with a unix write time
|
||||
kTypeColumnFamilyValuePreferredSeqno = 0x19, // WAL only
|
||||
kTypeColumnFamilyWideColumnEntity = 0x17, // WAL only
|
||||
kTypeMaxValid, // Should be after the last valid type, only used for
|
||||
// validation
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
@@ -79,12 +78,37 @@ enum ValueType : unsigned char {
|
||||
extern const ValueType kValueTypeForSeek;
|
||||
extern const ValueType kValueTypeForSeekForPrev;
|
||||
|
||||
// A range of user keys used internally by RocksDB. Also see `Range` used by
|
||||
// public APIs.
|
||||
struct UserKeyRange {
|
||||
// In case of user_defined timestamp, if enabled, `start` and `limit` should
|
||||
// include user_defined timestamps.
|
||||
Slice start;
|
||||
Slice limit;
|
||||
|
||||
UserKeyRange() = default;
|
||||
UserKeyRange(const Slice& s, const Slice& l) : start(s), limit(l) {}
|
||||
};
|
||||
|
||||
// A range of user keys used internally by RocksDB. Also see `RangePtr` used by
|
||||
// public APIs.
|
||||
struct UserKeyRangePtr {
|
||||
// In case of user_defined timestamp, if enabled, `start` and `limit` should
|
||||
// point to key with timestamp part.
|
||||
// An optional range start, if missing, indicating a start before all keys.
|
||||
std::optional<Slice> start;
|
||||
// An optional range end, if missing, indicating an end after all keys.
|
||||
std::optional<Slice> limit;
|
||||
|
||||
UserKeyRangePtr(const std::optional<Slice>& s, const std::optional<Slice>& l)
|
||||
: start(s), limit(l) {}
|
||||
};
|
||||
|
||||
// Checks whether a type is an inline value type
|
||||
// (i.e. a type used in memtable skiplist and sst file datablock).
|
||||
inline bool IsValueType(ValueType t) {
|
||||
return t <= kTypeMerge || kTypeSingleDeletion == t || kTypeBlobIndex == t ||
|
||||
kTypeDeletionWithTimestamp == t || kTypeWideColumnEntity == t ||
|
||||
kTypeValuePreferredSeqno == t;
|
||||
kTypeDeletionWithTimestamp == t || kTypeWideColumnEntity == t;
|
||||
}
|
||||
|
||||
// Checks whether a type is from user operation
|
||||
@@ -168,6 +192,9 @@ inline void UnPackSequenceAndType(uint64_t packed, uint64_t* seq,
|
||||
// assert(IsExtendedValueType(*t));
|
||||
}
|
||||
|
||||
const uint64_t kRangeTombstoneSentinel =
|
||||
PackSequenceAndType(kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
|
||||
EntryType GetEntryType(ValueType value_type);
|
||||
|
||||
// Append the serialization of "key" to *result.
|
||||
@@ -187,6 +214,15 @@ void AppendInternalKeyWithDifferentTimestamp(std::string* result,
|
||||
const ParsedInternalKey& key,
|
||||
const Slice& ts);
|
||||
|
||||
// Append the user key to *result, replacing the original timestamp with
|
||||
// argument ts.
|
||||
//
|
||||
// input [user key]: <user_provided_key | original_ts>
|
||||
// output before: empty
|
||||
// output after: <user_provided_key | ts>
|
||||
void AppendUserKeyWithDifferentTimestamp(std::string* result, const Slice& key,
|
||||
const Slice& ts);
|
||||
|
||||
// Serialized internal key consists of user key followed by footer.
|
||||
// This function appends the footer to *result, assuming that *result already
|
||||
// contains the user key at the end.
|
||||
@@ -240,6 +276,16 @@ void AppendUserKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
void PadInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// `key` is an internal key containing a user key without timestamp. Create a
|
||||
// new key in *result by padding a max timestamp of size `ts_sz` to the user key
|
||||
// and copying the remaining internal key bytes.
|
||||
//
|
||||
// input [internal key]: <user_provided_key | seqno + type>
|
||||
// output before: empty
|
||||
// output after: <user_provided_key | max_ts | seqno + type>
|
||||
void PadInternalKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// `key` is an internal key containing a user key with timestamp of size
|
||||
// `ts_sz`. Create a new internal key in *result by stripping the timestamp from
|
||||
// the user key and copying the remaining internal key bytes.
|
||||
@@ -824,19 +870,19 @@ class InternalKeySliceTransform : public SliceTransform {
|
||||
explicit InternalKeySliceTransform(const SliceTransform* transform)
|
||||
: transform_(transform) {}
|
||||
|
||||
virtual const char* Name() const override { return transform_->Name(); }
|
||||
const char* Name() const override { return transform_->Name(); }
|
||||
|
||||
virtual Slice Transform(const Slice& src) const override {
|
||||
Slice Transform(const Slice& src) const override {
|
||||
auto user_key = ExtractUserKey(src);
|
||||
return transform_->Transform(user_key);
|
||||
}
|
||||
|
||||
virtual bool InDomain(const Slice& src) const override {
|
||||
bool InDomain(const Slice& src) const override {
|
||||
auto user_key = ExtractUserKey(src);
|
||||
return transform_->InDomain(user_key);
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& dst) const override {
|
||||
bool InRange(const Slice& dst) const override {
|
||||
auto user_key = ExtractUserKey(dst);
|
||||
return transform_->InRange(user_key);
|
||||
}
|
||||
@@ -886,17 +932,25 @@ struct RangeTombstone {
|
||||
// User-defined timestamp is enabled, `sk` and `ek` should be user key
|
||||
// with timestamp, `ts` will replace the timestamps in `sk` and
|
||||
// `ek`.
|
||||
RangeTombstone(Slice sk, Slice ek, SequenceNumber sn, Slice ts)
|
||||
: seq_(sn), ts_(ts) {
|
||||
assert(!ts.empty());
|
||||
// When `logical_strip_timestamp` is true, the timestamps in `sk` and `ek`
|
||||
// will be replaced with min timestamp.
|
||||
RangeTombstone(Slice sk, Slice ek, SequenceNumber sn, Slice ts,
|
||||
bool logical_strip_timestamp)
|
||||
: seq_(sn) {
|
||||
const size_t ts_sz = ts.size();
|
||||
assert(ts_sz > 0);
|
||||
pinned_start_key_.reserve(sk.size());
|
||||
pinned_start_key_.append(sk.data(), sk.size() - ts.size());
|
||||
pinned_start_key_.append(ts.data(), ts.size());
|
||||
pinned_end_key_.reserve(ek.size());
|
||||
pinned_end_key_.append(ek.data(), ek.size() - ts.size());
|
||||
pinned_end_key_.append(ts.data(), ts.size());
|
||||
if (logical_strip_timestamp) {
|
||||
AppendUserKeyWithMinTimestamp(&pinned_start_key_, sk, ts_sz);
|
||||
AppendUserKeyWithMinTimestamp(&pinned_end_key_, ek, ts_sz);
|
||||
} else {
|
||||
AppendUserKeyWithDifferentTimestamp(&pinned_start_key_, sk, ts);
|
||||
AppendUserKeyWithDifferentTimestamp(&pinned_end_key_, ek, ts);
|
||||
}
|
||||
start_key_ = pinned_start_key_;
|
||||
end_key_ = pinned_end_key_;
|
||||
ts_ = Slice(pinned_start_key_.data() + sk.size() - ts_sz, ts_sz);
|
||||
}
|
||||
|
||||
RangeTombstone(ParsedInternalKey parsed_key, Slice value) {
|
||||
|
||||
@@ -125,7 +125,7 @@ class DeleteFileTest : public DBTestBase {
|
||||
}
|
||||
|
||||
static void DoSleep(void* arg) {
|
||||
auto test = reinterpret_cast<DeleteFileTest*>(arg);
|
||||
auto test = static_cast<DeleteFileTest*>(arg);
|
||||
test->env_->SleepForMicroseconds(2 * 1000 * 1000);
|
||||
}
|
||||
|
||||
|
||||
+63
-82
@@ -234,7 +234,7 @@ void ErrorHandler::CancelErrorRecovery() {
|
||||
// recovery gets scheduled at that point
|
||||
auto_recovery_ = false;
|
||||
SstFileManagerImpl* sfm =
|
||||
reinterpret_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
if (sfm) {
|
||||
// This may or may not cancel a pending recovery
|
||||
db_mutex_->Unlock();
|
||||
@@ -277,10 +277,6 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
return kOkStatus;
|
||||
}
|
||||
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set regular background error\n");
|
||||
|
||||
@@ -365,25 +361,28 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
|
||||
// This is the main function for looking at IO related error during the
|
||||
// background operations. The main logic is:
|
||||
// 1) File scope IO error is treated as retryable IO error in the write
|
||||
// path. In RocksDB, If a file has write IO error and it is at file scope,
|
||||
// RocksDB never write to the same file again. RocksDB will create a new
|
||||
// file and rewrite the whole content. Thus, it is retryable.
|
||||
// File scope IO error is treated as retryable IO error in the write path. In
|
||||
// RocksDB, If a file has write IO error and it is at file scope, RocksDB never
|
||||
// write to the same file again. RocksDB will create a new file and rewrite the
|
||||
// whole content. Thus, it is retryable.
|
||||
// There are three main categories of error handling:
|
||||
// 1) if the error is caused by data loss, the error is mapped to
|
||||
// unrecoverable error. Application/user must take action to handle
|
||||
// this situation (File scope case is excluded).
|
||||
// 2) if the error is a Retryable IO error (i.e., it is a file scope IO error,
|
||||
// or its retryable flag is set and not a data loss error), auto resume
|
||||
// will be called and the auto resume can be controlled by resume count
|
||||
// and resume interval options. There are three sub-cases:
|
||||
// or its retryable flag is set and not a data loss error), auto resume (
|
||||
// DBImpl::ResumeImpl) may be called and the auto resume can be controlled
|
||||
// by resume count and resume interval options. There are three sub-cases:
|
||||
// a) if the error happens during compaction, it is mapped to a soft error.
|
||||
// the compaction thread will reschedule a new compaction.
|
||||
// the compaction thread will reschedule a new compaction. This doesn't
|
||||
// call auto resume.
|
||||
// b) if the error happens during flush and also WAL is empty, it is mapped
|
||||
// to a soft error. Note that, it includes the case that IO error happens
|
||||
// in SST or manifest write during flush.
|
||||
// c) all other errors are mapped to hard error.
|
||||
// 3) for other cases, SetBGError(const Status& bg_err, BackgroundErrorReason
|
||||
// reason) will be called to handle other error cases.
|
||||
// in SST or manifest write during flush. Auto resume will be called.
|
||||
// c) all other errors are mapped to hard error. Auto resume will be called.
|
||||
// 3) for other cases, HandleKnownErrors(const Status& bg_err,
|
||||
// BackgroundErrorReason reason) will be called to handle other error cases
|
||||
// such as delegating to SstFileManager to handle no space error.
|
||||
const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
@@ -396,6 +395,9 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "Background IO error %s",
|
||||
bg_io_err.ToString().c_str());
|
||||
|
||||
RecordStats({ERROR_HANDLER_BG_ERROR_COUNT, ERROR_HANDLER_BG_IO_ERROR_COUNT},
|
||||
{} /* int_histograms */);
|
||||
|
||||
Status new_bg_io_err = bg_io_err;
|
||||
DBRecoverContext context;
|
||||
if (bg_io_err.GetScope() != IOStatus::IOErrorScope::kIOErrorScopeFile &&
|
||||
@@ -405,14 +407,6 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
bool auto_recovery = false;
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kUnrecoverableError);
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Set background IO error as unrecoverable error\n");
|
||||
@@ -436,18 +430,9 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&new_bg_io_err, db_mutex_,
|
||||
&auto_recovery);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
|
||||
RecordStats({ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT},
|
||||
{} /* int_histograms */);
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set background retryable IO error\n");
|
||||
if (BackgroundErrorReason::kCompaction == reason) {
|
||||
@@ -456,17 +441,18 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
// this case
|
||||
// TODO: a better way to set or clean the retryable IO error which
|
||||
// happens during compaction SST file write.
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_AUTORESUME_COUNT);
|
||||
}
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_COUNT}, {} /* int_histograms */);
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Compaction will schedule by itself to resume\n");
|
||||
// Not used in this code path.
|
||||
new_bg_io_err.PermitUncheckedError();
|
||||
return bg_error_;
|
||||
} else if (BackgroundErrorReason::kFlushNoWAL == reason ||
|
||||
BackgroundErrorReason::kManifestWriteNoWAL == reason) {
|
||||
}
|
||||
|
||||
Status::Severity severity;
|
||||
if (BackgroundErrorReason::kFlushNoWAL == reason ||
|
||||
BackgroundErrorReason::kManifestWriteNoWAL == 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
|
||||
@@ -474,24 +460,17 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
// 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);
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
severity = Status::Severity::kSoftError;
|
||||
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);
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
recover_context_ = context;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
severity = Status::Severity::kHardError;
|
||||
}
|
||||
Status bg_err(new_bg_io_err, severity);
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
recover_context_ = context;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
} else {
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
return HandleKnownErrors(new_bg_io_err, reason);
|
||||
}
|
||||
}
|
||||
@@ -555,7 +534,7 @@ Status ErrorHandler::OverrideNoSpaceError(const Status& bg_error,
|
||||
|
||||
void ErrorHandler::RecoverFromNoSpace() {
|
||||
SstFileManagerImpl* sfm =
|
||||
reinterpret_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
|
||||
// Inform SFM of the error, so it can kick-off the recovery
|
||||
if (sfm) {
|
||||
@@ -593,7 +572,7 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
|
||||
// If its a manual recovery and there's a background recovery in progress
|
||||
// return busy status
|
||||
if (recovery_in_prog_) {
|
||||
return Status::Busy();
|
||||
return Status::Busy("Recovery already in progress");
|
||||
}
|
||||
recovery_in_prog_ = true;
|
||||
|
||||
@@ -659,9 +638,7 @@ const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
db_mutex_->AssertHeld();
|
||||
return bg_error_;
|
||||
}
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_AUTORESUME_COUNT);
|
||||
}
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_COUNT}, {} /* int_histograms */);
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Call StartRecoverFromRetryableBGIOError to resume\n");
|
||||
@@ -729,19 +706,15 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
recovery_error_ = IOStatus::OK();
|
||||
retry_count++;
|
||||
Status s = db_->ResumeImpl(context);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT);
|
||||
}
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT},
|
||||
{} /* int_histograms */);
|
||||
if (s.IsShutdownInProgress() ||
|
||||
bg_error_.severity() >= Status::Severity::kFatalError) {
|
||||
// If DB shutdown in progress or the error severity is higher than
|
||||
// Hard Error, stop auto resume and returns.
|
||||
recovery_in_prog_ = false;
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
RecordStats({} /* ticker_types */,
|
||||
{{ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count}});
|
||||
EventHelpers::NotifyOnErrorRecoveryEnd(db_options_.listeners, bg_error_,
|
||||
bg_error_, db_mutex_);
|
||||
return;
|
||||
@@ -764,21 +737,15 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
// recover from the retryable IO error and no other BG errors. Clean
|
||||
// the bg_error and notify user.
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:RecoverSuccess");
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT);
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT},
|
||||
{{ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count}});
|
||||
return;
|
||||
} else {
|
||||
// In this case: 1) recovery_error_ is more serious or not retryable
|
||||
// 2) other error happens. The auto recovery stops.
|
||||
recovery_in_prog_ = false;
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
RecordStats({} /* ticker_types */,
|
||||
{{ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count}});
|
||||
EventHelpers::NotifyOnErrorRecoveryEnd(
|
||||
db_options_.listeners, bg_error_,
|
||||
!recovery_error_.ok() ? recovery_error_ : s, db_mutex_);
|
||||
@@ -792,10 +759,8 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
db_options_.listeners, bg_error_,
|
||||
Status::Aborted("Exceeded resume retry count"), db_mutex_);
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:LoopOut");
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
RecordStats({} /* ticker_types */,
|
||||
{{ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count}});
|
||||
}
|
||||
|
||||
void ErrorHandler::CheckAndSetRecoveryAndBGError(const Status& bg_err) {
|
||||
@@ -827,4 +792,20 @@ void ErrorHandler::EndAutoRecovery() {
|
||||
TEST_SYNC_POINT("PostEndAutoRecovery");
|
||||
}
|
||||
|
||||
void ErrorHandler::RecordStats(
|
||||
const std::vector<Tickers>& ticker_types,
|
||||
const std::vector<std::tuple<Histograms, uint64_t>>& int_histograms) {
|
||||
if (bg_error_stats_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (const auto& ticker_type : ticker_types) {
|
||||
RecordTick(bg_error_stats_.get(), ticker_type);
|
||||
}
|
||||
|
||||
for (const auto& hist : int_histograms) {
|
||||
RecordInHistogram(bg_error_stats_.get(), std::get<0>(hist),
|
||||
std::get<1>(hist));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+8
-1
@@ -94,6 +94,10 @@ class ErrorHandler {
|
||||
void ClearFilesToQuarantine();
|
||||
|
||||
private:
|
||||
void RecordStats(
|
||||
const std::vector<Tickers>& ticker_types,
|
||||
const std::vector<std::tuple<Histograms, uint64_t>>& int_histograms);
|
||||
|
||||
DBImpl* db_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
Status bg_error_;
|
||||
@@ -107,7 +111,10 @@ class ErrorHandler {
|
||||
std::unique_ptr<port::Thread> recovery_thread_;
|
||||
|
||||
InstrumentedMutex* db_mutex_;
|
||||
// A flag indicating whether automatic recovery from errors is enabled
|
||||
// A flag indicating whether automatic recovery from errors is enabled. Auto
|
||||
// recovery applies for delegating to SstFileManager to handle no space type
|
||||
// of errors. This flag doesn't control the auto resume behavior to recover
|
||||
// from retryable IO errors.
|
||||
bool auto_recovery_;
|
||||
bool recovery_in_prog_;
|
||||
// A flag to indicate that for the soft error, we should not allow any
|
||||
|
||||
+1067
File diff suppressed because it is too large.
Load diff
@@ -1095,7 +1095,7 @@ TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
||||
size_t total_fadvised_bytes = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileWriter::Rep::InvalidatePageCache", [&](void* arg) {
|
||||
size_t fadvise_size = *(reinterpret_cast<size_t*>(arg));
|
||||
size_t fadvise_size = *(static_cast<size_t*>(arg));
|
||||
total_fadvised_bytes += fadvise_size;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -1632,7 +1632,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithBadBlockChecksum) {
|
||||
bool change_checksum_called = false;
|
||||
const auto& change_checksum = [&](void* arg) {
|
||||
if (!change_checksum_called) {
|
||||
char* buf = reinterpret_cast<char*>(arg);
|
||||
char* buf = static_cast<char*>(arg);
|
||||
assert(nullptr != buf);
|
||||
buf[0] ^= 0x1;
|
||||
change_checksum_called = true;
|
||||
@@ -1729,10 +1729,10 @@ TEST_P(ExternalSSTFileBasicTest, IngestExternalFileWithCorruptedPropsBlock) {
|
||||
uint64_t props_block_offset = 0;
|
||||
size_t props_block_size = 0;
|
||||
const auto& get_props_block_offset = [&](void* arg) {
|
||||
props_block_offset = *reinterpret_cast<uint64_t*>(arg);
|
||||
props_block_offset = *static_cast<uint64_t*>(arg);
|
||||
};
|
||||
const auto& get_props_block_size = [&](void* arg) {
|
||||
props_block_size = *reinterpret_cast<uint64_t*>(arg);
|
||||
props_block_size = *static_cast<uint64_t*>(arg);
|
||||
};
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
@@ -97,7 +97,15 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
}
|
||||
|
||||
if (ingestion_options_.ingest_behind && files_overlap_) {
|
||||
return Status::NotSupported("Files have overlapping ranges");
|
||||
return Status::NotSupported(
|
||||
"Files with overlapping ranges cannot be ingested with ingestion "
|
||||
"behind mode.");
|
||||
}
|
||||
|
||||
if (ucmp->timestamp_size() > 0 && files_overlap_) {
|
||||
return Status::NotSupported(
|
||||
"Files with overlapping ranges cannot be ingested to column "
|
||||
"family with user-defined timestamp enabled.");
|
||||
}
|
||||
|
||||
// Copy/Move external files into DB
|
||||
@@ -142,7 +150,7 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
// Original file is on a different FS, use copy instead of hard linking.
|
||||
f.copy_file = true;
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"Triy to link file %s but it's not supported : %s",
|
||||
"Tried to link file %s but it's not supported : %s",
|
||||
path_outside_db.c_str(), status.ToString().c_str());
|
||||
}
|
||||
} else {
|
||||
@@ -188,7 +196,7 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
// Generate and check the sst file checksum. Note that, if
|
||||
// IngestExternalFileOptions::write_global_seqno is true, we will not update
|
||||
// the checksum information in the files_to_ingests_ here, since the file is
|
||||
// upadted with the new global_seqno. After global_seqno is updated, DB will
|
||||
// updated with the new global_seqno. After global_seqno is updated, DB will
|
||||
// generate the new checksum and store it in the Manifest. In all other cases
|
||||
// if ingestion_options_.write_global_seqno == true and
|
||||
// verify_file_checksum is false, we only check the checksum function name.
|
||||
@@ -299,8 +307,7 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
}
|
||||
}
|
||||
} else if (files_checksums.size() != files_checksum_func_names.size() ||
|
||||
(files_checksums.size() == files_checksum_func_names.size() &&
|
||||
files_checksums.size() != 0)) {
|
||||
files_checksums.size() != 0) {
|
||||
// The checksum or checksum function name vector are not both empty
|
||||
// and they are incomplete.
|
||||
status = Status::InvalidArgument(
|
||||
@@ -316,59 +323,32 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The following is duplicated with Cleanup().
|
||||
if (!status.ok()) {
|
||||
IOOptions io_opts;
|
||||
// We failed, remove all files that we copied into the db
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
if (f.internal_file_path.empty()) {
|
||||
continue;
|
||||
}
|
||||
Status s = fs_->DeleteFile(f.internal_file_path, io_opts, nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed,
|
||||
SuperVersion* super_version) {
|
||||
autovector<Range> ranges;
|
||||
autovector<std::string> keys;
|
||||
size_t ts_sz = cfd_->user_comparator()->timestamp_size();
|
||||
if (ts_sz) {
|
||||
// Check all ranges [begin, end] inclusively. Add maximum
|
||||
// timestamp to include all `begin` keys, and add minimal timestamp to
|
||||
// include all `end` keys.
|
||||
for (const IngestedFileInfo& file_to_ingest : files_to_ingest_) {
|
||||
std::string begin_str;
|
||||
std::string end_str;
|
||||
AppendUserKeyWithMaxTimestamp(
|
||||
&begin_str, file_to_ingest.smallest_internal_key.user_key(), ts_sz);
|
||||
AppendUserKeyWithMinTimestamp(
|
||||
&end_str, file_to_ingest.largest_internal_key.user_key(), ts_sz);
|
||||
keys.emplace_back(std::move(begin_str));
|
||||
keys.emplace_back(std::move(end_str));
|
||||
}
|
||||
for (size_t i = 0; i < files_to_ingest_.size(); ++i) {
|
||||
ranges.emplace_back(keys[2 * i], keys[2 * i + 1]);
|
||||
}
|
||||
} else {
|
||||
for (const IngestedFileInfo& file_to_ingest : files_to_ingest_) {
|
||||
ranges.emplace_back(file_to_ingest.smallest_internal_key.user_key(),
|
||||
file_to_ingest.largest_internal_key.user_key());
|
||||
}
|
||||
size_t n = files_to_ingest_.size();
|
||||
autovector<UserKeyRange> ranges;
|
||||
ranges.reserve(n);
|
||||
for (const IngestedFileInfo& file_to_ingest : files_to_ingest_) {
|
||||
ranges.emplace_back(file_to_ingest.smallest_internal_key.user_key(),
|
||||
file_to_ingest.largest_internal_key.user_key());
|
||||
}
|
||||
Status status = cfd_->RangesOverlapWithMemtables(
|
||||
ranges, super_version, db_options_.allow_data_in_errors, flush_needed);
|
||||
if (status.ok() && *flush_needed &&
|
||||
!ingestion_options_.allow_blocking_flush) {
|
||||
status = Status::InvalidArgument("External file requires flush");
|
||||
if (status.ok() && *flush_needed) {
|
||||
if (!ingestion_options_.allow_blocking_flush) {
|
||||
status = Status::InvalidArgument("External file requires flush");
|
||||
}
|
||||
auto ucmp = cfd_->user_comparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
status = Status::InvalidArgument(
|
||||
"Column family enables user-defined timestamps, please make "
|
||||
"sure the key range (without timestamp) of external file does not "
|
||||
"overlap with key range in the memtables.");
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -387,7 +367,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
return status;
|
||||
}
|
||||
if (need_flush) {
|
||||
return Status::TryAgain();
|
||||
return Status::TryAgain("need_flush");
|
||||
}
|
||||
assert(status.ok() && need_flush == false);
|
||||
#endif
|
||||
@@ -431,26 +411,26 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (smallest_parsed.sequence == 0) {
|
||||
if (smallest_parsed.sequence == 0 && assigned_seqno != 0) {
|
||||
UpdateInternalKey(f.smallest_internal_key.rep(), assigned_seqno,
|
||||
smallest_parsed.type);
|
||||
}
|
||||
if (largest_parsed.sequence == 0) {
|
||||
if (largest_parsed.sequence == 0 && assigned_seqno != 0) {
|
||||
UpdateInternalKey(f.largest_internal_key.rep(), assigned_seqno,
|
||||
largest_parsed.type);
|
||||
}
|
||||
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, assigned_seqno);
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Run",
|
||||
&assigned_seqno);
|
||||
if (assigned_seqno > last_seqno) {
|
||||
assert(assigned_seqno == last_seqno + 1);
|
||||
last_seqno = assigned_seqno;
|
||||
++consumed_seqno_count_;
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Run",
|
||||
&assigned_seqno);
|
||||
assert(assigned_seqno == 0 || assigned_seqno == last_seqno + 1);
|
||||
if (assigned_seqno > last_seqno) {
|
||||
last_seqno = assigned_seqno;
|
||||
++consumed_seqno_count_;
|
||||
}
|
||||
|
||||
status = GenerateChecksumForIngestedFile(&f);
|
||||
if (!status.ok()) {
|
||||
@@ -631,17 +611,7 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
if (!status.ok()) {
|
||||
// We failed to add the files to the database
|
||||
// remove all the files we copied
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
if (f.internal_file_path.empty()) {
|
||||
continue;
|
||||
}
|
||||
Status s = fs_->DeleteFile(f.internal_file_path, io_opts, nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
DeleteInternalFiles();
|
||||
consumed_seqno_count_ = 0;
|
||||
files_overlap_ = false;
|
||||
} else if (status.ok() && ingestion_options_.move_files) {
|
||||
@@ -659,6 +629,21 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalSstFileIngestionJob::DeleteInternalFiles() {
|
||||
IOOptions io_opts;
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
if (f.internal_file_path.empty()) {
|
||||
continue;
|
||||
}
|
||||
Status s = fs_->DeleteFile(f.internal_file_path, io_opts, nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
const std::string& external_file, uint64_t new_file_number,
|
||||
IngestedFileInfo* file_to_ingest, SuperVersion* sv) {
|
||||
@@ -886,16 +871,19 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
SequenceNumber* assigned_seqno) {
|
||||
Status status;
|
||||
*assigned_seqno = 0;
|
||||
if (force_global_seqno) {
|
||||
auto ucmp = cfd_->user_comparator();
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
if (force_global_seqno || files_overlap_) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
if (compaction_style == kCompactionStyleUniversal || files_overlap_) {
|
||||
// If files overlap, we have to ingest them at level 0.
|
||||
if (files_overlap_) {
|
||||
assert(ts_sz == 0);
|
||||
file_to_ingest->picked_level = 0;
|
||||
if (ingestion_options_.fail_if_not_bottommost_level) {
|
||||
status = Status::TryAgain(
|
||||
"Files cannot be ingested to Lmax. Please make sure key range of "
|
||||
"Lmax does not overlap with files to ingest.");
|
||||
return status;
|
||||
}
|
||||
file_to_ingest->picked_level = 0;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -947,12 +935,6 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
target_level = lvl;
|
||||
}
|
||||
}
|
||||
// If files overlap, we have to ingest them at level 0 and assign the newest
|
||||
// sequence number
|
||||
if (files_overlap_) {
|
||||
target_level = 0;
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
}
|
||||
|
||||
if (ingestion_options_.fail_if_not_bottommost_level &&
|
||||
target_level < cfd_->NumberLevels() - 1) {
|
||||
@@ -967,8 +949,16 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
"ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile",
|
||||
&overlap_with_db);
|
||||
file_to_ingest->picked_level = target_level;
|
||||
if (overlap_with_db && *assigned_seqno == 0) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
if (overlap_with_db) {
|
||||
if (ts_sz > 0) {
|
||||
status = Status::InvalidArgument(
|
||||
"Column family enables user-defined timestamps, please make sure the "
|
||||
"key range (without timestamp) of external file does not overlap "
|
||||
"with key range (without timestamp) in the db");
|
||||
}
|
||||
if (*assigned_seqno == 0) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -1007,7 +997,8 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
return Status::OK();
|
||||
} else if (!ingestion_options_.allow_global_seqno) {
|
||||
return Status::InvalidArgument("Global seqno is required, but disabled");
|
||||
} else if (file_to_ingest->global_seqno_offset == 0) {
|
||||
} else if (ingestion_options_.write_global_seqno &&
|
||||
file_to_ingest->global_seqno_offset == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Trying to set global seqno for a file that don't have a global seqno "
|
||||
"field");
|
||||
@@ -1079,8 +1070,8 @@ IOStatus ExternalSstFileIngestionJob::GenerateChecksumForIngestedFile(
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
file_to_ingest->file_checksum = file_checksum;
|
||||
file_to_ingest->file_checksum_func_name = file_checksum_func_name;
|
||||
file_to_ingest->file_checksum = std::move(file_checksum);
|
||||
file_to_ingest->file_checksum_func_name = std::move(file_checksum_func_name);
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ struct IngestedFileInfo {
|
||||
uint64_t num_entries;
|
||||
// total number of range deletions in external file
|
||||
uint64_t num_range_deletions;
|
||||
// Id of column family this file shoule be ingested into
|
||||
// Id of column family this file should be ingested into
|
||||
uint32_t cf_id;
|
||||
// TableProperties read from external file
|
||||
TableProperties table_properties;
|
||||
@@ -102,16 +102,7 @@ class ExternalSstFileIngestionJob {
|
||||
assert(directories != nullptr);
|
||||
}
|
||||
|
||||
~ExternalSstFileIngestionJob() {
|
||||
for (const auto& c : file_ingesting_compactions_) {
|
||||
cfd_->compaction_picker()->UnregisterCompaction(c);
|
||||
delete c;
|
||||
}
|
||||
|
||||
for (const auto& f : compaction_input_metdatas_) {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
~ExternalSstFileIngestionJob() { UnregisterRange(); }
|
||||
|
||||
// Prepare the job by copying external files into the DB.
|
||||
Status Prepare(const std::vector<std::string>& external_files_paths,
|
||||
@@ -156,7 +147,7 @@ class ExternalSstFileIngestionJob {
|
||||
return files_to_ingest_;
|
||||
}
|
||||
|
||||
// How many sequence numbers did we consume as part of the ingest job?
|
||||
// How many sequence numbers did we consume as part of the ingestion job?
|
||||
int ConsumedSequenceNumbersCount() const { return consumed_seqno_count_; }
|
||||
|
||||
private:
|
||||
@@ -203,6 +194,9 @@ class ExternalSstFileIngestionJob {
|
||||
// compactions.
|
||||
void CreateEquivalentFileIngestingCompactions();
|
||||
|
||||
// Remove all the internal files created, called when ingestion job fails.
|
||||
void DeleteInternalFiles();
|
||||
|
||||
SystemClock* clock_;
|
||||
FileSystemPtr fs_;
|
||||
VersionSet* versions_;
|
||||
|
||||
@@ -1714,9 +1714,8 @@ TEST_F(ExternalSSTFileTest, WithUnorderedWrite) {
|
||||
{"DBImpl::WaitForPendingWrites:BeforeBlock",
|
||||
"DBImpl::WriteImpl:BeforeUnorderedWriteMemtable"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::IngestExternalFile:NeedFlush", [&](void* need_flush) {
|
||||
ASSERT_TRUE(*reinterpret_cast<bool*>(need_flush));
|
||||
});
|
||||
"DBImpl::IngestExternalFile:NeedFlush",
|
||||
[&](void* need_flush) { ASSERT_TRUE(*static_cast<bool*>(need_flush)); });
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.unordered_write = true;
|
||||
@@ -1848,6 +1847,92 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedUniversal) {
|
||||
bool write_global_seqno = std::get<0>(GetParam());
|
||||
bool verify_checksums_before_ingest = std::get<1>(GetParam());
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 5;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
std::vector<std::pair<std::string, std::string>> file_data;
|
||||
std::map<std::string, std::string> true_data;
|
||||
|
||||
// Write 200 -> 250 into the bottommost level
|
||||
for (int i = 200; i <= 250; i++) {
|
||||
ASSERT_OK(Put(Key(i), "bottommost"));
|
||||
true_data[Key(i)] = "bottommost";
|
||||
}
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
// Take a snapshot to enforce global sequence number.
|
||||
const Snapshot* snap = db_->GetSnapshot();
|
||||
|
||||
// Insert 100 -> 200 into the memtable
|
||||
for (int i = 100; i <= 200; i++) {
|
||||
ASSERT_OK(Put(Key(i), "memtable"));
|
||||
true_data[Key(i)] = "memtable";
|
||||
}
|
||||
|
||||
// Insert 0 -> 20 using AddFile
|
||||
file_data.clear();
|
||||
for (int i = 0; i <= 20; i++) {
|
||||
file_data.emplace_back(Key(i), "L4");
|
||||
}
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, file_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, false, &true_data));
|
||||
|
||||
// This file don't overlap with anything in the DB, will go to L4
|
||||
ASSERT_EQ("0,0,0,0,2", FilesPerLevel());
|
||||
|
||||
// Insert 80 -> 130 using AddFile
|
||||
file_data.clear();
|
||||
for (int i = 80; i <= 130; i++) {
|
||||
file_data.emplace_back(Key(i), "L0");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, file_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, false, &true_data));
|
||||
|
||||
// This file overlap with the memtable, so it will flush it and add
|
||||
// it self to L0
|
||||
ASSERT_EQ("2,0,0,0,2", FilesPerLevel());
|
||||
|
||||
// Insert 30 -> 50 using AddFile
|
||||
file_data.clear();
|
||||
for (int i = 30; i <= 50; i++) {
|
||||
file_data.emplace_back(Key(i), "L4");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, file_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, false, &true_data));
|
||||
|
||||
// This file don't overlap with anything in the DB and fit in L4 as well
|
||||
ASSERT_EQ("2,0,0,0,3", FilesPerLevel());
|
||||
|
||||
// Insert 10 -> 40 using AddFile
|
||||
file_data.clear();
|
||||
for (int i = 10; i <= 40; i++) {
|
||||
file_data.emplace_back(Key(i), "L3");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, file_data, -1, true, write_global_seqno,
|
||||
verify_checksums_before_ingest, false, false, &true_data));
|
||||
|
||||
// This file overlap with files in L4, we will ingest it into the last
|
||||
// non-overlapping and non-empty level, in this case, it's L0.
|
||||
ASSERT_EQ("3,0,0,0,3", FilesPerLevel());
|
||||
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
db_->ReleaseSnapshot(snap);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -2978,6 +3063,285 @@ TEST_P(ExternalSSTFileTest,
|
||||
delete iter;
|
||||
}
|
||||
|
||||
class ExternalSSTFileWithTimestampTest : public ExternalSSTFileTest {
|
||||
public:
|
||||
ExternalSSTFileWithTimestampTest() = default;
|
||||
|
||||
static const std::string kValueNotFound;
|
||||
static const std::string kTsNotFound;
|
||||
|
||||
std::string EncodeAsUint64(uint64_t v) {
|
||||
std::string dst;
|
||||
PutFixed64(&dst, v);
|
||||
return dst;
|
||||
}
|
||||
|
||||
Status IngestExternalUDTFile(const std::vector<std::string>& files,
|
||||
bool allow_global_seqno = true) {
|
||||
IngestExternalFileOptions opts;
|
||||
opts.snapshot_consistency = true;
|
||||
opts.allow_global_seqno = allow_global_seqno;
|
||||
return db_->IngestExternalFile(files, opts);
|
||||
}
|
||||
|
||||
void VerifyValueAndTs(const std::string& key,
|
||||
const std::string& read_timestamp,
|
||||
const std::string& expected_value,
|
||||
const std::string& expected_timestamp) {
|
||||
Slice read_ts = read_timestamp;
|
||||
ReadOptions read_options;
|
||||
read_options.timestamp = &read_ts;
|
||||
std::string value;
|
||||
std::string timestamp;
|
||||
Status s = db_->Get(read_options, key, &value, ×tamp);
|
||||
if (s.ok()) {
|
||||
ASSERT_EQ(value, expected_value);
|
||||
ASSERT_EQ(timestamp, expected_timestamp);
|
||||
} else if (s.IsNotFound()) {
|
||||
ASSERT_EQ(kValueNotFound, expected_value);
|
||||
ASSERT_EQ(kTsNotFound, expected_timestamp);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const std::string ExternalSSTFileWithTimestampTest::kValueNotFound =
|
||||
"NOT_FOUND";
|
||||
const std::string ExternalSSTFileWithTimestampTest::kTsNotFound =
|
||||
"NOT_FOUND_TS";
|
||||
|
||||
TEST_F(ExternalSSTFileWithTimestampTest, Basic) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
options.persist_user_defined_timestamps = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Current file size should be 0 after sst_file_writer init and before open
|
||||
// a file.
|
||||
ASSERT_EQ(sst_file_writer.FileSize(), 0);
|
||||
|
||||
// file1.sst [0, 50)
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 0; k < 50; k++) {
|
||||
// write 3 versions of values for each key, write newer version first
|
||||
// they are treated as logically smaller by the comparator.
|
||||
for (int version = 3; version > 0; version--) {
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(k + version),
|
||||
Key(k) + "_val" + std::to_string(version)));
|
||||
}
|
||||
}
|
||||
|
||||
ExternalSstFileInfo file1_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file1_info));
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
ASSERT_NOK(sst_file_writer.Put(Key(100), EncodeAsUint64(1), "bad_val"));
|
||||
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 150);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(0) + EncodeAsUint64(0 + 3));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(49) + EncodeAsUint64(49 + 1));
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
// Add file using file path
|
||||
ASSERT_OK(IngestExternalUDTFile({file1}));
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
|
||||
for (int k = 0; k < 50; k++) {
|
||||
for (int version = 3; version > 0; version--) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(k + version),
|
||||
Key(k) + "_val" + std::to_string(version),
|
||||
EncodeAsUint64(k + version));
|
||||
}
|
||||
}
|
||||
|
||||
// file2.sst [50, 200)
|
||||
// Put [key=k, ts=k, value=k_val] for k in [50, 200)
|
||||
// RangeDelete[start_key=75, end_key=125, ts=100]
|
||||
std::string file2 = sst_files_dir_ + "file2.sst";
|
||||
int range_del_begin = 75, range_del_end = 125, range_del_ts = 100;
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
for (int k = 50; k < 200; k++) {
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(k), Key(k) + "_val"));
|
||||
if (k == range_del_ts) {
|
||||
ASSERT_OK(sst_file_writer.DeleteRange(
|
||||
Key(range_del_begin), Key(range_del_end), EncodeAsUint64(k)));
|
||||
}
|
||||
}
|
||||
|
||||
ExternalSstFileInfo file2_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
|
||||
ASSERT_EQ(file2_info.file_path, file2);
|
||||
ASSERT_EQ(file2_info.num_entries, 150);
|
||||
ASSERT_EQ(file2_info.smallest_key, Key(50) + EncodeAsUint64(50));
|
||||
ASSERT_EQ(file2_info.largest_key, Key(199) + EncodeAsUint64(199));
|
||||
ASSERT_EQ(file2_info.num_range_del_entries, 1);
|
||||
ASSERT_EQ(file2_info.smallest_range_del_key,
|
||||
Key(range_del_begin) + EncodeAsUint64(range_del_ts));
|
||||
ASSERT_EQ(file2_info.largest_range_del_key,
|
||||
Key(range_del_end) + EncodeAsUint64(range_del_ts));
|
||||
// Add file using file path
|
||||
ASSERT_OK(IngestExternalUDTFile({file2}));
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
|
||||
for (int k = 50; k < 200; k++) {
|
||||
if (k < range_del_begin || k >= range_del_end) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(k), Key(k) + "_val",
|
||||
EncodeAsUint64(k));
|
||||
}
|
||||
// else {
|
||||
// // FIXME(yuzhangyu): when range tombstone and point data has the
|
||||
// // same seq, on read path, make range tombstone overrides point
|
||||
// // data if it has a newer user-defined timestamp. This is how
|
||||
// // we determine point data's overriding relationship, so we
|
||||
// // should keep it consistent.
|
||||
// VerifyValueAndTs(Key(k), EncodeAsUint64(k), Key(k) + "_val",
|
||||
// EncodeAsUint64(k));
|
||||
// VerifyValueAndTs(Key(k), EncodeAsUint64(range_del_ts),
|
||||
// kValueNotFound,
|
||||
// kTsNotFound);
|
||||
// }
|
||||
}
|
||||
|
||||
// file3.sst [100, 200), key range overlap with db
|
||||
std::string file3 = sst_files_dir_ + "file3.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file3));
|
||||
for (int k = 100; k < 200; k++) {
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(k + 1), Key(k) + "_val1"));
|
||||
}
|
||||
ExternalSstFileInfo file3_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file3_info));
|
||||
ASSERT_EQ(file3_info.file_path, file3);
|
||||
ASSERT_EQ(file3_info.num_entries, 100);
|
||||
ASSERT_EQ(file3_info.smallest_key, Key(100) + EncodeAsUint64(101));
|
||||
ASSERT_EQ(file3_info.largest_key, Key(199) + EncodeAsUint64(200));
|
||||
|
||||
// Allowing ingesting a file containing overlap key range with the db is
|
||||
// not safe without verifying the overlapped key has a higher timestamp
|
||||
// than what the db contains, so we do not allow this regardless of
|
||||
// whether global sequence number is allowed.
|
||||
ASSERT_NOK(IngestExternalUDTFile({file2}));
|
||||
ASSERT_NOK(IngestExternalUDTFile({file2}, /*allow_global_seqno*/ false));
|
||||
|
||||
// Write [0, 50)
|
||||
// Write to DB newer versions to cover ingested data and move sequence
|
||||
// number forward.
|
||||
for (int k = 0; k < 50; k++) {
|
||||
ASSERT_OK(dbfull()->Put(WriteOptions(), Key(k), EncodeAsUint64(k + 4),
|
||||
Key(k) + "_val" + std::to_string(4)));
|
||||
}
|
||||
|
||||
// Read all 4 versions (3 from ingested, 1 from live writes).
|
||||
for (int k = 0; k < 50; k++) {
|
||||
for (int version = 4; version > 0; version--) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(k + version),
|
||||
Key(k) + "_val" + std::to_string(version),
|
||||
EncodeAsUint64(k + version));
|
||||
}
|
||||
}
|
||||
SequenceNumber seq_num_before_ingestion = db_->GetLatestSequenceNumber();
|
||||
ASSERT_GT(seq_num_before_ingestion, 0U);
|
||||
|
||||
// file4.sst [200, 250)
|
||||
std::string file4 = sst_files_dir_ + "file4.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file4));
|
||||
for (int k = 200; k < 250; k++) {
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(k), Key(k) + "_val"));
|
||||
}
|
||||
|
||||
ExternalSstFileInfo file4_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file4_info));
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
|
||||
ASSERT_EQ(file4_info.file_path, file4);
|
||||
ASSERT_EQ(file4_info.num_entries, 50);
|
||||
ASSERT_EQ(file4_info.smallest_key, Key(200) + EncodeAsUint64(200));
|
||||
ASSERT_EQ(file4_info.largest_key, Key(249) + EncodeAsUint64(249));
|
||||
ASSERT_EQ(file4_info.num_range_del_entries, 0);
|
||||
ASSERT_EQ(file4_info.smallest_range_del_key, "");
|
||||
ASSERT_EQ(file4_info.largest_range_del_key, "");
|
||||
|
||||
ASSERT_OK(IngestExternalUDTFile({file4}));
|
||||
|
||||
// In UDT mode, any external file that can be successfully ingested also
|
||||
// should not overlap with the db. As a result, they can always get the
|
||||
// seq 0 assigned.
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), seq_num_before_ingestion);
|
||||
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction |
|
||||
kRangeDelSkipConfigs));
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileWithTimestampTest, SanityCheck) {
|
||||
Options options = CurrentOptions();
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
options.persist_user_defined_timestamps = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// file1.sst [0, 100)
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), EncodeAsUint64(k), Key(k) + "_val"));
|
||||
}
|
||||
|
||||
ExternalSstFileInfo file1_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file1_info));
|
||||
|
||||
// file2.sst [50, 75)
|
||||
std::string file2 = sst_files_dir_ + "file2.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
for (int k = 50; k < 75; k++) {
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(k + 2), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file2_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
||||
|
||||
// Cannot ingest when files' user key range overlaps. There is no
|
||||
// straightforward way to assign sequence number to the files so that they
|
||||
// meet the user-defined timestamps invariant: for the same user provided key,
|
||||
// the entry with a higher sequence number should not have a smaller
|
||||
// timestamp. In this case: file1 has (key=k, ts=k) for k in [50, 75),
|
||||
// file2 has (key=k, ts=k+2) for k in [50, 75).
|
||||
// The invariant is only met if file2 is ingested after file1. In other cases
|
||||
// when user key ranges are interleaved in files, no order of ingestion can
|
||||
// guarantee this invariant. So we do not allow ingesting files with
|
||||
// overlapping key ranges.
|
||||
ASSERT_TRUE(IngestExternalUDTFile({file1, file2}).IsNotSupported());
|
||||
|
||||
options.allow_ingest_behind = true;
|
||||
DestroyAndReopen(options);
|
||||
IngestExternalFileOptions opts;
|
||||
|
||||
// TODO(yuzhangyu): support ingestion behind for user-defined timestamps?
|
||||
// Ingesting external files with user-defined timestamps requires searching
|
||||
// through the whole lsm tree to make sure there is no key range overlap with
|
||||
// the db. Ingestion behind currently is doing a simply placing it at the
|
||||
// bottom level step without a search, so we don't allow it either.
|
||||
opts.ingest_behind = true;
|
||||
ASSERT_TRUE(db_->IngestExternalFile({file1}, opts).IsNotSupported());
|
||||
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
|
||||
+1
-1
@@ -965,7 +965,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
const WriteOptions write_options(io_priority, Env::IOActivity::kFlush);
|
||||
TableBuilderOptions tboptions(
|
||||
*cfd_->ioptions(), mutable_cf_options_, read_options, write_options,
|
||||
cfd_->internal_comparator(), cfd_->int_tbl_prop_collector_factories(),
|
||||
cfd_->internal_comparator(), cfd_->internal_tbl_prop_coll_factories(),
|
||||
output_compression_, mutable_cf_options_.compression_opts,
|
||||
cfd_->GetID(), cfd_->GetName(), 0 /* level */,
|
||||
false /* is_bottommost */, TableFileCreationReason::kFlush,
|
||||
|
||||
Loaded 100 of 511 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user