mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-08 11:55:21 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee95900680 | ||
|
|
e5451b30db | ||
|
|
877f8b43df | ||
|
|
05769ea7fb | ||
|
|
f103aadad9 | ||
|
|
7818d19169 | ||
|
|
6f8323a009 | ||
|
|
da478f3eae | ||
|
|
d36eda014f | ||
|
|
d24dd13024 | ||
|
|
e8f116deab | ||
|
|
a750b8a3a3 | ||
|
|
e7ac7363b4 | ||
|
|
fc9d4071f0 | ||
|
|
7711f8cbb4 | ||
|
|
8064a3ac31 | ||
|
|
cd50078ae0 | ||
|
|
93a0e9f3fa | ||
|
|
ffe1e4b820 | ||
|
|
875bfd75a0 | ||
|
|
f61df25cc2 | ||
|
|
1a8e9f0e07 | ||
|
|
5576ded762 | ||
|
|
288dfd0ba5 | ||
|
|
53c8f739fd | ||
|
|
c9042db619 | ||
|
|
93b1de4f45 | ||
|
|
5602b1d3d9 | ||
|
|
d247230aec | ||
|
|
5f2b661f54 | ||
|
|
0376869f05 | ||
|
|
21e71d1c73 | ||
|
|
f8bdd5797f | ||
|
|
9c6fb26033 | ||
|
|
1973fcba11 | ||
|
|
6bab278291 | ||
|
|
55a2105258 | ||
|
|
255aefb628 | ||
|
|
3dfee770c6 | ||
|
|
9110685e8c | ||
|
|
f62efb9d35 | ||
|
|
fb0a76a9e2 | ||
|
|
b2e53ab2d8 | ||
|
|
6892f19b11 | ||
|
|
b860a42158 | ||
|
|
fe31dc53ca | ||
|
|
677d2b4a8f | ||
|
|
a931bacf5d | ||
|
|
1c39b7952b | ||
|
|
0a563ae278 | ||
|
|
26a238f5b7 | ||
|
|
aa2b3bf675 | ||
|
|
2ee20a669d | ||
|
|
2e5f764294 | ||
|
|
538d2365e9 | ||
|
|
1b076e82db | ||
|
|
dfff1cecff | ||
|
|
65996dd757 | ||
|
|
2d3c626b62 | ||
|
|
2e51b33de9 | ||
|
|
393fc231af | ||
|
|
9a116ab4b4 | ||
|
|
7ae213f735 | ||
|
|
82670fb17b | ||
|
|
782fcc44e1 | ||
|
|
b448b71222 | ||
|
|
791723c1ec | ||
|
|
48b53441fd | ||
|
|
863c78d2c9 | ||
|
|
84228e21e8 | ||
|
|
cc1d4e3d33 |
No files matched your search
+71
-25
@@ -10,13 +10,6 @@ aliases:
|
||||
only_for_branches: main
|
||||
|
||||
commands:
|
||||
install-pyenv-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
name: Install pyenv on macos
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install pyenv
|
||||
|
||||
install-cmake-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
@@ -24,6 +17,13 @@ commands:
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
|
||||
|
||||
install-jdk8-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
name: Install JDK 8 on macos
|
||||
command: |
|
||||
brew install --cask adoptopenjdk/openjdk/adoptopenjdk8
|
||||
|
||||
increase-max-open-files-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
@@ -34,14 +34,8 @@ commands:
|
||||
sudo launchctl limit maxfiles 1048576
|
||||
|
||||
pre-steps:
|
||||
parameters:
|
||||
python-version:
|
||||
default: "3.5.9"
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- run: pyenv install --skip-existing <<parameters.python-version>>
|
||||
- run: pyenv global <<parameters.python-version>>
|
||||
- run:
|
||||
name: Setup Environment Variables
|
||||
command: |
|
||||
@@ -51,11 +45,15 @@ commands:
|
||||
echo "export GTEST_COLOR=1" >> $BASH_ENV
|
||||
echo "export CTEST_OUTPUT_ON_FAILURE=1" >> $BASH_ENV
|
||||
echo "export CTEST_TEST_TIMEOUT=300" >> $BASH_ENV
|
||||
echo "export ZLIB_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zlib" >> $BASH_ENV
|
||||
echo "export BZIP2_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/bzip2" >> $BASH_ENV
|
||||
echo "export SNAPPY_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/snappy" >> $BASH_ENV
|
||||
echo "export LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> $BASH_ENV
|
||||
echo "export ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> $BASH_ENV
|
||||
|
||||
pre-steps-macos:
|
||||
steps:
|
||||
- pre-steps:
|
||||
python-version: "3.6.0"
|
||||
- pre-steps
|
||||
|
||||
post-steps:
|
||||
steps:
|
||||
@@ -83,6 +81,17 @@ commands:
|
||||
echo "APT::Acquire::Retries \"10\";" | sudo tee -a /etc/apt/apt.conf.d/80-retries # llvm.org unreliable
|
||||
sudo apt-get update -y && sudo apt-get install -y clang-10
|
||||
|
||||
install-clang-13:
|
||||
steps:
|
||||
- run:
|
||||
name: Install Clang 13
|
||||
command: |
|
||||
echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" | sudo tee -a /etc/apt/sources.list
|
||||
echo "APT::Acquire::Retries \"10\";" | sudo tee -a /etc/apt/apt.conf.d/80-retries # llvm.org unreliable
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
sudo apt-get update -y && sudo apt-get install -y clang-13
|
||||
|
||||
install-gflags:
|
||||
steps:
|
||||
- run:
|
||||
@@ -144,11 +153,12 @@ executors:
|
||||
jobs:
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: 11.3.0
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
environment:
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc cause env_test hang, disable it for now
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-pyenv-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run: ulimit -S -n 1048576 && OPT=-DCIRCLECI make V=1 J=32 -j32 check 2>&1 | .circleci/cat_ignore_eagain
|
||||
@@ -156,11 +166,10 @@ jobs:
|
||||
|
||||
build-macos-cmake:
|
||||
macos:
|
||||
xcode: 11.3.0
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-pyenv-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
@@ -396,6 +405,16 @@ jobs:
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 SKIP_LINK=1 make -j16 all 2>&1 | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- post-steps
|
||||
|
||||
build-linux-clang-13-no_test_run:
|
||||
machine:
|
||||
image: ubuntu-2004:202010-01
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-clang-13
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 make -j16 all 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
|
||||
build-linux-microbench:
|
||||
machine:
|
||||
@@ -522,14 +541,15 @@ jobs:
|
||||
|
||||
build-macos-java:
|
||||
macos:
|
||||
xcode: 11.3.0
|
||||
xcode: 12.5.1
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1 # jemalloc causes java 8 crash
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-pyenv-on-macos
|
||||
- install-gflags-on-macos
|
||||
- install-jdk8-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
@@ -548,15 +568,15 @@ jobs:
|
||||
|
||||
build-macos-java-static:
|
||||
macos:
|
||||
xcode: 11.3.0
|
||||
xcode: 12.5.1
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-pyenv-on-macos
|
||||
- install-gflags-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-jdk8-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
@@ -566,8 +586,32 @@ jobs:
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Static Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastatic 2>&1 | .circleci/cat_ignore_eagain
|
||||
name: "Build RocksDBJava x86 and ARM Static Libraries"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastaticosx 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static-universal:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-gflags-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-jdk8-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Universal Binary Static Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastaticosx_ub 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-examples:
|
||||
@@ -772,6 +816,7 @@ workflows:
|
||||
- build-linux-java-static
|
||||
- build-macos-java
|
||||
- build-macos-java-static
|
||||
- build-macos-java-static-universal
|
||||
build-examples:
|
||||
jobs:
|
||||
- build-examples
|
||||
@@ -792,6 +837,7 @@ workflows:
|
||||
build-linux-compilers-no_test_run:
|
||||
jobs:
|
||||
- build-linux-clang-no_test_run
|
||||
- build-linux-clang-13-no_test_run
|
||||
- build-linux-gcc-4_8-no_test_run
|
||||
- build-linux-gcc-8-no_test_run
|
||||
- build-linux-gcc-9-no_test_run
|
||||
|
||||
+12
-15
@@ -352,25 +352,17 @@ int main() {
|
||||
return 0;
|
||||
}
|
||||
" BUILTIN_ATOMIC)
|
||||
if (NOT BUILTIN_ATOMIC)
|
||||
#TODO: Check if -latomic exists
|
||||
list(APPEND THIRDPARTY_LIBS atomic)
|
||||
endif()
|
||||
if (NOT BUILTIN_ATOMIC)
|
||||
#TODO: Check if -latomic exists
|
||||
list(APPEND THIRDPARTY_LIBS atomic)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WITH_LIBURING)
|
||||
set(CMAKE_REQUIRED_FLAGS "-luring")
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
" HAS_LIBURING)
|
||||
if (HAS_LIBURING)
|
||||
find_package(uring)
|
||||
if (uring_FOUND)
|
||||
add_definitions(-DROCKSDB_IOURING_PRESENT)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -luring")
|
||||
list(APPEND THIRDPARTY_LIBS uring::uring)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -624,6 +616,11 @@ if(HAVE_AUXV_GETAUXVAL)
|
||||
add_definitions(-DROCKSDB_AUXV_GETAUXVAL_PRESENT)
|
||||
endif()
|
||||
|
||||
check_cxx_symbol_exists(F_FULLFSYNC "fcntl.h" HAVE_FULLFSYNC)
|
||||
if(HAVE_FULLFSYNC)
|
||||
add_definitions(-DHAVE_FULLFSYNC)
|
||||
endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# RocksDB default options change log
|
||||
# RocksDB default options change log (NO LONGER MAINTAINED)
|
||||
## Unreleased
|
||||
* delayed_write_rate takes the rate given by rate_limiter if not specified.
|
||||
|
||||
|
||||
+45
@@ -1,7 +1,49 @@
|
||||
# Rocksdb Change Log
|
||||
## 6.29.3 (02/17/2022)
|
||||
### Bug Fixes
|
||||
* Fix a data loss bug for 2PC write-committed transaction caused by concurrent transaction commit and memtable switch (#9571).
|
||||
|
||||
## 6.29.2 (02/15/2022)
|
||||
### Performance Improvements
|
||||
* DisableManualCompaction() doesn't have to wait scheduled manual compaction to be executed in thread-pool to cancel the job.
|
||||
|
||||
## 6.29.1 (01/31/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a major bug in which batched MultiGet could return old values for keys deleted by DeleteRange when memtable Bloom filter is enabled (memtable_prefix_bloom_size_ratio > 0). (The fix includes a substantial MultiGet performance improvement in the unusual case of both memtable_whole_key_filtering and prefix_extractor.)
|
||||
|
||||
## 6.29.0 (01/21/2022)
|
||||
Note: The next release will be major release 7.0. See https://github.com/facebook/rocksdb/issues/9390 for more info.
|
||||
### Public API change
|
||||
* Added values to `TraceFilterType`: `kTraceFilterIteratorSeek`, `kTraceFilterIteratorSeekForPrev`, and `kTraceFilterMultiGet`. They can be set in `TraceOptions` to filter out the operation types after which they are named.
|
||||
* Added `TraceOptions::preserve_write_order`. When enabled it guarantees write records are traced in the same order they are logged to WAL and applied to the DB. By default it is disabled (false) to match the legacy behavior and prevent regression.
|
||||
* Made the Env class extend the Customizable class. Implementations need to be registered with the ObjectRegistry and to implement a Name() method in order to be created via this method.
|
||||
* `Options::OldDefaults` is marked deprecated, as it is no longer maintained.
|
||||
* Add ObjectLibrary::AddFactory and ObjectLibrary::PatternEntry classes. This method and associated class are the preferred mechanism for registering factories with the ObjectLibrary going forward. The ObjectLibrary::Register method, which uses regular expressions and may be problematic, is deprecated and will be in a future release.
|
||||
* Changed `BlockBasedTableOptions::block_size` from `size_t` to `uint64_t`.
|
||||
* Added API warning against using `Iterator::Refresh()` together with `DB::DeleteRange()`, which are incompatible and have always risked causing the refreshed iterator to return incorrect results.
|
||||
|
||||
### Behavior Changes
|
||||
* `DB::DestroyColumnFamilyHandle()` will return Status::InvalidArgument() if called with `DB::DefaultColumnFamily()`.
|
||||
* On 32-bit platforms, mmap reads are no longer quietly disabled, just discouraged.
|
||||
|
||||
### New Features
|
||||
* Added `Options::DisableExtraChecks()` that can be used to improve peak write performance by disabling checks that should not be necessary in the absence of software logic errors or CPU+memory hardware errors. (Default options are slowly moving toward some performance overheads for extra correctness checking.)
|
||||
|
||||
### Performance Improvements
|
||||
* Improved read performance when a prefix extractor is used (Seek, Get, MultiGet), even compared to version 6.25 baseline (see bug fix below), by optimizing the common case of prefix extractor compatible with table file and unchanging.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug that FlushMemTable may return ok even flush not succeed.
|
||||
* Fixed a bug of Sync() and Fsync() not using `fcntl(F_FULLFSYNC)` on OS X and iOS.
|
||||
* Fixed a significant performance regression in version 6.26 when a prefix extractor is used on the read path (Seek, Get, MultiGet). (Excessive time was spent in SliceTransform::AsString().)
|
||||
|
||||
### New Features
|
||||
* Added RocksJava support for MacOS universal binary (ARM+x86)
|
||||
|
||||
## 6.28.0 (2021-12-17)
|
||||
### New Features
|
||||
* Introduced 'CommitWithTimestamp' as a new tag. Currently, there is no API for user to trigger a write with this tag to the WAL. This is part of the efforts to support write-commited transactions with user-defined timestamps.
|
||||
* Introduce SimulatedHybridFileSystem which can help simulating HDD latency in db_bench. Tiered Storage latency simulation can be enabled using -simulate_hybrid_fs_file (note that it doesn't work if db_bench is interrupted in the middle). -simulate_hdd can also be used to simulate all files on HDD.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a bug in rocksdb automatic implicit prefetching which got broken because of new feature adaptive_readahead and internal prefetching got disabled when iterator moves from one file to next.
|
||||
@@ -10,6 +52,7 @@
|
||||
* Fixed a bug causing two duplicate entries to be appended to a file opened in non-direct mode and tracked by `FaultInjectionTestFS`.
|
||||
* Fixed a bug in TableOptions.prepopulate_block_cache to support block-based filters also.
|
||||
* Block cache keys no longer use `FSRandomAccessFile::GetUniqueId()` (previously used when available), so a filesystem recycling unique ids can no longer lead to incorrect result or crash (#7405). For files generated by RocksDB >= 6.24, the cache keys are stable across DB::Open and DB directory move / copy / import / export / migration, etc. Although collisions are still theoretically possible, they are (a) impossible in many common cases, (b) not dependent on environmental factors, and (c) much less likely than a CPU miscalculation while executing RocksDB.
|
||||
* Fixed a bug in C bindings causing iterator to return incorrect result (#9343).
|
||||
|
||||
### Behavior Changes
|
||||
* MemTableList::TrimHistory now use allocated bytes when max_write_buffer_size_to_maintain > 0(default in TrasactionDB, introduced in PR#5022) Fix #8371.
|
||||
@@ -17,6 +60,8 @@
|
||||
### Public API change
|
||||
* Extend WriteBatch::AssignTimestamp and AssignTimestamps API so that both functions can accept an optional `checker` argument that performs additional checking on timestamp sizes.
|
||||
* Introduce a new EventListener callback that will be called upon the end of automatic error recovery.
|
||||
* Add IncreaseFullHistoryTsLow API so users can advance each column family's full_history_ts_low seperately.
|
||||
* Add GetFullHistoryTsLow API so users can query current full_history_low value of specified column family.
|
||||
|
||||
### Performance Improvements
|
||||
* Replaced map property `TableProperties::properties_offsets` with uint64_t property `external_sst_file_global_seqno_offset` to save table properties's memory.
|
||||
|
||||
@@ -134,12 +134,6 @@ CFLAGS += -DHAVE_POWER8
|
||||
HAVE_POWER8=1
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -march=armv8-a+crc+crypto -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -march=armv8-a+crc+crypto
|
||||
CFLAGS += -march=armv8-a+crc+crypto
|
||||
ARMCRC_SOURCE=1
|
||||
endif
|
||||
|
||||
# if we're compiling for shared libraries, add the shared flags
|
||||
ifeq ($(LIB_MODE),shared)
|
||||
CXXFLAGS += $(PLATFORM_SHARED_CFLAGS) -DROCKSDB_DLL
|
||||
@@ -253,7 +247,7 @@ ROCKSDB_PLUGIN_PKGCONFIG_REQUIRES = $(foreach plugin, $(ROCKSDB_PLUGINS), $($(pl
|
||||
PLATFORM_LDFLAGS += $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_LDFLAGS))
|
||||
CXXFLAGS += $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_CXXFLAGS))
|
||||
|
||||
ifneq ($(ROCKSDB_PLUGIN_PKGCONFIG_REQUIRES),)
|
||||
ifneq ($(strip $(ROCKSDB_PLUGIN_PKGCONFIG_REQUIRES)),)
|
||||
LDFLAGS := $(LDFLAGS) $(shell pkg-config --libs $(ROCKSDB_PLUGIN_PKGCONFIG_REQUIRES))
|
||||
ifneq ($(.SHELLSTATUS),0)
|
||||
$(error pkg-config failed)
|
||||
@@ -264,6 +258,16 @@ $(error pkg-config failed)
|
||||
endif
|
||||
endif
|
||||
|
||||
CXXFLAGS += $(ARCHFLAG)
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -march=armv8-a+crc+crypto -xc /dev/null 2>&1))
|
||||
ifneq ($(PLATFORM),OS_MACOSX)
|
||||
CXXFLAGS += -march=armv8-a+crc+crypto
|
||||
CFLAGS += -march=armv8-a+crc+crypto
|
||||
ARMCRC_SOURCE=1
|
||||
endif
|
||||
endif
|
||||
|
||||
export JAVAC_ARGS
|
||||
CLEAN_FILES += make_config.mk rocksdb.pc
|
||||
|
||||
@@ -313,6 +317,19 @@ ifeq ($(LIB_MODE),shared)
|
||||
EXEC_LDFLAGS += -Wl,-rpath -Wl,'$$ORIGIN'
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
ifeq ($(ARCHFLAG), -arch arm64)
|
||||
ifneq ($(MACHINE), arm64)
|
||||
# If we're building on a non-arm64 machine but targeting arm64 Mac, we need to disable
|
||||
# linking with jemalloc (as it won't be arm64-compatible) and remove some other options
|
||||
# set during platform detection
|
||||
DISABLE_JEMALLOC=1
|
||||
PLATFORM_CFLAGS := $(filter-out -march=native -DHAVE_SSE42, $(PLATFORM_CFLAGS))
|
||||
PLATFORM_CXXFLAGS := $(filter-out -march=native -DHAVE_SSE42, $(PLATFORM_CXXFLAGS))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# ASAN doesn't work well with jemalloc. If we're compiling with ASAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_ASAN
|
||||
DISABLE_JEMALLOC=1
|
||||
@@ -2031,7 +2048,6 @@ gen-pc:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Jni stuff
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
@@ -2056,11 +2072,13 @@ ifneq ($(origin JNI_LIBC), undefined)
|
||||
JNI_LIBC_POSTFIX = -$(JNI_LIBC)
|
||||
endif
|
||||
|
||||
ifeq (,$(ROCKSDBJNILIB))
|
||||
ifneq (,$(filter ppc% s390x arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE)$(JNI_LIBC_POSTFIX).so
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
|
||||
endif
|
||||
endif
|
||||
ROCKSDB_JAVA_VERSION ?= $(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux$(ARCH)$(JNI_LIBC_POSTFIX).jar
|
||||
ROCKSDB_JAR_ALL = rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar
|
||||
@@ -2086,7 +2104,13 @@ ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
ifeq (,$(findstring librocksdbjni-osx,$(ROCKSDBJNILIB)))
|
||||
ifeq ($(MACHINE),arm64)
|
||||
ROCKSDBJNILIB = librocksdbjni-osx-aarch64.jnilib
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-osx.jnilib
|
||||
endif
|
||||
endif
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-osx.jar
|
||||
SHA256_CMD = openssl sha256 -r
|
||||
ifneq ("$(wildcard $(JAVA_HOME)/include/darwin)","")
|
||||
@@ -2095,6 +2119,7 @@ else
|
||||
JAVA_INCLUDE = -I/System/Library/Frameworks/JavaVM.framework/Headers/
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_FREEBSD)
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/freebsd
|
||||
ROCKSDBJNILIB = librocksdbjni-freebsd$(ARCH).so
|
||||
@@ -2129,7 +2154,11 @@ zlib-$(ZLIB_VER).tar.gz:
|
||||
libz.a: zlib-$(ZLIB_VER).tar.gz
|
||||
-rm -rf zlib-$(ZLIB_VER)
|
||||
tar xvzf zlib-$(ZLIB_VER).tar.gz
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' ./configure --static && $(MAKE)
|
||||
if [ -n"$(ARCHFLAG)" ]; then \
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' ./configure --static --archs="$(ARCHFLAG)" && $(MAKE); \
|
||||
else \
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' ./configure --static && $(MAKE); \
|
||||
fi
|
||||
cp zlib-$(ZLIB_VER)/libz.a .
|
||||
|
||||
bzip2-$(BZIP2_VER).tar.gz:
|
||||
@@ -2143,7 +2172,7 @@ bzip2-$(BZIP2_VER).tar.gz:
|
||||
libbz2.a: bzip2-$(BZIP2_VER).tar.gz
|
||||
-rm -rf bzip2-$(BZIP2_VER)
|
||||
tar xvzf bzip2-$(BZIP2_VER).tar.gz
|
||||
cd bzip2-$(BZIP2_VER) && $(MAKE) CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64 ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' AR='ar ${EXTRA_ARFLAGS}'
|
||||
cd bzip2-$(BZIP2_VER) && $(MAKE) CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64 $(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' AR='ar ${EXTRA_ARFLAGS}' libbz2.a
|
||||
cp bzip2-$(BZIP2_VER)/libbz2.a .
|
||||
|
||||
snappy-$(SNAPPY_VER).tar.gz:
|
||||
@@ -2158,7 +2187,7 @@ libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
mkdir snappy-$(SNAPPY_VER)/build
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
|
||||
|
||||
lz4-$(LZ4_VER).tar.gz:
|
||||
@@ -2172,7 +2201,7 @@ lz4-$(LZ4_VER).tar.gz:
|
||||
liblz4.a: lz4-$(LZ4_VER).tar.gz
|
||||
-rm -rf lz4-$(LZ4_VER)
|
||||
tar xvzf lz4-$(LZ4_VER).tar.gz
|
||||
cd lz4-$(LZ4_VER)/lib && $(MAKE) CFLAGS='-fPIC -O2 ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' all
|
||||
cd lz4-$(LZ4_VER)/lib && $(MAKE) CFLAGS='-fPIC -O2 $(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' all
|
||||
cp lz4-$(LZ4_VER)/lib/liblz4.a .
|
||||
|
||||
zstd-$(ZSTD_VER).tar.gz:
|
||||
@@ -2186,7 +2215,7 @@ zstd-$(ZSTD_VER).tar.gz:
|
||||
libzstd.a: zstd-$(ZSTD_VER).tar.gz
|
||||
-rm -rf zstd-$(ZSTD_VER)
|
||||
tar xvzf zstd-$(ZSTD_VER).tar.gz
|
||||
cd zstd-$(ZSTD_VER)/lib && DESTDIR=. PREFIX= $(MAKE) CFLAGS='-fPIC -O2 ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' libzstd.a
|
||||
cd zstd-$(ZSTD_VER)/lib && DESTDIR=. PREFIX= $(MAKE) CFLAGS='-fPIC -O2 $(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' libzstd.a
|
||||
cp zstd-$(ZSTD_VER)/lib/libzstd.a .
|
||||
|
||||
# A version of each $(LIB_OBJECTS) compiled with -fPIC and a fixed set of static compression libraries
|
||||
@@ -2208,7 +2237,37 @@ endif
|
||||
$(MAKE) rocksdbjavastatic_deps
|
||||
$(MAKE) rocksdbjavastatic_libobjects
|
||||
$(MAKE) rocksdbjavastatic_javalib
|
||||
$(MAKE) rocksdbjavastatic_jar
|
||||
|
||||
rocksdbjavastaticosx: rocksdbjavastaticosx_archs
|
||||
mv java/target/librocksdbjni-osx-x86_64.jnilib java/target/librocksdbjni-osx.jnilib
|
||||
mv java/target/librocksdbjni-osx-arm64.jnilib java/target/librocksdbjni-osx-aarch64.jnilib
|
||||
|
||||
rocksdbjavastaticosx_ub: rocksdbjavastaticosx_archs
|
||||
lipo -create -output ./java/target/$(ROCKSDBJNILIB) java/target/librocksdbjni-osx-x86_64.jnilib java/target/librocksdbjni-osx-arm64.jnilib
|
||||
$(MAKE) rocksdbjavastatic_jar
|
||||
|
||||
rocksdbjavastaticosx_archs:
|
||||
$(MAKE) rocksdbjavastaticosx_arch_x86_64
|
||||
$(MAKE) rocksdbjavastaticosx_arch_arm64
|
||||
|
||||
rocksdbjavastaticosx_arch_%:
|
||||
ifeq ($(JAVA_HOME),)
|
||||
$(error JAVA_HOME is not set)
|
||||
endif
|
||||
$(MAKE) clean-ext-libraries-bin
|
||||
$(MAKE) clean-rocks
|
||||
ARCHFLAG="-arch $*" $(MAKE) rocksdbjavastatic_deps
|
||||
ARCHFLAG="-arch $*" $(MAKE) rocksdbjavastatic_libobjects
|
||||
ARCHFLAG="-arch $*" ROCKSDBJNILIB="librocksdbjni-osx-$*.jnilib" $(MAKE) rocksdbjavastatic_javalib
|
||||
|
||||
ifeq ($(JAR_CMD),)
|
||||
ifneq ($(JAVA_HOME),)
|
||||
JAR_CMD := $(JAVA_HOME)/bin/jar
|
||||
else
|
||||
JAR_CMD := jar
|
||||
endif
|
||||
endif
|
||||
rocksdbjavastatic_javalib:
|
||||
cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) javalib
|
||||
rm -f java/target/$(ROCKSDBJNILIB)
|
||||
@@ -2219,11 +2278,13 @@ rocksdbjavastatic_javalib:
|
||||
cd java/target;if [ "$(DEBUG_LEVEL)" == "0" ]; then \
|
||||
strip $(STRIPFLAGS) $(ROCKSDBJNILIB); \
|
||||
fi
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
cd java/target/apidocs;jar -cf ../$(ROCKSDB_JAVADOCS_JAR) *
|
||||
cd java/src/main/java;jar -cf ../../../target/$(ROCKSDB_SOURCES_JAR) org
|
||||
|
||||
rocksdbjavastatic_jar:
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
cd java/target/apidocs; $(JAR_CMD) -cf ../$(ROCKSDB_JAVADOCS_JAR) *
|
||||
cd java/src/main/java; $(JAR_CMD) -cf ../../../target/$(ROCKSDB_SOURCES_JAR) org
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
openssl sha1 java/target/$(ROCKSDB_JAVADOCS_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAVADOCS_JAR).sha1
|
||||
openssl sha1 java/target/$(ROCKSDB_SOURCES_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_SOURCES_JAR).sha1
|
||||
@@ -2232,17 +2293,17 @@ rocksdbjavastatic_deps: $(JAVA_COMPRESSIONS)
|
||||
|
||||
rocksdbjavastatic_libobjects: $(LIB_OBJECTS)
|
||||
|
||||
rocksdbjavastaticrelease: rocksdbjavastatic
|
||||
rocksdbjavastaticrelease: rocksdbjavastaticosx
|
||||
cd java/crossbuild && (vagrant destroy -f || true) && vagrant up linux32 && vagrant halt linux32 && vagrant up linux64 && vagrant halt linux64 && vagrant up linux64-musl && vagrant halt linux64-musl
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR_ALL) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR_ALL).sha1
|
||||
|
||||
rocksdbjavastaticreleasedocker: rocksdbjavastatic rocksdbjavastaticdockerx86 rocksdbjavastaticdockerx86_64 rocksdbjavastaticdockerx86musl rocksdbjavastaticdockerx86_64musl
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
rocksdbjavastaticreleasedocker: rocksdbjavastaticosx rocksdbjavastaticdockerx86 rocksdbjavastaticdockerx86_64 rocksdbjavastaticdockerx86musl rocksdbjavastaticdockerx86_64musl
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR_ALL) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR_ALL).sha1
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
@@ -2261,21 +2322,29 @@ rocksdbjavastaticdockerarm64v8:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_arm64v8-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockers390x:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64lemusl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticdockers390xmusl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
@@ -2297,8 +2366,8 @@ rocksdbjavastaticnexusbundlejar: rocksdbjavageneratepom
|
||||
gpg --yes --output java/target/pom.xml.asc -ab java/pom.xml
|
||||
gpg --yes -ab java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar
|
||||
$(foreach classifier, $(ROCKSDB_JAVA_RELEASE_CLASSIFIERS), gpg --yes -ab java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar;)
|
||||
jar cvf java/target/nexus-bundle-rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java pom.xml -C java/target pom.xml.sha1 -C java/target pom.xml.asc -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar.sha1 -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar.asc
|
||||
$(foreach classifier, $(ROCKSDB_JAVA_RELEASE_CLASSIFIERS), jar uf java/target/nexus-bundle-rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar.sha1 -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar.asc;)
|
||||
$(JAR_CMD) cvf java/target/nexus-bundle-rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java pom.xml -C java/target pom.xml.sha1 -C java/target pom.xml.asc -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar.sha1 -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar.asc
|
||||
$(foreach classifier, $(ROCKSDB_JAVA_RELEASE_CLASSIFIERS), $(JAR_CMD) uf java/target/nexus-bundle-rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar.sha1 -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar.asc;)
|
||||
|
||||
|
||||
# A version of each $(LIBOBJECTS) compiled with -fPIC
|
||||
@@ -2313,9 +2382,9 @@ endif
|
||||
$(AM_V_GEN)cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) javalib;
|
||||
$(AM_V_at)rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(AM_V_at)$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIB_OBJECTS) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_V_at)cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
$(AM_V_at)cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
$(AM_V_at)cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
$(AM_V_at)cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
$(AM_V_at)cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
$(AM_V_at)cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
$(AM_V_at)openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
|
||||
jclean:
|
||||
|
||||
@@ -25,7 +25,7 @@ The public interface is in `include/`. Callers should not include or
|
||||
rely on the details of any other header files in this package. Those
|
||||
internal APIs may be changed without warning.
|
||||
|
||||
Design discussions are conducted in https://www.facebook.com/groups/rocksdb.dev/ and https://rocksdb.slack.com/
|
||||
Questions and discussions are welcome on the [RocksDB Developers Public](https://www.facebook.com/groups/rocksdb.dev/) Facebook group and [email list](https://groups.google.com/g/rocksdb) on Google Groups.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -21,9 +21,6 @@ environment:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CMAKE_GENERATOR: Visual Studio 14 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.com
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
|
||||
@@ -176,7 +176,7 @@ case "$TARGET_OS" in
|
||||
fi
|
||||
if test "$ROCKSDB_USE_IO_URING" -ne 0; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
@@ -284,7 +284,7 @@ if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
else
|
||||
if ! test $ROCKSDB_DISABLE_FALLOCATE; then
|
||||
# Test whether fallocate is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
@@ -300,7 +300,7 @@ EOF
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# Test whether Snappy library is installed
|
||||
# http://code.google.com/p/snappy/
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <snappy.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -315,7 +315,7 @@ EOF
|
||||
# Test whether gflags library is installed
|
||||
# http://gflags.github.io/gflags/
|
||||
# check if the namespace is gflags
|
||||
if $CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
if $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace GFLAGS_NAMESPACE;
|
||||
int main() {}
|
||||
@@ -324,7 +324,7 @@ EOF
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
# check if namespace is gflags
|
||||
elif $CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
elif $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace gflags;
|
||||
int main() {}
|
||||
@@ -333,7 +333,7 @@ EOF
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1 -DGFLAGS_NAMESPACE=gflags"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
# check if namespace is google
|
||||
elif $CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
elif $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace google;
|
||||
int main() {}
|
||||
@@ -346,7 +346,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# Test whether zlib library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <zlib.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -359,7 +359,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# Test whether bzip library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <bzlib.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -372,7 +372,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
# Test whether lz4 library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <lz4.h>
|
||||
#include <lz4hc.h>
|
||||
int main() {}
|
||||
@@ -399,7 +399,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_NUMA; then
|
||||
# Test whether numa is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -lnuma 2>/dev/null <<EOF
|
||||
#include <numa.h>
|
||||
#include <numaif.h>
|
||||
int main() {}
|
||||
@@ -413,7 +413,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_TBB; then
|
||||
# Test whether tbb is available
|
||||
$CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o /dev/null -ltbb 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -ltbb 2>/dev/null <<EOF
|
||||
#include <tbb/tbb.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -426,7 +426,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_JEMALLOC; then
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null -ljemalloc \
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -ljemalloc \
|
||||
2>/dev/null; then
|
||||
# This will enable some preprocessor identifiers in the Makefile
|
||||
JEMALLOC=1
|
||||
@@ -447,7 +447,7 @@ EOF
|
||||
fi
|
||||
if ! test $JEMALLOC && ! test $ROCKSDB_DISABLE_TCMALLOC; then
|
||||
# jemalloc is not available. Let's try tcmalloc
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null \
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o \
|
||||
-ltcmalloc 2>/dev/null; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltcmalloc"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltcmalloc"
|
||||
@@ -456,7 +456,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_MALLOC_USABLE_SIZE; then
|
||||
# Test whether malloc_usable_size is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <malloc.h>
|
||||
int main() {
|
||||
size_t res = malloc_usable_size(0);
|
||||
@@ -471,7 +471,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_MEMKIND; then
|
||||
# Test whether memkind library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -lmemkind -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -lmemkind -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <memkind.h>
|
||||
int main() {
|
||||
memkind_malloc(MEMKIND_DAX_KMEM, 1024);
|
||||
@@ -487,7 +487,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_PTHREAD_MUTEX_ADAPTIVE_NP; then
|
||||
# Test whether PTHREAD_MUTEX_ADAPTIVE_NP mutex type is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <pthread.h>
|
||||
int main() {
|
||||
int x = PTHREAD_MUTEX_ADAPTIVE_NP;
|
||||
@@ -502,7 +502,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BACKTRACE; then
|
||||
# Test whether backtrace is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <execinfo.h>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
@@ -514,7 +514,7 @@ EOF
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
else
|
||||
# Test whether execinfo library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS -lexecinfo -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -lexecinfo -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <execinfo.h>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
@@ -531,7 +531,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_PG; then
|
||||
# Test if -pg is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -pg -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -pg -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
@@ -543,7 +543,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SYNC_FILE_RANGE; then
|
||||
# Test whether sync_file_range is supported for compatibility with an old glibc
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
int main() {
|
||||
int fd = open("/dev/null", 0);
|
||||
@@ -557,7 +557,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SCHED_GETCPU; then
|
||||
# Test whether sched_getcpu is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
int cpuid = sched_getcpu();
|
||||
@@ -571,7 +571,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_AUXV_GETAUXVAL; then
|
||||
# Test whether getauxval is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <sys/auxv.h>
|
||||
int main() {
|
||||
uint64_t auxv = getauxval(AT_HWCAP);
|
||||
@@ -585,7 +585,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ALIGNED_NEW; then
|
||||
# Test whether c++17 aligned-new is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -faligned-new -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -faligned-new -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
struct alignas(1024) t {int a;};
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -609,7 +609,7 @@ fi
|
||||
# -Wshorten-64-to-32 breaks compilation on FreeBSD aarch64 and i386
|
||||
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 /dev/null -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -723,7 +723,7 @@ if test "$TRY_SSE_ETC"; then
|
||||
TRY_LZCNT="-mlzcnt"
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_SSE42 -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_SSE42 -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
int main() {
|
||||
@@ -737,7 +737,7 @@ elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_PCLMUL -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_PCLMUL -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
@@ -754,7 +754,7 @@ elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_AVX2 -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_AVX2 -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main() {
|
||||
@@ -769,7 +769,7 @@ elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use AVX2 intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_BMI -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_BMI -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
@@ -783,7 +783,7 @@ elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use BMI intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_LZCNT -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_LZCNT -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
@@ -797,7 +797,7 @@ elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use LZCNT intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
int main() {
|
||||
uint64_t a = 0xffffFFFFffffFFFF;
|
||||
@@ -814,7 +814,7 @@ fi
|
||||
# succeed because the cross-compiler flags are added by the Makefile, not this
|
||||
# script.
|
||||
if [ "$PLATFORM" != IOS ]; then
|
||||
$CXX $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#if defined(_MSC_VER) && !defined(__thread)
|
||||
#define __thread __declspec(thread)
|
||||
#endif
|
||||
@@ -834,7 +834,7 @@ if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
void dummy_func() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_LDFLAGS test_dl.o -o /dev/null 2>/dev/null
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_LDFLAGS test_dl.o -o test.o 2>/dev/null
|
||||
if [ "$?" = 0 ]; then
|
||||
EXEC_LDFLAGS+="-ldl"
|
||||
rm -f test_dl.o
|
||||
@@ -842,6 +842,20 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# check for F_FULLFSYNC
|
||||
$CXX $PLATFORM_CXXFALGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
int main() {
|
||||
fcntl(0, F_FULLFSYNC);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_FULLFSYNC"
|
||||
fi
|
||||
|
||||
rm -f test.o test_dl.o
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
|
||||
|
||||
@@ -23,6 +23,14 @@ if [ "$?" != "1" ]; then
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep 'using namespace' -- ':!build_tools' ':!docs' \
|
||||
':!third-party/folly/folly/lang/Align.h' \
|
||||
':!third-party/gtest-1.8.1/fused-src/gtest/gtest.h'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^ Do not use "using namespace"'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
if [ "$BAD" ]; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -109,6 +109,7 @@ ASAN="COMPILE_WITH_ASAN=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
TSAN="COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
ASAN_CRASH="ASAN_OPTIONS=disable_coredump=0"
|
||||
TSAN_CRASH="CRASH_TEST_EXT_ARGS=\\\"--compression_type=zstd --log2_keys_per_lock=22\\\""
|
||||
NON_TSAN_CRASH="CRASH_TEST_EXT_ARGS=--compression_type=zstd"
|
||||
DISABLE_JEMALLOC="DISABLE_JEMALLOC=1"
|
||||
@@ -618,7 +619,7 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug asan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $ASAN_CRASH $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -642,7 +643,7 @@ ASAN_BLACKBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug blackbox asan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM blackbox_asan_crash_test || $CONTRUN_NAME=blackbox_asan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $ASAN_CRASH $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM blackbox_asan_crash_test || $CONTRUN_NAME=blackbox_asan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -666,7 +667,7 @@ ASAN_WHITEBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug whitebox asan_crash_test\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM whitebox_asan_crash_test || $CONTRUN_NAME=whitebox_asan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $ASAN_CRASH $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM whitebox_asan_crash_test || $CONTRUN_NAME=whitebox_asan_crash_test $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -690,7 +691,7 @@ ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug asan_crash_test_with_atomic_flush\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM asan_crash_test_with_atomic_flush || $CONTRUN_NAME=asan_crash_test_with_atomic_flush $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $ASAN_CRASH $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM asan_crash_test_with_atomic_flush || $CONTRUN_NAME=asan_crash_test_with_atomic_flush $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
@@ -714,7 +715,7 @@ ASAN_CRASH_TEST_WITH_TXN_COMMANDS="[
|
||||
{
|
||||
\"name\":\"Build and run RocksDB debug asan_crash_test_with_txn\",
|
||||
\"timeout\": 86400,
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM asan_crash_test_with_txn || $CONTRUN_NAME=asan_crash_test_with_txn $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; $SHM $DEBUG $ASAN_CRASH $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make $PARALLELISM asan_crash_test_with_txn || $CONTRUN_NAME=asan_crash_test_with_txn $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
},
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# - Find liburing
|
||||
#
|
||||
# uring_INCLUDE_DIR - Where to find liburing.h
|
||||
# uring_LIBRARIES - List of libraries when using uring.
|
||||
# uring_FOUND - True if uring found.
|
||||
|
||||
find_path(uring_INCLUDE_DIR
|
||||
NAMES liburing.h)
|
||||
find_library(uring_LIBRARIES
|
||||
NAMES liburing.a liburing)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(uring
|
||||
DEFAULT_MSG uring_LIBRARIES uring_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
uring_INCLUDE_DIR
|
||||
uring_LIBRARIES)
|
||||
|
||||
if(uring_FOUND AND NOT TARGET uring::uring)
|
||||
add_library(uring::uring UNKNOWN IMPORTED)
|
||||
set_target_properties(uring::uring PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${uring_INCLUDE_DIR}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${uring_LIBRARIES}")
|
||||
endif()
|
||||
+2
-2
@@ -328,8 +328,8 @@ Status BuildTable(
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
read_options, file_options, tboptions.internal_comparator, *meta,
|
||||
nullptr /* range_del_agg */,
|
||||
mutable_cf_options.prefix_extractor.get(), nullptr,
|
||||
nullptr /* range_del_agg */, mutable_cf_options.prefix_extractor,
|
||||
nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
|
||||
@@ -2834,7 +2834,7 @@ int rocksdb_options_get_bottommost_compression(rocksdb_options_t* opt) {
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_per_level(rocksdb_options_t* opt,
|
||||
int* level_values,
|
||||
const int* level_values,
|
||||
size_t num_levels) {
|
||||
opt->rep.compression_per_level.resize(num_levels);
|
||||
for (size_t i = 0; i < num_levels; ++i) {
|
||||
@@ -4595,10 +4595,11 @@ void rocksdb_slicetransform_destroy(rocksdb_slicetransform_t* st) {
|
||||
delete st;
|
||||
}
|
||||
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
struct SliceTransformWrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
~SliceTransformWrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
std::string GetId() const override { return rep_->GetId(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
}
|
||||
@@ -4610,18 +4611,18 @@ struct Wrapper : public rocksdb_slicetransform_t {
|
||||
};
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create_fixed_prefix(size_t prefixLen) {
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
SliceTransformWrapper* wrapper = new SliceTransformWrapper;
|
||||
wrapper->rep_ = ROCKSDB_NAMESPACE::NewFixedPrefixTransform(prefixLen);
|
||||
wrapper->state_ = nullptr;
|
||||
wrapper->destructor_ = &Wrapper::DoNothing;
|
||||
wrapper->destructor_ = &SliceTransformWrapper::DoNothing;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create_noop() {
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
SliceTransformWrapper* wrapper = new SliceTransformWrapper;
|
||||
wrapper->rep_ = ROCKSDB_NAMESPACE::NewNoopTransform();
|
||||
wrapper->state_ = nullptr;
|
||||
wrapper->destructor_ = &Wrapper::DoNothing;
|
||||
wrapper->destructor_ = &SliceTransformWrapper::DoNothing;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
||||
+45
@@ -2956,6 +2956,51 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
StartPhase("filter_with_prefix_seek");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_prefix_extractor(
|
||||
options, rocksdb_slicetransform_create_fixed_prefix(1));
|
||||
rocksdb_filterpolicy_t* filter_policy =
|
||||
rocksdb_filterpolicy_create_bloom_full(8.0);
|
||||
rocksdb_block_based_options_set_filter_policy(table_options, filter_policy);
|
||||
rocksdb_options_set_block_based_table_factory(options, table_options);
|
||||
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
char key = '0' + (char)i;
|
||||
rocksdb_put(db, woptions, &key, 1, "", 1, &err);
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
// Flush to generate an L0 so that filter will be used later.
|
||||
rocksdb_flushoptions_t* flush_options = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_options, 1);
|
||||
rocksdb_flush(db, flush_options, &err);
|
||||
rocksdb_flushoptions_destroy(flush_options);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_readoptions_t* ropts = rocksdb_readoptions_create();
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator(db, ropts);
|
||||
|
||||
rocksdb_iter_seek(iter, "0", 1);
|
||||
int cnt = 0;
|
||||
while (rocksdb_iter_valid(iter)) {
|
||||
++cnt;
|
||||
rocksdb_iter_next(iter);
|
||||
}
|
||||
CheckCondition(10 == cnt);
|
||||
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_readoptions_destroy(ropts);
|
||||
}
|
||||
|
||||
StartPhase("cancel_all_background_work");
|
||||
rocksdb_cancel_all_background_work(db, 1);
|
||||
|
||||
|
||||
@@ -790,8 +790,8 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
}
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
auto prefix_extractor =
|
||||
compact_->compaction->mutable_cf_options()->prefix_extractor.get();
|
||||
auto& prefix_extractor =
|
||||
compact_->compaction->mutable_cf_options()->prefix_extractor;
|
||||
std::atomic<size_t> next_file_idx(0);
|
||||
auto verify_table = [&](Status& output_status) {
|
||||
while (true) {
|
||||
|
||||
@@ -964,13 +964,11 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
|
||||
// verify whether compaction is needed based on the current
|
||||
// size of L0 files.
|
||||
uint64_t current_size = 0;
|
||||
for (int i = 1; i <= kFileCount; ++i) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
Add(0, i, ToString((i + 100) * 1000).c_str(),
|
||||
ToString((i + 100) * 1000 + 999).c_str(),
|
||||
kFileSize, 0, i * 100, i * 100 + 99);
|
||||
current_size += kFileSize;
|
||||
ToString((i + 100) * 1000 + 999).c_str(), kFileSize, 0, i * 100,
|
||||
i * 100 + 99);
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
vstorage_->CompactionScore(0) >= 1);
|
||||
|
||||
@@ -62,7 +62,7 @@ std::shared_ptr<SstPartitionerFactory> NewSstPartitionerFixedPrefixFactory(
|
||||
namespace {
|
||||
static int RegisterSstPartitionerFactories(ObjectLibrary& library,
|
||||
const std::string& /*arg*/) {
|
||||
library.Register<SstPartitionerFactory>(
|
||||
library.AddFactory<SstPartitionerFactory>(
|
||||
SstPartitionerFixedPrefixFactory::kClassName(),
|
||||
[](const std::string& /*uri*/,
|
||||
std::unique_ptr<SstPartitionerFactory>* guard,
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
new RandomAccessFileReader(std::move(file), file_path));
|
||||
const bool kImmortal = true;
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions, options.prefix_extractor.get(), env_options,
|
||||
TableReaderOptions(ioptions, options.prefix_extractor, env_options,
|
||||
internal_comparator, false /* skip_filters */,
|
||||
!kImmortal, false /* force_direct_prefetch */,
|
||||
-1 /* level */),
|
||||
|
||||
+26
-2
@@ -39,11 +39,35 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
static constexpr int kValueSize = 1000;
|
||||
namespace {
|
||||
// A wrapper that allows injection of errors.
|
||||
class ErrorEnv : public EnvWrapper {
|
||||
public:
|
||||
bool writable_file_error_;
|
||||
int num_writable_file_errors_;
|
||||
|
||||
explicit ErrorEnv(Env* _target)
|
||||
: EnvWrapper(_target),
|
||||
writable_file_error_(false),
|
||||
num_writable_file_errors_(0) {}
|
||||
const char* Name() const override { return "ErrorEnv"; }
|
||||
|
||||
virtual Status NewWritableFile(const std::string& fname,
|
||||
std::unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& soptions) override {
|
||||
result->reset();
|
||||
if (writable_file_error_) {
|
||||
++num_writable_file_errors_;
|
||||
return Status::IOError(fname, "fake error");
|
||||
}
|
||||
return target()->NewWritableFile(fname, result, soptions);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
class CorruptionTest : public testing::Test {
|
||||
public:
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
test::ErrorEnv* env_;
|
||||
ErrorEnv* env_;
|
||||
std::string dbname_;
|
||||
std::shared_ptr<Cache> tiny_cache_;
|
||||
Options options_;
|
||||
@@ -58,7 +82,7 @@ class CorruptionTest : public testing::Test {
|
||||
EXPECT_OK(
|
||||
test::CreateEnvFromSystem(ConfigOptions(), &base_env, &env_guard_));
|
||||
EXPECT_NE(base_env, nullptr);
|
||||
env_ = new test::ErrorEnv(base_env);
|
||||
env_ = new ErrorEnv(base_env);
|
||||
options_.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
options_.env = env_;
|
||||
dbname_ = test::PerThreadDBPath(env_, "corruption_test");
|
||||
|
||||
+33
-3
@@ -1048,6 +1048,8 @@ TEST_F(DBBasicTest, MmapAndBufferOptions) {
|
||||
class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv(Env* base_env) : EnvWrapper(base_env), close_count(0) {}
|
||||
static const char* kClassName() { return "TestEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
@@ -1890,7 +1892,6 @@ TEST_F(DBBasicTest, MultiGetBatchedValueSizeMultiLevelMerge) {
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
|
||||
uint64_t curr_value_size = 0;
|
||||
for (unsigned int j = 0; j < 26; ++j) {
|
||||
int key = j + 10;
|
||||
std::string value;
|
||||
@@ -1907,11 +1908,9 @@ TEST_F(DBBasicTest, MultiGetBatchedValueSizeMultiLevelMerge) {
|
||||
value.append(",");
|
||||
value.append("val_mem_" + std::to_string(key));
|
||||
}
|
||||
curr_value_size += value.size();
|
||||
ASSERT_EQ(values[j], value);
|
||||
ASSERT_OK(statuses[j]);
|
||||
}
|
||||
// ASSERT_TRUE(curr_value_size <= read_options.value_size_hard_limit);
|
||||
|
||||
// All remaning keys status is set Status::Abort
|
||||
for (unsigned int j = 26; j < 40; j++) {
|
||||
@@ -3064,6 +3063,8 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetDirectIO) {
|
||||
|
||||
public:
|
||||
FakeDirectIOEnv(Env* env) : EnvWrapper(env) {}
|
||||
static const char* kClassName() { return "FakeDirectIOEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
Status NewRandomAccessFile(const std::string& fname,
|
||||
std::unique_ptr<RandomAccessFile>* result,
|
||||
@@ -3628,6 +3629,35 @@ TEST_F(DBBasicTest, ManifestWriteFailure) {
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DestroyDefaultCfHandle) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
for (const auto* h : handles_) {
|
||||
ASSERT_NE(db_->DefaultColumnFamily(), h);
|
||||
}
|
||||
|
||||
// We have two handles to the default column family. The two handles point to
|
||||
// different ColumnFamilyHandle objects.
|
||||
assert(db_->DefaultColumnFamily());
|
||||
ASSERT_EQ(0U, db_->DefaultColumnFamily()->GetID());
|
||||
assert(handles_[0]);
|
||||
ASSERT_EQ(0U, handles_[0]->GetID());
|
||||
|
||||
// You can destroy handles_[...].
|
||||
for (auto* h : handles_) {
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(h));
|
||||
}
|
||||
handles_.clear();
|
||||
|
||||
// But you should not destroy db_->DefaultColumnFamily(), since it's going to
|
||||
// be deleted in `DBImpl::CloseHelper()`. Before that, it may be used
|
||||
// elsewhere internally too.
|
||||
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
|
||||
ASSERT_TRUE(db_->DestroyColumnFamilyHandle(default_cf).IsInvalidArgument());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTest, VerifyFileChecksums) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
@@ -1502,6 +1502,63 @@ TEST_F(DBBloomFilterTest, MemtableWholeKeyBloomFilter) {
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_hit_count);
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, MemtableWholeKeyBloomFilterMultiGet) {
|
||||
Options options = CurrentOptions();
|
||||
options.memtable_prefix_bloom_size_ratio = 0.015;
|
||||
options.memtable_whole_key_filtering = true;
|
||||
Reopen(options);
|
||||
std::string key1("AA");
|
||||
std::string key2("BB");
|
||||
std::string key3("CC");
|
||||
std::string key4("DD");
|
||||
std::string key_not("EE");
|
||||
std::string value1("Value1");
|
||||
std::string value2("Value2");
|
||||
std::string value3("Value3");
|
||||
std::string value4("Value4");
|
||||
|
||||
ASSERT_OK(Put(key1, value1, WriteOptions()));
|
||||
ASSERT_OK(Put(key2, value2, WriteOptions()));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(key3, value3, WriteOptions()));
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(Put(key4, value4, WriteOptions()));
|
||||
|
||||
// Delete key2 and key3
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "BA", "CZ"));
|
||||
|
||||
// Read without snapshot
|
||||
auto results = MultiGet({key_not, key1, key2, key3, key4});
|
||||
ASSERT_EQ(results[0], "NOT_FOUND");
|
||||
ASSERT_EQ(results[1], value1);
|
||||
ASSERT_EQ(results[2], "NOT_FOUND");
|
||||
ASSERT_EQ(results[3], "NOT_FOUND");
|
||||
ASSERT_EQ(results[4], value4);
|
||||
|
||||
// Also check Get
|
||||
ASSERT_EQ(Get(key1), value1);
|
||||
ASSERT_EQ(Get(key2), "NOT_FOUND");
|
||||
ASSERT_EQ(Get(key3), "NOT_FOUND");
|
||||
ASSERT_EQ(Get(key4), value4);
|
||||
|
||||
// Read with snapshot
|
||||
results = MultiGet({key_not, key1, key2, key3, key4}, snapshot);
|
||||
ASSERT_EQ(results[0], "NOT_FOUND");
|
||||
ASSERT_EQ(results[1], value1);
|
||||
ASSERT_EQ(results[2], value2);
|
||||
ASSERT_EQ(results[3], value3);
|
||||
ASSERT_EQ(results[4], "NOT_FOUND");
|
||||
|
||||
// Also check Get
|
||||
ASSERT_EQ(Get(key1, snapshot), value1);
|
||||
ASSERT_EQ(Get(key2, snapshot), value2);
|
||||
ASSERT_EQ(Get(key3, snapshot), value3);
|
||||
ASSERT_EQ(Get(key4, snapshot), "NOT_FOUND");
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, MemtablePrefixBloomOutOfDomain) {
|
||||
constexpr size_t kPrefixSize = 8;
|
||||
const std::string kKey = "key";
|
||||
|
||||
@@ -6881,6 +6881,123 @@ TEST_F(DBCompactionTest, FIFOWarm) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFull) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::RunManualCompaction:Scheduled",
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFull:"
|
||||
"PreDisableManualCompaction"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// Block compaction queue
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
|
||||
// generate files, but avoid trigger auto compaction
|
||||
for (int i = 0; i < kNumL0Files / 2; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
port::Thread compact_thread([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = true;
|
||||
auto s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFull:"
|
||||
"PreDisableManualCompaction");
|
||||
|
||||
// Generate more files to trigger auto compaction which is scheduled after
|
||||
// manual compaction. Has to generate 4 more files because existing files are
|
||||
// pending compaction
|
||||
for (int i = 0; i < kNumL0Files; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(ToString(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
// CompactRange should return before the compaction has the chance to run
|
||||
compact_thread.join();
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFullDBClose) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::RunManualCompaction:Scheduled",
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFull:"
|
||||
"PreDisableManualCompaction"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// Block compaction queue
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
|
||||
// generate files, but avoid trigger auto compaction
|
||||
for (int i = 0; i < kNumL0Files / 2; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
port::Thread compact_thread([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = true;
|
||||
auto s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFull:"
|
||||
"PreDisableManualCompaction");
|
||||
|
||||
// Generate more files to trigger auto compaction which is scheduled after
|
||||
// manual compaction. Has to generate 4 more files because existing files are
|
||||
// pending compaction
|
||||
for (int i = 0; i < kNumL0Files; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(ToString(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
|
||||
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
// CompactRange should return before the compaction has the chance to run
|
||||
compact_thread.join();
|
||||
|
||||
// Try close DB while manual compaction is canceled but still in the queue.
|
||||
// And an auto-triggered compaction is also in the queue.
|
||||
auto s = db_->Close();
|
||||
ASSERT_OK(s);
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest,
|
||||
DisableManualCompactionDoesNotWaitForDrainingAutomaticCompaction) {
|
||||
// When `CompactRangeOptions::exclusive_manual_compaction == true`, we wait
|
||||
|
||||
@@ -1496,6 +1496,30 @@ bool DBImpl::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string* ts_low) {
|
||||
if (ts_low == nullptr) {
|
||||
return Status::InvalidArgument("ts_low is nullptr");
|
||||
}
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
if (column_family == nullptr) {
|
||||
cfd = default_cf_handle_->cfd();
|
||||
} else {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
assert(cfh != nullptr);
|
||||
cfd = cfh->cfd();
|
||||
}
|
||||
assert(cfd != nullptr && cfd->user_comparator() != nullptr);
|
||||
if (cfd->user_comparator()->timestamp_size() == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Timestamp is not enabled in this column family");
|
||||
}
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
*ts_low = cfd->GetFullHistoryTsLow();
|
||||
assert(cfd->user_comparator()->timestamp_size() == ts_low->size());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
@@ -4007,6 +4031,10 @@ Status DB::DropColumnFamilies(
|
||||
}
|
||||
|
||||
Status DB::DestroyColumnFamilyHandle(ColumnFamilyHandle* column_family) {
|
||||
if (DefaultColumnFamily() == column_family) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot destroy the handle returned by DefaultColumnFamily()");
|
||||
}
|
||||
delete column_family;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
+18
-4
@@ -355,6 +355,16 @@ class DBImpl : public DB {
|
||||
|
||||
virtual bool SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) override;
|
||||
|
||||
// IncreaseFullHistoryTsLow(ColumnFamilyHandle*, std::string) will acquire
|
||||
// and release db_mutex
|
||||
Status IncreaseFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string ts_low) override;
|
||||
|
||||
// GetFullHistoryTsLow(ColumnFamilyHandle*, std::string*) will acquire and
|
||||
// release db_mutex
|
||||
Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string* ts_low) override;
|
||||
|
||||
virtual Status GetDbIdentity(std::string& identity) const override;
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
|
||||
@@ -974,6 +984,9 @@ class DBImpl : public DB {
|
||||
Status TEST_AtomicFlushMemTables(const autovector<ColumnFamilyData*>& cfds,
|
||||
const FlushOptions& flush_opts);
|
||||
|
||||
// Wait for background threads to complete scheduled work.
|
||||
Status TEST_WaitForBackgroundWork();
|
||||
|
||||
// Wait for memtable compaction
|
||||
Status TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
@@ -1504,6 +1517,7 @@ class DBImpl : public DB {
|
||||
ManualCompactionState* manual_compaction_state; // nullptr if non-manual
|
||||
// task limiter token is requested during compaction picking.
|
||||
std::unique_ptr<TaskLimiterToken> task_token;
|
||||
bool is_canceled = false;
|
||||
};
|
||||
|
||||
struct CompactionArg {
|
||||
@@ -1984,7 +1998,8 @@ class DBImpl : public DB {
|
||||
|
||||
Status DisableFileDeletionsWithLock();
|
||||
|
||||
Status IncreaseFullHistoryTsLow(ColumnFamilyData* cfd, std::string ts_low);
|
||||
Status IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
std::string ts_low);
|
||||
|
||||
// Lock over the persistent DB state. Non-nullptr iff successfully acquired.
|
||||
FileLock* db_lock_;
|
||||
@@ -2371,11 +2386,10 @@ extern uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
// will not depend on any WAL file. nullptr means no memtable is being flushed.
|
||||
// The function is only applicable to 2pc mode.
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const ColumnFamilyData* cfd_to_flush,
|
||||
const autovector<MemTable*>& memtables_to_flush);
|
||||
VersionSet* vset, const autovector<MemTable*>& memtables_to_flush);
|
||||
// For atomic flush.
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
VersionSet* vset,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush);
|
||||
|
||||
// Fix user-supplied options to be reasonable
|
||||
|
||||
@@ -917,8 +917,29 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
end_with_ts);
|
||||
}
|
||||
|
||||
Status DBImpl::IncreaseFullHistoryTsLow(ColumnFamilyData* cfd,
|
||||
Status DBImpl::IncreaseFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string ts_low) {
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
if (column_family == nullptr) {
|
||||
cfd = default_cf_handle_->cfd();
|
||||
} else {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
assert(cfh != nullptr);
|
||||
cfd = cfh->cfd();
|
||||
}
|
||||
assert(cfd != nullptr && cfd->user_comparator() != nullptr);
|
||||
if (cfd->user_comparator()->timestamp_size() == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Timestamp is not enabled in this column family");
|
||||
}
|
||||
if (cfd->user_comparator()->timestamp_size() != ts_low.size()) {
|
||||
return Status::InvalidArgument("ts_low size mismatch");
|
||||
}
|
||||
return IncreaseFullHistoryTsLowImpl(cfd, ts_low);
|
||||
}
|
||||
|
||||
Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
std::string ts_low) {
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.SetFullHistoryTsLow(ts_low);
|
||||
@@ -926,6 +947,7 @@ Status DBImpl::IncreaseFullHistoryTsLow(ColumnFamilyData* cfd,
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
std::string current_ts_low = cfd->GetFullHistoryTsLow();
|
||||
const Comparator* ucmp = cfd->user_comparator();
|
||||
assert(ucmp->timestamp_size() == ts_low.size() && !ts_low.empty());
|
||||
if (!current_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(ts_low, current_ts_low) < 0) {
|
||||
return Status::InvalidArgument(
|
||||
@@ -956,7 +978,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
return Status::InvalidArgument(
|
||||
"Cannot specify compaction range with full_history_ts_low");
|
||||
}
|
||||
Status s = IncreaseFullHistoryTsLow(cfd, ts_low);
|
||||
Status s = IncreaseFullHistoryTsLowImpl(cfd, ts_low);
|
||||
if (!s.ok()) {
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return s;
|
||||
@@ -1730,7 +1752,7 @@ Status DBImpl::RunManualCompaction(
|
||||
input_level >= 0);
|
||||
|
||||
InternalKey begin_storage, end_storage;
|
||||
CompactionArg* ca;
|
||||
CompactionArg* ca = nullptr;
|
||||
|
||||
bool scheduled = false;
|
||||
bool manual_conflict = false;
|
||||
@@ -1849,6 +1871,16 @@ Status DBImpl::RunManualCompaction(
|
||||
assert(!exclusive || !manual_conflict);
|
||||
// Running either this or some other manual compaction
|
||||
bg_cv_.Wait();
|
||||
if (manual_compaction_paused_ > 0 && !manual.done &&
|
||||
!manual.in_progress) {
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
if (ca && ca->prepicked_compaction) {
|
||||
ca->prepicked_compaction->is_canceled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (scheduled && manual.incomplete == true) {
|
||||
assert(!manual.in_progress);
|
||||
scheduled = false;
|
||||
@@ -1885,6 +1917,7 @@ Status DBImpl::RunManualCompaction(
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
}
|
||||
scheduled = true;
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:Scheduled");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2242,21 +2275,27 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
int num = static_cast<int>(cfds.size());
|
||||
// Wait until the compaction completes
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
Status s;
|
||||
// If the caller is trying to resume from bg error, then
|
||||
// error_handler_.IsDBStopped() is true.
|
||||
while (resuming_from_bg_err || !error_handler_.IsDBStopped()) {
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
s = Status::ShutdownInProgress();
|
||||
return s;
|
||||
}
|
||||
// If an error has occurred during resumption, then no need to wait.
|
||||
// But flush operation may fail because of this error, so need to
|
||||
// return the status.
|
||||
if (!error_handler_.GetRecoveryError().ok()) {
|
||||
s = error_handler_.GetRecoveryError();
|
||||
break;
|
||||
}
|
||||
// If BGWorkStopped, which indicate that there is a BG error and
|
||||
// 1) soft error but requires no BG work, 2) no in auto_recovery_
|
||||
if (!resuming_from_bg_err && error_handler_.IsBGWorkStopped() &&
|
||||
error_handler_.GetBGError().severity() < Status::Severity::kHardError) {
|
||||
return error_handler_.GetBGError();
|
||||
s = error_handler_.GetBGError();
|
||||
return s;
|
||||
}
|
||||
|
||||
// Number of column families that have been dropped.
|
||||
@@ -2274,7 +2313,8 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
}
|
||||
}
|
||||
if (1 == num_dropped && 1 == num) {
|
||||
return Status::ColumnFamilyDropped();
|
||||
s = Status::ColumnFamilyDropped();
|
||||
return s;
|
||||
}
|
||||
// Column families involved in this flush request have either been dropped
|
||||
// or finished flush. Then it's time to finish waiting.
|
||||
@@ -2283,7 +2323,6 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
}
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
Status s;
|
||||
// If not resuming from bg error, and an error has caused the DB to stop,
|
||||
// then report the bg error to caller.
|
||||
if (!resuming_from_bg_err && error_handler_.IsDBStopped()) {
|
||||
@@ -2804,91 +2843,106 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
Env::Priority bg_thread_pri) {
|
||||
bool made_progress = false;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:0");
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
// This call will unlock/lock the mutex to wait for current running
|
||||
// IngestExternalFile() calls to finish.
|
||||
WaitForIngestFile();
|
||||
if (prepicked_compaction && prepicked_compaction->is_canceled) {
|
||||
assert(prepicked_compaction->compaction);
|
||||
ROCKS_LOG_BUFFER(&log_buffer, "[%s] Skip canceled manual compaction job",
|
||||
prepicked_compaction->compaction->column_family_data()
|
||||
->GetName()
|
||||
.c_str());
|
||||
prepicked_compaction->compaction->ReleaseCompactionFiles(
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused));
|
||||
delete prepicked_compaction->compaction;
|
||||
} else {
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
// This call will unlock/lock the mutex to wait for current running
|
||||
// IngestExternalFile() calls to finish.
|
||||
WaitForIngestFile();
|
||||
|
||||
num_running_compactions_++;
|
||||
num_running_compactions_++;
|
||||
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>
|
||||
pending_outputs_inserted_elem(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>
|
||||
pending_outputs_inserted_elem(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
|
||||
assert((bg_thread_pri == Env::Priority::BOTTOM &&
|
||||
bg_bottom_compaction_scheduled_) ||
|
||||
(bg_thread_pri == Env::Priority::LOW && bg_compaction_scheduled_));
|
||||
Status s = BackgroundCompaction(&made_progress, &job_context, &log_buffer,
|
||||
prepicked_compaction, bg_thread_pri);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:1");
|
||||
if (s.IsBusy()) {
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
immutable_db_options_.clock->SleepForMicroseconds(
|
||||
10000); // prevent hot loop
|
||||
mutex_.Lock();
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() && !s.IsColumnFamilyDropped()) {
|
||||
// Wait a little bit before retrying background compaction in
|
||||
// case this is an environmental problem and we do not want to
|
||||
// chew up resources for failed compactions for the duration of
|
||||
// the problem.
|
||||
uint64_t error_cnt =
|
||||
default_cf_internal_stats_->BumpAndGetBackgroundErrorCount();
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
log_buffer.FlushBufferToLog();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Waiting after background compaction error: %s, "
|
||||
"Accumulated background error counts: %" PRIu64,
|
||||
s.ToString().c_str(), error_cnt);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
immutable_db_options_.clock->SleepForMicroseconds(1000000);
|
||||
mutex_.Lock();
|
||||
} else if (s.IsManualCompactionPaused()) {
|
||||
ManualCompactionState* m = prepicked_compaction->manual_compaction_state;
|
||||
assert(m);
|
||||
ROCKS_LOG_BUFFER(&log_buffer, "[%s] [JOB %d] Manual compaction paused",
|
||||
m->cfd->GetName().c_str(), job_context.job_id);
|
||||
}
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
// If compaction failed, we want to delete all temporary files that we might
|
||||
// have created (they might not be all recorded in job_context in case of a
|
||||
// failure). Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() &&
|
||||
!s.IsColumnFamilyDropped() &&
|
||||
!s.IsBusy());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:FoundObsoleteFiles");
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
// states of DB so info_log might not be available after that point.
|
||||
// It also applies to access other states that DB owns.
|
||||
log_buffer.FlushBufferToLog();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles");
|
||||
assert((bg_thread_pri == Env::Priority::BOTTOM &&
|
||||
bg_bottom_compaction_scheduled_) ||
|
||||
(bg_thread_pri == Env::Priority::LOW && bg_compaction_scheduled_));
|
||||
Status s = BackgroundCompaction(&made_progress, &job_context, &log_buffer,
|
||||
prepicked_compaction, bg_thread_pri);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:1");
|
||||
if (s.IsBusy()) {
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
immutable_db_options_.clock->SleepForMicroseconds(
|
||||
10000); // prevent hot loop
|
||||
mutex_.Lock();
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() && !s.IsColumnFamilyDropped()) {
|
||||
// Wait a little bit before retrying background compaction in
|
||||
// case this is an environmental problem and we do not want to
|
||||
// chew up resources for failed compactions for the duration of
|
||||
// the problem.
|
||||
uint64_t error_cnt =
|
||||
default_cf_internal_stats_->BumpAndGetBackgroundErrorCount();
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
log_buffer.FlushBufferToLog();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Waiting after background compaction error: %s, "
|
||||
"Accumulated background error counts: %" PRIu64,
|
||||
s.ToString().c_str(), error_cnt);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
immutable_db_options_.clock->SleepForMicroseconds(1000000);
|
||||
mutex_.Lock();
|
||||
} else if (s.IsManualCompactionPaused()) {
|
||||
assert(prepicked_compaction);
|
||||
ManualCompactionState* m =
|
||||
prepicked_compaction->manual_compaction_state;
|
||||
assert(m);
|
||||
ROCKS_LOG_BUFFER(&log_buffer, "[%s] [JOB %d] Manual compaction paused",
|
||||
m->cfd->GetName().c_str(), job_context.job_id);
|
||||
}
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
// If compaction failed, we want to delete all temporary files that we
|
||||
// might have created (they might not be all recorded in job_context in
|
||||
// case of a failure). Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() &&
|
||||
!s.IsColumnFamilyDropped() &&
|
||||
!s.IsBusy());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:FoundObsoleteFiles");
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
// states of DB so info_log might not be available after that point.
|
||||
// It also applies to access other states that DB owns.
|
||||
log_buffer.FlushBufferToLog();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles");
|
||||
}
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
assert(num_running_compactions_ > 0);
|
||||
num_running_compactions_--;
|
||||
}
|
||||
|
||||
assert(num_running_compactions_ > 0);
|
||||
num_running_compactions_--;
|
||||
if (bg_thread_pri == Env::Priority::LOW) {
|
||||
bg_compaction_scheduled_--;
|
||||
} else {
|
||||
@@ -2907,7 +2961,6 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
// must be done before we potentially signal the DB close process to
|
||||
// proceed below.
|
||||
prepicked_compaction->task_token.reset();
|
||||
;
|
||||
}
|
||||
|
||||
if (made_progress ||
|
||||
|
||||
@@ -156,6 +156,12 @@ Status DBImpl::TEST_AtomicFlushMemTables(
|
||||
return AtomicFlushMemTables(cfds, flush_opts, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForBackgroundWork() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
WaitForBackgroundWork();
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
@@ -256,8 +262,7 @@ size_t DBImpl::TEST_LogsWithPrepSize() {
|
||||
|
||||
uint64_t DBImpl::TEST_FindMinPrepLogReferencedByMemTable() {
|
||||
autovector<MemTable*> empty_list;
|
||||
return FindMinPrepLogReferencedByMemTable(versions_.get(), nullptr,
|
||||
empty_list);
|
||||
return FindMinPrepLogReferencedByMemTable(versions_.get(), empty_list);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_GetLatestMutableCFOptions(
|
||||
|
||||
@@ -670,8 +670,7 @@ void DBImpl::DeleteObsoleteFiles() {
|
||||
}
|
||||
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const ColumnFamilyData* cfd_to_flush,
|
||||
const autovector<MemTable*>& memtables_to_flush) {
|
||||
VersionSet* vset, const autovector<MemTable*>& memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
// we must look through the memtables for two phase transactions
|
||||
@@ -679,7 +678,7 @@ uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
std::unordered_set<MemTable*> memtables_to_flush_set(
|
||||
memtables_to_flush.begin(), memtables_to_flush.end());
|
||||
for (auto loop_cfd : *vset->GetColumnFamilySet()) {
|
||||
if (loop_cfd->IsDropped() || loop_cfd == cfd_to_flush) {
|
||||
if (loop_cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -701,18 +700,16 @@ uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
}
|
||||
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
VersionSet* vset,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
std::unordered_set<ColumnFamilyData*> cfds_to_flush_set(cfds_to_flush.begin(),
|
||||
cfds_to_flush.end());
|
||||
std::unordered_set<MemTable*> memtables_to_flush_set;
|
||||
for (const autovector<MemTable*>* memtables : memtables_to_flush) {
|
||||
memtables_to_flush_set.insert(memtables->begin(), memtables->end());
|
||||
}
|
||||
for (auto loop_cfd : *vset->GetColumnFamilySet()) {
|
||||
if (loop_cfd->IsDropped() || cfds_to_flush_set.count(loop_cfd)) {
|
||||
if (loop_cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -828,8 +825,8 @@ uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
min_log_number_to_keep = min_log_in_prep_heap;
|
||||
}
|
||||
|
||||
uint64_t min_log_refed_by_mem = FindMinPrepLogReferencedByMemTable(
|
||||
vset, &cfd_to_flush, memtables_to_flush);
|
||||
uint64_t min_log_refed_by_mem =
|
||||
FindMinPrepLogReferencedByMemTable(vset, memtables_to_flush);
|
||||
|
||||
if (min_log_refed_by_mem != 0 &&
|
||||
min_log_refed_by_mem < min_log_number_to_keep) {
|
||||
@@ -859,8 +856,8 @@ uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
min_log_number_to_keep = min_log_in_prep_heap;
|
||||
}
|
||||
|
||||
uint64_t min_log_refed_by_mem = FindMinPrepLogReferencedByMemTable(
|
||||
vset, cfds_to_flush, memtables_to_flush);
|
||||
uint64_t min_log_refed_by_mem =
|
||||
FindMinPrepLogReferencedByMemTable(vset, memtables_to_flush);
|
||||
|
||||
if (min_log_refed_by_mem != 0 &&
|
||||
min_log_refed_by_mem < min_log_number_to_keep) {
|
||||
|
||||
@@ -1371,6 +1371,12 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
MemTable* mem, VersionEdit* edit) {
|
||||
mutex_.AssertHeld();
|
||||
assert(cfd);
|
||||
assert(cfd->imm());
|
||||
// The immutable memtable list must be empty.
|
||||
assert(std::numeric_limits<uint64_t>::max() ==
|
||||
cfd->imm()->GetEarliestMemTableID());
|
||||
|
||||
const uint64_t start_micros = immutable_db_options_.clock->NowMicros();
|
||||
|
||||
FileMetaData meta;
|
||||
|
||||
@@ -75,9 +75,14 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
if (my_batch == nullptr) {
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
}
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
if (tracer_ && !tracer_->IsWriteOrderPreserved()) {
|
||||
// We don't have to preserve write order so can trace anywhere. It's more
|
||||
// efficient to trace here than to add latency to a phase of the log/apply
|
||||
// pipeline.
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(my_batch).PermitUncheckedError();
|
||||
}
|
||||
@@ -249,6 +254,17 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
IOStatus io_s;
|
||||
Status pre_release_cb_status;
|
||||
if (status.ok()) {
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_ && tracer_->IsWriteOrderPreserved()) {
|
||||
for (auto* writer : write_group) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Rules for when we can update the memtable concurrently
|
||||
// 1. supported by memtable
|
||||
// 2. Puts are not okay if inplace_update_support
|
||||
@@ -498,6 +514,17 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
size_t total_byte_size = 0;
|
||||
|
||||
if (w.status.ok()) {
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
|
||||
for (auto* writer : wal_write_group) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
for (auto writer : wal_write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
@@ -722,6 +749,17 @@ Status DBImpl::WriteImplWALOnly(
|
||||
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
|
||||
// Note: no need to update last_batch_group_size_ here since the batch writes
|
||||
// to WAL only
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_ != nullptr && tracer_->IsWriteOrderPreserved()) {
|
||||
for (auto* writer : write_group) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t pre_release_callback_cnt = 0;
|
||||
size_t total_byte_size = 0;
|
||||
|
||||
@@ -426,6 +426,9 @@ namespace {
|
||||
class TraceFileEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TraceFileEnv(Env* _target) : EnvWrapper(_target) {}
|
||||
static const char* kClassName() { return "TraceFileEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
Status NewRandomAccessFile(const std::string& f,
|
||||
std::unique_ptr<RandomAccessFile>* r,
|
||||
const EnvOptions& env_options) override {
|
||||
|
||||
+2
-2
@@ -789,8 +789,8 @@ TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
|
||||
class MyEnv : public EnvWrapper {
|
||||
public:
|
||||
MyEnv(Env* t) : EnvWrapper(t), fake_log_delete(false) {}
|
||||
|
||||
Status DeleteFile(const std::string& fname) {
|
||||
const char* Name() const override { return "MyEnv"; }
|
||||
Status DeleteFile(const std::string& fname) override {
|
||||
if (fname.find(".log.trash") != std::string::npos && fake_log_delete) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
+40
-18
@@ -2576,9 +2576,7 @@ static const int kNumKeys = 1000;
|
||||
|
||||
struct MTState {
|
||||
DBTest* test;
|
||||
std::atomic<bool> stop;
|
||||
std::atomic<int> counter[kNumThreads];
|
||||
std::atomic<bool> thread_done[kNumThreads];
|
||||
};
|
||||
|
||||
struct MTThread {
|
||||
@@ -2592,10 +2590,13 @@ static void MTThreadBody(void* arg) {
|
||||
int id = t->id;
|
||||
DB* db = t->state->test->db_;
|
||||
int counter = 0;
|
||||
std::shared_ptr<SystemClock> clock = SystemClock::Default();
|
||||
auto end_micros = clock->NowMicros() + kTestSeconds * 1000000U;
|
||||
|
||||
fprintf(stderr, "... starting thread %d\n", id);
|
||||
Random rnd(1000 + id);
|
||||
char valbuf[1500];
|
||||
while (t->state->stop.load(std::memory_order_acquire) == false) {
|
||||
while (clock->NowMicros() < end_micros) {
|
||||
t->state->counter[id].store(counter, std::memory_order_release);
|
||||
|
||||
int key = rnd.Uniform(kNumKeys);
|
||||
@@ -2692,7 +2693,6 @@ static void MTThreadBody(void* arg) {
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
t->state->thread_done[id].store(true, std::memory_order_release);
|
||||
fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter));
|
||||
}
|
||||
|
||||
@@ -2731,10 +2731,8 @@ TEST_P(MultiThreadedDBTest, MultiThreaded) {
|
||||
// Initialize state
|
||||
MTState mt;
|
||||
mt.test = this;
|
||||
mt.stop.store(false, std::memory_order_release);
|
||||
for (int id = 0; id < kNumThreads; id++) {
|
||||
mt.counter[id].store(0, std::memory_order_release);
|
||||
mt.thread_done[id].store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
// Start threads
|
||||
@@ -2746,16 +2744,7 @@ TEST_P(MultiThreadedDBTest, MultiThreaded) {
|
||||
env_->StartThread(MTThreadBody, &thread[id]);
|
||||
}
|
||||
|
||||
// Let them run for a while
|
||||
env_->SleepForMicroseconds(kTestSeconds * 1000000);
|
||||
|
||||
// Stop the threads and wait for them to finish
|
||||
mt.stop.store(true, std::memory_order_release);
|
||||
for (int id = 0; id < kNumThreads; id++) {
|
||||
while (mt.thread_done[id].load(std::memory_order_acquire) == false) {
|
||||
env_->SleepForMicroseconds(100000);
|
||||
}
|
||||
}
|
||||
env_->WaitForJoin();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
@@ -3214,6 +3203,16 @@ class ModelDB : public DB {
|
||||
return true;
|
||||
}
|
||||
|
||||
Status IncreaseFullHistoryTsLow(ColumnFamilyHandle* /*cf*/,
|
||||
std::string /*ts_low*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetFullHistoryTsLow(ColumnFamilyHandle* /*cf*/,
|
||||
std::string* /*ts_low*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override { return nullptr; }
|
||||
|
||||
private:
|
||||
@@ -3443,6 +3442,27 @@ TEST_F(DBTest, BlockBasedTablePrefixIndexTest) {
|
||||
ASSERT_EQ("v1", Get("k1"));
|
||||
ASSERT_EQ("v2", Get("k2"));
|
||||
}
|
||||
TEST_F(DBTest, BlockBasedTablePrefixHashIndexTest) {
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(2));
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("kk1", "v1"));
|
||||
ASSERT_OK(Put("kk2", "v2"));
|
||||
ASSERT_OK(Put("kk", "v3"));
|
||||
ASSERT_OK(Put("k", "v4"));
|
||||
Flush();
|
||||
|
||||
ASSERT_EQ("v1", Get("kk1"));
|
||||
ASSERT_EQ("v2", Get("kk2"));
|
||||
|
||||
ASSERT_EQ("v3", Get("kk"));
|
||||
ASSERT_EQ("v4", Get("k"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, BlockBasedTablePrefixIndexTotalOrderSeek) {
|
||||
// create a DB with block prefix index
|
||||
@@ -4911,6 +4931,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
||||
|
||||
ASSERT_LT(SizeAtLevel(0) + SizeAtLevel(3) + SizeAtLevel(4),
|
||||
120U * 4000U + 50U * 24);
|
||||
// Make sure data in files in L3 is not compacted by removing all files
|
||||
@@ -6979,8 +7000,9 @@ TEST_F(DBTest, MemoryUsageWithMaxWriteBufferSizeToMaintain) {
|
||||
if ((size_all_mem_table > cur_active_mem) &&
|
||||
(cur_active_mem >=
|
||||
static_cast<uint64_t>(options.max_write_buffer_size_to_maintain)) &&
|
||||
(size_all_mem_table > options.max_write_buffer_size_to_maintain +
|
||||
options.write_buffer_size)) {
|
||||
(size_all_mem_table >
|
||||
static_cast<uint64_t>(options.max_write_buffer_size_to_maintain) +
|
||||
options.write_buffer_size)) {
|
||||
ASSERT_FALSE(memory_limit_exceeded);
|
||||
memory_limit_exceeded = true;
|
||||
} else {
|
||||
|
||||
@@ -2827,7 +2827,6 @@ TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
Close();
|
||||
Reopen(options);
|
||||
|
||||
uint64_t total_useful_bytes = 0;
|
||||
std::set<int> read_keys;
|
||||
std::string value;
|
||||
// Iter1: Read half the DB, Read even keys
|
||||
@@ -2838,8 +2837,6 @@ TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
|
||||
if (read_keys.find(i) == read_keys.end()) {
|
||||
auto internal_key = InternalKey(key, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes +=
|
||||
GetEncodedEntrySize(internal_key.size(), value.size());
|
||||
read_keys.insert(i);
|
||||
}
|
||||
}
|
||||
@@ -2866,8 +2863,6 @@ TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
|
||||
if (read_keys.find(i) == read_keys.end()) {
|
||||
auto internal_key = InternalKey(key, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes +=
|
||||
GetEncodedEntrySize(internal_key.size(), value.size());
|
||||
read_keys.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,6 +478,8 @@ Options DBTestBase::GetOptions(
|
||||
break;
|
||||
case kXXH3Checksum: {
|
||||
table_options.checksum = kXXH3;
|
||||
// Thrown in here for basic coverage:
|
||||
options.DisableExtraChecks();
|
||||
break;
|
||||
}
|
||||
case kFIFOCompaction: {
|
||||
|
||||
@@ -115,6 +115,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit SpecialEnv(Env* base, bool time_elapse_only_sleep = false);
|
||||
|
||||
static const char* kClassName() { return "SpecialEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
Status NewWritableFile(const std::string& f, std::unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
class SSTableFile : public WritableFile {
|
||||
|
||||
@@ -435,6 +435,51 @@ TEST_F(DBBasicTestWithTimestamp, UpdateFullHistoryTsLow) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, UpdateFullHistoryTsLowWithPublicAPI) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
std::string ts_low_str = Timestamp(9, 0);
|
||||
ASSERT_OK(
|
||||
db_->IncreaseFullHistoryTsLow(db_->DefaultColumnFamily(), ts_low_str));
|
||||
std::string result_ts_low;
|
||||
ASSERT_OK(db_->GetFullHistoryTsLow(nullptr, &result_ts_low));
|
||||
ASSERT_TRUE(test_cmp.CompareTimestamp(ts_low_str, result_ts_low) == 0);
|
||||
// test increase full_history_low backward
|
||||
std::string ts_low_str_back = Timestamp(8, 0);
|
||||
auto s = db_->IncreaseFullHistoryTsLow(db_->DefaultColumnFamily(),
|
||||
ts_low_str_back);
|
||||
ASSERT_EQ(s, Status::InvalidArgument());
|
||||
// test IncreaseFullHistoryTsLow with a timestamp whose length is longger
|
||||
// than the cf's timestamp size
|
||||
std::string ts_low_str_long(Timestamp(0, 0).size() + 1, 'a');
|
||||
s = db_->IncreaseFullHistoryTsLow(db_->DefaultColumnFamily(),
|
||||
ts_low_str_long);
|
||||
ASSERT_EQ(s, Status::InvalidArgument());
|
||||
// test IncreaseFullHistoryTsLow with a timestamp which is null
|
||||
std::string ts_low_str_null = "";
|
||||
s = db_->IncreaseFullHistoryTsLow(db_->DefaultColumnFamily(),
|
||||
ts_low_str_null);
|
||||
ASSERT_EQ(s, Status::InvalidArgument());
|
||||
// test IncreaseFullHistoryTsLow for a column family that does not enable
|
||||
// timestamp
|
||||
options.comparator = BytewiseComparator();
|
||||
DestroyAndReopen(options);
|
||||
ts_low_str = Timestamp(10, 0);
|
||||
s = db_->IncreaseFullHistoryTsLow(db_->DefaultColumnFamily(), ts_low_str);
|
||||
ASSERT_EQ(s, Status::InvalidArgument());
|
||||
// test GetFullHistoryTsLow for a column family that does not enable
|
||||
// timestamp
|
||||
std::string current_ts_low;
|
||||
s = db_->GetFullHistoryTsLow(db_->DefaultColumnFamily(), ¤t_ts_low);
|
||||
ASSERT_EQ(s, Status::InvalidArgument());
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, GetApproximateSizes) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100000000; // Large write buffer
|
||||
@@ -772,11 +817,13 @@ TEST_P(DBBasicTestWithTimestampTableOptions, SeekWithPrefixLessThanKey) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, SeekWithPrefixLongerThanKey) {
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, SeekWithCappedPrefix) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(20));
|
||||
// All of the keys or this test must be longer than 3 characters
|
||||
constexpr int kMinKeyLen = 3;
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(kMinKeyLen));
|
||||
options.memtable_whole_key_filtering = true;
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
BlockBasedTableOptions bbto;
|
||||
@@ -1494,7 +1541,7 @@ TEST_P(DBBasicTestWithTimestampTableOptions, MultiGetPrefixFilter) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(5));
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(3));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
|
||||
@@ -90,13 +90,6 @@ class ErrorHandlerFSListener : public EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
void OnErrorRecoveryCompleted(Status old_bg_error) override {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
recovery_complete_ = true;
|
||||
cv_.SignalAll();
|
||||
old_bg_error.PermitUncheckedError();
|
||||
}
|
||||
|
||||
void OnErrorRecoveryEnd(const BackgroundErrorRecoveryInfo& info) override {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
recovery_complete_ = true;
|
||||
|
||||
@@ -573,7 +573,7 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
|
||||
status = cfd_->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(
|
||||
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor.get(),
|
||||
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
|
||||
env_options_, cfd_->internal_comparator(),
|
||||
/*skip_filters*/ false, /*immortal*/ false,
|
||||
/*force_direct_prefetch*/ false, /*level*/ -1,
|
||||
|
||||
@@ -27,6 +27,8 @@ class ExternalSSTTestEnv : public EnvWrapper {
|
||||
public:
|
||||
ExternalSSTTestEnv(Env* t, bool fail_link)
|
||||
: EnvWrapper(t), fail_link_(fail_link) {}
|
||||
static const char* kClassName() { return "ExternalSSTTestEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
Status LinkFile(const std::string& s, const std::string& t) override {
|
||||
if (fail_link_) {
|
||||
|
||||
+15
-14
@@ -33,11 +33,11 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// iter.Next()
|
||||
class ForwardLevelIterator : public InternalIterator {
|
||||
public:
|
||||
ForwardLevelIterator(const ColumnFamilyData* const cfd,
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const SliceTransform* prefix_extractor,
|
||||
bool allow_unprepared_value)
|
||||
ForwardLevelIterator(
|
||||
const ColumnFamilyData* const cfd, const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
bool allow_unprepared_value)
|
||||
: cfd_(cfd),
|
||||
read_options_(read_options),
|
||||
files_(files),
|
||||
@@ -211,7 +211,8 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
Status status_;
|
||||
InternalIterator* file_iter_;
|
||||
PinnedIteratorsManager* pinned_iters_mgr_;
|
||||
const SliceTransform* prefix_extractor_;
|
||||
// Kept alive by ForwardIterator::sv_->mutable_cf_options
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor_;
|
||||
const bool allow_unprepared_value_;
|
||||
};
|
||||
|
||||
@@ -692,7 +693,7 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
l0_iters_.push_back(cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(), *l0,
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
|
||||
sv_->mutable_cf_options.prefix_extractor.get(),
|
||||
sv_->mutable_cf_options.prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
@@ -700,7 +701,7 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr, allow_unprepared_value_));
|
||||
}
|
||||
BuildLevelIterators(vstorage);
|
||||
BuildLevelIterators(vstorage, sv_);
|
||||
current_ = nullptr;
|
||||
is_prev_set_ = false;
|
||||
|
||||
@@ -772,7 +773,7 @@ void ForwardIterator::RenewIterators() {
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
|
||||
*l0_files_new[inew],
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
|
||||
svnew->mutable_cf_options.prefix_extractor.get(),
|
||||
svnew->mutable_cf_options.prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
@@ -791,7 +792,7 @@ void ForwardIterator::RenewIterators() {
|
||||
DeleteIterator(l);
|
||||
}
|
||||
level_iters_.clear();
|
||||
BuildLevelIterators(vstorage_new);
|
||||
BuildLevelIterators(vstorage_new, svnew);
|
||||
current_ = nullptr;
|
||||
is_prev_set_ = false;
|
||||
SVCleanup();
|
||||
@@ -805,7 +806,8 @@ void ForwardIterator::RenewIterators() {
|
||||
}
|
||||
}
|
||||
|
||||
void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage) {
|
||||
void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage,
|
||||
SuperVersion* sv) {
|
||||
level_iters_.reserve(vstorage->num_levels() - 1);
|
||||
for (int32_t level = 1; level < vstorage->num_levels(); ++level) {
|
||||
const auto& level_files = vstorage->LevelFiles(level);
|
||||
@@ -821,8 +823,7 @@ void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage) {
|
||||
} else {
|
||||
level_iters_.push_back(new ForwardLevelIterator(
|
||||
cfd_, read_options_, level_files,
|
||||
sv_->mutable_cf_options.prefix_extractor.get(),
|
||||
allow_unprepared_value_));
|
||||
sv->mutable_cf_options.prefix_extractor, allow_unprepared_value_));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -838,7 +839,7 @@ void ForwardIterator::ResetIncompleteIterators() {
|
||||
l0_iters_[i] = cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
|
||||
*l0_files[i], /*range_del_agg=*/nullptr,
|
||||
sv_->mutable_cf_options.prefix_extractor.get(),
|
||||
sv_->mutable_cf_options.prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
|
||||
@@ -97,7 +97,8 @@ class ForwardIterator : public InternalIterator {
|
||||
|
||||
void RebuildIterators(bool refresh_sv);
|
||||
void RenewIterators();
|
||||
void BuildLevelIterators(const VersionStorageInfo* vstorage);
|
||||
void BuildLevelIterators(const VersionStorageInfo* vstorage,
|
||||
SuperVersion* sv);
|
||||
void ResetIncompleteIterators();
|
||||
void SeekInternal(const Slice& internal_key, bool seek_to_first);
|
||||
void UpdateCurrent();
|
||||
|
||||
@@ -272,7 +272,6 @@ struct StatsThread {
|
||||
: db_(db), thread_(&StatsThread::run, this) {}
|
||||
|
||||
void run() {
|
||||
// using namespace std::chrono;
|
||||
auto tstart = std::chrono::steady_clock::now(), tlast = tstart;
|
||||
uint64_t wlast = 0, rlast = 0;
|
||||
while (!done_.load()) {
|
||||
|
||||
@@ -227,7 +227,7 @@ Status ImportColumnFamilyJob::GetIngestedFileInfo(
|
||||
|
||||
status = cfd_->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(
|
||||
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor.get(),
|
||||
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
|
||||
env_options_, cfd_->internal_comparator(),
|
||||
/*skip_filters*/ false, /*immortal*/ false,
|
||||
/*force_direct_prefetch*/ false, /*level*/ -1,
|
||||
|
||||
@@ -274,6 +274,9 @@ class TestFlushListener : public EventListener {
|
||||
ASSERT_TRUE(test_);
|
||||
if (db == test_->db_) {
|
||||
std::vector<std::vector<FileMetaData>> files_by_level;
|
||||
ASSERT_LT(info.cf_id, test_->handles_.size());
|
||||
ASSERT_GE(info.cf_id, 0u);
|
||||
ASSERT_NE(test_->handles_[info.cf_id], nullptr);
|
||||
test_->dbfull()->TEST_GetFilesMetaData(test_->handles_[info.cf_id],
|
||||
&files_by_level);
|
||||
|
||||
@@ -512,6 +515,9 @@ TEST_F(EventListenerTest, DisableBGCompaction) {
|
||||
db_->GetColumnFamilyMetaData(handles_[1], &cf_meta);
|
||||
}
|
||||
ASSERT_GE(listener->slowdown_count, kSlowdownTrigger * 9);
|
||||
// We don't want the listener executing during DBTestBase::Close() due to
|
||||
// race on handles_.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
}
|
||||
|
||||
class TestCompactionReasonListener : public EventListener {
|
||||
@@ -687,6 +693,8 @@ class TableFileCreationListener : public EventListener {
|
||||
class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv(Env* t) : EnvWrapper(t) {}
|
||||
static const char* kClassName() { return "TestEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
void SetStatus(Status s) { status_ = s; }
|
||||
|
||||
|
||||
@@ -13,7 +13,19 @@
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
using namespace ROCKSDB_NAMESPACE;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilter;
|
||||
using ROCKSDB_NAMESPACE::CompactionStyle;
|
||||
using ROCKSDB_NAMESPACE::CompactRangeOptions;
|
||||
using ROCKSDB_NAMESPACE::CompressionType;
|
||||
using ROCKSDB_NAMESPACE::DB;
|
||||
using ROCKSDB_NAMESPACE::DestroyDB;
|
||||
using ROCKSDB_NAMESPACE::FlushOptions;
|
||||
using ROCKSDB_NAMESPACE::Iterator;
|
||||
using ROCKSDB_NAMESPACE::Options;
|
||||
using ROCKSDB_NAMESPACE::ReadOptions;
|
||||
using ROCKSDB_NAMESPACE::Slice;
|
||||
using ROCKSDB_NAMESPACE::WriteBatch;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -38,7 +50,8 @@ class ManualCompactionTest : public testing::Test {
|
||||
public:
|
||||
ManualCompactionTest() {
|
||||
// Get rid of any state from an old run.
|
||||
dbname_ = test::PerThreadDBPath("rocksdb_manual_compaction_test");
|
||||
dbname_ = ROCKSDB_NAMESPACE::test::PerThreadDBPath(
|
||||
"rocksdb_manual_compaction_test");
|
||||
DestroyDB(dbname_, Options());
|
||||
}
|
||||
|
||||
@@ -91,12 +104,12 @@ TEST_F(ManualCompactionTest, CompactTouchesAllKeys) {
|
||||
Options options;
|
||||
if (iter == 0) { // level compaction
|
||||
options.num_levels = 3;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
|
||||
} else { // universal compaction
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
|
||||
}
|
||||
options.create_if_missing = true;
|
||||
options.compression = kNoCompression;
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
options.compaction_filter = new DestroyAllCompactionFilter();
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db));
|
||||
|
||||
@@ -129,7 +142,7 @@ TEST_F(ManualCompactionTest, Test) {
|
||||
Options db_options;
|
||||
db_options.write_buffer_size = 1024;
|
||||
db_options.create_if_missing = true;
|
||||
db_options.compression = kNoCompression;
|
||||
db_options.compression = CompressionType::kNoCompression;
|
||||
ASSERT_OK(DB::Open(db_options, dbname_, &db));
|
||||
|
||||
// create first key range
|
||||
@@ -182,9 +195,9 @@ TEST_F(ManualCompactionTest, SkipLevel) {
|
||||
options.num_levels = 3;
|
||||
// Initially, flushed L0 files won't exceed 100.
|
||||
options.level0_file_num_compaction_trigger = 100;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
|
||||
options.create_if_missing = true;
|
||||
options.compression = kNoCompression;
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
LogCompactionFilter* filter = new LogCompactionFilter();
|
||||
options.compaction_filter = filter;
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db));
|
||||
|
||||
+37
-29
@@ -33,6 +33,7 @@
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
@@ -447,11 +448,13 @@ FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIterator(
|
||||
is_range_del_table_empty_.load(std::memory_order_relaxed)) {
|
||||
return nullptr;
|
||||
}
|
||||
return NewRangeTombstoneIteratorInternal(read_options, read_seq);
|
||||
}
|
||||
|
||||
FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIteratorInternal(
|
||||
const ReadOptions& read_options, SequenceNumber read_seq) {
|
||||
auto* unfragmented_iter = new MemTableIterator(
|
||||
*this, read_options, nullptr /* arena */, true /* use_range_del_table */);
|
||||
if (unfragmented_iter == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto fragmented_tombstone_list =
|
||||
std::make_shared<FragmentedRangeTombstoneList>(
|
||||
std::unique_ptr<InternalIterator>(unfragmented_iter),
|
||||
@@ -960,53 +963,58 @@ void MemTable::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
PERF_TIMER_GUARD(get_from_memtable_time);
|
||||
|
||||
// For now, memtable Bloom filter is effectively disabled if there are any
|
||||
// range tombstones. This is the simplest way to ensure range tombstones are
|
||||
// handled. TODO: allow Bloom checks where max_covering_tombstone_seq==0
|
||||
bool no_range_del = read_options.ignore_range_deletions ||
|
||||
is_range_del_table_empty_.load(std::memory_order_relaxed);
|
||||
MultiGetRange temp_range(*range, range->begin(), range->end());
|
||||
if (bloom_filter_) {
|
||||
std::array<Slice*, MultiGetContext::MAX_BATCH_SIZE> keys;
|
||||
std::array<bool, MultiGetContext::MAX_BATCH_SIZE> may_match = {{true}};
|
||||
autovector<Slice, MultiGetContext::MAX_BATCH_SIZE> prefixes;
|
||||
if (bloom_filter_ && no_range_del) {
|
||||
bool whole_key =
|
||||
!prefix_extractor_ || moptions_.memtable_whole_key_filtering;
|
||||
std::array<Slice, MultiGetContext::MAX_BATCH_SIZE> bloom_keys;
|
||||
std::array<bool, MultiGetContext::MAX_BATCH_SIZE> may_match;
|
||||
std::array<size_t, MultiGetContext::MAX_BATCH_SIZE> range_indexes;
|
||||
int num_keys = 0;
|
||||
for (auto iter = temp_range.begin(); iter != temp_range.end(); ++iter) {
|
||||
if (!prefix_extractor_) {
|
||||
keys[num_keys++] = &iter->ukey_without_ts;
|
||||
if (whole_key) {
|
||||
bloom_keys[num_keys] = iter->ukey_without_ts;
|
||||
range_indexes[num_keys++] = iter.index();
|
||||
} else if (prefix_extractor_->InDomain(iter->ukey_without_ts)) {
|
||||
prefixes.emplace_back(
|
||||
prefix_extractor_->Transform(iter->ukey_without_ts));
|
||||
keys[num_keys++] = &prefixes.back();
|
||||
bloom_keys[num_keys] =
|
||||
prefix_extractor_->Transform(iter->ukey_without_ts);
|
||||
range_indexes[num_keys++] = iter.index();
|
||||
} else {
|
||||
// TODO: consider not counting these as Bloom hits to more closely
|
||||
// match bloom_sst_hit_count
|
||||
PERF_COUNTER_ADD(bloom_memtable_hit_count, 1);
|
||||
}
|
||||
}
|
||||
bloom_filter_->MayContain(num_keys, &keys[0], &may_match[0]);
|
||||
int idx = 0;
|
||||
for (auto iter = temp_range.begin(); iter != temp_range.end(); ++iter) {
|
||||
if (prefix_extractor_ &&
|
||||
!prefix_extractor_->InDomain(iter->ukey_without_ts)) {
|
||||
PERF_COUNTER_ADD(bloom_memtable_hit_count, 1);
|
||||
continue;
|
||||
}
|
||||
if (!may_match[idx]) {
|
||||
temp_range.SkipKey(iter);
|
||||
bloom_filter_->MayContain(num_keys, &bloom_keys[0], &may_match[0]);
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
if (!may_match[i]) {
|
||||
temp_range.SkipIndex(range_indexes[i]);
|
||||
PERF_COUNTER_ADD(bloom_memtable_miss_count, 1);
|
||||
} else {
|
||||
PERF_COUNTER_ADD(bloom_memtable_hit_count, 1);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
for (auto iter = temp_range.begin(); iter != temp_range.end(); ++iter) {
|
||||
SequenceNumber seq = kMaxSequenceNumber;
|
||||
bool found_final_value{false};
|
||||
bool merge_in_progress = iter->s->IsMergeInProgress();
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
NewRangeTombstoneIterator(
|
||||
read_options, GetInternalKeySeqno(iter->lkey->internal_key())));
|
||||
if (range_del_iter != nullptr) {
|
||||
if (!no_range_del) {
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
NewRangeTombstoneIteratorInternal(
|
||||
read_options, GetInternalKeySeqno(iter->lkey->internal_key())));
|
||||
iter->max_covering_tombstone_seq = std::max(
|
||||
iter->max_covering_tombstone_seq,
|
||||
range_del_iter->MaxCoveringTombstoneSeqnum(iter->lkey->user_key()));
|
||||
}
|
||||
SequenceNumber dummy_seq;
|
||||
GetFromTable(*(iter->lkey), iter->max_covering_tombstone_seq, true,
|
||||
callback, &iter->is_blob_index, iter->value->GetSelf(),
|
||||
iter->timestamp, iter->s, &(iter->merge_context), &seq,
|
||||
iter->timestamp, iter->s, &(iter->merge_context), &dummy_seq,
|
||||
&found_final_value, &merge_in_progress);
|
||||
|
||||
if (!found_final_value && merge_in_progress) {
|
||||
|
||||
@@ -600,6 +600,10 @@ class MemTable {
|
||||
std::string* value, std::string* timestamp, Status* s,
|
||||
MergeContext* merge_context, SequenceNumber* seq,
|
||||
bool* found_final_value, bool* merge_in_progress);
|
||||
|
||||
// Always returns non-null and assumes certain pre-checks are done
|
||||
FragmentedRangeTombstoneIterator* NewRangeTombstoneIteratorInternal(
|
||||
const ReadOptions& read_options, SequenceNumber read_seq);
|
||||
};
|
||||
|
||||
extern const char* EncodeKey(std::string* scratch, const Slice& target);
|
||||
|
||||
@@ -72,6 +72,8 @@ class CountMergeOperator : public AssociativeMergeOperator {
|
||||
class EnvMergeTest : public EnvWrapper {
|
||||
public:
|
||||
EnvMergeTest() : EnvWrapper(Env::Default()) {}
|
||||
static const char* kClassName() { return "MergeEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
// ~EnvMergeTest() override {}
|
||||
|
||||
uint64_t NowNanos() override {
|
||||
|
||||
@@ -368,7 +368,7 @@ class TestPlainTableFactory : public PlainTableFactory {
|
||||
table_reader_options.internal_comparator, encoding_type, file_size,
|
||||
bloom_bits_per_key_, hash_table_ratio_, index_sparseness_,
|
||||
std::move(props), std::move(file), table_reader_options.ioptions,
|
||||
table_reader_options.prefix_extractor, expect_bloom_not_match_,
|
||||
table_reader_options.prefix_extractor.get(), expect_bloom_not_match_,
|
||||
store_index_in_file_, column_family_id_, column_family_name_));
|
||||
|
||||
*table = std::move(new_reader);
|
||||
|
||||
+1
-1
@@ -544,7 +544,7 @@ class Repairer {
|
||||
InternalIterator* iter = table_cache_->NewIterator(
|
||||
ropts, file_options_, cfd->internal_comparator(), t->meta,
|
||||
nullptr /* range_del_agg */,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kRepair, /*arena=*/nullptr, /*skip_filters=*/false,
|
||||
/*level=*/-1, /*max_file_size_for_l0_meta_pin=*/0,
|
||||
|
||||
+28
-28
@@ -102,8 +102,8 @@ Status TableCache::GetTableReader(
|
||||
const InternalKeyComparator& internal_comparator, const FileDescriptor& fd,
|
||||
bool sequential_mode, bool record_read_stats, HistogramImpl* file_read_hist,
|
||||
std::unique_ptr<TableReader>* table_reader,
|
||||
const SliceTransform* prefix_extractor, bool skip_filters, int level,
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
bool skip_filters, int level, bool prefetch_index_and_filter_in_cache,
|
||||
size_t max_file_size_for_l0_meta_pin, Temperature file_temperature) {
|
||||
std::string fname =
|
||||
TableFileName(ioptions_.cf_paths, fd.GetNumber(), fd.GetPathId());
|
||||
@@ -160,7 +160,8 @@ void TableCache::EraseHandle(const FileDescriptor& fd, Cache::Handle* handle) {
|
||||
Status TableCache::FindTable(
|
||||
const ReadOptions& ro, const FileOptions& file_options,
|
||||
const InternalKeyComparator& internal_comparator, const FileDescriptor& fd,
|
||||
Cache::Handle** handle, const SliceTransform* prefix_extractor,
|
||||
Cache::Handle** handle,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
const bool no_io, bool record_read_stats, HistogramImpl* file_read_hist,
|
||||
bool skip_filters, int level, bool prefetch_index_and_filter_in_cache,
|
||||
size_t max_file_size_for_l0_meta_pin, Temperature file_temperature) {
|
||||
@@ -209,7 +210,8 @@ Status TableCache::FindTable(
|
||||
InternalIterator* TableCache::NewIterator(
|
||||
const ReadOptions& options, const FileOptions& file_options,
|
||||
const InternalKeyComparator& icomparator, const FileMetaData& file_meta,
|
||||
RangeDelAggregator* range_del_agg, const SliceTransform* prefix_extractor,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
TableReader** table_reader_ptr, HistogramImpl* file_read_hist,
|
||||
TableReaderCaller caller, Arena* arena, bool skip_filters, int level,
|
||||
size_t max_file_size_for_l0_meta_pin,
|
||||
@@ -243,10 +245,9 @@ InternalIterator* TableCache::NewIterator(
|
||||
!options.table_filter(*table_reader->GetTableProperties())) {
|
||||
result = NewEmptyInternalIterator<Slice>(arena);
|
||||
} else {
|
||||
result = table_reader->NewIterator(options, prefix_extractor, arena,
|
||||
skip_filters, caller,
|
||||
file_options.compaction_readahead_size,
|
||||
allow_unprepared_value);
|
||||
result = table_reader->NewIterator(
|
||||
options, prefix_extractor.get(), arena, skip_filters, caller,
|
||||
file_options.compaction_readahead_size, allow_unprepared_value);
|
||||
}
|
||||
if (handle != nullptr) {
|
||||
result->RegisterCleanup(&UnrefEntry, cache_, handle);
|
||||
@@ -395,13 +396,13 @@ bool TableCache::GetFromRowCache(const Slice& user_key, IterKey& row_cache_key,
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status TableCache::Get(const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, const Slice& k,
|
||||
GetContext* get_context,
|
||||
const SliceTransform* prefix_extractor,
|
||||
HistogramImpl* file_read_hist, bool skip_filters,
|
||||
int level, size_t max_file_size_for_l0_meta_pin) {
|
||||
Status TableCache::Get(
|
||||
const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, const Slice& k, GetContext* get_context,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
HistogramImpl* file_read_hist, bool skip_filters, int level,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
auto& fd = file_meta.fd;
|
||||
std::string* row_cache_entry = nullptr;
|
||||
bool done = false;
|
||||
@@ -451,7 +452,7 @@ Status TableCache::Get(const ReadOptions& options,
|
||||
}
|
||||
if (s.ok()) {
|
||||
get_context->SetReplayLog(row_cache_entry); // nullptr if no cache.
|
||||
s = t->Get(options, k, get_context, prefix_extractor, skip_filters);
|
||||
s = t->Get(options, k, get_context, prefix_extractor.get(), skip_filters);
|
||||
get_context->SetReplayLog(nullptr);
|
||||
} else if (options.read_tier == kBlockCacheTier && s.IsIncomplete()) {
|
||||
// Couldn't find Table in cache but treat as kFound if no_io set
|
||||
@@ -482,13 +483,12 @@ Status TableCache::Get(const ReadOptions& options,
|
||||
}
|
||||
|
||||
// Batched version of TableCache::MultiGet.
|
||||
Status TableCache::MultiGet(const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta,
|
||||
const MultiGetContext::Range* mget_range,
|
||||
const SliceTransform* prefix_extractor,
|
||||
HistogramImpl* file_read_hist, bool skip_filters,
|
||||
int level) {
|
||||
Status TableCache::MultiGet(
|
||||
const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, const MultiGetContext::Range* mget_range,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
HistogramImpl* file_read_hist, bool skip_filters, int level) {
|
||||
auto& fd = file_meta.fd;
|
||||
Status s;
|
||||
TableReader* t = fd.table_reader;
|
||||
@@ -559,7 +559,7 @@ Status TableCache::MultiGet(const ReadOptions& options,
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
t->MultiGet(options, &table_range, prefix_extractor, skip_filters);
|
||||
t->MultiGet(options, &table_range, prefix_extractor.get(), skip_filters);
|
||||
} else if (options.read_tier == kBlockCacheTier && s.IsIncomplete()) {
|
||||
for (auto iter = table_range.begin(); iter != table_range.end(); ++iter) {
|
||||
Status* status = iter->s;
|
||||
@@ -612,7 +612,7 @@ Status TableCache::GetTableProperties(
|
||||
const FileOptions& file_options,
|
||||
const InternalKeyComparator& internal_comparator, const FileDescriptor& fd,
|
||||
std::shared_ptr<const TableProperties>* properties,
|
||||
const SliceTransform* prefix_extractor, bool no_io) {
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor, bool no_io) {
|
||||
auto table_reader = fd.table_reader;
|
||||
// table already been pre-loaded?
|
||||
if (table_reader) {
|
||||
@@ -637,7 +637,7 @@ Status TableCache::GetTableProperties(
|
||||
size_t TableCache::GetMemoryUsageByTableReader(
|
||||
const FileOptions& file_options,
|
||||
const InternalKeyComparator& internal_comparator, const FileDescriptor& fd,
|
||||
const SliceTransform* prefix_extractor) {
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor) {
|
||||
auto table_reader = fd.table_reader;
|
||||
// table already been pre-loaded?
|
||||
if (table_reader) {
|
||||
@@ -674,7 +674,7 @@ void TableCache::Evict(Cache* cache, uint64_t file_number) {
|
||||
uint64_t TableCache::ApproximateOffsetOf(
|
||||
const Slice& key, const FileDescriptor& fd, TableReaderCaller caller,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const SliceTransform* prefix_extractor) {
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor) {
|
||||
uint64_t result = 0;
|
||||
TableReader* table_reader = fd.table_reader;
|
||||
Cache::Handle* table_handle = nullptr;
|
||||
@@ -701,7 +701,7 @@ uint64_t TableCache::ApproximateOffsetOf(
|
||||
uint64_t TableCache::ApproximateSize(
|
||||
const Slice& start, const Slice& end, const FileDescriptor& fd,
|
||||
TableReaderCaller caller, const InternalKeyComparator& internal_comparator,
|
||||
const SliceTransform* prefix_extractor) {
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor) {
|
||||
uint64_t result = 0;
|
||||
TableReader* table_reader = fd.table_reader;
|
||||
Cache::Handle* table_handle = nullptr;
|
||||
|
||||
+52
-49
@@ -73,9 +73,10 @@ class TableCache {
|
||||
const ReadOptions& options, const FileOptions& toptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, RangeDelAggregator* range_del_agg,
|
||||
const SliceTransform* prefix_extractor, TableReader** table_reader_ptr,
|
||||
HistogramImpl* file_read_hist, TableReaderCaller caller, Arena* arena,
|
||||
bool skip_filters, int level, size_t max_file_size_for_l0_meta_pin,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
TableReader** table_reader_ptr, HistogramImpl* file_read_hist,
|
||||
TableReaderCaller caller, Arena* arena, bool skip_filters, int level,
|
||||
size_t max_file_size_for_l0_meta_pin,
|
||||
const InternalKey* smallest_compaction_key,
|
||||
const InternalKey* largest_compaction_key, bool allow_unprepared_value);
|
||||
|
||||
@@ -89,13 +90,13 @@ class TableCache {
|
||||
// recorded
|
||||
// @param skip_filters Disables loading/accessing the filter block
|
||||
// @param level The level this table is at, -1 for "not set / don't know"
|
||||
Status Get(const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, const Slice& k,
|
||||
GetContext* get_context,
|
||||
const SliceTransform* prefix_extractor = nullptr,
|
||||
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
|
||||
int level = -1, size_t max_file_size_for_l0_meta_pin = 0);
|
||||
Status Get(
|
||||
const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, const Slice& k, GetContext* get_context,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
|
||||
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
|
||||
int level = -1, size_t max_file_size_for_l0_meta_pin = 0);
|
||||
|
||||
// Return the range delete tombstone iterator of the file specified by
|
||||
// `file_meta`.
|
||||
@@ -114,13 +115,13 @@ class TableCache {
|
||||
// in the embedded GetContext
|
||||
// @param skip_filters Disables loading/accessing the filter block
|
||||
// @param level The level this table is at, -1 for "not set / don't know"
|
||||
Status MultiGet(const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta,
|
||||
const MultiGetContext::Range* mget_range,
|
||||
const SliceTransform* prefix_extractor = nullptr,
|
||||
HistogramImpl* file_read_hist = nullptr,
|
||||
bool skip_filters = false, int level = -1);
|
||||
Status MultiGet(
|
||||
const ReadOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, const MultiGetContext::Range* mget_range,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
|
||||
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
|
||||
int level = -1);
|
||||
|
||||
// Evict any entry for the specified file number
|
||||
static void Evict(Cache* cache, uint64_t file_number);
|
||||
@@ -135,16 +136,16 @@ class TableCache {
|
||||
// Find table reader
|
||||
// @param skip_filters Disables loading/accessing the filter block
|
||||
// @param level == -1 means not specified
|
||||
Status FindTable(const ReadOptions& ro, const FileOptions& toptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& file_fd, Cache::Handle**,
|
||||
const SliceTransform* prefix_extractor = nullptr,
|
||||
const bool no_io = false, bool record_read_stats = true,
|
||||
HistogramImpl* file_read_hist = nullptr,
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
size_t max_file_size_for_l0_meta_pin = 0,
|
||||
Temperature file_temperature = Temperature::kUnknown);
|
||||
Status FindTable(
|
||||
const ReadOptions& ro, const FileOptions& toptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& file_fd, Cache::Handle**,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
|
||||
const bool no_io = false, bool record_read_stats = true,
|
||||
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
|
||||
int level = -1, bool prefetch_index_and_filter_in_cache = true,
|
||||
size_t max_file_size_for_l0_meta_pin = 0,
|
||||
Temperature file_temperature = Temperature::kUnknown);
|
||||
|
||||
// Get TableReader from a cache handle.
|
||||
TableReader* GetTableReaderFromHandle(Cache::Handle* handle);
|
||||
@@ -155,12 +156,13 @@ class TableCache {
|
||||
// @returns: `properties` will be reset on success. Please note that we will
|
||||
// return Status::Incomplete() if table is not present in cache and
|
||||
// we set `no_io` to be true.
|
||||
Status GetTableProperties(const FileOptions& toptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& file_meta,
|
||||
std::shared_ptr<const TableProperties>* properties,
|
||||
const SliceTransform* prefix_extractor = nullptr,
|
||||
bool no_io = false);
|
||||
Status GetTableProperties(
|
||||
const FileOptions& toptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& file_meta,
|
||||
std::shared_ptr<const TableProperties>* properties,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
|
||||
bool no_io = false);
|
||||
|
||||
// Return total memory usage of the table reader of the file.
|
||||
// 0 if table reader of the file is not loaded.
|
||||
@@ -168,20 +170,21 @@ class TableCache {
|
||||
const FileOptions& toptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& fd,
|
||||
const SliceTransform* prefix_extractor = nullptr);
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr);
|
||||
|
||||
// Returns approximated offset of a key in a file represented by fd.
|
||||
uint64_t ApproximateOffsetOf(
|
||||
const Slice& key, const FileDescriptor& fd, TableReaderCaller caller,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const SliceTransform* prefix_extractor = nullptr);
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr);
|
||||
|
||||
// Returns approximated data size between start and end keys in a file
|
||||
// represented by fd (the start key must not be greater than the end key).
|
||||
uint64_t ApproximateSize(const Slice& start, const Slice& end,
|
||||
const FileDescriptor& fd, TableReaderCaller caller,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const SliceTransform* prefix_extractor = nullptr);
|
||||
uint64_t ApproximateSize(
|
||||
const Slice& start, const Slice& end, const FileDescriptor& fd,
|
||||
TableReaderCaller caller,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr);
|
||||
|
||||
// Release the handle from a cache
|
||||
void ReleaseHandle(Cache::Handle* handle);
|
||||
@@ -202,16 +205,16 @@ class TableCache {
|
||||
|
||||
private:
|
||||
// Build a table reader
|
||||
Status GetTableReader(const ReadOptions& ro, const FileOptions& file_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& fd, bool sequential_mode,
|
||||
bool record_read_stats, HistogramImpl* file_read_hist,
|
||||
std::unique_ptr<TableReader>* table_reader,
|
||||
const SliceTransform* prefix_extractor = nullptr,
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
size_t max_file_size_for_l0_meta_pin = 0,
|
||||
Temperature file_temperature = Temperature::kUnknown);
|
||||
Status GetTableReader(
|
||||
const ReadOptions& ro, const FileOptions& file_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileDescriptor& fd, bool sequential_mode, bool record_read_stats,
|
||||
HistogramImpl* file_read_hist, std::unique_ptr<TableReader>* table_reader,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
size_t max_file_size_for_l0_meta_pin = 0,
|
||||
Temperature file_temperature = Temperature::kUnknown);
|
||||
|
||||
// Create a key prefix for looking up the row cache. The prefix is of the
|
||||
// format row_cache_id + fd_number + seq_no. Later, the user key can be
|
||||
|
||||
@@ -1143,11 +1143,11 @@ class VersionBuilder::Rep {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status LoadTableHandlers(InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
Status LoadTableHandlers(
|
||||
InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
assert(table_cache_ != nullptr);
|
||||
|
||||
size_t table_cache_capacity = table_cache_->get_cache()->GetCapacity();
|
||||
@@ -1272,7 +1272,7 @@ Status VersionBuilder::SaveTo(VersionStorageInfo* vstorage) const {
|
||||
Status VersionBuilder::LoadTableHandlers(
|
||||
InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
return rep_->LoadTableHandlers(
|
||||
internal_stats, max_threads, prefetch_index_and_filter_in_cache,
|
||||
|
||||
@@ -39,11 +39,11 @@ class VersionBuilder {
|
||||
bool CheckConsistencyForNumLevels();
|
||||
Status Apply(const VersionEdit* edit);
|
||||
Status SaveTo(VersionStorageInfo* vstorage) const;
|
||||
Status LoadTableHandlers(InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor,
|
||||
size_t max_file_size_for_l0_meta_pin);
|
||||
Status LoadTableHandlers(
|
||||
InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
size_t max_file_size_for_l0_meta_pin);
|
||||
uint64_t GetMinOldestBlobFileNumber() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -418,11 +418,17 @@ class VersionEdit {
|
||||
temperature, oldest_blob_file_number, oldest_ancester_time,
|
||||
file_creation_time, file_checksum, file_checksum_func_name,
|
||||
min_timestamp, max_timestamp));
|
||||
if (!HasLastSequence() || largest_seqno > GetLastSequence()) {
|
||||
SetLastSequence(largest_seqno);
|
||||
}
|
||||
}
|
||||
|
||||
void AddFile(int level, const FileMetaData& f) {
|
||||
assert(f.fd.smallest_seqno <= f.fd.largest_seqno);
|
||||
new_files_.emplace_back(level, f);
|
||||
if (!HasLastSequence() || f.fd.largest_seqno > GetLastSequence()) {
|
||||
SetLastSequence(f.fd.largest_seqno);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the table files added as well as their associated levels.
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "db/version_edit_handler.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <sstream>
|
||||
|
||||
#include "db/blob/blob_file_cache.h"
|
||||
#include "db/blob/blob_file_reader.h"
|
||||
@@ -68,6 +69,26 @@ void VersionEditHandlerBase::Iterate(log::Reader& reader,
|
||||
CheckIterationResult(reader, &s);
|
||||
|
||||
if (!s.ok()) {
|
||||
if (s.IsCorruption()) {
|
||||
// when we find a Corruption error, something is
|
||||
// wrong with the underlying file. in this case we
|
||||
// want to report the filename, so in here we append
|
||||
// the filename to the Corruption message
|
||||
assert(reader.file());
|
||||
|
||||
// build a new error message
|
||||
std::stringstream message;
|
||||
// append previous dynamic state message
|
||||
const char* state = s.getState();
|
||||
if (state != nullptr) {
|
||||
message << state;
|
||||
message << ' ';
|
||||
}
|
||||
// append the filename to the corruption message
|
||||
message << "in file " << reader.file()->file_name();
|
||||
// overwrite the status with the extended status
|
||||
s = Status(s.code(), s.subcode(), s.severity(), message.str());
|
||||
}
|
||||
status_ = s;
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("VersionEditHandlerBase::Iterate:Finish",
|
||||
@@ -442,6 +463,14 @@ void VersionEditHandler::CheckIterationResult(const log::Reader& reader,
|
||||
last_seq > version_set_->last_sequence_.load()) {
|
||||
version_set_->last_sequence_.store(last_seq);
|
||||
}
|
||||
if (last_seq != kMaxSequenceNumber &&
|
||||
last_seq > version_set_->descriptor_last_sequence_) {
|
||||
// This is the maximum last sequence of all `VersionEdit`s iterated. It
|
||||
// may be greater than the maximum `largest_seqno` of all files in case
|
||||
// the newest data referred to by the MANIFEST has been dropped or had its
|
||||
// sequence number zeroed through compaction.
|
||||
version_set_->descriptor_last_sequence_ = last_seq;
|
||||
}
|
||||
version_set_->prev_log_number_ = version_edit_params_.prev_log_number_;
|
||||
}
|
||||
}
|
||||
@@ -535,7 +564,7 @@ Status VersionEditHandler::LoadTables(ColumnFamilyData* cfd,
|
||||
cfd->internal_stats(),
|
||||
version_set_->db_options_->max_file_opening_threads,
|
||||
prefetch_index_and_filter_in_cache, is_initial_load,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor,
|
||||
MaxFileSizeForL0MetaPin(*cfd->GetLatestMutableCFOptions()));
|
||||
if ((s.IsPathNotFound() || s.IsCorruption()) && no_error_if_files_missing_) {
|
||||
s = Status::OK();
|
||||
@@ -597,6 +626,11 @@ Status VersionEditHandler::ExtractInfoFromVersionEdit(ColumnFamilyData* cfd,
|
||||
edit.min_log_number_to_keep_);
|
||||
}
|
||||
if (edit.has_last_sequence_) {
|
||||
// `VersionEdit::last_sequence_`s are assumed to be non-decreasing. This
|
||||
// is legacy behavior that cannot change without breaking downgrade
|
||||
// compatibility.
|
||||
assert(!version_edit_params_.has_last_sequence_ ||
|
||||
version_edit_params_.last_sequence_ <= edit.last_sequence_);
|
||||
version_edit_params_.SetLastSequence(edit.last_sequence_);
|
||||
}
|
||||
if (!version_edit_params_.has_prev_log_number_) {
|
||||
|
||||
+64
-34
@@ -858,9 +858,10 @@ class LevelIterator final : public InternalIterator {
|
||||
const FileOptions& file_options,
|
||||
const InternalKeyComparator& icomparator,
|
||||
const LevelFilesBrief* flevel,
|
||||
const SliceTransform* prefix_extractor, bool should_sample,
|
||||
HistogramImpl* file_read_hist, TableReaderCaller caller,
|
||||
bool skip_filters, int level, RangeDelAggregator* range_del_agg,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
bool should_sample, HistogramImpl* file_read_hist,
|
||||
TableReaderCaller caller, bool skip_filters, int level,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const std::vector<AtomicCompactionUnitBoundary>*
|
||||
compaction_boundaries = nullptr,
|
||||
bool allow_unprepared_value = false)
|
||||
@@ -1011,7 +1012,7 @@ class LevelIterator final : public InternalIterator {
|
||||
// `prefix_extractor_` may be non-null even for total order seek. Checking
|
||||
// this variable is not the right way to identify whether prefix iterator
|
||||
// is used.
|
||||
const SliceTransform* prefix_extractor_;
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor_;
|
||||
|
||||
HistogramImpl* file_read_hist_;
|
||||
bool should_sample_;
|
||||
@@ -1243,7 +1244,7 @@ Status Version::GetTableProperties(std::shared_ptr<const TableProperties>* tp,
|
||||
auto ioptions = cfd_->ioptions();
|
||||
Status s = table_cache->GetTableProperties(
|
||||
file_options_, cfd_->internal_comparator(), file_meta->fd, tp,
|
||||
mutable_cf_options_.prefix_extractor.get(), true /* no io */);
|
||||
mutable_cf_options_.prefix_extractor, true /* no io */);
|
||||
if (s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1436,7 +1437,7 @@ size_t Version::GetMemoryUsageByTableReaders() {
|
||||
for (size_t i = 0; i < file_level.num_files; i++) {
|
||||
total_usage += cfd_->table_cache()->GetMemoryUsageByTableReader(
|
||||
file_options_, cfd_->internal_comparator(), file_level.files[i].fd,
|
||||
mutable_cf_options_.prefix_extractor.get());
|
||||
mutable_cf_options_.prefix_extractor);
|
||||
}
|
||||
}
|
||||
return total_usage;
|
||||
@@ -1616,7 +1617,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
merge_iter_builder->AddIterator(cfd_->table_cache()->NewIterator(
|
||||
read_options, soptions, cfd_->internal_comparator(),
|
||||
*file.file_metadata, range_del_agg,
|
||||
mutable_cf_options_.prefix_extractor.get(), nullptr,
|
||||
mutable_cf_options_.prefix_extractor, nullptr,
|
||||
cfd_->internal_stats()->GetFileReadHist(0),
|
||||
TableReaderCaller::kUserIterator, arena,
|
||||
/*skip_filters=*/false, /*level=*/0, max_file_size_for_l0_meta_pin_,
|
||||
@@ -1640,7 +1641,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
merge_iter_builder->AddIterator(new (mem) LevelIterator(
|
||||
cfd_->table_cache(), read_options, soptions,
|
||||
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
|
||||
mutable_cf_options_.prefix_extractor.get(), should_sample_file_read(),
|
||||
mutable_cf_options_.prefix_extractor, should_sample_file_read(),
|
||||
cfd_->internal_stats()->GetFileReadHist(level),
|
||||
TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
|
||||
range_del_agg,
|
||||
@@ -1675,7 +1676,7 @@ Status Version::OverlapWithLevelIterator(const ReadOptions& read_options,
|
||||
ScopedArenaIterator iter(cfd_->table_cache()->NewIterator(
|
||||
read_options, file_options, cfd_->internal_comparator(),
|
||||
*file->file_metadata, &range_del_agg,
|
||||
mutable_cf_options_.prefix_extractor.get(), nullptr,
|
||||
mutable_cf_options_.prefix_extractor, nullptr,
|
||||
cfd_->internal_stats()->GetFileReadHist(0),
|
||||
TableReaderCaller::kUserIterator, &arena,
|
||||
/*skip_filters=*/false, /*level=*/0, max_file_size_for_l0_meta_pin_,
|
||||
@@ -1693,7 +1694,7 @@ Status Version::OverlapWithLevelIterator(const ReadOptions& read_options,
|
||||
ScopedArenaIterator iter(new (mem) LevelIterator(
|
||||
cfd_->table_cache(), read_options, file_options,
|
||||
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
|
||||
mutable_cf_options_.prefix_extractor.get(), should_sample_file_read(),
|
||||
mutable_cf_options_.prefix_extractor, should_sample_file_read(),
|
||||
cfd_->internal_stats()->GetFileReadHist(level),
|
||||
TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
|
||||
&range_del_agg));
|
||||
@@ -2026,7 +2027,7 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
StopWatchNano timer(clock_, timer_enabled /* auto_start */);
|
||||
*status = table_cache_->Get(
|
||||
read_options, *internal_comparator(), *f->file_metadata, ikey,
|
||||
&get_context, mutable_cf_options_.prefix_extractor.get(),
|
||||
&get_context, mutable_cf_options_.prefix_extractor,
|
||||
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
||||
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
|
||||
fp.IsHitFileLastInLevel()),
|
||||
@@ -2196,7 +2197,7 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
StopWatchNano timer(clock_, timer_enabled /* auto_start */);
|
||||
s = table_cache_->MultiGet(
|
||||
read_options, *internal_comparator(), *f->file_metadata, &file_range,
|
||||
mutable_cf_options_.prefix_extractor.get(),
|
||||
mutable_cf_options_.prefix_extractor,
|
||||
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
||||
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
|
||||
fp.IsHitFileLastInLevel()),
|
||||
@@ -4158,9 +4159,16 @@ Status VersionSet::ProcessManifestWrites(
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_ptrs;
|
||||
std::vector<std::unique_ptr<BaseReferencedVersionBuilder>> builder_guards;
|
||||
|
||||
// Tracking `max_last_sequence` is needed to ensure we write
|
||||
// `VersionEdit::last_sequence_`s in non-decreasing order according to the
|
||||
// recovery code's requirement. It also allows us to defer updating
|
||||
// `descriptor_last_sequence_` until the apply phase, after the log phase
|
||||
// succeeds.
|
||||
SequenceNumber max_last_sequence = descriptor_last_sequence_;
|
||||
|
||||
if (first_writer.edit_list.front()->IsColumnFamilyManipulation()) {
|
||||
// No group commits for column family add or drop
|
||||
LogAndApplyCFHelper(first_writer.edit_list.front());
|
||||
LogAndApplyCFHelper(first_writer.edit_list.front(), &max_last_sequence);
|
||||
batch_edits.push_back(first_writer.edit_list.front());
|
||||
} else {
|
||||
auto it = manifest_writers_.cbegin();
|
||||
@@ -4248,7 +4256,8 @@ Status VersionSet::ProcessManifestWrites(
|
||||
} else if (group_start != std::numeric_limits<size_t>::max()) {
|
||||
group_start = std::numeric_limits<size_t>::max();
|
||||
}
|
||||
Status s = LogAndApplyHelper(last_writer->cfd, builder, e, mu);
|
||||
Status s = LogAndApplyHelper(last_writer->cfd, builder, e,
|
||||
&max_last_sequence, mu);
|
||||
if (!s.ok()) {
|
||||
// free up the allocated memory
|
||||
for (auto v : versions) {
|
||||
@@ -4366,7 +4375,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
cfd->internal_stats(), 1 /* max_threads */,
|
||||
true /* prefetch_index_and_filter_in_cache */,
|
||||
false /* is_initial_load */,
|
||||
mutable_cf_options_ptrs[i]->prefix_extractor.get(),
|
||||
mutable_cf_options_ptrs[i]->prefix_extractor,
|
||||
MaxFileSizeForL0MetaPin(*mutable_cf_options_ptrs[i]));
|
||||
if (!s.ok()) {
|
||||
if (db_options_->paranoid_checks) {
|
||||
@@ -4520,9 +4529,11 @@ Status VersionSet::ProcessManifestWrites(
|
||||
if (first_writer.edit_list.front()->is_column_family_add_) {
|
||||
assert(batch_edits.size() == 1);
|
||||
assert(new_cf_options != nullptr);
|
||||
assert(max_last_sequence == descriptor_last_sequence_);
|
||||
CreateColumnFamily(*new_cf_options, first_writer.edit_list.front());
|
||||
} else if (first_writer.edit_list.front()->is_column_family_drop_) {
|
||||
assert(batch_edits.size() == 1);
|
||||
assert(max_last_sequence == descriptor_last_sequence_);
|
||||
first_writer.cfd->SetDropped();
|
||||
first_writer.cfd->UnrefAndTryDelete();
|
||||
} else {
|
||||
@@ -4562,6 +4573,8 @@ Status VersionSet::ProcessManifestWrites(
|
||||
AppendVersion(cfd, versions[i]);
|
||||
}
|
||||
}
|
||||
assert(max_last_sequence >= descriptor_last_sequence_);
|
||||
descriptor_last_sequence_ = max_last_sequence;
|
||||
manifest_file_number_ = pending_manifest_file_number_;
|
||||
manifest_file_size_ = new_manifest_file_size;
|
||||
prev_log_number_ = first_writer.edit_list.front()->prev_log_number_;
|
||||
@@ -4628,6 +4641,23 @@ Status VersionSet::ProcessManifestWrites(
|
||||
|
||||
pending_manifest_file_number_ = 0;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// This is here kind of awkwardly because there's no other consistency
|
||||
// checks on `VersionSet`'s updates for the new `Version`s. We might want
|
||||
// to move it to a dedicated function, or remove it if we gain enough
|
||||
// confidence in `descriptor_last_sequence_`.
|
||||
if (s.ok()) {
|
||||
for (const auto* v : versions) {
|
||||
const auto* vstorage = v->storage_info();
|
||||
for (int level = 0; level < vstorage->num_levels(); ++level) {
|
||||
for (const auto& file : vstorage->LevelFiles(level)) {
|
||||
assert(file->fd.largest_seqno <= descriptor_last_sequence_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
// wake up all the waiting writers
|
||||
while (true) {
|
||||
ManifestWriter* ready = manifest_writers_.front();
|
||||
@@ -4751,16 +4781,13 @@ Status VersionSet::LogAndApply(
|
||||
new_cf_options);
|
||||
}
|
||||
|
||||
void VersionSet::LogAndApplyCFHelper(VersionEdit* edit) {
|
||||
void VersionSet::LogAndApplyCFHelper(VersionEdit* edit,
|
||||
SequenceNumber* max_last_sequence) {
|
||||
assert(max_last_sequence != nullptr);
|
||||
assert(edit->IsColumnFamilyManipulation());
|
||||
edit->SetNextFile(next_file_number_.load());
|
||||
// The log might have data that is not visible to memtbale and hence have not
|
||||
// updated the last_sequence_ yet. It is also possible that the log has is
|
||||
// expecting some new data that is not written yet. Since LastSequence is an
|
||||
// upper bound on the sequence, it is ok to record
|
||||
// last_allocated_sequence_ as the last sequence.
|
||||
edit->SetLastSequence(db_options_->two_write_queues ? last_allocated_sequence_
|
||||
: last_sequence_);
|
||||
assert(!edit->HasLastSequence());
|
||||
edit->SetLastSequence(*max_last_sequence);
|
||||
if (edit->is_column_family_drop_) {
|
||||
// if we drop column family, we have to make sure to save max column family,
|
||||
// so that we don't reuse existing ID
|
||||
@@ -4770,12 +4797,14 @@ void VersionSet::LogAndApplyCFHelper(VersionEdit* edit) {
|
||||
|
||||
Status VersionSet::LogAndApplyHelper(ColumnFamilyData* cfd,
|
||||
VersionBuilder* builder, VersionEdit* edit,
|
||||
SequenceNumber* max_last_sequence,
|
||||
InstrumentedMutex* mu) {
|
||||
#ifdef NDEBUG
|
||||
(void)cfd;
|
||||
#endif
|
||||
mu->AssertHeld();
|
||||
assert(!edit->IsColumnFamilyManipulation());
|
||||
assert(max_last_sequence != nullptr);
|
||||
|
||||
if (edit->has_log_number_) {
|
||||
assert(edit->log_number_ >= cfd->GetLogNumber());
|
||||
@@ -4786,13 +4815,11 @@ Status VersionSet::LogAndApplyHelper(ColumnFamilyData* cfd,
|
||||
edit->SetPrevLogNumber(prev_log_number_);
|
||||
}
|
||||
edit->SetNextFile(next_file_number_.load());
|
||||
// The log might have data that is not visible to memtbale and hence have not
|
||||
// updated the last_sequence_ yet. It is also possible that the log has is
|
||||
// expecting some new data that is not written yet. Since LastSequence is an
|
||||
// upper bound on the sequence, it is ok to record
|
||||
// last_allocated_sequence_ as the last sequence.
|
||||
edit->SetLastSequence(db_options_->two_write_queues ? last_allocated_sequence_
|
||||
: last_sequence_);
|
||||
if (edit->HasLastSequence() && edit->GetLastSequence() > *max_last_sequence) {
|
||||
*max_last_sequence = edit->GetLastSequence();
|
||||
} else {
|
||||
edit->SetLastSequence(*max_last_sequence);
|
||||
}
|
||||
|
||||
// The builder can be nullptr only if edit is WAL manipulation,
|
||||
// because WAL edits do not need to be applied to versions,
|
||||
@@ -5431,6 +5458,9 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
if (!full_history_ts_low.empty()) {
|
||||
edit.SetFullHistoryTsLow(full_history_ts_low);
|
||||
}
|
||||
|
||||
edit.SetLastSequence(descriptor_last_sequence_);
|
||||
|
||||
std::string record;
|
||||
if (!edit.EncodeTo(&record)) {
|
||||
return Status::Corruption(
|
||||
@@ -5601,7 +5631,7 @@ uint64_t VersionSet::ApproximateOffsetOf(Version* v, const FdWithKeyRange& f,
|
||||
if (table_cache != nullptr) {
|
||||
result = table_cache->ApproximateOffsetOf(
|
||||
key, f.file_metadata->fd, caller, icmp,
|
||||
v->GetMutableCFOptions().prefix_extractor.get());
|
||||
v->GetMutableCFOptions().prefix_extractor);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -5641,7 +5671,7 @@ uint64_t VersionSet::ApproximateSize(Version* v, const FdWithKeyRange& f,
|
||||
}
|
||||
return table_cache->ApproximateSize(
|
||||
start, end, f.file_metadata->fd, caller, icmp,
|
||||
v->GetMutableCFOptions().prefix_extractor.get());
|
||||
v->GetMutableCFOptions().prefix_extractor);
|
||||
}
|
||||
|
||||
void VersionSet::AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
@@ -5733,7 +5763,7 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
list[num++] = cfd->table_cache()->NewIterator(
|
||||
read_options, file_options_compactions,
|
||||
cfd->internal_comparator(), *flevel->files[i].file_metadata,
|
||||
range_del_agg, c->mutable_cf_options()->prefix_extractor.get(),
|
||||
range_del_agg, c->mutable_cf_options()->prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr,
|
||||
/*file_read_hist=*/nullptr, TableReaderCaller::kCompaction,
|
||||
/*arena=*/nullptr,
|
||||
@@ -5749,7 +5779,7 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
list[num++] = new LevelIterator(
|
||||
cfd->table_cache(), read_options, file_options_compactions,
|
||||
cfd->internal_comparator(), c->input_levels(which),
|
||||
c->mutable_cf_options()->prefix_extractor.get(),
|
||||
c->mutable_cf_options()->prefix_extractor,
|
||||
/*should_sample=*/false,
|
||||
/*no per level latency histogram=*/nullptr,
|
||||
TableReaderCaller::kCompaction, /*skip_filters=*/false,
|
||||
|
||||
+7
-7
@@ -851,11 +851,6 @@ class Version {
|
||||
// This accumulated stats will be used in compaction.
|
||||
void UpdateAccumulatedStats(bool update_stats);
|
||||
|
||||
// Sort all files for this version based on their file size and
|
||||
// record results in files_by_compaction_pri_. The largest files are listed
|
||||
// first.
|
||||
void UpdateFilesByCompactionPri();
|
||||
|
||||
ColumnFamilyData* cfd_; // ColumnFamilyData to which this Version belongs
|
||||
Logger* info_log_;
|
||||
Statistics* db_statistics_;
|
||||
@@ -1386,6 +1381,9 @@ class VersionSet {
|
||||
// the memtable but when using two write queues it could also indicate the
|
||||
// last sequence in the WAL visible to reads.
|
||||
std::atomic<uint64_t> last_sequence_;
|
||||
// The last sequence number of data committed to the descriptor (manifest
|
||||
// file).
|
||||
SequenceNumber descriptor_last_sequence_ = 0;
|
||||
// The last seq that is already allocated. It is applicable only when we have
|
||||
// two write queues. In that case seq might or might not have appreated in
|
||||
// memtable but it is expected to appear in the WAL.
|
||||
@@ -1434,9 +1432,11 @@ class VersionSet {
|
||||
bool new_descriptor_log,
|
||||
const ColumnFamilyOptions* new_cf_options);
|
||||
|
||||
void LogAndApplyCFHelper(VersionEdit* edit);
|
||||
void LogAndApplyCFHelper(VersionEdit* edit,
|
||||
SequenceNumber* max_last_sequence);
|
||||
Status LogAndApplyHelper(ColumnFamilyData* cfd, VersionBuilder* b,
|
||||
VersionEdit* edit, InstrumentedMutex* mu);
|
||||
VersionEdit* edit, SequenceNumber* max_last_sequence,
|
||||
InstrumentedMutex* mu);
|
||||
};
|
||||
|
||||
// ReactiveVersionSet represents a collection of versions of the column
|
||||
|
||||
@@ -2629,6 +2629,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromIncompleteManifest0) {
|
||||
if (iter == cf_names.end()) {
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
} else {
|
||||
ASSERT_NE(s.ToString().find(manifest_path), std::string::npos);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
}
|
||||
@@ -2670,6 +2671,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromIncompleteManifest1) {
|
||||
if (iter == cf_names.end()) {
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
} else {
|
||||
ASSERT_NE(s.ToString().find(manifest_path), std::string::npos);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
}
|
||||
@@ -2716,6 +2718,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromInCompleteManifest2) {
|
||||
if (iter == cf_names.end()) {
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
} else {
|
||||
ASSERT_NE(s.ToString().find(manifest_path), std::string::npos);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
}
|
||||
@@ -2773,6 +2776,7 @@ TEST_P(VersionSetTestEmptyDb, OpenManifestWithUnknownCF) {
|
||||
if (iter == cf_names.end()) {
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
} else {
|
||||
ASSERT_NE(s.ToString().find(manifest_path), std::string::npos);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,8 @@ class BatchedOpsStressTest : public StressTest {
|
||||
for (size_t i = 1; i < num_prefixes; i++) {
|
||||
if (values[i] != values[0]) {
|
||||
fprintf(stderr, "error : inconsistent values for key %s: %s, %s\n",
|
||||
key_str[i].c_str(), StringToHex(values[0].ToString()).c_str(),
|
||||
StringToHex(key_str[i]).c_str(),
|
||||
StringToHex(values[0].ToString()).c_str(),
|
||||
StringToHex(values[i].ToString()).c_str());
|
||||
// we continue after error rather than exiting so that we can
|
||||
// find more errors if any
|
||||
|
||||
@@ -68,24 +68,31 @@ bool RunStressTest(StressTest* stress) {
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t n = shared.GetNumThreads();
|
||||
|
||||
uint32_t n = FLAGS_threads;
|
||||
uint64_t now = clock->NowMicros();
|
||||
fprintf(stdout, "%s Initializing worker threads\n",
|
||||
clock->TimeToString(now / 1000000).c_str());
|
||||
std::vector<ThreadState*> threads(n);
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
threads[i] = new ThreadState(i, &shared);
|
||||
db_stress_env->StartThread(ThreadBody, threads[i]);
|
||||
}
|
||||
|
||||
ThreadState bg_thread(0, &shared);
|
||||
if (FLAGS_compaction_thread_pool_adjust_interval > 0) {
|
||||
db_stress_env->StartThread(PoolSizeChangeThread, &bg_thread);
|
||||
}
|
||||
ThreadState continuous_verification_thread(0, &shared);
|
||||
if (FLAGS_continuous_verification_interval > 0) {
|
||||
db_stress_env->StartThread(DbVerificationThread,
|
||||
&continuous_verification_thread);
|
||||
std::vector<ThreadState*> threads(n);
|
||||
{
|
||||
MutexLock l(shared.GetMutex());
|
||||
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
shared.IncThreads();
|
||||
threads[i] = new ThreadState(i, &shared);
|
||||
db_stress_env->StartThread(ThreadBody, threads[i]);
|
||||
}
|
||||
if (FLAGS_compaction_thread_pool_adjust_interval > 0) {
|
||||
shared.IncBgThreads();
|
||||
db_stress_env->StartThread(PoolSizeChangeThread, &bg_thread);
|
||||
}
|
||||
if (FLAGS_continuous_verification_interval > 0) {
|
||||
shared.IncBgThreads();
|
||||
db_stress_env->StartThread(DbVerificationThread,
|
||||
&continuous_verification_thread);
|
||||
}
|
||||
}
|
||||
|
||||
// Each thread goes through the following states:
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DbStressEnvWrapper : public EnvWrapper {
|
||||
public:
|
||||
explicit DbStressEnvWrapper(Env* t) : EnvWrapper(t) {}
|
||||
static const char* kClassName() { return "DbStressEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
Status DeleteFile(const std::string& f) override {
|
||||
// We determine whether it is a manifest file by searching a strong,
|
||||
|
||||
@@ -68,7 +68,7 @@ class SharedState {
|
||||
seed_(static_cast<uint32_t>(FLAGS_seed)),
|
||||
max_key_(FLAGS_max_key),
|
||||
log2_keys_per_lock_(static_cast<uint32_t>(FLAGS_log2_keys_per_lock)),
|
||||
num_threads_(FLAGS_threads),
|
||||
num_threads_(0),
|
||||
num_initialized_(0),
|
||||
num_populated_(0),
|
||||
vote_reopen_(0),
|
||||
@@ -163,14 +163,6 @@ class SharedState {
|
||||
ptr.reset(new port::Mutex);
|
||||
}
|
||||
}
|
||||
if (FLAGS_compaction_thread_pool_adjust_interval > 0) {
|
||||
++num_bg_threads_;
|
||||
fprintf(stdout, "Starting compaction_thread_pool_adjust_thread\n");
|
||||
}
|
||||
if (FLAGS_continuous_verification_interval > 0) {
|
||||
++num_bg_threads_;
|
||||
fprintf(stdout, "Starting continuous_verification_thread\n");
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
if (FLAGS_read_fault_one_in) {
|
||||
SyncPoint::GetInstance()->SetCallBack("FaultInjectionIgnoreError",
|
||||
@@ -199,6 +191,8 @@ class SharedState {
|
||||
|
||||
uint32_t GetNumThreads() const { return num_threads_; }
|
||||
|
||||
void IncThreads() { num_threads_++; }
|
||||
|
||||
void IncInitialized() { num_initialized_++; }
|
||||
|
||||
void IncOperated() { num_populated_++; }
|
||||
@@ -317,6 +311,8 @@ class SharedState {
|
||||
|
||||
bool ShouldStopBgThread() { return should_stop_bg_thread_; }
|
||||
|
||||
void IncBgThreads() { ++num_bg_threads_; }
|
||||
|
||||
void IncBgThreadsFinished() { ++bg_thread_finished_; }
|
||||
|
||||
bool BgThreadsFinished() const {
|
||||
@@ -347,7 +343,7 @@ class SharedState {
|
||||
const uint32_t seed_;
|
||||
const int64_t max_key_;
|
||||
const uint32_t log2_keys_per_lock_;
|
||||
const int num_threads_;
|
||||
int num_threads_;
|
||||
long num_initialized_;
|
||||
long num_populated_;
|
||||
long vote_reopen_;
|
||||
|
||||
@@ -298,17 +298,8 @@ void StressTest::FinishInitDb(SharedState* shared) {
|
||||
clock_->TimeToString(now / 1000000).c_str(), FLAGS_max_key);
|
||||
PreloadDbAndReopenAsReadOnly(FLAGS_max_key, shared);
|
||||
}
|
||||
if (FLAGS_enable_compaction_filter) {
|
||||
auto* compaction_filter_factory =
|
||||
reinterpret_cast<DbStressCompactionFilterFactory*>(
|
||||
options_.compaction_filter_factory.get());
|
||||
assert(compaction_filter_factory);
|
||||
compaction_filter_factory->SetSharedState(shared);
|
||||
fprintf(stdout, "Compaction filter factory: %s\n",
|
||||
compaction_filter_factory->Name());
|
||||
}
|
||||
|
||||
if (shared->HasHistory() && IsStateTracked()) {
|
||||
if (shared->HasHistory()) {
|
||||
// The way it works right now is, if there's any history, that means the
|
||||
// previous run mutating the DB had all its operations traced, in which case
|
||||
// we should always be able to `Restore()` the expected values to match the
|
||||
@@ -321,7 +312,7 @@ void StressTest::FinishInitDb(SharedState* shared) {
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAGS_sync_fault_injection && IsStateTracked()) {
|
||||
if ((FLAGS_sync_fault_injection || FLAGS_disable_wal) && IsStateTracked()) {
|
||||
Status s = shared->SaveAtAndAfter(db_);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Error enabling history tracing: %s\n",
|
||||
@@ -329,6 +320,19 @@ void StressTest::FinishInitDb(SharedState* shared) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (FLAGS_enable_compaction_filter) {
|
||||
auto* compaction_filter_factory =
|
||||
reinterpret_cast<DbStressCompactionFilterFactory*>(
|
||||
options_.compaction_filter_factory.get());
|
||||
assert(compaction_filter_factory);
|
||||
// This must be called only after any potential `SharedState::Restore()` has
|
||||
// completed in order for the `compaction_filter_factory` to operate on the
|
||||
// correct latest values file.
|
||||
compaction_filter_factory->SetSharedState(shared);
|
||||
fprintf(stdout, "Compaction filter factory: %s\n",
|
||||
compaction_filter_factory->Name());
|
||||
}
|
||||
}
|
||||
|
||||
bool StressTest::VerifySecondaries() {
|
||||
@@ -1445,7 +1449,20 @@ Status StressTest::TestBackupRestore(
|
||||
test_opts.file_sizes = thread->rand.OneIn(2) == 0;
|
||||
TEST_EnableWriteFutureSchemaVersion2(backup_engine, test_opts);
|
||||
}
|
||||
s = backup_engine->CreateNewBackup(db_);
|
||||
CreateBackupOptions create_opts;
|
||||
if (FLAGS_disable_wal) {
|
||||
// The verification can only work when latest value of `key` is backed up,
|
||||
// which requires flushing in case of WAL disabled.
|
||||
//
|
||||
// Note this triggers a flush with a key lock held. Meanwhile, operations
|
||||
// like flush/compaction may attempt to grab key locks like in
|
||||
// `DbStressCompactionFilter`. The philosophy around preventing deadlock
|
||||
// is the background operation key lock acquisition only tries but does
|
||||
// not wait for the lock. So here in the foreground it is OK to hold the
|
||||
// lock and wait on a background operation (flush).
|
||||
create_opts.flush_before_backup = true;
|
||||
}
|
||||
s = backup_engine->CreateNewBackup(create_opts, db_);
|
||||
if (!s.ok()) {
|
||||
from = "BackupEngine::CreateNewBackup";
|
||||
}
|
||||
@@ -1898,6 +1915,9 @@ void StressTest::TestCompactFiles(ThreadState* thread,
|
||||
|
||||
Status StressTest::TestFlush(const std::vector<int>& rand_column_families) {
|
||||
FlushOptions flush_opts;
|
||||
if (FLAGS_atomic_flush) {
|
||||
return db_->Flush(flush_opts, column_families_);
|
||||
}
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
std::for_each(rand_column_families.begin(), rand_column_families.end(),
|
||||
[this, &cfhs](int k) { cfhs.push_back(column_families_[k]); });
|
||||
@@ -2823,7 +2843,7 @@ void StressTest::Reopen(ThreadState* thread) {
|
||||
clock_->TimeToString(now / 1000000).c_str(), num_times_reopened_);
|
||||
Open();
|
||||
|
||||
if (FLAGS_sync_fault_injection && IsStateTracked()) {
|
||||
if ((FLAGS_sync_fault_injection || FLAGS_disable_wal) && IsStateTracked()) {
|
||||
Status s = thread->shared->SaveAtAndAfter(db_);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Error enabling history tracing: %s\n",
|
||||
|
||||
@@ -296,7 +296,13 @@ Status FileExpectedStateManager::SaveAtAndAfter(DB* db) {
|
||||
&trace_writer);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = db->StartTrace(TraceOptions(), std::move(trace_writer));
|
||||
TraceOptions trace_opts;
|
||||
trace_opts.filter |= kTraceFilterGet;
|
||||
trace_opts.filter |= kTraceFilterMultiGet;
|
||||
trace_opts.filter |= kTraceFilterIteratorSeek;
|
||||
trace_opts.filter |= kTraceFilterIteratorSeekForPrev;
|
||||
trace_opts.preserve_write_order = true;
|
||||
s = db->StartTrace(trace_opts, std::move(trace_writer));
|
||||
}
|
||||
|
||||
// Delete old state/trace files. Deletion order does not matter since we only
|
||||
@@ -326,6 +332,8 @@ bool FileExpectedStateManager::HasHistory() {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
namespace {
|
||||
|
||||
// An `ExpectedStateTraceRecordHandler` applies a configurable number of
|
||||
// write operation trace records to the configured expected state. It is used in
|
||||
// `FileExpectedStateManager::Restore()` to sync the expected state with the
|
||||
@@ -336,13 +344,14 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
ExpectedStateTraceRecordHandler(uint64_t max_write_ops, ExpectedState* state)
|
||||
: max_write_ops_(max_write_ops), state_(state) {}
|
||||
|
||||
~ExpectedStateTraceRecordHandler() {
|
||||
assert(num_write_ops_ == max_write_ops_);
|
||||
}
|
||||
~ExpectedStateTraceRecordHandler() { assert(IsDone()); }
|
||||
|
||||
// True if we have already reached the limit on write operations to apply.
|
||||
bool IsDone() { return num_write_ops_ == max_write_ops_; }
|
||||
|
||||
Status Handle(const WriteQueryTraceRecord& record,
|
||||
std::unique_ptr<TraceRecordResult>* /* result */) override {
|
||||
if (num_write_ops_ == max_write_ops_) {
|
||||
if (IsDone()) {
|
||||
return Status::OK();
|
||||
}
|
||||
WriteBatch batch(record.GetWriteBatchRep().ToString());
|
||||
@@ -429,6 +438,8 @@ class ExpectedStateTraceRecordHandler : public TraceRecord::Handler,
|
||||
ExpectedState* state_;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
Status FileExpectedStateManager::Restore(DB* db) {
|
||||
assert(HasHistory());
|
||||
SequenceNumber seqno = db->GetLatestSequenceNumber();
|
||||
@@ -462,7 +473,7 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
{
|
||||
std::unique_ptr<Replayer> replayer;
|
||||
std::unique_ptr<ExpectedState> state;
|
||||
std::unique_ptr<TraceRecord::Handler> handler;
|
||||
std::unique_ptr<ExpectedStateTraceRecordHandler> handler;
|
||||
if (s.ok()) {
|
||||
state.reset(new FileExpectedState(latest_file_temp_path, max_key_,
|
||||
num_column_families_));
|
||||
@@ -490,6 +501,13 @@ Status FileExpectedStateManager::Restore(DB* db) {
|
||||
std::unique_ptr<TraceRecordResult> res;
|
||||
record->Accept(handler.get(), &res);
|
||||
}
|
||||
if (s.IsCorruption() && handler->IsDone()) {
|
||||
// There could be a corruption reading the tail record of the trace due to
|
||||
// `db_stress` crashing while writing it. It shouldn't matter as long as
|
||||
// we already found all the write ops we need to catch up the expected
|
||||
// state.
|
||||
s = Status::OK();
|
||||
}
|
||||
if (s.IsIncomplete()) {
|
||||
// OK because `Status::Incomplete` is expected upon finishing all the
|
||||
// trace records.
|
||||
|
||||
@@ -20,7 +20,9 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// TODO: move these to gflags.
|
||||
static constexpr uint32_t kInitNumC = 1000;
|
||||
#ifndef ROCKSDB_LITE
|
||||
static constexpr uint32_t kInitialCARatio = 3;
|
||||
#endif // ROCKSDB_LITE
|
||||
static constexpr bool kDoPreload = true;
|
||||
|
||||
std::string MultiOpsTxnsStressTest::Record::EncodePrimaryKey(uint32_t a) {
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
---
|
||||
title: Ribbon Filter
|
||||
layout: post
|
||||
author: pdillinger
|
||||
category: blog
|
||||
---
|
||||
|
||||
## Summary
|
||||
Since version 6.15 last year, RocksDB supports Ribbon filters, a new
|
||||
alternative to Bloom filters that save space, especially memory, at
|
||||
the cost of more CPU usage, mostly in constructing the filters in the
|
||||
background. Most applications with long-lived data (many hours or
|
||||
longer) will likely benefit from adopting a Ribbon+Bloom hybrid filter
|
||||
policy. Here we explain why and how.
|
||||
|
||||
[Ribbon filter on RocksDB wiki](https://github.com/facebook/rocksdb/wiki/RocksDB-Bloom-Filter#ribbon-filter)
|
||||
|
||||
[Ribbon filter paper](https://arxiv.org/abs/2103.02515)
|
||||
|
||||
## Problem & background
|
||||
Bloom filters play a critical role in optimizing point queries and
|
||||
some range queries in LSM-tree storage systems like RocksDB. Very
|
||||
large DBs can use 10% or more of their RAM memory for (Bloom) filters,
|
||||
so that (average case) read performance can be very good despite high
|
||||
(worst case) read amplification, [which is useful for lowering write
|
||||
and/or space
|
||||
amplification](http://smalldatum.blogspot.com/2015/11/read-write-space-amplification-pick-2_23.html).
|
||||
Although the `format_version=5` Bloom filter in RocksDB is extremely
|
||||
fast, all Bloom filters use around 50% more space than is
|
||||
theoretically possible for a hashed structure configured for the same
|
||||
false positive (FP) rate and number of keys added. What would it take
|
||||
to save that significant share of “wasted” filter memory, and when
|
||||
does it make sense to use such a Bloom alternative?
|
||||
|
||||
A number of alternatives to Bloom filters were known, especially for
|
||||
static filters (not modified after construction), but all the
|
||||
previously known structures were unsatisfying for SSTs because of some
|
||||
combination of
|
||||
* Not enough space savings for CPU increase. For example, [Xor
|
||||
filters](https://arxiv.org/abs/1912.08258) use 3-4x more CPU than
|
||||
Bloom but only save 15-20% of
|
||||
space. [GOV](https://arxiv.org/pdf/1603.04330.pdf) can save around
|
||||
30% space but requires around 10x more CPU than Bloom.
|
||||
* Inconsistent space savings. [Cuckoo
|
||||
filters](https://www.cs.cmu.edu/~dga/papers/cuckoo-conext2014.pdf)
|
||||
and Xor+ filters offer significant space savings for very low FP
|
||||
rates (high bits per key) but little or no savings for higher FP
|
||||
rates (low bits per key). ([Higher FP rates are considered best for
|
||||
largest levels of
|
||||
LSM.](https://stratos.seas.harvard.edu/files/stratos/files/monkeykeyvaluestore.pdf))
|
||||
[Spatially-coupled Xor
|
||||
filters](https://arxiv.org/pdf/2001.10500.pdf) require very large
|
||||
number of keys per filter for large space savings.
|
||||
* Inflexible configuration. No published alternatives offered the same
|
||||
continuous configurability of Bloom filters, where any FP rate and
|
||||
any fractional bits per key could be chosen. This flexibility
|
||||
improves memory efficiency with the `optimize_filters_for_memory`
|
||||
option that minimizes internal fragmentation on filters.
|
||||
|
||||
## Ribbon filter development and implementation
|
||||
The Ribbon filter came about when I developed a faster, simpler, and
|
||||
more adaptable algorithm for constructing a little-known [Xor-based
|
||||
structure from Dietzfelbinger and
|
||||
Walzer](https://arxiv.org/pdf/1907.04750.pdf). It has very good space
|
||||
usage for required CPU time (~30% space savings for 3-4x CPU) and,
|
||||
with some engineering, Bloom-like configurability. The complications
|
||||
were managable for use in RocksDB:
|
||||
* Ribbon space efficiency does not naturally scale to very large
|
||||
number of keys in a single filter (whole SST file or partition), but
|
||||
with the current 128-bit Ribbon implementation in RocksDB, even 100
|
||||
million keys in one filter saves 27% space vs. Bloom rather than 30%
|
||||
for 100,000 keys in a filter.
|
||||
* More temporary memory is required during construction, ~230 bits per
|
||||
key for 128-bit Ribbon vs. ~75 bits per key for Bloom filter. A
|
||||
quick calculation shows that if you are saving 3 bits per key on the
|
||||
generated filter, you only need about 50 generated filters in memory
|
||||
to offset this temporary memory usage. (Thousands of filters in
|
||||
memory is typical.) Starting in RocksDB version 6.27, this temporary
|
||||
memory can be accounted for under block cache using
|
||||
`BlockBasedTableOptions::reserve_table_builder_memory`.
|
||||
* Ribbon filter queries use relatively more CPU for lower FP rates
|
||||
(but still O(1) relative to number of keys added to filter). This
|
||||
should be OK because lower FP rates are only appropriate when then
|
||||
cost of a false positive is very high (worth extra query time) or
|
||||
memory is not so constrained (can use Bloom instead).
|
||||
|
||||
Future: data in [the paper](https://arxiv.org/abs/2103.02515) suggests
|
||||
that 32-bit Balanced Ribbon (new name: [Bump-Once
|
||||
Ribbon](https://arxiv.org/pdf/2109.01892.pdf)) would improve all of
|
||||
these issues and be better all around (except for code complexity).
|
||||
|
||||
## Ribbon vs. Bloom in RocksDB configuration
|
||||
Different applications and hardware configurations have different
|
||||
constraints, but we can use hardware costs to examine and better
|
||||
understand the trade-off between Bloom and Ribbon.
|
||||
|
||||
### Same FP rate, RAM vs. CPU hardware cost
|
||||
Under ideal conditions where we can adjust our hardware to suit the
|
||||
application, in terms of dollars, how much does it cost to construct,
|
||||
query, and keep in memory a Bloom filter vs. a Ribbon filter? The
|
||||
Ribbon filter costs more for CPU but less for RAM. Importantly, the
|
||||
RAM cost directly depends on how long the filter is kept in memory,
|
||||
which in RocksDB is essentially the lifetime of the filter.
|
||||
(Temporary RAM during construction is so short-lived that it is
|
||||
ignored.) Using some consumer hardware and electricity prices and a
|
||||
predicted balance between construction and queries, we can compute a
|
||||
“break even” duration in memory. To minimize cost, filters with a
|
||||
lifetime shorter than this should be Bloom and filters with a lifetime
|
||||
longer than this should be Ribbon. (Python code)
|
||||
|
||||
```
|
||||
# Commodity prices based roughly on consumer prices and rough guesses
|
||||
# Upfront cost of a CPU per hardware thread
|
||||
upfront_dollars_per_cpu_thread = 30.0
|
||||
|
||||
# CPU average power usage per hardware thread
|
||||
watts_per_cpu_thread = 3.5
|
||||
|
||||
# Upfront cost of a GB of RAM
|
||||
upfront_dollars_per_gb_ram = 8.0
|
||||
|
||||
# RAM average power usage per GB
|
||||
# https://www.crucial.com/support/articles-faq-memory/how-much-power-does-memory-use
|
||||
watts_per_gb_ram = 0.375
|
||||
|
||||
# Estimated price of power per kilowatt-hour, including overheads like conversion losses and cooling
|
||||
dollars_per_kwh = 0.35
|
||||
|
||||
# Assume 3 year hardware lifetime
|
||||
hours_per_lifetime = 3 * 365 * 24
|
||||
seconds_per_lifetime = hours_per_lifetime * 60 * 60
|
||||
|
||||
# Number of filter queries per key added in filter construction is heavily dependent on workload.
|
||||
# When replication is in layer above RocksDB, it will be low, likely < 1. When replication is in
|
||||
# storage layer below RocksDB, it will likely be > 1. Using a rough and general guesstimate.
|
||||
key_query_per_construct = 1.0
|
||||
|
||||
#==================================
|
||||
# Bloom & Ribbon filter performance
|
||||
typical_bloom_bits_per_key = 10.0
|
||||
typical_ribbon_bits_per_key = 7.0
|
||||
|
||||
# Speeds here are sensitive to many variables, especially query speed because it
|
||||
# is so dependent on memory latency. Using this benchmark here:
|
||||
# for IMPL in 2 3; do
|
||||
# ./filter_bench -impl=$IMPL -quick -m_keys_total_max=200 -use_full_block_reader
|
||||
# done
|
||||
# and "Random filter" queries.
|
||||
nanoseconds_per_construct_bloom_key = 32.0
|
||||
nanoseconds_per_construct_ribbon_key = 140.0
|
||||
|
||||
nanoseconds_per_query_bloom_key = 500.0
|
||||
nanoseconds_per_query_ribbon_key = 600.0
|
||||
|
||||
#==================================
|
||||
# Some constants
|
||||
kwh_per_watt_lifetime = hours_per_lifetime / 1000.0
|
||||
bits_per_gb = 8 * 1024 * 1024 * 1024
|
||||
|
||||
#==================================
|
||||
# Crunching the numbers
|
||||
# on CPU for constructing filters
|
||||
dollars_per_cpu_thread_lifetime = upfront_dollars_per_cpu_thread + watts_per_cpu_thread * kwh_per_watt_lifetime * dollars_per_kwh
|
||||
dollars_per_cpu_thread_second = dollars_per_cpu_thread_lifetime / seconds_per_lifetime
|
||||
|
||||
dollars_per_construct_bloom_key = dollars_per_cpu_thread_second * nanoseconds_per_construct_bloom_key / 10**9
|
||||
dollars_per_construct_ribbon_key = dollars_per_cpu_thread_second * nanoseconds_per_construct_ribbon_key / 10**9
|
||||
|
||||
dollars_per_query_bloom_key = dollars_per_cpu_thread_second * nanoseconds_per_query_bloom_key / 10**9
|
||||
dollars_per_query_ribbon_key = dollars_per_cpu_thread_second * nanoseconds_per_query_ribbon_key / 10**9
|
||||
|
||||
dollars_per_bloom_key_cpu = dollars_per_construct_bloom_key + key_query_per_construct * dollars_per_query_bloom_key
|
||||
dollars_per_ribbon_key_cpu = dollars_per_construct_ribbon_key + key_query_per_construct * dollars_per_query_ribbon_key
|
||||
|
||||
# on holding filters in RAM
|
||||
dollars_per_gb_ram_lifetime = upfront_dollars_per_gb_ram + watts_per_gb_ram * kwh_per_watt_lifetime * dollars_per_kwh
|
||||
dollars_per_gb_ram_second = dollars_per_gb_ram_lifetime / seconds_per_lifetime
|
||||
|
||||
dollars_per_bloom_key_in_ram_second = dollars_per_gb_ram_second / bits_per_gb * typical_bloom_bits_per_key
|
||||
dollars_per_ribbon_key_in_ram_second = dollars_per_gb_ram_second / bits_per_gb * typical_ribbon_bits_per_key
|
||||
|
||||
#==================================
|
||||
# How many seconds does it take for the added cost of constructing a ribbon filter instead
|
||||
# of bloom to be offset by the added cost of holding the bloom filter in memory?
|
||||
break_even_seconds = (dollars_per_ribbon_key_cpu - dollars_per_bloom_key_cpu) / (dollars_per_bloom_key_in_ram_second - dollars_per_ribbon_key_in_ram_second)
|
||||
print(break_even_seconds)
|
||||
# -> 3235.1647730256936
|
||||
```
|
||||
|
||||
So roughly speaking, filters that live in memory for more than an hour
|
||||
should be Ribbon, and filters that live less than an hour should be
|
||||
Bloom. This is very interesting, but how long do filters live in
|
||||
RocksDB?
|
||||
|
||||
First let's consider the average case. Write-heavy RocksDB loads are
|
||||
often backed by flash storage, which has some specified write
|
||||
endurance for its intended lifetime. This can be expressed as *device
|
||||
writes per day* (DWPD), and supported DWPD is typically < 10.0 even
|
||||
for high end devices (excluding NVRAM). Roughly speaking, the DB would
|
||||
need to be writing at a rate of 20+ DWPD for data to have an average
|
||||
lifetime of less than one hour. Thus, unless you are prematurely
|
||||
burning out your flash or massively under-utilizing available storage,
|
||||
using the Ribbon filter has the better cost profile *on average*.
|
||||
|
||||
### Predictable lifetime
|
||||
But we can do even better than optimizing for the average case. LSM
|
||||
levels give us very strong data lifetime hints. Data in L0 might live
|
||||
for minutes or a small number of hours. Data in Lmax might live for
|
||||
days or weeks. So even if Ribbon filters weren't the best choice on
|
||||
average for a workload, they almost certainly make sense for the
|
||||
larger, longer-lived levels of the LSM. As of RocksDB 6.24, you can
|
||||
specify a minimum LSM level for Ribbon filters with
|
||||
`NewRibbonFilterPolicy`, and earlier levels will use Bloom filters.
|
||||
|
||||
### Resident filter memory
|
||||
The above analysis assumes that nearly all filters for all live SST
|
||||
files are resident in memory. This is true if using
|
||||
`cache_index_and_filter_blocks=0` and `max_open_files=-1` (defaults),
|
||||
but `cache_index_and_filter_blocks=1` is popular. In that case,
|
||||
if you use `optimize_filters_for_hits=1` and non-partitioned filters
|
||||
(a popular MyRocks configuration), it is also likely that nearly all
|
||||
live filters are in memory. However, if you don't use
|
||||
`optimize_filters_for_hits` and use partitioned filters, then
|
||||
cold data (by age or by key range) can lead to only a portion of
|
||||
filters being resident in memory. In that case, benefit from Ribbon
|
||||
filter is not as clear, though because Ribbon filters are smaller,
|
||||
they are more efficient to read into memory.
|
||||
|
||||
RocksDB version 6.21 and later include a rough feature to determine
|
||||
block cache usage for data blocks, filter blocks, index blocks, etc.
|
||||
Data like this is periodically dumped to LOG file
|
||||
(`stats_dump_period_sec`):
|
||||
|
||||
```
|
||||
Block cache entry stats(count,size,portion): DataBlock(441761,6.82 GB,75.765%) FilterBlock(3002,1.27 GB,14.1387%) IndexBlock(17777,887.75 MB,9.63267%) Misc(1,0.00 KB,0%)
|
||||
Block cache LRUCache@0x7fdd08104290#7004432 capacity: 9.00 GB collections: 2573 last_copies: 10 last_secs: 0.143248 secs_since: 0
|
||||
```
|
||||
|
||||
This indicates that at this moment in time, the block cache object
|
||||
identified by `LRUCache@0x7fdd08104290#7004432` (potentially used
|
||||
by multiple DBs) uses roughly 14% of its 9GB, about 1.27 GB, on filter
|
||||
blocks. This same data is available through `DB::GetMapProperty` with
|
||||
`DB::Properties::kBlockCacheEntryStats`, and (with some effort) can
|
||||
be compared to total size of all filters (not necessarily in memory)
|
||||
using `rocksdb.filter.size` from
|
||||
`DB::Properties::kAggregatedTableProperties`.
|
||||
|
||||
### Sanity checking lifetime
|
||||
Can we be sure that using filters even makes sense for such long-lived
|
||||
data? We can apply [the current 5 minute rule for caching SSD data in
|
||||
RAM](http://renata.borovica-gajic.com/data/adms2017_5minuterule.pdf). A
|
||||
4KB filter page holds data for roughly 4K keys. If we assume at least
|
||||
one negative (useful) filter query in its lifetime per added key, it
|
||||
can satisfy the 5 minute rule with a lifetime of up to about two
|
||||
weeks. Thus, the lifetime threshold for “no filter” is about 300x
|
||||
higher than the lifetime threshold for Ribbon filter.
|
||||
|
||||
### What to do with saved memory
|
||||
The default way to improve overall RocksDB performance with more
|
||||
available memory is to use more space for caching, which improves
|
||||
latency, CPU load, read IOs, etc. With
|
||||
`cache_index_and_filter_blocks=1`, savings in filters will
|
||||
automatically make room for caching more data blocks in block
|
||||
cache. With `cache_index_and_filter_blocks=0`, consider increasing
|
||||
block cache size.
|
||||
|
||||
Using the space savings to lower filter FP rates is also an option,
|
||||
but there is less evidence for this commonly improving existing
|
||||
*optimized* configurations.
|
||||
|
||||
## Generic recommendation
|
||||
If using `NewBloomFilterPolicy(bpk)` for a large persistent DB using
|
||||
compression, try using `NewRibbonFilterPolicy(bpk)` instead, which
|
||||
will generate Ribbon filters during compaction and Bloom filters
|
||||
for flush, both with the same FP rate as the old setting. Once new SST
|
||||
files are generated under the new policy, this should free up some
|
||||
memory for more caching without much effect on burst or sustained
|
||||
write speed. Both kinds of filters can be read under either policy, so
|
||||
there's always an option to adjust settings or gracefully roll back to
|
||||
using Bloom filter only (keeping in mind that SST files must be
|
||||
replaced to see effect of that change).
|
||||
Vendored
+81
@@ -4,6 +4,7 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "rocksdb/utilities/options_type.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
@@ -380,4 +381,84 @@ Status CompositeEnv::NewDirectory(const std::string& name,
|
||||
return status;
|
||||
}
|
||||
|
||||
namespace {
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
composite_env_wrapper_type_info = {
|
||||
#ifndef ROCKSDB_LITE
|
||||
{"target",
|
||||
{0, OptionType::kCustomizable, OptionVerificationType::kByName,
|
||||
OptionTypeFlags::kDontSerialize | OptionTypeFlags::kRawPointer,
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const std::string& value, void* addr) {
|
||||
auto target = static_cast<EnvWrapper::Target*>(addr);
|
||||
return Env::CreateFromString(opts, value, &(target->env),
|
||||
&(target->guard));
|
||||
},
|
||||
nullptr, nullptr}},
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
composite_fs_wrapper_type_info = {
|
||||
#ifndef ROCKSDB_LITE
|
||||
{"file_system",
|
||||
OptionTypeInfo::AsCustomSharedPtr<FileSystem>(
|
||||
0, OptionVerificationType::kByName, OptionTypeFlags::kNone)},
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
composite_clock_wrapper_type_info = {
|
||||
#ifndef ROCKSDB_LITE
|
||||
{"clock",
|
||||
OptionTypeInfo::AsCustomSharedPtr<SystemClock>(
|
||||
0, OptionVerificationType::kByName, OptionTypeFlags::kNone)},
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Env> NewCompositeEnv(const std::shared_ptr<FileSystem>& fs) {
|
||||
return std::unique_ptr<Env>(new CompositeEnvWrapper(Env::Default(), fs));
|
||||
}
|
||||
|
||||
CompositeEnvWrapper::CompositeEnvWrapper(Env* env,
|
||||
const std::shared_ptr<FileSystem>& fs,
|
||||
const std::shared_ptr<SystemClock>& sc)
|
||||
: CompositeEnv(fs, sc), target_(env) {
|
||||
RegisterOptions("", &target_, &composite_env_wrapper_type_info);
|
||||
RegisterOptions("", &file_system_, &composite_fs_wrapper_type_info);
|
||||
RegisterOptions("", &system_clock_, &composite_clock_wrapper_type_info);
|
||||
}
|
||||
|
||||
CompositeEnvWrapper::CompositeEnvWrapper(const std::shared_ptr<Env>& env,
|
||||
const std::shared_ptr<FileSystem>& fs,
|
||||
const std::shared_ptr<SystemClock>& sc)
|
||||
: CompositeEnv(fs, sc), target_(env) {
|
||||
RegisterOptions("", &target_, &composite_env_wrapper_type_info);
|
||||
RegisterOptions("", &file_system_, &composite_fs_wrapper_type_info);
|
||||
RegisterOptions("", &system_clock_, &composite_clock_wrapper_type_info);
|
||||
}
|
||||
|
||||
Status CompositeEnvWrapper::PrepareOptions(const ConfigOptions& options) {
|
||||
target_.Prepare();
|
||||
if (file_system_ == nullptr) {
|
||||
file_system_ = target_.env->GetFileSystem();
|
||||
}
|
||||
if (system_clock_ == nullptr) {
|
||||
system_clock_ = target_.env->GetSystemClock();
|
||||
}
|
||||
return Env::PrepareOptions(options);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string CompositeEnvWrapper::SerializeOptions(
|
||||
const ConfigOptions& config_options, const std::string& header) const {
|
||||
auto options = CompositeEnv::SerializeOptions(config_options, header);
|
||||
if (target_.env != nullptr && target_.env != Env::Default()) {
|
||||
options.append("target=");
|
||||
options.append(target_.env->ToString(config_options));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+51
-21
@@ -254,6 +254,8 @@ class CompositeEnvWrapper : public CompositeEnv {
|
||||
public:
|
||||
// Initialize a CompositeEnvWrapper that delegates all thread/time related
|
||||
// calls to env, and all file operations to fs
|
||||
explicit CompositeEnvWrapper(Env* env)
|
||||
: CompositeEnvWrapper(env, env->GetFileSystem(), env->GetSystemClock()) {}
|
||||
explicit CompositeEnvWrapper(Env* env, const std::shared_ptr<FileSystem>& fs)
|
||||
: CompositeEnvWrapper(env, fs, env->GetSystemClock()) {}
|
||||
|
||||
@@ -261,82 +263,110 @@ class CompositeEnvWrapper : public CompositeEnv {
|
||||
: CompositeEnvWrapper(env, env->GetFileSystem(), sc) {}
|
||||
|
||||
explicit CompositeEnvWrapper(Env* env, const std::shared_ptr<FileSystem>& fs,
|
||||
const std::shared_ptr<SystemClock>& sc);
|
||||
|
||||
explicit CompositeEnvWrapper(const std::shared_ptr<Env>& env,
|
||||
const std::shared_ptr<FileSystem>& fs)
|
||||
: CompositeEnvWrapper(env, fs, env->GetSystemClock()) {}
|
||||
|
||||
explicit CompositeEnvWrapper(const std::shared_ptr<Env>& env,
|
||||
const std::shared_ptr<SystemClock>& sc)
|
||||
: CompositeEnv(fs, sc), env_target_(env) {}
|
||||
: CompositeEnvWrapper(env, env->GetFileSystem(), sc) {}
|
||||
|
||||
explicit CompositeEnvWrapper(const std::shared_ptr<Env>& env,
|
||||
const std::shared_ptr<FileSystem>& fs,
|
||||
const std::shared_ptr<SystemClock>& sc);
|
||||
|
||||
static const char* kClassName() { return "CompositeEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
bool IsInstanceOf(const std::string& name) const override {
|
||||
if (name == kClassName()) {
|
||||
return true;
|
||||
} else {
|
||||
return CompositeEnv::IsInstanceOf(name);
|
||||
}
|
||||
}
|
||||
const Customizable* Inner() const override { return target_.env; }
|
||||
|
||||
Status PrepareOptions(const ConfigOptions& options) override;
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string SerializeOptions(const ConfigOptions& config_options,
|
||||
const std::string& header) const override;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Return the target to which this Env forwards all calls
|
||||
Env* env_target() const { return env_target_; }
|
||||
Env* env_target() const { return target_.env; }
|
||||
|
||||
#if !defined(OS_WIN) && !defined(ROCKSDB_NO_DYNAMIC_EXTENSION)
|
||||
Status LoadLibrary(const std::string& lib_name,
|
||||
const std::string& search_path,
|
||||
std::shared_ptr<DynamicLibrary>* result) override {
|
||||
return env_target_->LoadLibrary(lib_name, search_path, result);
|
||||
return target_.env->LoadLibrary(lib_name, search_path, result);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Schedule(void (*f)(void* arg), void* a, Priority pri,
|
||||
void* tag = nullptr, void (*u)(void* arg) = nullptr) override {
|
||||
return env_target_->Schedule(f, a, pri, tag, u);
|
||||
return target_.env->Schedule(f, a, pri, tag, u);
|
||||
}
|
||||
|
||||
int UnSchedule(void* tag, Priority pri) override {
|
||||
return env_target_->UnSchedule(tag, pri);
|
||||
return target_.env->UnSchedule(tag, pri);
|
||||
}
|
||||
|
||||
void StartThread(void (*f)(void*), void* a) override {
|
||||
return env_target_->StartThread(f, a);
|
||||
return target_.env->StartThread(f, a);
|
||||
}
|
||||
void WaitForJoin() override { return env_target_->WaitForJoin(); }
|
||||
void WaitForJoin() override { return target_.env->WaitForJoin(); }
|
||||
unsigned int GetThreadPoolQueueLen(Priority pri = LOW) const override {
|
||||
return env_target_->GetThreadPoolQueueLen(pri);
|
||||
return target_.env->GetThreadPoolQueueLen(pri);
|
||||
}
|
||||
|
||||
Status GetHostName(char* name, uint64_t len) override {
|
||||
return env_target_->GetHostName(name, len);
|
||||
return target_.env->GetHostName(name, len);
|
||||
}
|
||||
void SetBackgroundThreads(int num, Priority pri) override {
|
||||
return env_target_->SetBackgroundThreads(num, pri);
|
||||
return target_.env->SetBackgroundThreads(num, pri);
|
||||
}
|
||||
int GetBackgroundThreads(Priority pri) override {
|
||||
return env_target_->GetBackgroundThreads(pri);
|
||||
return target_.env->GetBackgroundThreads(pri);
|
||||
}
|
||||
|
||||
Status SetAllowNonOwnerAccess(bool allow_non_owner_access) override {
|
||||
return env_target_->SetAllowNonOwnerAccess(allow_non_owner_access);
|
||||
return target_.env->SetAllowNonOwnerAccess(allow_non_owner_access);
|
||||
}
|
||||
|
||||
void IncBackgroundThreadsIfNeeded(int num, Priority pri) override {
|
||||
return env_target_->IncBackgroundThreadsIfNeeded(num, pri);
|
||||
return target_.env->IncBackgroundThreadsIfNeeded(num, pri);
|
||||
}
|
||||
|
||||
void LowerThreadPoolIOPriority(Priority pool) override {
|
||||
env_target_->LowerThreadPoolIOPriority(pool);
|
||||
target_.env->LowerThreadPoolIOPriority(pool);
|
||||
}
|
||||
|
||||
void LowerThreadPoolCPUPriority(Priority pool) override {
|
||||
env_target_->LowerThreadPoolCPUPriority(pool);
|
||||
target_.env->LowerThreadPoolCPUPriority(pool);
|
||||
}
|
||||
|
||||
Status LowerThreadPoolCPUPriority(Priority pool, CpuPriority pri) override {
|
||||
return env_target_->LowerThreadPoolCPUPriority(pool, pri);
|
||||
return target_.env->LowerThreadPoolCPUPriority(pool, pri);
|
||||
}
|
||||
|
||||
Status GetThreadList(std::vector<ThreadStatus>* thread_list) override {
|
||||
return env_target_->GetThreadList(thread_list);
|
||||
return target_.env->GetThreadList(thread_list);
|
||||
}
|
||||
|
||||
ThreadStatusUpdater* GetThreadStatusUpdater() const override {
|
||||
return env_target_->GetThreadStatusUpdater();
|
||||
return target_.env->GetThreadStatusUpdater();
|
||||
}
|
||||
|
||||
uint64_t GetThreadID() const override { return env_target_->GetThreadID(); }
|
||||
uint64_t GetThreadID() const override { return target_.env->GetThreadID(); }
|
||||
|
||||
std::string GenerateUniqueId() override {
|
||||
return env_target_->GenerateUniqueId();
|
||||
return target_.env->GenerateUniqueId();
|
||||
}
|
||||
|
||||
private:
|
||||
Env* env_target_;
|
||||
EnvWrapper::Target target_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+155
-40
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "env/emulated_clock.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "env/unique_id_gen.h"
|
||||
#include "logging/env_logger.h"
|
||||
#include "memory/arena.h"
|
||||
@@ -29,13 +30,43 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
#ifndef ROCKSDB_LITE
|
||||
static int RegisterBuiltinEnvs(ObjectLibrary& library,
|
||||
const std::string& /*arg*/) {
|
||||
library.AddFactory<Env>(MockEnv::kClassName(), [](const std::string& /*uri*/,
|
||||
std::unique_ptr<Env>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
guard->reset(MockEnv::Create(Env::Default()));
|
||||
return guard->get();
|
||||
});
|
||||
library.AddFactory<Env>(
|
||||
CompositeEnvWrapper::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<Env>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
guard->reset(new CompositeEnvWrapper(Env::Default()));
|
||||
return guard->get();
|
||||
});
|
||||
size_t num_types;
|
||||
return static_cast<int>(library.GetFactoryCount(&num_types));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
static void RegisterSystemEnvs() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
static std::once_flag loaded;
|
||||
std::call_once(loaded, [&]() {
|
||||
RegisterBuiltinEnvs(*(ObjectLibrary::Default().get()), "");
|
||||
});
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
class LegacySystemClock : public SystemClock {
|
||||
private:
|
||||
Env* env_;
|
||||
|
||||
public:
|
||||
explicit LegacySystemClock(Env* env) : env_(env) {}
|
||||
const char* Name() const override { return "Legacy System Clock"; }
|
||||
const char* Name() const override { return "LegacySystemClock"; }
|
||||
|
||||
// Returns the number of micro-seconds since some fixed point in time.
|
||||
// It is often used as system time such as in GenericRateLimiter
|
||||
@@ -66,6 +97,16 @@ class LegacySystemClock : public SystemClock {
|
||||
std::string TimeToString(uint64_t time) override {
|
||||
return env_->TimeToString(time);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string SerializeOptions(const ConfigOptions& /*config_options*/,
|
||||
const std::string& /*prefix*/) const override {
|
||||
// We do not want the LegacySystemClock to appear in the serialized output.
|
||||
// This clock is an internal class for those who do not implement one and
|
||||
// would be part of the Env. As such, do not serialize it here.
|
||||
return "";
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
|
||||
class LegacySequentialFileWrapper : public FSSequentialFile {
|
||||
@@ -561,6 +602,15 @@ class LegacyFileSystemWrapper : public FileSystem {
|
||||
return status_to_io_status(target_->IsDirectory(path, is_dir));
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string SerializeOptions(const ConfigOptions& /*config_options*/,
|
||||
const std::string& /*prefix*/) const override {
|
||||
// We do not want the LegacyFileSystem to appear in the serialized output.
|
||||
// This clock is an internal class for those who do not implement one and
|
||||
// would be part of the Env. As such, do not serialize it here.
|
||||
return "";
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
private:
|
||||
Env* target_;
|
||||
};
|
||||
@@ -594,19 +644,19 @@ Status Env::LoadEnv(const std::string& value, Env** result) {
|
||||
|
||||
Status Env::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value, Env** result) {
|
||||
Env* env = *result;
|
||||
Status s;
|
||||
#ifndef ROCKSDB_LITE
|
||||
(void)config_options;
|
||||
s = ObjectRegistry::NewInstance()->NewStaticObject<Env>(value, &env);
|
||||
#else
|
||||
(void)config_options;
|
||||
s = Status::NotSupported("Cannot load environment in LITE mode", value);
|
||||
#endif
|
||||
if (s.ok()) {
|
||||
*result = env;
|
||||
Env* base = Env::Default();
|
||||
if (value.empty() || base->IsInstanceOf(value)) {
|
||||
*result = base;
|
||||
return Status::OK();
|
||||
} else {
|
||||
RegisterSystemEnvs();
|
||||
Env* env = *result;
|
||||
Status s = LoadStaticObject<Env>(config_options, value, nullptr, &env);
|
||||
if (s.ok()) {
|
||||
*result = env;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status Env::LoadEnv(const std::string& value, Env** result,
|
||||
@@ -618,37 +668,46 @@ Status Env::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value, Env** result,
|
||||
std::shared_ptr<Env>* guard) {
|
||||
assert(result);
|
||||
if (value.empty()) {
|
||||
*result = Env::Default();
|
||||
return Status::OK();
|
||||
}
|
||||
Status s;
|
||||
#ifndef ROCKSDB_LITE
|
||||
Env* env = nullptr;
|
||||
std::unique_ptr<Env> uniq_guard;
|
||||
std::string err_msg;
|
||||
assert(guard != nullptr);
|
||||
(void)config_options;
|
||||
env = ObjectRegistry::NewInstance()->NewObject<Env>(value, &uniq_guard,
|
||||
&err_msg);
|
||||
if (!env) {
|
||||
s = Status::NotSupported(std::string("Cannot load ") + Env::Type() + ": " +
|
||||
value);
|
||||
env = Env::Default();
|
||||
std::unique_ptr<Env> uniq;
|
||||
|
||||
Env* env = *result;
|
||||
std::string id;
|
||||
std::unordered_map<std::string, std::string> opt_map;
|
||||
|
||||
Status status =
|
||||
Customizable::GetOptionsMap(config_options, env, value, &id, &opt_map);
|
||||
if (!status.ok()) { // GetOptionsMap failed
|
||||
return status;
|
||||
}
|
||||
if (s.ok() && uniq_guard) {
|
||||
guard->reset(uniq_guard.release());
|
||||
*result = guard->get();
|
||||
Env* base = Env::Default();
|
||||
if (id.empty() || base->IsInstanceOf(id)) {
|
||||
env = base;
|
||||
status = Status::OK();
|
||||
} else {
|
||||
RegisterSystemEnvs();
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string errmsg;
|
||||
env = config_options.registry->NewObject<Env>(id, &uniq, &errmsg);
|
||||
if (!env) {
|
||||
status = Status::NotSupported(
|
||||
std::string("Cannot load environment[") + id + "]: ", errmsg);
|
||||
}
|
||||
#else
|
||||
status =
|
||||
Status::NotSupported("Cannot load environment in LITE mode", value);
|
||||
#endif
|
||||
}
|
||||
if (config_options.ignore_unsupported_options && status.IsNotSupported()) {
|
||||
status = Status::OK();
|
||||
} else if (status.ok()) {
|
||||
status = Customizable::ConfigureNewObject(config_options, env, opt_map);
|
||||
}
|
||||
if (status.ok()) {
|
||||
guard->reset(uniq.release());
|
||||
*result = env;
|
||||
}
|
||||
#else
|
||||
(void)config_options;
|
||||
(void)result;
|
||||
(void)guard;
|
||||
s = Status::NotSupported("Cannot load environment in LITE mode", value);
|
||||
#endif
|
||||
return s;
|
||||
return status;
|
||||
}
|
||||
|
||||
Status Env::CreateFromUri(const ConfigOptions& config_options,
|
||||
@@ -1029,9 +1088,65 @@ Status ReadFileToString(Env* env, const std::string& fname, std::string* data) {
|
||||
return ReadFileToString(fs.get(), fname, data);
|
||||
}
|
||||
|
||||
namespace {
|
||||
static std::unordered_map<std::string, OptionTypeInfo> env_wrapper_type_info = {
|
||||
#ifndef ROCKSDB_LITE
|
||||
{"target",
|
||||
{0, OptionType::kCustomizable, OptionVerificationType::kByName,
|
||||
OptionTypeFlags::kDontSerialize | OptionTypeFlags::kRawPointer,
|
||||
[](const ConfigOptions& opts, const std::string& /*name*/,
|
||||
const std::string& value, void* addr) {
|
||||
EnvWrapper::Target* target = static_cast<EnvWrapper::Target*>(addr);
|
||||
return Env::CreateFromString(opts, value, &(target->env),
|
||||
&(target->guard));
|
||||
},
|
||||
nullptr, nullptr}},
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
} // namespace
|
||||
|
||||
EnvWrapper::EnvWrapper(Env* t) : target_(t) {
|
||||
RegisterOptions("", &target_, &env_wrapper_type_info);
|
||||
}
|
||||
|
||||
EnvWrapper::EnvWrapper(std::unique_ptr<Env>&& t) : target_(std::move(t)) {
|
||||
RegisterOptions("", &target_, &env_wrapper_type_info);
|
||||
}
|
||||
|
||||
EnvWrapper::EnvWrapper(const std::shared_ptr<Env>& t) : target_(t) {
|
||||
RegisterOptions("", &target_, &env_wrapper_type_info);
|
||||
}
|
||||
|
||||
EnvWrapper::~EnvWrapper() {
|
||||
}
|
||||
|
||||
Status EnvWrapper::PrepareOptions(const ConfigOptions& options) {
|
||||
target_.Prepare();
|
||||
return Env::PrepareOptions(options);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string EnvWrapper::SerializeOptions(const ConfigOptions& config_options,
|
||||
const std::string& header) const {
|
||||
auto parent = Env::SerializeOptions(config_options, "");
|
||||
if (config_options.IsShallow() || target_.env == nullptr ||
|
||||
target_.env == Env::Default()) {
|
||||
return parent;
|
||||
} else {
|
||||
std::string result = header;
|
||||
if (!StartsWith(parent, OptionTypeInfo::kIdPropName())) {
|
||||
result.append(OptionTypeInfo::kIdPropName()).append("=");
|
||||
}
|
||||
result.append(parent);
|
||||
if (!EndsWith(result, config_options.delimiter)) {
|
||||
result.append(config_options.delimiter);
|
||||
}
|
||||
result.append("target=").append(target_.env->ToString(config_options));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
namespace { // anonymous namespace
|
||||
|
||||
void AssignEnvOptions(EnvOptions* env_options, const DBOptions& options) {
|
||||
@@ -1179,7 +1294,7 @@ std::string SystemClockWrapper::SerializeOptions(
|
||||
#ifndef ROCKSDB_LITE
|
||||
static int RegisterBuiltinSystemClocks(ObjectLibrary& library,
|
||||
const std::string& /*arg*/) {
|
||||
library.Register<SystemClock>(
|
||||
library.AddFactory<SystemClock>(
|
||||
EmulatedSystemClock::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<SystemClock>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
|
||||
Vendored
+9
-7
@@ -1274,10 +1274,10 @@ static void RegisterEncryptionBuiltins() {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&]() {
|
||||
auto lib = ObjectRegistry::Default()->AddLibrary("encryption");
|
||||
std::string ctr =
|
||||
std::string(CTREncryptionProvider::kClassName()) + "?(://test)";
|
||||
lib->Register<EncryptionProvider>(
|
||||
std::string(CTREncryptionProvider::kClassName()) + "(://test)?",
|
||||
// Match "CTR" or "CTR://test"
|
||||
lib->AddFactory<EncryptionProvider>(
|
||||
ObjectLibrary::PatternEntry(CTREncryptionProvider::kClassName(), true)
|
||||
.AddSuffix("://test"),
|
||||
[](const std::string& uri, std::unique_ptr<EncryptionProvider>* guard,
|
||||
std::string* /*errmsg*/) {
|
||||
if (EndsWith(uri, "://test")) {
|
||||
@@ -1290,7 +1290,7 @@ static void RegisterEncryptionBuiltins() {
|
||||
return guard->get();
|
||||
});
|
||||
|
||||
lib->Register<EncryptionProvider>(
|
||||
lib->AddFactory<EncryptionProvider>(
|
||||
"1://test", [](const std::string& /*uri*/,
|
||||
std::unique_ptr<EncryptionProvider>* guard,
|
||||
std::string* /*errmsg*/) {
|
||||
@@ -1300,8 +1300,10 @@ static void RegisterEncryptionBuiltins() {
|
||||
return guard->get();
|
||||
});
|
||||
|
||||
lib->Register<BlockCipher>(
|
||||
std::string(ROT13BlockCipher::kClassName()) + "(:.*)?",
|
||||
// Match "ROT13" or "ROT13:[0-9]+"
|
||||
lib->AddFactory<BlockCipher>(
|
||||
ObjectLibrary::PatternEntry(ROT13BlockCipher::kClassName(), true)
|
||||
.AddNumber(":"),
|
||||
[](const std::string& uri, std::unique_ptr<BlockCipher>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
size_t colon = uri.find(':');
|
||||
|
||||
Vendored
+4
-16
@@ -209,7 +209,10 @@ class PosixClock : public SystemClock {
|
||||
|
||||
class PosixEnv : public CompositeEnv {
|
||||
public:
|
||||
PosixEnv(const PosixEnv* default_env, const std::shared_ptr<FileSystem>& fs);
|
||||
static const char* kClassName() { return "PosixEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
const char* NickName() const override { return kDefaultName(); }
|
||||
|
||||
~PosixEnv() override {
|
||||
if (this == Env::Default()) {
|
||||
for (const auto tid : threads_to_join_) {
|
||||
@@ -419,16 +422,6 @@ PosixEnv::PosixEnv()
|
||||
thread_status_updater_ = CreateThreadStatusUpdater();
|
||||
}
|
||||
|
||||
PosixEnv::PosixEnv(const PosixEnv* default_env,
|
||||
const std::shared_ptr<FileSystem>& fs)
|
||||
: CompositeEnv(fs, default_env->GetSystemClock()),
|
||||
thread_pools_(default_env->thread_pools_),
|
||||
mu_(default_env->mu_),
|
||||
threads_to_join_(default_env->threads_to_join_),
|
||||
allow_non_owner_access_(default_env->allow_non_owner_access_) {
|
||||
thread_status_updater_ = default_env->thread_status_updater_;
|
||||
}
|
||||
|
||||
void PosixEnv::Schedule(void (*function)(void* arg1), void* arg, Priority pri,
|
||||
void* tag, void (*unschedFunction)(void* arg)) {
|
||||
assert(pri >= Priority::BOTTOM && pri <= Priority::HIGH);
|
||||
@@ -499,11 +492,6 @@ Env* Env::Default() {
|
||||
return &default_env;
|
||||
}
|
||||
|
||||
std::unique_ptr<Env> NewCompositeEnv(const std::shared_ptr<FileSystem>& fs) {
|
||||
PosixEnv* default_env = static_cast<PosixEnv*>(Env::Default());
|
||||
return std::unique_ptr<Env>(new PosixEnv(default_env, fs));
|
||||
}
|
||||
|
||||
//
|
||||
// Default Posix SystemClock
|
||||
//
|
||||
|
||||
Vendored
+200
-21
@@ -40,11 +40,13 @@
|
||||
#include "env/env_chroot.h"
|
||||
#include "env/env_encryption_ctr.h"
|
||||
#include "env/fs_readonly.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "env/unique_id_gen.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "port/malloc.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/env_encryption.h"
|
||||
@@ -2136,29 +2138,29 @@ class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv() : EnvWrapper(Env::Default()),
|
||||
close_count(0) { }
|
||||
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
TestLogger(TestEnv* env_ptr) : Logger() { env = env_ptr; }
|
||||
~TestLogger() override {
|
||||
if (!closed_) {
|
||||
Status s = CloseHelper();
|
||||
s.PermitUncheckedError();
|
||||
const char* Name() const override { return "TestEnv"; }
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
explicit TestLogger(TestEnv* env_ptr) : Logger() { env = env_ptr; }
|
||||
~TestLogger() override {
|
||||
if (!closed_) {
|
||||
Status s = CloseHelper();
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
void Logv(const char* /*format*/, va_list /*ap*/) override{};
|
||||
void Logv(const char* /*format*/, va_list /*ap*/) override {}
|
||||
|
||||
protected:
|
||||
Status CloseImpl() override { return CloseHelper(); }
|
||||
protected:
|
||||
Status CloseImpl() override { return CloseHelper(); }
|
||||
|
||||
private:
|
||||
Status CloseHelper() {
|
||||
env->CloseCountInc();;
|
||||
return Status::OK();
|
||||
}
|
||||
TestEnv* env;
|
||||
};
|
||||
private:
|
||||
Status CloseHelper() {
|
||||
env->CloseCountInc();
|
||||
return Status::OK();
|
||||
}
|
||||
TestEnv* env;
|
||||
};
|
||||
|
||||
void CloseCountInc() { close_count++; }
|
||||
|
||||
@@ -2490,7 +2492,7 @@ TEST_F(CreateEnvTest, CreateDefaultSystemClock) {
|
||||
TEST_F(CreateEnvTest, CreateMockSystemClock) {
|
||||
std::shared_ptr<SystemClock> mock, copy;
|
||||
|
||||
config_options_.registry->AddLibrary("test")->Register<SystemClock>(
|
||||
config_options_.registry->AddLibrary("test")->AddFactory<SystemClock>(
|
||||
MockSystemClock::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<SystemClock>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
@@ -2894,9 +2896,186 @@ TEST_F(EnvTest, FailureToCreateLockFile) {
|
||||
// Clean up
|
||||
ASSERT_OK(DestroyDir(env, dir));
|
||||
}
|
||||
|
||||
TEST_F(EnvTest, CreateDefaultEnv) {
|
||||
ConfigOptions options;
|
||||
options.ignore_unsupported_options = false;
|
||||
|
||||
std::shared_ptr<Env> guard;
|
||||
Env* env = nullptr;
|
||||
ASSERT_OK(Env::CreateFromString(options, "", &env));
|
||||
ASSERT_EQ(env, Env::Default());
|
||||
|
||||
env = nullptr;
|
||||
ASSERT_OK(Env::CreateFromString(options, Env::kDefaultName(), &env));
|
||||
ASSERT_EQ(env, Env::Default());
|
||||
|
||||
env = nullptr;
|
||||
ASSERT_OK(Env::CreateFromString(options, "", &env, &guard));
|
||||
ASSERT_EQ(env, Env::Default());
|
||||
ASSERT_EQ(guard, nullptr);
|
||||
|
||||
env = nullptr;
|
||||
ASSERT_OK(Env::CreateFromString(options, Env::kDefaultName(), &env, &guard));
|
||||
ASSERT_EQ(env, Env::Default());
|
||||
ASSERT_EQ(guard, nullptr);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string opt_str = env->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env));
|
||||
ASSERT_EQ(env, Env::Default());
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, &guard));
|
||||
ASSERT_EQ(env, Env::Default());
|
||||
ASSERT_EQ(guard, nullptr);
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace {
|
||||
class WrappedEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit WrappedEnv(Env* t) : EnvWrapper(t) {}
|
||||
explicit WrappedEnv(const std::shared_ptr<Env>& t) : EnvWrapper(t) {}
|
||||
static const char* kClassName() { return "WrappedEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
static void Register(ObjectLibrary& lib, const std::string& /*arg*/) {
|
||||
lib.AddFactory<Env>(
|
||||
WrappedEnv::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<Env>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
guard->reset(new WrappedEnv(nullptr));
|
||||
return guard->get();
|
||||
});
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
TEST_F(EnvTest, CreateMockEnv) {
|
||||
ConfigOptions options;
|
||||
options.ignore_unsupported_options = false;
|
||||
WrappedEnv::Register(*(options.registry->AddLibrary("test")), "");
|
||||
std::shared_ptr<Env> guard, copy;
|
||||
std::string opt_str;
|
||||
|
||||
Env* env = nullptr;
|
||||
ASSERT_NOK(Env::CreateFromString(options, MockEnv::kClassName(), &env));
|
||||
ASSERT_OK(
|
||||
Env::CreateFromString(options, MockEnv::kClassName(), &env, &guard));
|
||||
ASSERT_NE(env, nullptr);
|
||||
ASSERT_NE(env, Env::Default());
|
||||
opt_str = env->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
ASSERT_NE(copy, guard);
|
||||
std::string mismatch;
|
||||
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
|
||||
guard.reset(MockEnv::Create(Env::Default(), SystemClock::Default()));
|
||||
opt_str = guard->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
std::unique_ptr<Env> wrapped_env(new WrappedEnv(Env::Default()));
|
||||
guard.reset(MockEnv::Create(wrapped_env.get(), SystemClock::Default()));
|
||||
opt_str = guard->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
opt_str = copy->ToString(options);
|
||||
}
|
||||
|
||||
TEST_F(EnvTest, CreateWrappedEnv) {
|
||||
ConfigOptions options;
|
||||
options.ignore_unsupported_options = false;
|
||||
WrappedEnv::Register(*(options.registry->AddLibrary("test")), "");
|
||||
Env* env = nullptr;
|
||||
std::shared_ptr<Env> guard, copy;
|
||||
std::string opt_str;
|
||||
std::string mismatch;
|
||||
|
||||
ASSERT_NOK(Env::CreateFromString(options, WrappedEnv::kClassName(), &env));
|
||||
ASSERT_OK(
|
||||
Env::CreateFromString(options, WrappedEnv::kClassName(), &env, &guard));
|
||||
ASSERT_NE(env, nullptr);
|
||||
ASSERT_NE(env, Env::Default());
|
||||
ASSERT_FALSE(guard->AreEquivalent(options, Env::Default(), &mismatch));
|
||||
|
||||
opt_str = env->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
ASSERT_NE(copy, guard);
|
||||
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
|
||||
|
||||
guard.reset(new WrappedEnv(std::make_shared<WrappedEnv>(Env::Default())));
|
||||
ASSERT_NE(guard.get(), env);
|
||||
opt_str = guard->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
ASSERT_NE(copy, guard);
|
||||
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
|
||||
|
||||
guard.reset(new WrappedEnv(std::make_shared<WrappedEnv>(
|
||||
std::make_shared<WrappedEnv>(Env::Default()))));
|
||||
ASSERT_NE(guard.get(), env);
|
||||
opt_str = guard->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
ASSERT_NE(copy, guard);
|
||||
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
|
||||
}
|
||||
|
||||
TEST_F(EnvTest, CreateCompositeEnv) {
|
||||
ConfigOptions options;
|
||||
options.ignore_unsupported_options = false;
|
||||
std::shared_ptr<Env> guard, copy;
|
||||
Env* env = nullptr;
|
||||
std::string mismatch, opt_str;
|
||||
|
||||
WrappedEnv::Register(*(options.registry->AddLibrary("test")), "");
|
||||
std::unique_ptr<Env> base(NewCompositeEnv(FileSystem::Default()));
|
||||
std::unique_ptr<Env> wrapped(new WrappedEnv(Env::Default()));
|
||||
std::shared_ptr<FileSystem> timed_fs =
|
||||
std::make_shared<TimedFileSystem>(FileSystem::Default());
|
||||
std::shared_ptr<SystemClock> clock =
|
||||
std::make_shared<EmulatedSystemClock>(SystemClock::Default());
|
||||
|
||||
opt_str = base->ToString(options);
|
||||
ASSERT_NOK(Env::CreateFromString(options, opt_str, &env));
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, &guard));
|
||||
ASSERT_NE(env, nullptr);
|
||||
ASSERT_NE(env, Env::Default());
|
||||
ASSERT_EQ(env->GetFileSystem(), FileSystem::Default());
|
||||
ASSERT_EQ(env->GetSystemClock(), SystemClock::Default());
|
||||
|
||||
base = NewCompositeEnv(timed_fs);
|
||||
opt_str = base->ToString(options);
|
||||
ASSERT_NOK(Env::CreateFromString(options, opt_str, &env));
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, &guard));
|
||||
ASSERT_NE(env, nullptr);
|
||||
ASSERT_NE(env, Env::Default());
|
||||
ASSERT_NE(env->GetFileSystem(), FileSystem::Default());
|
||||
ASSERT_EQ(env->GetSystemClock(), SystemClock::Default());
|
||||
|
||||
env = nullptr;
|
||||
guard.reset(new CompositeEnvWrapper(wrapped.get(), timed_fs));
|
||||
opt_str = guard->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
ASSERT_NE(env, nullptr);
|
||||
ASSERT_NE(env, Env::Default());
|
||||
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
|
||||
|
||||
env = nullptr;
|
||||
guard.reset(new CompositeEnvWrapper(wrapped.get(), clock));
|
||||
opt_str = guard->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
ASSERT_NE(env, nullptr);
|
||||
ASSERT_NE(env, Env::Default());
|
||||
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
|
||||
|
||||
env = nullptr;
|
||||
guard.reset(new CompositeEnvWrapper(wrapped.get(), timed_fs, clock));
|
||||
opt_str = guard->ToString(options);
|
||||
ASSERT_OK(Env::CreateFromString(options, opt_str, &env, ©));
|
||||
ASSERT_NE(env, nullptr);
|
||||
ASSERT_NE(env, Env::Default());
|
||||
ASSERT_TRUE(guard->AreEquivalent(options, copy.get(), &mismatch));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Vendored
+11
-4
@@ -32,21 +32,21 @@ Status FileSystem::Load(const std::string& value,
|
||||
#ifndef ROCKSDB_LITE
|
||||
static int RegisterBuiltinFileSystems(ObjectLibrary& library,
|
||||
const std::string& /*arg*/) {
|
||||
library.Register<FileSystem>(
|
||||
library.AddFactory<FileSystem>(
|
||||
TimedFileSystem::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
guard->reset(new TimedFileSystem(nullptr));
|
||||
return guard->get();
|
||||
});
|
||||
library.Register<FileSystem>(
|
||||
library.AddFactory<FileSystem>(
|
||||
ReadOnlyFileSystem::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
guard->reset(new ReadOnlyFileSystem(nullptr));
|
||||
return guard->get();
|
||||
});
|
||||
library.Register<FileSystem>(
|
||||
library.AddFactory<FileSystem>(
|
||||
EncryptedFileSystem::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
|
||||
std::string* errmsg) {
|
||||
@@ -56,8 +56,15 @@ static int RegisterBuiltinFileSystems(ObjectLibrary& library,
|
||||
}
|
||||
return guard->get();
|
||||
});
|
||||
library.AddFactory<FileSystem>(
|
||||
MockFileSystem::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
|
||||
std::string* /*errmsg*/) {
|
||||
guard->reset(new MockFileSystem(SystemClock::Default()));
|
||||
return guard->get();
|
||||
});
|
||||
#ifndef OS_WIN
|
||||
library.Register<FileSystem>(
|
||||
library.AddFactory<FileSystem>(
|
||||
ChrootFileSystem::kClassName(),
|
||||
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
|
||||
std::string* /* errmsg */) {
|
||||
|
||||
Vendored
+3
-3
@@ -238,7 +238,7 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
SetFD_CLOEXEC(fd, &options);
|
||||
|
||||
if (options.use_mmap_reads && sizeof(void*) >= 8) {
|
||||
if (options.use_mmap_reads) {
|
||||
// Use of mmap for random reads has been removed because it
|
||||
// kills performance when storage is fast.
|
||||
// Use mmap when virtual address-space is plentiful.
|
||||
@@ -1124,8 +1124,8 @@ std::shared_ptr<FileSystem> FileSystem::Default() {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
static FactoryFunc<FileSystem> posix_filesystem_reg =
|
||||
ObjectLibrary::Default()->Register<FileSystem>(
|
||||
"posix://.*",
|
||||
ObjectLibrary::Default()->AddFactory<FileSystem>(
|
||||
ObjectLibrary::PatternEntry("posix").AddSeparator("://", false),
|
||||
[](const std::string& /* uri */, std::unique_ptr<FileSystem>* f,
|
||||
std::string* /* errmsg */) {
|
||||
f->reset(new PosixFileSystem());
|
||||
|
||||
Vendored
+47
-2
@@ -558,7 +558,7 @@ PosixRandomAccessFile::PosixRandomAccessFile(
|
||||
#endif
|
||||
{
|
||||
assert(!options.use_direct_reads || !options.use_mmap_reads);
|
||||
assert(!options.use_mmap_reads || sizeof(void*) < 8);
|
||||
assert(!options.use_mmap_reads);
|
||||
}
|
||||
|
||||
PosixRandomAccessFile::~PosixRandomAccessFile() { close(fd_); }
|
||||
@@ -1108,9 +1108,15 @@ IOStatus PosixMmapFile::Flush(const IOOptions& /*opts*/,
|
||||
|
||||
IOStatus PosixMmapFile::Sync(const IOOptions& /*opts*/,
|
||||
IODebugContext* /*dbg*/) {
|
||||
#ifdef HAVE_FULLFSYNC
|
||||
if (::fcntl(fd_, F_FULLFSYNC) < 0) {
|
||||
return IOError("while fcntl(F_FULLSYNC) mmapped file", filename_, errno);
|
||||
}
|
||||
#else // HAVE_FULLFSYNC
|
||||
if (fdatasync(fd_) < 0) {
|
||||
return IOError("While fdatasync mmapped file", filename_, errno);
|
||||
}
|
||||
#endif // HAVE_FULLFSYNC
|
||||
|
||||
return Msync();
|
||||
}
|
||||
@@ -1120,9 +1126,15 @@ IOStatus PosixMmapFile::Sync(const IOOptions& /*opts*/,
|
||||
*/
|
||||
IOStatus PosixMmapFile::Fsync(const IOOptions& /*opts*/,
|
||||
IODebugContext* /*dbg*/) {
|
||||
#ifdef HAVE_FULLFSYNC
|
||||
if (::fcntl(fd_, F_FULLFSYNC) < 0) {
|
||||
return IOError("While fcntl(F_FULLSYNC) on mmaped file", filename_, errno);
|
||||
}
|
||||
#else // HAVE_FULLFSYNC
|
||||
if (fsync(fd_) < 0) {
|
||||
return IOError("While fsync mmaped file", filename_, errno);
|
||||
}
|
||||
#endif // HAVE_FULLFSYNC
|
||||
|
||||
return Msync();
|
||||
}
|
||||
@@ -1320,17 +1332,29 @@ IOStatus PosixWritableFile::Flush(const IOOptions& /*opts*/,
|
||||
|
||||
IOStatus PosixWritableFile::Sync(const IOOptions& /*opts*/,
|
||||
IODebugContext* /*dbg*/) {
|
||||
#ifdef HAVE_FULLFSYNC
|
||||
if (::fcntl(fd_, F_FULLFSYNC) < 0) {
|
||||
return IOError("while fcntl(F_FULLFSYNC)", filename_, errno);
|
||||
}
|
||||
#else // HAVE_FULLFSYNC
|
||||
if (fdatasync(fd_) < 0) {
|
||||
return IOError("While fdatasync", filename_, errno);
|
||||
}
|
||||
#endif // HAVE_FULLFSYNC
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
IOStatus PosixWritableFile::Fsync(const IOOptions& /*opts*/,
|
||||
IODebugContext* /*dbg*/) {
|
||||
#ifdef HAVE_FULLFSYNC
|
||||
if (::fcntl(fd_, F_FULLFSYNC) < 0) {
|
||||
return IOError("while fcntl(F_FULLFSYNC)", filename_, errno);
|
||||
}
|
||||
#else // HAVE_FULLFSYNC
|
||||
if (fsync(fd_) < 0) {
|
||||
return IOError("While fsync", filename_, errno);
|
||||
}
|
||||
#endif // HAVE_FULLFSYNC
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
@@ -1503,17 +1527,29 @@ IOStatus PosixRandomRWFile::Flush(const IOOptions& /*opts*/,
|
||||
|
||||
IOStatus PosixRandomRWFile::Sync(const IOOptions& /*opts*/,
|
||||
IODebugContext* /*dbg*/) {
|
||||
#ifdef HAVE_FULLFSYNC
|
||||
if (::fcntl(fd_, F_FULLFSYNC) < 0) {
|
||||
return IOError("while fcntl(F_FULLFSYNC) random rw file", filename_, errno);
|
||||
}
|
||||
#else // HAVE_FULLFSYNC
|
||||
if (fdatasync(fd_) < 0) {
|
||||
return IOError("While fdatasync random read/write file", filename_, errno);
|
||||
}
|
||||
#endif // HAVE_FULLFSYNC
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
IOStatus PosixRandomRWFile::Fsync(const IOOptions& /*opts*/,
|
||||
IODebugContext* /*dbg*/) {
|
||||
#ifdef HAVE_FULLFSYNC
|
||||
if (::fcntl(fd_, F_FULLFSYNC) < 0) {
|
||||
return IOError("While fcntl(F_FULLSYNC) random rw file", filename_, errno);
|
||||
}
|
||||
#else // HAVE_FULLFSYNC
|
||||
if (fsync(fd_) < 0) {
|
||||
return IOError("While fsync random read/write file", filename_, errno);
|
||||
}
|
||||
#endif // HAVE_FULLFSYNC
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
@@ -1585,10 +1621,19 @@ IOStatus PosixDirectory::FsyncWithDirOptions(
|
||||
}
|
||||
// fallback to dir-fsync for kDefault, kDirRenamed and kFileDeleted
|
||||
}
|
||||
#ifdef HAVE_FULLFSYNC
|
||||
// btrfs is a Linux file system, while currently F_FULLFSYNC is available on
|
||||
// Mac OS.
|
||||
assert(!is_btrfs_);
|
||||
if (::fcntl(fd_, F_FULLFSYNC) < 0) {
|
||||
return IOError("while fcntl(F_FULLFSYNC)", "a directory", errno);
|
||||
}
|
||||
#else // HAVE_FULLFSYNC
|
||||
if (fsync(fd_) == -1) {
|
||||
s = IOError("While fsync", "a directory", errno);
|
||||
}
|
||||
#endif
|
||||
#endif // HAVE_FULLFSYNC
|
||||
#endif // OS_AIX
|
||||
return s;
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Vendored
+19
@@ -565,12 +565,21 @@ class TestMemLogger : public Logger {
|
||||
}
|
||||
size_t GetLogFileSize() const override { return log_size_; }
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo> mock_fs_type_info = {
|
||||
#ifndef ROCKSDB_LITE
|
||||
{"supports_direct_io",
|
||||
{0, OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
} // namespace
|
||||
|
||||
MockFileSystem::MockFileSystem(const std::shared_ptr<SystemClock>& clock,
|
||||
bool supports_direct_io)
|
||||
: system_clock_(clock), supports_direct_io_(supports_direct_io) {
|
||||
clock_ = system_clock_.get();
|
||||
RegisterOptions("", &supports_direct_io_, &mock_fs_type_info);
|
||||
}
|
||||
|
||||
MockFileSystem::~MockFileSystem() {
|
||||
@@ -578,6 +587,16 @@ MockFileSystem::~MockFileSystem() {
|
||||
i->second->Unref();
|
||||
}
|
||||
}
|
||||
|
||||
Status MockFileSystem::PrepareOptions(const ConfigOptions& options) {
|
||||
Status s = FileSystem::PrepareOptions(options);
|
||||
if (s.ok() && system_clock_ == SystemClock::Default()) {
|
||||
system_clock_ = options.env->GetSystemClock();
|
||||
clock_ = system_clock_.get();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus MockFileSystem::GetAbsolutePath(const std::string& db_path,
|
||||
const IOOptions& /*options*/,
|
||||
std::string* output_path,
|
||||
|
||||
Vendored
+4
@@ -107,6 +107,7 @@ class MockFileSystem : public FileSystem {
|
||||
}
|
||||
|
||||
Status CorruptBuffer(const std::string& fname);
|
||||
Status PrepareOptions(const ConfigOptions& options) override;
|
||||
|
||||
private:
|
||||
bool RenameFileInternal(const std::string& src, const std::string& dest);
|
||||
@@ -130,6 +131,9 @@ class MockEnv : public CompositeEnvWrapper {
|
||||
static MockEnv* Create(Env* base);
|
||||
static MockEnv* Create(Env* base, const std::shared_ptr<SystemClock>& clock);
|
||||
|
||||
static const char* kClassName() { return "MockEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
Status CorruptBuffer(const std::string& fname);
|
||||
private:
|
||||
MockEnv(Env* env, const std::shared_ptr<FileSystem>& fs,
|
||||
|
||||
@@ -10,14 +10,24 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
using namespace ROCKSDB_NAMESPACE;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
std::string kDBPath = "C:\\Windows\\TEMP\\rocksdb_column_families_example";
|
||||
#else
|
||||
std::string kDBPath = "/tmp/rocksdb_column_families_example";
|
||||
#endif
|
||||
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyDescriptor;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyHandle;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyOptions;
|
||||
using ROCKSDB_NAMESPACE::DB;
|
||||
using ROCKSDB_NAMESPACE::DBOptions;
|
||||
using ROCKSDB_NAMESPACE::Options;
|
||||
using ROCKSDB_NAMESPACE::ReadOptions;
|
||||
using ROCKSDB_NAMESPACE::Slice;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::WriteBatch;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
|
||||
int main() {
|
||||
// open DB
|
||||
Options options;
|
||||
@@ -40,7 +50,7 @@ int main() {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
// have to open default column family
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
kDefaultColumnFamilyName, ColumnFamilyOptions()));
|
||||
ROCKSDB_NAMESPACE::kDefaultColumnFamilyName, ColumnFamilyOptions()));
|
||||
// open the new one, too
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
"new_cf", ColumnFamilyOptions()));
|
||||
|
||||
@@ -12,7 +12,15 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
using namespace ROCKSDB_NAMESPACE;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyMetaData;
|
||||
using ROCKSDB_NAMESPACE::CompactionOptions;
|
||||
using ROCKSDB_NAMESPACE::DB;
|
||||
using ROCKSDB_NAMESPACE::EventListener;
|
||||
using ROCKSDB_NAMESPACE::FlushJobInfo;
|
||||
using ROCKSDB_NAMESPACE::Options;
|
||||
using ROCKSDB_NAMESPACE::ReadOptions;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
std::string kDBPath = "C:\\Windows\\TEMP\\rocksdb_compact_files_example";
|
||||
@@ -142,7 +150,7 @@ int main() {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Disable RocksDB background compaction.
|
||||
options.compaction_style = kCompactionStyleNone;
|
||||
options.compaction_style = ROCKSDB_NAMESPACE::kCompactionStyleNone;
|
||||
// Small slowdown and stop trigger for experimental purpose.
|
||||
options.level0_slowdown_writes_trigger = 3;
|
||||
options.level0_stop_writes_trigger = 5;
|
||||
@@ -150,7 +158,7 @@ int main() {
|
||||
options.listeners.emplace_back(new FullCompactor(options));
|
||||
|
||||
DB* db = nullptr;
|
||||
DestroyDB(kDBPath, options);
|
||||
ROCKSDB_NAMESPACE::DestroyDB(kDBPath, options);
|
||||
Status s = DB::Open(options, kDBPath, &db);
|
||||
assert(s.ok());
|
||||
assert(db);
|
||||
|
||||
@@ -11,7 +11,15 @@
|
||||
#include "rocksdb/utilities/transaction.h"
|
||||
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||
|
||||
using namespace ROCKSDB_NAMESPACE;
|
||||
using ROCKSDB_NAMESPACE::DB;
|
||||
using ROCKSDB_NAMESPACE::OptimisticTransactionDB;
|
||||
using ROCKSDB_NAMESPACE::OptimisticTransactionOptions;
|
||||
using ROCKSDB_NAMESPACE::Options;
|
||||
using ROCKSDB_NAMESPACE::ReadOptions;
|
||||
using ROCKSDB_NAMESPACE::Snapshot;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::Transaction;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
std::string kDBPath = "C:\\Windows\\TEMP\\rocksdb_transaction_example";
|
||||
|
||||
@@ -18,7 +18,18 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/utilities/options_util.h"
|
||||
|
||||
using namespace ROCKSDB_NAMESPACE;
|
||||
using ROCKSDB_NAMESPACE::BlockBasedTableOptions;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyDescriptor;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyHandle;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyOptions;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilter;
|
||||
using ROCKSDB_NAMESPACE::ConfigOptions;
|
||||
using ROCKSDB_NAMESPACE::DB;
|
||||
using ROCKSDB_NAMESPACE::DBOptions;
|
||||
using ROCKSDB_NAMESPACE::NewLRUCache;
|
||||
using ROCKSDB_NAMESPACE::Options;
|
||||
using ROCKSDB_NAMESPACE::Slice;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
std::string kDBPath = "C:\\Windows\\TEMP\\rocksdb_options_file_example";
|
||||
@@ -45,7 +56,8 @@ int main() {
|
||||
db_opt.create_if_missing = true;
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.push_back({kDefaultColumnFamilyName, ColumnFamilyOptions()});
|
||||
cf_descs.push_back(
|
||||
{ROCKSDB_NAMESPACE::kDefaultColumnFamilyName, ColumnFamilyOptions()});
|
||||
cf_descs.push_back({"new_cf", ColumnFamilyOptions()});
|
||||
|
||||
// initialize BlockBasedTableOptions
|
||||
@@ -63,7 +75,8 @@ int main() {
|
||||
|
||||
// destroy and open DB
|
||||
DB* db;
|
||||
Status s = DestroyDB(kDBPath, Options(db_opt, cf_descs[0].options));
|
||||
Status s = ROCKSDB_NAMESPACE::DestroyDB(kDBPath,
|
||||
Options(db_opt, cf_descs[0].options));
|
||||
assert(s.ok());
|
||||
s = DB::Open(Options(db_opt, cf_descs[0].options), kDBPath, &db);
|
||||
assert(s.ok());
|
||||
|
||||
@@ -10,7 +10,13 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
using namespace ROCKSDB_NAMESPACE;
|
||||
using ROCKSDB_NAMESPACE::DB;
|
||||
using ROCKSDB_NAMESPACE::Options;
|
||||
using ROCKSDB_NAMESPACE::PinnableSlice;
|
||||
using ROCKSDB_NAMESPACE::ReadOptions;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::WriteBatch;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
std::string kDBPath = "C:\\Windows\\TEMP\\rocksdb_simple_example";
|
||||
|
||||
@@ -11,7 +11,15 @@
|
||||
#include "rocksdb/utilities/transaction.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
|
||||
using namespace ROCKSDB_NAMESPACE;
|
||||
using ROCKSDB_NAMESPACE::Options;
|
||||
using ROCKSDB_NAMESPACE::ReadOptions;
|
||||
using ROCKSDB_NAMESPACE::Snapshot;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::Transaction;
|
||||
using ROCKSDB_NAMESPACE::TransactionDB;
|
||||
using ROCKSDB_NAMESPACE::TransactionDBOptions;
|
||||
using ROCKSDB_NAMESPACE::TransactionOptions;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
std::string kDBPath = "C:\\Windows\\TEMP\\rocksdb_transaction_example";
|
||||
@@ -183,7 +191,7 @@ int main() {
|
||||
|
||||
// Cleanup
|
||||
delete txn_db;
|
||||
DestroyDB(kDBPath, options);
|
||||
ROCKSDB_NAMESPACE::DestroyDB(kDBPath, options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include "table/table_reader.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace ROCKSDB_NAMESPACE;
|
||||
|
||||
// Keys in SST file writer operations must be unique and in ascending order.
|
||||
// For each DBOperation generated by the fuzzer, this function is called on
|
||||
// it to deduplicate and sort the keys in the DBOperations.
|
||||
|
||||
@@ -48,6 +48,10 @@ class HdfsEnv : public Env {
|
||||
posixEnv = Env::Default();
|
||||
fileSys_ = connectToPath(fsname_);
|
||||
}
|
||||
static const char* kClassName() { return "HdfsEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
static const char* kNickName() { return "hdfs"; }
|
||||
const char* NickName() const override { return kNickName(); }
|
||||
|
||||
virtual ~HdfsEnv() {
|
||||
fprintf(stderr, "Destroying HdfsEnv::Default()\n");
|
||||
@@ -242,6 +246,10 @@ class HdfsEnv : public Env {
|
||||
fprintf(stderr, "Please see hdfs/README for details\n");
|
||||
abort();
|
||||
}
|
||||
static const char* kClassName() { return "HdfsEnv"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
static const char* kNickName() { return "hdfs"; }
|
||||
const char* NickName() const override { return kNickName(); }
|
||||
|
||||
virtual ~HdfsEnv() {
|
||||
}
|
||||
|
||||
@@ -366,10 +366,16 @@ struct AdvancedColumnFamilyOptions {
|
||||
Slice delta_value,
|
||||
std::string* merged_value) = nullptr;
|
||||
|
||||
// if prefix_extractor is set and memtable_prefix_bloom_size_ratio is not 0,
|
||||
// create prefix bloom for memtable with the size of
|
||||
// Should really be called `memtable_bloom_size_ratio`. Enables a dynamic
|
||||
// Bloom filter in memtable to optimize many queries that must go beyond
|
||||
// the memtable. The size in bytes of the filter is
|
||||
// write_buffer_size * memtable_prefix_bloom_size_ratio.
|
||||
// If it is larger than 0.25, it is sanitized to 0.25.
|
||||
// * If prefix_extractor is set, the filter includes prefixes.
|
||||
// * If memtable_whole_key_filtering, the filter includes whole keys.
|
||||
// * If both, the filter includes both.
|
||||
// * If neither, the feature is disabled.
|
||||
//
|
||||
// If this value is larger than 0.25, it is sanitized to 0.25.
|
||||
//
|
||||
// Default: 0 (disable)
|
||||
//
|
||||
@@ -736,7 +742,9 @@ struct AdvancedColumnFamilyOptions {
|
||||
// LSM changes (Flush, Compaction, AddFile). When this option is true, these
|
||||
// checks are also enabled in release mode. These checks were historically
|
||||
// disabled in release mode, but are now enabled by default for proactive
|
||||
// corruption detection, at almost no cost in extra CPU.
|
||||
// corruption detection. The CPU overhead is negligible for normal mixed
|
||||
// operations but can slow down saturated writing. See
|
||||
// Options::DisableExtraChecks().
|
||||
// Default: true
|
||||
bool force_consistency_checks = true;
|
||||
|
||||
|
||||
+1
-1
@@ -950,7 +950,7 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_merge_operator(
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_uint64add_merge_operator(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compression_per_level(
|
||||
rocksdb_options_t* opt, int* level_values, size_t num_levels);
|
||||
rocksdb_options_t* opt, const int* level_values, size_t num_levels);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_create_if_missing(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_create_if_missing(
|
||||
|
||||
@@ -40,7 +40,7 @@ class Comparator : public Customizable {
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~Comparator() {}
|
||||
~Comparator() override {}
|
||||
|
||||
static Status CreateFromString(const ConfigOptions& opts,
|
||||
const std::string& id,
|
||||
@@ -75,7 +75,7 @@ class Comparator : public Customizable {
|
||||
//
|
||||
// Names starting with "rocksdb." are reserved and should not be used
|
||||
// by any clients of this package.
|
||||
virtual const char* Name() const override = 0;
|
||||
const char* Name() const override = 0;
|
||||
|
||||
// Advanced functions: these are used to reduce the space requirements
|
||||
// for internal data structures like index blocks.
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
*/
|
||||
class Customizable : public Configurable {
|
||||
public:
|
||||
virtual ~Customizable() {}
|
||||
~Customizable() override {}
|
||||
|
||||
// Returns the name of this class of Customizable
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
@@ -403,6 +403,10 @@ class DB {
|
||||
// If "end_key" comes before "start_key" according to the user's comparator,
|
||||
// a `Status::InvalidArgument` is returned.
|
||||
//
|
||||
// WARNING: Do not use `Iterator::Refresh()` API on DBs where `DeleteRange()`
|
||||
// has been used or will be used. This feature combination is neither
|
||||
// supported nor programmatically prevented.
|
||||
//
|
||||
// This feature is now usable in production, with the following caveats:
|
||||
// 1) Accumulating many range tombstones in the memtable will degrade read
|
||||
// performance; this can be avoided by manually flushing occasionally.
|
||||
@@ -1356,6 +1360,15 @@ class DB {
|
||||
// times have the same effect as calling it once.
|
||||
virtual Status DisableFileDeletions() = 0;
|
||||
|
||||
// Increase the full_history_ts of column family. The new ts_low value should
|
||||
// be newer than current full_history_ts value.
|
||||
virtual Status IncreaseFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string ts_low) = 0;
|
||||
|
||||
// Get current full_history_ts value.
|
||||
virtual Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string* ts_low) = 0;
|
||||
|
||||
// Allow compactions to delete obsolete files.
|
||||
// If force == true, the call to EnableFileDeletions() will guarantee that
|
||||
// file deletions are enabled after the call, even if DisableFileDeletions()
|
||||
|
||||
+120
-69
@@ -25,6 +25,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/customizable.h"
|
||||
#include "rocksdb/functor_wrapper.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/thread_status.h"
|
||||
@@ -83,7 +84,8 @@ struct EnvOptions {
|
||||
// Construct from Options
|
||||
explicit EnvOptions(const DBOptions& options);
|
||||
|
||||
// If true, then use mmap to read data
|
||||
// If true, then use mmap to read data.
|
||||
// Not recommended for 32-bit OS.
|
||||
bool use_mmap_reads = false;
|
||||
|
||||
// If true, then use mmap to write data
|
||||
@@ -148,8 +150,9 @@ struct EnvOptions {
|
||||
// Exceptions MUST NOT propagate out of overridden functions into RocksDB,
|
||||
// because RocksDB is not exception-safe. This could cause undefined behavior
|
||||
// including data loss, unreported corruption, deadlocks, and more.
|
||||
class Env {
|
||||
class Env : public Customizable {
|
||||
public:
|
||||
static const char* kDefaultName() { return "DefaultEnv"; }
|
||||
struct FileAttributes {
|
||||
// File name
|
||||
std::string name;
|
||||
@@ -168,10 +171,14 @@ class Env {
|
||||
Env(const Env&) = delete;
|
||||
void operator=(const Env&) = delete;
|
||||
|
||||
virtual ~Env();
|
||||
~Env() override;
|
||||
|
||||
static const char* Type() { return "Environment"; }
|
||||
|
||||
// Deprecated. Will be removed in a major release. Derived classes
|
||||
// should implement this method.
|
||||
const char* Name() const override { return ""; }
|
||||
|
||||
// Loads the environment specified by the input value into the result
|
||||
// The CreateFromString alternative should be used; this method may be
|
||||
// deprecated in a future release.
|
||||
@@ -1339,253 +1346,297 @@ extern Status ReadFileToString(Env* env, const std::string& fname,
|
||||
// functionality of another Env.
|
||||
class EnvWrapper : public Env {
|
||||
public:
|
||||
// The Target struct allows an Env to be stored as a raw (Env*) or
|
||||
// std::shared_ptr<Env>. By using this struct, the wrapping/calling
|
||||
// class does not need to worry about the ownership/lifetime of the
|
||||
// wrapped target env. If the guard is set, then the Env will point
|
||||
// to the guard.get().
|
||||
struct Target {
|
||||
Env* env; // The raw Env
|
||||
std::shared_ptr<Env> guard; // The guarded Env
|
||||
|
||||
// Creates a Target without assuming ownership of the target Env
|
||||
explicit Target(Env* t) : env(t) {}
|
||||
|
||||
// Creates a Target from the guarded env, assuming ownership
|
||||
explicit Target(std::unique_ptr<Env>&& t) : guard(t.release()) {
|
||||
env = guard.get();
|
||||
}
|
||||
|
||||
// Creates a Target from the guarded env, assuming ownership
|
||||
explicit Target(const std::shared_ptr<Env>& t) : guard(t) {
|
||||
env = guard.get();
|
||||
}
|
||||
|
||||
// Makes sure the raw Env is not nullptr
|
||||
void Prepare() {
|
||||
if (guard.get() != nullptr) {
|
||||
env = guard.get();
|
||||
} else if (env == nullptr) {
|
||||
env = Env::Default();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize an EnvWrapper that delegates all calls to *t
|
||||
explicit EnvWrapper(Env* t) : target_(t) {}
|
||||
explicit EnvWrapper(Env* t);
|
||||
explicit EnvWrapper(std::unique_ptr<Env>&& t);
|
||||
explicit EnvWrapper(const std::shared_ptr<Env>& t);
|
||||
~EnvWrapper() override;
|
||||
|
||||
// Return the target to which this Env forwards all calls
|
||||
Env* target() const { return target_; }
|
||||
Env* target() const { return target_.env; }
|
||||
|
||||
// Deprecated. Will be removed in a major release. Derived classes
|
||||
// should implement this method.
|
||||
const char* Name() const override { return target_.env->Name(); }
|
||||
|
||||
// The following text is boilerplate that forwards all methods to target()
|
||||
Status RegisterDbPaths(const std::vector<std::string>& paths) override {
|
||||
return target_->RegisterDbPaths(paths);
|
||||
return target_.env->RegisterDbPaths(paths);
|
||||
}
|
||||
|
||||
Status UnregisterDbPaths(const std::vector<std::string>& paths) override {
|
||||
return target_->UnregisterDbPaths(paths);
|
||||
return target_.env->UnregisterDbPaths(paths);
|
||||
}
|
||||
|
||||
Status NewSequentialFile(const std::string& f,
|
||||
std::unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& options) override {
|
||||
return target_->NewSequentialFile(f, r, options);
|
||||
return target_.env->NewSequentialFile(f, r, options);
|
||||
}
|
||||
Status NewRandomAccessFile(const std::string& f,
|
||||
std::unique_ptr<RandomAccessFile>* r,
|
||||
const EnvOptions& options) override {
|
||||
return target_->NewRandomAccessFile(f, r, options);
|
||||
return target_.env->NewRandomAccessFile(f, r, options);
|
||||
}
|
||||
Status NewWritableFile(const std::string& f, std::unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& options) override {
|
||||
return target_->NewWritableFile(f, r, options);
|
||||
return target_.env->NewWritableFile(f, r, options);
|
||||
}
|
||||
Status ReopenWritableFile(const std::string& fname,
|
||||
std::unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& options) override {
|
||||
return target_->ReopenWritableFile(fname, result, options);
|
||||
return target_.env->ReopenWritableFile(fname, result, options);
|
||||
}
|
||||
Status ReuseWritableFile(const std::string& fname,
|
||||
const std::string& old_fname,
|
||||
std::unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& options) override {
|
||||
return target_->ReuseWritableFile(fname, old_fname, r, options);
|
||||
return target_.env->ReuseWritableFile(fname, old_fname, r, options);
|
||||
}
|
||||
Status NewRandomRWFile(const std::string& fname,
|
||||
std::unique_ptr<RandomRWFile>* result,
|
||||
const EnvOptions& options) override {
|
||||
return target_->NewRandomRWFile(fname, result, options);
|
||||
return target_.env->NewRandomRWFile(fname, result, options);
|
||||
}
|
||||
Status NewMemoryMappedFileBuffer(
|
||||
const std::string& fname,
|
||||
std::unique_ptr<MemoryMappedFileBuffer>* result) override {
|
||||
return target_->NewMemoryMappedFileBuffer(fname, result);
|
||||
return target_.env->NewMemoryMappedFileBuffer(fname, result);
|
||||
}
|
||||
Status NewDirectory(const std::string& name,
|
||||
std::unique_ptr<Directory>* result) override {
|
||||
return target_->NewDirectory(name, result);
|
||||
return target_.env->NewDirectory(name, result);
|
||||
}
|
||||
Status FileExists(const std::string& f) override {
|
||||
return target_->FileExists(f);
|
||||
return target_.env->FileExists(f);
|
||||
}
|
||||
Status GetChildren(const std::string& dir,
|
||||
std::vector<std::string>* r) override {
|
||||
return target_->GetChildren(dir, r);
|
||||
return target_.env->GetChildren(dir, r);
|
||||
}
|
||||
Status GetChildrenFileAttributes(
|
||||
const std::string& dir, std::vector<FileAttributes>* result) override {
|
||||
return target_->GetChildrenFileAttributes(dir, result);
|
||||
return target_.env->GetChildrenFileAttributes(dir, result);
|
||||
}
|
||||
Status DeleteFile(const std::string& f) override {
|
||||
return target_->DeleteFile(f);
|
||||
return target_.env->DeleteFile(f);
|
||||
}
|
||||
Status Truncate(const std::string& fname, size_t size) override {
|
||||
return target_->Truncate(fname, size);
|
||||
return target_.env->Truncate(fname, size);
|
||||
}
|
||||
Status CreateDir(const std::string& d) override {
|
||||
return target_->CreateDir(d);
|
||||
return target_.env->CreateDir(d);
|
||||
}
|
||||
Status CreateDirIfMissing(const std::string& d) override {
|
||||
return target_->CreateDirIfMissing(d);
|
||||
return target_.env->CreateDirIfMissing(d);
|
||||
}
|
||||
Status DeleteDir(const std::string& d) override {
|
||||
return target_->DeleteDir(d);
|
||||
return target_.env->DeleteDir(d);
|
||||
}
|
||||
Status GetFileSize(const std::string& f, uint64_t* s) override {
|
||||
return target_->GetFileSize(f, s);
|
||||
return target_.env->GetFileSize(f, s);
|
||||
}
|
||||
|
||||
Status GetFileModificationTime(const std::string& fname,
|
||||
uint64_t* file_mtime) override {
|
||||
return target_->GetFileModificationTime(fname, file_mtime);
|
||||
return target_.env->GetFileModificationTime(fname, file_mtime);
|
||||
}
|
||||
|
||||
Status RenameFile(const std::string& s, const std::string& t) override {
|
||||
return target_->RenameFile(s, t);
|
||||
return target_.env->RenameFile(s, t);
|
||||
}
|
||||
|
||||
Status LinkFile(const std::string& s, const std::string& t) override {
|
||||
return target_->LinkFile(s, t);
|
||||
return target_.env->LinkFile(s, t);
|
||||
}
|
||||
|
||||
Status NumFileLinks(const std::string& fname, uint64_t* count) override {
|
||||
return target_->NumFileLinks(fname, count);
|
||||
return target_.env->NumFileLinks(fname, count);
|
||||
}
|
||||
|
||||
Status AreFilesSame(const std::string& first, const std::string& second,
|
||||
bool* res) override {
|
||||
return target_->AreFilesSame(first, second, res);
|
||||
return target_.env->AreFilesSame(first, second, res);
|
||||
}
|
||||
|
||||
Status LockFile(const std::string& f, FileLock** l) override {
|
||||
return target_->LockFile(f, l);
|
||||
return target_.env->LockFile(f, l);
|
||||
}
|
||||
|
||||
Status UnlockFile(FileLock* l) override { return target_->UnlockFile(l); }
|
||||
Status UnlockFile(FileLock* l) override { return target_.env->UnlockFile(l); }
|
||||
|
||||
Status IsDirectory(const std::string& path, bool* is_dir) override {
|
||||
return target_->IsDirectory(path, is_dir);
|
||||
return target_.env->IsDirectory(path, is_dir);
|
||||
}
|
||||
|
||||
Status LoadLibrary(const std::string& lib_name,
|
||||
const std::string& search_path,
|
||||
std::shared_ptr<DynamicLibrary>* result) override {
|
||||
return target_->LoadLibrary(lib_name, search_path, result);
|
||||
return target_.env->LoadLibrary(lib_name, search_path, result);
|
||||
}
|
||||
|
||||
void Schedule(void (*f)(void* arg), void* a, Priority pri,
|
||||
void* tag = nullptr, void (*u)(void* arg) = nullptr) override {
|
||||
return target_->Schedule(f, a, pri, tag, u);
|
||||
return target_.env->Schedule(f, a, pri, tag, u);
|
||||
}
|
||||
|
||||
int UnSchedule(void* tag, Priority pri) override {
|
||||
return target_->UnSchedule(tag, pri);
|
||||
return target_.env->UnSchedule(tag, pri);
|
||||
}
|
||||
|
||||
void StartThread(void (*f)(void*), void* a) override {
|
||||
return target_->StartThread(f, a);
|
||||
return target_.env->StartThread(f, a);
|
||||
}
|
||||
void WaitForJoin() override { return target_->WaitForJoin(); }
|
||||
void WaitForJoin() override { return target_.env->WaitForJoin(); }
|
||||
unsigned int GetThreadPoolQueueLen(Priority pri = LOW) const override {
|
||||
return target_->GetThreadPoolQueueLen(pri);
|
||||
return target_.env->GetThreadPoolQueueLen(pri);
|
||||
}
|
||||
Status GetTestDirectory(std::string* path) override {
|
||||
return target_->GetTestDirectory(path);
|
||||
return target_.env->GetTestDirectory(path);
|
||||
}
|
||||
Status NewLogger(const std::string& fname,
|
||||
std::shared_ptr<Logger>* result) override {
|
||||
return target_->NewLogger(fname, result);
|
||||
return target_.env->NewLogger(fname, result);
|
||||
}
|
||||
uint64_t NowMicros() override { return target_->NowMicros(); }
|
||||
uint64_t NowNanos() override { return target_->NowNanos(); }
|
||||
uint64_t NowCPUNanos() override { return target_->NowCPUNanos(); }
|
||||
uint64_t NowMicros() override { return target_.env->NowMicros(); }
|
||||
uint64_t NowNanos() override { return target_.env->NowNanos(); }
|
||||
uint64_t NowCPUNanos() override { return target_.env->NowCPUNanos(); }
|
||||
|
||||
void SleepForMicroseconds(int micros) override {
|
||||
target_->SleepForMicroseconds(micros);
|
||||
target_.env->SleepForMicroseconds(micros);
|
||||
}
|
||||
Status GetHostName(char* name, uint64_t len) override {
|
||||
return target_->GetHostName(name, len);
|
||||
return target_.env->GetHostName(name, len);
|
||||
}
|
||||
Status GetCurrentTime(int64_t* unix_time) override {
|
||||
return target_->GetCurrentTime(unix_time);
|
||||
return target_.env->GetCurrentTime(unix_time);
|
||||
}
|
||||
Status GetAbsolutePath(const std::string& db_path,
|
||||
std::string* output_path) override {
|
||||
return target_->GetAbsolutePath(db_path, output_path);
|
||||
return target_.env->GetAbsolutePath(db_path, output_path);
|
||||
}
|
||||
void SetBackgroundThreads(int num, Priority pri) override {
|
||||
return target_->SetBackgroundThreads(num, pri);
|
||||
return target_.env->SetBackgroundThreads(num, pri);
|
||||
}
|
||||
int GetBackgroundThreads(Priority pri) override {
|
||||
return target_->GetBackgroundThreads(pri);
|
||||
return target_.env->GetBackgroundThreads(pri);
|
||||
}
|
||||
|
||||
Status SetAllowNonOwnerAccess(bool allow_non_owner_access) override {
|
||||
return target_->SetAllowNonOwnerAccess(allow_non_owner_access);
|
||||
return target_.env->SetAllowNonOwnerAccess(allow_non_owner_access);
|
||||
}
|
||||
|
||||
void IncBackgroundThreadsIfNeeded(int num, Priority pri) override {
|
||||
return target_->IncBackgroundThreadsIfNeeded(num, pri);
|
||||
return target_.env->IncBackgroundThreadsIfNeeded(num, pri);
|
||||
}
|
||||
|
||||
void LowerThreadPoolIOPriority(Priority pool) override {
|
||||
target_->LowerThreadPoolIOPriority(pool);
|
||||
target_.env->LowerThreadPoolIOPriority(pool);
|
||||
}
|
||||
|
||||
void LowerThreadPoolCPUPriority(Priority pool) override {
|
||||
target_->LowerThreadPoolCPUPriority(pool);
|
||||
target_.env->LowerThreadPoolCPUPriority(pool);
|
||||
}
|
||||
|
||||
Status LowerThreadPoolCPUPriority(Priority pool, CpuPriority pri) override {
|
||||
return target_->LowerThreadPoolCPUPriority(pool, pri);
|
||||
return target_.env->LowerThreadPoolCPUPriority(pool, pri);
|
||||
}
|
||||
|
||||
std::string TimeToString(uint64_t time) override {
|
||||
return target_->TimeToString(time);
|
||||
return target_.env->TimeToString(time);
|
||||
}
|
||||
|
||||
Status GetThreadList(std::vector<ThreadStatus>* thread_list) override {
|
||||
return target_->GetThreadList(thread_list);
|
||||
return target_.env->GetThreadList(thread_list);
|
||||
}
|
||||
|
||||
ThreadStatusUpdater* GetThreadStatusUpdater() const override {
|
||||
return target_->GetThreadStatusUpdater();
|
||||
return target_.env->GetThreadStatusUpdater();
|
||||
}
|
||||
|
||||
uint64_t GetThreadID() const override { return target_->GetThreadID(); }
|
||||
uint64_t GetThreadID() const override { return target_.env->GetThreadID(); }
|
||||
|
||||
std::string GenerateUniqueId() override {
|
||||
return target_->GenerateUniqueId();
|
||||
return target_.env->GenerateUniqueId();
|
||||
}
|
||||
|
||||
EnvOptions OptimizeForLogRead(const EnvOptions& env_options) const override {
|
||||
return target_->OptimizeForLogRead(env_options);
|
||||
return target_.env->OptimizeForLogRead(env_options);
|
||||
}
|
||||
EnvOptions OptimizeForManifestRead(
|
||||
const EnvOptions& env_options) const override {
|
||||
return target_->OptimizeForManifestRead(env_options);
|
||||
return target_.env->OptimizeForManifestRead(env_options);
|
||||
}
|
||||
EnvOptions OptimizeForLogWrite(const EnvOptions& env_options,
|
||||
const DBOptions& db_options) const override {
|
||||
return target_->OptimizeForLogWrite(env_options, db_options);
|
||||
return target_.env->OptimizeForLogWrite(env_options, db_options);
|
||||
}
|
||||
EnvOptions OptimizeForManifestWrite(
|
||||
const EnvOptions& env_options) const override {
|
||||
return target_->OptimizeForManifestWrite(env_options);
|
||||
return target_.env->OptimizeForManifestWrite(env_options);
|
||||
}
|
||||
EnvOptions OptimizeForCompactionTableWrite(
|
||||
const EnvOptions& env_options,
|
||||
const ImmutableDBOptions& immutable_ops) const override {
|
||||
return target_->OptimizeForCompactionTableWrite(env_options, immutable_ops);
|
||||
return target_.env->OptimizeForCompactionTableWrite(env_options,
|
||||
immutable_ops);
|
||||
}
|
||||
EnvOptions OptimizeForCompactionTableRead(
|
||||
const EnvOptions& env_options,
|
||||
const ImmutableDBOptions& db_options) const override {
|
||||
return target_->OptimizeForCompactionTableRead(env_options, db_options);
|
||||
return target_.env->OptimizeForCompactionTableRead(env_options, db_options);
|
||||
}
|
||||
EnvOptions OptimizeForBlobFileRead(
|
||||
const EnvOptions& env_options,
|
||||
const ImmutableDBOptions& db_options) const override {
|
||||
return target_->OptimizeForBlobFileRead(env_options, db_options);
|
||||
return target_.env->OptimizeForBlobFileRead(env_options, db_options);
|
||||
}
|
||||
Status GetFreeSpace(const std::string& path, uint64_t* diskfree) override {
|
||||
return target_->GetFreeSpace(path, diskfree);
|
||||
return target_.env->GetFreeSpace(path, diskfree);
|
||||
}
|
||||
void SanitizeEnvOptions(EnvOptions* env_opts) const override {
|
||||
target_->SanitizeEnvOptions(env_opts);
|
||||
target_.env->SanitizeEnvOptions(env_opts);
|
||||
}
|
||||
Status PrepareOptions(const ConfigOptions& options) override;
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::string SerializeOptions(const ConfigOptions& config_options,
|
||||
const std::string& header) const override;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
private:
|
||||
Env* target_;
|
||||
Target target_;
|
||||
};
|
||||
|
||||
class SequentialFileWrapper : public SequentialFile {
|
||||
|
||||
@@ -74,7 +74,7 @@ class FileChecksumGenerator {
|
||||
// including data loss, unreported corruption, deadlocks, and more.
|
||||
class FileChecksumGenFactory : public Customizable {
|
||||
public:
|
||||
virtual ~FileChecksumGenFactory() {}
|
||||
~FileChecksumGenFactory() override {}
|
||||
static const char* Type() { return "FileChecksumGenFactory"; }
|
||||
static Status CreateFromString(
|
||||
const ConfigOptions& options, const std::string& value,
|
||||
@@ -85,7 +85,7 @@ class FileChecksumGenFactory : public Customizable {
|
||||
const FileChecksumGenContext& context) = 0;
|
||||
|
||||
// Return the name of this FileChecksumGenFactory.
|
||||
virtual const char* Name() const override = 0;
|
||||
const char* Name() const override = 0;
|
||||
};
|
||||
|
||||
// FileChecksumList stores the checksum information of a list of files (e.g.,
|
||||
|
||||
@@ -177,6 +177,9 @@ class FilterPolicy {
|
||||
// passed to methods of this type.
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
// DEPRECATED: This function is part of the deprecated block-based
|
||||
// filter, which will be removed in a future release.
|
||||
//
|
||||
// keys[0,n-1] contains a list of keys (potentially with duplicates)
|
||||
// that are ordered according to the user supplied comparator.
|
||||
// Append a filter that summarizes keys[0,n-1] to *dst.
|
||||
@@ -186,6 +189,9 @@ class FilterPolicy {
|
||||
virtual void CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const = 0;
|
||||
|
||||
// DEPRECATED: This function is part of the deprecated block-based
|
||||
// filter, which will be removed in a future release.
|
||||
//
|
||||
// "filter" contains the data appended by a preceding call to
|
||||
// CreateFilter() on this class. This method must return true if
|
||||
// the key was in the list of keys passed to CreateFilter().
|
||||
@@ -198,6 +204,7 @@ class FilterPolicy {
|
||||
// NOTE: This function is only called by GetBuilderWithContext() below for
|
||||
// custom FilterPolicy implementations. Thus, it is not necessary to
|
||||
// override this function if overriding GetBuilderWithContext().
|
||||
// DEPRECATED: This function will be removed in a future release.
|
||||
virtual FilterBitsBuilder* GetFilterBitsBuilder() const { return nullptr; }
|
||||
|
||||
// A newer variant of GetFilterBitsBuilder that allows a FilterPolicy
|
||||
@@ -282,7 +289,7 @@ extern const FilterPolicy* NewBloomFilterPolicy(
|
||||
extern const FilterPolicy* NewRibbonFilterPolicy(
|
||||
double bloom_equivalent_bits_per_key, int bloom_before_level = 0);
|
||||
|
||||
// Old name and old default behavior
|
||||
// Old name and old default behavior (DEPRECATED)
|
||||
inline const FilterPolicy* NewExperimentalRibbonFilterPolicy(
|
||||
double bloom_equivalent_bits_per_key) {
|
||||
return NewRibbonFilterPolicy(bloom_equivalent_bits_per_key, -1);
|
||||
|
||||
@@ -92,6 +92,10 @@ class Iterator : public Cleanable {
|
||||
// If supported, renew the iterator to represent the latest state. The
|
||||
// iterator will be invalidated after the call. Not supported if
|
||||
// ReadOptions.snapshot is given when creating the iterator.
|
||||
//
|
||||
// WARNING: Do not use `Iterator::Refresh()` API on DBs where `DeleteRange()`
|
||||
// has been used or will be used. This feature combination is neither
|
||||
// supported nor programmatically prevented.
|
||||
virtual Status Refresh() {
|
||||
return Status::NotSupported("Refresh() is not supported");
|
||||
}
|
||||
|
||||
@@ -706,7 +706,9 @@ class EventListener : public Customizable {
|
||||
// is recovered from read-only mode after an error. When this is called, it
|
||||
// means normal writes to the database can be issued and the user can
|
||||
// initiate any further recovery actions needed
|
||||
virtual void OnErrorRecoveryCompleted(Status /* old_bg_error */) {}
|
||||
virtual void OnErrorRecoveryCompleted(Status old_bg_error) {
|
||||
old_bg_error.PermitUncheckedError();
|
||||
}
|
||||
|
||||
// A callback function for RocksDB which will be called once the recovery
|
||||
// attempt from a background retryable error is completed. The recovery
|
||||
@@ -748,7 +750,7 @@ class EventListener : public Customizable {
|
||||
// happens. ShouldBeNotifiedOnFileIO should be set to true to get a callback.
|
||||
virtual void OnIOError(const IOErrorInfo& /*info*/) {}
|
||||
|
||||
virtual ~EventListener() {}
|
||||
~EventListener() override {}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
@@ -294,7 +294,7 @@ class MemTableRep {
|
||||
// new MemTableRep objects
|
||||
class MemTableRepFactory : public Customizable {
|
||||
public:
|
||||
virtual ~MemTableRepFactory() {}
|
||||
~MemTableRepFactory() override {}
|
||||
|
||||
static const char* Type() { return "MemTableRepFactory"; }
|
||||
static Status CreateFromString(const ConfigOptions& config_options,
|
||||
@@ -311,7 +311,7 @@ class MemTableRepFactory : public Customizable {
|
||||
return CreateMemTableRep(key_cmp, allocator, slice_transform, logger);
|
||||
}
|
||||
|
||||
virtual const char* Name() const override = 0;
|
||||
const char* Name() const override = 0;
|
||||
|
||||
// Return true if the current MemTableRep supports concurrent inserts
|
||||
// Default: false
|
||||
|
||||
@@ -513,7 +513,7 @@ struct DBOptions {
|
||||
// Default: Env::Default()
|
||||
Env* env = Env::Default();
|
||||
|
||||
// Use to control write rate of flush and compaction. Flush has higher
|
||||
// Use to control write/read rate of flush and compaction. Flush has higher
|
||||
// priority than compaction. Rate limiting is disabled if nullptr.
|
||||
// If rate limiter is enabled, bytes_per_sync is set to 1MB by default.
|
||||
// Default: nullptr
|
||||
@@ -772,7 +772,9 @@ struct DBOptions {
|
||||
// large amounts of data (such as xfs's allocsize option).
|
||||
size_t manifest_preallocation_size = 4 * 1024 * 1024;
|
||||
|
||||
// Allow the OS to mmap file for reading sst tables. Default: false
|
||||
// Allow the OS to mmap file for reading sst tables.
|
||||
// Not recommended for 32-bit OS.
|
||||
// Default: false
|
||||
bool allow_mmap_reads = false;
|
||||
|
||||
// Allow the OS to mmap file for writing.
|
||||
@@ -1365,7 +1367,11 @@ struct Options : public DBOptions, public ColumnFamilyOptions {
|
||||
const ColumnFamilyOptions& column_family_options)
|
||||
: DBOptions(db_options), ColumnFamilyOptions(column_family_options) {}
|
||||
|
||||
// The function recovers options to the option as in version 4.6.
|
||||
// Change to some default settings from an older version.
|
||||
// NOT MAINTAINED: This function has not been and is not maintained.
|
||||
// DEPRECATED: This function might be removed in a future release.
|
||||
// In general, defaults are changed to suit broad interests. Opting
|
||||
// out of a change on upgrade should be deliberate and considered.
|
||||
Options* OldDefaults(int rocksdb_major_version = 4,
|
||||
int rocksdb_minor_version = 6);
|
||||
|
||||
@@ -1388,6 +1394,12 @@ struct Options : public DBOptions, public ColumnFamilyOptions {
|
||||
// Use this if your DB is very small (like under 1GB) and you don't want to
|
||||
// spend lots of memory for memtables.
|
||||
Options* OptimizeForSmallDb();
|
||||
|
||||
// Disable some checks that should not be necessary in the absence of
|
||||
// software logic errors or CPU+memory hardware errors. This can improve
|
||||
// write speeds but is only recommended for temporary use. Does not
|
||||
// change protection against corrupt storage (e.g. verify_checksums).
|
||||
Options* DisableExtraChecks();
|
||||
};
|
||||
|
||||
//
|
||||
@@ -1851,7 +1863,13 @@ enum TraceFilterType : uint64_t {
|
||||
// Do not trace the get operations
|
||||
kTraceFilterGet = 0x1 << 0,
|
||||
// Do not trace the write operations
|
||||
kTraceFilterWrite = 0x1 << 1
|
||||
kTraceFilterWrite = 0x1 << 1,
|
||||
// Do not trace the `Iterator::Seek()` operations
|
||||
kTraceFilterIteratorSeek = 0x1 << 2,
|
||||
// Do not trace the `Iterator::SeekForPrev()` operations
|
||||
kTraceFilterIteratorSeekForPrev = 0x1 << 3,
|
||||
// Do not trace the `MultiGet()` operations
|
||||
kTraceFilterMultiGet = 0x1 << 4,
|
||||
};
|
||||
|
||||
// TraceOptions is used for StartTrace
|
||||
@@ -1864,6 +1882,13 @@ struct TraceOptions {
|
||||
uint64_t sampling_frequency = 1;
|
||||
// Note: The filtering happens before sampling.
|
||||
uint64_t filter = kTraceFilterNone;
|
||||
// When true, the order of write records in the trace will match the order of
|
||||
// the corresponding write records in the WAL and applied to the DB. There may
|
||||
// be a performance penalty associated with preserving this ordering.
|
||||
//
|
||||
// Default: false. This means write records in the trace may be in an order
|
||||
// different from the WAL's order.
|
||||
bool preserve_write_order = false;
|
||||
};
|
||||
|
||||
// ImportColumnFamilyOptions is used by ImportColumnFamily()
|
||||
|
||||
@@ -59,13 +59,15 @@ class RateLimiter : public Customizable {
|
||||
// Request for token for bytes. If this request can not be satisfied, the call
|
||||
// is blocked. Caller is responsible to make sure
|
||||
// bytes <= GetSingleBurstBytes()
|
||||
// and bytes >= 0.
|
||||
virtual void Request(const int64_t /*bytes*/, const Env::IOPriority /*pri*/) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Request for token for bytes and potentially update statistics. If this
|
||||
// request can not be satisfied, the call is blocked. Caller is responsible to
|
||||
// make sure bytes <= GetSingleBurstBytes().
|
||||
// make sure bytes <= GetSingleBurstBytes()
|
||||
// and bytes >= 0.
|
||||
virtual void Request(const int64_t bytes, const Env::IOPriority pri,
|
||||
Statistics* /* stats */) {
|
||||
// For API compatibility, default implementation calls the older API in
|
||||
|
||||
@@ -83,7 +83,7 @@ class SstPartitioner {
|
||||
// including data loss, unreported corruption, deadlocks, and more.
|
||||
class SstPartitionerFactory : public Customizable {
|
||||
public:
|
||||
virtual ~SstPartitionerFactory() {}
|
||||
~SstPartitionerFactory() override {}
|
||||
static const char* Type() { return "SstPartitionerFactory"; }
|
||||
static Status CreateFromString(
|
||||
const ConfigOptions& options, const std::string& value,
|
||||
@@ -93,7 +93,7 @@ class SstPartitionerFactory : public Customizable {
|
||||
const SstPartitioner::Context& context) const = 0;
|
||||
|
||||
// Returns a name that identifies this partitioner factory.
|
||||
virtual const char* Name() const override = 0;
|
||||
const char* Name() const override = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -124,7 +124,7 @@ class SstPartitionerFixedPrefixFactory : public SstPartitionerFactory {
|
||||
public:
|
||||
explicit SstPartitionerFixedPrefixFactory(size_t len);
|
||||
|
||||
virtual ~SstPartitionerFixedPrefixFactory() {}
|
||||
~SstPartitionerFixedPrefixFactory() override {}
|
||||
|
||||
static const char* kClassName() { return "SstPartitionerFixedPrefixFactory"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
Loaded 100 of 196 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user