mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2732f11849 | ||
|
|
71f9e62906 | ||
|
|
5c2be544f5 | ||
|
|
4db41ec2ff | ||
|
|
aac648c1b6 | ||
|
|
e5907aa96a | ||
|
|
388e9f3564 | ||
|
|
f6d4775ed3 | ||
|
|
c2467b141e | ||
|
|
ef0cadd38f | ||
|
|
9d9014d4d0 | ||
|
|
412b7fd2bd | ||
|
|
8a201ce68a | ||
|
|
a53094983b | ||
|
|
cc20520d5f | ||
|
|
36797a43fe | ||
|
|
7c06a1e503 | ||
|
|
d9e7f6a3b9 | ||
|
|
bf61a59a5b | ||
|
|
ec2c2921e6 |
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' 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 --use_io_uring=0' 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' blackbox_crash_test_with_tiered_storage
|
||||
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
|
||||
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' whitebox_crash_test_with_tiered_storage
|
||||
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
|
||||
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' 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 --use_io_uring=0' 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 build only ======================== #
|
||||
# ========================= MacOS with Tests ======================== #
|
||||
build-macos:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: macos-13
|
||||
@@ -417,10 +417,9 @@ jobs:
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Build
|
||||
- name: Run all tests
|
||||
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
|
||||
@@ -606,4 +605,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_DIRS})
|
||||
include_directories(${ZSTD_INCLUDE_DIR})
|
||||
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_DIRS})
|
||||
include_directories(${NUMA_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS NUMA::NUMA)
|
||||
endif()
|
||||
|
||||
|
||||
+12
-28
@@ -1,41 +1,25 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 9.0.1 (04/11/2024)
|
||||
## 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)
|
||||
### Bug Fixes
|
||||
* Fixed CMake Javadoc and source jar builds
|
||||
* Fixed CMake Javadoc build
|
||||
* Fixed Java `SstFileMetaData` to prevent throwing `java.lang.NoSuchMethodError`
|
||||
|
||||
## 9.0.0 (02/16/2024)
|
||||
### New Features
|
||||
* Provide support for FSBuffer for point lookups. Also added support for scans and compactions that don't go through prefetching.
|
||||
* *Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
|
||||
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
|
||||
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
|
||||
## 8.11.3 (02/27/2024)
|
||||
* Correct CMake Javadoc and source jar 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.2 (02/16/2024)
|
||||
* Update zlib to 1.3.1 for Java builds
|
||||
|
||||
## 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
|
||||
|
||||
+3
-8
@@ -169,26 +169,21 @@ most processors made since roughly 2013.
|
||||
|
||||
* Install the dependencies for RocksDB:
|
||||
|
||||
`pkg_add gmake gflags snappy bzip2 lz4 zstd git bash findutils gnuwatch`
|
||||
pkg_add gmake gflags snappy bzip2 lz4 zstd git jdk bash findutils gnuwatch
|
||||
|
||||
* Build RocksDB from source:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
git clone https://github.com/facebook/rocksdb.git
|
||||
cd rocksdb
|
||||
gmake static_lib
|
||||
```
|
||||
|
||||
* Build RocksJava from source (optional):
|
||||
* In OpenBSD, JDK depends on XWindows system, so please check that you installed OpenBSD with `xbase` package.
|
||||
* Install dependencies : `pkg_add -v jdk%1.8`
|
||||
```bash
|
||||
|
||||
cd rocksdb
|
||||
export JAVA_HOME=/usr/local/jdk-1.8.0
|
||||
export PATH=$PATH:/usr/local/jdk-1.8.0/bin
|
||||
gmake rocksdbjava SHA256_CMD='sha256 -q'
|
||||
```
|
||||
gmake rocksdbjava
|
||||
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define an important pre-processing macros: `IOS_CROSS_COMPILE`.
|
||||
|
||||
@@ -539,8 +539,7 @@ endif
|
||||
|
||||
ifdef USE_CLANG
|
||||
# Used by some teams in Facebook
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator \
|
||||
-Wimplicit-fallthrough -Wreinterpret-base-class -Wundefined-reinterpret-cast
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator -Wimplicit-fallthrough
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
|
||||
@@ -163,6 +163,24 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -ldl"
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if test "$ROCKSDB_USE_IO_URING" -ne 0; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
@@ -596,29 +614,11 @@ 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 -o "$TARGET_OS" = OpenBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
if ! { [ "$TARGET_OS" = FreeBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
# Test whether -Wshorten-64-to-32 is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
|
||||
@@ -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/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
|
||||
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
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/09703139cfc376bd8a82642385a0e97726b28287/1.0.6/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/ff23d17b932725cc1734a14896a8b67c518ba169/1.9.4/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/576397d8b1d9cea7306ad1e454d5e55caaa2ff1c/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/fecac07861cb829f5e60dbeff0503d3272db73c0/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/0bb3f5756788ce26e2e16a1cb2f2af2c59b51abe/master/platform010/f57cc4a
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/5b602edd46fda54cdd7ea45f77dbe4061206e174/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/97cac22a149c2e202917e05d44e87e516b68216f/1.4/platform010/5074a48
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/53953ebc4e3eda85ad6fc3e429ba146035e97b90/2018_U5/platform010/76ebdda
|
||||
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
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/624a2f8f6c93c3c1df8aa4a6255d8202631a6c80/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/39579e8603b48b3540f8b0633f43adf29acccb8b/2.37/centos8-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/cd9cc656d49ecb53797ce4d055e49fde29fd57ff/3.19.0/platform010/76ebdda
|
||||
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
|
||||
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) || true
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1)
|
||||
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
|
||||
echo "Checking format of uncommitted changes..."
|
||||
fi
|
||||
|
||||
@@ -149,9 +149,6 @@ if [ -z "$diffs" ]
|
||||
then
|
||||
echo "Nothing needs to be reformatted!"
|
||||
exit 0
|
||||
elif [ $? -ne 1 ]; then
|
||||
# CLANG_FORMAT_DIFF will exit on 1 while there is suggested changes.
|
||||
exit $?
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
|
||||
@@ -75,8 +75,8 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos8-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/15/platform010/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/12/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 1.2.8 platform010
|
||||
get_lib_base zlib LATEST 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 centos8-native
|
||||
get_lib_base binutils LATEST centos7-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_),
|
||||
static_cast<Cache::Handle*>(h),
|
||||
reinterpret_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 static_cast<const typename Table::HandleImpl*>(handle)->value;
|
||||
return reinterpret_cast<const typename Table::HandleImpl*>(handle)->value;
|
||||
}
|
||||
|
||||
template <class Table>
|
||||
size_t BaseHyperClockCache<Table>::GetCharge(Handle* handle) const {
|
||||
return static_cast<const typename Table::HandleImpl*>(handle)
|
||||
return reinterpret_cast<const typename Table::HandleImpl*>(handle)
|
||||
->GetTotalCharge();
|
||||
}
|
||||
|
||||
template <class Table>
|
||||
const Cache::CacheItemHelper* BaseHyperClockCache<Table>::GetCacheItemHelper(
|
||||
Handle* handle) const {
|
||||
auto h = static_cast<const typename Table::HandleImpl*>(handle);
|
||||
auto h = reinterpret_cast<const typename Table::HandleImpl*>(handle);
|
||||
return h->helper;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -298,7 +298,7 @@ class ClockCacheTest;
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
struct ClockHandleBasicData : public Cache::Handle {
|
||||
struct ClockHandleBasicData {
|
||||
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 =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_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 =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_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 =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_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 =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_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 =
|
||||
static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
std::shared_ptr<Cache> tiered_cache = GetTieredCache();
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(110 << 20));
|
||||
|
||||
Vendored
+6
-5
@@ -339,7 +339,8 @@ void LRUCacheShard::NotifyEvicted(
|
||||
MemoryAllocator* alloc = table_.GetAllocator();
|
||||
for (LRUHandle* entry : evicted_handles) {
|
||||
if (eviction_callback_ &&
|
||||
eviction_callback_(entry->key(), static_cast<Cache::Handle*>(entry),
|
||||
eviction_callback_(entry->key(),
|
||||
reinterpret_cast<Cache::Handle*>(entry),
|
||||
entry->HasHit())) {
|
||||
// Callback took ownership of obj; just free handle
|
||||
free(entry);
|
||||
@@ -505,7 +506,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(), static_cast<Cache::Handle*>(e),
|
||||
eviction_callback_(e->key(), reinterpret_cast<Cache::Handle*>(e),
|
||||
e->HasHit())) {
|
||||
// Callback took ownership of obj; just free handle
|
||||
free(e);
|
||||
@@ -660,18 +661,18 @@ LRUCache::LRUCache(const LRUCacheOptions& opts) : ShardedCache(opts) {
|
||||
}
|
||||
|
||||
Cache::ObjectPtr LRUCache::Value(Handle* handle) {
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
auto h = reinterpret_cast<const LRUHandle*>(handle);
|
||||
return h->value;
|
||||
}
|
||||
|
||||
size_t LRUCache::GetCharge(Handle* handle) const {
|
||||
return static_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
return reinterpret_cast<const LRUHandle*>(handle)->GetCharge(
|
||||
GetShard(0).metadata_charge_policy_);
|
||||
}
|
||||
|
||||
const Cache::CacheItemHelper* LRUCache::GetCacheItemHelper(
|
||||
Handle* handle) const {
|
||||
auto h = static_cast<const LRUHandle*>(handle);
|
||||
auto h = reinterpret_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 : public Cache::Handle {
|
||||
struct LRUHandle {
|
||||
Cache::ObjectPtr value;
|
||||
const Cache::CacheItemHelper* helper;
|
||||
LRUHandle* next_hash;
|
||||
|
||||
Vendored
+3
-2
@@ -47,7 +47,7 @@ class LRUCacheTest : public testing::Test {
|
||||
double low_pri_pool_ratio = 1.0,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex) {
|
||||
DeleteCache();
|
||||
cache_ = static_cast<LRUCacheShard*>(
|
||||
cache_ = reinterpret_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,7 +392,8 @@ class ClockCacheTest : public testing::Test {
|
||||
void NewShard(size_t capacity, bool strict_capacity_limit = true,
|
||||
int eviction_effort_cap = 30) {
|
||||
DeleteShard();
|
||||
shard_ = static_cast<Shard*>(port::cacheline_aligned_alloc(sizeof(Shard)));
|
||||
shard_ =
|
||||
reinterpret_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_(static_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
shards_(reinterpret_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 static_cast<Handle*>(result);
|
||||
return reinterpret_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 static_cast<Handle*>(result);
|
||||
return reinterpret_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 = static_cast<HandleImpl*>(handle);
|
||||
auto h = reinterpret_cast<HandleImpl*>(handle);
|
||||
return GetShard(h->GetHash()).Release(h, useful, erase_if_last_ref);
|
||||
}
|
||||
bool Ref(Handle* handle) override {
|
||||
auto h = static_cast<HandleImpl*>(handle);
|
||||
auto h = reinterpret_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);
|
||||
}
|
||||
|
||||
void EraseUnRefEntries() override {
|
||||
virtual void EraseUnRefEntries() override {
|
||||
ForEachShard([](CacheShard* cs) { cs->EraseUnRefEntries(); });
|
||||
}
|
||||
|
||||
|
||||
Vendored
+10
-8
@@ -42,25 +42,27 @@ 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.
|
||||
Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
virtual Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
bool /*force_insert*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Warm up the nvm tier directly
|
||||
Status InsertSaved(const Slice& key, const Slice& saved,
|
||||
CompressionType type = CompressionType::kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
virtual Status InsertSaved(
|
||||
const Slice& key, const Slice& saved,
|
||||
CompressionType type = CompressionType::kNoCompression,
|
||||
CacheTier source = CacheTier::kVolatileTier) override {
|
||||
return nvm_sec_cache_->InsertSaved(key, saved, type, source);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
virtual std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
Cache::CreateContext* create_context, bool wait, bool advise_erase,
|
||||
Statistics* stats, bool& kept_in_sec_cache) override;
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override;
|
||||
virtual void WaitAll(
|
||||
std::vector<SecondaryCacheResultHandle*> handles) override;
|
||||
|
||||
private:
|
||||
struct CreateContext : public Cache::CreateContext {
|
||||
|
||||
Vendored
+4
-3
@@ -155,7 +155,7 @@ class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
using BaseCacheInterface<CachePtr>::BaseCacheInterface;
|
||||
struct TypedAsyncLookupHandle : public Cache::AsyncLookupHandle {
|
||||
TypedHandle* Result() {
|
||||
return static_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
return reinterpret_cast<TypedHandle*>(Cache::AsyncLookupHandle::Result());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -169,7 +169,8 @@ class BasicTypedCacheInterface : public BaseCacheInterface<CachePtr>,
|
||||
}
|
||||
|
||||
inline TypedHandle* Lookup(const Slice& key, Statistics* stats = nullptr) {
|
||||
return static_cast<TypedHandle*>(this->cache_->BasicLookup(key, stats));
|
||||
return reinterpret_cast<TypedHandle*>(
|
||||
this->cache_->BasicLookup(key, stats));
|
||||
}
|
||||
|
||||
inline void StartAsyncLookup(TypedAsyncLookupHandle& async_handle) {
|
||||
@@ -346,7 +347,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 static_cast<TypedHandle*>(this->cache_->Lookup(
|
||||
return reinterpret_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.
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
extern 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,7 +325,8 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
|
||||
auto check_is_blob = [&](bool is_blob) {
|
||||
return [is_blob](Iterator* iterator) {
|
||||
ASSERT_EQ(is_blob, static_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
|
||||
ASSERT_EQ(is_blob,
|
||||
reinterpret_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+11
-10
@@ -83,8 +83,11 @@ 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_hash=*/paranoid_file_checks);
|
||||
OutputValidator output_validator(
|
||||
tboptions.internal_comparator,
|
||||
/*enable_order_check=*/
|
||||
mutable_cf_options.check_flush_compaction_key_order,
|
||||
/*enable_hash=*/paranoid_file_checks);
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
iter->SeekToFirst();
|
||||
@@ -207,7 +210,7 @@ Status BuildTable(
|
||||
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
|
||||
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool logical_strip_timestamp =
|
||||
const bool strip_timestamp =
|
||||
ts_sz > 0 && !ioptions.persist_user_defined_timestamps;
|
||||
|
||||
std::string key_after_flush_buf;
|
||||
@@ -221,7 +224,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 (logical_strip_timestamp) {
|
||||
if (strip_timestamp) {
|
||||
key_after_flush_buf.clear();
|
||||
ReplaceInternalKeyWithMinTimestamp(&key_after_flush_buf, key, ts_sz);
|
||||
key_after_flush = key_after_flush_buf;
|
||||
@@ -264,12 +267,9 @@ Status BuildTable(
|
||||
Slice last_tombstone_start_user_key{};
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
// 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();
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
// TODO(yuzhangyu): handle range deletion for UDT in memtables only.
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone_end, tombstone.seq_,
|
||||
@@ -422,6 +422,7 @@ 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.
|
||||
Status BuildTable(
|
||||
extern Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
const FileOptions& file_options, TableCache* table_cache,
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#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;
|
||||
@@ -116,7 +115,6 @@ 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;
|
||||
@@ -1687,11 +1685,6 @@ 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)) {
|
||||
@@ -1879,8 +1872,9 @@ void rocksdb_disable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->DisableFileDeletions());
|
||||
}
|
||||
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions());
|
||||
void rocksdb_enable_file_deletions(rocksdb_t* db, unsigned char force,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->EnableFileDeletions(force));
|
||||
}
|
||||
|
||||
void rocksdb_destroy_db(const rocksdb_options_t* options, const char* name,
|
||||
@@ -2925,23 +2919,6 @@ 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);
|
||||
}
|
||||
@@ -2952,12 +2929,6 @@ 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);
|
||||
}
|
||||
@@ -3148,7 +3119,7 @@ void rocksdb_options_set_enable_blob_files(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.enable_blob_files = val;
|
||||
}
|
||||
ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_enable_blob_files(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_blob_files;
|
||||
}
|
||||
@@ -3520,6 +3491,35 @@ 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,16 +4005,6 @@ 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;
|
||||
}
|
||||
@@ -4611,7 +4601,7 @@ void rocksdb_readoptions_set_io_timeout(rocksdb_readoptions_t* opt,
|
||||
opt->rep.io_timeout = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
ROCKSDB_LIBRARY_API uint64_t
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.io_timeout.count();
|
||||
}
|
||||
@@ -5444,7 +5434,9 @@ uint64_t rocksdb_livefiles_deletions(const rocksdb_livefiles_t* lf, int index) {
|
||||
return lf->rep[index].num_deletions;
|
||||
}
|
||||
|
||||
void rocksdb_livefiles_destroy(const rocksdb_livefiles_t* lf) { delete lf; }
|
||||
extern 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,
|
||||
|
||||
+10
-30
@@ -718,11 +718,6 @@ 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);
|
||||
@@ -2039,6 +2034,8 @@ 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));
|
||||
@@ -2234,6 +2231,8 @@ 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));
|
||||
@@ -2516,6 +2515,11 @@ 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));
|
||||
@@ -2784,30 +2788,6 @@ 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;
|
||||
@@ -3714,7 +3694,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
StartPhase("statistics");
|
||||
{
|
||||
const uint32_t BYTES_WRITTEN_TICKER = 60;
|
||||
const uint32_t BYTES_WRITTEN_TICKER = 40;
|
||||
const uint32_t DB_WRITE_HIST = 1;
|
||||
|
||||
rocksdb_statistics_histogram_data_t* hist =
|
||||
|
||||
+7
-26
@@ -96,16 +96,16 @@ const Comparator* ColumnFamilyHandleImpl::GetComparator() const {
|
||||
return cfd()->user_comparator();
|
||||
}
|
||||
|
||||
void GetInternalTblPropCollFactory(
|
||||
void GetIntTblPropCollectorFactory(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
InternalTblPropCollFactories* internal_tbl_prop_coll_factories) {
|
||||
assert(internal_tbl_prop_coll_factories);
|
||||
IntTblPropCollectorFactories* int_tbl_prop_collector_factories) {
|
||||
assert(int_tbl_prop_collector_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]);
|
||||
internal_tbl_prop_coll_factories->emplace_back(
|
||||
int_tbl_prop_collector_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.
|
||||
GetInternalTblPropCollFactory(ioptions_, &internal_tbl_prop_coll_factories_);
|
||||
GetIntTblPropCollectorFactory(ioptions_, &int_tbl_prop_collector_factories_);
|
||||
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
@@ -893,14 +893,6 @@ 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>
|
||||
@@ -1082,16 +1074,6 @@ 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();
|
||||
}
|
||||
@@ -1179,7 +1161,7 @@ bool ColumnFamilyData::RangeOverlapWithCompaction(
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
const autovector<UserKeyRange>& ranges, SuperVersion* super_version,
|
||||
const autovector<Range>& ranges, SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap) {
|
||||
assert(overlap != nullptr);
|
||||
*overlap = false;
|
||||
@@ -1223,8 +1205,7 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
|
||||
if (status.ok()) {
|
||||
if (memtable_iter->Valid() &&
|
||||
ucmp->CompareWithoutTimestamp(seek_result.user_key,
|
||||
ranges[i].limit) <= 0) {
|
||||
ucmp->Compare(seek_result.user_key, ranges[i].limit) <= 0) {
|
||||
*overlap = true;
|
||||
} else if (range_del_agg.IsRangeOverlapped(ranges[i].start,
|
||||
ranges[i].limit)) {
|
||||
|
||||
+26
-24
@@ -169,10 +169,10 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
|
||||
uint32_t GetID() const override;
|
||||
const std::string& GetName() const override;
|
||||
Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
const Comparator* GetComparator() const override;
|
||||
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;
|
||||
|
||||
private:
|
||||
ColumnFamilyData* cfd_;
|
||||
@@ -193,7 +193,7 @@ class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
||||
internal_cfd_(nullptr) {}
|
||||
|
||||
void SetCFD(ColumnFamilyData* _cfd) { internal_cfd_ = _cfd; }
|
||||
ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
virtual ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
|
||||
private:
|
||||
ColumnFamilyData* internal_cfd_;
|
||||
@@ -251,21 +251,22 @@ struct SuperVersion {
|
||||
autovector<MemTable*> to_delete;
|
||||
};
|
||||
|
||||
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
|
||||
Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckConcurrentWritesSupported(
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
extern ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
// Wrap user defined table properties collector factories `from cf_options`
|
||||
// into internal ones in internal_tbl_prop_coll_factories. Add a system internal
|
||||
// into internal ones in int_tbl_prop_collector_factories. Add a system internal
|
||||
// one too.
|
||||
void GetInternalTblPropCollFactory(
|
||||
extern void GetIntTblPropCollectorFactory(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
InternalTblPropCollFactories* internal_tbl_prop_coll_factories);
|
||||
IntTblPropCollectorFactories* int_tbl_prop_collector_factories);
|
||||
|
||||
class ColumnFamilySet;
|
||||
|
||||
@@ -400,7 +401,7 @@ class ColumnFamilyData {
|
||||
// duration of this function.
|
||||
//
|
||||
// Thread-safe
|
||||
Status RangesOverlapWithMemtables(const autovector<UserKeyRange>& ranges,
|
||||
Status RangesOverlapWithMemtables(const autovector<Range>& ranges,
|
||||
SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap);
|
||||
|
||||
@@ -429,8 +430,8 @@ class ColumnFamilyData {
|
||||
return internal_comparator_;
|
||||
}
|
||||
|
||||
const InternalTblPropCollFactories* internal_tbl_prop_coll_factories() const {
|
||||
return &internal_tbl_prop_coll_factories_;
|
||||
const IntTblPropCollectorFactories* int_tbl_prop_collector_factories() const {
|
||||
return &int_tbl_prop_collector_factories_;
|
||||
}
|
||||
|
||||
SuperVersion* GetSuperVersion() { return super_version_; }
|
||||
@@ -573,7 +574,7 @@ class ColumnFamilyData {
|
||||
std::atomic<bool> dropped_; // true if client dropped it
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
InternalTblPropCollFactories internal_tbl_prop_coll_factories_;
|
||||
IntTblPropCollectorFactories int_tbl_prop_collector_factories_;
|
||||
|
||||
const ColumnFamilyOptions initial_cf_options_;
|
||||
const ImmutableOptions ioptions_;
|
||||
@@ -853,17 +854,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
|
||||
MemTable* GetMemTable() const override;
|
||||
virtual 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
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle() override;
|
||||
virtual 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
|
||||
ColumnFamilyData* current() override { return current_; }
|
||||
virtual ColumnFamilyData* current() override { return current_; }
|
||||
|
||||
private:
|
||||
ColumnFamilySet* column_family_set_;
|
||||
@@ -871,11 +872,12 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
ColumnFamilyHandleInternal handle_;
|
||||
};
|
||||
|
||||
uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
extern uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family);
|
||||
|
||||
const Comparator* GetColumnFamilyUserComparator(
|
||||
extern const Comparator* GetColumnFamilyUserComparator(
|
||||
ColumnFamilyHandle* column_family);
|
||||
|
||||
const ImmutableOptions& GetImmutableOptions(ColumnFamilyHandle* column_family);
|
||||
extern const ImmutableOptions& GetImmutableOptions(
|
||||
ColumnFamilyHandle* column_family);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+43
-124
@@ -2604,92 +2604,90 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(450);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(205);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(202);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(198);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(599);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(2001);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(100);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2708,7 +2706,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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2720,14 +2718,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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(200);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(0);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2746,7 +2744,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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2755,14 +2753,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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2787,21 +2785,19 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit = 200;
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
@@ -2857,55 +2853,53 @@ TEST_P(ColumnFamilyTest, WriteStallTwoColumnFamilies) {
|
||||
MutableCFOptions mutable_cf_options1 = mutable_cf_options;
|
||||
mutable_cf_options1.soft_pending_compaction_bytes_limit = 500;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
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, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(800);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
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, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(700);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
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, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
@@ -2938,31 +2932,29 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
MutableCFOptions mutable_cf_options1 = mutable_cf_options;
|
||||
mutable_cf_options1.level0_slowdown_writes_trigger = 16;
|
||||
|
||||
auto dbmu = dbfull()->TEST_Mutex();
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60);
|
||||
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, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3, dbmu);
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
@@ -2988,13 +2980,10 @@ 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 */,
|
||||
dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
@@ -3007,88 +2996,18 @@ 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 */,
|
||||
dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(1048576 /* 1MB */);
|
||||
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, dbmu);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(8);
|
||||
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,7 +13,6 @@
|
||||
#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"
|
||||
@@ -22,6 +21,9 @@
|
||||
|
||||
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) {
|
||||
@@ -350,9 +352,11 @@ 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()
|
||||
? target_output_file_size_
|
||||
: 2 * target_output_file_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_;
|
||||
|
||||
#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`.
|
||||
uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
extern uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -450,6 +450,14 @@ 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.
|
||||
@@ -752,6 +760,7 @@ 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());
|
||||
@@ -822,27 +831,24 @@ 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())) {
|
||||
if (!(ts_sz > 0 && !trim_ts_.empty()) &&
|
||||
db_options_.compaction_verify_record_count) {
|
||||
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 =
|
||||
"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;
|
||||
"Total number of input records: " + std::to_string(expected) +
|
||||
", but processed " + std::to_string(actual) + " records.";
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log, "[%s] [JOB %d] Compaction with status: %s",
|
||||
db_options_.info_log, "[%s] [JOB %d] Compaction %s",
|
||||
compact_->compaction->column_family_data()->GetName().c_str(),
|
||||
job_context_->job_id, msg.c_str());
|
||||
if (db_options_.compaction_verify_record_count) {
|
||||
status = Status::Corruption(msg);
|
||||
}
|
||||
status = Status::Corruption(
|
||||
"Compaction number of input keys does not match number of keys "
|
||||
"processed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1279,9 +1285,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
: nullptr);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:1",
|
||||
static_cast<void*>(const_cast<std::atomic<bool>*>(
|
||||
&manual_compaction_canceled_)));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
reinterpret_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_;
|
||||
@@ -1329,7 +1336,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
Status status;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::ProcessKeyValueCompaction()::Processing",
|
||||
static_cast<void*>(const_cast<Compaction*>(sub_compact->compaction)));
|
||||
reinterpret_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()
|
||||
@@ -1360,9 +1368,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
break;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():PausingManualCompaction:2",
|
||||
static_cast<void*>(const_cast<std::atomic<bool>*>(
|
||||
&manual_compaction_canceled_)));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:2",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(&manual_compaction_canceled_)));
|
||||
c_iter->Next();
|
||||
if (c_iter->status().IsManualCompactionPaused()) {
|
||||
break;
|
||||
@@ -1936,6 +1945,8 @@ 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_);
|
||||
}
|
||||
|
||||
@@ -1958,7 +1969,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->internal_tbl_prop_coll_factories(),
|
||||
cfd->int_tbl_prop_collector_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_->internal_tbl_prop_coll_factories(),
|
||||
cfd_->int_tbl_prop_collector_factories(),
|
||||
CompressionType::kNoCompression,
|
||||
CompressionOptions(), 0 /* column_family_id */,
|
||||
kDefaultColumnFamilyName, -1 /* level */),
|
||||
@@ -1752,9 +1752,23 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutForMaxCompactionBytes) {
|
||||
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) {
|
||||
// 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;
|
||||
@@ -1828,7 +1842,10 @@ TEST_F(CompactionJobTest, CutForMaxCompactionBytes) {
|
||||
{expected_file1, expected_file2});
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, CutToSkipGrandparentFile) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToSkipGrandparentFile) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
|
||||
NewDB();
|
||||
// Make sure the grandparent level file size (10) qualifies skipping.
|
||||
// Currently, it has to be > 1/8 of target file size.
|
||||
@@ -1863,15 +1880,28 @@ TEST_F(CompactionJobTest, 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_F(CompactionJobTest, CutToAlignGrandparentBoundary) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundary) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
NewDB();
|
||||
|
||||
// MockTable has 1 byte per entry by default and each file is 10 bytes.
|
||||
@@ -1938,15 +1968,40 @@ TEST_F(CompactionJobTest, 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_F(CompactionJobTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, CutToAlignGrandparentBoundarySameKey) {
|
||||
bool enable_dyanmic_file_size = GetParam();
|
||||
cf_options_.level_compaction_dynamic_file_size = enable_dyanmic_file_size;
|
||||
NewDB();
|
||||
|
||||
// MockTable has 1 byte per entry by default and each file is 10 bytes.
|
||||
@@ -1983,9 +2038,13 @@ TEST_F(CompactionJobTest, 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
|
||||
@@ -1994,6 +2053,9 @@ TEST_F(CompactionJobTest, 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);
|
||||
@@ -2004,13 +2066,20 @@ TEST_F(CompactionJobTest, 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_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
TEST_P(CompactionJobDynamicFileSizeTest, 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;
|
||||
@@ -2067,6 +2136,9 @@ TEST_F(CompactionJobTest, CutForMaxCompactionBytesSameKey) {
|
||||
{expected_file1, expected_file2, expected_file3}, snapshots);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionJobDynamicFileSizeTest,
|
||||
CompactionJobDynamicFileSizeTest, testing::Bool());
|
||||
|
||||
class CompactionJobTimestampTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobTimestampTest()
|
||||
|
||||
@@ -320,6 +320,7 @@ 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 >
|
||||
@@ -341,6 +342,7 @@ 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,9 +30,11 @@ class CompactionOutputs {
|
||||
// compaction output file
|
||||
struct Output {
|
||||
Output(FileMetaData&& _meta, const InternalKeyComparator& _icmp,
|
||||
bool _enable_hash, bool _finished, uint64_t precalculated_hash)
|
||||
bool _enable_order_check, bool _enable_hash, bool _finished,
|
||||
uint64_t precalculated_hash)
|
||||
: meta(std::move(_meta)),
|
||||
validator(_icmp, _enable_hash, precalculated_hash),
|
||||
validator(_icmp, _enable_order_check, _enable_hash,
|
||||
precalculated_hash),
|
||||
finished(_finished) {}
|
||||
FileMetaData meta;
|
||||
OutputValidator validator;
|
||||
@@ -47,10 +49,10 @@ class CompactionOutputs {
|
||||
|
||||
// Add generated output to the list
|
||||
void AddOutput(FileMetaData&& meta, const InternalKeyComparator& icmp,
|
||||
bool enable_hash, bool finished = false,
|
||||
uint64_t precalculated_hash = 0) {
|
||||
outputs_.emplace_back(std::move(meta), icmp, enable_hash, finished,
|
||||
precalculated_hash);
|
||||
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);
|
||||
}
|
||||
|
||||
// Set new table builder for the current output
|
||||
|
||||
@@ -456,9 +456,10 @@ 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, VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs, CompactionInputFiles* output_level_inputs,
|
||||
int* parent_index, int base_index, bool only_expand_towards_right) {
|
||||
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) {
|
||||
assert(!inputs->empty());
|
||||
assert(output_level_inputs->empty());
|
||||
const int input_level = inputs->level;
|
||||
@@ -499,6 +500,7 @@ 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);
|
||||
@@ -525,6 +527,8 @@ 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);
|
||||
@@ -547,6 +551,8 @@ 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;
|
||||
}
|
||||
@@ -806,8 +812,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
output_level_inputs.level = output_level;
|
||||
if (input_level != output_level) {
|
||||
int parent_index = -1;
|
||||
if (!SetupOtherInputs(cf_name, vstorage, &inputs, &output_level_inputs,
|
||||
&parent_index, -1)) {
|
||||
if (!SetupOtherInputs(cf_name, mutable_cf_options, 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,6 +186,7 @@ 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,
|
||||
@@ -276,7 +277,8 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
}
|
||||
|
||||
// Always returns false.
|
||||
bool NeedsCompaction(const VersionStorageInfo* /*vstorage*/) const override {
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* /*vstorage*/) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,27 +18,26 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
|
||||
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* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) 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;
|
||||
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;
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
int MaxOutputLevel() const override { return 0; }
|
||||
virtual int MaxOutputLevel() const override { return 0; }
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
private:
|
||||
Compaction* PickTTLCompaction(const std::string& cf_name,
|
||||
|
||||
@@ -113,15 +113,6 @@ 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,
|
||||
@@ -465,8 +456,9 @@ bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
}
|
||||
if (!is_l0_trivial_move_ &&
|
||||
!compaction_picker_->SetupOtherInputs(
|
||||
cf_name_, vstorage_, &start_level_inputs_, &output_level_inputs_,
|
||||
&parent_index_, base_index_, round_robin_expanding)) {
|
||||
cf_name_, mutable_cf_options_, vstorage_, &start_level_inputs_,
|
||||
&output_level_inputs_, &parent_index_, base_index_,
|
||||
round_robin_expanding)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -784,9 +776,6 @@ 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;
|
||||
}
|
||||
@@ -799,9 +788,6 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
if (TryPickL0TrivialMove()) {
|
||||
return true;
|
||||
}
|
||||
if (start_level_ == 0 && PickSizeBasedIntraL0Compaction()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
@@ -906,56 +892,6 @@ 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,13 +20,14 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
LevelCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1443,12 +1443,15 @@ 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;
|
||||
|
||||
// Overlaps with file 26, 27, ratio is
|
||||
// (60000000U + 60000000U / 180000000U) = 0.67
|
||||
// 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.
|
||||
Add(2, 6U, "150", "167", 60000000U, 0, 100, 100, 180000000U);
|
||||
Add(2, 7U, "178", "189", 60000000U); // Overlaps with file 28
|
||||
Add(2, 8U, "401", "500", 61000000U); // Overlaps with file 29
|
||||
Add(2, 7U, "168", "169", 60000000U); // Overlaps with file 27
|
||||
Add(2, 8U, "201", "300", 61000000U); // Overlaps with file 28
|
||||
|
||||
Add(3, 26U, "160", "165", 60000000U);
|
||||
// Boosted file size in output level is not considered.
|
||||
@@ -1462,7 +1465,7 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping4) {
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
// Picking file 6 because overlapping ratio is the biggest.
|
||||
// Picking file 8 because overlapping ratio is the biggest.
|
||||
ASSERT_EQ(6U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
@@ -2465,23 +2468,20 @@ TEST_F(CompactionPickerTest, IsBottommostLevelTest) {
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IgnoreCompactionLimitWhenAddFileFromInputLevel) {
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesHit) {
|
||||
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 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.
|
||||
// It can expand because adding file 1 and 3, the compaction size will
|
||||
// exceed mutable_cf_options_.max_bytes_for_level_base.
|
||||
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(1, 6U, "325", "400", 300000U, 0, 0);
|
||||
Add(2, 5U, "100", "350", 1U);
|
||||
Add(2, 7U, "375", "425", 1U);
|
||||
Add(2, 5U, "100", "256", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -2489,12 +2489,37 @@ TEST_F(CompactionPickerTest, IgnoreCompactionLimitWhenAddFileFromInputLevel) {
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
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(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());
|
||||
}
|
||||
|
||||
@@ -4243,58 +4268,6 @@ 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,7 +1139,8 @@ 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_, vstorage_, &second_last_level_inputs,
|
||||
if (!picker_->SetupOtherInputs(cf_name_, mutable_cf_options_, vstorage_,
|
||||
&second_last_level_inputs,
|
||||
&bottom_level_inputs, &parent_index,
|
||||
/*base_index=*/-1)) {
|
||||
return nullptr;
|
||||
@@ -1310,8 +1311,9 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
int parent_index = -1;
|
||||
|
||||
output_level_inputs.level = output_level;
|
||||
if (!picker_->SetupOtherInputs(cf_name_, vstorage_, &start_level_inputs,
|
||||
&output_level_inputs, &parent_index, -1)) {
|
||||
if (!picker_->SetupOtherInputs(cf_name_, mutable_cf_options_, vstorage_,
|
||||
&start_level_inputs, &output_level_inputs,
|
||||
&parent_index, -1)) {
|
||||
return nullptr;
|
||||
}
|
||||
inputs.push_back(start_level_inputs);
|
||||
|
||||
@@ -17,13 +17,14 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
UniversalCompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
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 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; }
|
||||
|
||||
bool NeedsCompaction(const VersionStorageInfo* vstorage) const override;
|
||||
virtual 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, true,
|
||||
file.paranoid_hash);
|
||||
cfd->internal_comparator(), false, 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 =
|
||||
static_cast<PeriodicTaskScheduler*>(arg);
|
||||
reinterpret_cast<PeriodicTaskScheduler*>(arg);
|
||||
periodic_task_scheduler_ptr->TEST_OverrideTimer(mock_clock_.get());
|
||||
});
|
||||
mock_clock_->SetCurrentTime(kMockStartTime);
|
||||
@@ -1823,6 +1823,7 @@ 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);
|
||||
|
||||
|
||||
+38
-7
@@ -53,9 +53,10 @@ class ErrorFS : public FileSystemWrapper {
|
||||
num_writable_file_errors_(0) {}
|
||||
const char* Name() const override { return "ErrorEnv"; }
|
||||
|
||||
IOStatus NewWritableFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
virtual 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_;
|
||||
@@ -822,6 +823,7 @@ 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;
|
||||
@@ -852,6 +854,7 @@ 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_;
|
||||
@@ -883,6 +886,7 @@ 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}) {
|
||||
@@ -919,6 +923,7 @@ 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}) {
|
||||
@@ -958,6 +963,7 @@ 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}) {
|
||||
@@ -1026,6 +1032,7 @@ 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));
|
||||
@@ -1040,6 +1047,7 @@ 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(
|
||||
@@ -1052,6 +1060,7 @@ 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"));
|
||||
@@ -1076,6 +1085,28 @@ 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;
|
||||
@@ -1102,7 +1133,7 @@ TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
int count{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::VerifyFullFileChecksum:mismatch", [&](void* arg) {
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
ASSERT_NE(s, nullptr);
|
||||
++count;
|
||||
ASSERT_NOK(*s);
|
||||
@@ -1247,7 +1278,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLogSizeAndMaybeTruncate:0", [&](void* arg) {
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
@@ -1429,7 +1460,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::Open::BeforeSyncWAL", [&](void* arg) {
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
auto* tmp_s = reinterpret_cast<Status*>(arg);
|
||||
assert(tmp_s);
|
||||
*tmp_s = Status::IOError("Injected");
|
||||
});
|
||||
@@ -1597,7 +1628,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecoveryWithFlush) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::GetLogSizeAndMaybeTruncate:0", [&](void* arg) {
|
||||
auto* tmp_s = static_cast<Status*>(arg);
|
||||
auto* tmp_s = reinterpret_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(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(reinterpret_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(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(reinterpret_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(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(reinterpret_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(static_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
ASSERT_OK(reinterpret_cast<DB*>(dbfull())->GetPropertiesOfAllTables(&ptc));
|
||||
VerifySstUniqueIds(ptc);
|
||||
ASSERT_EQ(1U, ptc.size());
|
||||
ASSERT_EQ(2U, ptc.begin()->second->num_entries);
|
||||
|
||||
+93
-227
@@ -25,60 +25,10 @@
|
||||
#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; }
|
||||
@@ -1903,7 +1853,7 @@ TEST_P(DBMultiGetTestWithParam, MultiGetBatchedMultiLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetBatchedEmptyLevel) {
|
||||
#ifndef USE_COROUTINES
|
||||
if (std::get<1>(GetParam())) {
|
||||
ROCKSDB_GTEST_BYPASS("This test requires coroutine support");
|
||||
@@ -1915,37 +1865,12 @@ TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
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());
|
||||
@@ -1956,9 +1881,6 @@ TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
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);
|
||||
@@ -1977,138 +1899,8 @@ TEST_P(DBMultiGetTestWithParam, MultiGetDuplicatesEmptyLevel) {
|
||||
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) {
|
||||
@@ -3342,7 +3134,8 @@ TEST_F(DBBasicTest, BestEffortsRecoveryWithVersionBuildingFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("Inject corruption");
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -3773,7 +3566,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(10));
|
||||
new MyFlushBlockPolicyFactory());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
if (!compression_enabled_) {
|
||||
options.compression = kNoCompression;
|
||||
@@ -3861,6 +3654,46 @@ 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)
|
||||
@@ -4277,7 +4110,7 @@ class DeadlineRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
const IOOptions& options, IODebugContext* dbg) override;
|
||||
|
||||
IOStatus ReadAsync(FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(FSReadRequest&, void*)> cb,
|
||||
std::function<void(const FSReadRequest&, void*)> cb,
|
||||
void* cb_arg, void** io_handle, IOHandleDeleter* del_fn,
|
||||
IODebugContext* dbg) override;
|
||||
|
||||
@@ -4423,7 +4256,7 @@ IOStatus DeadlineRandomAccessFile::Read(uint64_t offset, size_t len,
|
||||
|
||||
IOStatus DeadlineRandomAccessFile::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
std::function<void(const 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();
|
||||
@@ -4504,35 +4337,68 @@ TEST_P(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
ReopenWithColumnFamilies(GetCFNames(), options);
|
||||
|
||||
// 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.
|
||||
// Test the non-batched version of MultiGet with multiple column
|
||||
// families
|
||||
std::vector<std::string> key_str;
|
||||
size_t i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
for (i = 0; i < 5; ++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<PinnableSlice> pin_values(keys.size());
|
||||
|
||||
std::vector<std::string> values(key_str.size());
|
||||
for (i = 0; i < key_str.size(); ++i) {
|
||||
// 2 keys per CF
|
||||
cfs[i] = handles_[i / 2];
|
||||
cfs[i] = handles_[i];
|
||||
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(key_str.size());
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
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);
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 2);
|
||||
|
||||
// Similar to the previous one, but an IO delay in the third CF data block
|
||||
@@ -4610,7 +4476,7 @@ TEST_F(DBBasicTest, ManifestWriteFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::ProcessManifestWrites:AfterSyncManifest", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
ASSERT_OK(*s);
|
||||
// Manually overwrite return status
|
||||
*s = Status::IOError();
|
||||
@@ -4665,7 +4531,7 @@ TEST_F(DBBasicTest, FailOpenIfLoggerCreationFail) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"rocksdb::CreateLoggerFromOptions:AfterGetPath", [&](void* arg) {
|
||||
auto* s = static_cast<Status*>(arg);
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected");
|
||||
});
|
||||
|
||||
@@ -1373,7 +1373,7 @@ class StableCacheKeyTestFS : public FaultInjectionTestFS {
|
||||
SetFailGetUniqueId(true);
|
||||
}
|
||||
|
||||
~StableCacheKeyTestFS() override {}
|
||||
virtual ~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 = static_cast<TableProperties*>(arg);
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
props->orig_file_number = 0;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#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"
|
||||
@@ -3563,277 +3562,6 @@ 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
|
||||
|
||||
|
||||
+50
-99
@@ -505,7 +505,7 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::FindTable:0", [&](void* arg) {
|
||||
assert(arg != nullptr);
|
||||
bool no_io = *(static_cast<bool*>(arg));
|
||||
bool no_io = *(reinterpret_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 = static_cast<Env::Priority*>(arg);
|
||||
Env::Priority* pri = reinterpret_cast<Env::Priority*>(arg);
|
||||
// First time is low pri pool in the test case.
|
||||
if (low_pri_count == 0 && bottom_pri_count == 0) {
|
||||
ASSERT_EQ(Env::Priority::LOW, *pri);
|
||||
@@ -1870,60 +1870,13 @@ TEST_F(DBCompactionTest, ManualCompactionWithUnorderedWrite) {
|
||||
ASSERT_EQ(Get("foo"), "v2");
|
||||
}
|
||||
|
||||
// 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) {
|
||||
TEST_F(DBCompactionTest, 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
|
||||
@@ -1935,14 +1888,14 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
// file 1 [0 => 100]
|
||||
for (int32_t i = 0; i < 100; i++) {
|
||||
values[i] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
ASSERT_OK(Put(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);
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -1958,7 +1911,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
// file 3 [ 0 => 200]
|
||||
for (int32_t i = 0; i < 200; i++) {
|
||||
values[i] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(i), values[i]);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -1970,7 +1923,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
}
|
||||
values[j] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(j), values[j]);
|
||||
ASSERT_OK(Put(Key(j), values[j]));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
@@ -1995,10 +1948,11 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
int32_t deleted_count = 0;
|
||||
for (int32_t i = 0; i < 4300; i++) {
|
||||
if (i < 1000 || i > 2000) {
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
} else {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
Status s = MaybeGetValue(Key(i), &result);
|
||||
Status s = db_->Get(roptions, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound() || s.ok());
|
||||
if (s.IsNotFound()) {
|
||||
deleted_count++;
|
||||
@@ -2028,7 +1982,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
for (int32_t i = 0; i < 4300; i++) {
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
ASSERT_TRUE(MaybeGetValue(Key(i), &result).IsNotFound());
|
||||
ASSERT_TRUE(db_->Get(roptions, Key(i), &result).IsNotFound());
|
||||
deleted_count2++;
|
||||
}
|
||||
ASSERT_GT(deleted_count2, deleted_count);
|
||||
@@ -2036,16 +1990,13 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
|
||||
ASSERT_GT(old_num_files, new_num_files);
|
||||
}
|
||||
|
||||
TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
TEST_F(DBCompactionTest, 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
|
||||
@@ -2058,7 +2009,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
values[k] = rnd.RandomString(value_size);
|
||||
PutKeyValue(Key(k), values[k]);
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -2073,7 +2024,7 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
for (auto i = 0; i < 10; i += 2) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
PutKeyValue(Key(k), values[k]);
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
@@ -2099,12 +2050,13 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
|
||||
// Keys [0, 300) should not exist.
|
||||
for (auto i = 0; i < 300; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 300; i < 1000; i++) {
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2126,15 +2078,16 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
|
||||
// Keys [600, 900) should not exist.
|
||||
for (auto i = 600; i < 900; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 300; i < 600; i++) {
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
for (auto i = 900; i < 1000; i++) {
|
||||
ASSERT_EQ(GetValue(Key(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2145,14 +2098,15 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
for (auto i = 0; i < 1000; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = MaybeGetValue(Key(i), &result);
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
TEST_F(DBCompactionTest, 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
|
||||
@@ -2164,9 +2118,6 @@ TEST_P(DBDeleteFileRangeTest, 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
|
||||
@@ -2190,8 +2141,8 @@ TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
std::string vals[kNumL0Files];
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
vals[i] = rnd.RandomString(kValSize);
|
||||
PutKeyValue(Key(i), vals[i]);
|
||||
PutKeyValue(Key(i + 1), vals[i]);
|
||||
ASSERT_OK(Put(Key(i), vals[i]));
|
||||
ASSERT_OK(Put(Key(i + 1), vals[i]));
|
||||
ASSERT_OK(Flush());
|
||||
if (i == 0) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
@@ -2204,14 +2155,11 @@ TEST_P(DBDeleteFileRangeTest, 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], GetValue(Key(1)));
|
||||
ASSERT_EQ(vals[1], Get(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;
|
||||
@@ -4244,7 +4192,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 = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kBottommostFiles);
|
||||
});
|
||||
@@ -4300,7 +4248,7 @@ TEST_F(DBCompactionTest, DelayCompactBottomLevelFilesWithDeletions) {
|
||||
std::atomic_int compaction_count = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kBottommostFiles);
|
||||
compaction_count++;
|
||||
@@ -4431,7 +4379,7 @@ TEST_F(DBCompactionTest, RoundRobinTtlCompactionNormal) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -4581,7 +4529,7 @@ TEST_F(DBCompactionTest, RoundRobinTtlCompactionUnsortedTime) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -4697,7 +4645,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -4745,7 +4693,7 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -4772,6 +4720,7 @@ 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);
|
||||
@@ -4872,7 +4821,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
int ttl_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
@@ -5020,7 +4969,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
int periodic_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5204,7 +5153,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
bool set_creation_time_to_zero = true;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5212,7 +5161,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* arg) {
|
||||
TableProperties* props = static_cast<TableProperties*>(arg);
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
if (set_file_creation_time_to_zero) {
|
||||
props->file_creation_time = 0;
|
||||
}
|
||||
@@ -5276,7 +5225,7 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
int ttl_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -5459,7 +5408,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
|
||||
int periodic_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
@@ -7197,7 +7146,8 @@ TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency0", [&](void* arg) {
|
||||
auto p = static_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
@@ -7234,7 +7184,8 @@ TEST_F(DBCompactionTest, ConsistencyFailTest2) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency1", [&](void* arg) {
|
||||
auto p = static_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
@@ -8047,7 +7998,7 @@ TEST_F(DBCompactionTest, UpdateLevelSubCompactionTest) {
|
||||
bool has_compaction = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 10);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8071,7 +8022,7 @@ TEST_F(DBCompactionTest, UpdateLevelSubCompactionTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 2);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8099,7 +8050,7 @@ TEST_F(DBCompactionTest, UpdateUniversalSubCompactionTest) {
|
||||
bool has_compaction = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 10);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -8122,7 +8073,7 @@ TEST_F(DBCompactionTest, UpdateUniversalSubCompactionTest) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->max_subcompactions() == 2);
|
||||
has_compaction = true;
|
||||
});
|
||||
@@ -9699,7 +9650,6 @@ 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:"
|
||||
@@ -10248,7 +10198,8 @@ TEST_F(DBCompactionTest, ErrorWhenReadFileHead) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::BeforeReturn",
|
||||
[&count, &error_file](void* pair_ptr) {
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p =
|
||||
reinterpret_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 = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_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();
|
||||
Status s2 = EnableFileDeletions(/*force=*/false);
|
||||
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 = *static_cast<int*>(arg);
|
||||
auto unscheduled_flushes = *reinterpret_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 = static_cast<uint64_t*>(arg);
|
||||
uint64_t* mems_size = reinterpret_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 = static_cast<autovector<MemTable*>*>(arg);
|
||||
auto* mems = reinterpret_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 = static_cast<FlushJob*>(arg);
|
||||
auto* job = reinterpret_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 = static_cast<DBImpl*>(db_);
|
||||
DBImpl* db_impl = reinterpret_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 = static_cast<IOStatus*>(arg);
|
||||
auto* ptr = reinterpret_cast<IOStatus*>(arg);
|
||||
assert(ptr);
|
||||
*ptr = IOStatus::IOError("Injected failure");
|
||||
});
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
|
||||
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,
|
||||
@@ -33,6 +37,12 @@ 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) {
|
||||
@@ -102,59 +112,62 @@ Status CompactedDBImpl::Get(const ReadOptions& _read_options,
|
||||
return Status::NotFound();
|
||||
}
|
||||
|
||||
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*/) {
|
||||
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) {
|
||||
assert(user_comparator_);
|
||||
Status s;
|
||||
size_t num_keys = keys.size();
|
||||
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
|
||||
_read_options.io_activity != Env::IOActivity::kMultiGet) {
|
||||
s = Status::InvalidArgument(
|
||||
Status 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 (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.io_activity == Env::IOActivity::kUnknown) {
|
||||
read_options.io_activity = Env::IOActivity::kMultiGet;
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = s;
|
||||
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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the timestamps for returning results so that we can distinguish
|
||||
// between tombstone or key that has never been written
|
||||
if (timestamps) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
timestamps[i].clear();
|
||||
for (auto& ts : *timestamps) {
|
||||
ts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
GetWithTimestampReadCallback read_cb(kMaxSequenceNumber);
|
||||
autovector<TableReader*, 16> reader_list;
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
const Slice& key = keys[i];
|
||||
for (const auto& key : keys) {
|
||||
LookupKey lkey(key, kMaxSequenceNumber, read_options.timestamp);
|
||||
const FdWithKeyRange& f = files_.files[FindFile(lkey.user_key())];
|
||||
if (user_comparator_->CompareWithoutTimestamp(
|
||||
@@ -168,26 +181,30 @@ void CompactedDBImpl::MultiGet(const ReadOptions& _read_options,
|
||||
reader_list.push_back(f.fd.table_reader);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = Status::NotFound();
|
||||
std::vector<Status> statuses(num_keys, Status::NotFound());
|
||||
values->resize(num_keys);
|
||||
if (timestamps) {
|
||||
timestamps->resize(num_keys);
|
||||
}
|
||||
int idx = 0;
|
||||
for (auto* r : reader_list) {
|
||||
if (r != nullptr) {
|
||||
PinnableSlice& pinnable_val = values[idx];
|
||||
PinnableSlice pinnable_val;
|
||||
std::string& value = (*values)[idx];
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber, read_options.timestamp);
|
||||
std::string* timestamp = timestamps ? ×tamps[idx] : nullptr;
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[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 status =
|
||||
Status s =
|
||||
r->Get(read_options, lkey.internal_key(), &get_context, nullptr);
|
||||
assert(static_cast<size_t>(idx) < num_keys);
|
||||
if (!status.ok() && !status.IsNotFound()) {
|
||||
statuses[idx] = status;
|
||||
assert(static_cast<size_t>(idx) < statuses.size());
|
||||
if (!s.ok() && !s.IsNotFound()) {
|
||||
statuses[idx] = s;
|
||||
} else {
|
||||
value.assign(pinnable_val.data(), pinnable_val.size());
|
||||
if (get_context.State() == GetContext::kFound) {
|
||||
statuses[idx] = Status::OK();
|
||||
}
|
||||
@@ -195,6 +212,7 @@ void CompactedDBImpl::MultiGet(const ReadOptions& _read_options,
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return statuses;
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Init(const Options& options) {
|
||||
|
||||
@@ -26,23 +26,33 @@ class CompactedDBImpl : public DBImpl {
|
||||
|
||||
// Implementations of the DB interface
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp) override;
|
||||
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;
|
||||
|
||||
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.
|
||||
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;
|
||||
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;
|
||||
|
||||
using DBImpl::Put;
|
||||
Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
virtual Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
@@ -55,53 +65,54 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DBImpl::Merge;
|
||||
Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
virtual 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;
|
||||
Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
virtual Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DBImpl::CompactRange;
|
||||
Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/, const Slice* /*end*/) override {
|
||||
virtual Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/,
|
||||
const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
Status DisableFileDeletions() override {
|
||||
virtual Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
Status EnableFileDeletions() override {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
virtual 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;
|
||||
Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
virtual Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
Status SyncWAL() override {
|
||||
virtual Status SyncWAL() override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
Status IngestExternalFile(
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
@@ -109,7 +120,7 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
Status CreateColumnFamilyWithImport(
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
@@ -119,9 +130,9 @@ class CompactedDBImpl : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
|
||||
+310
-90
@@ -309,7 +309,7 @@ Status DBImpl::Resume() {
|
||||
|
||||
if (error_handler_.IsRecoveryInProgress()) {
|
||||
// Don't allow a mix of manual and automatic recovery
|
||||
return Status::Busy("Recovery in progress");
|
||||
return Status::Busy();
|
||||
}
|
||||
|
||||
mutex_.Unlock();
|
||||
@@ -1907,7 +1907,7 @@ struct SuperVersionHandle {
|
||||
};
|
||||
|
||||
static void CleanupSuperVersionHandle(void* arg1, void* /*arg2*/) {
|
||||
SuperVersionHandle* sv_handle = static_cast<SuperVersionHandle*>(arg1);
|
||||
SuperVersionHandle* sv_handle = reinterpret_cast<SuperVersionHandle*>(arg1);
|
||||
|
||||
if (sv_handle->super_version->Unref()) {
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
@@ -2020,6 +2020,12 @@ 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) {
|
||||
@@ -2263,7 +2269,7 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
snapshot = get_impl_options.callback->max_visible_seq();
|
||||
} else {
|
||||
snapshot =
|
||||
static_cast<const SnapshotImpl*>(read_options.snapshot)->number_;
|
||||
reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)->number_;
|
||||
}
|
||||
} else {
|
||||
// Note that the snapshot is assigned AFTER referencing the super
|
||||
@@ -2491,6 +2497,250 @@ 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,
|
||||
@@ -2639,8 +2889,7 @@ Status DBImpl::MultiCFSnapshot(
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::MultiCFSnapshot:AfterGetSeqNum1");
|
||||
TEST_SYNC_POINT("DBImpl::MultiCFSnapshot:AfterGetSeqNum2");
|
||||
// Keep track of bytes that we read for statistics-recording later
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
*sv_from_thread_local = !last_try;
|
||||
if (!s.ok()) {
|
||||
@@ -2649,6 +2898,14 @@ 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,
|
||||
@@ -2865,23 +3122,38 @@ void DBImpl::PrepareMultiGetKeys(
|
||||
CompareKeyContext());
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -4013,7 +4285,7 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
// inplace_update_support enabled.
|
||||
return;
|
||||
}
|
||||
const SnapshotImpl* casted_s = static_cast<const SnapshotImpl*>(s);
|
||||
const SnapshotImpl* casted_s = reinterpret_cast<const SnapshotImpl*>(s);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
snapshots_.Delete(casted_s);
|
||||
@@ -4098,25 +4370,7 @@ Status DBImpl::GetPropertiesOfTablesInRange(ColumnFamilyHandle* column_family,
|
||||
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
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);
|
||||
auto s = version->GetPropertiesOfTablesInRange(read_options, range, n, props);
|
||||
|
||||
// Decrement the ref count
|
||||
mutex_.Lock();
|
||||
@@ -4633,24 +4887,6 @@ 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);
|
||||
@@ -4659,9 +4895,8 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
Version* input_version = cfd->current();
|
||||
|
||||
auto* vstorage = input_version->storage_info();
|
||||
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 (size_t r = 0; r < n; r++) {
|
||||
auto begin = ranges[r].start, end = ranges[r].limit;
|
||||
for (int i = 1; i < cfd->NumberLevels(); i++) {
|
||||
if (vstorage->LevelFiles(i).empty() ||
|
||||
!vstorage->OverlapInLevel(i, begin, end)) {
|
||||
@@ -4695,8 +4930,8 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
continue;
|
||||
}
|
||||
if (!include_end && end != nullptr &&
|
||||
(ucmp->CompareWithoutTimestamp(level_file->largest.user_key(),
|
||||
*end) == 0)) {
|
||||
cfd->user_comparator()->Compare(level_file->largest.user_key(),
|
||||
*end) == 0) {
|
||||
continue;
|
||||
}
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
@@ -5279,23 +5514,16 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
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();
|
||||
}
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
versions_->options_file_number_ = options_file_number;
|
||||
versions_->options_file_size_ = options_file_size;
|
||||
}
|
||||
|
||||
if (0 == disable_delete_obsolete_files_) {
|
||||
// TODO: Should we check for errors here?
|
||||
DeleteObsoleteOptionsFiles().PermitUncheckedError();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -5552,18 +5780,10 @@ Status DBImpl::IngestExternalFiles(
|
||||
}
|
||||
for (const auto& arg : args) {
|
||||
const IngestExternalFileOptions& ingest_opts = arg.options;
|
||||
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.");
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+153
-113
@@ -229,12 +229,16 @@ class DBImpl : public DB {
|
||||
const Slice& end_key, const Slice& ts) override;
|
||||
|
||||
using DB::Write;
|
||||
Status Write(const WriteOptions& options, WriteBatch* updates) override;
|
||||
virtual Status Write(const WriteOptions& options,
|
||||
WriteBatch* updates) override;
|
||||
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value, std::string* timestamp) override;
|
||||
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;
|
||||
|
||||
using DB::GetEntity;
|
||||
Status GetEntity(const ReadOptions& options,
|
||||
@@ -259,6 +263,17 @@ 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
|
||||
@@ -266,6 +281,19 @@ 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,
|
||||
@@ -291,9 +319,9 @@ class DBImpl : public DB {
|
||||
const Slice* keys,
|
||||
PinnableAttributeGroups* results) override;
|
||||
|
||||
Status CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle) override {
|
||||
virtual 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);
|
||||
@@ -303,7 +331,7 @@ class DBImpl : public DB {
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle);
|
||||
Status CreateColumnFamilies(
|
||||
virtual Status CreateColumnFamilies(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::vector<std::string>& column_family_names,
|
||||
std::vector<ColumnFamilyHandle*>* handles) override {
|
||||
@@ -317,7 +345,7 @@ class DBImpl : public DB {
|
||||
const std::vector<std::string>& column_family_names,
|
||||
std::vector<ColumnFamilyHandle*>* handles);
|
||||
|
||||
Status CreateColumnFamilies(
|
||||
virtual Status CreateColumnFamilies(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles) override {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
@@ -328,8 +356,8 @@ class DBImpl : public DB {
|
||||
const ReadOptions& read_options, const WriteOptions& write_options,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles);
|
||||
Status DropColumnFamily(ColumnFamilyHandle* column_family) override;
|
||||
Status DropColumnFamilies(
|
||||
virtual Status DropColumnFamily(ColumnFamilyHandle* column_family) override;
|
||||
virtual Status DropColumnFamilies(
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
|
||||
// Returns false if key doesn't exist in the database and true if it may.
|
||||
@@ -337,20 +365,21 @@ class DBImpl : public DB {
|
||||
// memory. On return, if value was found, then value_found will be set to true
|
||||
// , otherwise false.
|
||||
using DB::KeyMayExist;
|
||||
bool KeyMayExist(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value, std::string* timestamp,
|
||||
bool* value_found = nullptr) override;
|
||||
virtual 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;
|
||||
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 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;
|
||||
|
||||
const Snapshot* GetSnapshot() override;
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
virtual const Snapshot* GetSnapshot() override;
|
||||
virtual 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
|
||||
@@ -365,33 +394,35 @@ class DBImpl : public DB {
|
||||
timestamped_snapshots) const;
|
||||
|
||||
using DB::GetProperty;
|
||||
bool GetProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::string* value) override;
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) override;
|
||||
using DB::GetMapProperty;
|
||||
bool GetMapProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override;
|
||||
virtual bool GetMapProperty(
|
||||
ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override;
|
||||
using DB::GetIntProperty;
|
||||
bool GetIntProperty(ColumnFamilyHandle* column_family, const Slice& property,
|
||||
uint64_t* value) override;
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, uint64_t* value) override;
|
||||
using DB::GetAggregatedIntProperty;
|
||||
bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* aggregated_value) override;
|
||||
virtual bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* aggregated_value) override;
|
||||
using DB::GetApproximateSizes;
|
||||
Status GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes) override;
|
||||
virtual Status GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes) override;
|
||||
using DB::GetApproximateMemTableStats;
|
||||
void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range, uint64_t* const count,
|
||||
uint64_t* const size) override;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override;
|
||||
using DB::CompactRange;
|
||||
Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice* begin,
|
||||
const Slice* end) override;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) override;
|
||||
|
||||
using DB::CompactFiles;
|
||||
Status CompactFiles(
|
||||
virtual Status CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names, const int output_level,
|
||||
@@ -399,54 +430,55 @@ class DBImpl : public DB {
|
||||
std::vector<std::string>* const output_file_names = nullptr,
|
||||
CompactionJobInfo* compaction_job_info = nullptr) override;
|
||||
|
||||
Status PauseBackgroundWork() override;
|
||||
Status ContinueBackgroundWork() override;
|
||||
virtual Status PauseBackgroundWork() override;
|
||||
virtual Status ContinueBackgroundWork() override;
|
||||
|
||||
Status EnableAutoCompaction(
|
||||
virtual Status EnableAutoCompaction(
|
||||
const std::vector<ColumnFamilyHandle*>& column_family_handles) override;
|
||||
|
||||
void EnableManualCompaction() override;
|
||||
void DisableManualCompaction() override;
|
||||
virtual void EnableManualCompaction() override;
|
||||
virtual void DisableManualCompaction() override;
|
||||
|
||||
using DB::SetOptions;
|
||||
Status SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
Status SetDBOptions(
|
||||
virtual Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
using DB::NumberLevels;
|
||||
int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
using DB::MaxMemCompactionLevel;
|
||||
int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) override;
|
||||
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) override;
|
||||
using DB::Level0StopWriteTrigger;
|
||||
int Level0StopWriteTrigger(ColumnFamilyHandle* column_family) override;
|
||||
const std::string& GetName() const override;
|
||||
Env* GetEnv() const override;
|
||||
FileSystem* GetFileSystem() const override;
|
||||
virtual int Level0StopWriteTrigger(
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Env* GetEnv() const override;
|
||||
virtual FileSystem* GetFileSystem() const override;
|
||||
using DB::GetOptions;
|
||||
Options GetOptions(ColumnFamilyHandle* column_family) const override;
|
||||
virtual Options GetOptions(ColumnFamilyHandle* column_family) const override;
|
||||
using DB::GetDBOptions;
|
||||
DBOptions GetDBOptions() const override;
|
||||
virtual DBOptions GetDBOptions() const override;
|
||||
using DB::Flush;
|
||||
Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
Status Flush(
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status Flush(
|
||||
const FlushOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
Status FlushWAL(bool sync) override {
|
||||
virtual 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();
|
||||
Status SyncWAL() override;
|
||||
Status LockWAL() override;
|
||||
Status UnlockWAL() override;
|
||||
virtual Status SyncWAL() override;
|
||||
virtual Status LockWAL() override;
|
||||
virtual Status UnlockWAL() override;
|
||||
|
||||
SequenceNumber GetLatestSequenceNumber() const override;
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
|
||||
// IncreaseFullHistoryTsLow(ColumnFamilyHandle*, std::string) will acquire
|
||||
// and release db_mutex
|
||||
@@ -458,21 +490,21 @@ class DBImpl : public DB {
|
||||
Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string* ts_low) override;
|
||||
|
||||
Status GetDbIdentity(std::string& identity) const override;
|
||||
virtual Status GetDbIdentity(std::string& identity) const override;
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
|
||||
|
||||
Status GetDbSessionId(std::string& session_id) const override;
|
||||
virtual Status GetDbSessionId(std::string& session_id) const override;
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override;
|
||||
|
||||
ColumnFamilyHandle* PersistentStatsColumnFamily() const;
|
||||
|
||||
Status Close() override;
|
||||
virtual Status Close() override;
|
||||
|
||||
Status DisableFileDeletions() override;
|
||||
virtual Status DisableFileDeletions() override;
|
||||
|
||||
Status EnableFileDeletions() override;
|
||||
virtual Status EnableFileDeletions(bool force) override;
|
||||
|
||||
virtual bool IsFileDeletionsEnabled() const;
|
||||
|
||||
@@ -481,35 +513,40 @@ class DBImpl : public DB {
|
||||
std::unique_ptr<StatsHistoryIterator>* stats_iterator) override;
|
||||
|
||||
using DB::ResetStats;
|
||||
Status ResetStats() override;
|
||||
virtual Status ResetStats() override;
|
||||
// All the returned filenames start with "/"
|
||||
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 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 GetUpdatesSince(
|
||||
virtual Status GetUpdatesSince(
|
||||
SequenceNumber seq_number, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options =
|
||||
TransactionLogIterator::ReadOptions()) override;
|
||||
Status DeleteFile(std::string name) override;
|
||||
virtual Status DeleteFile(std::string name) override;
|
||||
Status DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end = true);
|
||||
|
||||
void GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) override;
|
||||
virtual void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData>* metadata) override;
|
||||
|
||||
Status GetLiveFilesChecksumInfo(FileChecksumList* checksum_list) override;
|
||||
virtual Status GetLiveFilesChecksumInfo(
|
||||
FileChecksumList* checksum_list) override;
|
||||
|
||||
Status GetLiveFilesStorageInfo(
|
||||
virtual 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.
|
||||
void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
virtual void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
|
||||
void GetAllColumnFamilyMetaData(
|
||||
std::vector<ColumnFamilyMetaData>* metadata) override;
|
||||
@@ -521,32 +558,32 @@ class DBImpl : public DB {
|
||||
int target_level) override;
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
Status IngestExternalFile(
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override;
|
||||
|
||||
using DB::IngestExternalFiles;
|
||||
Status IngestExternalFiles(
|
||||
virtual Status IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& args) override;
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
Status CreateColumnFamilyWithImport(
|
||||
virtual 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;
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key,
|
||||
const Slice& end_key) override;
|
||||
virtual 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;
|
||||
Status VerifyChecksum(const ReadOptions& /*read_options*/) override;
|
||||
virtual 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
|
||||
@@ -567,16 +604,18 @@ class DBImpl : public DB {
|
||||
const ReadOptions& read_options);
|
||||
|
||||
using DB::StartTrace;
|
||||
Status StartTrace(const TraceOptions& options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) override;
|
||||
virtual Status StartTrace(
|
||||
const TraceOptions& options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) override;
|
||||
|
||||
using DB::EndTrace;
|
||||
Status EndTrace() override;
|
||||
virtual Status EndTrace() override;
|
||||
|
||||
using DB::NewDefaultReplayer;
|
||||
Status NewDefaultReplayer(const std::vector<ColumnFamilyHandle*>& handles,
|
||||
std::unique_ptr<TraceReader>&& reader,
|
||||
std::unique_ptr<Replayer>* replayer) override;
|
||||
virtual Status NewDefaultReplayer(
|
||||
const std::vector<ColumnFamilyHandle*>& handles,
|
||||
std::unique_ptr<TraceReader>&& reader,
|
||||
std::unique_ptr<Replayer>* replayer) override;
|
||||
|
||||
using DB::StartBlockCacheTrace;
|
||||
Status StartBlockCacheTrace(
|
||||
@@ -598,12 +637,14 @@ class DBImpl : public DB {
|
||||
Status EndIOTrace() override;
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
Status GetPropertiesOfAllTables(ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) override;
|
||||
Status GetPropertiesOfTablesInRange(
|
||||
virtual Status GetPropertiesOfAllTables(
|
||||
ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) override;
|
||||
virtual 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;
|
||||
|
||||
@@ -1140,8 +1181,6 @@ class DBImpl : public DB {
|
||||
|
||||
void TEST_UnlockMutex();
|
||||
|
||||
InstrumentedMutex* TEST_Mutex() { return &mutex_; }
|
||||
|
||||
void TEST_SignalAllBgCv();
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
@@ -2814,16 +2853,17 @@ class GetWithTimestampReadCallback : public ReadCallback {
|
||||
}
|
||||
};
|
||||
|
||||
Options SanitizeOptions(const std::string& db, const Options& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
extern Options SanitizeOptions(const std::string& db, const Options& 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 DBOptions SanitizeOptions(const std::string& db, const DBOptions& src,
|
||||
bool read_only = false,
|
||||
Status* logger_creation_s = nullptr);
|
||||
|
||||
CompressionType GetCompressionFlush(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
extern CompressionType GetCompressionFlush(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Return the earliest log file to keep after the memtable flush is
|
||||
// finalized.
|
||||
@@ -2831,13 +2871,13 @@ CompressionType GetCompressionFlush(const ImmutableCFOptions& ioptions,
|
||||
// `memtables_to_flush`) will be flushed and thus will not depend on any WAL
|
||||
// file.
|
||||
// The function is only applicable to 2pc mode.
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
extern 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.
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
extern 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,
|
||||
@@ -2845,21 +2885,21 @@ 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.
|
||||
uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
extern uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list);
|
||||
// For atomic flush.
|
||||
uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
extern 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.
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<MemTable*>& memtables_to_flush);
|
||||
// For atomic flush.
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
extern 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.
|
||||
UserKeyRange range(*begin, *end);
|
||||
Range range(*begin, *end);
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
s = cfd->RangesOverlapWithMemtables(
|
||||
{range}, super_version, immutable_db_options_.allow_data_in_errors,
|
||||
@@ -1419,9 +1419,10 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
|
||||
// Perform CompactFiles
|
||||
TEST_SYNC_POINT("TestCompactFiles::IngestExternalFile2");
|
||||
TEST_SYNC_POINT_CALLBACK("TestCompactFiles:PausingManualCompaction:3",
|
||||
static_cast<void*>(const_cast<std::atomic<int>*>(
|
||||
&manual_compaction_paused_)));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"TestCompactFiles:PausingManualCompaction:3",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<int>*>(&manual_compaction_paused_)));
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto* current = cfd->current();
|
||||
@@ -1694,7 +1695,7 @@ Status DBImpl::PauseBackgroundWork() {
|
||||
Status DBImpl::ContinueBackgroundWork() {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
if (bg_work_paused_ == 0) {
|
||||
return Status::InvalidArgument("Background work already unpaused");
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
assert(bg_work_paused_ > 0);
|
||||
assert(bg_compaction_paused_ > 0);
|
||||
@@ -3044,8 +3045,8 @@ void DBImpl::SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkFlush(void* arg) {
|
||||
FlushThreadArg fta = *(static_cast<FlushThreadArg*>(arg));
|
||||
delete static_cast<FlushThreadArg*>(arg);
|
||||
FlushThreadArg fta = *(reinterpret_cast<FlushThreadArg*>(arg));
|
||||
delete reinterpret_cast<FlushThreadArg*>(arg);
|
||||
|
||||
IOSTATS_SET_THREAD_POOL_ID(fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush");
|
||||
@@ -3054,8 +3055,8 @@ void DBImpl::BGWorkFlush(void* arg) {
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkCompaction(void* arg) {
|
||||
CompactionArg ca = *(static_cast<CompactionArg*>(arg));
|
||||
delete static_cast<CompactionArg*>(arg);
|
||||
CompactionArg ca = *(reinterpret_cast<CompactionArg*>(arg));
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::LOW);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkCompaction");
|
||||
auto prepicked_compaction =
|
||||
@@ -3079,12 +3080,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");
|
||||
static_cast<DBImpl*>(db)->BackgroundCallPurge();
|
||||
reinterpret_cast<DBImpl*>(db)->BackgroundCallPurge();
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkPurge:end");
|
||||
}
|
||||
|
||||
void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
CompactionArg* ca_ptr = static_cast<CompactionArg*>(arg);
|
||||
CompactionArg* ca_ptr = reinterpret_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
|
||||
@@ -3094,7 +3095,7 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
ca_ptr->db->bg_compaction_scheduled_--;
|
||||
}
|
||||
CompactionArg ca = *(ca_ptr);
|
||||
delete static_cast<CompactionArg*>(arg);
|
||||
delete reinterpret_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) {
|
||||
@@ -3114,14 +3115,14 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
|
||||
void DBImpl::UnscheduleFlushCallback(void* arg) {
|
||||
// Decrement bg_flush_scheduled_ in flush callback
|
||||
static_cast<FlushThreadArg*>(arg)->db_->bg_flush_scheduled_--;
|
||||
Env::Priority flush_pri = static_cast<FlushThreadArg*>(arg)->thread_pri_;
|
||||
reinterpret_cast<FlushThreadArg*>(arg)->db_->bg_flush_scheduled_--;
|
||||
Env::Priority flush_pri = reinterpret_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 static_cast<FlushThreadArg*>(arg);
|
||||
delete reinterpret_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 static_cast<void*>(w);
|
||||
return reinterpret_cast<void*>(w);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_EndWrite(void* w) {
|
||||
auto writer = static_cast<WriteThread::Writer*>(w);
|
||||
auto writer = reinterpret_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_INFO(immutable_db_options_.info_log,
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
my_disable_delete_obsolete_files);
|
||||
}
|
||||
@@ -67,14 +67,17 @@ Status DBImpl::DisableFileDeletionsWithLock() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::EnableFileDeletions() {
|
||||
Status DBImpl::EnableFileDeletions(bool force) {
|
||||
// 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 (disable_delete_obsolete_files_ > 0) {
|
||||
if (force) {
|
||||
// if force, we need to enable file deletions right away
|
||||
disable_delete_obsolete_files_ = 0;
|
||||
} else if (disable_delete_obsolete_files_ > 0) {
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
saved_counter = disable_delete_obsolete_files_;
|
||||
@@ -89,7 +92,7 @@ Status DBImpl::EnableFileDeletions() {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
ROCKS_LOG_WARN(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->internal_tbl_prop_coll_factories(),
|
||||
cfd->internal_comparator(), cfd->int_tbl_prop_collector_factories(),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
mutable_cf_options.compression_opts, cfd->GetID(), cfd->GetName(),
|
||||
0 /* level */, false /* is_bottommost */,
|
||||
|
||||
@@ -169,7 +169,8 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& _read_options,
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
SequenceNumber read_seq =
|
||||
read_options.snapshot != nullptr
|
||||
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot;
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
@@ -215,7 +216,8 @@ Status DBImplReadOnly::NewIterators(
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
SequenceNumber read_seq =
|
||||
read_options.snapshot != nullptr
|
||||
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot;
|
||||
|
||||
autovector<std::tuple<ColumnFamilyData*, SuperVersion*>> cfd_to_sv;
|
||||
|
||||
@@ -31,17 +31,18 @@ class DBImplReadOnly : public DBImpl {
|
||||
// TODO: Implement ReadOnly MultiGet?
|
||||
|
||||
using DBImpl::NewIterator;
|
||||
Iterator* NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Iterator* NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
|
||||
Status NewIterators(const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
virtual Status NewIterators(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
|
||||
using DBImpl::Put;
|
||||
Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
virtual 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.");
|
||||
}
|
||||
|
||||
@@ -58,36 +59,37 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
using DBImpl::Merge;
|
||||
Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
virtual 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;
|
||||
Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
virtual 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;
|
||||
Status SingleDelete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
virtual Status SingleDelete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
using DBImpl::CompactRange;
|
||||
Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/, const Slice* /*end*/) override {
|
||||
virtual 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;
|
||||
Status CompactFiles(
|
||||
virtual Status CompactFiles(
|
||||
const CompactionOptions& /*compact_options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*input_file_names*/,
|
||||
@@ -97,33 +99,33 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
Status DisableFileDeletions() override {
|
||||
virtual Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
Status EnableFileDeletions() override {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
virtual 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;
|
||||
Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
virtual Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DBImpl::SyncWAL;
|
||||
Status SyncWAL() override {
|
||||
virtual Status SyncWAL() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
Status IngestExternalFile(
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
@@ -131,7 +133,7 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
Status CreateColumnFamilyWithImport(
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
@@ -140,7 +142,7 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
Status CreateColumnFamilyWithImport(
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
@@ -150,9 +152,9 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
virtual 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() override {
|
||||
Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
|
||||
@@ -819,9 +819,6 @@ 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());
|
||||
@@ -831,10 +828,6 @@ 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);
|
||||
@@ -867,9 +860,6 @@ 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(
|
||||
@@ -881,8 +871,6 @@ 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;
|
||||
@@ -953,13 +941,9 @@ 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);
|
||||
|
||||
+7
-20
@@ -98,12 +98,7 @@ 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.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()) {
|
||||
if (!s.ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir %s\n",
|
||||
db_path.path.c_str(), s.ToString().c_str());
|
||||
continue;
|
||||
@@ -126,18 +121,12 @@ 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.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;
|
||||
if (!s.ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir %s\n", wal_dir.c_str(),
|
||||
s.ToString().c_str());
|
||||
return;
|
||||
}
|
||||
wal_info.clear();
|
||||
for (const std::string& file : files) {
|
||||
@@ -157,9 +146,7 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (log_wal_info) {
|
||||
Header(options.info_log, "Write Ahead Log file in %s: %s\n",
|
||||
wal_dir.c_str(), wal_info.c_str());
|
||||
}
|
||||
Header(options.info_log, "Write Ahead Log file in %s: %s\n", wal_dir.c_str(),
|
||||
wal_info.c_str());
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+8
-10
@@ -428,15 +428,13 @@ 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.
|
||||
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);
|
||||
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);
|
||||
|
||||
} // 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 = static_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = reinterpret_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 = static_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = reinterpret_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 = static_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = reinterpret_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 = static_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = reinterpret_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 = static_cast<IteratorWrapper*>(arg);
|
||||
IteratorWrapper* it = reinterpret_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) {
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("test status");
|
||||
*(reinterpret_cast<Status*>(arg)) = Status::Corruption("test status");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -3448,7 +3448,8 @@ TEST_F(DBIteratorTest, ErrorWhenReadFile) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::BeforeReturn",
|
||||
[&error_file](void* io_s_ptr) {
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(io_s_ptr);
|
||||
auto p =
|
||||
reinterpret_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);
|
||||
@@ -3528,7 +3529,8 @@ TEST_F(DBIteratorTest, ErrorWhenReadFile) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::AnyOffset", [&f1](void* pair_ptr) {
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p =
|
||||
reinterpret_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,13 +437,14 @@ 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 = static_cast<WriteThread::Writer*>(arg_leader);
|
||||
auto* leader = reinterpret_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 = static_cast<WriteThread::Writer*>(arg_follower);
|
||||
auto* follower =
|
||||
reinterpret_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);
|
||||
@@ -548,13 +549,14 @@ 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 = static_cast<WriteThread::Writer*>(arg_leader);
|
||||
auto* leader = reinterpret_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 = static_cast<WriteThread::Writer*>(arg_follower);
|
||||
auto* follower =
|
||||
reinterpret_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);
|
||||
@@ -660,7 +662,7 @@ TEST_F(DbKVChecksumWALToWriteBatchTest, WriteBatchChecksumHandoff) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RecoverLogFiles:BeforeUpdateProtectionInfo:batch",
|
||||
[&](void* batch_ptr) {
|
||||
WriteBatch* batch = static_cast<WriteBatch*>(batch_ptr);
|
||||
WriteBatch* batch = reinterpret_cast<WriteBatch*>(batch_ptr);
|
||||
content.assign(batch->Data().data(), batch->GetDataSize());
|
||||
Slice batch_content = batch->Data();
|
||||
// Corrupt first bit
|
||||
@@ -670,7 +672,7 @@ TEST_F(DbKVChecksumWALToWriteBatchTest, WriteBatchChecksumHandoff) {
|
||||
"DBImpl::RecoverLogFiles:BeforeUpdateProtectionInfo:checksum",
|
||||
[&](void* checksum_ptr) {
|
||||
// Verify that checksum is produced on the batch content
|
||||
uint64_t checksum = *static_cast<uint64_t*>(checksum_ptr);
|
||||
uint64_t checksum = *reinterpret_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());
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/false));
|
||||
|
||||
// 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 static_cast<const char*>(memchr(key.data(), '_', key.size()));
|
||||
return reinterpret_cast<const char*>(memchr(key.data(), '_', key.size()));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -287,7 +287,7 @@ TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
MockMemTableRep* rep =
|
||||
static_cast<MockMemTableRepFactory*>(options.memtable_factory.get())
|
||||
reinterpret_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 = static_cast<Compaction*>(arg);
|
||||
Compaction* c = reinterpret_cast<Compaction*>(arg);
|
||||
compression_used = c->output_compression();
|
||||
compression_opt_used = c->output_compression_opts();
|
||||
compacted = true;
|
||||
|
||||
@@ -106,18 +106,12 @@ TEST_F(DBPropertiesTest, Empty) {
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("0", num);
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/false));
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("0", num);
|
||||
|
||||
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_OK(db_->EnableFileDeletions(/*force=*/true));
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("1", num);
|
||||
@@ -1749,7 +1743,7 @@ TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
ASSERT_EQ(obsolete_sst_size, sst_size);
|
||||
|
||||
// Let the obsolete files be deleted.
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/false));
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kObsoleteSstFilesSize,
|
||||
&obsolete_sst_size));
|
||||
ASSERT_EQ(obsolete_sst_size, 0);
|
||||
|
||||
+27
-64
@@ -1036,6 +1036,9 @@ 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
|
||||
@@ -1104,9 +1107,14 @@ TEST_F(DBRangeDelTest, RangeTombstoneEndKeyAsSstableUpperBound) {
|
||||
options.level0_file_num_compaction_trigger = kNumFilesPerLevel;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(2 /* num_entries_flush */));
|
||||
// Compaction can generate files of size at most 2 * target_file_size_base.
|
||||
options.target_file_size_base = kValueBytes / 2;
|
||||
options.target_file_size_base = kValueBytes;
|
||||
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);
|
||||
|
||||
@@ -1146,13 +1154,6 @@ 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:
|
||||
@@ -1171,12 +1172,6 @@ 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());
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1195,20 +1190,6 @@ 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);
|
||||
@@ -2328,13 +2309,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 if sentinel in LevelIterator works
|
||||
// in L1_0. [4, 7) from L1_0 should cover 5 is sentinel works
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.target_file_size_base = 4 * 1024;
|
||||
options.target_file_size_base = 2 * 1024;
|
||||
options.level_compaction_dynamic_file_size = false;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -2354,7 +2335,6 @@ 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()));
|
||||
@@ -2362,11 +2342,6 @@ 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());
|
||||
@@ -2386,7 +2361,6 @@ TEST_F(DBRangeDelTest, NonOverlappingTombstonAtBoundary) {
|
||||
ASSERT_EQ(get_perf_context()->internal_range_del_reseek_count, 1);
|
||||
}
|
||||
delete iter;
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, OlderLevelHasNewerData) {
|
||||
@@ -3152,18 +3126,14 @@ 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 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.
|
||||
// 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.
|
||||
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
|
||||
@@ -3208,12 +3178,6 @@ 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);
|
||||
}
|
||||
@@ -3284,12 +3248,13 @@ 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 in
|
||||
// versions->ApproximateSize() to calculate compensated range tombstone size
|
||||
// non-bottommost level and goes through code path like compensate range
|
||||
// tombstone size.
|
||||
ASSERT_OK(Put(Key(1), "v1"));
|
||||
ASSERT_OK(Put(Key(4), "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -3312,12 +3277,6 @@ 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
|
||||
@@ -3366,6 +3325,7 @@ 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);
|
||||
@@ -3526,6 +3486,7 @@ 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);
|
||||
@@ -3605,7 +3566,8 @@ TEST_F(DBRangeDelTest, RangeDelReseekAfterFileReadError) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::BeforeReturn", [&fname](void* pair_ptr) {
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
if (p->first->find(fname) != std::string::npos) {
|
||||
*p->second = IOStatus::IOError();
|
||||
p->second->SetRetryable(true);
|
||||
@@ -3665,7 +3627,8 @@ TEST_F(DBRangeDelTest, RangeDelReseekAfterFileReadError) {
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::AnyOffset", [&fname](void* pair_ptr) {
|
||||
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
auto p =
|
||||
reinterpret_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 = static_cast<Status*>(arg);
|
||||
auto* s = reinterpret_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 = static_cast<Status*>(arg);
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
ASSERT_NOK(*s);
|
||||
});
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
@@ -1229,7 +1229,8 @@ TEST_F(DBSecondaryTest, StartFromInconsistent) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("Inject corruption");
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options1;
|
||||
@@ -1262,7 +1263,8 @@ TEST_F(DBSecondaryTest, InconsistencyDuringCatchUp) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(static_cast<Status*>(arg)) = Status::Corruption("Inject corruption");
|
||||
*(reinterpret_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 = static_cast<std::string*>(arg);
|
||||
std::string* filename = reinterpret_cast<std::string*>(arg);
|
||||
if (filename->find(".blob") != std::string::npos) {
|
||||
is_get_file_size_called = true;
|
||||
}
|
||||
|
||||
+46
-108
@@ -59,6 +59,9 @@ 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) {
|
||||
@@ -74,7 +77,8 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
if (table == 3) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WritePropertiesBlock:Meta", [&](void* meta) {
|
||||
*static_cast<const std::string**>(meta) = &kPropertiesBlockOldName;
|
||||
*reinterpret_cast<const std::string**>(meta) =
|
||||
&kPropertiesBlockOldName;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
}
|
||||
@@ -92,7 +96,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) { *static_cast<bool*>(skip_load) = true; });
|
||||
[&](void* skip_load) { *reinterpret_cast<bool*>(skip_load) = true; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// 1. Read table properties directly from file
|
||||
@@ -177,7 +181,9 @@ TEST_F(DBTablePropertiesTest, InvalidIgnored) {
|
||||
// Inject properties block data that Block considers invalid
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WritePropertiesBlock:BlockData",
|
||||
[&](void* block_data) { *static_cast<Slice*>(block_data) = Slice("X"); });
|
||||
[&](void* block_data) {
|
||||
*reinterpret_cast<Slice*>(block_data) = Slice("X");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Corrupting the table properties corrupts the unique id.
|
||||
@@ -230,109 +236,49 @@ TEST_F(DBTablePropertiesTest, CreateOnDeletionCollectorFactory) {
|
||||
ASSERT_EQ(0.5, del_factory->GetDeletionRatio());
|
||||
}
|
||||
|
||||
// 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) {}
|
||||
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));
|
||||
|
||||
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);
|
||||
// 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 : ranges) {
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (num_properties) {
|
||||
*num_properties = props.size();
|
||||
if (!in_range) {
|
||||
EXPECT_EQ(props.count(fn), 0);
|
||||
}
|
||||
|
||||
if (num_files) {
|
||||
*num_files = vmd.size();
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
bool enable_udt_ = false;
|
||||
Slice min_ts_ = MinU64Ts();
|
||||
};
|
||||
if (num_properties) {
|
||||
*num_properties = props.size();
|
||||
}
|
||||
|
||||
TEST_P(DBTablePropertiesInRangeTest, GetPropertiesOfTablesInRange) {
|
||||
if (num_files) {
|
||||
*num_files = vmd.size();
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
// Fixed random sead
|
||||
Random rnd(301);
|
||||
|
||||
@@ -350,21 +296,17 @@ TEST_P(DBTablePropertiesInRangeTest, 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++) {
|
||||
PutKeyValue(test::RandomKey(&rnd, 5), rnd.RandomString(102));
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), rnd.RandomString(102)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
if (NumTableFilesAtLevel(0) == 0) {
|
||||
PutKeyValue(test::RandomKey(&rnd, 5), rnd.RandomString(102));
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), rnd.RandomString(102)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
@@ -429,10 +371,6 @@ TEST_P(DBTablePropertiesInRangeTest, 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 = static_cast<ForwardIterator*>(arg);
|
||||
ForwardIterator* fiter = reinterpret_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 = static_cast<ForwardIterator*>(arg);
|
||||
ForwardIterator* fiter = reinterpret_cast<ForwardIterator*>(arg);
|
||||
ASSERT_TRUE(!file_iters_deleted ||
|
||||
fiter->TEST_CheckDeletedIters(&deleted_iters, &num_iters));
|
||||
});
|
||||
|
||||
+66
-55
@@ -2517,7 +2517,7 @@ TEST_F(DBTest, SnapshotFiles) {
|
||||
}
|
||||
|
||||
// release file snapshot
|
||||
ASSERT_OK(dbfull()->EnableFileDeletions());
|
||||
ASSERT_OK(dbfull()->EnableFileDeletions(/*force*/ false));
|
||||
// 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 = static_cast<MTThread*>(arg);
|
||||
MTThread* t = reinterpret_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 = static_cast<GCThread*>(arg);
|
||||
GCThread* t = reinterpret_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*/,
|
||||
std::string* /*timestamp*/) override {
|
||||
const Slice& key, PinnableSlice* /*value*/) override {
|
||||
return Status::NotSupported(key);
|
||||
}
|
||||
|
||||
using DB::GetMergeOperands;
|
||||
Status GetMergeOperands(const ReadOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& key, PinnableSlice* /*slice*/,
|
||||
GetMergeOperandsOptions* /*merge_operands_options*/,
|
||||
int* /*number_of_operands*/) override {
|
||||
virtual 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;
|
||||
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.");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
@@ -3137,7 +3137,7 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
Status CreateColumnFamilyWithImport(
|
||||
virtual 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;
|
||||
Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
virtual 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 =
|
||||
&(static_cast<const ModelSnapshot*>(options.snapshot)->map_);
|
||||
&(reinterpret_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 static_cast<const ModelSnapshot*>(snapshot);
|
||||
delete reinterpret_cast<const ModelSnapshot*>(snapshot);
|
||||
}
|
||||
|
||||
Status Write(const WriteOptions& /*options*/, WriteBatch* batch) override {
|
||||
@@ -3308,7 +3308,7 @@ class ModelDB : public DB {
|
||||
|
||||
void DisableManualCompaction() override {}
|
||||
|
||||
Status WaitForCompact(
|
||||
virtual 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() override { return Status::OK(); }
|
||||
Status EnableFileDeletions(bool /*force*/) override { return Status::OK(); }
|
||||
|
||||
Status GetLiveFiles(std::vector<std::string>&, uint64_t* /*size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
@@ -3381,7 +3381,8 @@ class ModelDB : public DB {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetCreationTimeOfOldestFile(uint64_t* /*creation_time*/) override {
|
||||
virtual Status GetCreationTimeOfOldestFile(
|
||||
uint64_t* /*creation_time*/) override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
@@ -4669,13 +4670,6 @@ 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
|
||||
@@ -5247,7 +5241,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
std::atomic<int> num_no(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
if (compaction->output_level() == 4) {
|
||||
ASSERT_TRUE(compaction->output_compression() == kLZ4Compression);
|
||||
num_lz4.fetch_add(1);
|
||||
@@ -5255,7 +5249,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:output_compression", [&](void* arg) {
|
||||
auto* compression = static_cast<CompressionType*>(arg);
|
||||
auto* compression = reinterpret_cast<CompressionType*>(arg);
|
||||
ASSERT_TRUE(*compression == kNoCompression);
|
||||
num_no.fetch_add(1);
|
||||
});
|
||||
@@ -5289,7 +5283,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
num_no.store(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
if (compaction->output_level() == 4 && compaction->start_level() == 3) {
|
||||
ASSERT_TRUE(compaction->output_compression() == kZlibCompression);
|
||||
num_zlib.fetch_add(1);
|
||||
@@ -5300,7 +5294,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:output_compression", [&](void* arg) {
|
||||
auto* compression = static_cast<CompressionType*>(arg);
|
||||
auto* compression = reinterpret_cast<CompressionType*>(arg);
|
||||
ASSERT_TRUE(*compression == kNoCompression);
|
||||
num_no.fetch_add(1);
|
||||
});
|
||||
@@ -5813,6 +5807,13 @@ 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) {
|
||||
@@ -6192,7 +6193,7 @@ TEST_F(DBTest, SuggestCompactRangeTest) {
|
||||
return "CompactionFilterFactoryGetContext";
|
||||
}
|
||||
static bool IsManual(CompactionFilterFactory* compaction_filter_factory) {
|
||||
return static_cast<CompactionFilterFactoryGetContext*>(
|
||||
return reinterpret_cast<CompactionFilterFactoryGetContext*>(
|
||||
compaction_filter_factory)
|
||||
->saved_context.is_manual_compaction;
|
||||
}
|
||||
@@ -7075,8 +7076,9 @@ TEST_F(DBTest, PinnableSliceAndRowCache) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(Get("foo"), "bar");
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
PinnableSlice pin_slice;
|
||||
@@ -7084,11 +7086,13 @@ TEST_F(DBTest, PinnableSliceAndRowCache) {
|
||||
ASSERT_EQ(pin_slice.ToString(), "bar");
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(), 0);
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ReusePinnableSlice) {
|
||||
@@ -7101,8 +7105,9 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(Get("foo"), "bar");
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
PinnableSlice pin_slice;
|
||||
@@ -7112,11 +7117,13 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(), 0);
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
std::vector<Slice> multiget_keys;
|
||||
@@ -7135,11 +7142,13 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(), 0);
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
|
||||
{
|
||||
std::vector<ColumnFamilyHandle*> multiget_cfs;
|
||||
@@ -7160,11 +7169,13 @@ TEST_F(DBTest, ReusePinnableSlice) {
|
||||
|
||||
// Entry is already in cache, lookup will remove the element from lru
|
||||
ASSERT_EQ(
|
||||
static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(), 0);
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
0);
|
||||
}
|
||||
// After PinnableSlice destruction element is added back in LRU
|
||||
ASSERT_EQ(static_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
ASSERT_EQ(
|
||||
reinterpret_cast<LRUCache*>(options.row_cache.get())->TEST_GetLRUSize(),
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
@@ -7323,7 +7334,7 @@ TEST_F(DBTest, CreationTimeOfOldestFile) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* arg) {
|
||||
TableProperties* props = static_cast<TableProperties*>(arg);
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
if (set_file_creation_time_to_zero) {
|
||||
if (idx == 0) {
|
||||
props->file_creation_time = 0;
|
||||
|
||||
+10
-8
@@ -2330,7 +2330,7 @@ TEST_F(DBTest2, MaxCompactionBytesTest) {
|
||||
}
|
||||
|
||||
static void UniqueIdCallback(void* arg) {
|
||||
int* result = static_cast<int*>(arg);
|
||||
int* result = reinterpret_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());
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/false));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
@@ -4242,7 +4242,7 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
|
||||
TraceExecutionResultHandler() = default;
|
||||
~TraceExecutionResultHandler() override = default;
|
||||
|
||||
Status Handle(const StatusOnlyTraceExecutionResult& result) override {
|
||||
virtual Status Handle(const StatusOnlyTraceExecutionResult& result) override {
|
||||
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
|
||||
return Status::InvalidArgument("Invalid timestamps.");
|
||||
}
|
||||
@@ -4260,7 +4260,8 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Handle(const SingleValueTraceExecutionResult& result) override {
|
||||
virtual Status Handle(
|
||||
const SingleValueTraceExecutionResult& result) override {
|
||||
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
|
||||
return Status::InvalidArgument("Invalid timestamps.");
|
||||
}
|
||||
@@ -4278,7 +4279,8 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Handle(const MultiValuesTraceExecutionResult& result) override {
|
||||
virtual Status Handle(
|
||||
const MultiValuesTraceExecutionResult& result) override {
|
||||
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
|
||||
return Status::InvalidArgument("Invalid timestamps.");
|
||||
}
|
||||
@@ -4298,7 +4300,7 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Handle(const IteratorTraceExecutionResult& result) override {
|
||||
virtual Status Handle(const IteratorTraceExecutionResult& result) override {
|
||||
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
|
||||
return Status::InvalidArgument("Invalid timestamps.");
|
||||
}
|
||||
@@ -6472,7 +6474,7 @@ class RenameCurrentTest : public DBTestBase,
|
||||
void SetupSyncPoints() {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [&](void* arg) {
|
||||
Status* s = static_cast<Status*>(arg);
|
||||
Status* s = reinterpret_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected IO error.");
|
||||
});
|
||||
@@ -7174,7 +7176,7 @@ TEST_F(DBTest2, PointInTimeRecoveryWithIOErrorWhileReadingWal) {
|
||||
"LogReader::ReadMore:AfterReadFile", [&](void* arg) {
|
||||
if (should_inject_error) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*static_cast<Status*>(arg) = Status::IOError("Injected IOError");
|
||||
*reinterpret_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) {}
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
virtual 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;
|
||||
}
|
||||
|
||||
Status Prefetch(uint64_t offset, size_t n) override {
|
||||
virtual 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) {}
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
if (Random::GetTLSInstance()->OneIn(fail_odd_)) {
|
||||
fail_cnt_->fetch_add(1);
|
||||
return Status::IOError("random error");
|
||||
@@ -474,7 +474,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
return target_->Read(offset, n, result, scratch);
|
||||
}
|
||||
|
||||
Status Prefetch(uint64_t offset, size_t n) override {
|
||||
virtual Status Prefetch(uint64_t offset, size_t n) override {
|
||||
return target_->Prefetch(offset, n);
|
||||
}
|
||||
|
||||
@@ -502,19 +502,19 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status NewSequentialFile(const std::string& f,
|
||||
std::unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
virtual 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) {}
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
counter_->Increment();
|
||||
return target_->Read(n, result, scratch);
|
||||
}
|
||||
Status Skip(uint64_t n) override { return target_->Skip(n); }
|
||||
virtual 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;
|
||||
}
|
||||
|
||||
void SleepForMicroseconds(int micros) override {
|
||||
virtual 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);
|
||||
}
|
||||
|
||||
Status GetCurrentTime(int64_t* unix_time) override {
|
||||
virtual 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;
|
||||
}
|
||||
|
||||
uint64_t NowCPUNanos() override {
|
||||
virtual uint64_t NowCPUNanos() override {
|
||||
now_cpu_count_.fetch_add(1);
|
||||
return target()->NowCPUNanos();
|
||||
}
|
||||
|
||||
uint64_t NowNanos() override {
|
||||
virtual uint64_t NowNanos() override {
|
||||
return (time_elapse_only_sleep_ ? 0 : target()->NowNanos()) +
|
||||
addon_microseconds_.load() * 1000;
|
||||
}
|
||||
|
||||
uint64_t NowMicros() override {
|
||||
virtual uint64_t NowMicros() override {
|
||||
return (time_elapse_only_sleep_ ? 0 : target()->NowMicros()) +
|
||||
addon_microseconds_.load();
|
||||
}
|
||||
|
||||
Status DeleteFile(const std::string& fname) override {
|
||||
virtual 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:
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
virtual 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;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "TestPutOperator"; }
|
||||
virtual 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 = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_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 = static_cast<Compaction*>(arg);
|
||||
Compaction* compaction = reinterpret_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());
|
||||
ASSERT_OK(db_->EnableFileDeletions(/*force=*/true));
|
||||
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) { *(static_cast<size_t*>(arg)) = 0; });
|
||||
[](void* arg) { *(reinterpret_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) {
|
||||
*(static_cast<size_t*>(arg)) = 0;
|
||||
*(reinterpret_cast<size_t*>(arg)) = 0;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -2610,10 +2610,10 @@ TEST_F(DataVisibilityTest, MultiGetWithoutSnapshot) {
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::MultiCFSnapshot:AfterGetSeqNum1",
|
||||
{"DBImpl::MultiGet:AfterGetSeqNum1",
|
||||
"DataVisibilityTest::MultiGetWithoutSnapshot:BeforePut"},
|
||||
{"DataVisibilityTest::MultiGetWithoutSnapshot:AfterPut",
|
||||
"DBImpl::MultiCFSnapshot:AfterGetSeqNum2"},
|
||||
"DBImpl::MultiGet: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 = static_cast<VersionEdit*>(arg); });
|
||||
[&](void* arg) { version_edit = reinterpret_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, ConfigurePersistUdtWithPut) {
|
||||
TEST_P(HandleFileBoundariesTest, ConfigurePersistUdt) {
|
||||
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, ConfigurePersistUdtWithPut) {
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), largest_ukey_without_ts, write_ts, "val2"));
|
||||
|
||||
// Create a L0 SST file and its record is added to the Manifest.
|
||||
// Create a L0 SST file and its record is added to the Manfiest.
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
|
||||
@@ -3571,61 +3571,6 @@ TEST_P(HandleFileBoundariesTest, ConfigurePersistUdtWithPut) {
|
||||
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(
|
||||
@@ -4061,80 +4006,42 @@ TEST_F(DBBasicTestWithTimestamp,
|
||||
Close();
|
||||
}
|
||||
|
||||
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) {
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, DeleteRangeBaiscReadAndIterate) {
|
||||
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 = std::get<0>(GetParam());
|
||||
bbto.index_type = GetParam();
|
||||
bbto.block_size = 100;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
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;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
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), write_ts));
|
||||
Key1(kRangeBegin), Key1(kRangeEnd),
|
||||
Timestamp(i, 0)));
|
||||
}
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key1(i), write_ts, "val" + std::to_string(i)));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key1(i), Timestamp(i, 0),
|
||||
"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;
|
||||
PutFixed64(&read_ts, kNum);
|
||||
std::string read_ts = Timestamp(kNum, 0);
|
||||
Slice read_ts_slice = read_ts;
|
||||
read_opts.timestamp = &read_ts_slice;
|
||||
{
|
||||
@@ -4169,43 +4076,33 @@ TEST_P(DeleteRangeWithTimestampTableOptions, BasicReadAndIterate) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(-1, expected);
|
||||
|
||||
// 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(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());
|
||||
}
|
||||
|
||||
read_ts.clear();
|
||||
PutFixed64(&read_ts, kNum);
|
||||
read_ts = Timestamp(kNum, 0);
|
||||
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());
|
||||
int_expected_ts = (persist_udt || kNum / 2 >= cutoff_ts) ? kNum / 2 : 0;
|
||||
ASSERT_EQ(timestamp, Timestamp(kNum / 2, 0));
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(value, "val" + std::to_string(i));
|
||||
int_expected_ts = (persist_udt || i >= cutoff_ts) ? i : 0;
|
||||
ASSERT_EQ(timestamp, Timestamp(i, 0));
|
||||
}
|
||||
expected_ts.clear();
|
||||
PutFixed64(&expected_ts, int_expected_ts);
|
||||
ASSERT_EQ(timestamp, expected_ts);
|
||||
}
|
||||
|
||||
size_t batch_size = kNum;
|
||||
@@ -4224,41 +4121,11 @@ TEST_P(DeleteRangeWithTimestampTableOptions, BasicReadAndIterate) {
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
if (i >= kRangeBegin && i < kNum / 2) {
|
||||
ASSERT_TRUE(statuses[i].IsNotFound());
|
||||
int_expected_ts = (persist_udt || kNum / 2 >= cutoff_ts) ? kNum / 2 : 0;
|
||||
ASSERT_EQ(timestamps[i], Timestamp(kNum / 2, 0));
|
||||
} else {
|
||||
ASSERT_OK(statuses[i]);
|
||||
ASSERT_EQ(values[i], "val" + std::to_string(i));
|
||||
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());
|
||||
ASSERT_EQ(timestamps[i], Timestamp(i, 0));
|
||||
}
|
||||
}
|
||||
Close();
|
||||
@@ -4737,4 +4604,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 = static_cast<Compaction*>(arg);
|
||||
const auto* compaction = reinterpret_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 = static_cast<WriteThread::Writer*>(arg);
|
||||
WriteThread::Writer* w = reinterpret_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 = static_cast<WriteThread::Writer*>(arg);
|
||||
WriteThread::Writer* w = reinterpret_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 = static_cast<WriteThread::Writer*>(arg);
|
||||
WriteThread::Writer* w = reinterpret_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 = static_cast<WriteThread::Writer*>(arg);
|
||||
WriteThread::Writer* w = reinterpret_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 = static_cast<WriteThread::Writer*>(arg);
|
||||
auto* w = reinterpret_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 = static_cast<WriteThread::Writer*>(arg);
|
||||
auto* w = reinterpret_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 = static_cast<WriteThread::WriteGroup*>(arg);
|
||||
auto* wg = reinterpret_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 = static_cast<WriteThread::WriteGroup*>(arg);
|
||||
auto* wg = reinterpret_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,13 +66,6 @@ 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));
|
||||
@@ -117,7 +110,6 @@ 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);
|
||||
@@ -125,17 +117,6 @@ 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);
|
||||
|
||||
+16
-70
@@ -11,7 +11,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -68,7 +67,9 @@ enum ValueType : unsigned char {
|
||||
kTypeDeletionWithTimestamp = 0x14,
|
||||
kTypeCommitXIDAndTimestamp = 0x15, // WAL only
|
||||
kTypeWideColumnEntity = 0x16,
|
||||
kTypeColumnFamilyWideColumnEntity = 0x17, // WAL only
|
||||
kTypeColumnFamilyWideColumnEntity = 0x17, // WAL only
|
||||
kTypeValuePreferredSeqno = 0x18, // Value with a unix write time
|
||||
kTypeColumnFamilyValuePreferredSeqno = 0x19, // WAL only
|
||||
kTypeMaxValid, // Should be after the last valid type, only used for
|
||||
// validation
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
@@ -78,37 +79,12 @@ 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;
|
||||
kTypeDeletionWithTimestamp == t || kTypeWideColumnEntity == t ||
|
||||
kTypeValuePreferredSeqno == t;
|
||||
}
|
||||
|
||||
// Checks whether a type is from user operation
|
||||
@@ -192,9 +168,6 @@ 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.
|
||||
@@ -214,15 +187,6 @@ 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.
|
||||
@@ -276,16 +240,6 @@ 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.
|
||||
@@ -870,19 +824,19 @@ class InternalKeySliceTransform : public SliceTransform {
|
||||
explicit InternalKeySliceTransform(const SliceTransform* transform)
|
||||
: transform_(transform) {}
|
||||
|
||||
const char* Name() const override { return transform_->Name(); }
|
||||
virtual const char* Name() const override { return transform_->Name(); }
|
||||
|
||||
Slice Transform(const Slice& src) const override {
|
||||
virtual Slice Transform(const Slice& src) const override {
|
||||
auto user_key = ExtractUserKey(src);
|
||||
return transform_->Transform(user_key);
|
||||
}
|
||||
|
||||
bool InDomain(const Slice& src) const override {
|
||||
virtual bool InDomain(const Slice& src) const override {
|
||||
auto user_key = ExtractUserKey(src);
|
||||
return transform_->InDomain(user_key);
|
||||
}
|
||||
|
||||
bool InRange(const Slice& dst) const override {
|
||||
virtual bool InRange(const Slice& dst) const override {
|
||||
auto user_key = ExtractUserKey(dst);
|
||||
return transform_->InRange(user_key);
|
||||
}
|
||||
@@ -932,25 +886,17 @@ 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`.
|
||||
// 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);
|
||||
RangeTombstone(Slice sk, Slice ek, SequenceNumber sn, Slice ts)
|
||||
: seq_(sn), ts_(ts) {
|
||||
assert(!ts.empty());
|
||||
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());
|
||||
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);
|
||||
}
|
||||
pinned_end_key_.append(ek.data(), ek.size() - ts.size());
|
||||
pinned_end_key_.append(ts.data(), ts.size());
|
||||
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 = static_cast<DeleteFileTest*>(arg);
|
||||
auto test = reinterpret_cast<DeleteFileTest*>(arg);
|
||||
test->env_->SleepForMicroseconds(2 * 1000 * 1000);
|
||||
}
|
||||
|
||||
|
||||
+82
-63
@@ -234,7 +234,7 @@ void ErrorHandler::CancelErrorRecovery() {
|
||||
// recovery gets scheduled at that point
|
||||
auto_recovery_ = false;
|
||||
SstFileManagerImpl* sfm =
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
reinterpret_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
if (sfm) {
|
||||
// This may or may not cancel a pending recovery
|
||||
db_mutex_->Unlock();
|
||||
@@ -277,6 +277,10 @@ 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");
|
||||
|
||||
@@ -361,28 +365,25 @@ 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:
|
||||
// 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) 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.
|
||||
// 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 (
|
||||
// DBImpl::ResumeImpl) may 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
|
||||
// will 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. This doesn't
|
||||
// call auto resume.
|
||||
// the compaction thread will reschedule a new compaction.
|
||||
// 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. 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.
|
||||
// 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.
|
||||
const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
@@ -395,9 +396,6 @@ 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 &&
|
||||
@@ -407,6 +405,14 @@ 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");
|
||||
@@ -430,9 +436,18 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&new_bg_io_err, db_mutex_,
|
||||
&auto_recovery);
|
||||
|
||||
RecordStats({ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT},
|
||||
{} /* int_histograms */);
|
||||
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);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set background retryable IO error\n");
|
||||
if (BackgroundErrorReason::kCompaction == reason) {
|
||||
@@ -441,18 +456,17 @@ 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.
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_COUNT}, {} /* int_histograms */);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_AUTORESUME_COUNT);
|
||||
}
|
||||
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_;
|
||||
}
|
||||
|
||||
Status::Severity severity;
|
||||
if (BackgroundErrorReason::kFlushNoWAL == reason ||
|
||||
BackgroundErrorReason::kManifestWriteNoWAL == reason) {
|
||||
} else 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
|
||||
@@ -460,17 +474,24 @@ 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.
|
||||
severity = Status::Severity::kSoftError;
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kSoftError);
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
soft_error_no_bg_work_ = true;
|
||||
context.flush_reason = FlushReason::kErrorRecoveryRetryFlush;
|
||||
recover_context_ = context;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
} else {
|
||||
severity = Status::Severity::kHardError;
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kHardError);
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
recover_context_ = context;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -534,7 +555,7 @@ Status ErrorHandler::OverrideNoSpaceError(const Status& bg_error,
|
||||
|
||||
void ErrorHandler::RecoverFromNoSpace() {
|
||||
SstFileManagerImpl* sfm =
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
reinterpret_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
|
||||
// Inform SFM of the error, so it can kick-off the recovery
|
||||
if (sfm) {
|
||||
@@ -572,7 +593,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("Recovery already in progress");
|
||||
return Status::Busy();
|
||||
}
|
||||
recovery_in_prog_ = true;
|
||||
|
||||
@@ -638,7 +659,9 @@ const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
db_mutex_->AssertHeld();
|
||||
return bg_error_;
|
||||
}
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_COUNT}, {} /* int_histograms */);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_AUTORESUME_COUNT);
|
||||
}
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Call StartRecoverFromRetryableBGIOError to resume\n");
|
||||
@@ -706,15 +729,19 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
recovery_error_ = IOStatus::OK();
|
||||
retry_count++;
|
||||
Status s = db_->ResumeImpl(context);
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT},
|
||||
{} /* int_histograms */);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT);
|
||||
}
|
||||
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;
|
||||
RecordStats({} /* ticker_types */,
|
||||
{{ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count}});
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
EventHelpers::NotifyOnErrorRecoveryEnd(db_options_.listeners, bg_error_,
|
||||
bg_error_, db_mutex_);
|
||||
return;
|
||||
@@ -737,15 +764,21 @@ 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");
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT},
|
||||
{{ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count}});
|
||||
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);
|
||||
}
|
||||
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;
|
||||
RecordStats({} /* ticker_types */,
|
||||
{{ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count}});
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
EventHelpers::NotifyOnErrorRecoveryEnd(
|
||||
db_options_.listeners, bg_error_,
|
||||
!recovery_error_.ok() ? recovery_error_ : s, db_mutex_);
|
||||
@@ -759,8 +792,10 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
db_options_.listeners, bg_error_,
|
||||
Status::Aborted("Exceeded resume retry count"), db_mutex_);
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:LoopOut");
|
||||
RecordStats({} /* ticker_types */,
|
||||
{{ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count}});
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorHandler::CheckAndSetRecoveryAndBGError(const Status& bg_err) {
|
||||
@@ -792,20 +827,4 @@ 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
|
||||
+1
-8
@@ -94,10 +94,6 @@ 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_;
|
||||
@@ -111,10 +107,7 @@ class ErrorHandler {
|
||||
std::unique_ptr<port::Thread> recovery_thread_;
|
||||
|
||||
InstrumentedMutex* db_mutex_;
|
||||
// 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.
|
||||
// A flag indicating whether automatic recovery from errors is enabled
|
||||
bool auto_recovery_;
|
||||
bool recovery_in_prog_;
|
||||
// A flag to indicate that for the soft error, we should not allow any
|
||||
|
||||
-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 = *(static_cast<size_t*>(arg));
|
||||
size_t fadvise_size = *(reinterpret_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 = static_cast<char*>(arg);
|
||||
char* buf = reinterpret_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 = *static_cast<uint64_t*>(arg);
|
||||
props_block_offset = *reinterpret_cast<uint64_t*>(arg);
|
||||
};
|
||||
const auto& get_props_block_size = [&](void* arg) {
|
||||
props_block_size = *static_cast<uint64_t*>(arg);
|
||||
props_block_size = *reinterpret_cast<uint64_t*>(arg);
|
||||
};
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
@@ -97,15 +97,7 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
}
|
||||
|
||||
if (ingestion_options_.ingest_behind && files_overlap_) {
|
||||
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.");
|
||||
return Status::NotSupported("Files have overlapping ranges");
|
||||
}
|
||||
|
||||
// Copy/Move external files into DB
|
||||
@@ -150,7 +142,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,
|
||||
"Tried to link file %s but it's not supported : %s",
|
||||
"Triy to link file %s but it's not supported : %s",
|
||||
path_outside_db.c_str(), status.ToString().c_str());
|
||||
}
|
||||
} else {
|
||||
@@ -196,7 +188,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
|
||||
// updated with the new global_seqno. After global_seqno is updated, DB will
|
||||
// upadted 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.
|
||||
@@ -307,7 +299,8 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
}
|
||||
}
|
||||
} else if (files_checksums.size() != files_checksum_func_names.size() ||
|
||||
files_checksums.size() != 0) {
|
||||
(files_checksums.size() == files_checksum_func_names.size() &&
|
||||
files_checksums.size() != 0)) {
|
||||
// The checksum or checksum function name vector are not both empty
|
||||
// and they are incomplete.
|
||||
status = Status::InvalidArgument(
|
||||
@@ -323,32 +316,59 @@ 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) {
|
||||
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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
Status status = cfd_->RangesOverlapWithMemtables(
|
||||
ranges, super_version, db_options_.allow_data_in_errors, flush_needed);
|
||||
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.");
|
||||
}
|
||||
if (status.ok() && *flush_needed &&
|
||||
!ingestion_options_.allow_blocking_flush) {
|
||||
status = Status::InvalidArgument("External file requires flush");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -367,7 +387,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
return status;
|
||||
}
|
||||
if (need_flush) {
|
||||
return Status::TryAgain("need_flush");
|
||||
return Status::TryAgain();
|
||||
}
|
||||
assert(status.ok() && need_flush == false);
|
||||
#endif
|
||||
@@ -411,26 +431,26 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (smallest_parsed.sequence == 0 && assigned_seqno != 0) {
|
||||
if (smallest_parsed.sequence == 0) {
|
||||
UpdateInternalKey(f.smallest_internal_key.rep(), assigned_seqno,
|
||||
smallest_parsed.type);
|
||||
}
|
||||
if (largest_parsed.sequence == 0 && assigned_seqno != 0) {
|
||||
if (largest_parsed.sequence == 0) {
|
||||
UpdateInternalKey(f.largest_internal_key.rep(), assigned_seqno,
|
||||
largest_parsed.type);
|
||||
}
|
||||
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, assigned_seqno);
|
||||
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) {
|
||||
assert(assigned_seqno == last_seqno + 1);
|
||||
last_seqno = assigned_seqno;
|
||||
++consumed_seqno_count_;
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = GenerateChecksumForIngestedFile(&f);
|
||||
if (!status.ok()) {
|
||||
@@ -611,7 +631,17 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
if (!status.ok()) {
|
||||
// We failed to add the files to the database
|
||||
// remove all the files we copied
|
||||
DeleteInternalFiles();
|
||||
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());
|
||||
}
|
||||
}
|
||||
consumed_seqno_count_ = 0;
|
||||
files_overlap_ = false;
|
||||
} else if (status.ok() && ingestion_options_.move_files) {
|
||||
@@ -629,21 +659,6 @@ 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) {
|
||||
@@ -871,19 +886,16 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
SequenceNumber* assigned_seqno) {
|
||||
Status status;
|
||||
*assigned_seqno = 0;
|
||||
auto ucmp = cfd_->user_comparator();
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
if (force_global_seqno || files_overlap_) {
|
||||
if (force_global_seqno) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
// 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 (compaction_style == kCompactionStyleUniversal || files_overlap_) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -935,6 +947,12 @@ 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) {
|
||||
@@ -949,16 +967,8 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
"ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile",
|
||||
&overlap_with_db);
|
||||
file_to_ingest->picked_level = target_level;
|
||||
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;
|
||||
}
|
||||
if (overlap_with_db && *assigned_seqno == 0) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -997,8 +1007,7 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
return Status::OK();
|
||||
} else if (!ingestion_options_.allow_global_seqno) {
|
||||
return Status::InvalidArgument("Global seqno is required, but disabled");
|
||||
} else if (ingestion_options_.write_global_seqno &&
|
||||
file_to_ingest->global_seqno_offset == 0) {
|
||||
} else if (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");
|
||||
@@ -1070,8 +1079,8 @@ IOStatus ExternalSstFileIngestionJob::GenerateChecksumForIngestedFile(
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
file_to_ingest->file_checksum = std::move(file_checksum);
|
||||
file_to_ingest->file_checksum_func_name = std::move(file_checksum_func_name);
|
||||
file_to_ingest->file_checksum = file_checksum;
|
||||
file_to_ingest->file_checksum_func_name = 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 should be ingested into
|
||||
// Id of column family this file shoule be ingested into
|
||||
uint32_t cf_id;
|
||||
// TableProperties read from external file
|
||||
TableProperties table_properties;
|
||||
@@ -102,7 +102,16 @@ class ExternalSstFileIngestionJob {
|
||||
assert(directories != nullptr);
|
||||
}
|
||||
|
||||
~ExternalSstFileIngestionJob() { UnregisterRange(); }
|
||||
~ExternalSstFileIngestionJob() {
|
||||
for (const auto& c : file_ingesting_compactions_) {
|
||||
cfd_->compaction_picker()->UnregisterCompaction(c);
|
||||
delete c;
|
||||
}
|
||||
|
||||
for (const auto& f : compaction_input_metdatas_) {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the job by copying external files into the DB.
|
||||
Status Prepare(const std::vector<std::string>& external_files_paths,
|
||||
@@ -147,7 +156,7 @@ class ExternalSstFileIngestionJob {
|
||||
return files_to_ingest_;
|
||||
}
|
||||
|
||||
// How many sequence numbers did we consume as part of the ingestion job?
|
||||
// How many sequence numbers did we consume as part of the ingest job?
|
||||
int ConsumedSequenceNumbersCount() const { return consumed_seqno_count_; }
|
||||
|
||||
private:
|
||||
@@ -194,9 +203,6 @@ 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,8 +1714,9 @@ TEST_F(ExternalSSTFileTest, WithUnorderedWrite) {
|
||||
{"DBImpl::WaitForPendingWrites:BeforeBlock",
|
||||
"DBImpl::WriteImpl:BeforeUnorderedWriteMemtable"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::IngestExternalFile:NeedFlush",
|
||||
[&](void* need_flush) { ASSERT_TRUE(*static_cast<bool*>(need_flush)); });
|
||||
"DBImpl::IngestExternalFile:NeedFlush", [&](void* need_flush) {
|
||||
ASSERT_TRUE(*reinterpret_cast<bool*>(need_flush));
|
||||
});
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.unordered_write = true;
|
||||
@@ -1847,92 +1848,6 @@ 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);
|
||||
@@ -3063,285 +2978,6 @@ 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_->internal_tbl_prop_coll_factories(),
|
||||
cfd_->internal_comparator(), cfd_->int_tbl_prop_collector_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