mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79f08d7ffa | ||
|
|
da11a59034 | ||
|
|
13a03a5452 | ||
|
|
2e7a386229 | ||
|
|
92ea271a6a | ||
|
|
e3a1707d05 | ||
|
|
f4790bdd1b | ||
|
|
de91718e99 | ||
|
|
6a385e90cd | ||
|
|
aadbf86b6c | ||
|
|
00191c84e6 | ||
|
|
02f6ebb7d4 | ||
|
|
ccaadd8705 | ||
|
|
7629bf17fe | ||
|
|
9f0a64c4f1 | ||
|
|
94a71b686e | ||
|
|
bcd86d9074 | ||
|
|
96a2453d7a | ||
|
|
32ad0dcafe | ||
|
|
4f37eb4db2 | ||
|
|
446a152aa6 | ||
|
|
947c5fd441 | ||
|
|
f94fce8623 | ||
|
|
1a2781d48c | ||
|
|
2e9a9f04d7 | ||
|
|
e40bbc57db | ||
|
|
82f1c1418b | ||
|
|
39af4e9565 | ||
|
|
b7f2164a6f | ||
|
|
529efcc5b2 | ||
|
|
d6bb43202e | ||
|
|
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
+58
-24
@@ -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:
|
||||
@@ -41,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: |
|
||||
@@ -58,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.7.8"
|
||||
- pre-steps
|
||||
|
||||
post-steps:
|
||||
steps:
|
||||
@@ -90,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:
|
||||
@@ -154,10 +156,9 @@ jobs:
|
||||
xcode: 12.5.1
|
||||
resource_class: large
|
||||
environment:
|
||||
DISABLE_JEMALLOC: 1 # jemalloc cause env_test hang, disable it for now
|
||||
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
|
||||
@@ -169,7 +170,6 @@ jobs:
|
||||
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
|
||||
@@ -405,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:
|
||||
@@ -535,10 +545,9 @@ jobs:
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
|
||||
DISABLE_JEMALLOC: 1 # jemalloc causes java 8 crash, maybe a known issue
|
||||
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
|
||||
@@ -565,10 +574,9 @@ jobs:
|
||||
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-jdk8-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-jdk8-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
@@ -578,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:
|
||||
@@ -784,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
|
||||
@@ -804,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
|
||||
|
||||
+20
-21
@@ -321,7 +321,8 @@ if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
if (NOT PORTABLE OR FORCE_SSE42)
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
@@ -333,11 +334,12 @@ int main() {
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check if -latomic is required or not
|
||||
@@ -352,25 +354,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 +618,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.
|
||||
|
||||
|
||||
+59
-3
@@ -1,15 +1,68 @@
|
||||
# Rocksdb Change Log
|
||||
## 6.28.2 (2022-01-31)
|
||||
## 6.29.5 (03/29/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a race condition for `alive_log_files_` in non-two-write-queues mode. The race is between the write_thread_ in WriteToWAL() and another thread executing `FindObsoleteFiles()`. The race condition will be caught if `__glibcxx_requires_nonempty` is enabled.
|
||||
* Fixed a race condition when mmaping a WritableFile on POSIX.
|
||||
* Fixed a race condition when 2PC is disabled and WAL tracking in the MANIFEST is enabled. The race condition is between two background flush threads trying to install flush results, causing a WAL deletion not tracked in the MANIFEST. A future DB open may fail.
|
||||
* Fixed a heap use-after-free race with DropColumnFamily.
|
||||
* Fixed a bug that `rocksdb.read.block.compaction.micros` cannot track compaction stats (#9722).
|
||||
|
||||
## 6.29.4 (03/22/2022)
|
||||
### Bug Fixes
|
||||
* Fixed a bug caused by race among flush, incoming writes and taking snapshots. Queries to snapshots created with these race condition can return incorrect result, e.g. resurfacing deleted data.
|
||||
* Fixed a bug that DisableManualCompaction may assert when disable an unscheduled manual compaction.
|
||||
* Fixed a bug that `Iterator::Refresh()` reads stale keys after DeleteRange() performed.
|
||||
* Fixed a race condition when disable and re-enable manual compaction.
|
||||
* Fix a race condition when cancel manual compaction with `DisableManualCompaction`. Also DB close can cancel the manual compaction thread.
|
||||
* Fixed a data race on `versions_` between `DBImpl::ResumeImpl()` and threads waiting for recovery to complete (#9496)
|
||||
* Fixed a read-after-free bug in `DB::GetMergeOperands()`.
|
||||
* Fixed NUM_INDEX_AND_FILTER_BLOCKS_READ_PER_LEVEL, NUM_DATA_BLOCKS_READ_PER_LEVEL, and NUM_SST_READ_PER_LEVEL stats to be reported once per MultiGet batch per level.
|
||||
|
||||
## 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.28.1 (2022-01-10)
|
||||
## 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
|
||||
* Fixed compilation errors on newer compiler, e.g. clang-12
|
||||
* 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.
|
||||
@@ -18,6 +71,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.
|
||||
@@ -25,6 +79,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 -DHAVE_AVX2, $(PLATFORM_CFLAGS))
|
||||
PLATFORM_CXXFLAGS := $(filter-out -march=native -DHAVE_SSE42 -DHAVE_AVX2, $(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
|
||||
@@ -2070,7 +2088,7 @@ SHA256_CMD = sha256sum
|
||||
|
||||
ZLIB_VER ?= 1.2.11
|
||||
ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
ZLIB_DOWNLOAD_BASE ?= https://zlib.net/fossils
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
@@ -2086,7 +2104,15 @@ 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-arm64.jnilib
|
||||
else ifeq ($(MACHINE),x86_64)
|
||||
ROCKSDBJNILIB = librocksdbjni-osx-x86_64.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 +2121,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 +2156,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 +2174,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 +2189,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 +2203,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 +2217,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 +2239,42 @@ endif
|
||||
$(MAKE) rocksdbjavastatic_deps
|
||||
$(MAKE) rocksdbjavastatic_libobjects
|
||||
$(MAKE) rocksdbjavastatic_javalib
|
||||
$(MAKE) rocksdbjava_jar
|
||||
|
||||
rocksdbjavastaticosx: rocksdbjavastaticosx_archs
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR) librocksdbjni-osx-x86_64.jnilib librocksdbjni-osx-arm64.jnilib
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
|
||||
rocksdbjavastaticosx_ub: rocksdbjavastaticosx_archs
|
||||
cd java/target; lipo -create -output librocksdbjni-osx.jnilib librocksdbjni-osx-x86_64.jnilib librocksdbjni-osx-arm64.jnilib
|
||||
cd java; $(JAR_CMD) -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target; $(JAR_CMD) -uf $(ROCKSDB_JAR) librocksdbjni-osx.jnilib
|
||||
cd java/target/classes; $(JAR_CMD) -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
|
||||
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,30 +2285,36 @@ 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
|
||||
|
||||
rocksdbjava_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
|
||||
openssl sha1 java/target/$(ROCKSDB_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAR).sha1
|
||||
|
||||
rocksdbjava_javadocs_jar:
|
||||
cd java/target/apidocs; $(JAR_CMD) -cf ../$(ROCKSDB_JAVADOCS_JAR) *
|
||||
openssl sha1 java/target/$(ROCKSDB_JAVADOCS_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAVADOCS_JAR).sha1
|
||||
|
||||
rocksdbjava_sources_jar:
|
||||
cd java/src/main/java; $(JAR_CMD) -cf ../../../target/$(ROCKSDB_SOURCES_JAR) org
|
||||
openssl sha1 java/target/$(ROCKSDB_SOURCES_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_SOURCES_JAR).sha1
|
||||
|
||||
rocksdbjavastatic_deps: $(JAVA_COMPRESSIONS)
|
||||
|
||||
rocksdbjavastatic_libobjects: $(LIB_OBJECTS)
|
||||
|
||||
rocksdbjavastaticrelease: rocksdbjavastatic
|
||||
rocksdbjavastaticrelease: rocksdbjavastaticosx rocksdbjava_javadocs_jar rocksdbjava_sources_jar
|
||||
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 rocksdbjava_javadocs_jar rocksdbjava_sources_jar
|
||||
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 +2333,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 +2377,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 +2393,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()
|
||||
+47
-22
@@ -58,30 +58,55 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
while (true) {
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
// Refresh range-tombstones in MemTable
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
SuperVersion* sv = cfd_->GetThreadLocalSuperVersion(db_impl_);
|
||||
ReadRangeDelAggregator* range_del_agg =
|
||||
db_iter_->GetRangeDelAggregator();
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter;
|
||||
range_del_iter.reset(
|
||||
sv->mem->NewRangeTombstoneIterator(read_options_, latest_seq));
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
cfd_->ReturnThreadLocalSuperVersion(sv);
|
||||
}
|
||||
// Refresh latest sequence number
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_valid(false);
|
||||
// Check again if the latest super version number is changed
|
||||
uint64_t latest_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (latest_sv_number != cur_sv_number) {
|
||||
// If the super version number is changed after refreshing,
|
||||
// fallback to Re-Init the InternalIterator
|
||||
cur_sv_number = latest_sv_number;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(db_impl_->GetLatestSequenceNumber());
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
|
||||
+80
-7
@@ -7,12 +7,13 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE // Lite does not support C API
|
||||
|
||||
#include "rocksdb/c.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "rocksdb/c.h"
|
||||
#ifndef OS_WIN
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -89,10 +90,8 @@ static void CheckEqual(const char* expected, const char* v, size_t n) {
|
||||
// ok
|
||||
return;
|
||||
} else {
|
||||
fprintf(stderr, "%s: expected '%s', got '%s'\n",
|
||||
phase,
|
||||
(expected ? expected : "(null)"),
|
||||
(v ? v : "(null"));
|
||||
fprintf(stderr, "%s: expected '%s', got '%s'\n", phase,
|
||||
(expected ? expected : "(null)"), (v ? v : "(null)"));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -1019,7 +1018,36 @@ int main(int argc, char** argv) {
|
||||
CheckGet(db, roptions, "foo", NULL);
|
||||
rocksdb_release_snapshot(db, snap);
|
||||
}
|
||||
|
||||
StartPhase("snapshot_with_memtable_inplace_update");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
const rocksdb_snapshot_t* snap = NULL;
|
||||
const char* s_key = "foo_snap";
|
||||
const char* value1 = "hello_s1";
|
||||
const char* value2 = "hello_s2";
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
|
||||
rocksdb_options_set_inplace_update_support(options, 1);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, s_key, 8, value1, 8, &err);
|
||||
snap = rocksdb_create_snapshot(db);
|
||||
assert(snap != NULL);
|
||||
rocksdb_put(db, woptions, s_key, 8, value2, 8, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_readoptions_set_snapshot(roptions, snap);
|
||||
CheckGet(db, roptions, "foo", NULL);
|
||||
// snapshot syntax is invalid, because of inplace update supported is set
|
||||
CheckGet(db, roptions, s_key, value2);
|
||||
// restore the data and options
|
||||
rocksdb_delete(db, woptions, s_key, 8, &err);
|
||||
CheckGet(db, roptions, s_key, NULL);
|
||||
rocksdb_release_snapshot(db, snap);
|
||||
rocksdb_readoptions_set_snapshot(roptions, NULL);
|
||||
rocksdb_options_set_inplace_update_support(options, 0);
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 1);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
StartPhase("repair");
|
||||
{
|
||||
// If we do not compact here, then the lazy deletion of
|
||||
@@ -2956,6 +2984,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);
|
||||
|
||||
|
||||
@@ -1562,20 +1562,6 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
return new_cfd;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
void ColumnFamilySet::FreeDeadColumnFamilies() {
|
||||
autovector<ColumnFamilyData*> to_delete;
|
||||
for (auto cfd = dummy_cfd_->next_; cfd != dummy_cfd_; cfd = cfd->next_) {
|
||||
if (cfd->refs_.load(std::memory_order_relaxed) == 0) {
|
||||
to_delete.push_back(cfd);
|
||||
}
|
||||
}
|
||||
for (auto cfd : to_delete) {
|
||||
// this is very rare, so it's not a problem that we do it under a mutex
|
||||
delete cfd;
|
||||
}
|
||||
}
|
||||
|
||||
// under a DB mutex AND from a write thread
|
||||
void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
|
||||
auto cfd_iter = column_family_data_.find(cfd->GetID());
|
||||
|
||||
+56
-17
@@ -519,9 +519,10 @@ class ColumnFamilyData {
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
WriteBufferManager* write_buffer_mgr() { return write_buffer_manager_; }
|
||||
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
static const uint32_t kDummyColumnFamilyDataId;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
@@ -627,10 +628,8 @@ class ColumnFamilyData {
|
||||
// held and it needs to be executed from the write thread. SetDropped() also
|
||||
// guarantees that it will be called only from single-threaded LogAndApply(),
|
||||
// but this condition is not that important.
|
||||
// * Iteration -- hold DB mutex, but you can release it in the body of
|
||||
// iteration. If you release DB mutex in body, reference the column
|
||||
// family before the mutex and unreference after you unlock, since the column
|
||||
// family might get dropped when the DB mutex is released
|
||||
// * Iteration -- hold DB mutex. If you want to release the DB mutex in the
|
||||
// body of the iteration, wrap in a RefedColumnFamilySet.
|
||||
// * GetDefault() -- thread safe
|
||||
// * GetColumnFamily() -- either inside of DB mutex or from a write thread
|
||||
// * GetNextColumnFamilyID(), GetMaxColumnFamily(), UpdateMaxColumnFamily(),
|
||||
@@ -642,17 +641,12 @@ class ColumnFamilySet {
|
||||
public:
|
||||
explicit iterator(ColumnFamilyData* cfd)
|
||||
: current_(cfd) {}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
iterator& operator++() {
|
||||
// dropped column families might still be included in this iteration
|
||||
// (we're only removing them when client drops the last reference to the
|
||||
// column family).
|
||||
// dummy is never dead, so this will never be infinite
|
||||
do {
|
||||
current_ = current_->next_;
|
||||
} while (current_->refs_.load(std::memory_order_relaxed) == 0);
|
||||
current_ = current_->next_;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const iterator& other) {
|
||||
bool operator!=(const iterator& other) const {
|
||||
return this->current_ != other.current_;
|
||||
}
|
||||
ColumnFamilyData* operator*() { return current_; }
|
||||
@@ -691,10 +685,6 @@ class ColumnFamilySet {
|
||||
iterator begin() { return iterator(dummy_cfd_->next_); }
|
||||
iterator end() { return iterator(dummy_cfd_); }
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
// Don't call while iterating over ColumnFamilySet
|
||||
void FreeDeadColumnFamilies();
|
||||
|
||||
Cache* get_table_cache() { return table_cache_; }
|
||||
|
||||
WriteBufferManager* write_buffer_manager() { return write_buffer_manager_; }
|
||||
@@ -737,6 +727,55 @@ class ColumnFamilySet {
|
||||
std::string db_session_id_;
|
||||
};
|
||||
|
||||
// A wrapper for ColumnFamilySet that supports releasing DB mutex during each
|
||||
// iteration over the iterator, because the cfd is Refed and Unrefed during
|
||||
// each iteration to prevent concurrent CF drop from destroying it (until
|
||||
// Unref).
|
||||
class RefedColumnFamilySet {
|
||||
public:
|
||||
explicit RefedColumnFamilySet(ColumnFamilySet* cfs) : wrapped_(cfs) {}
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
explicit iterator(ColumnFamilySet::iterator wrapped) : wrapped_(wrapped) {
|
||||
MaybeRef(*wrapped_);
|
||||
}
|
||||
~iterator() { MaybeUnref(*wrapped_); }
|
||||
inline void MaybeRef(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
cfd->Ref();
|
||||
}
|
||||
}
|
||||
inline void MaybeUnref(ColumnFamilyData* cfd) {
|
||||
if (cfd->GetID() != ColumnFamilyData::kDummyColumnFamilyDataId) {
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
// NOTE: minimum operators for for-loop iteration
|
||||
inline iterator& operator++() {
|
||||
ColumnFamilyData* old = *wrapped_;
|
||||
++wrapped_;
|
||||
// Can only unref & potentially free cfd after accessing its next_
|
||||
MaybeUnref(old);
|
||||
MaybeRef(*wrapped_);
|
||||
return *this;
|
||||
}
|
||||
inline bool operator!=(const iterator& other) const {
|
||||
return this->wrapped_ != other.wrapped_;
|
||||
}
|
||||
inline ColumnFamilyData* operator*() { return *wrapped_; }
|
||||
|
||||
private:
|
||||
ColumnFamilySet::iterator wrapped_;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(wrapped_->begin()); }
|
||||
iterator end() { return iterator(wrapped_->end()); }
|
||||
|
||||
private:
|
||||
ColumnFamilySet* wrapped_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
// memtables of different column families (specified by ID in the write batch)
|
||||
class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
|
||||
@@ -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) {
|
||||
@@ -1228,7 +1228,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
uint64_t prev_cpu_micros = db_options_.clock->CPUNanos() / 1000;
|
||||
uint64_t prev_cpu_micros = db_options_.clock->CPUMicros();
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
|
||||
@@ -1572,7 +1572,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
sub_compact->compaction_job_stats.cpu_micros =
|
||||
db_options_.clock->CPUNanos() / 1000 - prev_cpu_micros;
|
||||
db_options_.clock->CPUMicros() - prev_cpu_micros;
|
||||
|
||||
if (measure_io_stats_) {
|
||||
sub_compact->compaction_job_stats.file_write_nanos +=
|
||||
|
||||
@@ -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");
|
||||
|
||||
+44
-12
@@ -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++) {
|
||||
@@ -1939,8 +1938,9 @@ TEST_F(DBBasicTest, MultiGetStats) {
|
||||
int total_keys = 2000;
|
||||
std::vector<std::string> keys_str(total_keys);
|
||||
std::vector<Slice> keys(total_keys);
|
||||
std::vector<PinnableSlice> values(total_keys);
|
||||
std::vector<Status> s(total_keys);
|
||||
static size_t kMultiGetBatchSize = 100;
|
||||
std::vector<PinnableSlice> values(kMultiGetBatchSize);
|
||||
std::vector<Status> s(kMultiGetBatchSize);
|
||||
ReadOptions read_opts;
|
||||
|
||||
Random rnd(309);
|
||||
@@ -1977,15 +1977,16 @@ TEST_F(DBBasicTest, MultiGetStats) {
|
||||
}
|
||||
}
|
||||
ASSERT_OK(Flush(1));
|
||||
MoveFilesToLevel(1, 1);
|
||||
Close();
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_OK(options.statistics->Reset());
|
||||
|
||||
db_->MultiGet(read_opts, handles_[1], total_keys, keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
db_->MultiGet(read_opts, handles_[1], kMultiGetBatchSize, &keys[1250],
|
||||
values.data(), s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), total_keys);
|
||||
ASSERT_EQ(values.size(), kMultiGetBatchSize);
|
||||
HistogramData hist_data_blocks;
|
||||
HistogramData hist_index_and_filter_blocks;
|
||||
HistogramData hist_sst;
|
||||
@@ -1997,16 +1998,16 @@ TEST_F(DBBasicTest, MultiGetStats) {
|
||||
options.statistics->histogramData(NUM_SST_READ_PER_LEVEL, &hist_sst);
|
||||
|
||||
// Maximum number of blocks read from a file system in a level.
|
||||
ASSERT_GT(hist_data_blocks.max, 0);
|
||||
ASSERT_EQ(hist_data_blocks.max, 32);
|
||||
ASSERT_GT(hist_index_and_filter_blocks.max, 0);
|
||||
// Maximum number of sst files read from file system in a level.
|
||||
ASSERT_GT(hist_sst.max, 0);
|
||||
ASSERT_EQ(hist_sst.max, 2);
|
||||
|
||||
// Minimun number of blocks read in a level.
|
||||
ASSERT_EQ(hist_data_blocks.min, 3);
|
||||
ASSERT_EQ(hist_data_blocks.min, 4);
|
||||
ASSERT_GT(hist_index_and_filter_blocks.min, 0);
|
||||
// Minimun number of sst files read in a level.
|
||||
ASSERT_GT(hist_sst.max, 0);
|
||||
ASSERT_EQ(hist_sst.min, 1);
|
||||
}
|
||||
|
||||
// Test class for batched MultiGet with prefix extractor
|
||||
@@ -3064,6 +3065,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 +3631,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();
|
||||
|
||||
@@ -6881,6 +6881,319 @@ TEST_F(DBCompactionTest, FIFOWarm) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
const int kNumL0Files = 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// Generate 2 levels of file to make sure the manual compaction is not skipped
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), "value"));
|
||||
if (i % 2) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), "value"));
|
||||
if (i % 2) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
// Block compaction queue
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
|
||||
port::Thread compact_thread1([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
std::string begin_str = Key(0);
|
||||
std::string end_str = Key(3);
|
||||
Slice b = begin_str;
|
||||
Slice e = end_str;
|
||||
auto s = db_->CompactRange(cro, &b, &e);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
port::Thread compact_thread2([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
std::string begin_str = Key(4);
|
||||
std::string end_str = Key(7);
|
||||
Slice b = begin_str;
|
||||
Slice e = end_str;
|
||||
auto s = db_->CompactRange(cro, &b, &e);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
// Disable manual compaction should cancel both manual compactions and both
|
||||
// compaction should return incomplete.
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread1.join();
|
||||
compact_thread2.join();
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
// make sure the manual compaction background is started but not yet set the
|
||||
// status to in_progress, then cancel the manual compaction, which should not
|
||||
// result in segfault
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkCompaction",
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"PreDisableManualCompaction"},
|
||||
{"DBImpl::RunManualCompaction:Unscheduled",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
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::DisableJustStartedManualCompaction:"
|
||||
"PreDisableManualCompaction");
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableInProgressManualCompaction) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction:InProgress",
|
||||
"DBCompactionTest::DisableInProgressManualCompaction:"
|
||||
"PreDisableManualCompaction"},
|
||||
{"DBImpl::RunManualCompaction:Unscheduled",
|
||||
"CompactionJob::Run():Start"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// 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::DisableInProgressManualCompaction:"
|
||||
"PreDisableManualCompaction");
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread.join();
|
||||
}
|
||||
|
||||
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::DisableManualCompactionThreadQueueFullDBClose:"
|
||||
"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::DisableManualCompactionThreadQueueFullDBClose:"
|
||||
"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, DBCloseWithManualCompaction) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::RunManualCompaction:Scheduled",
|
||||
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
|
||||
"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::DisableManualCompactionThreadQueueFullDBClose:"
|
||||
"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));
|
||||
|
||||
// Close DB with manual compaction and auto triggered compaction in the queue.
|
||||
auto s = db_->Close();
|
||||
ASSERT_OK(s);
|
||||
|
||||
// manual compaction thread should return with Incomplete().
|
||||
compact_thread.join();
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest,
|
||||
DisableManualCompactionDoesNotWaitForDrainingAutomaticCompaction) {
|
||||
// When `CompactRangeOptions::exclusive_manual_compaction == true`, we wait
|
||||
|
||||
@@ -45,17 +45,15 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
@@ -63,7 +61,6 @@ Status DBImpl::FlushForGetLiveFiles() {
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -676,6 +676,7 @@ class TestFlushListener : public EventListener {
|
||||
~TestFlushListener() override {
|
||||
prev_fc_info_.status.PermitUncheckedError(); // Ignore the status
|
||||
}
|
||||
|
||||
void OnTableFileCreated(const TableFileCreationInfo& info) override {
|
||||
// remember the info for later checking the FlushJobInfo.
|
||||
prev_fc_info_ = info;
|
||||
@@ -1999,6 +2000,61 @@ TEST_P(DBFlushTestBlobError, FlushError) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBFlushTest, TombstoneVisibleInSnapshot) {
|
||||
class SimpleTestFlushListener : public EventListener {
|
||||
public:
|
||||
explicit SimpleTestFlushListener(DBFlushTest* _test) : test_(_test) {}
|
||||
~SimpleTestFlushListener() override {}
|
||||
|
||||
void OnFlushBegin(DB* db, const FlushJobInfo& info) override {
|
||||
ASSERT_EQ(static_cast<uint32_t>(0), info.cf_id);
|
||||
|
||||
ASSERT_OK(db->Delete(WriteOptions(), "foo"));
|
||||
snapshot_ = db->GetSnapshot();
|
||||
ASSERT_OK(db->Put(WriteOptions(), "foo", "value"));
|
||||
|
||||
auto* dbimpl = static_cast_with_check<DBImpl>(db);
|
||||
assert(dbimpl);
|
||||
|
||||
ColumnFamilyHandle* cfh = db->DefaultColumnFamily();
|
||||
auto* cfhi = static_cast_with_check<ColumnFamilyHandleImpl>(cfh);
|
||||
assert(cfhi);
|
||||
ASSERT_OK(dbimpl->TEST_SwitchMemtable(cfhi->cfd()));
|
||||
}
|
||||
|
||||
DBFlushTest* test_ = nullptr;
|
||||
const Snapshot* snapshot_ = nullptr;
|
||||
};
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
auto* listener = new SimpleTestFlushListener(this);
|
||||
options.listeners.emplace_back(listener);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value0"));
|
||||
|
||||
ManagedSnapshot snapshot_guard(db_);
|
||||
|
||||
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), default_cf));
|
||||
|
||||
const Snapshot* snapshot = listener->snapshot_;
|
||||
assert(snapshot);
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.snapshot = snapshot;
|
||||
|
||||
// Using snapshot should not see "foo".
|
||||
{
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, "foo", &value);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
+89
-42
@@ -383,15 +383,12 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
s = AtomicFlushMemTables(cfds, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
s = FlushMemTable(cfd, flush_opts, context.flush_reason);
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -406,14 +403,6 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
|
||||
JobContext job_context(0);
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
if (s.ok()) {
|
||||
s = error_handler_.ClearBGError();
|
||||
} else {
|
||||
// NOTE: this is needed to pass ASSERT_STATUS_CHECKED
|
||||
// in the DBSSTTest.DBWithMaxSpaceAllowedRandomized test.
|
||||
// See https://github.com/facebook/rocksdb/pull/7715#issuecomment-754947952
|
||||
error_handler_.GetRecoveryError().PermitUncheckedError();
|
||||
}
|
||||
mutex_.Unlock();
|
||||
|
||||
job_context.manifest_file_number = 1;
|
||||
@@ -434,11 +423,31 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
immutable_db_options_.info_log,
|
||||
"DB resume requested but could not enable file deletions [%s]",
|
||||
s.ToString().c_str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Successfully resumed DB");
|
||||
}
|
||||
|
||||
mutex_.Lock();
|
||||
if (s.ok()) {
|
||||
// This will notify and unblock threads waiting for error recovery to
|
||||
// finish. Those previouly waiting threads can now proceed, which may
|
||||
// include closing the db.
|
||||
s = error_handler_.ClearBGError();
|
||||
} else {
|
||||
// NOTE: this is needed to pass ASSERT_STATUS_CHECKED
|
||||
// in the DBSSTTest.DBWithMaxSpaceAllowedRandomized test.
|
||||
// See https://github.com/facebook/rocksdb/pull/7715#issuecomment-754947952
|
||||
error_handler_.GetRecoveryError().PermitUncheckedError();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Successfully resumed DB");
|
||||
} else {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Failed to resume DB [%s]",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
|
||||
// Check for shutdown again before scheduling further compactions,
|
||||
// since we released and re-acquired the lock above
|
||||
if (shutdown_initiated_) {
|
||||
@@ -491,18 +500,14 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (!cfd->IsDropped() && cfd->initialized() && !cfd->mem()->IsEmpty()) {
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
Status s = FlushMemTable(cfd, FlushOptions(), FlushReason::kShutDown);
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
}
|
||||
|
||||
shutting_down_.store(true, std::memory_order_release);
|
||||
@@ -533,10 +538,19 @@ Status DBImpl::CloseHelper() {
|
||||
// marker. After this we do a variant of the waiting and unschedule work
|
||||
// (to consider: moving all the waiting into CancelAllBackgroundWork(true))
|
||||
CancelAllBackgroundWork(false);
|
||||
|
||||
// Cancel manual compaction if there's any
|
||||
if (HasPendingManualCompaction()) {
|
||||
DisableManualCompaction();
|
||||
}
|
||||
mutex_.Lock();
|
||||
env_->UnSchedule(this, Env::Priority::BOTTOM);
|
||||
env_->UnSchedule(this, Env::Priority::LOW);
|
||||
env_->UnSchedule(this, Env::Priority::HIGH);
|
||||
// Unschedule all tasks for this DB
|
||||
for (uint8_t i = 0; i < static_cast<uint8_t>(TaskType::kCount); i++) {
|
||||
env_->UnSchedule(GetTaskTag(i), Env::Priority::BOTTOM);
|
||||
env_->UnSchedule(GetTaskTag(i), Env::Priority::LOW);
|
||||
env_->UnSchedule(GetTaskTag(i), Env::Priority::HIGH);
|
||||
}
|
||||
|
||||
Status ret = Status::OK();
|
||||
|
||||
// Wait for background work to finish
|
||||
@@ -956,18 +970,13 @@ void DBImpl::DumpStats() {
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:StartRunning");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
// Release DB mutex for gathering cache entry stats. Pass over all
|
||||
// column families for this first so that other stats are dumped
|
||||
// near-atomically.
|
||||
// Get a ref before unlocking
|
||||
cfd->Ref();
|
||||
{
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
}
|
||||
cfd->UnrefAndTryDelete();
|
||||
InstrumentedMutexUnlock u(&mutex_);
|
||||
cfd->internal_stats()->CollectCacheEntryStats(/*foreground=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1496,6 +1505,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,
|
||||
@@ -1867,11 +1900,12 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
return s;
|
||||
}
|
||||
}
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
sv->current->Get(
|
||||
read_options, lkey, get_impl_options.value, timestamp, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
&merge_context, &max_covering_tombstone_seq, &pinned_iters_mgr,
|
||||
get_impl_options.get_value ? get_impl_options.value_found : nullptr,
|
||||
nullptr, nullptr,
|
||||
get_impl_options.get_value ? get_impl_options.callback : nullptr,
|
||||
@@ -2048,11 +2082,13 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
if (!done) {
|
||||
PinnableSlice pinnable_val;
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(
|
||||
read_options, lkey, &pinnable_val, timestamp, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, /*value_found=*/nullptr,
|
||||
/*key_exists=*/nullptr,
|
||||
/*seq=*/nullptr, read_callback);
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
super_version->current->Get(read_options, lkey, &pinnable_val, timestamp,
|
||||
&s, &merge_context,
|
||||
&max_covering_tombstone_seq,
|
||||
&pinned_iters_mgr, /*value_found=*/nullptr,
|
||||
/*key_exists=*/nullptr,
|
||||
/*seq=*/nullptr, read_callback);
|
||||
value->assign(pinnable_val.data(), pinnable_val.size());
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
@@ -3124,6 +3160,12 @@ bool CfdListContains(const CfdList& list, ColumnFamilyData* cfd) {
|
||||
} // namespace
|
||||
|
||||
void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
if (s == nullptr) {
|
||||
// DBImpl::GetSnapshot() can return nullptr when snapshot
|
||||
// not supported by specifying the condition:
|
||||
// inplace_update_support enabled.
|
||||
return;
|
||||
}
|
||||
const SnapshotImpl* casted_s = reinterpret_cast<const SnapshotImpl*>(s);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -3403,15 +3445,13 @@ bool DBImpl::GetAggregatedIntProperty(const Slice& property,
|
||||
// Needs mutex to protect the list of column families.
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
uint64_t value;
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (auto* cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
ret = GetIntPropertyInternal(cfd, *property_info, true, &value);
|
||||
// GetIntPropertyInternal may release db mutex and re-acquire it.
|
||||
mutex_.AssertHeld();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (ret) {
|
||||
sum += value;
|
||||
} else {
|
||||
@@ -4007,6 +4047,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();
|
||||
}
|
||||
@@ -4511,10 +4555,12 @@ Status DBImpl::GetLatestSequenceForKey(
|
||||
// SST files if cache_only=true?
|
||||
if (!cache_only) {
|
||||
// Check tables
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
sv->current->Get(read_options, lkey, /*value=*/nullptr, timestamp, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
nullptr /* value_found */, found_record_for_key, seq,
|
||||
nullptr /*read_callback*/, is_blob_index);
|
||||
&pinned_iters_mgr, nullptr /* value_found */,
|
||||
found_record_for_key, seq, nullptr /*read_callback*/,
|
||||
is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading SST files
|
||||
@@ -4996,6 +5042,7 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: simplify using GetRefedColumnFamilySet?
|
||||
std::vector<ColumnFamilyData*> cfd_list;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
+64
-16
@@ -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);
|
||||
|
||||
@@ -1479,19 +1492,31 @@ class DBImpl : public DB {
|
||||
|
||||
// Information for a manual compaction
|
||||
struct ManualCompactionState {
|
||||
ManualCompactionState(ColumnFamilyData* _cfd, int _input_level,
|
||||
int _output_level, uint32_t _output_path_id,
|
||||
bool _exclusive, bool _disallow_trivial_move,
|
||||
std::atomic<bool>* _canceled)
|
||||
: cfd(_cfd),
|
||||
input_level(_input_level),
|
||||
output_level(_output_level),
|
||||
output_path_id(_output_path_id),
|
||||
exclusive(_exclusive),
|
||||
disallow_trivial_move(_disallow_trivial_move),
|
||||
canceled(_canceled) {}
|
||||
|
||||
ColumnFamilyData* cfd;
|
||||
int input_level;
|
||||
int output_level;
|
||||
uint32_t output_path_id;
|
||||
Status status;
|
||||
bool done;
|
||||
bool in_progress; // compaction request being processed?
|
||||
bool incomplete; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin; // nullptr means beginning of key range
|
||||
const InternalKey* end; // nullptr means end of key range
|
||||
InternalKey* manual_end; // how far we are compacting
|
||||
bool done = false;
|
||||
bool in_progress = false; // compaction request being processed?
|
||||
bool incomplete = false; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin = nullptr; // nullptr means beginning of key range
|
||||
const InternalKey* end = nullptr; // nullptr means end of key range
|
||||
InternalKey* manual_end = nullptr; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
std::atomic<bool>* canceled; // Compaction canceled by the user?
|
||||
@@ -1698,6 +1723,25 @@ class DBImpl : public DB {
|
||||
}
|
||||
}
|
||||
|
||||
// TaskType is used to identify tasks in thread-pool, currently only
|
||||
// differentiate manual compaction, which could be unscheduled from the
|
||||
// thread-pool.
|
||||
enum class TaskType : uint8_t {
|
||||
kDefault = 0,
|
||||
kManualCompaction = 1,
|
||||
kCount = 2,
|
||||
};
|
||||
|
||||
// Task tag is used to identity tasks in thread-pool, which is
|
||||
// dbImpl obj address + type
|
||||
inline void* GetTaskTag(TaskType type) {
|
||||
return GetTaskTag(static_cast<uint8_t>(type));
|
||||
}
|
||||
|
||||
inline void* GetTaskTag(uint8_t type) {
|
||||
return static_cast<uint8_t*>(static_cast<void*>(this)) + type;
|
||||
}
|
||||
|
||||
// REQUIRES: mutex locked and in write thread.
|
||||
void AssignAtomicFlushSeq(const autovector<ColumnFamilyData*>& cfds);
|
||||
|
||||
@@ -1716,7 +1760,8 @@ class DBImpl : public DB {
|
||||
WriteBatch** to_be_cached_state);
|
||||
|
||||
IOStatus WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size);
|
||||
uint64_t* log_used, uint64_t* log_size,
|
||||
bool with_db_mutex = false, bool with_log_mutex = false);
|
||||
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
@@ -1984,7 +2029,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_;
|
||||
@@ -2041,12 +2087,15 @@ class DBImpl : public DB {
|
||||
bool persistent_stats_cfd_exists_ = true;
|
||||
|
||||
// Without two_write_queues, read and writes to alive_log_files_ are
|
||||
// protected by mutex_. However since back() is never popped, and push_back()
|
||||
// is done only from write_thread_, the same thread can access the item
|
||||
// reffered by back() without mutex_. With two_write_queues_, writes
|
||||
// protected by mutex_. With two_write_queues_, writes
|
||||
// are protected by locking both mutex_ and log_write_mutex_, and reads must
|
||||
// be under either mutex_ or log_write_mutex_.
|
||||
std::deque<LogFileNumberSize> alive_log_files_;
|
||||
// Caching the result of `alive_log_files_.back()` so that we do not have to
|
||||
// call `alive_log_files_.back()` in the write thread (WriteToWAL()) which
|
||||
// requires locking db mutex if log_mutex_ is not already held in
|
||||
// two-write-queues mode.
|
||||
std::deque<LogFileNumberSize>::reverse_iterator alive_log_files_tail_;
|
||||
// Log files that aren't fully synced, and the current log file.
|
||||
// Synchronization:
|
||||
// - push_back() is done from write_thread_ with locked mutex_ and
|
||||
@@ -2371,11 +2420,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
|
||||
|
||||
@@ -170,6 +170,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
const bool needs_to_sync_closed_wals =
|
||||
logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1;
|
||||
|
||||
// If needs_to_sync_closed_wals is true, we need to record the current
|
||||
// maximum memtable ID of this column family so that a later PickMemtables()
|
||||
// call will not pick memtables whose IDs are higher. This is due to the fact
|
||||
@@ -177,9 +178,33 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// happen for this column family in the meantime. The newly created memtables
|
||||
// have their data backed by unsynced WALs, thus they cannot be included in
|
||||
// this flush job.
|
||||
// Another reason why we must record the current maximum memtable ID of this
|
||||
// column family: SyncClosedLogs() may release db mutex, thus it's possible
|
||||
// for application to continue to insert into memtables increasing db's
|
||||
// sequence number. The application may take a snapshot, but this snapshot is
|
||||
// not included in `snapshot_seqs` which will be passed to flush job because
|
||||
// `snapshot_seqs` has already been computed before this function starts.
|
||||
// Recording the max memtable ID ensures that the flush job does not flush
|
||||
// a memtable without knowing such snapshot(s).
|
||||
uint64_t max_memtable_id = needs_to_sync_closed_wals
|
||||
? cfd->imm()->GetLatestMemTableID()
|
||||
: port::kMaxUint64;
|
||||
|
||||
// If needs_to_sync_closed_wals is false, then the flush job will pick ALL
|
||||
// existing memtables of the column family when PickMemTable() is called
|
||||
// later. Although we won't call SyncClosedLogs() in this case, we may still
|
||||
// call the callbacks of the listeners, i.e. NotifyOnFlushBegin() which also
|
||||
// releases and re-acquires the db mutex. In the meantime, the application
|
||||
// can still insert into the memtables and increase the db's sequence number.
|
||||
// The application can take a snapshot, hoping that the latest visible state
|
||||
// to this snapshto is preserved. This is hard to guarantee since db mutex
|
||||
// not held. This newly-created snapshot is not included in `snapshot_seqs`
|
||||
// and the flush job is unaware of its presence. Consequently, the flush job
|
||||
// may drop certain keys when generating the L0, causing incorrect data to be
|
||||
// returned for snapshot read using this snapshot.
|
||||
// To address this, we make sure NotifyOnFlushBegin() executes after memtable
|
||||
// picking so that no new snapshot can be taken between the two functions.
|
||||
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options, max_memtable_id,
|
||||
file_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
||||
@@ -192,11 +217,6 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
&blob_callback_);
|
||||
FileMetaData file_meta;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status s;
|
||||
bool need_cancel = false;
|
||||
IOStatus log_io_s = IOStatus::OK();
|
||||
@@ -221,6 +241,12 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables", &flush_job);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
bool switched_to_mempurge = false;
|
||||
// Within flush_job.Run, rocksdb may call event listener to notify
|
||||
// file creation and deletion.
|
||||
@@ -917,8 +943,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 +973,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 +1004,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,21 +1778,16 @@ Status DBImpl::RunManualCompaction(
|
||||
input_level >= 0);
|
||||
|
||||
InternalKey begin_storage, end_storage;
|
||||
CompactionArg* ca;
|
||||
CompactionArg* ca = nullptr;
|
||||
|
||||
bool scheduled = false;
|
||||
bool unscheduled = false;
|
||||
Env::Priority thread_pool_priority = Env::Priority::TOTAL;
|
||||
bool manual_conflict = false;
|
||||
ManualCompactionState manual;
|
||||
manual.cfd = cfd;
|
||||
manual.input_level = input_level;
|
||||
manual.output_level = output_level;
|
||||
manual.output_path_id = compact_range_options.target_path_id;
|
||||
manual.done = false;
|
||||
manual.in_progress = false;
|
||||
manual.incomplete = false;
|
||||
manual.exclusive = exclusive;
|
||||
manual.disallow_trivial_move = disallow_trivial_move;
|
||||
manual.canceled = compact_range_options.canceled;
|
||||
|
||||
ManualCompactionState manual(
|
||||
cfd, input_level, output_level, compact_range_options.target_path_id,
|
||||
exclusive, disallow_trivial_move, compact_range_options.canceled);
|
||||
// For universal compaction, we enforce every manual compaction to compact
|
||||
// all files.
|
||||
if (begin == nullptr ||
|
||||
@@ -1849,6 +1892,23 @@ Status DBImpl::RunManualCompaction(
|
||||
assert(!exclusive || !manual_conflict);
|
||||
// Running either this or some other manual compaction
|
||||
bg_cv_.Wait();
|
||||
if (manual_compaction_paused_ > 0 && scheduled && !unscheduled) {
|
||||
assert(thread_pool_priority != Env::Priority::TOTAL);
|
||||
// unschedule all manual compactions
|
||||
auto unscheduled_task_num = env_->UnSchedule(
|
||||
GetTaskTag(TaskType::kManualCompaction), thread_pool_priority);
|
||||
if (unscheduled_task_num > 0) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"[%s] Unscheduled %d number of manual compactions from the "
|
||||
"thread-pool",
|
||||
cfd->GetName().c_str(), unscheduled_task_num);
|
||||
// it may unschedule other manual compactions, notify others.
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
unscheduled = true;
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:Unscheduled");
|
||||
}
|
||||
if (scheduled && manual.incomplete == true) {
|
||||
assert(!manual.in_progress);
|
||||
scheduled = false;
|
||||
@@ -1876,15 +1936,20 @@ Status DBImpl::RunManualCompaction(
|
||||
bg_bottom_compaction_scheduled_++;
|
||||
ca->compaction_pri_ = Env::Priority::BOTTOM;
|
||||
env_->Schedule(&DBImpl::BGWorkBottomCompaction, ca,
|
||||
Env::Priority::BOTTOM, this,
|
||||
Env::Priority::BOTTOM,
|
||||
GetTaskTag(TaskType::kManualCompaction),
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
thread_pool_priority = Env::Priority::BOTTOM;
|
||||
} else {
|
||||
bg_compaction_scheduled_++;
|
||||
ca->compaction_pri_ = Env::Priority::LOW;
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW, this,
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW,
|
||||
GetTaskTag(TaskType::kManualCompaction),
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
thread_pool_priority = Env::Priority::LOW;
|
||||
}
|
||||
scheduled = true;
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:Scheduled");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1892,6 +1957,13 @@ Status DBImpl::RunManualCompaction(
|
||||
assert(!manual.in_progress);
|
||||
assert(HasPendingManualCompaction());
|
||||
RemoveManualCompaction(&manual);
|
||||
// if the manual job is unscheduled, try schedule other jobs in case there's
|
||||
// any unscheduled compaction job which was blocked by exclusive manual
|
||||
// compaction.
|
||||
if (manual.status.IsIncomplete() &&
|
||||
manual.status.subcode() == Status::SubCode::kManualCompactionPaused) {
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
bg_cv_.SignalAll();
|
||||
return manual.status;
|
||||
}
|
||||
@@ -2242,21 +2314,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 +2352,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 +2362,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()) {
|
||||
@@ -2613,7 +2691,15 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
CompactionArg ca = *(ca_ptr);
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
if (ca.prepicked_compaction != nullptr) {
|
||||
// if it's a manual compaction, set status to ManualCompactionPaused
|
||||
if (ca.prepicked_compaction->manual_compaction_state) {
|
||||
ca.prepicked_compaction->manual_compaction_state->done = true;
|
||||
ca.prepicked_compaction->manual_compaction_state->status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
if (ca.prepicked_compaction->compaction != nullptr) {
|
||||
ca.prepicked_compaction->compaction->ReleaseCompactionFiles(
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused));
|
||||
delete ca.prepicked_compaction->compaction;
|
||||
}
|
||||
delete ca.prepicked_compaction;
|
||||
@@ -2852,6 +2938,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
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",
|
||||
@@ -2860,9 +2947,9 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
|
||||
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()
|
||||
// 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() &&
|
||||
@@ -2889,6 +2976,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
|
||||
assert(num_running_compactions_ > 0);
|
||||
num_running_compactions_--;
|
||||
|
||||
if (bg_thread_pri == Env::Priority::LOW) {
|
||||
bg_compaction_scheduled_--;
|
||||
} else {
|
||||
@@ -2896,8 +2984,6 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
bg_bottom_compaction_scheduled_--;
|
||||
}
|
||||
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
|
||||
// See if there's more work to be done
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
@@ -2907,7 +2993,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 ||
|
||||
@@ -2994,6 +3079,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
manual_compaction->in_progress = true;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:InProgress");
|
||||
|
||||
std::unique_ptr<TaskLimiterToken> task_token;
|
||||
|
||||
// InternalKey manual_end_storage;
|
||||
|
||||
@@ -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(
|
||||
|
||||
+10
-17
@@ -23,11 +23,7 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
if (allow_2pc()) {
|
||||
return versions_->min_log_number_to_keep_2pc();
|
||||
} else {
|
||||
return versions_->MinLogNumberWithUnflushedData();
|
||||
}
|
||||
return versions_->min_log_number_to_keep();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
@@ -224,7 +220,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
|
||||
// Add log files in wal_dir
|
||||
|
||||
if (!immutable_db_options_.IsWalDirSameAsDBPath(dbname_)) {
|
||||
std::vector<std::string> log_files;
|
||||
Status s = env_->GetChildren(immutable_db_options_.wal_dir, &log_files);
|
||||
@@ -234,6 +229,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
log_file, immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
|
||||
// Add info log files in db_log_dir
|
||||
if (!immutable_db_options_.db_log_dir.empty() &&
|
||||
immutable_db_options_.db_log_dir != dbname_) {
|
||||
@@ -670,8 +666,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 +674,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 +696,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 +821,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 +852,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) {
|
||||
|
||||
@@ -864,6 +864,11 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_wal_number = kMaxSequenceNumber;
|
||||
uint64_t min_wal_number = MinLogNumberToKeep();
|
||||
if (!allow_2pc()) {
|
||||
// In non-2pc mode, we skip WALs that do not back unflushed data.
|
||||
min_wal_number =
|
||||
std::max(min_wal_number, versions_->MinLogNumberWithUnflushedData());
|
||||
}
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (wal_number < min_wal_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
@@ -1268,9 +1273,16 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
if (flushed) {
|
||||
wal_deletion = std::unique_ptr<VersionEdit>(new VersionEdit());
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
}
|
||||
if (!allow_2pc()) {
|
||||
// In non-2pc mode, flushing the memtables of the column families
|
||||
// means we can advance min_log_number_to_keep.
|
||||
wal_deletion->SetMinLogNumberToKeep(max_wal_number + 1);
|
||||
}
|
||||
edit_lists.back().push_back(wal_deletion.get());
|
||||
}
|
||||
|
||||
@@ -1349,7 +1361,14 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
// FindObsoleteFiles()
|
||||
total_log_size_ = 0;
|
||||
log_empty_ = false;
|
||||
uint64_t min_wal_with_unflushed_data =
|
||||
versions_->MinLogNumberWithUnflushedData();
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (!allow_2pc() && wal_number < min_wal_with_unflushed_data) {
|
||||
// In non-2pc mode, the WAL files not backing unflushed data are not
|
||||
// alive, thus should not be added to the alive_log_files_.
|
||||
continue;
|
||||
}
|
||||
// We preallocate space for wals, but then after a crash and restart, those
|
||||
// preallocated space are not needed anymore. It is likely only the last
|
||||
// log has such preallocated space, so we only truncate for the last log.
|
||||
@@ -1362,6 +1381,7 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
total_log_size_ += log.size;
|
||||
alive_log_files_.push_back(log);
|
||||
}
|
||||
alive_log_files_tail_ = alive_log_files_.rbegin();
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -1371,6 +1391,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;
|
||||
@@ -1699,6 +1725,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
impl->alive_log_files_.push_back(
|
||||
DBImpl::LogFileNumberSize(impl->logfile_number_));
|
||||
impl->alive_log_files_tail_ = impl->alive_log_files_.rbegin();
|
||||
if (impl->two_write_queues_) {
|
||||
impl->log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -1719,7 +1746,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
WriteOptions write_options;
|
||||
uint64_t log_used, log_size;
|
||||
log::Writer* log_writer = impl->logs_.back().writer;
|
||||
s = impl->WriteToWAL(empty_batch, log_writer, &log_used, &log_size);
|
||||
s = impl->WriteToWAL(empty_batch, log_writer, &log_used, &log_size,
|
||||
/*with_db_mutex==*/true);
|
||||
if (s.ok()) {
|
||||
// Need to fsync, otherwise it might get lost after a power reset.
|
||||
s = impl->FlushWAL(false);
|
||||
|
||||
@@ -58,9 +58,10 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
super_version->current->Get(read_options, lkey, pinnable_val,
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq);
|
||||
&max_covering_tombstone_seq, &pinned_iters_mgr);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
|
||||
@@ -377,9 +377,10 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
}
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
super_version->current->Get(read_options, lkey, pinnable_val,
|
||||
/*timestamp=*/nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq);
|
||||
&max_covering_tombstone_seq, &pinned_iters_mgr);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -1047,8 +1085,18 @@ WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
// write thread. Otherwise this must be called holding log_write_mutex_.
|
||||
IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size) {
|
||||
uint64_t* log_size,
|
||||
bool with_db_mutex, bool with_log_mutex) {
|
||||
assert(log_size != nullptr);
|
||||
|
||||
// Assert mutex explicitly.
|
||||
if (with_db_mutex) {
|
||||
mutex_.AssertHeld();
|
||||
} else if (two_write_queues_) {
|
||||
log_write_mutex_.AssertHeld();
|
||||
assert(with_log_mutex);
|
||||
}
|
||||
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
*log_size = log_entry.size();
|
||||
// When two_write_queues_ WriteToWAL has to be protected from concurretn calls
|
||||
@@ -1071,9 +1119,12 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
*log_used = logfile_number_;
|
||||
}
|
||||
total_log_size_ += log_entry.size();
|
||||
// TODO(myabandeh): it might be unsafe to access alive_log_files_.back() here
|
||||
// since alive_log_files_ might be modified concurrently
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
if (with_db_mutex || with_log_mutex) {
|
||||
assert(alive_log_files_tail_ == alive_log_files_.rbegin());
|
||||
assert(alive_log_files_tail_ != alive_log_files_.rend());
|
||||
}
|
||||
LogFileNumberSize& last_alive_log = *alive_log_files_tail_;
|
||||
last_alive_log.AddSize(*log_size);
|
||||
log_empty_ = false;
|
||||
return io_s;
|
||||
}
|
||||
@@ -1083,6 +1134,7 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence) {
|
||||
IOStatus io_s;
|
||||
assert(!two_write_queues_);
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
size_t write_with_wal = 0;
|
||||
@@ -1170,6 +1222,7 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
SequenceNumber* last_sequence, size_t seq_inc) {
|
||||
IOStatus io_s;
|
||||
|
||||
assert(two_write_queues_ || immutable_db_options_.unordered_write);
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
WriteBatch tmp_batch;
|
||||
@@ -1194,7 +1247,8 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
uint64_t log_size;
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size,
|
||||
/*with_db_mutex=*/false, /*with_log_mutex=*/true);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -1848,6 +1902,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
log_dir_synced_ = false;
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
alive_log_files_tail_ = alive_log_files_.rbegin();
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
|
||||
@@ -169,6 +169,36 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdateCallbackNoAction) {
|
||||
ASSERT_EQ(Get(1, "key"), "NOT_FOUND");
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBTestInPlaceUpdate, InPlaceUpdateAndSnapshot) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.inplace_update_support = true;
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Update key with values of smaller size, and
|
||||
// run GetSnapshot and ReleaseSnapshot
|
||||
int numValues = 2;
|
||||
for (int i = numValues; i > 0; i--) {
|
||||
const Snapshot* s = db_->GetSnapshot();
|
||||
ASSERT_EQ(nullptr, s);
|
||||
std::string value = DummyString(i, 'a');
|
||||
ASSERT_OK(Put(1, "key", value));
|
||||
ASSERT_EQ(value, Get(1, "key"));
|
||||
// release s (nullptr)
|
||||
db_->ReleaseSnapshot(s);
|
||||
}
|
||||
|
||||
// Only 1 instance for that key.
|
||||
validateNumberOfEntries(1, 1);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -47,6 +47,45 @@ class DBMergeOperandTest : public DBTestBase {
|
||||
: DBTestBase("db_merge_operand_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperandTest, MergeOperandReadAfterFreeBug) {
|
||||
// There was a bug of reading merge operands after they are mistakely freed
|
||||
// in DB::GetMergeOperands, which is surfaced by cache full.
|
||||
// See PR#9507 for more.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.env = env_;
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
// Small cache to simulate cache full
|
||||
table_options.block_cache = NewLRUCache(1);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
Reopen(options);
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
std::vector<PinnableSlice> values(num_records);
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = num_records;
|
||||
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k1", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k1", "v4"));
|
||||
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k1", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(number_of_operands, 4);
|
||||
ASSERT_EQ(values[0].ToString(), "v1");
|
||||
ASSERT_EQ(values[1].ToString(), "v2");
|
||||
ASSERT_EQ(values[2].ToString(), "v3");
|
||||
ASSERT_EQ(values[3].ToString(), "v4");
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
|
||||
@@ -1724,6 +1724,34 @@ TEST_F(DBRangeDelTest, OverlappedKeys) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, IteratorRefresh) {
|
||||
// Refreshing an iterator after a range tombstone is added should cause the
|
||||
// deleted range of keys to disappear.
|
||||
for (bool sv_changed : {false, true}) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key1", "value1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key2", "value2"));
|
||||
|
||||
auto* iter = db_->NewIterator(ReadOptions());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
"key2", "key3"));
|
||||
|
||||
if (sv_changed) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->Refresh());
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ("key1", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1481,6 +1481,93 @@ TEST_F(DBWALTest, kPointInTimeRecoveryCFConsistency) {
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "one", "two"}, options));
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RaceInstallFlushResultsWithWalObsoletion) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
// The following make sure there are two bg flush threads.
|
||||
options.max_background_jobs = 8;
|
||||
|
||||
const std::string cf1_name("cf1");
|
||||
CreateAndReopenWithCF({cf1_name}, options);
|
||||
assert(handles_.size() == 2);
|
||||
|
||||
{
|
||||
dbfull()->TEST_LockMutex();
|
||||
ASSERT_LE(2, dbfull()->GetBGJobLimits().max_flushes);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->PauseBackgroundWork());
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "foo", "value"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value"));
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable(false, true, handles_[1]));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "value"));
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable(false, true, handles_[0]));
|
||||
|
||||
bool called = false;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
// This callback will be called when the first bg flush thread reaches the
|
||||
// point before entering the MANIFEST write queue after flushing the SST
|
||||
// file.
|
||||
// The purpose of the sync points here is to ensure both bg flush threads
|
||||
// finish computing `min_wal_number_to_keep` before any of them updates the
|
||||
// `log_number` for the column family that's being flushed.
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTableList::TryInstallMemtableFlushResults:AfterComputeMinWalToKeep",
|
||||
[&](void* /*arg*/) {
|
||||
dbfull()->mutex()->AssertHeld();
|
||||
if (!called) {
|
||||
// We are the first bg flush thread in the MANIFEST write queue.
|
||||
// We set up the dependency between sync points for two threads that
|
||||
// will be executing the same code.
|
||||
// For the interleaving of events, see
|
||||
// https://github.com/facebook/rocksdb/pull/9715.
|
||||
// bg flush thread1 will release the db mutex while in the MANIFEST
|
||||
// write queue. In the meantime, bg flush thread2 locks db mutex and
|
||||
// computes the min_wal_number_to_keep (before thread1 writes to
|
||||
// MANIFEST thus before cf1->log_number is updated). Bg thread2 joins
|
||||
// the MANIFEST write queue afterwards and bg flush thread1 proceeds
|
||||
// with writing to MANIFEST.
|
||||
called = true;
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"VersionSet::LogAndApply:WriteManifestStart",
|
||||
"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2"},
|
||||
{"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2",
|
||||
"VersionSet::LogAndApply:WriteManifest"},
|
||||
});
|
||||
} else {
|
||||
// The other bg flush thread has already been in the MANIFEST write
|
||||
// queue, and we are after.
|
||||
TEST_SYNC_POINT(
|
||||
"DBWALTest::RaceInstallFlushResultsWithWalObsoletion:BgFlush2");
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->ContinueBackgroundWork());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[0]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
|
||||
ASSERT_TRUE(called);
|
||||
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
DB* db1 = nullptr;
|
||||
Status s = DB::OpenForReadOnly(options, dbname_, &db1);
|
||||
ASSERT_OK(s);
|
||||
assert(db1);
|
||||
delete db1;
|
||||
}
|
||||
|
||||
// Test scope:
|
||||
// - We expect to open data store under all circumstances
|
||||
// - We expect only data upto the point where the first error was encountered
|
||||
|
||||
@@ -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;
|
||||
|
||||
+3
-2
@@ -95,8 +95,9 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
jwriter << "cf_name" << cf_name << "job" << job_id << "event"
|
||||
<< "table_file_creation"
|
||||
<< "file_number" << fd.GetNumber() << "file_size"
|
||||
<< fd.GetFileSize() << "file_checksum" << file_checksum
|
||||
<< "file_checksum_func_name" << file_checksum_func_name;
|
||||
<< fd.GetFileSize() << "file_checksum"
|
||||
<< Slice(file_checksum).ToString(true) << "file_checksum_func_name"
|
||||
<< file_checksum_func_name;
|
||||
|
||||
// table_properties
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
@@ -773,10 +773,11 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage->LevelFiles(lvl);
|
||||
const SequenceNumber level_largest_seqno =
|
||||
(*max_element(level_files.begin(), level_files.end(),
|
||||
[](FileMetaData* f1, FileMetaData* f2) {
|
||||
return f1->fd.largest_seqno < f2->fd.largest_seqno;
|
||||
}))
|
||||
(*std::max_element(level_files.begin(), level_files.end(),
|
||||
[](FileMetaData* f1, FileMetaData* f2) {
|
||||
return f1->fd.largest_seqno <
|
||||
f2->fd.largest_seqno;
|
||||
}))
|
||||
->fd.largest_seqno;
|
||||
// should only assign seqno to current level's largest seqno when
|
||||
// the file fits
|
||||
|
||||
@@ -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_) {
|
||||
|
||||
+4
-4
@@ -351,7 +351,7 @@ Status FlushJob::MemPurge() {
|
||||
|
||||
// Measure purging time.
|
||||
const uint64_t start_micros = clock_->NowMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUNanos() / 1000;
|
||||
const uint64_t start_cpu_micros = clock_->CPUMicros();
|
||||
|
||||
MemTable* new_mem = nullptr;
|
||||
// For performance/log investigation purposes:
|
||||
@@ -603,7 +603,7 @@ Status FlushJob::MemPurge() {
|
||||
TEST_SYNC_POINT("DBImpl::FlushJob:MemPurgeUnsuccessful");
|
||||
}
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUMicros() - start_cpu_micros;
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Mempurge lasted %" PRIu64
|
||||
" microseconds, and %" PRIu64
|
||||
@@ -789,7 +789,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = clock_->NowMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUNanos() / 1000;
|
||||
const uint64_t start_cpu_micros = clock_->CPUMicros();
|
||||
Status s;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
@@ -976,7 +976,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUMicros() - start_cpu_micros;
|
||||
stats.micros = micros;
|
||||
stats.cpu_micros = cpu_micros;
|
||||
|
||||
|
||||
+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));
|
||||
|
||||
+14
-12
@@ -494,8 +494,8 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// TODO(myabandeh): Not sure how batch_count could be 0 here.
|
||||
if (batch_count > 0) {
|
||||
uint64_t min_wal_number_to_keep = 0;
|
||||
assert(edit_list.size() > 0);
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
assert(edit_list.size() > 0);
|
||||
// Note that if mempurge is successful, the edit_list will
|
||||
// not be applicable (contains info of new min_log number to keep,
|
||||
// and level 0 file path of SST file created during normal flush,
|
||||
@@ -504,23 +504,26 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, *cfd, edit_list, memtables_to_flush, prep_tracker);
|
||||
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// manifest entry for the last file flushed.
|
||||
edit_list.back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, *cfd, edit_list);
|
||||
}
|
||||
edit_list.back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (vset->db_options()->track_and_verify_wals_in_manifest) {
|
||||
if (!vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, *cfd, edit_list);
|
||||
}
|
||||
if (min_wal_number_to_keep >
|
||||
vset->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(min_wal_number_to_keep);
|
||||
edit_list.push_back(wal_deletion.get());
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"MemTableList::TryInstallMemtableFlushResults:"
|
||||
"AfterComputeMinWalToKeep",
|
||||
nullptr);
|
||||
}
|
||||
|
||||
const auto manifest_write_cb = [this, cfd, batch_count, log_buffer,
|
||||
@@ -805,15 +808,14 @@ Status InstallMemtableAtomicFlushResults(
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, cfds, edit_lists, mems_list, prep_tracker);
|
||||
edit_lists.back().back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
} else {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfds, edit_lists);
|
||||
}
|
||||
edit_lists.back().back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (vset->db_options()->track_and_verify_wals_in_manifest) {
|
||||
if (!vset->db_options()->allow_2pc) {
|
||||
min_wal_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfds, edit_lists);
|
||||
}
|
||||
if (min_wal_number_to_keep > vset->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(min_wal_number_to_keep);
|
||||
|
||||
@@ -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,
|
||||
|
||||
+34
-31
@@ -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());
|
||||
@@ -114,15 +114,18 @@ Status TableCache::GetTableReader(
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, fopts, &file, nullptr);
|
||||
}
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
if (s.IsPathNotFound()) {
|
||||
if (s.ok()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
} else if (s.IsPathNotFound()) {
|
||||
fname = Rocks2LevelTableFileName(fname);
|
||||
s = PrepareIOFromReadOptions(ro, ioptions_.clock, fopts.io_options);
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, file_options, &file,
|
||||
nullptr);
|
||||
}
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
if (s.ok()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -160,7 +163,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 +213,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 +248,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 +399,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 +455,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 +486,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 +562,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 +615,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 +640,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 +677,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 +704,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",
|
||||
@@ -373,7 +394,7 @@ void VersionEditHandler::CheckIterationResult(const log::Reader& reader,
|
||||
if (s->ok()) {
|
||||
version_set_->GetColumnFamilySet()->UpdateMaxColumnFamily(
|
||||
version_edit_params_.max_column_family_);
|
||||
version_set_->MarkMinLogNumberToKeep2PC(
|
||||
version_set_->MarkMinLogNumberToKeep(
|
||||
version_edit_params_.min_log_number_to_keep_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.prev_log_number_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.log_number_);
|
||||
@@ -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_) {
|
||||
@@ -936,12 +970,11 @@ void DumpManifestHandler::CheckIterationResult(const log::Reader& reader,
|
||||
fprintf(stdout,
|
||||
"next_file_number %" PRIu64 " last_sequence %" PRIu64
|
||||
" prev_log_number %" PRIu64 " max_column_family %" PRIu32
|
||||
" min_log_number_to_keep "
|
||||
"%" PRIu64 "\n",
|
||||
" min_log_number_to_keep %" PRIu64 "\n",
|
||||
version_set_->current_next_file_number(),
|
||||
version_set_->LastSequence(), version_set_->prev_log_number(),
|
||||
version_set_->column_family_set_->GetMaxColumnFamily(),
|
||||
version_set_->min_log_number_to_keep_2pc());
|
||||
version_set_->min_log_number_to_keep());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+107
-62
@@ -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));
|
||||
@@ -1964,7 +1965,8 @@ void Version::MultiGetBlob(
|
||||
void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
PinnableSlice* value, std::string* timestamp, Status* status,
|
||||
MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq, bool* value_found,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
PinnedIteratorsManager* pinned_iters_mgr, bool* value_found,
|
||||
bool* key_exists, SequenceNumber* seq, ReadCallback* callback,
|
||||
bool* is_blob, bool do_merge) {
|
||||
Slice ikey = k.internal_key();
|
||||
@@ -1977,7 +1979,6 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
*key_exists = true;
|
||||
}
|
||||
|
||||
PinnedIteratorsManager pinned_iters_mgr;
|
||||
uint64_t tracing_get_id = BlockCacheTraceHelper::kReservedGetId;
|
||||
if (vset_ && vset_->block_cache_tracer_ &&
|
||||
vset_->block_cache_tracer_->is_tracing_enabled()) {
|
||||
@@ -1991,17 +1992,18 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
bool* const is_blob_to_use = is_blob ? is_blob : &is_blob_index;
|
||||
BlobFetcher blob_fetcher(this, read_options);
|
||||
|
||||
assert(pinned_iters_mgr);
|
||||
GetContext get_context(
|
||||
user_comparator(), merge_operator_, info_log_, db_statistics_,
|
||||
status->ok() ? GetContext::kNotFound : GetContext::kMerge, user_key,
|
||||
do_merge ? value : nullptr, do_merge ? timestamp : nullptr, value_found,
|
||||
merge_context, do_merge, max_covering_tombstone_seq, clock_, seq,
|
||||
merge_operator_ ? &pinned_iters_mgr : nullptr, callback, is_blob_to_use,
|
||||
merge_operator_ ? pinned_iters_mgr : nullptr, callback, is_blob_to_use,
|
||||
tracing_get_id, &blob_fetcher);
|
||||
|
||||
// Pin blocks that we read to hold merge operands
|
||||
if (merge_operator_) {
|
||||
pinned_iters_mgr.StartPinning();
|
||||
pinned_iters_mgr->StartPinning();
|
||||
}
|
||||
|
||||
FilePicker fp(user_key, ikey, &storage_info_.level_files_brief_,
|
||||
@@ -2026,7 +2028,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()),
|
||||
@@ -2187,16 +2189,35 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
MultiGetRange keys_with_blobs_range(*range, range->begin(), range->end());
|
||||
// blob_file => [[blob_idx, it], ...]
|
||||
std::unordered_map<uint64_t, BlobReadRequests> blob_rqs;
|
||||
int level = -1;
|
||||
|
||||
while (f != nullptr) {
|
||||
MultiGetRange file_range = fp.CurrentFileRange();
|
||||
bool timer_enabled =
|
||||
GetPerfLevel() >= PerfLevel::kEnableTimeExceptForMutex &&
|
||||
get_perf_context()->per_level_perf_context_enabled;
|
||||
|
||||
// Report MultiGet stats per level.
|
||||
if (level >= 0 && level != (int)fp.GetHitFileLevel()) {
|
||||
// Dump the stats if the search has moved to the next level and
|
||||
// reset for next level.
|
||||
RecordInHistogram(db_statistics_,
|
||||
NUM_INDEX_AND_FILTER_BLOCKS_READ_PER_LEVEL,
|
||||
num_index_read + num_filter_read);
|
||||
RecordInHistogram(db_statistics_, NUM_DATA_BLOCKS_READ_PER_LEVEL,
|
||||
num_data_read);
|
||||
RecordInHistogram(db_statistics_, NUM_SST_READ_PER_LEVEL, num_sst_read);
|
||||
num_filter_read = 0;
|
||||
num_index_read = 0;
|
||||
num_data_read = 0;
|
||||
num_sst_read = 0;
|
||||
level = fp.GetHitFileLevel();
|
||||
}
|
||||
|
||||
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()),
|
||||
@@ -2237,6 +2258,11 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
num_filter_read += get_context.get_context_stats_.num_filter_read;
|
||||
num_data_read += get_context.get_context_stats_.num_data_read;
|
||||
num_sst_read += get_context.get_context_stats_.num_sst_read;
|
||||
// Reset these stats since they're specific to a level
|
||||
get_context.get_context_stats_.num_index_read = 0;
|
||||
get_context.get_context_stats_.num_filter_read = 0;
|
||||
get_context.get_context_stats_.num_data_read = 0;
|
||||
get_context.get_context_stats_.num_sst_read = 0;
|
||||
|
||||
// report the counters before returning
|
||||
if (get_context.State() != GetContext::kNotFound &&
|
||||
@@ -2313,22 +2339,6 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
}
|
||||
|
||||
// Report MultiGet stats per level.
|
||||
if (fp.IsHitFileLastInLevel()) {
|
||||
// Dump the stats if this is the last file of this level and reset for
|
||||
// next level.
|
||||
RecordInHistogram(db_statistics_,
|
||||
NUM_INDEX_AND_FILTER_BLOCKS_READ_PER_LEVEL,
|
||||
num_index_read + num_filter_read);
|
||||
RecordInHistogram(db_statistics_, NUM_DATA_BLOCKS_READ_PER_LEVEL,
|
||||
num_data_read);
|
||||
RecordInHistogram(db_statistics_, NUM_SST_READ_PER_LEVEL, num_sst_read);
|
||||
num_filter_read = 0;
|
||||
num_index_read = 0;
|
||||
num_data_read = 0;
|
||||
num_sst_read = 0;
|
||||
}
|
||||
|
||||
RecordInHistogram(db_statistics_, SST_BATCH_SIZE, batch_size);
|
||||
if (!s.ok() || file_picker_range.empty()) {
|
||||
break;
|
||||
@@ -2336,6 +2346,13 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
f = fp.GetNextFile();
|
||||
}
|
||||
|
||||
// Dump stats for most recent level
|
||||
RecordInHistogram(db_statistics_, NUM_INDEX_AND_FILTER_BLOCKS_READ_PER_LEVEL,
|
||||
num_index_read + num_filter_read);
|
||||
RecordInHistogram(db_statistics_, NUM_DATA_BLOCKS_READ_PER_LEVEL,
|
||||
num_data_read);
|
||||
RecordInHistogram(db_statistics_, NUM_SST_READ_PER_LEVEL, num_sst_read);
|
||||
|
||||
if (s.ok() && !blob_rqs.empty()) {
|
||||
MultiGetBlob(read_options, keys_with_blobs_range, blob_rqs);
|
||||
}
|
||||
@@ -4096,7 +4113,7 @@ void VersionSet::Reset() {
|
||||
}
|
||||
db_id_.clear();
|
||||
next_file_number_.store(2);
|
||||
min_log_number_to_keep_2pc_.store(0);
|
||||
min_log_number_to_keep_.store(0);
|
||||
manifest_file_number_ = 0;
|
||||
options_file_number_ = 0;
|
||||
pending_manifest_file_number_ = 0;
|
||||
@@ -4158,9 +4175,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 +4272,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 +4391,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 +4545,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 {
|
||||
@@ -4553,8 +4580,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
}
|
||||
|
||||
if (last_min_log_number_to_keep != 0) {
|
||||
// Should only be set in 2PC mode.
|
||||
MarkMinLogNumberToKeep2PC(last_min_log_number_to_keep);
|
||||
MarkMinLogNumberToKeep(last_min_log_number_to_keep);
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
|
||||
@@ -4562,6 +4588,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 +4656,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 +4796,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 +4812,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 +4830,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,
|
||||
@@ -4892,7 +4934,7 @@ Status VersionSet::Recover(
|
||||
",min_log_number_to_keep is %" PRIu64 "\n",
|
||||
manifest_path.c_str(), manifest_file_number_, next_file_number_.load(),
|
||||
last_sequence_.load(), log_number, prev_log_number_,
|
||||
column_family_set_->GetMaxColumnFamily(), min_log_number_to_keep_2pc());
|
||||
column_family_set_->GetMaxColumnFamily(), min_log_number_to_keep());
|
||||
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
@@ -5297,9 +5339,9 @@ void VersionSet::MarkFileNumberUsed(uint64_t number) {
|
||||
}
|
||||
// Called only either from ::LogAndApply which is protected by mutex or during
|
||||
// recovery which is single-threaded.
|
||||
void VersionSet::MarkMinLogNumberToKeep2PC(uint64_t number) {
|
||||
if (min_log_number_to_keep_2pc_.load(std::memory_order_relaxed) < number) {
|
||||
min_log_number_to_keep_2pc_.store(number, std::memory_order_relaxed);
|
||||
void VersionSet::MarkMinLogNumberToKeep(uint64_t number) {
|
||||
if (min_log_number_to_keep_.load(std::memory_order_relaxed) < number) {
|
||||
min_log_number_to_keep_.store(number, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5421,7 +5463,7 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
// min_log_number_to_keep is for the whole db, not for specific column family.
|
||||
// So it does not need to be set for every column family, just need to be set once.
|
||||
// Since default CF can never be dropped, we set the min_log to the default CF here.
|
||||
uint64_t min_log = min_log_number_to_keep_2pc();
|
||||
uint64_t min_log = min_log_number_to_keep();
|
||||
if (min_log != 0) {
|
||||
edit.SetMinLogNumberToKeep(min_log);
|
||||
}
|
||||
@@ -5431,6 +5473,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 +5646,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 +5686,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 +5778,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 +5794,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,
|
||||
|
||||
+21
-14
@@ -708,9 +708,11 @@ class Version {
|
||||
// If the key has any merge operands then store them in
|
||||
// merge_context.operands_list and don't merge the operands
|
||||
// REQUIRES: lock is not held
|
||||
// REQUIRES: pinned_iters_mgr != nullptr
|
||||
void Get(const ReadOptions&, const LookupKey& key, PinnableSlice* value,
|
||||
std::string* timestamp, Status* status, MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
PinnedIteratorsManager* pinned_iters_mgr,
|
||||
bool* value_found = nullptr, bool* key_exists = nullptr,
|
||||
SequenceNumber* seq = nullptr, ReadCallback* callback = nullptr,
|
||||
bool* is_blob = nullptr, bool do_merge = true);
|
||||
@@ -851,11 +853,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_;
|
||||
@@ -1104,8 +1101,8 @@ class VersionSet {
|
||||
|
||||
uint64_t current_next_file_number() const { return next_file_number_.load(); }
|
||||
|
||||
uint64_t min_log_number_to_keep_2pc() const {
|
||||
return min_log_number_to_keep_2pc_.load();
|
||||
uint64_t min_log_number_to_keep() const {
|
||||
return min_log_number_to_keep_.load();
|
||||
}
|
||||
|
||||
// Allocate and return a new file number
|
||||
@@ -1163,7 +1160,7 @@ class VersionSet {
|
||||
// Mark the specified log number as deleted
|
||||
// REQUIRED: this is only called during single-threaded recovery or repair, or
|
||||
// from ::LogAndApply where the global mutex is held.
|
||||
void MarkMinLogNumberToKeep2PC(uint64_t number);
|
||||
void MarkMinLogNumberToKeep(uint64_t number);
|
||||
|
||||
// Return the log file number for the log file that is currently
|
||||
// being compacted, or zero if there is no such log file.
|
||||
@@ -1172,10 +1169,12 @@ class VersionSet {
|
||||
// Returns the minimum log number which still has data not flushed to any SST
|
||||
// file.
|
||||
// In non-2PC mode, all the log numbers smaller than this number can be safely
|
||||
// deleted.
|
||||
// deleted, although we still use `min_log_number_to_keep_` to determine when
|
||||
// to delete a WAL file.
|
||||
uint64_t MinLogNumberWithUnflushedData() const {
|
||||
return PreComputeMinLogNumberWithUnflushedData(nullptr);
|
||||
}
|
||||
|
||||
// Returns the minimum log number which still has data not flushed to any SST
|
||||
// file.
|
||||
// Empty column families' log number is considered to be
|
||||
@@ -1273,6 +1272,10 @@ class VersionSet {
|
||||
uint64_t min_pending_output);
|
||||
|
||||
ColumnFamilySet* GetColumnFamilySet() { return column_family_set_.get(); }
|
||||
RefedColumnFamilySet GetRefedColumnFamilySet() {
|
||||
return RefedColumnFamilySet(GetColumnFamilySet());
|
||||
}
|
||||
|
||||
const FileOptions& file_options() { return file_options_; }
|
||||
void ChangeFileOptions(const MutableDBOptions& new_options) {
|
||||
file_options_.writable_file_max_buffer_size =
|
||||
@@ -1375,9 +1378,8 @@ class VersionSet {
|
||||
const ImmutableDBOptions* const db_options_;
|
||||
std::atomic<uint64_t> next_file_number_;
|
||||
// Any WAL number smaller than this should be ignored during recovery,
|
||||
// and is qualified for being deleted in 2PC mode. In non-2PC mode, this
|
||||
// number is ignored.
|
||||
std::atomic<uint64_t> min_log_number_to_keep_2pc_ = {0};
|
||||
// and is qualified for being deleted.
|
||||
std::atomic<uint64_t> min_log_number_to_keep_ = {0};
|
||||
uint64_t manifest_file_number_;
|
||||
uint64_t options_file_number_;
|
||||
uint64_t options_file_size_;
|
||||
@@ -1386,6 +1388,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 +1439,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());
|
||||
}
|
||||
}
|
||||
@@ -3200,6 +3204,7 @@ TEST_F(VersionSetTestMissingFiles, NoFileMissing) {
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTestMissingFiles, MinLogNumberToKeep2PC) {
|
||||
db_options_.allow_2pc = true;
|
||||
NewDB();
|
||||
|
||||
SstInfo sst(100, kDefaultColumnFamilyName, "a");
|
||||
@@ -3211,12 +3216,12 @@ TEST_F(VersionSetTestMissingFiles, MinLogNumberToKeep2PC) {
|
||||
edit.AddFile(0, file_metas[0]);
|
||||
edit.SetMinLogNumberToKeep(kMinWalNumberToKeep2PC);
|
||||
ASSERT_OK(LogAndApplyToDefaultCF(edit));
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep_2pc(), kMinWalNumberToKeep2PC);
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep(), kMinWalNumberToKeep2PC);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
CreateNewManifest();
|
||||
ReopenDB();
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep_2pc(), kMinWalNumberToKeep2PC);
|
||||
ASSERT_EQ(versions_->min_log_number_to_keep(), kMinWalNumberToKeep2PC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
+5
-17
@@ -166,7 +166,7 @@ class PosixClock : public SystemClock {
|
||||
defined(OS_AIX) || (defined(__MACH__) && defined(__MAC_10_12))
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
|
||||
return static_cast<uint64_t>(ts.tv_sec) * 1000000000;
|
||||
return (static_cast<uint64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec) / 1000;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -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
+29
-28
@@ -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.
|
||||
@@ -325,14 +325,7 @@ class PosixFileSystem : public FileSystem {
|
||||
SetFD_CLOEXEC(fd, &options);
|
||||
|
||||
if (options.use_mmap_writes) {
|
||||
if (!checkedDiskForMmap_) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fname)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
checkedDiskForMmap_ = true;
|
||||
}
|
||||
MaybeForceDisableMmap(fd);
|
||||
}
|
||||
if (options.use_mmap_writes && !forceMmapOff_) {
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
|
||||
@@ -431,14 +424,7 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
|
||||
if (options.use_mmap_writes) {
|
||||
if (!checkedDiskForMmap_) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fname)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
checkedDiskForMmap_ = true;
|
||||
}
|
||||
MaybeForceDisableMmap(fd);
|
||||
}
|
||||
if (options.use_mmap_writes && !forceMmapOff_) {
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
|
||||
@@ -753,8 +739,10 @@ class PosixFileSystem : public FileSystem {
|
||||
const IOOptions& /*opts*/,
|
||||
IODebugContext* /*dbg*/) override {
|
||||
if (link(src.c_str(), target.c_str()) != 0) {
|
||||
if (errno == EXDEV) {
|
||||
return IOStatus::NotSupported("No cross FS links allowed");
|
||||
if (errno == EXDEV || errno == ENOTSUP) {
|
||||
return IOStatus::NotSupported(errno == EXDEV
|
||||
? "No cross FS links allowed"
|
||||
: "Links not supported by FS");
|
||||
}
|
||||
return IOError("while link file to " + target, src, errno);
|
||||
}
|
||||
@@ -997,8 +985,7 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
bool checkedDiskForMmap_;
|
||||
bool forceMmapOff_; // do we override Env options?
|
||||
bool forceMmapOff_ = false; // do we override Env options?
|
||||
|
||||
// Returns true iff the named directory exists and is a directory.
|
||||
virtual bool DirExists(const std::string& dname) {
|
||||
@@ -1009,10 +996,10 @@ class PosixFileSystem : public FileSystem {
|
||||
return false; // stat() failed return false
|
||||
}
|
||||
|
||||
bool SupportsFastAllocate(const std::string& path) {
|
||||
bool SupportsFastAllocate(int fd) {
|
||||
#ifdef ROCKSDB_FALLOCATE_PRESENT
|
||||
struct statfs s;
|
||||
if (statfs(path.c_str(), &s)) {
|
||||
if (fstatfs(fd, &s)) {
|
||||
return false;
|
||||
}
|
||||
switch (s.f_type) {
|
||||
@@ -1026,11 +1013,26 @@ class PosixFileSystem : public FileSystem {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
(void)path;
|
||||
(void)fd;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MaybeForceDisableMmap(int fd) {
|
||||
static std::once_flag s_check_disk_for_mmap_once;
|
||||
assert(this == FileSystem::Default().get());
|
||||
std::call_once(
|
||||
s_check_disk_for_mmap_once,
|
||||
[this](int fdesc) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fdesc)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
},
|
||||
fd);
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
bool IsIOUringEnabled() {
|
||||
if (RocksDbIOUringEnable && RocksDbIOUringEnable()) {
|
||||
@@ -1094,8 +1096,7 @@ size_t PosixFileSystem::GetLogicalBlockSizeForWriteIfNeeded(
|
||||
}
|
||||
|
||||
PosixFileSystem::PosixFileSystem()
|
||||
: checkedDiskForMmap_(false),
|
||||
forceMmapOff_(false),
|
||||
: forceMmapOff_(false),
|
||||
page_size_(getpagesize()),
|
||||
allow_non_owner_access_(true) {
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
@@ -1124,8 +1125,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() {
|
||||
}
|
||||
|
||||
@@ -742,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,
|
||||
|
||||
Loaded 100 of 212 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user