mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ac4a218de |
No files matched your search
@@ -1,41 +0,0 @@
|
||||
name: Check buck targets and code format
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
check:
|
||||
name: Check TARGETS file and code format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout feature branch
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch from upstream
|
||||
run: |
|
||||
git remote add upstream https://github.com/facebook/rocksdb.git && git fetch upstream
|
||||
|
||||
- name: Where am I
|
||||
run: |
|
||||
echo git status && git status
|
||||
echo "git remote -v" && git remote -v
|
||||
echo git branch && git branch
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
|
||||
- name: Install Dependencies
|
||||
run: python -m pip install --upgrade pip
|
||||
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
|
||||
- name: Download clang-format-diff.py
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: https://raw.githubusercontent.com/llvm-mirror/clang/master/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: make check-format
|
||||
|
||||
- name: Compare buckify output
|
||||
run: make check-buck-targets
|
||||
+5
-58
@@ -50,18 +50,12 @@ env:
|
||||
- JOB_NAME=examples # 5-7 minutes
|
||||
- JOB_NAME=cmake # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc8 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9-c++20 # 3-5 minutes
|
||||
- JOB_NAME=cmake-mingw # 3 minutes
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: osx
|
||||
@@ -76,25 +70,7 @@ matrix:
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
compiler: clang
|
||||
# Exclude all but most unique cmake variants for pull requests, but build all in branches
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: amd64
|
||||
env: JOB_NAME=cmake
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: amd64
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: amd64
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
# Exclude most osx, arm64 and ppc64le tests for pull requests, but build in branches
|
||||
# Temporarily disable ppc64le unit tests in PRs until Travis gets its act together (#6653)
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=platform_dependent
|
||||
# NB: the cmake build is a partial java test
|
||||
- if: type = pull_request
|
||||
os: osx
|
||||
@@ -140,14 +116,14 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
- if: type = pull_request
|
||||
os : osx
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /java/
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=java_test
|
||||
@@ -181,22 +157,6 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
|
||||
install:
|
||||
- if [ "${TRAVIS_OS_NAME}" == osx ]; then
|
||||
@@ -206,10 +166,6 @@ install:
|
||||
sudo apt-get install -y g++-8;
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-gcc9 ] || [ "${JOB_NAME}" == cmake-gcc9-c++20 ]; then
|
||||
sudo apt-get install -y g++-9;
|
||||
CC=gcc-9 && CXX=g++-9;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-mingw ]; then
|
||||
sudo apt-get install -y mingw-w64 ;
|
||||
fi
|
||||
@@ -245,7 +201,7 @@ before_script:
|
||||
- ulimit -n 8192
|
||||
|
||||
script:
|
||||
- date; ${CXX} --version
|
||||
- ${CXX} --version
|
||||
- if [ `command -v ccache` ]; then ccache -C; fi
|
||||
- case $TEST_GROUP in
|
||||
platform_dependent)
|
||||
@@ -279,16 +235,7 @@ script:
|
||||
mkdir build && cd build && cmake -DJNI=1 -DWITH_GFLAGS=OFF .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
cmake*)
|
||||
case $JOB_NAME in
|
||||
*-c++20)
|
||||
OPT=-DCMAKE_CXX_STANDARD=20
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
status_checked)
|
||||
OPT=-DTRAVIS V=1 ASSERT_STATUS_CHECKED=1 make -j4 check_some
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
esac
|
||||
notifications:
|
||||
|
||||
+6
-25
@@ -83,10 +83,6 @@ else()
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" OFF)
|
||||
endif()
|
||||
|
||||
if( NOT DEFINED CMAKE_CXX_STANDARD )
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
endif()
|
||||
|
||||
include(CMakeDependentOption)
|
||||
CMAKE_DEPENDENT_OPTION(WITH_GFLAGS "build with GFlags" ON
|
||||
"NOT MSVC;NOT MINGW" OFF)
|
||||
@@ -195,6 +191,7 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format -fno-asynchronous-unwind-tables")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
include(CheckCXXCompilerFlag)
|
||||
@@ -257,7 +254,6 @@ include(CheckCXXSourceCompiles)
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
@@ -390,15 +386,7 @@ if(MSVC)
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1")
|
||||
|
||||
# Minimal Build is deprecated after MSVC 2015
|
||||
if( MSVC_VERSION GREATER 1900 )
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm-")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gm")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1 /Gm")
|
||||
endif()
|
||||
if(WITH_RUNTIME_DEBUG)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /${RUNTIME_LIBRARY}d")
|
||||
@@ -518,14 +506,12 @@ find_package(Threads REQUIRED)
|
||||
# Main library source code
|
||||
|
||||
set(SOURCES
|
||||
cache/cache.cc
|
||||
cache/clock_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_file_addition.cc
|
||||
db/blob/blob_file_garbage.cc
|
||||
db/blob/blob_file_meta.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
db/column_family.cc
|
||||
@@ -608,7 +594,6 @@ set(SOURCES
|
||||
memory/arena.cc
|
||||
memory/concurrent_arena.cc
|
||||
memory/jemalloc_nodump_allocator.cc
|
||||
memory/memkind_kmem_allocator.cc
|
||||
memtable/alloc_tracker.cc
|
||||
memtable/hash_linklist_rep.cc
|
||||
memtable/hash_skiplist_rep.cc
|
||||
@@ -633,6 +618,7 @@ set(SOURCES
|
||||
options/options.cc
|
||||
options/options_helper.cc
|
||||
options/options_parser.cc
|
||||
options/options_sanity_check.cc
|
||||
port/stack_trace.cc
|
||||
table/adaptive/adaptive_table_factory.cc
|
||||
table/block_based/binary_search_index_reader.cc
|
||||
@@ -861,6 +847,7 @@ if(ROCKSDB_BUILD_SHARED)
|
||||
LINKER_LANGUAGE CXX
|
||||
VERSION ${rocksdb_VERSION}
|
||||
SOVERSION ${rocksdb_VERSION_MAJOR}
|
||||
CXX_STANDARD 11
|
||||
OUTPUT_NAME "rocksdb")
|
||||
endif()
|
||||
endif()
|
||||
@@ -999,7 +986,6 @@ if(WITH_TESTS)
|
||||
db/db_logical_block_size_cache_test.cc
|
||||
db/db_universal_compaction_test.cc
|
||||
db/db_wal_test.cc
|
||||
db/db_with_timestamp_compaction_test.cc
|
||||
db/db_write_test.cc
|
||||
db/dbformat_test.cc
|
||||
db/deletefile_test.cc
|
||||
@@ -1042,7 +1028,6 @@ if(WITH_TESTS)
|
||||
logging/env_logger_test.cc
|
||||
logging/event_logger_test.cc
|
||||
memory/arena_test.cc
|
||||
memory/memkind_kmem_allocator_test.cc
|
||||
memtable/inlineskiplist_test.cc
|
||||
memtable/skiplist_test.cc
|
||||
memtable/write_buffer_manager_test.cc
|
||||
@@ -1063,7 +1048,6 @@ if(WITH_TESTS)
|
||||
table/merger_test.cc
|
||||
table/sst_file_reader_test.cc
|
||||
table/table_test.cc
|
||||
table/block_fetcher_test.cc
|
||||
tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
@@ -1085,10 +1069,8 @@ if(WITH_TESTS)
|
||||
util/slice_test.cc
|
||||
util/slice_transform_test.cc
|
||||
util/timer_queue_test.cc
|
||||
util/timer_test.cc
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
util/work_queue_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
utilities/blob_db/blob_db_test.cc
|
||||
utilities/cassandra/cassandra_functional_test.cc
|
||||
@@ -1108,7 +1090,6 @@ if(WITH_TESTS)
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
utilities/transactions/transaction_lock_mgr_test.cc
|
||||
utilities/transactions/write_prepared_transaction_test.cc
|
||||
utilities/transactions/write_unprepared_transaction_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
@@ -1142,7 +1123,7 @@ if(WITH_TESTS)
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
)
|
||||
)
|
||||
|
||||
foreach(sourcefile ${TESTS})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
@@ -1152,7 +1133,7 @@ if(WITH_TESTS)
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
OUTPUT_NAME ${exename}${ARTIFACT_SUFFIX}
|
||||
)
|
||||
)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} testharness gtest ${ROCKSDB_LIB})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
|
||||
+1
-73
@@ -1,93 +1,21 @@
|
||||
# Rocksdb Change Log
|
||||
## 6.10.4 (11/15/2020)
|
||||
### Bug Fixes
|
||||
* Fixed a bug of encoding and parsing BlockBasedTableOptions::read_amp_bytes_per_bit as a 64-bit integer.
|
||||
* Fixed the logic of populating native data structure for `read_amp_bytes_per_bit` during OPTIONS file parsing on big-endian architecture. Without this fix, original code introduced in PR7659, when running on big-endian machine, can mistakenly store read_amp_bytes_per_bit (an uint32) in little endian format. Future access to `read_amp_bytes_per_bit` will give wrong values. Little endian architecture is not affected.
|
||||
|
||||
## 6.10.3 (6/16/2020)
|
||||
### Bug fix
|
||||
* Fix potential file descriptor leakage in PosixEnv's IsDirectory() and NewRandomAccessFile().
|
||||
* Best-efforts recovery ignores CURRENT file completely. If CURRENT file is missing during recovery, best-efforts recovery still proceeds with MANIFEST file(s).
|
||||
* In best-efforts recovery, an error that is not Corruption or IOError::kNotFound or IOError::kPathNotFound will be overwritten silently. Fix this by checking all non-ok cases and return early.
|
||||
* Fail recovery and report once hitting a physical log record checksum mismatch, while reading MANIFEST. RocksDB should not continue processing the MANIFEST any further.
|
||||
* Fix a bug of wrong iterator result if another thread finishes an update and a DB flush between two statement.
|
||||
|
||||
## 6.10.2 (6/5/2020)
|
||||
### Bug fix
|
||||
* Fix false negative from the VerifyChecksum() API when there is a checksum mismatch in an index partition block in a BlockBasedTable format table file (index_type is kTwoLevelIndexSearch).
|
||||
|
||||
## 6.10.1 (5/27/2020)
|
||||
### Bug fix
|
||||
* Remove "u'<string>'" in TARGETS file.
|
||||
* Fix db_stress_lib target in buck.
|
||||
|
||||
## 6.10 (5/2/2020)
|
||||
### Behavior Changes
|
||||
* Disable delete triggered compaction (NewCompactOnDeletionCollectorFactory) in universal compaction mode and num_levels = 1 in order to avoid a corruption bug.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix wrong result being read from ingested file. May happen when a key in the file happen to be prefix of another key also in the file. The issue can further cause more data corruption. The issue exists with rocksdb >= 5.0.0 since DB::IngestExternalFile() was introduced.
|
||||
* Finish implementation of BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey. It's now ready for use. Significantly reduces read amplification in some setups, especially for iterator seeks.
|
||||
* Fix a bug by updating CURRENT file so that it points to the correct MANIFEST file after best-efforts recovery.
|
||||
* Fixed a bug where ColumnFamilyHandle objects were not cleaned up in case an error happened during BlobDB's open after the base DB had been opened.
|
||||
* Fix a potential undefined behavior caused by trying to dereference nullable pointer (timestamp argument) in DB::MultiGet.
|
||||
* Fix a bug caused by not including user timestamp in MultiGet LookupKey construction. This can lead to wrong query result since the trailing bytes of a user key, if not shorter than timestamp, will be mistaken for user timestamp.
|
||||
* Fix a bug caused by using wrong compare function when sorting the input keys of MultiGet with timestamps.
|
||||
* Upgraded version of bzip library (1.0.6 -> 1.0.8) used with RocksJava to address potential vulnerabilities if an attacker can manipulate compressed data saved and loaded by RocksDB (not normal). See issue #6703.
|
||||
* Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true.
|
||||
* Fix possible false NotFound status from batched MultiGet using index type kHashSearch.
|
||||
* Fix corruption caused by enabling delete triggered compaction (NewCompactOnDeletionCollectorFactory) in universal compaction mode, along with parallel compactions. The bug can result in two parallel compactions picking the same input files, resulting in the DB resurrecting older and deleted versions of some keys.
|
||||
* Fix a use-after-free bug in best-efforts recovery. column_family_memtables_ needs to point to valid ColumnFamilySet.
|
||||
* Let best-efforts recovery ignore corrupted files during table loading.
|
||||
* Fix a bug when making options.bottommost_compression, options.compression_opts and options.bottommost_compression_opts dynamically changeable: the modified values are not written to option files or returned back to users when being queried.
|
||||
* Fix a bug where index key comparisons were unaccounted in `PerfContext::user_key_comparison_count` for lookups in files written with `format_version >= 3`.
|
||||
* Fix many bloom.filter statistics not being updated in batch MultiGet.
|
||||
|
||||
|
||||
### Public API Change
|
||||
* Add a ConfigOptions argument to the APIs dealing with converting options to and from strings and files. The ConfigOptions is meant to replace some of the options (such as input_strings_escaped and ignore_unknown_options) and allow for more parameters to be passed in the future without changing the function signature.
|
||||
* Add NewFileChecksumGenCrc32cFactory to the file checksum public API, such that the builtin Crc32c based file checksum generator factory can be used by applications.
|
||||
* Add IsDirectory to Env and FS to indicate if a path is a directory.
|
||||
* ldb now uses options.force_consistency_checks = true by default and "--disable_consistency_checks" is added to disable it.
|
||||
* Add ReadOptions::deadline to allow users to specify a deadline for MultiGet requests
|
||||
|
||||
### New Features
|
||||
* Added support for pipelined & parallel compression optimization for `BlockBasedTableBuilder`. This optimization makes block building, block compression and block appending a pipeline, and uses multiple threads to accelerate block compression. Users can set `CompressionOptions::parallel_threads` greater than 1 to enable compression parallelism. This feature is experimental for now.
|
||||
* Provide an allocator for memkind to be used with block cache. This is to work with memory technologies (Intel DCPMM is one such technology currently available) that require different libraries for allocation and management (such as PMDK and memkind). The high capacities available make it possible to provision large caches (up to several TBs in size) beyond what is achievable with DRAM.
|
||||
* Option `max_background_flushes` can be set dynamically using DB::SetDBOptions().
|
||||
* Added functionality in sst_dump tool to check the compressed file size for different compression levels and print the time spent on compressing files with each compression type. Added arguments `--compression_level_from` and `--compression_level_to` to report size of all compression levels and one compression_type must be specified with it so that it will report compressed sizes of one compression type with different levels.
|
||||
* Added statistics for redundant insertions into block cache: rocksdb.block.cache.*add.redundant. (There is currently no coordination to ensure that only one thread loads a table block when many threads are trying to access that same table block.)
|
||||
|
||||
### Performance Improvements
|
||||
* Improve performance of batch MultiGet with partitioned filters, by sharing block cache lookups to applicable filter blocks.
|
||||
* Reduced memory copies when fetching and uncompressing compressed blocks from sst files.
|
||||
|
||||
## 6.9.0 (03/29/2020)
|
||||
### Behavior changes
|
||||
* Since RocksDB 6.8, ttl-based FIFO compaction can drop a file whose oldest key becomes older than options.ttl while others have not. This fix reverts this and makes ttl-based FIFO compaction use the file's flush time as the criterion. This fix also requires that max_open_files = -1 and compaction_options_fifo.allow_compaction = false to function properly.
|
||||
|
||||
## Unreleased
|
||||
### Public API Change
|
||||
* Fix spelling so that API now has correctly spelled transaction state name `COMMITTED`, while the old misspelled `COMMITED` is still available as an alias.
|
||||
* Updated default format_version in BlockBasedTableOptions from 2 to 4. SST files generated with the new default can be read by RocksDB versions 5.16 and newer, and use more efficient encoding of keys in index blocks.
|
||||
* A new parameter `CreateBackupOptions` is added to both `BackupEngine::CreateNewBackup` and `BackupEngine::CreateNewBackupWithMetadata`, you can decrease CPU priority of `BackupEngine`'s background threads by setting `decrease_background_thread_cpu_priority` and `background_thread_cpu_priority` in `CreateBackupOptions`.
|
||||
* Updated the public API of SST file checksum. Introduce the FileChecksumGenFactory to create the FileChecksumGenerator for each SST file, such that the FileChecksumGenerator is not shared and it can be more general for checksum implementations. Changed the FileChecksumGenerator interface from Value, Extend, and GetChecksum to Update, Finalize, and GetChecksum. Finalize should be only called once after all data is processed to generate the final checksum. Temproal data should be maintained by the FileChecksumGenerator object itself and finally it can return the checksum string.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug where range tombstone blocks in ingested files were cached incorrectly during ingestion. If range tombstones were read from those incorrectly cached blocks, the keys they covered would be exposed.
|
||||
* Fix a data race that might cause crash when calling DB::GetCreationTimeOfOldestFile() by a small chance. The bug was introduced in 6.6 Release.
|
||||
* Fix a bug where a boolean value optimize_filters_for_hits was for max threads when calling load table handles after a flush or compaction. The value is correct to 1. The bug should not cause user visible problems.
|
||||
* Fix a bug which might crash the service when write buffer manager fails to insert the dummy handle to the block cache.
|
||||
|
||||
### Performance Improvements
|
||||
* In CompactRange, for levels starting from 0, if the level does not have any file with any key falling in the specified range, the level is skipped. So instead of always compacting from level 0, the compaction starts from the first level with keys in the specified range until the last such level.
|
||||
* Reduced memory copy when reading sst footer and blobdb in direct IO mode.
|
||||
* When restarting a database with large numbers of sst files, large amount of CPU time is spent on getting logical block size of the sst files, which slows down the starting progress, this inefficiency is optimized away with an internal cache for the logical block sizes.
|
||||
|
||||
### New Features
|
||||
* Basic support for user timestamp in iterator. Seek/SeekToFirst/Next and lower/upper bounds are supported. Reverse iteration is not supported. Merge is not considered.
|
||||
* When file lock failure when the lock is held by the current process, return acquiring time and thread ID in the error message.
|
||||
* Added a new option, best_efforts_recovery (default: false), to allow database to open in a db dir with missing table files. During best efforts recovery, missing table files are ignored, and database recovers to the most recent state without missing table file. Cross-column-family consistency is not guaranteed even if WAL is enabled.
|
||||
* options.bottommost_compression, options.compression_opts and options.bottommost_compression_opts are now dynamically changeable.
|
||||
|
||||
## 6.8.0 (02/24/2020)
|
||||
### Java API Changes
|
||||
|
||||
@@ -204,17 +204,8 @@ LDFLAGS += -lrados
|
||||
endif
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
# Detect what platform we're building on.
|
||||
# Export some common variables that might have been passed as Make variables
|
||||
# instead of environment variables.
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export COMPILE_WITH_ASAN="$(COMPILE_WITH_ASAN)"; \
|
||||
export COMPILE_WITH_TSAN="$(COMPILE_WITH_TSAN)"; \
|
||||
export COMPILE_WITH_UBSAN="$(COMPILE_WITH_UBSAN)"; \
|
||||
export PORTABLE="$(PORTABLE)"; \
|
||||
export ROCKSDB_NO_FBCODE="$(ROCKSDB_NO_FBCODE)"; \
|
||||
export USE_CLANG="$(USE_CLANG)"; \
|
||||
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# detect what platform we're building on
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; export PORTABLE="$(PORTABLE)"; "$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
export JAVAC_ARGS
|
||||
@@ -450,12 +441,7 @@ BENCHTOOLOBJECTS = $(BENCH_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
ANALYZETOOLOBJECTS = $(ANALYZER_LIB_SOURCES:.cc=.o)
|
||||
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
STRESSTOOLOBJECTS = $(STRESS_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
else
|
||||
STRESSTOOLOBJECTS = $(STRESS_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL) \
|
||||
$(TESTHARNESS)
|
||||
endif
|
||||
|
||||
EXPOBJECTS = $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
@@ -480,7 +466,6 @@ TESTS = \
|
||||
hash_test \
|
||||
random_test \
|
||||
thread_local_test \
|
||||
work_queue_test \
|
||||
rate_limiter_test \
|
||||
perf_context_test \
|
||||
iostats_context_test \
|
||||
@@ -519,7 +504,6 @@ TESTS = \
|
||||
column_family_test \
|
||||
table_properties_collector_test \
|
||||
arena_test \
|
||||
memkind_kmem_allocator_test \
|
||||
block_test \
|
||||
data_block_hash_index_test \
|
||||
cache_test \
|
||||
@@ -575,7 +559,6 @@ TESTS = \
|
||||
deletefile_test \
|
||||
obsolete_files_test \
|
||||
table_test \
|
||||
block_fetcher_test \
|
||||
delete_scheduler_test \
|
||||
options_test \
|
||||
options_settable_test \
|
||||
@@ -600,7 +583,6 @@ TESTS = \
|
||||
compaction_job_stats_test \
|
||||
option_change_migration_test \
|
||||
transaction_test \
|
||||
transaction_lock_mgr_test \
|
||||
ldb_cmd_test \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
@@ -623,8 +605,10 @@ TESTS = \
|
||||
defer_test \
|
||||
blob_file_addition_test \
|
||||
blob_file_garbage_test \
|
||||
timer_test \
|
||||
db_with_timestamp_compaction_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -645,15 +629,9 @@ PARALLEL_TEST = \
|
||||
persistent_cache_test \
|
||||
table_test \
|
||||
transaction_test \
|
||||
transaction_lock_mgr_test \
|
||||
write_prepared_transaction_test \
|
||||
write_unprepared_transaction_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
PARALLEL_TEST += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
|
||||
# options_settable_test doesn't pass with UBSAN as we use hack in the test
|
||||
ifdef COMPILE_WITH_UBSAN
|
||||
TESTS := $(shell echo $(TESTS) | sed 's/\boptions_settable_test\b//g')
|
||||
@@ -967,24 +945,20 @@ check: all
|
||||
$(MAKE) T="$$t" TMPD=$(TMPD) check_0; \
|
||||
else \
|
||||
for t in $(TESTS); do \
|
||||
echo "===== Running $$t (`date`)"; ./$$t || exit 1; done; \
|
||||
echo "===== Running $$t"; ./$$t || exit 1; done; \
|
||||
fi
|
||||
rm -rf $(TMPD)
|
||||
ifneq ($(PLATFORM), OS_AIX)
|
||||
$(PYTHON) tools/check_all_python.py
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
ifndef ASSERT_STATUS_CHECKED # not yet working with these tests
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
sh tools/rocksdb_dump_test.sh
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
$(MAKE) check-format
|
||||
$(MAKE) check-buck-targets
|
||||
|
||||
# TODO add ldb_tests
|
||||
check_some: $(SUBSET)
|
||||
for t in $(SUBSET); do echo "===== Running $$t (`date`)"; ./$$t || exit 1; done
|
||||
for t in $(SUBSET); do echo "===== Running $$t"; ./$$t || exit 1; done
|
||||
|
||||
.PHONY: ldb_tests
|
||||
ldb_tests: ldb
|
||||
@@ -1090,7 +1064,7 @@ ifneq ($(PAR_TEST),)
|
||||
parloop:
|
||||
ret_bad=0; \
|
||||
for t in $(PAR_TEST); do \
|
||||
echo "===== Running $$t in parallel $(NUM_PAR) (`date`)";\
|
||||
echo "===== Running $$t in parallel $(NUM_PAR)";\
|
||||
if [ $(db_test) -eq 1 ]; then \
|
||||
seq $(J) | v="$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
'timeout 2m ./db_test --gtest_filter=$$v >> $$s/log-{} 2>1'; \
|
||||
@@ -1201,12 +1175,6 @@ tags0:
|
||||
format:
|
||||
build_tools/format-diff.sh
|
||||
|
||||
check-format:
|
||||
build_tools/format-diff.sh -c
|
||||
|
||||
check-buck-targets:
|
||||
buckifier/check_buck_targets.sh
|
||||
|
||||
package:
|
||||
bash build_tools/make_package.sh $(SHARED_MAJOR).$(SHARED_MINOR)
|
||||
|
||||
@@ -1269,9 +1237,6 @@ db_repl_stress: tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
arena_test: memory/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memkind_kmem_allocator_test: memory/memkind_kmem_allocator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
@@ -1330,9 +1295,6 @@ histogram_test: monitoring/histogram_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
thread_local_test: util/thread_local_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
work_queue_test: util/work_queue_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
corruption_test: db/corruption_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1351,9 +1313,6 @@ db_basic_test: db/db_basic_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
db_with_timestamp_basic_test: db/db_with_timestamp_basic_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_with_timestamp_compaction_test: db/db_with_timestamp_compaction_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_encryption_test: db/db_encryption_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1572,9 +1531,6 @@ cleanable_test: table/cleanable_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_fetcher_test: table/block_fetcher_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_test: table/block_based/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1713,9 +1669,6 @@ write_callback_test: db/write_callback_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
heap_test: util/heap_test.o $(GTEST)
|
||||
$(AM_LINK)
|
||||
|
||||
transaction_lock_mgr_test: utilities/transactions/transaction_lock_mgr_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
transaction_test: utilities/transactions/transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1791,9 +1744,6 @@ blob_file_addition_test: db/blob/blob_file_addition_test.o $(LIBOBJECTS) $(TESTH
|
||||
blob_file_garbage_test: db/blob/blob_file_garbage_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
timer_test: util/timer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
@@ -1873,9 +1823,9 @@ SHA256_CMD = sha256sum
|
||||
ZLIB_VER ?= 1.2.11
|
||||
ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= https://sourceware.org/pub/bzip2
|
||||
BZIP2_VER ?= 1.0.6
|
||||
BZIP2_SHA256 ?= a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
|
||||
BZIP2_DOWNLOAD_BASE ?= https://downloads.sourceforge.net/project/bzip2
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
|
||||
@@ -24,7 +24,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/
|
||||
Design discussions are conducted in https://www.facebook.com/groups/rocksdb.dev/
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -109,22 +109,15 @@ ROCKSDB_OS_DEPS += ([(
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_LIB_DEPS = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
] if not is_opt_mode else [":rocksdb_lib"]
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
srcs = [
|
||||
"cache/cache.cc",
|
||||
"cache/clock_cache.cc",
|
||||
"cache/lru_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
"db/column_family.cc",
|
||||
@@ -210,7 +203,6 @@ cpp_library(
|
||||
"memory/arena.cc",
|
||||
"memory/concurrent_arena.cc",
|
||||
"memory/jemalloc_nodump_allocator.cc",
|
||||
"memory/memkind_kmem_allocator.cc",
|
||||
"memtable/alloc_tracker.cc",
|
||||
"memtable/hash_linklist_rep.cc",
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
@@ -236,6 +228,7 @@ cpp_library(
|
||||
"options/options.cc",
|
||||
"options/options_helper.cc",
|
||||
"options/options_parser.cc",
|
||||
"options/options_sanity_check.cc",
|
||||
"port/port_posix.cc",
|
||||
"port/stack_trace.cc",
|
||||
"table/adaptive/adaptive_table_factory.cc",
|
||||
@@ -442,7 +435,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
@@ -531,13 +524,6 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_fetcher_test",
|
||||
"table/block_fetcher_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_test",
|
||||
"table/block_based/block_test.cc",
|
||||
@@ -944,13 +930,6 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_with_timestamp_compaction_test",
|
||||
"db/db_with_timestamp_compaction_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_write_test",
|
||||
"db/db_write_test.cc",
|
||||
@@ -1189,13 +1168,6 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"memkind_kmem_allocator_test",
|
||||
"memory/memkind_kmem_allocator_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"memory_test",
|
||||
"utilities/memory/memory_test.cc",
|
||||
@@ -1483,13 +1455,6 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"timer_test",
|
||||
"util/timer_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"trace_analyzer_test",
|
||||
"tools/trace_analyzer_test.cc",
|
||||
@@ -1497,13 +1462,6 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"transaction_lock_mgr_test",
|
||||
"utilities/transactions/transaction_lock_mgr_test.cc",
|
||||
"parallel",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"transaction_test",
|
||||
"utilities/transactions/transaction_test.cc",
|
||||
@@ -1553,13 +1511,6 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"work_queue_test",
|
||||
"util/work_queue_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"write_batch_test",
|
||||
"db/write_batch_test.cc",
|
||||
|
||||
+3
-14
@@ -1,6 +1,6 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2019
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
|
||||
@@ -24,15 +24,6 @@ environment:
|
||||
- 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
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
CMAKE_GENERATOR: Visual Studio 16
|
||||
CMAKE_PLATEFORM_NAME: x64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.com
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
CMAKE_GENERATOR: Visual Studio 16
|
||||
CMAKE_PLATEFORM_NAME: x64
|
||||
CMAKE_OPT: -DCMAKE_CXX_STANDARD=20
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.com
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
@@ -43,8 +34,7 @@ install:
|
||||
- cd snappy-1.1.7
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if DEFINED CMAKE_PLATEFORM_NAME (set "PLATEFORM_OPT=-A %CMAKE_PLATEFORM_NAME%")
|
||||
- cmake .. -G "%CMAKE_GENERATOR%" %PLATEFORM_OPT%
|
||||
- cmake -G "%CMAKE_GENERATOR%" ..
|
||||
- msbuild Snappy.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild Snappy.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building LZ4 dependency..."
|
||||
@@ -67,8 +57,7 @@ install:
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- if DEFINED CMAKE_PLATEFORM_NAME (set "PLATEFORM_OPT=-A %CMAKE_PLATEFORM_NAME%")
|
||||
- cmake .. -G "%CMAKE_GENERATOR%" %PLATEFORM_OPT% %CMAKE_OPT% -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1
|
||||
- cmake -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
|
||||
build:
|
||||
|
||||
@@ -168,13 +168,14 @@ def generate_targets(repo_path, deps_map):
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
# rocksdb_stress_lib
|
||||
TARGETS.add_rocksdb_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_stress_lib",
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get('STRESS_LIB_SOURCES', [])
|
||||
+ ["test_util/testutil.cc"])
|
||||
+ ["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
|
||||
print("Extra dependencies:\n{0}".format(json.dumps(deps_map)))
|
||||
print("Extra dependencies:\n{0}".format(str(deps_map)))
|
||||
# test for every test we found in the Makefile
|
||||
for target_alias, deps in deps_map.items():
|
||||
for test in sorted(tests):
|
||||
@@ -195,8 +196,8 @@ def generate_targets(repo_path, deps_map):
|
||||
test_target_name,
|
||||
match_src[0],
|
||||
is_parallel,
|
||||
json.dumps(deps['extra_deps']),
|
||||
json.dumps(deps['extra_compiler_flags']))
|
||||
deps['extra_deps'],
|
||||
deps['extra_compiler_flags'])
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test_target_name
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# If clang_format_diff.py command is not specfied, we assume we are able to
|
||||
# access directly without any path.
|
||||
|
||||
TGT_DIFF=`git diff TARGETS | head -n 1`
|
||||
|
||||
if [ ! -z "$TGT_DIFF" ]
|
||||
then
|
||||
echo "TARGETS file has uncommitted changes. Skip this check."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo Backup original TARGETS file.
|
||||
|
||||
cp TARGETS TARGETS.bkp
|
||||
|
||||
python buckifier/buckify_rocksdb.py
|
||||
|
||||
TGT_DIFF=`git diff TARGETS | head -n 1`
|
||||
|
||||
if [ -z "$TGT_DIFF" ]
|
||||
then
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 0
|
||||
else
|
||||
echo "Please run 'python buckifier/buckify_rocksdb.py' to update TARGETS file."
|
||||
echo "Do not manually update TARGETS file."
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 1
|
||||
fi
|
||||
@@ -50,18 +50,6 @@ class TARGETSBuilder(object):
|
||||
deps=pretty_list(deps)))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None):
|
||||
headers_attr_prefix = ""
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
self.targets_file.write(targets_cfg.rocksdb_library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers_attr_prefix=headers_attr_prefix,
|
||||
headers=headers))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_binary(self, name, srcs, deps=None):
|
||||
self.targets_file.write(targets_cfg.binary_template % (
|
||||
name,
|
||||
|
||||
@@ -114,11 +114,6 @@ ROCKSDB_OS_DEPS += ([(
|
||||
"linux",
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_LIB_DEPS = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
] if not is_opt_mode else [":rocksdb_lib"]
|
||||
"""
|
||||
|
||||
|
||||
@@ -137,21 +132,6 @@ cpp_library(
|
||||
)
|
||||
"""
|
||||
|
||||
rocksdb_library_template = """
|
||||
cpp_library(
|
||||
name = "{name}",
|
||||
srcs = [{srcs}],
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
|
||||
binary_template = """
|
||||
cpp_binary(
|
||||
name = "%s",
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
# -DZSTD if the ZSTD library is present
|
||||
# -DNUMA if the NUMA library is present
|
||||
# -DTBB if the TBB library is present
|
||||
# -DMEMKIND if the memkind library is present
|
||||
#
|
||||
# Using gflags in rocksdb:
|
||||
# Our project depends on gflags, which requires users to take some extra steps
|
||||
@@ -246,10 +245,6 @@ JAVAC_ARGS="-source 7"
|
||||
if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
# Cross-compiling; do not try any compilation tests.
|
||||
# Also don't need any compilation tests if compiling on fbcode
|
||||
if [ "$FBCODE_BUILD" = "true" ]; then
|
||||
# Enable backtrace on fbcode since the necessary libraries are present
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
fi
|
||||
true
|
||||
else
|
||||
if ! test $ROCKSDB_DISABLE_FALLOCATE; then
|
||||
@@ -430,23 +425,7 @@ EOF
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_MEMKIND; then
|
||||
# Test whether memkind library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -lmemkind -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <memkind.h>
|
||||
int main() {
|
||||
memkind_malloc(MEMKIND_DAX_KMEM, 1024);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DMEMKIND"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lmemkind"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lmemkind"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_PTHREAD_MUTEX_ADAPTIVE_NP; then
|
||||
# Test whether PTHREAD_MUTEX_ADAPTIVE_NP mutex type is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
@@ -705,7 +684,6 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_CFLAGS -x c++ -c - -o test_dl.o 2>/dev/null <<EOF
|
||||
void dummy_func() {}
|
||||
|
||||
+13
-51
@@ -2,30 +2,6 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# If clang_format_diff.py command is not specfied, we assume we are able to
|
||||
# access directly without any path.
|
||||
|
||||
print_usage () {
|
||||
echo "Usage:"
|
||||
echo "format-diff.sh [OPTIONS]"
|
||||
echo "-c: check only."
|
||||
echo "-h: print this message."
|
||||
}
|
||||
|
||||
while getopts ':ch' OPTION; do
|
||||
case "$OPTION" in
|
||||
c)
|
||||
CHECK_ONLY=1
|
||||
;;
|
||||
h)
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
?)
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z $CLANG_FORMAT_DIFF ]
|
||||
then
|
||||
CLANG_FORMAT_DIFF="clang-format-diff.py"
|
||||
@@ -34,28 +10,18 @@ fi
|
||||
# Check clang-format-diff.py
|
||||
if ! which $CLANG_FORMAT_DIFF &> /dev/null
|
||||
then
|
||||
if [ ! -f ./clang-format-diff.py ]
|
||||
then
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
exit 128
|
||||
else
|
||||
if [ -x ./clang-format-diff.py ]
|
||||
then
|
||||
PATH=$PATH:.
|
||||
else
|
||||
CLANG_FORMAT_DIFF="python ./clang-format-diff.py"
|
||||
fi
|
||||
fi
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
exit 128
|
||||
fi
|
||||
|
||||
# Check argparse, a library that clang-format-diff.py requires.
|
||||
@@ -121,10 +87,6 @@ if [ -z "$diffs" ]
|
||||
then
|
||||
echo "Nothing needs to be reformatted!"
|
||||
exit 0
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Highlight the insertion/deletion from the clang-format-diff.py's output
|
||||
@@ -159,7 +121,7 @@ if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -i -p 1
|
||||
else
|
||||
git diff -U0 HEAD | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 HEAD^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
fi
|
||||
echo "Files reformatted!"
|
||||
|
||||
|
||||
@@ -818,8 +818,6 @@ run_format_compatible()
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
|
||||
export https_proxy="fwdproxy:8080"
|
||||
|
||||
tools/check_format_compatible.sh
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
set -ex
|
||||
set -e
|
||||
|
||||
ROCKSDB_VERSION="6.7.3"
|
||||
ZSTD_VERSION="1.4.4"
|
||||
ROCKSDB_VERSION="5.10.3"
|
||||
ZSTD_VERSION="1.1.3"
|
||||
|
||||
echo "This script configures CentOS with everything needed to build and run RocksDB"
|
||||
|
||||
@@ -40,6 +40,5 @@ cd /usr/local/rocksdb
|
||||
chown -R vagrant:vagrant /usr/local/rocksdb/
|
||||
sudo -u vagrant make static_lib
|
||||
cd examples/
|
||||
sudo -u vagrant LD_LIBRARY_PATH=/usr/local/lib/ make all
|
||||
sudo -u vagrant LD_LIBRARY_PATH=/usr/local/lib/ ./c_simple_example
|
||||
|
||||
sudo -u vagrant make all
|
||||
sudo -u vagrant ./c_simple_example
|
||||
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
Status Cache::CreateFromString(const ConfigOptions& /*opts*/,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Cache>* result) {
|
||||
Status status;
|
||||
std::shared_ptr<Cache> cache;
|
||||
if (value.find('=') == std::string::npos) {
|
||||
cache = NewLRUCache(ParseSizeT(value));
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
LRUCacheOptions cache_opts;
|
||||
if (!ParseOptionHelper(reinterpret_cast<char*>(&cache_opts),
|
||||
OptionType::kLRUCacheOptions, value)) {
|
||||
status = Status::InvalidArgument("Invalid cache options");
|
||||
}
|
||||
cache = NewLRUCache(cache_opts);
|
||||
#else
|
||||
status = Status::NotSupported("Cannot load cache in LITE mode ", value);
|
||||
#endif //! ROCKSDB_LITE
|
||||
}
|
||||
if (status.ok()) {
|
||||
result->swap(cache);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+62
-162
@@ -14,47 +14,34 @@ int main() {
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
|
||||
using GFLAGS_NAMESPACE::ParseCommandLineFlags;
|
||||
|
||||
static constexpr uint32_t KiB = uint32_t{1} << 10;
|
||||
static constexpr uint32_t MiB = KiB << 10;
|
||||
static constexpr uint64_t GiB = MiB << 10;
|
||||
static const uint32_t KB = 1024;
|
||||
|
||||
DEFINE_uint32(threads, 16, "Number of concurrent threads to run.");
|
||||
DEFINE_uint64(cache_size, 1 * GiB,
|
||||
"Number of bytes to use as a cache of uncompressed data.");
|
||||
DEFINE_uint32(num_shard_bits, 6, "shard_bits.");
|
||||
DEFINE_int32(threads, 16, "Number of concurrent threads to run.");
|
||||
DEFINE_int64(cache_size, 8 * KB * KB,
|
||||
"Number of bytes to use as a cache of uncompressed data.");
|
||||
DEFINE_int32(num_shard_bits, 4, "shard_bits.");
|
||||
|
||||
DEFINE_double(resident_ratio, 0.25,
|
||||
"Ratio of keys fitting in cache to keyspace.");
|
||||
DEFINE_uint64(ops_per_thread, 0,
|
||||
"Number of operations per thread. (Default: 5 * keyspace size)");
|
||||
DEFINE_uint32(value_bytes, 8 * KiB, "Size of each value added.");
|
||||
DEFINE_int64(max_key, 1 * KB * KB * KB, "Max number of key to place in cache");
|
||||
DEFINE_uint64(ops_per_thread, 1200000, "Number of operations per thread.");
|
||||
|
||||
DEFINE_uint32(skew, 5, "Degree of skew in key selection");
|
||||
DEFINE_bool(populate_cache, true, "Populate cache before operations");
|
||||
|
||||
DEFINE_uint32(lookup_insert_percent, 87,
|
||||
"Ratio of lookup (+ insert on not found) to total workload "
|
||||
"(expressed as a percentage)");
|
||||
DEFINE_uint32(insert_percent, 2,
|
||||
"Ratio of insert to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(lookup_percent, 10,
|
||||
"Ratio of lookup to total workload (expressed as a percentage)");
|
||||
DEFINE_uint32(erase_percent, 1,
|
||||
"Ratio of erase to total workload (expressed as a percentage)");
|
||||
DEFINE_bool(populate_cache, false, "Populate cache before operations");
|
||||
DEFINE_int32(insert_percent, 40,
|
||||
"Ratio of insert to total workload (expressed as a percentage)");
|
||||
DEFINE_int32(lookup_percent, 50,
|
||||
"Ratio of lookup to total workload (expressed as a percentage)");
|
||||
DEFINE_int32(erase_percent, 10,
|
||||
"Ratio of erase to total workload (expressed as a percentage)");
|
||||
|
||||
DEFINE_bool(use_clock_cache, false, "");
|
||||
|
||||
@@ -62,15 +49,21 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class CacheBench;
|
||||
namespace {
|
||||
void deleter(const Slice& /*key*/, void* value) {
|
||||
delete reinterpret_cast<char *>(value);
|
||||
}
|
||||
|
||||
// State shared by all concurrent executions of the same benchmark.
|
||||
class SharedState {
|
||||
public:
|
||||
explicit SharedState(CacheBench* cache_bench)
|
||||
: cv_(&mu_),
|
||||
num_threads_(FLAGS_threads),
|
||||
num_initialized_(0),
|
||||
start_(false),
|
||||
num_done_(0),
|
||||
cache_bench_(cache_bench) {}
|
||||
cache_bench_(cache_bench) {
|
||||
}
|
||||
|
||||
~SharedState() {}
|
||||
|
||||
@@ -94,9 +87,13 @@ class SharedState {
|
||||
num_done_++;
|
||||
}
|
||||
|
||||
bool AllInitialized() const { return num_initialized_ >= FLAGS_threads; }
|
||||
bool AllInitialized() const {
|
||||
return num_initialized_ >= num_threads_;
|
||||
}
|
||||
|
||||
bool AllDone() const { return num_done_ >= FLAGS_threads; }
|
||||
bool AllDone() const {
|
||||
return num_done_ >= num_threads_;
|
||||
}
|
||||
|
||||
void SetStart() {
|
||||
start_ = true;
|
||||
@@ -110,6 +107,7 @@ class SharedState {
|
||||
port::Mutex mu_;
|
||||
port::CondVar cv_;
|
||||
|
||||
const uint64_t num_threads_;
|
||||
uint64_t num_initialized_;
|
||||
bool start_;
|
||||
uint64_t num_done_;
|
||||
@@ -120,69 +118,17 @@ class SharedState {
|
||||
// Per-thread state for concurrent executions of the same benchmark.
|
||||
struct ThreadState {
|
||||
uint32_t tid;
|
||||
Random64 rnd;
|
||||
Random rnd;
|
||||
SharedState* shared;
|
||||
|
||||
ThreadState(uint32_t index, SharedState* _shared)
|
||||
: tid(index), rnd(1000 + index), shared(_shared) {}
|
||||
};
|
||||
|
||||
struct KeyGen {
|
||||
char key_data[27];
|
||||
|
||||
Slice GetRand(Random64& rnd, uint64_t max_key) {
|
||||
uint64_t raw = rnd.Next();
|
||||
// Skew according to setting
|
||||
for (uint32_t i = 0; i < FLAGS_skew; ++i) {
|
||||
raw = std::min(raw, rnd.Next());
|
||||
}
|
||||
uint64_t key = fastrange64(raw, max_key);
|
||||
// Variable size and alignment
|
||||
size_t off = key % 8;
|
||||
key_data[0] = char{42};
|
||||
EncodeFixed64(key_data + 1, key);
|
||||
key_data[9] = char{11};
|
||||
EncodeFixed64(key_data + 10, key);
|
||||
key_data[18] = char{4};
|
||||
EncodeFixed64(key_data + 19, key);
|
||||
return Slice(&key_data[off], sizeof(key_data) - off);
|
||||
}
|
||||
};
|
||||
|
||||
char* createValue(Random64& rnd) {
|
||||
char* rv = new char[FLAGS_value_bytes];
|
||||
// Fill with some filler data, and take some CPU time
|
||||
for (uint32_t i = 0; i < FLAGS_value_bytes; i += 8) {
|
||||
EncodeFixed64(rv + i, rnd.Next());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void deleter(const Slice& /*key*/, void* value) {
|
||||
delete[] static_cast<char*>(value);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class CacheBench {
|
||||
static constexpr uint64_t kHundredthUint64 =
|
||||
std::numeric_limits<uint64_t>::max() / 100U;
|
||||
|
||||
public:
|
||||
CacheBench()
|
||||
: max_key_(static_cast<uint64_t>(FLAGS_cache_size / FLAGS_resident_ratio /
|
||||
FLAGS_value_bytes)),
|
||||
lookup_insert_threshold_(kHundredthUint64 *
|
||||
FLAGS_lookup_insert_percent),
|
||||
insert_threshold_(lookup_insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_insert_percent),
|
||||
lookup_threshold_(insert_threshold_ +
|
||||
kHundredthUint64 * FLAGS_lookup_percent),
|
||||
erase_threshold_(lookup_threshold_ +
|
||||
kHundredthUint64 * FLAGS_erase_percent) {
|
||||
if (erase_threshold_ != 100U * kHundredthUint64) {
|
||||
fprintf(stderr, "Percentages must add to 100.\n");
|
||||
exit(1);
|
||||
}
|
||||
CacheBench() : num_threads_(FLAGS_threads) {
|
||||
if (FLAGS_use_clock_cache) {
|
||||
cache_ = NewClockCache(FLAGS_cache_size, FLAGS_num_shard_bits);
|
||||
if (!cache_) {
|
||||
@@ -192,19 +138,18 @@ class CacheBench {
|
||||
} else {
|
||||
cache_ = NewLRUCache(FLAGS_cache_size, FLAGS_num_shard_bits);
|
||||
}
|
||||
if (FLAGS_ops_per_thread == 0) {
|
||||
FLAGS_ops_per_thread = 5 * max_key_;
|
||||
}
|
||||
}
|
||||
|
||||
~CacheBench() {}
|
||||
|
||||
void PopulateCache() {
|
||||
Random64 rnd(1);
|
||||
KeyGen keygen;
|
||||
for (uint64_t i = 0; i < 2 * FLAGS_cache_size; i += FLAGS_value_bytes) {
|
||||
cache_->Insert(keygen.GetRand(rnd, max_key_), createValue(rnd),
|
||||
FLAGS_value_bytes, &deleter);
|
||||
Random rnd(1);
|
||||
for (int64_t i = 0; i < FLAGS_cache_size; i++) {
|
||||
uint64_t rand_key = rnd.Next() % FLAGS_max_key;
|
||||
// Cast uint64* to be char*, data would be copied to cache
|
||||
Slice key(reinterpret_cast<char*>(&rand_key), 8);
|
||||
// do insert
|
||||
cache_->Insert(key, new char[10], 1, &deleter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,10 +158,10 @@ class CacheBench {
|
||||
|
||||
PrintEnv();
|
||||
SharedState shared(this);
|
||||
std::vector<std::unique_ptr<ThreadState> > threads(FLAGS_threads);
|
||||
for (uint32_t i = 0; i < FLAGS_threads; i++) {
|
||||
threads[i].reset(new ThreadState(i, &shared));
|
||||
env->StartThread(ThreadBody, threads[i].get());
|
||||
std::vector<ThreadState*> threads(num_threads_);
|
||||
for (uint32_t i = 0; i < num_threads_; i++) {
|
||||
threads[i] = new ThreadState(i, &shared);
|
||||
env->StartThread(ThreadBody, threads[i]);
|
||||
}
|
||||
{
|
||||
MutexLock l(shared.GetMutex());
|
||||
@@ -247,15 +192,10 @@ class CacheBench {
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
const uint64_t max_key_;
|
||||
// Cumulative thresholds in the space of a random uint64_t
|
||||
const uint64_t lookup_insert_threshold_;
|
||||
const uint64_t insert_threshold_;
|
||||
const uint64_t lookup_threshold_;
|
||||
const uint64_t erase_threshold_;
|
||||
uint32_t num_threads_;
|
||||
|
||||
static void ThreadBody(void* v) {
|
||||
ThreadState* thread = static_cast<ThreadState*>(v);
|
||||
ThreadState* thread = reinterpret_cast<ThreadState*>(v);
|
||||
SharedState* shared = thread->shared;
|
||||
|
||||
{
|
||||
@@ -280,78 +220,40 @@ class CacheBench {
|
||||
}
|
||||
|
||||
void OperateCache(ThreadState* thread) {
|
||||
// To use looked-up values
|
||||
uint64_t result = 0;
|
||||
// To hold handles for a non-trivial amount of time
|
||||
Cache::Handle* handle = nullptr;
|
||||
KeyGen gen;
|
||||
for (uint64_t i = 0; i < FLAGS_ops_per_thread; i++) {
|
||||
Slice key = gen.GetRand(thread->rnd, max_key_);
|
||||
uint64_t random_op = thread->rnd.Next();
|
||||
if (random_op < lookup_insert_threshold_) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do lookup
|
||||
handle = cache_->Lookup(key);
|
||||
if (handle) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
} else {
|
||||
// do insert
|
||||
cache_->Insert(key, createValue(thread->rnd), FLAGS_value_bytes,
|
||||
&deleter, &handle);
|
||||
}
|
||||
} else if (random_op < insert_threshold_) {
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
uint64_t rand_key = thread->rnd.Next() % FLAGS_max_key;
|
||||
// Cast uint64* to be char*, data would be copied to cache
|
||||
Slice key(reinterpret_cast<char*>(&rand_key), 8);
|
||||
int32_t prob_op = thread->rnd.Uniform(100);
|
||||
if (prob_op >= 0 && prob_op < FLAGS_insert_percent) {
|
||||
// do insert
|
||||
cache_->Insert(key, createValue(thread->rnd), FLAGS_value_bytes,
|
||||
&deleter, &handle);
|
||||
} else if (random_op < lookup_threshold_) {
|
||||
cache_->Insert(key, new char[10], 1, &deleter);
|
||||
} else if (prob_op -= FLAGS_insert_percent &&
|
||||
prob_op < FLAGS_lookup_percent) {
|
||||
// do lookup
|
||||
auto handle = cache_->Lookup(key);
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
// do lookup
|
||||
handle = cache_->Lookup(key);
|
||||
if (handle) {
|
||||
// do something with the data
|
||||
result += NPHash64(static_cast<char*>(cache_->Value(handle)),
|
||||
FLAGS_value_bytes);
|
||||
}
|
||||
} else if (random_op < erase_threshold_) {
|
||||
} else if (prob_op -= FLAGS_lookup_percent &&
|
||||
prob_op < FLAGS_erase_percent) {
|
||||
// do erase
|
||||
cache_->Erase(key);
|
||||
} else {
|
||||
// Should be extremely unlikely (noop)
|
||||
assert(random_op >= kHundredthUint64 * 100U);
|
||||
}
|
||||
}
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintEnv() const {
|
||||
printf("RocksDB version : %d.%d\n", kMajorVersion, kMinorVersion);
|
||||
printf("Number of threads : %u\n", FLAGS_threads);
|
||||
printf("Number of threads : %d\n", FLAGS_threads);
|
||||
printf("Ops per thread : %" PRIu64 "\n", FLAGS_ops_per_thread);
|
||||
printf("Cache size : %" PRIu64 "\n", FLAGS_cache_size);
|
||||
printf("Num shard bits : %u\n", FLAGS_num_shard_bits);
|
||||
printf("Max key : %" PRIu64 "\n", max_key_);
|
||||
printf("Resident ratio : %g\n", FLAGS_resident_ratio);
|
||||
printf("Skew degree : %u\n", FLAGS_skew);
|
||||
printf("Populate cache : %d\n", int{FLAGS_populate_cache});
|
||||
printf("Lookup+Insert pct : %u%%\n", FLAGS_lookup_insert_percent);
|
||||
printf("Insert percentage : %u%%\n", FLAGS_insert_percent);
|
||||
printf("Lookup percentage : %u%%\n", FLAGS_lookup_percent);
|
||||
printf("Erase percentage : %u%%\n", FLAGS_erase_percent);
|
||||
printf("Num shard bits : %d\n", FLAGS_num_shard_bits);
|
||||
printf("Max key : %" PRIu64 "\n", FLAGS_max_key);
|
||||
printf("Populate cache : %d\n", FLAGS_populate_cache);
|
||||
printf("Insert percentage : %d%%\n", FLAGS_insert_percent);
|
||||
printf("Lookup percentage : %d%%\n", FLAGS_lookup_percent);
|
||||
printf("Erase percentage : %d%%\n", FLAGS_erase_percent);
|
||||
printf("----------------------------\n");
|
||||
}
|
||||
};
|
||||
@@ -368,8 +270,6 @@ int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::CacheBench bench;
|
||||
if (FLAGS_populate_cache) {
|
||||
bench.PopulateCache();
|
||||
printf("Population complete\n");
|
||||
printf("----------------------------\n");
|
||||
}
|
||||
if (bench.Run()) {
|
||||
return 0;
|
||||
|
||||
Vendored
+3
-11
@@ -341,8 +341,7 @@ class ClockCacheShard final : public CacheShard {
|
||||
CacheHandle* Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t change,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
bool hold_reference, CleanupContext* context,
|
||||
bool* overwritten);
|
||||
bool hold_reference, CleanupContext* context);
|
||||
|
||||
// Guards list_, head_, and recycle_. In addition, updating table_ also has
|
||||
// to hold the mutex, to avoid the cache being in inconsistent state.
|
||||
@@ -565,8 +564,7 @@ void ClockCacheShard::SetStrictCapacityLimit(bool strict_capacity_limit) {
|
||||
CacheHandle* ClockCacheShard::Insert(
|
||||
const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value), bool hold_reference,
|
||||
CleanupContext* context, bool* overwritten) {
|
||||
assert(overwritten != nullptr && *overwritten == false);
|
||||
CleanupContext* context) {
|
||||
size_t total_charge =
|
||||
CacheHandle::CalcTotalCharge(key, charge, metadata_charge_policy_);
|
||||
MutexLock l(&mutex_);
|
||||
@@ -599,7 +597,6 @@ CacheHandle* ClockCacheShard::Insert(
|
||||
handle->flags.store(flags, std::memory_order_relaxed);
|
||||
HashTable::accessor accessor;
|
||||
if (table_.find(accessor, CacheKey(key, hash))) {
|
||||
*overwritten = true;
|
||||
CacheHandle* existing_handle = accessor->second;
|
||||
table_.erase(accessor);
|
||||
UnsetInCache(existing_handle, context);
|
||||
@@ -622,9 +619,8 @@ Status ClockCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
char* key_data = new char[key.size()];
|
||||
memcpy(key_data, key.data(), key.size());
|
||||
Slice key_copy(key_data, key.size());
|
||||
bool overwritten = false;
|
||||
CacheHandle* handle = Insert(key_copy, hash, value, charge, deleter,
|
||||
out_handle != nullptr, &context, &overwritten);
|
||||
out_handle != nullptr, &context);
|
||||
Status s;
|
||||
if (out_handle != nullptr) {
|
||||
if (handle == nullptr) {
|
||||
@@ -633,10 +629,6 @@ Status ClockCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
*out_handle = reinterpret_cast<Cache::Handle*>(handle);
|
||||
}
|
||||
}
|
||||
if (overwritten) {
|
||||
assert(s.ok());
|
||||
s = Status::OkOverwritten();
|
||||
}
|
||||
Cleanup(context);
|
||||
return s;
|
||||
}
|
||||
|
||||
Vendored
-1
@@ -386,7 +386,6 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
LRUHandle* old = table_.Insert(e);
|
||||
usage_ += total_charge;
|
||||
if (old != nullptr) {
|
||||
s = Status::OkOverwritten();
|
||||
assert(old->InCache());
|
||||
old->SetInCache(false);
|
||||
if (!old->HasRefs()) {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
macro(get_cxx_std_flags FLAGS_VARIABLE)
|
||||
if( CMAKE_CXX_STANDARD_REQUIRED )
|
||||
set(${FLAGS_VARIABLE} ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION})
|
||||
else()
|
||||
set(${FLAGS_VARIABLE} ${CMAKE_CXX${CMAKE_CXX_STANDARD}_EXTENSION_COMPILE_OPTION})
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -56,9 +56,8 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
// TODO(yiwu): For last_seq_same_as_publish_seq_==false, this is not the
|
||||
// correct behavior. Will be corrected automatically when we take a snapshot
|
||||
// here for the case of WritePreparedTxnDB.
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
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();
|
||||
@@ -66,7 +65,6 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
@@ -77,10 +75,10 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
latest_seq);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(db_impl_->GetLatestSequenceNumber());
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_file_meta.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
std::string SharedBlobFileMetaData::DebugString() const {
|
||||
std::ostringstream oss;
|
||||
oss << (*this);
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const SharedBlobFileMetaData& shared_meta) {
|
||||
os << "blob_file_number: " << shared_meta.GetBlobFileNumber()
|
||||
<< " total_blob_count: " << shared_meta.GetTotalBlobCount()
|
||||
<< " total_blob_bytes: " << shared_meta.GetTotalBlobBytes()
|
||||
<< " checksum_method: " << shared_meta.GetChecksumMethod()
|
||||
<< " checksum_value: " << shared_meta.GetChecksumValue();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string BlobFileMetaData::DebugString() const {
|
||||
std::ostringstream oss;
|
||||
oss << (*this);
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const BlobFileMetaData& meta) {
|
||||
const auto& shared_meta = meta.GetSharedMeta();
|
||||
assert(shared_meta);
|
||||
|
||||
os << (*shared_meta) << " garbage_blob_count: " << meta.GetGarbageBlobCount()
|
||||
<< " garbage_blob_bytes: " << meta.GetGarbageBlobBytes();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// SharedBlobFileMetaData represents the immutable part of blob files' metadata,
|
||||
// like the blob file number, total number and size of blobs, or checksum
|
||||
// method and value. There is supposed to be one object of this class per blob
|
||||
// file (shared across all versions that include the blob file in question);
|
||||
// hence, the type is neither copyable nor movable. A blob file can be marked
|
||||
// obsolete when the corresponding SharedBlobFileMetaData object is destroyed.
|
||||
|
||||
class SharedBlobFileMetaData {
|
||||
public:
|
||||
static std::shared_ptr<SharedBlobFileMetaData> Create(
|
||||
uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value) {
|
||||
return std::shared_ptr<SharedBlobFileMetaData>(new SharedBlobFileMetaData(
|
||||
blob_file_number, total_blob_count, total_blob_bytes,
|
||||
std::move(checksum_method), std::move(checksum_value)));
|
||||
}
|
||||
|
||||
template <typename Deleter>
|
||||
static std::shared_ptr<SharedBlobFileMetaData> Create(
|
||||
uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value, Deleter deleter) {
|
||||
return std::shared_ptr<SharedBlobFileMetaData>(
|
||||
new SharedBlobFileMetaData(blob_file_number, total_blob_count,
|
||||
total_blob_bytes, std::move(checksum_method),
|
||||
std::move(checksum_value)),
|
||||
deleter);
|
||||
}
|
||||
|
||||
SharedBlobFileMetaData(const SharedBlobFileMetaData&) = delete;
|
||||
SharedBlobFileMetaData& operator=(const SharedBlobFileMetaData&) = delete;
|
||||
|
||||
SharedBlobFileMetaData(SharedBlobFileMetaData&&) = delete;
|
||||
SharedBlobFileMetaData& operator=(SharedBlobFileMetaData&&) = delete;
|
||||
|
||||
uint64_t GetBlobFileNumber() const { return blob_file_number_; }
|
||||
uint64_t GetTotalBlobCount() const { return total_blob_count_; }
|
||||
uint64_t GetTotalBlobBytes() const { return total_blob_bytes_; }
|
||||
const std::string& GetChecksumMethod() const { return checksum_method_; }
|
||||
const std::string& GetChecksumValue() const { return checksum_value_; }
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
SharedBlobFileMetaData(uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value)
|
||||
: blob_file_number_(blob_file_number),
|
||||
total_blob_count_(total_blob_count),
|
||||
total_blob_bytes_(total_blob_bytes),
|
||||
checksum_method_(std::move(checksum_method)),
|
||||
checksum_value_(std::move(checksum_value)) {
|
||||
assert(checksum_method_.empty() == checksum_value_.empty());
|
||||
}
|
||||
|
||||
uint64_t blob_file_number_;
|
||||
uint64_t total_blob_count_;
|
||||
uint64_t total_blob_bytes_;
|
||||
std::string checksum_method_;
|
||||
std::string checksum_value_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const SharedBlobFileMetaData& shared_meta);
|
||||
|
||||
// BlobFileMetaData contains the part of the metadata for blob files that can
|
||||
// vary across versions, like the amount of garbage in the blob file. In
|
||||
// addition, BlobFileMetaData objects point to and share the ownership of the
|
||||
// SharedBlobFileMetaData object for the corresponding blob file. Similarly to
|
||||
// SharedBlobFileMetaData, BlobFileMetaData are not copyable or movable. They
|
||||
// are meant to be jointly owned by the versions in which the blob file has the
|
||||
// same (immutable *and* mutable) state.
|
||||
|
||||
class BlobFileMetaData {
|
||||
public:
|
||||
static std::shared_ptr<BlobFileMetaData> Create(
|
||||
std::shared_ptr<SharedBlobFileMetaData> shared_meta,
|
||||
uint64_t garbage_blob_count, uint64_t garbage_blob_bytes) {
|
||||
return std::shared_ptr<BlobFileMetaData>(new BlobFileMetaData(
|
||||
std::move(shared_meta), garbage_blob_count, garbage_blob_bytes));
|
||||
}
|
||||
|
||||
BlobFileMetaData(const BlobFileMetaData&) = delete;
|
||||
BlobFileMetaData& operator=(const BlobFileMetaData&) = delete;
|
||||
|
||||
BlobFileMetaData(BlobFileMetaData&&) = delete;
|
||||
BlobFileMetaData& operator=(BlobFileMetaData&&) = delete;
|
||||
|
||||
const std::shared_ptr<SharedBlobFileMetaData>& GetSharedMeta() const {
|
||||
return shared_meta_;
|
||||
}
|
||||
|
||||
uint64_t GetBlobFileNumber() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetBlobFileNumber();
|
||||
}
|
||||
uint64_t GetTotalBlobCount() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetTotalBlobCount();
|
||||
}
|
||||
uint64_t GetTotalBlobBytes() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetTotalBlobBytes();
|
||||
}
|
||||
const std::string& GetChecksumMethod() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetChecksumMethod();
|
||||
}
|
||||
const std::string& GetChecksumValue() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetChecksumValue();
|
||||
}
|
||||
|
||||
uint64_t GetGarbageBlobCount() const { return garbage_blob_count_; }
|
||||
uint64_t GetGarbageBlobBytes() const { return garbage_blob_bytes_; }
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
private:
|
||||
BlobFileMetaData(std::shared_ptr<SharedBlobFileMetaData> shared_meta,
|
||||
uint64_t garbage_blob_count, uint64_t garbage_blob_bytes)
|
||||
: shared_meta_(std::move(shared_meta)),
|
||||
garbage_blob_count_(garbage_blob_count),
|
||||
garbage_blob_bytes_(garbage_blob_bytes) {
|
||||
assert(shared_meta_);
|
||||
assert(garbage_blob_count_ <= shared_meta_->GetTotalBlobCount());
|
||||
assert(garbage_blob_bytes_ <= shared_meta_->GetTotalBlobBytes());
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedBlobFileMetaData> shared_meta_;
|
||||
uint64_t garbage_blob_count_;
|
||||
uint64_t garbage_blob_bytes_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const BlobFileMetaData& meta);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+14
-25
@@ -81,11 +81,10 @@ Status BuildTable(
|
||||
SnapshotChecker* snapshot_checker, const CompressionType compression,
|
||||
uint64_t sample_for_compression, const CompressionOptions& compression_opts,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
TableFileCreationReason reason, IOStatus* io_status,
|
||||
EventLogger* event_logger, int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, int level, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, Env::WriteLifeTimeHint write_hint,
|
||||
const uint64_t file_creation_time) {
|
||||
TableFileCreationReason reason, EventLogger* event_logger, int job_id,
|
||||
const Env::IOPriority io_priority, TableProperties* table_properties,
|
||||
int level, const uint64_t creation_time, const uint64_t oldest_key_time,
|
||||
Env::WriteLifeTimeHint write_hint, const uint64_t file_creation_time) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -134,7 +133,7 @@ Status BuildTable(
|
||||
|
||||
file_writer.reset(new WritableFileWriter(
|
||||
std::move(file), fname, file_options, env, ioptions.statistics,
|
||||
ioptions.listeners, ioptions.file_checksum_gen_factory));
|
||||
ioptions.listeners, ioptions.sst_file_checksum_func));
|
||||
|
||||
builder = NewTableBuilder(
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
@@ -183,15 +182,14 @@ Status BuildTable(
|
||||
}
|
||||
|
||||
// Finish and check for builder errors
|
||||
bool empty = builder->IsEmpty();
|
||||
tp = builder->GetTableProperties();
|
||||
bool empty = builder->NumEntries() == 0 && tp.num_range_deletions == 0;
|
||||
s = c_iter.status();
|
||||
TEST_SYNC_POINT("BuildTable:BeforeFinishBuildTable");
|
||||
if (!s.ok() || empty) {
|
||||
builder->Abandon();
|
||||
} else {
|
||||
s = builder->Finish();
|
||||
}
|
||||
*io_status = builder->io_status();
|
||||
|
||||
if (s.ok() && !empty) {
|
||||
uint64_t file_size = builder->FileSize();
|
||||
@@ -202,28 +200,20 @@ Status BuildTable(
|
||||
if (table_properties) {
|
||||
*table_properties = tp;
|
||||
}
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = builder->GetFileChecksum();
|
||||
meta->file_checksum_func_name = builder->GetFileChecksumFuncName();
|
||||
}
|
||||
delete builder;
|
||||
|
||||
// Finish and check for file errors
|
||||
if (s.ok() && !empty) {
|
||||
StopWatch sw(env, ioptions.statistics, TABLE_SYNC_MICROS);
|
||||
*io_status = file_writer->Sync(ioptions.use_fsync);
|
||||
s = file_writer->Sync(ioptions.use_fsync);
|
||||
}
|
||||
if (io_status->ok() && !empty) {
|
||||
*io_status = file_writer->Close();
|
||||
if (s.ok() && !empty) {
|
||||
s = file_writer->Close();
|
||||
}
|
||||
if (io_status->ok() && !empty) {
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = file_writer->GetFileChecksum();
|
||||
meta->file_checksum_func_name = file_writer->GetFileChecksumFuncName();
|
||||
}
|
||||
|
||||
if (!io_status->ok()) {
|
||||
s = *io_status;
|
||||
}
|
||||
|
||||
// TODO Also check the IO status when create the Iterator.
|
||||
|
||||
if (s.ok() && !empty) {
|
||||
// Verify that the table is usable
|
||||
@@ -240,8 +230,7 @@ Status BuildTable(
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
/*skip_filter=*/false, level, /*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr,
|
||||
/*allow_unprepared_value*/ false));
|
||||
/*largest_compaction_key*/ nullptr));
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ extern Status BuildTable(
|
||||
const uint64_t sample_for_compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
IOStatus* io_status, EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH,
|
||||
TableProperties* table_properties = nullptr, int level = -1,
|
||||
const uint64_t creation_time = 0, const uint64_t oldest_key_time = 0,
|
||||
|
||||
@@ -4441,16 +4441,6 @@ uint64_t rocksdb_approximate_memory_usage_get_cache_total(
|
||||
return memory_usage->cache_total;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_dump_malloc_stats(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.dump_malloc_stats = val;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_whole_key_filtering(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.memtable_whole_key_filtering = val;
|
||||
}
|
||||
|
||||
// deletes container with memory usage estimates
|
||||
void rocksdb_approximate_memory_usage_destroy(rocksdb_memory_usage_t* usage) {
|
||||
delete usage;
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -287,9 +287,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
// Verify the CF options of the returned CF handle.
|
||||
ColumnFamilyDescriptor desc;
|
||||
ASSERT_OK(handles_[cfi]->GetDescriptor(&desc));
|
||||
RocksDBOptionsParser::VerifyCFOptions(ConfigOptions(), desc.options,
|
||||
current_cf_opt);
|
||||
|
||||
RocksDBOptionsParser::VerifyCFOptions(desc.options, current_cf_opt);
|
||||
#endif // !ROCKSDB_LITE
|
||||
cfi++;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ const uint64_t kRangeTombstoneSentinel =
|
||||
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b) {
|
||||
auto c = user_cmp->CompareWithoutTimestamp(a.user_key(), b.user_key());
|
||||
auto c = user_cmp->Compare(a.user_key(), b.user_key());
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -613,13 +613,8 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus io_s;
|
||||
if (status.ok() && output_directory_) {
|
||||
io_s = output_directory_->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
status = io_s;
|
||||
status = output_directory_->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
@@ -655,8 +650,7 @@ Status CompactionJob::Run() {
|
||||
TableReaderCaller::kCompactionRefill, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, compact_->compaction->output_level(),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
/*largest_compaction_key=*/nullptr);
|
||||
auto s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
@@ -719,13 +713,9 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
cfd->internal_stats()->AddCompactionStats(
|
||||
compact_->compaction->output_level(), thread_pri_, compaction_stats_);
|
||||
|
||||
versions_->SetIOStatusOK();
|
||||
if (status.ok()) {
|
||||
status = InstallCompactionResults(mutable_cf_options);
|
||||
}
|
||||
if (!versions_->io_status().ok()) {
|
||||
io_status_ = versions_->io_status();
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
auto vstorage = cfd->current()->storage_info();
|
||||
const auto& stats = compaction_stats_;
|
||||
@@ -938,8 +928,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact->builder != nullptr);
|
||||
assert(sub_compact->current_output() != nullptr);
|
||||
sub_compact->builder->Add(key, value);
|
||||
sub_compact->current_output_file_size =
|
||||
sub_compact->builder->EstimatedFileSize();
|
||||
sub_compact->current_output_file_size = sub_compact->builder->FileSize();
|
||||
const ParsedInternalKey& ikey = c_iter->ikey();
|
||||
sub_compact->current_output()->meta.UpdateBoundaries(
|
||||
key, value, ikey.sequence, ikey.type);
|
||||
@@ -1305,35 +1294,25 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
} else {
|
||||
sub_compact->builder->Abandon();
|
||||
}
|
||||
if (!sub_compact->builder->io_status().ok()) {
|
||||
io_status_ = sub_compact->builder->io_status();
|
||||
s = io_status_;
|
||||
}
|
||||
const uint64_t current_bytes = sub_compact->builder->FileSize();
|
||||
if (s.ok()) {
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = sub_compact->builder->GetFileChecksum();
|
||||
meta->file_checksum_func_name =
|
||||
sub_compact->builder->GetFileChecksumFuncName();
|
||||
|
||||
meta->fd.file_size = current_bytes;
|
||||
}
|
||||
sub_compact->current_output()->finished = true;
|
||||
sub_compact->total_bytes += current_bytes;
|
||||
|
||||
// Finish and check for file errors
|
||||
IOStatus io_s;
|
||||
if (s.ok()) {
|
||||
StopWatch sw(env_, stats_, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
io_s = sub_compact->outfile->Sync(db_options_.use_fsync);
|
||||
s = sub_compact->outfile->Sync(db_options_.use_fsync);
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
io_s = sub_compact->outfile->Close();
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
// Add the checksum information to file metadata.
|
||||
meta->file_checksum = sub_compact->outfile->GetFileChecksum();
|
||||
meta->file_checksum_func_name =
|
||||
sub_compact->outfile->GetFileChecksumFuncName();
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
s = io_s;
|
||||
if (s.ok()) {
|
||||
s = sub_compact->outfile->Close();
|
||||
}
|
||||
sub_compact->outfile.reset();
|
||||
|
||||
@@ -1535,7 +1514,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->outfile.reset(
|
||||
new WritableFileWriter(std::move(writable_file), fname, file_options_,
|
||||
env_, db_options_.statistics.get(), listeners,
|
||||
db_options_.file_checksum_gen_factory.get()));
|
||||
db_options_.sst_file_checksum_func.get()));
|
||||
|
||||
// If the Column family flag is to only optimize filters for hits,
|
||||
// we can skip creating filters if this is the bottommost_level where
|
||||
|
||||
@@ -100,9 +100,6 @@ class CompactionJob {
|
||||
// Add compaction input/output to the current version
|
||||
Status Install(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Return the IO status
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
private:
|
||||
struct SubcompactionState;
|
||||
|
||||
@@ -196,7 +193,6 @@ class CompactionJob {
|
||||
std::vector<uint64_t> sizes_;
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
Env::Priority thread_pri_;
|
||||
IOStatus io_status_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -281,7 +281,7 @@ class CompactionJobTest : public testing::Test {
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
@@ -314,11 +314,11 @@ class CompactionJobTest : public testing::Test {
|
||||
num_input_files += level_files.size();
|
||||
}
|
||||
|
||||
Compaction compaction(
|
||||
cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(), compaction_input_files, output_level,
|
||||
1024 * 1024, 10 * 1024 * 1024, 0, kNoCompression,
|
||||
cfd->GetLatestMutableCFOptions()->compression_opts, 0, {}, true);
|
||||
Compaction compaction(cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
compaction_input_files, output_level, 1024 * 1024,
|
||||
10 * 1024 * 1024, 0, kNoCompression,
|
||||
cfd->ioptions()->compression_opts, 0, {}, true);
|
||||
compaction.SetInputVersion(cfd->current());
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
|
||||
@@ -110,9 +110,9 @@ CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
|
||||
// If bottommost_compression is set and we are compacting to the
|
||||
// bottommost level then we should use it.
|
||||
if (mutable_cf_options.bottommost_compression != kDisableCompressionOption &&
|
||||
if (ioptions.bottommost_compression != kDisableCompressionOption &&
|
||||
level >= (vstorage->num_non_empty_levels() - 1)) {
|
||||
return mutable_cf_options.bottommost_compression;
|
||||
return ioptions.bottommost_compression;
|
||||
}
|
||||
// If the user has specified a different compression level for each level,
|
||||
// then pick the compression for that level.
|
||||
@@ -132,22 +132,22 @@ CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
}
|
||||
}
|
||||
|
||||
CompressionOptions GetCompressionOptions(const MutableCFOptions& cf_options,
|
||||
CompressionOptions GetCompressionOptions(const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo* vstorage,
|
||||
int level,
|
||||
const bool enable_compression) {
|
||||
if (!enable_compression) {
|
||||
return cf_options.compression_opts;
|
||||
return ioptions.compression_opts;
|
||||
}
|
||||
// If bottommost_compression is set and we are compacting to the
|
||||
// bottommost level then we should use the specified compression options
|
||||
// for the bottmomost_compression.
|
||||
if (cf_options.bottommost_compression != kDisableCompressionOption &&
|
||||
if (ioptions.bottommost_compression != kDisableCompressionOption &&
|
||||
level >= (vstorage->num_non_empty_levels() - 1) &&
|
||||
cf_options.bottommost_compression_opts.enabled) {
|
||||
return cf_options.bottommost_compression_opts;
|
||||
ioptions.bottommost_compression_opts.enabled) {
|
||||
return ioptions.bottommost_compression_opts;
|
||||
}
|
||||
return cf_options.compression_opts;
|
||||
return ioptions.compression_opts;
|
||||
}
|
||||
|
||||
CompactionPicker::CompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
@@ -359,7 +359,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
vstorage, ioptions_, mutable_cf_options, input_files, output_level,
|
||||
compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id, compression_type,
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
@@ -634,7 +634,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
compact_range_options.max_subcompactions, /* grandparents */ {},
|
||||
/* is manual */ true);
|
||||
RegisterCompaction(c);
|
||||
@@ -787,7 +787,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
/* is manual compaction */ true);
|
||||
|
||||
@@ -1085,8 +1085,6 @@ void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
Random64 rnd(/* seed */ reinterpret_cast<uint64_t>(vstorage));
|
||||
size_t random_file_index = static_cast<size_t>(rnd.Uniform(
|
||||
static_cast<uint64_t>(vstorage->FilesMarkedForCompaction().size())));
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::PickFilesMarkedForCompaction",
|
||||
&random_file_index);
|
||||
|
||||
if (continuation(vstorage->FilesMarkedForCompaction()[random_file_index])) {
|
||||
// found the compaction!
|
||||
|
||||
@@ -305,9 +305,9 @@ CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
int level, int base_level,
|
||||
const bool enable_compression = true);
|
||||
|
||||
CompressionOptions GetCompressionOptions(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const VersionStorageInfo* vstorage, int level,
|
||||
const bool enable_compression = true);
|
||||
CompressionOptions GetCompressionOptions(const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo* vstorage,
|
||||
int level,
|
||||
const bool enable_compression = true);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -71,14 +71,10 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
if (current_time > mutable_cf_options.ttl) {
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
FileMetaData* f = *ritr;
|
||||
assert(f);
|
||||
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
|
||||
uint64_t creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time == 0 ||
|
||||
creation_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
break;
|
||||
}
|
||||
uint64_t creation_time = f->TryGetFileCreationTime();
|
||||
if (creation_time == kUnknownFileCreationTime ||
|
||||
creation_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
break;
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
@@ -96,22 +92,17 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
}
|
||||
|
||||
for (const auto& f : inputs[0].files) {
|
||||
uint64_t creation_time = 0;
|
||||
assert(f);
|
||||
if (f->fd.table_reader && f->fd.table_reader->GetTableProperties()) {
|
||||
creation_time = f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
}
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with creation time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), creation_time);
|
||||
cf_name.c_str(), f->fd.GetNumber(),
|
||||
f->TryGetFileCreationTime());
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, mutable_cf_options.compression_opts,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
kNoCompression, ioptions_.compression_opts, /* max_subcompactions */ 0,
|
||||
{}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
return c;
|
||||
}
|
||||
@@ -151,7 +142,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
16 * 1024 * 1024 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts, 0 /* max_subcompactions */, {},
|
||||
ioptions_.compression_opts, 0 /* max_subcompactions */, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
@@ -199,9 +190,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, mutable_cf_options.compression_opts,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
kNoCompression, ioptions_.compression_opts, /* max_subcompactions */ 0,
|
||||
{}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -250,6 +250,7 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
cf_name_, vstorage_, &start_level_, &output_level_,
|
||||
&start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
is_manual_ = true;
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
@@ -383,7 +384,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level_),
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
|
||||
@@ -78,17 +78,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
// Create a new VersionStorageInfo object so we can add mode files and then
|
||||
// merge it with the existing VersionStorageInfo
|
||||
void AddVersionStorage() {
|
||||
temp_vstorage_.reset(new VersionStorageInfo(
|
||||
&icmp_, ucmp_, options_.num_levels, ioptions_.compaction_style,
|
||||
vstorage_.get(), false));
|
||||
}
|
||||
|
||||
void DeleteVersionStorage() {
|
||||
vstorage_.reset();
|
||||
temp_vstorage_.reset();
|
||||
files_.clear();
|
||||
file_map_.clear();
|
||||
input_files_.clear();
|
||||
@@ -97,24 +88,18 @@ class CompactionPickerTest : public testing::Test {
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0, bool marked_for_compact = false) {
|
||||
VersionStorageInfo* vstorage;
|
||||
if (temp_vstorage_) {
|
||||
vstorage = temp_vstorage_.get();
|
||||
} else {
|
||||
vstorage = vstorage_.get();
|
||||
}
|
||||
assert(level < vstorage->num_levels());
|
||||
size_t compensated_file_size = 0) {
|
||||
assert(level < vstorage_->num_levels());
|
||||
FileMetaData* f = new FileMetaData(
|
||||
file_number, path_id, file_size,
|
||||
InternalKey(smallest, smallest_seq, kTypeValue),
|
||||
InternalKey(largest, largest_seq, kTypeValue), smallest_seq,
|
||||
largest_seq, marked_for_compact, kInvalidBlobFileNumber,
|
||||
largest_seq, /* marked_for_compact */ false, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
vstorage->AddFile(level, f);
|
||||
vstorage_->AddFile(level, f);
|
||||
files_.emplace_back(f);
|
||||
file_map_.insert({file_number, {f, level}});
|
||||
}
|
||||
@@ -137,12 +122,6 @@ class CompactionPickerTest : public testing::Test {
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
if (temp_vstorage_) {
|
||||
VersionBuilder builder(FileOptions(), &ioptions_, nullptr,
|
||||
vstorage_.get(), nullptr);
|
||||
builder.SaveTo(temp_vstorage_.get());
|
||||
vstorage_ = std::move(temp_vstorage_);
|
||||
}
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(ioptions_.compaction_pri);
|
||||
vstorage_->UpdateNumNonEmptyLevels();
|
||||
@@ -153,28 +132,6 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_->ComputeFilesMarkedForCompaction();
|
||||
vstorage_->SetFinalized();
|
||||
}
|
||||
void AddFileToVersionStorage(int level, uint32_t file_number,
|
||||
const char* smallest, const char* largest,
|
||||
uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0,
|
||||
bool marked_for_compact = false) {
|
||||
VersionStorageInfo* base_vstorage = vstorage_.release();
|
||||
vstorage_.reset(new VersionStorageInfo(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleUniversal,
|
||||
base_vstorage, false));
|
||||
Add(level, file_number, smallest, largest, file_size, path_id, smallest_seq,
|
||||
largest_seq, compensated_file_size, marked_for_compact);
|
||||
|
||||
VersionBuilder builder(FileOptions(), &ioptions_, nullptr, base_vstorage,
|
||||
nullptr);
|
||||
builder.SaveTo(vstorage_.get());
|
||||
UpdateVersionStorageInfo();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<VersionStorageInfo> temp_vstorage_;
|
||||
};
|
||||
|
||||
TEST_F(CompactionPickerTest, Empty) {
|
||||
@@ -1776,163 +1733,6 @@ TEST_F(CompactionPickerTest, IntraL0ForEarliestSeqno) {
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a "regular" universal compaction is
|
||||
// scheduled first, followed by a delete triggered compaction. The latter
|
||||
// should fail
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450);
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300);
|
||||
Add(3, 5U, "010", "080", 8 * kFileSize, 0, 200, 251);
|
||||
Add(4, 3U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 6U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a compaction to reduce sorted runs
|
||||
ASSERT_EQ(CompactionReason::kUniversalSortedRunNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
|
||||
AddVersionStorage();
|
||||
// Simulate a flush and mark the file for compaction
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 551, 600, 0, true);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap2) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a delete triggered compaction is
|
||||
// scheduled first, followed by a "regular" compaction. The latter
|
||||
// should fail
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
// Mark file number 4 for compaction
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(3, 5U, "240", "290", 8 * kFileSize, 0, 201, 250);
|
||||
Add(4, 3U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 6U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(3, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
|
||||
AddVersionStorage();
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionStartOutputOverlap) {
|
||||
// The case where universal periodic compaction can be picked
|
||||
// with some newer files being compacted.
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
bool input_level_overlap = false;
|
||||
bool output_level_overlap = false;
|
||||
// Let's mark 2 files in 2 different levels for compaction. The
|
||||
// compaction picker will randomly pick one, so use the sync point to
|
||||
// ensure a deterministic order. Loop until both cases are covered
|
||||
size_t random_index = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionPicker::PickFilesMarkedForCompaction", [&](void* arg) {
|
||||
size_t* index = static_cast<size_t*>(arg);
|
||||
*index = random_index;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
while (!input_level_overlap || !output_level_overlap) {
|
||||
// Ensure that the L0 file gets picked first
|
||||
random_index = !input_level_overlap ? 0 : 1;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(3, 2U, "010", "020", 2 * kFileSize, 0, 201, 248);
|
||||
Add(3, 3U, "250", "270", 2 * kFileSize, 0, 202, 249);
|
||||
Add(3, 4U, "290", "310", 2 * kFileSize, 0, 203, 250);
|
||||
Add(3, 5U, "310", "320", 2 * kFileSize, 0, 204, 251, 0, true);
|
||||
Add(4, 6U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 7U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_TRUE(compaction->start_level() == 0 ||
|
||||
compaction->start_level() == 3);
|
||||
if (compaction->start_level() == 0) {
|
||||
// The L0 file was picked. The next compaction will detect an
|
||||
// overlap on its input level
|
||||
input_level_overlap = true;
|
||||
ASSERT_EQ(3, compaction->output_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(3U, compaction->num_input_files(1));
|
||||
} else {
|
||||
// The level 3 file was picked. The next compaction will pick
|
||||
// the L0 file and will detect overlap when adding output
|
||||
// level inputs
|
||||
output_level_overlap = true;
|
||||
ASSERT_EQ(4, compaction->output_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
}
|
||||
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
// After recomputing the compaction score, only one marked file will remain
|
||||
random_index = 0;
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -120,7 +120,8 @@ class UniversalCompactionBuilder {
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage);
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -324,7 +325,8 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage) {
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& /*ioptions*/,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
@@ -334,16 +336,27 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
bool is_first = true;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(level)) {
|
||||
total_compensated_size += f->compensated_file_size;
|
||||
total_size += f->fd.GetFileSize();
|
||||
// Size amp, read amp and periodic compactions always include all files
|
||||
// for a non-zero level. However, a delete triggered compaction and
|
||||
// a trivial move might pick a subset of files in a sorted run. So
|
||||
// always check all files in a sorted run and mark the entire run as
|
||||
// being compacted if one or more files are being compacted
|
||||
if (f->being_compacted) {
|
||||
if (mutable_cf_options.compaction_options_universal.allow_trivial_move ==
|
||||
true) {
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
}
|
||||
} else {
|
||||
// Compaction always includes all files for a non-zero level, so for a
|
||||
// non-zero level, all the files should share the same being_compacted
|
||||
// value.
|
||||
// This assumption is only valid when
|
||||
// mutable_cf_options.compaction_options_universal.allow_trivial_move
|
||||
// is false
|
||||
assert(is_first || f->being_compacted == being_compacted);
|
||||
}
|
||||
if (is_first) {
|
||||
being_compacted = f->being_compacted;
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
if (total_compensated_size > 0) {
|
||||
@@ -359,7 +372,8 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
const int kLevel0 = 0;
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_);
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, ioptions_, mutable_cf_options_);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
@@ -375,7 +389,7 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER_MAX_SZ(
|
||||
log_buffer_, 3072,
|
||||
"[%s] Universal: sorted runs: %" ROCKSDB_PRIszt " files: %s\n",
|
||||
"[%s] Universal: sorted runs files(%" ROCKSDB_PRIszt "): %s\n",
|
||||
cf_name_.c_str(), sorted_runs_.size(), vstorage_->LevelSummary(&tmp));
|
||||
|
||||
Compaction* c = nullptr;
|
||||
@@ -737,7 +751,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_, start_level,
|
||||
1, enable_compression),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, start_level,
|
||||
GetCompressionOptions(ioptions_, vstorage_, start_level,
|
||||
enable_compression),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
@@ -841,7 +855,6 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
|
||||
if (vstorage_->num_levels() == 1) {
|
||||
#if defined(ENABLE_SINGLE_LEVEL_DTC)
|
||||
// This is single level universal. Since we're basically trying to reclaim
|
||||
// space by processing files marked for compaction due to high tombstone
|
||||
// density, let's do the same thing as compaction to reduce size amp which
|
||||
@@ -864,11 +877,6 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
return nullptr;
|
||||
}
|
||||
inputs.push_back(start_level_inputs);
|
||||
#else
|
||||
// Disable due to a known race condition.
|
||||
// TODO: Reenable once the race condition is fixed
|
||||
return nullptr;
|
||||
#endif // ENABLE_SINGLE_LEVEL_DTC
|
||||
} else {
|
||||
int start_level;
|
||||
|
||||
@@ -951,8 +959,8 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ true,
|
||||
score_, false /* deletion_compaction */,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
@@ -1021,7 +1029,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
|
||||
LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_, start_level,
|
||||
1, true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, start_level,
|
||||
GetCompressionOptions(ioptions_, vstorage_, start_level,
|
||||
true /* enable_compression */),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
void CancelAllBackgroundWork(DB* db, bool wait) {
|
||||
(static_cast_with_check<DBImpl>(db->GetRootDB()))
|
||||
(static_cast_with_check<DBImpl, DB>(db->GetRootDB()))
|
||||
->CancelAllBackgroundWork(wait);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end) {
|
||||
return (static_cast_with_check<DBImpl>(db->GetRootDB()))
|
||||
return (static_cast_with_check<DBImpl, DB>(db->GetRootDB()))
|
||||
->DeleteFilesInRanges(column_family, ranges, n, include_end);
|
||||
}
|
||||
|
||||
|
||||
+39
-3
@@ -157,6 +157,42 @@ class CorruptionTest : public testing::Test {
|
||||
ASSERT_GE(max_expected, correct);
|
||||
}
|
||||
|
||||
void CorruptFile(const std::string& fname, int offset, int bytes_to_corrupt) {
|
||||
struct stat sbuf;
|
||||
if (stat(fname.c_str(), &sbuf) != 0) {
|
||||
const char* msg = strerror(errno);
|
||||
FAIL() << fname << ": " << msg;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
// Relative to end of file; make it absolute
|
||||
if (-offset > sbuf.st_size) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = static_cast<int>(sbuf.st_size + offset);
|
||||
}
|
||||
}
|
||||
if (offset > sbuf.st_size) {
|
||||
offset = static_cast<int>(sbuf.st_size);
|
||||
}
|
||||
if (offset + bytes_to_corrupt > sbuf.st_size) {
|
||||
bytes_to_corrupt = static_cast<int>(sbuf.st_size - offset);
|
||||
}
|
||||
|
||||
// Do it
|
||||
std::string contents;
|
||||
Status s = ReadFileToString(Env::Default(), fname, &contents);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
for (int i = 0; i < bytes_to_corrupt; i++) {
|
||||
contents[i + offset] ^= 0x80;
|
||||
}
|
||||
s = WriteStringToFile(Env::Default(), contents, fname);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
Options options;
|
||||
EnvOptions env_options;
|
||||
ASSERT_NOK(VerifySstFileChecksum(options, env_options, fname));
|
||||
}
|
||||
|
||||
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
||||
// Pick file to corrupt
|
||||
std::vector<std::string> filenames;
|
||||
@@ -175,7 +211,7 @@ class CorruptionTest : public testing::Test {
|
||||
}
|
||||
ASSERT_TRUE(!fname.empty()) << filetype;
|
||||
|
||||
test::CorruptFile(fname, offset, bytes_to_corrupt);
|
||||
CorruptFile(fname, offset, bytes_to_corrupt);
|
||||
}
|
||||
|
||||
// corrupts exactly one file at level `level`. if no file found at level,
|
||||
@@ -185,7 +221,7 @@ class CorruptionTest : public testing::Test {
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
for (const auto& m : metadata) {
|
||||
if (m.level == level) {
|
||||
test::CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
|
||||
CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -520,7 +556,7 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
ImmutableCFOptions(options_), kRangeDelBlock, &range_del_handle));
|
||||
|
||||
ASSERT_OK(TryReopen());
|
||||
test::CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
ASSERT_TRUE(TryReopen().IsCorruption());
|
||||
}
|
||||
|
||||
|
||||
+91
-486
@@ -1295,18 +1295,14 @@ TEST_F(DBBasicTest, MultiGetBatchedSimpleUnsorted) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSortedMultiFile) {
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSimpleSorted) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
// To expand the power of this test, generate > 1 table file and
|
||||
// mix with memtable
|
||||
ASSERT_OK(Put(1, "k1", "v1"));
|
||||
ASSERT_OK(Put(1, "k2", "v2"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "k3", "v3"));
|
||||
ASSERT_OK(Put(1, "k4", "v4"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Delete(1, "k4"));
|
||||
ASSERT_OK(Put(1, "k5", "v5"));
|
||||
ASSERT_OK(Delete(1, "no_key"));
|
||||
@@ -1337,7 +1333,7 @@ TEST_F(DBBasicTest, MultiGetBatchedSortedMultiFile) {
|
||||
ASSERT_TRUE(s[5].IsNotFound());
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
} while (ChangeOptions());
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedMultiLevel) {
|
||||
@@ -1778,28 +1774,6 @@ TEST_F(DBBasicTest, IncrementalRecoveryNoCorrupt) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryWithVersionBuildingFailure) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options.best_efforts_recovery = true;
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace {
|
||||
class TableFileListener : public EventListener {
|
||||
@@ -1840,16 +1814,11 @@ TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
ASSERT_OK(Flush(static_cast<int>(cf)));
|
||||
}
|
||||
|
||||
// Delete and corrupt files
|
||||
// Delete files
|
||||
for (size_t i = 0; i < all_cf_names.size(); ++i) {
|
||||
std::vector<std::string>& files = listener->GetFiles(all_cf_names[i]);
|
||||
ASSERT_EQ(3, files.size());
|
||||
std::string corrupted_data;
|
||||
ASSERT_OK(ReadFileToString(env_, files[files.size() - 1], &corrupted_data));
|
||||
ASSERT_OK(WriteStringToFile(
|
||||
env_, corrupted_data.substr(0, corrupted_data.size() - 2),
|
||||
files[files.size() - 1], /*should_sync=*/true));
|
||||
for (int j = static_cast<int>(files.size() - 2); j >= static_cast<int>(i);
|
||||
for (int j = static_cast<int>(files.size() - 1); j >= static_cast<int>(i);
|
||||
--j) {
|
||||
ASSERT_OK(env_->DeleteFile(files[j]));
|
||||
}
|
||||
@@ -1881,84 +1850,6 @@ TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
{
|
||||
// Hack by adding a new MANIFEST with high file number
|
||||
std::string garbage(10, '\0');
|
||||
ASSERT_OK(WriteStringToFile(env_, garbage, dbname_ + "/MANIFEST-001000",
|
||||
/*should_sync=*/true));
|
||||
}
|
||||
{
|
||||
// Hack by adding a corrupted SST not referenced by any MANIFEST
|
||||
std::string garbage(10, '\0');
|
||||
ASSERT_OK(WriteStringToFile(env_, garbage, dbname_ + "/001001.sst",
|
||||
/*should_sync=*/true));
|
||||
}
|
||||
|
||||
options.best_efforts_recovery = true;
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, RecoverWithNoCurrentFile) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
options.best_efforts_recovery = true;
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put(1, "bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Flush(1));
|
||||
Close();
|
||||
ASSERT_OK(env_->DeleteFile(CurrentFileName(dbname_)));
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
std::vector<std::string> cf_names;
|
||||
ASSERT_OK(DB::ListColumnFamilies(DBOptions(options), dbname_, &cf_names));
|
||||
ASSERT_EQ(2, cf_names.size());
|
||||
for (const auto& name : cf_names) {
|
||||
ASSERT_TRUE(name == kDefaultColumnFamilyName || name == "pikachu");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, RecoverWithNoManifest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
{
|
||||
// Delete all MANIFEST.
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &files));
|
||||
for (const auto& file : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type = kLogFile;
|
||||
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||
ASSERT_OK(env_->DeleteFile(dbname_ + "/" + file));
|
||||
}
|
||||
}
|
||||
}
|
||||
options.best_efforts_recovery = true;
|
||||
options.create_if_missing = false;
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
// Since no MANIFEST exists, best-efforts recovery creates a new, empty db.
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, SkipWALIfMissingTableFiles) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -1999,41 +1890,15 @@ TEST_F(DBBasicTest, SkipWALIfMissingTableFiles) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBBasicTest, ManifestChecksumMismatch) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LogWriter::EmitPhysicalRecord:BeforeEncodeChecksum", [&](void* arg) {
|
||||
auto* crc = reinterpret_cast<uint32_t*>(arg);
|
||||
*crc = *crc + 1;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions write_opts;
|
||||
write_opts.disableWAL = true;
|
||||
Status s = db_->Put(write_opts, "foo", "value");
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ASSERT_OK(Put("foo", "value1"));
|
||||
ASSERT_OK(Flush());
|
||||
s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
class DBBasicTestMultiGet : public DBTestBase {
|
||||
class DBBasicTestWithParallelIO
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
|
||||
public:
|
||||
DBBasicTestMultiGet(std::string test_dir, int num_cfs, bool compressed_cache,
|
||||
bool uncompressed_cache, bool compression_enabled,
|
||||
bool fill_cache, uint32_t compression_parallel_threads)
|
||||
: DBTestBase(test_dir) {
|
||||
compression_enabled_ = compression_enabled;
|
||||
fill_cache_ = fill_cache;
|
||||
DBBasicTestWithParallelIO() : DBTestBase("/db_basic_test_with_parallel_io") {
|
||||
bool compressed_cache = std::get<0>(GetParam());
|
||||
bool uncompressed_cache = std::get<1>(GetParam());
|
||||
compression_enabled_ = std::get<2>(GetParam());
|
||||
fill_cache_ = std::get<3>(GetParam());
|
||||
|
||||
if (compressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
@@ -2088,48 +1953,25 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
if (!compression_enabled_) {
|
||||
options.compression = kNoCompression;
|
||||
} else {
|
||||
options.compression_opts.parallel_threads = compression_parallel_threads;
|
||||
}
|
||||
Reopen(options);
|
||||
|
||||
if (num_cfs > 1) {
|
||||
for (int cf = 0; cf < num_cfs; ++cf) {
|
||||
cf_names_.emplace_back("cf" + std::to_string(cf));
|
||||
}
|
||||
CreateColumnFamilies(cf_names_, options);
|
||||
cf_names_.emplace_back("default");
|
||||
}
|
||||
|
||||
std::string zero_str(128, '\0');
|
||||
for (int cf = 0; cf < num_cfs; ++cf) {
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// Make the value compressible. A purely random string doesn't compress
|
||||
// and the resultant data block will not be compressed
|
||||
values_.emplace_back(RandomString(&rnd, 128) + zero_str);
|
||||
assert(((num_cfs == 1) ? Put(Key(i), values_[i])
|
||||
: Put(cf, Key(i), values_[i])) == Status::OK());
|
||||
}
|
||||
if (num_cfs == 1) {
|
||||
Flush();
|
||||
} else {
|
||||
dbfull()->Flush(FlushOptions(), handles_[cf]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// block cannot gain space by compression
|
||||
uncompressable_values_.emplace_back(RandomString(&rnd, 256) + '\0');
|
||||
std::string tmp_key = "a" + Key(i);
|
||||
assert(((num_cfs == 1) ? Put(tmp_key, uncompressable_values_[i])
|
||||
: Put(cf, tmp_key, uncompressable_values_[i])) ==
|
||||
Status::OK());
|
||||
}
|
||||
if (num_cfs == 1) {
|
||||
Flush();
|
||||
} else {
|
||||
dbfull()->Flush(FlushOptions(), handles_[cf]);
|
||||
}
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// Make the value compressible. A purely random string doesn't compress
|
||||
// and the resultant data block will not be compressed
|
||||
values_.emplace_back(RandomString(&rnd, 128) + zero_str);
|
||||
assert(Put(Key(i), values_[i]) == Status::OK());
|
||||
}
|
||||
Flush();
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// block cannot gain space by compression
|
||||
uncompressable_values_.emplace_back(RandomString(&rnd, 256) + '\0');
|
||||
std::string tmp_key = "a" + Key(i);
|
||||
assert(Put(tmp_key, uncompressable_values_[i]) == Status::OK());
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
bool CheckValue(int i, const std::string& value) {
|
||||
@@ -2146,8 +1988,6 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetCFNames() const { return cf_names_; }
|
||||
|
||||
int num_lookups() { return uncompressed_cache_->num_lookups(); }
|
||||
int num_found() { return uncompressed_cache_->num_found(); }
|
||||
int num_inserts() { return uncompressed_cache_->num_inserts(); }
|
||||
@@ -2164,7 +2004,7 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
static void SetUpTestCase() {}
|
||||
static void TearDownTestCase() {}
|
||||
|
||||
protected:
|
||||
private:
|
||||
class MyFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
|
||||
public:
|
||||
MyFlushBlockPolicyFactory() {}
|
||||
@@ -2205,25 +2045,23 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
const BlockBuilder& data_block_builder_;
|
||||
};
|
||||
|
||||
class MyBlockCache : public CacheWrapper {
|
||||
class MyBlockCache : public Cache {
|
||||
public:
|
||||
explicit MyBlockCache(std::shared_ptr<Cache> target)
|
||||
: CacheWrapper(target),
|
||||
num_lookups_(0),
|
||||
num_found_(0),
|
||||
num_inserts_(0) {}
|
||||
explicit MyBlockCache(std::shared_ptr<Cache>& target)
|
||||
: target_(target), num_lookups_(0), num_found_(0), num_inserts_(0) {}
|
||||
|
||||
const char* Name() const override { return "MyBlockCache"; }
|
||||
virtual const char* Name() const override { return "MyBlockCache"; }
|
||||
|
||||
Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
num_inserts_++;
|
||||
return target_->Insert(key, value, charge, deleter, handle, priority);
|
||||
}
|
||||
|
||||
Handle* Lookup(const Slice& key, Statistics* stats = nullptr) override {
|
||||
virtual Handle* Lookup(const Slice& key,
|
||||
Statistics* stats = nullptr) override {
|
||||
num_lookups_++;
|
||||
Handle* handle = target_->Lookup(key, stats);
|
||||
if (handle != nullptr) {
|
||||
@@ -2231,6 +2069,57 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
virtual bool Ref(Handle* handle) override { return target_->Ref(handle); }
|
||||
|
||||
virtual bool Release(Handle* handle, bool force_erase = false) override {
|
||||
return target_->Release(handle, force_erase);
|
||||
}
|
||||
|
||||
virtual void* Value(Handle* handle) override {
|
||||
return target_->Value(handle);
|
||||
}
|
||||
|
||||
virtual void Erase(const Slice& key) override { target_->Erase(key); }
|
||||
virtual uint64_t NewId() override { return target_->NewId(); }
|
||||
|
||||
virtual void SetCapacity(size_t capacity) override {
|
||||
target_->SetCapacity(capacity);
|
||||
}
|
||||
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override {
|
||||
target_->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
virtual bool HasStrictCapacityLimit() const override {
|
||||
return target_->HasStrictCapacityLimit();
|
||||
}
|
||||
|
||||
virtual size_t GetCapacity() const override {
|
||||
return target_->GetCapacity();
|
||||
}
|
||||
|
||||
virtual size_t GetUsage() const override { return target_->GetUsage(); }
|
||||
|
||||
virtual size_t GetUsage(Handle* handle) const override {
|
||||
return target_->GetUsage(handle);
|
||||
}
|
||||
|
||||
virtual size_t GetPinnedUsage() const override {
|
||||
return target_->GetPinnedUsage();
|
||||
}
|
||||
|
||||
virtual size_t GetCharge(Handle* /*handle*/) const override { return 0; }
|
||||
|
||||
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override {
|
||||
return target_->ApplyToAllCacheEntries(callback, thread_safe);
|
||||
}
|
||||
|
||||
virtual void EraseUnRefEntries() override {
|
||||
return target_->EraseUnRefEntries();
|
||||
}
|
||||
|
||||
int num_lookups() { return num_lookups_; }
|
||||
|
||||
int num_found() { return num_found_; }
|
||||
@@ -2238,6 +2127,7 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
int num_inserts() { return num_inserts_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> target_;
|
||||
int num_lookups_;
|
||||
int num_found_;
|
||||
int num_inserts_;
|
||||
@@ -2249,19 +2139,6 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
std::vector<std::string> values_;
|
||||
std::vector<std::string> uncompressable_values_;
|
||||
bool fill_cache_;
|
||||
std::vector<std::string> cf_names_;
|
||||
};
|
||||
|
||||
class DBBasicTestWithParallelIO
|
||||
: public DBBasicTestMultiGet,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<bool, bool, bool, bool, uint32_t>> {
|
||||
public:
|
||||
DBBasicTestWithParallelIO()
|
||||
: DBBasicTestMultiGet("/db_basic_test_with_parallel_io", 1,
|
||||
std::get<0>(GetParam()), std::get<1>(GetParam()),
|
||||
std::get<2>(GetParam()), std::get<3>(GetParam()),
|
||||
std::get<4>(GetParam())) {}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithParallelIO, MultiGet) {
|
||||
@@ -2477,282 +2354,10 @@ INSTANTIATE_TEST_CASE_P(ParallelIO, DBBasicTestWithParallelIO,
|
||||
// Param 1 - Uncompressed cache enabled
|
||||
// Param 2 - Data compression enabled
|
||||
// Param 3 - ReadOptions::fill_cache
|
||||
// Param 4 - CompressionOptions::parallel_threads
|
||||
::testing::Combine(::testing::Bool(), ::testing::Bool(),
|
||||
::testing::Bool(), ::testing::Bool(),
|
||||
::testing::Values(1, 4)));
|
||||
::testing::Bool(),
|
||||
::testing::Bool()));
|
||||
|
||||
// A test class for intercepting random reads and injecting artificial
|
||||
// delays. Used for testing the deadline/timeout feature
|
||||
class DBBasicTestMultiGetDeadline : public DBBasicTestMultiGet {
|
||||
public:
|
||||
DBBasicTestMultiGetDeadline()
|
||||
: DBBasicTestMultiGet("db_basic_test_multiget_deadline" /*Test dir*/,
|
||||
10 /*# of column families*/,
|
||||
false /*compressed cache enabled*/,
|
||||
true /*uncompressed cache enabled*/,
|
||||
true /*compression enabled*/,
|
||||
true /*ReadOptions.fill_cache*/,
|
||||
1 /*# of parallel compression threads*/) {}
|
||||
|
||||
// Forward declaration
|
||||
class DeadlineFS;
|
||||
|
||||
class DeadlineRandomAccessFile : public FSRandomAccessFileWrapper {
|
||||
public:
|
||||
DeadlineRandomAccessFile(DeadlineFS& fs, SpecialEnv* env,
|
||||
std::unique_ptr<FSRandomAccessFile>& file)
|
||||
: FSRandomAccessFileWrapper(file.get()),
|
||||
fs_(fs),
|
||||
file_(std::move(file)),
|
||||
env_(env) {}
|
||||
|
||||
IOStatus Read(uint64_t offset, size_t len, const IOOptions& opts,
|
||||
Slice* result, char* scratch, IODebugContext* dbg) const override {
|
||||
int delay;
|
||||
const std::chrono::microseconds deadline = fs_.GetDeadline();
|
||||
if (deadline.count()) {
|
||||
AssertDeadline(deadline, opts);
|
||||
}
|
||||
if (fs_.ShouldDelay(&delay)) {
|
||||
env_->SleepForMicroseconds(delay);
|
||||
}
|
||||
return FSRandomAccessFileWrapper::Read(offset, len, opts, result, scratch,
|
||||
dbg);
|
||||
}
|
||||
|
||||
IOStatus MultiRead(FSReadRequest* reqs, size_t num_reqs,
|
||||
const IOOptions& options, IODebugContext* dbg) override {
|
||||
int delay;
|
||||
const std::chrono::microseconds deadline = fs_.GetDeadline();
|
||||
if (deadline.count()) {
|
||||
AssertDeadline(deadline, options);
|
||||
}
|
||||
if (fs_.ShouldDelay(&delay)) {
|
||||
env_->SleepForMicroseconds(delay);
|
||||
}
|
||||
return FSRandomAccessFileWrapper::MultiRead(reqs, num_reqs, options, dbg);
|
||||
}
|
||||
|
||||
private:
|
||||
void AssertDeadline(const std::chrono::microseconds deadline,
|
||||
const IOOptions& opts) const {
|
||||
// Give a leeway of +- 10us as it can take some time for the Get/
|
||||
// MultiGet call to reach here, in order to avoid false alarms
|
||||
std::chrono::microseconds now =
|
||||
std::chrono::microseconds(env_->NowMicros());
|
||||
ASSERT_EQ(deadline - now, opts.timeout);
|
||||
}
|
||||
DeadlineFS& fs_;
|
||||
std::unique_ptr<FSRandomAccessFile> file_;
|
||||
SpecialEnv* env_;
|
||||
};
|
||||
|
||||
class DeadlineFS : public FileSystemWrapper {
|
||||
public:
|
||||
DeadlineFS(SpecialEnv* env)
|
||||
: FileSystemWrapper(FileSystem::Default()),
|
||||
delay_idx_(0),
|
||||
deadline_(std::chrono::microseconds::zero()),
|
||||
env_(env) {}
|
||||
~DeadlineFS() = default;
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
IOStatus s;
|
||||
|
||||
s = target()->NewRandomAccessFile(fname, opts, &file, dbg);
|
||||
result->reset(new DeadlineRandomAccessFile(*this, env_, file));
|
||||
return s;
|
||||
}
|
||||
|
||||
// Set a vector of {IO counter, delay in microseconds} pairs that control
|
||||
// when to inject a delay and duration of the delay
|
||||
void SetDelaySequence(const std::chrono::microseconds deadline,
|
||||
const std::vector<std::pair<int, int>>&& seq) {
|
||||
int total_delay = 0;
|
||||
for (auto& seq_iter : seq) {
|
||||
// Ensure no individual delay is > 500ms
|
||||
ASSERT_LT(seq_iter.second, 500000);
|
||||
total_delay += seq_iter.second;
|
||||
}
|
||||
// ASSERT total delay is < 1s. This is mainly to keep the test from
|
||||
// timing out in CI test frameworks
|
||||
ASSERT_LT(total_delay, 1000000);
|
||||
delay_seq_ = seq;
|
||||
delay_idx_ = 0;
|
||||
io_count_ = 0;
|
||||
deadline_ = deadline;
|
||||
}
|
||||
|
||||
// Increment the IO counter and return a delay in microseconds
|
||||
bool ShouldDelay(int* delay) {
|
||||
if (delay_idx_ < delay_seq_.size() &&
|
||||
delay_seq_[delay_idx_].first == io_count_++) {
|
||||
*delay = delay_seq_[delay_idx_].second;
|
||||
delay_idx_++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::chrono::microseconds GetDeadline() { return deadline_; }
|
||||
|
||||
private:
|
||||
std::vector<std::pair<int, int>> delay_seq_;
|
||||
size_t delay_idx_;
|
||||
int io_count_;
|
||||
std::chrono::microseconds deadline_;
|
||||
SpecialEnv* env_;
|
||||
};
|
||||
|
||||
inline void CheckStatus(std::vector<Status>& statuses, size_t num_ok) {
|
||||
for (size_t i = 0; i < statuses.size(); ++i) {
|
||||
if (i < num_ok) {
|
||||
EXPECT_OK(statuses[i]);
|
||||
} else {
|
||||
EXPECT_EQ(statuses[i], Status::TimedOut());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
std::shared_ptr<DBBasicTestMultiGetDeadline::DeadlineFS> fs(
|
||||
new DBBasicTestMultiGetDeadline::DeadlineFS(env_));
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = cache;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.env = env.get();
|
||||
ReopenWithColumnFamilies(GetCFNames(), options);
|
||||
|
||||
// Test the non-batched version of MultiGet with multiple column
|
||||
// families
|
||||
std::vector<std::string> key_str;
|
||||
size_t i;
|
||||
for (i = 0; i < 5; ++i) {
|
||||
key_str.emplace_back(Key(static_cast<int>(i)));
|
||||
}
|
||||
std::vector<ColumnFamilyHandle*> cfs(key_str.size());
|
||||
;
|
||||
std::vector<Slice> keys(key_str.size());
|
||||
std::vector<std::string> values(key_str.size());
|
||||
for (i = 0; i < key_str.size(); ++i) {
|
||||
cfs[i] = handles_[i];
|
||||
keys[i] = Slice(key_str[i].data(), key_str[i].size());
|
||||
}
|
||||
|
||||
ReadOptions ro;
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
// Delay the first IO by 200ms
|
||||
fs->SetDelaySequence(ro.deadline, {{0, 20000}});
|
||||
|
||||
std::vector<Status> statuses = dbfull()->MultiGet(ro, cfs, keys, &values);
|
||||
// The first key is successful because we check after the lookup, but
|
||||
// subsequent keys fail due to deadline exceeded
|
||||
CheckStatus(statuses, 1);
|
||||
|
||||
// Clear the cache
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
// Test non-batched Multiget with multiple column families and
|
||||
// introducing an IO delay in one of the middle CFs
|
||||
key_str.clear();
|
||||
for (i = 0; i < 10; ++i) {
|
||||
key_str.emplace_back(Key(static_cast<int>(i)));
|
||||
}
|
||||
cfs.resize(key_str.size());
|
||||
keys.resize(key_str.size());
|
||||
values.resize(key_str.size());
|
||||
for (i = 0; i < key_str.size(); ++i) {
|
||||
// 2 keys per CF
|
||||
cfs[i] = handles_[i / 2];
|
||||
keys[i] = Slice(key_str[i].data(), key_str[i].size());
|
||||
}
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(ro.deadline, {{1, 20000}});
|
||||
statuses = dbfull()->MultiGet(ro, cfs, keys, &values);
|
||||
CheckStatus(statuses, 3);
|
||||
|
||||
// Test batched MultiGet with an IO delay in the first data block read.
|
||||
// Both keys in the first CF should succeed as they're in the same data
|
||||
// block and would form one batch, and we check for deadline between
|
||||
// batches.
|
||||
std::vector<PinnableSlice> pin_values(keys.size());
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(ro.deadline, {{0, 20000}});
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 2);
|
||||
|
||||
// Similar to the previous one, but an IO delay in the third CF data block
|
||||
// read
|
||||
for (PinnableSlice& value : pin_values) {
|
||||
value.Reset();
|
||||
}
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(ro.deadline, {{2, 20000}});
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 6);
|
||||
|
||||
// Similar to the previous one, but an IO delay in the last but one CF
|
||||
for (PinnableSlice& value : pin_values) {
|
||||
value.Reset();
|
||||
}
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(ro.deadline, {{3, 20000}});
|
||||
dbfull()->MultiGet(ro, keys.size(), cfs.data(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 8);
|
||||
|
||||
// Test batched MultiGet with single CF and lots of keys. Inject delay
|
||||
// into the second batch of keys. As each batch is 32, the first 64 keys,
|
||||
// i.e first two batches, should succeed and the rest should time out
|
||||
for (PinnableSlice& value : pin_values) {
|
||||
value.Reset();
|
||||
}
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1048576);
|
||||
key_str.clear();
|
||||
for (i = 0; i < 100; ++i) {
|
||||
key_str.emplace_back(Key(static_cast<int>(i)));
|
||||
}
|
||||
keys.resize(key_str.size());
|
||||
pin_values.clear();
|
||||
pin_values.resize(key_str.size());
|
||||
for (i = 0; i < key_str.size(); ++i) {
|
||||
keys[i] = Slice(key_str[i].data(), key_str[i].size());
|
||||
}
|
||||
statuses.clear();
|
||||
statuses.resize(keys.size());
|
||||
ro.deadline = std::chrono::microseconds{env->NowMicros() + 10000};
|
||||
fs->SetDelaySequence(ro.deadline, {{1, 20000}});
|
||||
dbfull()->MultiGet(ro, handles_[0], keys.size(), keys.data(),
|
||||
pin_values.data(), statuses.data());
|
||||
CheckStatus(statuses, 64);
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -517,139 +517,6 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksCachePriority) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// An LRUCache wrapper that can falsely report "not found" on Lookup.
|
||||
// This allows us to manipulate BlockBasedTableReader into thinking
|
||||
// another thread inserted the data in between Lookup and Insert,
|
||||
// while mostly preserving the LRUCache interface/behavior.
|
||||
class LookupLiarCache : public CacheWrapper {
|
||||
int nth_lookup_not_found_ = 0;
|
||||
|
||||
public:
|
||||
explicit LookupLiarCache(std::shared_ptr<Cache> target)
|
||||
: CacheWrapper(std::move(target)) {}
|
||||
|
||||
Handle* Lookup(const Slice& key, Statistics* stats) override {
|
||||
if (nth_lookup_not_found_ == 1) {
|
||||
nth_lookup_not_found_ = 0;
|
||||
return nullptr;
|
||||
}
|
||||
if (nth_lookup_not_found_ > 1) {
|
||||
--nth_lookup_not_found_;
|
||||
}
|
||||
return CacheWrapper::Lookup(key, stats);
|
||||
}
|
||||
|
||||
// 1 == next lookup, 2 == after next, etc.
|
||||
void SetNthLookupNotFound(int n) { nth_lookup_not_found_ = n; }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, AddRedundantStats) {
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
int iterations_tested = 0;
|
||||
for (std::shared_ptr<Cache> base_cache :
|
||||
{NewLRUCache(capacity, num_shard_bits),
|
||||
NewClockCache(capacity, num_shard_bits)}) {
|
||||
if (!base_cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
}
|
||||
++iterations_tested;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<LookupLiarCache> cache =
|
||||
std::make_shared<LookupLiarCache>(base_cache);
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// Normal access filter+index+data.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Againt access filter+index+data, but force redundant load+insert on index
|
||||
cache->SetNthLookupNotFound(2);
|
||||
ASSERT_EQ("value", Get("bar"));
|
||||
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just filter (with high probability), and force redundant
|
||||
// load+insert
|
||||
cache->SetNthLookupNotFound(1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("this key was not added"));
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(5, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just data, forcing redundant load+insert
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(read_options)};
|
||||
cache->SetNthLookupNotFound(1);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), "bar");
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(6, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
}
|
||||
EXPECT_GE(iterations_tested, 1);
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
+2
-202
@@ -8,7 +8,6 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "table/block_based/filter_policy_internal.h"
|
||||
@@ -1030,206 +1029,6 @@ TEST_F(DBBloomFilterTest, MemtablePrefixBloomOutOfDomain) {
|
||||
ASSERT_EQ(kKey, iter->key());
|
||||
}
|
||||
|
||||
class DBBloomFilterTestVaryPrefixAndFormatVer
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, uint32_t>> {
|
||||
protected:
|
||||
bool use_prefix_;
|
||||
uint32_t format_version_;
|
||||
|
||||
public:
|
||||
DBBloomFilterTestVaryPrefixAndFormatVer()
|
||||
: DBTestBase("/db_bloom_filter_tests") {}
|
||||
|
||||
~DBBloomFilterTestVaryPrefixAndFormatVer() override {}
|
||||
|
||||
void SetUp() override {
|
||||
use_prefix_ = std::get<0>(GetParam());
|
||||
format_version_ = std::get<1>(GetParam());
|
||||
}
|
||||
|
||||
static std::string UKey(uint32_t i) { return Key(static_cast<int>(i)); }
|
||||
};
|
||||
|
||||
TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
Options options = CurrentOptions();
|
||||
if (use_prefix_) {
|
||||
// Entire key from UKey()
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(9));
|
||||
}
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
bbto.whole_key_filtering = !use_prefix_;
|
||||
bbto.metadata_block_size = 290;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
ReadOptions ropts;
|
||||
|
||||
constexpr uint32_t N = 12000;
|
||||
// Add N/2 evens
|
||||
for (uint32_t i = 0; i < N; i += 2) {
|
||||
ASSERT_OK(Put(UKey(i), UKey(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(TotalTableFiles(), 1);
|
||||
#endif
|
||||
|
||||
constexpr uint32_t Q = 29;
|
||||
// MultiGet In
|
||||
std::array<std::string, Q> keys;
|
||||
std::array<Slice, Q> key_slices;
|
||||
std::array<ColumnFamilyHandle*, Q> column_families;
|
||||
// MultiGet Out
|
||||
std::array<Status, Q> statuses;
|
||||
std::array<PinnableSlice, Q> values;
|
||||
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_POSITIVE);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE);
|
||||
|
||||
// Check that initial clump of keys only loads one partition filter from
|
||||
// block cache.
|
||||
// And that spread out keys load many partition filters.
|
||||
// In both cases, mix present vs. not present keys.
|
||||
for (uint32_t stride : {uint32_t{1}, (N / Q) | 1}) {
|
||||
for (uint32_t i = 0; i < Q; ++i) {
|
||||
keys[i] = UKey(i * stride);
|
||||
key_slices[i] = Slice(keys[i]);
|
||||
column_families[i] = db_->DefaultColumnFamily();
|
||||
statuses[i] = Status();
|
||||
values[i] = PinnableSlice();
|
||||
}
|
||||
|
||||
db_->MultiGet(ropts, Q, &column_families[0], &key_slices[0], &values[0],
|
||||
/*timestamps=*/nullptr, &statuses[0], true);
|
||||
|
||||
// Confirm correct status results
|
||||
uint32_t number_not_found = 0;
|
||||
for (uint32_t i = 0; i < Q; ++i) {
|
||||
if ((i * stride % 2) == 0) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
} else {
|
||||
ASSERT_TRUE(statuses[i].IsNotFound());
|
||||
++number_not_found;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm correct Bloom stats (no FPs)
|
||||
uint64_t filter_useful = TestGetAndResetTickerCount(
|
||||
options,
|
||||
use_prefix_ ? BLOOM_FILTER_PREFIX_USEFUL : BLOOM_FILTER_USEFUL);
|
||||
uint64_t filter_checked =
|
||||
TestGetAndResetTickerCount(options, use_prefix_
|
||||
? BLOOM_FILTER_PREFIX_CHECKED
|
||||
: BLOOM_FILTER_FULL_POSITIVE) +
|
||||
(use_prefix_ ? 0 : filter_useful);
|
||||
EXPECT_EQ(filter_useful, number_not_found);
|
||||
EXPECT_EQ(filter_checked, Q);
|
||||
if (!use_prefix_) {
|
||||
EXPECT_EQ(
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
Q - number_not_found);
|
||||
}
|
||||
|
||||
// Confirm no duplicate loading same filter partition
|
||||
uint64_t filter_accesses =
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT) +
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
if (stride == 1) {
|
||||
EXPECT_EQ(filter_accesses, 1);
|
||||
} else {
|
||||
// for large stride
|
||||
EXPECT_GE(filter_accesses, Q / 2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that a clump of keys (present and not) works when spanning
|
||||
// two partitions
|
||||
int found_spanning = 0;
|
||||
for (uint32_t start = 0; start < N / 2;) {
|
||||
for (uint32_t i = 0; i < Q; ++i) {
|
||||
keys[i] = UKey(start + i);
|
||||
key_slices[i] = Slice(keys[i]);
|
||||
column_families[i] = db_->DefaultColumnFamily();
|
||||
statuses[i] = Status();
|
||||
values[i] = PinnableSlice();
|
||||
}
|
||||
|
||||
db_->MultiGet(ropts, Q, &column_families[0], &key_slices[0], &values[0],
|
||||
/*timestamps=*/nullptr, &statuses[0], true);
|
||||
|
||||
// Confirm correct status results
|
||||
uint32_t number_not_found = 0;
|
||||
for (uint32_t i = 0; i < Q; ++i) {
|
||||
if (((start + i) % 2) == 0) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
} else {
|
||||
ASSERT_TRUE(statuses[i].IsNotFound());
|
||||
++number_not_found;
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm correct Bloom stats (might see some FPs)
|
||||
uint64_t filter_useful = TestGetAndResetTickerCount(
|
||||
options,
|
||||
use_prefix_ ? BLOOM_FILTER_PREFIX_USEFUL : BLOOM_FILTER_USEFUL);
|
||||
uint64_t filter_checked =
|
||||
TestGetAndResetTickerCount(options, use_prefix_
|
||||
? BLOOM_FILTER_PREFIX_CHECKED
|
||||
: BLOOM_FILTER_FULL_POSITIVE) +
|
||||
(use_prefix_ ? 0 : filter_useful);
|
||||
EXPECT_GE(filter_useful, number_not_found - 2); // possible FP
|
||||
EXPECT_EQ(filter_checked, Q);
|
||||
if (!use_prefix_) {
|
||||
EXPECT_EQ(
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
Q - number_not_found);
|
||||
}
|
||||
|
||||
// Confirm no duplicate loading of same filter partition
|
||||
uint64_t filter_accesses =
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT) +
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
if (filter_accesses == 2) {
|
||||
// Spanned across partitions.
|
||||
++found_spanning;
|
||||
if (found_spanning >= 2) {
|
||||
break;
|
||||
} else {
|
||||
// Ensure that at least once we have at least one present and
|
||||
// one non-present key on both sides of partition boundary.
|
||||
start += 2;
|
||||
}
|
||||
} else {
|
||||
EXPECT_EQ(filter_accesses, 1);
|
||||
// See explanation at "start += 2"
|
||||
start += Q - 4;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found_spanning >= 2);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBloomFilterTestVaryPrefixAndFormatVer,
|
||||
DBBloomFilterTestVaryPrefixAndFormatVer,
|
||||
::testing::Values(
|
||||
// (use_prefix, format_version)
|
||||
std::make_tuple(false, 2),
|
||||
std::make_tuple(false, 3),
|
||||
std::make_tuple(false, 4),
|
||||
std::make_tuple(false, 5),
|
||||
std::make_tuple(true, 2),
|
||||
std::make_tuple(true, 3),
|
||||
std::make_tuple(true, 4),
|
||||
std::make_tuple(true, 5)));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace {
|
||||
namespace BFP2 {
|
||||
@@ -1544,7 +1343,8 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
for (int i = 0; i < numkeys; i += 2) {
|
||||
keys.push_back(i);
|
||||
}
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
int num_inserted = 0;
|
||||
for (int key : keys) {
|
||||
ASSERT_OK(Put(1, Key(key), "val"));
|
||||
|
||||
@@ -157,21 +157,21 @@ Options DeletionTriggerOptions(Options options) {
|
||||
bool HaveOverlappingKeyRanges(
|
||||
const Comparator* c,
|
||||
const SstFileMetaData& a, const SstFileMetaData& b) {
|
||||
if (c->CompareWithoutTimestamp(a.smallestkey, b.smallestkey) >= 0) {
|
||||
if (c->CompareWithoutTimestamp(a.smallestkey, b.largestkey) <= 0) {
|
||||
if (c->Compare(a.smallestkey, b.smallestkey) >= 0) {
|
||||
if (c->Compare(a.smallestkey, b.largestkey) <= 0) {
|
||||
// b.smallestkey <= a.smallestkey <= b.largestkey
|
||||
return true;
|
||||
}
|
||||
} else if (c->CompareWithoutTimestamp(a.largestkey, b.smallestkey) >= 0) {
|
||||
} else if (c->Compare(a.largestkey, b.smallestkey) >= 0) {
|
||||
// a.smallestkey < b.smallestkey <= a.largestkey
|
||||
return true;
|
||||
}
|
||||
if (c->CompareWithoutTimestamp(a.largestkey, b.largestkey) <= 0) {
|
||||
if (c->CompareWithoutTimestamp(a.largestkey, b.smallestkey) >= 0) {
|
||||
if (c->Compare(a.largestkey, b.largestkey) <= 0) {
|
||||
if (c->Compare(a.largestkey, b.smallestkey) >= 0) {
|
||||
// b.smallestkey <= a.largestkey <= b.largestkey
|
||||
return true;
|
||||
}
|
||||
} else if (c->CompareWithoutTimestamp(a.smallestkey, b.largestkey) <= 0) {
|
||||
} else if (c->Compare(a.smallestkey, b.largestkey) <= 0) {
|
||||
// a.smallestkey <= b.largestkey < a.largestkey
|
||||
return true;
|
||||
}
|
||||
@@ -4762,7 +4762,7 @@ TEST_P(CompactionPriTest, Test) {
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
RandomShuffle(std::begin(keys), std::end(keys), rnd.Next());
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
ASSERT_OK(Put(Key(keys[i]), RandomString(&rnd, 102)));
|
||||
@@ -5217,6 +5217,31 @@ TEST_P(DBCompactionTestWithParam,
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, FifoCompactionGetFileCreationTime) {
|
||||
MockEnv mock_env(env_);
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.table_factory.reset(new BlockBasedTableFactory());
|
||||
options.env = &mock_env;
|
||||
options.ttl = static_cast<uint64_t>(24) * 3600;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
constexpr size_t kNumFiles = 24;
|
||||
options.max_open_files = 20;
|
||||
constexpr size_t kNumKeysPerFile = 10;
|
||||
DestroyAndReopen(options);
|
||||
for (size_t i = 0; i < kNumFiles; ++i) {
|
||||
for (size_t j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(std::to_string(j), "value_" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
mock_env.FakeSleepForMicroseconds(
|
||||
static_cast<uint64_t>(1000 * 1000 * (1 + options.ttl)));
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
|
||||
keys[i] = i;
|
||||
}
|
||||
if (ordered_insert == 0) {
|
||||
RandomShuffle(std::begin(keys), std::end(keys), rnd.Next());
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
}
|
||||
for (int max_background_compactions = 1; max_background_compactions < 4;
|
||||
max_background_compactions += 2) {
|
||||
|
||||
+50
-203
@@ -311,11 +311,6 @@ Status DBImpl::ResumeImpl() {
|
||||
s = bg_error;
|
||||
}
|
||||
|
||||
// Make sure the IO Status stored in version set is set to OK.
|
||||
if (s.ok()) {
|
||||
versions_->SetIOStatusOK();
|
||||
}
|
||||
|
||||
// We cannot guarantee consistency of the WAL. So force flush Memtables of
|
||||
// all the column families
|
||||
if (s.ok()) {
|
||||
@@ -870,7 +865,9 @@ void DBImpl::DumpStats() {
|
||||
Status DBImpl::TablesRangeTombstoneSummary(ColumnFamilyHandle* column_family,
|
||||
int max_entries_to_print,
|
||||
std::string* out_str) {
|
||||
auto* cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto* cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl, ColumnFamilyHandle>(
|
||||
column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
@@ -1008,12 +1005,12 @@ Status DBImpl::SetDBOptions(
|
||||
}
|
||||
if (s.ok()) {
|
||||
const BGJobLimits current_bg_job_limits =
|
||||
GetBGJobLimits(mutable_db_options_.max_background_flushes,
|
||||
GetBGJobLimits(immutable_db_options_.max_background_flushes,
|
||||
mutable_db_options_.max_background_compactions,
|
||||
mutable_db_options_.max_background_jobs,
|
||||
/* parallelize_compactions */ true);
|
||||
const BGJobLimits new_bg_job_limits = GetBGJobLimits(
|
||||
new_options.max_background_flushes,
|
||||
immutable_db_options_.max_background_flushes,
|
||||
new_options.max_background_compactions,
|
||||
new_options.max_background_jobs, /* parallelize_compactions */ true);
|
||||
|
||||
@@ -1149,25 +1146,25 @@ int DBImpl::FindMinimumEmptyLevelFitting(
|
||||
|
||||
Status DBImpl::FlushWAL(bool sync) {
|
||||
if (manual_wal_flush_) {
|
||||
IOStatus io_s;
|
||||
Status s;
|
||||
{
|
||||
// We need to lock log_write_mutex_ since logs_ might change concurrently
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
io_s = cur_log_writer->WriteBuffer();
|
||||
s = cur_log_writer->WriteBuffer();
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "WAL flush error %s",
|
||||
io_s.ToString().c_str());
|
||||
s.ToString().c_str());
|
||||
// In case there is a fs error we should set it globally to prevent the
|
||||
// future writes
|
||||
IOStatusCheck(io_s);
|
||||
WriteStatusCheck(s);
|
||||
// whether sync or not, we should abort the rest of function upon error
|
||||
return std::move(io_s);
|
||||
return s;
|
||||
}
|
||||
if (!sync) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "FlushWAL sync=false");
|
||||
return std::move(io_s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (!sync) {
|
||||
@@ -1219,21 +1216,12 @@ Status DBImpl::SyncWAL() {
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
io_s = log->file()->SyncWithoutFlush(immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
status = io_s;
|
||||
status = log->file()->SyncWithoutFlush(immutable_db_options_.use_fsync);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "WAL Sync error %s",
|
||||
io_s.ToString().c_str());
|
||||
// In case there is a fs error we should set it globally to prevent the
|
||||
// future writes
|
||||
IOStatusCheck(io_s);
|
||||
}
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
status = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
@@ -1260,7 +1248,7 @@ Status DBImpl::LockWAL() {
|
||||
// future writes
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
return std::move(status);
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::UnlockWAL() {
|
||||
@@ -1311,7 +1299,7 @@ bool DBImpl::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) {
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family, bool allow_unprepared_value) {
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
cfd = default_cf_handle_->cfd();
|
||||
@@ -1325,7 +1313,7 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
mutex_.Unlock();
|
||||
ReadOptions roptions;
|
||||
return NewInternalIterator(roptions, cfd, super_version, arena, range_del_agg,
|
||||
sequence, allow_unprepared_value);
|
||||
sequence);
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePurge() {
|
||||
@@ -1448,8 +1436,7 @@ InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber sequence,
|
||||
bool allow_unprepared_value) {
|
||||
SequenceNumber sequence) {
|
||||
InternalIterator* internal_iter;
|
||||
assert(arena != nullptr);
|
||||
assert(range_del_agg != nullptr);
|
||||
@@ -1481,8 +1468,7 @@ InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
// Collect iterators for files in L0 - Ln
|
||||
if (read_options.read_tier != kMemtableTier) {
|
||||
super_version->current->AddIterators(read_options, file_options_,
|
||||
&merge_iter_builder, range_del_agg,
|
||||
allow_unprepared_value);
|
||||
&merge_iter_builder, range_del_agg);
|
||||
}
|
||||
internal_iter = merge_iter_builder.Finish();
|
||||
IterState* cleanup =
|
||||
@@ -1527,20 +1513,6 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
GetImplOptions& get_impl_options) {
|
||||
assert(get_impl_options.value != nullptr ||
|
||||
get_impl_options.merge_operands != nullptr);
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(get_impl_options.column_family);
|
||||
ColumnFamilyHandle* cf = get_impl_options.column_family;
|
||||
const Comparator* const ucmp = cf->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
assert(read_options.timestamp);
|
||||
assert(read_options.timestamp->size() == ucmp->timestamp_size());
|
||||
} else {
|
||||
assert(!read_options.timestamp);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
@@ -1716,7 +1688,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values) {
|
||||
return MultiGet(read_options, column_family, keys, values,
|
||||
/*timestamps=*/nullptr);
|
||||
/*timestamps*/ nullptr);
|
||||
}
|
||||
|
||||
std::vector<Status> DBImpl::MultiGet(
|
||||
@@ -1728,20 +1700,6 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
StopWatch sw(env_, stats_, DB_MULTIGET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (const auto* cfh : column_family) {
|
||||
assert(cfh);
|
||||
const Comparator* const ucmp = cfh->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
assert(read_options.timestamp);
|
||||
assert(ucmp->timestamp_size() == read_options.timestamp->size());
|
||||
} else {
|
||||
assert(!read_options.timestamp);
|
||||
}
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
SequenceNumber consistent_seqnum;
|
||||
|
||||
std::unordered_map<uint32_t, MultiGetColumnFamilyData> multiget_cf_data(
|
||||
@@ -1786,16 +1744,14 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
// s is both in/out. When in, s could either be OK or MergeInProgress.
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t num_found = 0;
|
||||
size_t keys_read;
|
||||
for (keys_read = 0; keys_read < num_keys; ++keys_read) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
merge_context.Clear();
|
||||
Status& s = stat_list[keys_read];
|
||||
std::string* value = &(*values)[keys_read];
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[keys_read] : nullptr;
|
||||
Status& s = stat_list[i];
|
||||
std::string* value = &(*values)[i];
|
||||
std::string* timestamp = timestamps ? &(*timestamps)[i] : nullptr;
|
||||
|
||||
LookupKey lkey(keys[keys_read], consistent_seqnum, read_options.timestamp);
|
||||
auto cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(column_family[keys_read]);
|
||||
LookupKey lkey(keys[i], consistent_seqnum, read_options.timestamp);
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family[i]);
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
auto mgd_iter = multiget_cf_data.find(cfh->cfd()->GetID());
|
||||
assert(mgd_iter != multiget_cf_data.end());
|
||||
@@ -1831,22 +1787,6 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
bytes_read += value->size();
|
||||
num_found++;
|
||||
}
|
||||
|
||||
if (read_options.deadline.count() &&
|
||||
env_->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keys_read < num_keys) {
|
||||
// The only reason to break out of the loop is when the deadline is
|
||||
// exceeded
|
||||
assert(env_->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count()));
|
||||
for (++keys_read; keys_read < num_keys; ++keys_read) {
|
||||
stat_list[keys_read] = Status::TimedOut();
|
||||
}
|
||||
}
|
||||
|
||||
// Post processing (decrement reference counts and record statistics)
|
||||
@@ -2001,7 +1941,7 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
return MultiGet(read_options, num_keys, column_families, keys, values,
|
||||
/*timestamps=*/nullptr, statuses, sorted_input);
|
||||
/*timestamps*/ nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
@@ -2011,29 +1951,12 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
if (num_keys == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
ColumnFamilyHandle* cfh = column_families[i];
|
||||
assert(cfh);
|
||||
const Comparator* const ucmp = cfh->GetComparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0) {
|
||||
assert(read_options.timestamp);
|
||||
assert(read_options.timestamp->size() == ucmp->timestamp_size());
|
||||
} else {
|
||||
assert(!read_options.timestamp);
|
||||
}
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
autovector<KeyContext, MultiGetContext::MAX_BATCH_SIZE> key_context;
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
key_context.emplace_back(column_families[i], keys[i], &values[i],
|
||||
timestamps ? ×tamps[i] : nullptr,
|
||||
&statuses[i]);
|
||||
×tamps[i], &statuses[i]);
|
||||
}
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
sorted_keys[i] = &key_context[i];
|
||||
@@ -2073,31 +1996,14 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
read_options, nullptr, iter_deref_lambda, &multiget_cf_data,
|
||||
&consistent_seqnum);
|
||||
|
||||
Status s;
|
||||
auto cf_iter = multiget_cf_data.begin();
|
||||
for (; cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
s = MultiGetImpl(read_options, cf_iter->start, cf_iter->num_keys,
|
||||
&sorted_keys, cf_iter->super_version, consistent_seqnum,
|
||||
nullptr, nullptr);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
assert(s.IsTimedOut());
|
||||
for (++cf_iter; cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
for (size_t i = cf_iter->start; i < cf_iter->start + cf_iter->num_keys;
|
||||
++i) {
|
||||
*sorted_keys[i]->s = Status::TimedOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& iter : multiget_cf_data) {
|
||||
for (auto cf_iter = multiget_cf_data.begin();
|
||||
cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
MultiGetImpl(read_options, cf_iter->start, cf_iter->num_keys, &sorted_keys,
|
||||
cf_iter->super_version, consistent_seqnum, nullptr, nullptr);
|
||||
if (!unref_only) {
|
||||
ReturnAndCleanupSuperVersion(iter.cfd, iter.super_version);
|
||||
ReturnAndCleanupSuperVersion(cf_iter->cfd, cf_iter->super_version);
|
||||
} else {
|
||||
iter.cfd->GetSuperVersion()->Unref();
|
||||
cf_iter->cfd->GetSuperVersion()->Unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2120,8 +2026,7 @@ struct CompareKeyContext {
|
||||
}
|
||||
|
||||
// Both keys are from the same column family
|
||||
int cmp = comparator->CompareWithoutTimestamp(
|
||||
*(lhs->key), /*a_has_ts=*/false, *(rhs->key), /*b_has_ts=*/false);
|
||||
int cmp = comparator->Compare(*(lhs->key), *(rhs->key));
|
||||
if (cmp < 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -2153,8 +2058,7 @@ void DBImpl::PrepareMultiGetKeys(
|
||||
}
|
||||
|
||||
// Both keys are from the same column family
|
||||
int cmp = comparator->CompareWithoutTimestamp(
|
||||
*(lhs->key), /*a_has_ts=*/false, *(rhs->key), /*b_has_ts=*/false);
|
||||
int cmp = comparator->Compare(*(lhs->key), *(rhs->key));
|
||||
assert(cmp <= 0);
|
||||
}
|
||||
index++;
|
||||
@@ -2240,24 +2144,14 @@ void DBImpl::MultiGetWithCallback(
|
||||
consistent_seqnum = callback->max_visible_seq();
|
||||
}
|
||||
|
||||
Status s = MultiGetImpl(read_options, 0, num_keys, sorted_keys,
|
||||
multiget_cf_data[0].super_version, consistent_seqnum,
|
||||
nullptr, nullptr);
|
||||
assert(s.ok() || s.IsTimedOut());
|
||||
MultiGetImpl(read_options, 0, num_keys, sorted_keys,
|
||||
multiget_cf_data[0].super_version, consistent_seqnum, nullptr,
|
||||
nullptr);
|
||||
ReturnAndCleanupSuperVersion(multiget_cf_data[0].cfd,
|
||||
multiget_cf_data[0].super_version);
|
||||
}
|
||||
|
||||
// The actual implementation of batched MultiGet. Parameters -
|
||||
// start_key - Index in the sorted_keys vector to start processing from
|
||||
// num_keys - Number of keys to lookup, starting with sorted_keys[start_key]
|
||||
// sorted_keys - The entire batch of sorted keys for this CF
|
||||
//
|
||||
// The per key status is returned in the KeyContext structures pointed to by
|
||||
// sorted_keys. An overall Status is also returned, with the only possible
|
||||
// values being Status::OK() and Status::TimedOut(). The latter indicates
|
||||
// that the call exceeded read_options.deadline
|
||||
Status DBImpl::MultiGetImpl(
|
||||
void DBImpl::MultiGetImpl(
|
||||
const ReadOptions& read_options, size_t start_key, size_t num_keys,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys,
|
||||
SuperVersion* super_version, SequenceNumber snapshot,
|
||||
@@ -2270,20 +2164,12 @@ Status DBImpl::MultiGetImpl(
|
||||
// s is both in/out. When in, s could either be OK or MergeInProgress.
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t keys_left = num_keys;
|
||||
Status s;
|
||||
while (keys_left) {
|
||||
if (read_options.deadline.count() &&
|
||||
env_->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count())) {
|
||||
s = Status::TimedOut();
|
||||
break;
|
||||
}
|
||||
|
||||
size_t batch_size = (keys_left > MultiGetContext::MAX_BATCH_SIZE)
|
||||
? MultiGetContext::MAX_BATCH_SIZE
|
||||
: keys_left;
|
||||
MultiGetContext ctx(sorted_keys, start_key + num_keys - keys_left,
|
||||
batch_size, snapshot, read_options);
|
||||
batch_size, snapshot);
|
||||
MultiGetRange range = ctx.GetMultiGetRange();
|
||||
bool lookup_current = false;
|
||||
|
||||
@@ -2321,21 +2207,13 @@ Status DBImpl::MultiGetImpl(
|
||||
PERF_TIMER_GUARD(get_post_process_time);
|
||||
size_t num_found = 0;
|
||||
uint64_t bytes_read = 0;
|
||||
for (size_t i = start_key; i < start_key + num_keys - keys_left; ++i) {
|
||||
for (size_t i = start_key; i < start_key + num_keys; ++i) {
|
||||
KeyContext* key = (*sorted_keys)[i];
|
||||
if (key->s->ok()) {
|
||||
bytes_read += key->value->size();
|
||||
num_found++;
|
||||
}
|
||||
}
|
||||
if (keys_left) {
|
||||
assert(s.IsTimedOut());
|
||||
for (size_t i = start_key + num_keys - keys_left; i < start_key + num_keys;
|
||||
++i) {
|
||||
KeyContext* key = (*sorted_keys)[i];
|
||||
*key->s = Status::TimedOut();
|
||||
}
|
||||
}
|
||||
|
||||
RecordTick(stats_, NUMBER_MULTIGET_CALLS);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_KEYS_READ, num_keys);
|
||||
@@ -2344,8 +2222,6 @@ Status DBImpl::MultiGetImpl(
|
||||
RecordInHistogram(stats_, BYTES_PER_MULTIGET, bytes_read);
|
||||
PERF_COUNTER_ADD(multiget_read_bytes, bytes_read);
|
||||
PERF_TIMER_STOP(get_post_process_time);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
@@ -2634,12 +2510,6 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
return NewErrorIterator(
|
||||
Status::NotSupported("Managed iterator is not supported anymore."));
|
||||
}
|
||||
// We will eventually support deadline for iterators too, but safeguard
|
||||
// for now
|
||||
if (read_options.deadline != std::chrono::microseconds::zero()) {
|
||||
return NewErrorIterator(
|
||||
Status::NotSupported("ReadOptions deadline is not supported"));
|
||||
}
|
||||
Iterator* result = nullptr;
|
||||
if (read_options.read_tier == kPersistedTier) {
|
||||
return NewErrorIterator(Status::NotSupported(
|
||||
@@ -2655,8 +2525,7 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
" guaranteed to be preserved, try larger iter_start_seqnum opt."));
|
||||
}
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
assert(cfd != nullptr);
|
||||
auto cfd = cfh->cfd();
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (read_options.tailing) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
@@ -2665,8 +2534,7 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
|
||||
#else
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv,
|
||||
/* allow_unprepared_value */ true);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv);
|
||||
result = NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
@@ -2677,11 +2545,10 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
|
||||
// WritePreparedTxnDB
|
||||
result = NewIteratorImpl(read_options, cfd,
|
||||
(read_options.snapshot != nullptr)
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: kMaxSequenceNumber,
|
||||
read_callback);
|
||||
auto snapshot = read_options.snapshot != nullptr
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: versions_->LastSequence();
|
||||
result = NewIteratorImpl(read_options, cfd, snapshot, read_callback);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2694,24 +2561,6 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
bool allow_refresh) {
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:1");
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:2");
|
||||
|
||||
if (snapshot == kMaxSequenceNumber) {
|
||||
// Note that the snapshot is assigned AFTER referencing the super
|
||||
// version because otherwise a flush happening in between may compact away
|
||||
// data for the snapshot, so the reader would see neither data that was be
|
||||
// visible to the snapshot before compaction nor the newer data inserted
|
||||
// afterwards.
|
||||
// Note that the super version might not contain all the data available
|
||||
// to this snapshot, but in that case it can see all the data in the
|
||||
// super version, which is a valid consistent state after the user
|
||||
// calls NewIterator().
|
||||
snapshot = versions_->LastSequence();
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:3");
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:4");
|
||||
}
|
||||
|
||||
// Try to generate a DB iterator tree in continuous memory area to be
|
||||
// cache friendly. Here is an example of result:
|
||||
// +-------------------------------+
|
||||
@@ -2762,8 +2611,7 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->GetRangeDelAggregator(), snapshot);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
|
||||
return db_iter;
|
||||
@@ -2791,8 +2639,7 @@ Status DBImpl::NewIterators(
|
||||
for (auto cfh : column_families) {
|
||||
auto cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv,
|
||||
/* allow_unprepared_value */ true);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv);
|
||||
iterators->push_back(NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
|
||||
+16
-25
@@ -477,7 +477,6 @@ class DBImpl : public DB {
|
||||
Status GetImpl(const ReadOptions& options, const Slice& key,
|
||||
GetImplOptions& get_impl_options);
|
||||
|
||||
// If `snapshot` == kMaxSequenceNumber, set a recent one inside the file.
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& options,
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
@@ -582,14 +581,9 @@ class DBImpl : public DB {
|
||||
// Return an internal iterator over the current state of the database.
|
||||
// The keys of this iterator are internal keys (see format.h).
|
||||
// The returned iterator should be deleted when no longer needed.
|
||||
// If allow_unprepared_value is true, the returned iterator may defer reading
|
||||
// the value and so will require PrepareValue() to be called before value();
|
||||
// allow_unprepared_value = false is convenient when this optimization is not
|
||||
// useful, e.g. when reading the whole column family.
|
||||
InternalIterator* NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool allow_unprepared_value = false);
|
||||
ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
LogsWithPrepTracker* logs_with_prep_tracker() {
|
||||
return &logs_with_prep_tracker_;
|
||||
@@ -715,8 +709,7 @@ class DBImpl : public DB {
|
||||
|
||||
InternalIterator* NewInternalIterator(
|
||||
const ReadOptions&, ColumnFamilyData* cfd, SuperVersion* super_version,
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
bool allow_unprepared_value);
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence);
|
||||
|
||||
// hollow transactions shell used for recovery.
|
||||
// these will then be passed to TransactionDB so that
|
||||
@@ -1150,11 +1143,13 @@ class DBImpl : public DB {
|
||||
// REQUIRES: db mutex held when calling this function, but the db mutex can
|
||||
// be released and re-acquired. Db mutex will be held when the function
|
||||
// returns.
|
||||
// Currently, this function should be called only in best-efforts recovery
|
||||
// mode.
|
||||
// After best-efforts recovery, there may be SST files in db/cf paths that are
|
||||
// not referenced in the MANIFEST. We delete these SST files. In the
|
||||
// meantime, we find out the largest file number present in the paths, and
|
||||
// bump up the version set's next_file_number_ to be 1 + largest_file_number.
|
||||
Status FinishBestEffortsRecovery();
|
||||
Status CleanupFilesAfterRecovery();
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
@@ -1369,7 +1364,7 @@ class DBImpl : public DB {
|
||||
void ReleaseFileNumberFromPendingOutputs(
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v);
|
||||
|
||||
IOStatus SyncClosedLogs(JobContext* job_context);
|
||||
Status SyncClosedLogs(JobContext* job_context);
|
||||
|
||||
// Flush the in-memory write buffer to storage. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful. Then
|
||||
@@ -1506,25 +1501,21 @@ class DBImpl : public DB {
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal,
|
||||
WriteBatch** to_be_cached_state);
|
||||
|
||||
IOStatus WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size);
|
||||
Status WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size);
|
||||
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence);
|
||||
Status WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence);
|
||||
|
||||
IOStatus ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used,
|
||||
SequenceNumber* last_sequence, size_t seq_inc);
|
||||
Status ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used, SequenceNumber* last_sequence,
|
||||
size_t seq_inc);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
void WriteStatusCheck(const Status& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ when IO error happens, e.g., write
|
||||
// WAL, sync WAL fails, if paranoid check is enabled.
|
||||
void IOStatusCheck(const IOStatus& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
|
||||
void MemTableInsertStatusCheck(const Status& memtable_insert_status);
|
||||
|
||||
@@ -1768,7 +1759,7 @@ class DBImpl : public DB {
|
||||
// to have acquired the SuperVersion and pass in a snapshot sequence number
|
||||
// in order to construct the LookupKeys. The start_key and num_keys specify
|
||||
// the range of keys in the sorted_keys vector for a single column family.
|
||||
Status MultiGetImpl(
|
||||
void MultiGetImpl(
|
||||
const ReadOptions& read_options, size_t start_key, size_t num_keys,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys,
|
||||
SuperVersion* sv, SequenceNumber snap_seqnum, ReadCallback* callback,
|
||||
|
||||
@@ -79,7 +79,7 @@ bool DBImpl::RequestCompactionToken(ColumnFamilyData* cfd, bool force,
|
||||
return false;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
mutex_.AssertHeld();
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
@@ -96,7 +96,7 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
IOStatus io_s;
|
||||
Status s;
|
||||
if (!logs_to_sync.empty()) {
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -104,34 +104,34 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
log->get_log_number());
|
||||
io_s = log->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
s = log->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (immutable_db_options_.recycle_log_file_num > 0) {
|
||||
io_s = log->Close();
|
||||
if (!io_s.ok()) {
|
||||
s = log->Close();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
io_s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
if (s.ok()) {
|
||||
s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
|
||||
mutex_.Lock();
|
||||
|
||||
// "number <= current_log_number - 1" is equivalent to
|
||||
// "number < current_log_number".
|
||||
MarkLogsSynced(current_log_number - 1, true, io_s);
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
MarkLogsSynced(current_log_number - 1, true, s);
|
||||
if (!s.ok()) {
|
||||
error_handler_.SetBGError(s, BackgroundErrorReason::kFlush);
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return io_s;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTableToOutputFile(
|
||||
@@ -155,6 +155,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri);
|
||||
|
||||
FileMetaData file_meta;
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforePickMemtables");
|
||||
@@ -167,7 +168,6 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
if (logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1) {
|
||||
// If there are more than one column families, we need to make sure that
|
||||
@@ -176,8 +176,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// flushed SST may contain data from write batches whose updates to
|
||||
// other column families are missing.
|
||||
// SyncClosedLogs() may unlock and re-lock the db_mutex.
|
||||
io_s = SyncClosedLogs(job_context);
|
||||
s = io_s;
|
||||
s = SyncClosedLogs(job_context);
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Skip");
|
||||
}
|
||||
@@ -193,7 +192,6 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
} else {
|
||||
flush_job.Cancel();
|
||||
}
|
||||
io_s = flush_job.io_status();
|
||||
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, superversion_context,
|
||||
@@ -208,13 +206,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
|
||||
if (!s.ok() && !s.IsShutdownInProgress() && !s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && !io_s.IsShutdownInProgress() &&
|
||||
!io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
if (s.ok()) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -351,7 +344,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
assert(num_cfs == static_cast<int>(jobs.size()));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -366,18 +358,15 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
if (logfile_number_ > 0) {
|
||||
// TODO (yanqin) investigate whether we should sync the closed logs for
|
||||
// single column family case.
|
||||
io_s = SyncClosedLogs(job_context);
|
||||
s = io_s;
|
||||
s = SyncClosedLogs(job_context);
|
||||
}
|
||||
|
||||
// exec_status stores the execution status of flush_jobs as
|
||||
// <bool /* executed */, Status /* status code */>
|
||||
autovector<std::pair<bool, Status>> exec_status;
|
||||
autovector<IOStatus> io_status;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
// Initially all jobs are not executed, with status OK.
|
||||
exec_status.emplace_back(false, Status::OK());
|
||||
io_status.emplace_back(IOStatus::OK());
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -386,7 +375,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
exec_status[i].second =
|
||||
jobs[i]->Run(&logs_with_prep_tracker_, &file_meta[i]);
|
||||
exec_status[i].first = true;
|
||||
io_status[i] = jobs[i]->io_status();
|
||||
}
|
||||
if (num_cfs > 1) {
|
||||
TEST_SYNC_POINT(
|
||||
@@ -399,7 +387,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
exec_status[0].second =
|
||||
jobs[0]->Run(&logs_with_prep_tracker_, &file_meta[0]);
|
||||
exec_status[0].first = true;
|
||||
io_status[0] = jobs[0]->io_status();
|
||||
|
||||
Status error_status;
|
||||
for (const auto& e : exec_status) {
|
||||
@@ -418,20 +405,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
s = error_status.ok() ? s : error_status;
|
||||
}
|
||||
|
||||
if (io_s.ok()) {
|
||||
IOStatus io_error = IOStatus::OK();
|
||||
for (int i = 0; i != static_cast<int>(io_status.size()); i++) {
|
||||
if (!io_status[i].ok() && !io_status[i].IsShutdownInProgress() &&
|
||||
!io_status[i].IsColumnFamilyDropped()) {
|
||||
io_error = io_status[i];
|
||||
}
|
||||
}
|
||||
io_s = io_error;
|
||||
if (s.ok() && !io_s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.IsColumnFamilyDropped()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
@@ -570,15 +543,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -2030,7 +1997,7 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
|
||||
DBImpl::BGJobLimits DBImpl::GetBGJobLimits() const {
|
||||
mutex_.AssertHeld();
|
||||
return GetBGJobLimits(mutable_db_options_.max_background_flushes,
|
||||
return GetBGJobLimits(immutable_db_options_.max_background_flushes,
|
||||
mutable_db_options_.max_background_compactions,
|
||||
mutable_db_options_.max_background_jobs,
|
||||
write_controller_.NeedSpeedupCompaction());
|
||||
@@ -2143,7 +2110,8 @@ void DBImpl::BGWorkFlush(void* arg) {
|
||||
|
||||
IOSTATS_SET_THREAD_POOL_ID(fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush");
|
||||
static_cast_with_check<DBImpl>(fta.db_)->BackgroundCallFlush(fta.thread_pri_);
|
||||
static_cast_with_check<DBImpl, DB>(fta.db_)->BackgroundCallFlush(
|
||||
fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush:done");
|
||||
}
|
||||
|
||||
@@ -2154,7 +2122,7 @@ void DBImpl::BGWorkCompaction(void* arg) {
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkCompaction");
|
||||
auto prepicked_compaction =
|
||||
static_cast<PrepickedCompaction*>(ca.prepicked_compaction);
|
||||
static_cast_with_check<DBImpl>(ca.db)->BackgroundCallCompaction(
|
||||
static_cast_with_check<DBImpl, DB>(ca.db)->BackgroundCallCompaction(
|
||||
prepicked_compaction, Env::Priority::LOW);
|
||||
delete prepicked_compaction;
|
||||
}
|
||||
@@ -2548,8 +2516,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
"[%s] Manual compaction from level-%d from %s .. "
|
||||
"%s; nothing to do\n",
|
||||
m->cfd->GetName().c_str(), m->input_level,
|
||||
(m->begin ? m->begin->DebugString(true).c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString(true).c_str() : "(end)"));
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"));
|
||||
} else {
|
||||
// First check if we have enough room to do the compaction
|
||||
bool enough_room = EnoughRoomForCompaction(
|
||||
@@ -2568,11 +2536,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
"[%s] Manual compaction from level-%d to level-%d from %s .. "
|
||||
"%s; will stop at %s\n",
|
||||
m->cfd->GetName().c_str(), m->input_level, c->output_level(),
|
||||
(m->begin ? m->begin->DebugString(true).c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString(true).c_str() : "(end)"),
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"),
|
||||
((m->done || m->manual_end == nullptr)
|
||||
? "(end)"
|
||||
: m->manual_end->DebugString(true).c_str()));
|
||||
: m->manual_end->DebugString().c_str()));
|
||||
}
|
||||
}
|
||||
} else if (!is_prepicked && !compaction_queue_.empty()) {
|
||||
@@ -2665,7 +2633,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus io_s;
|
||||
if (!c) {
|
||||
// Nothing to do
|
||||
ROCKS_LOG_BUFFER(log_buffer, "Compaction nothing to do");
|
||||
@@ -2687,11 +2654,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
for (const auto& f : *c->inputs(0)) {
|
||||
c->edit()->DeleteFile(c->level(), f->fd.GetNumber());
|
||||
}
|
||||
versions_->SetIOStatusOK();
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
io_s = versions_->io_status();
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
@@ -2745,11 +2710,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
}
|
||||
}
|
||||
|
||||
versions_->SetIOStatusOK();
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
io_s = versions_->io_status();
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
@@ -2836,7 +2799,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
mutex_.Lock();
|
||||
|
||||
status = compaction_job.Install(*c->mutable_cf_options());
|
||||
io_s = compaction_job.io_status();
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
@@ -2846,11 +2808,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
c->column_family_data());
|
||||
}
|
||||
|
||||
if (status.ok() && !io_s.ok()) {
|
||||
status = io_s;
|
||||
}
|
||||
|
||||
if (c != nullptr) {
|
||||
c->ReleaseCompactionFiles(status);
|
||||
*made_progress = true;
|
||||
@@ -2876,11 +2833,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "Compaction error: %s",
|
||||
status.ToString().c_str());
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kCompaction);
|
||||
} else {
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction);
|
||||
}
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction);
|
||||
if (c != nullptr && !is_manual && !error_handler_.IsBGWorkStopped()) {
|
||||
// Put this cfd back in the compaction queue so we can retry after some
|
||||
// time
|
||||
|
||||
@@ -86,9 +86,9 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
|
||||
// Get obsolete files. This function will also update the list of
|
||||
// pending files in VersionSet().
|
||||
versions_->GetObsoleteFiles(
|
||||
&job_context->sst_delete_files, &job_context->blob_delete_files,
|
||||
&job_context->manifest_delete_files, job_context->min_pending_output);
|
||||
versions_->GetObsoleteFiles(&job_context->sst_delete_files,
|
||||
&job_context->manifest_delete_files,
|
||||
job_context->min_pending_output);
|
||||
|
||||
// Mark the elements in job_context->sst_delete_files as grabbedForPurge
|
||||
// so that other threads calling FindObsoleteFiles with full_scan=true
|
||||
@@ -665,18 +665,16 @@ uint64_t PrecomputeMinLogNumberToKeep(
|
||||
return min_log_number_to_keep;
|
||||
}
|
||||
|
||||
Status DBImpl::FinishBestEffortsRecovery() {
|
||||
Status DBImpl::CleanupFilesAfterRecovery() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<std::string> paths;
|
||||
paths.push_back(NormalizePath(dbname_ + std::string(1, kFilePathSeparator)));
|
||||
paths.push_back(dbname_);
|
||||
for (const auto& db_path : immutable_db_options_.db_paths) {
|
||||
paths.push_back(
|
||||
NormalizePath(db_path.path + std::string(1, kFilePathSeparator)));
|
||||
paths.push_back(db_path.path);
|
||||
}
|
||||
for (const auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (const auto& cf_path : cfd->ioptions()->cf_paths) {
|
||||
paths.push_back(
|
||||
NormalizePath(cf_path.path + std::string(1, kFilePathSeparator)));
|
||||
paths.push_back(cf_path.path);
|
||||
}
|
||||
}
|
||||
// Dedup paths
|
||||
@@ -695,8 +693,7 @@ Status DBImpl::FinishBestEffortsRecovery() {
|
||||
if (!ParseFileName(fname, &number, &type)) {
|
||||
continue;
|
||||
}
|
||||
// path ends with '/' or '\\'
|
||||
const std::string normalized_fpath = path + fname;
|
||||
const std::string normalized_fpath = NormalizePath(path + fname);
|
||||
largest_file_number = std::max(largest_file_number, number);
|
||||
if (type == kTableFile && number >= next_file_number &&
|
||||
files_to_delete.find(normalized_fpath) == files_to_delete.end()) {
|
||||
@@ -707,22 +704,8 @@ Status DBImpl::FinishBestEffortsRecovery() {
|
||||
if (largest_file_number > next_file_number) {
|
||||
versions_->next_file_number_.store(largest_file_number + 1);
|
||||
}
|
||||
|
||||
VersionEdit edit;
|
||||
edit.SetNextFile(versions_->next_file_number_.load());
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
ColumnFamilyData* default_cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
assert(default_cfd);
|
||||
// Even if new_descriptor_log is false, we will still switch to a new
|
||||
// MANIFEST and update CURRENT file, since this is in recovery.
|
||||
Status s = versions_->LogAndApply(
|
||||
default_cfd, *default_cfd->GetLatestMutableCFOptions(), &edit, &mutex_,
|
||||
directories_.GetDbDir(), /*new_descriptor_log*/ false);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
mutex_.Unlock();
|
||||
Status s;
|
||||
for (const auto& fname : files_to_delete) {
|
||||
s = env_->DeleteFile(fname);
|
||||
if (!s.ok()) {
|
||||
|
||||
@@ -292,7 +292,7 @@ Status DBImpl::NewDB() {
|
||||
}
|
||||
if (s.ok()) {
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, directories_.GetDbDir());
|
||||
s = SetCurrentFile(env_, dbname_, 1, directories_.GetDbDir());
|
||||
} else {
|
||||
fs_->DeleteFile(manifest, IOOptions(), nullptr);
|
||||
}
|
||||
@@ -370,30 +370,7 @@ Status DBImpl::Recover(
|
||||
}
|
||||
|
||||
std::string current_fname = CurrentFileName(dbname_);
|
||||
// Path to any MANIFEST file in the db dir. It does not matter which one.
|
||||
// Since best-efforts recovery ignores CURRENT file, existence of a
|
||||
// MANIFEST indicates the recovery to recover existing db. If no MANIFEST
|
||||
// can be found, a new db will be created.
|
||||
std::string manifest_path;
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
s = env_->FileExists(current_fname);
|
||||
} else {
|
||||
s = Status::NotFound();
|
||||
std::vector<std::string> files;
|
||||
// No need to check return value
|
||||
env_->GetChildren(dbname_, &files);
|
||||
for (const std::string& file : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type = kLogFile; // initialize
|
||||
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||
// Found MANIFEST (descriptor log), thus best-efforts recovery does
|
||||
// not have to treat the db as empty.
|
||||
s = Status::OK();
|
||||
manifest_path = dbname_ + "/" + file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
s = env_->FileExists(current_fname);
|
||||
if (s.IsNotFound()) {
|
||||
if (immutable_db_options_.create_if_missing) {
|
||||
s = NewDB();
|
||||
@@ -421,14 +398,14 @@ Status DBImpl::Recover(
|
||||
FileOptions customized_fs(file_options_);
|
||||
customized_fs.use_direct_reads |=
|
||||
immutable_db_options_.use_direct_io_for_flush_and_compaction;
|
||||
const std::string& fname =
|
||||
manifest_path.empty() ? current_fname : manifest_path;
|
||||
s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr);
|
||||
s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile,
|
||||
nullptr);
|
||||
if (!s.ok()) {
|
||||
std::string error_str = s.ToString();
|
||||
// Check if unsupported Direct I/O is the root cause
|
||||
customized_fs.use_direct_reads = false;
|
||||
s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr);
|
||||
s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile,
|
||||
nullptr);
|
||||
if (s.ok()) {
|
||||
return Status::InvalidArgument(
|
||||
"Direct I/O is not supported by the specified DB.");
|
||||
@@ -448,10 +425,7 @@ Status DBImpl::Recover(
|
||||
s = versions_->TryRecover(column_families, read_only, &db_id_,
|
||||
&missing_table_file);
|
||||
if (s.ok()) {
|
||||
// TryRecover may delete previous column_family_set_.
|
||||
column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
s = FinishBestEffortsRecovery();
|
||||
s = CleanupFilesAfterRecovery();
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
@@ -1265,7 +1239,6 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
IOStatus io_s;
|
||||
s = BuildTable(
|
||||
dbname_, env_, fs_.get(), *cfd->ioptions(), mutable_cf_options,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
@@ -1274,8 +1247,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
mutable_cf_options.sample_for_compression,
|
||||
mutable_cf_options.compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery, &io_s,
|
||||
cfd->ioptions()->compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery,
|
||||
&event_logger_, job_id, Env::IO_HIGH, nullptr /* table_properties */,
|
||||
-1 /* level */, current_time, write_hint);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
@@ -87,8 +87,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->GetRangeDelAggregator(), read_seq);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
@@ -119,8 +118,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
sv->version_number, read_callback);
|
||||
auto* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->GetRangeDelAggregator(), read_seq);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
iterators->push_back(db_iter);
|
||||
}
|
||||
|
||||
@@ -417,8 +417,7 @@ ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot,
|
||||
/* allow_unprepared_value */ true);
|
||||
db_iter->GetRangeDelAggregator(), snapshot);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
+38
-68
@@ -101,7 +101,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
disable_memtable);
|
||||
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
if (write_options.low_pri) {
|
||||
status = ThrottleLowPriWritesIfNeeded(write_options, my_batch);
|
||||
if (!status.ok()) {
|
||||
@@ -323,22 +322,21 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
if (!two_write_queues_) {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
io_s = WriteToWAL(write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, last_sequence + 1);
|
||||
status = WriteToWAL(write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, last_sequence + 1);
|
||||
}
|
||||
} else {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
// LastAllocatedSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
io_s = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
seq_inc);
|
||||
status = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
seq_inc);
|
||||
} else {
|
||||
// Otherwise we inc seq number for memtable writes
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
}
|
||||
}
|
||||
status = io_s;
|
||||
assert(last_sequence != kMaxSequenceNumber);
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += seq_inc;
|
||||
@@ -413,11 +411,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -521,7 +515,6 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
IOStatus io_s;
|
||||
if (w.status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1);
|
||||
@@ -531,17 +524,12 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
wal_write_group.size - 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
|
||||
}
|
||||
io_s = WriteToWAL(wal_write_group, log_writer, log_used, need_log_sync,
|
||||
need_log_dir_sync, current_sequence);
|
||||
w.status = io_s;
|
||||
w.status = WriteToWAL(wal_write_group, log_writer, log_used,
|
||||
need_log_sync, need_log_dir_sync, current_sequence);
|
||||
}
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(w.status);
|
||||
}
|
||||
WriteStatusCheck(w.status);
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -752,10 +740,9 @@ Status DBImpl::WriteImplWALOnly(
|
||||
}
|
||||
seq_inc = total_batch_cnt;
|
||||
}
|
||||
IOStatus io_s;
|
||||
if (!write_options.disableWAL) {
|
||||
io_s = ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
status = io_s;
|
||||
status =
|
||||
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
} else {
|
||||
// Otherwise we inc seq number to do solely the seq allocation
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
@@ -790,11 +777,7 @@ Status DBImpl::WriteImplWALOnly(
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
if (!io_s.ok()) {
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
if (status.ok()) {
|
||||
size_t index = 0;
|
||||
@@ -840,17 +823,6 @@ void DBImpl::WriteStatusCheck(const Status& status) {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::IOStatusCheck(const IOStatus& io_status) {
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
if (immutable_db_options_.paranoid_checks && !io_status.ok() &&
|
||||
!io_status.IsBusy() && !io_status.IsIncomplete()) {
|
||||
mutex_.Lock();
|
||||
error_handler_.SetBGError(io_status, BackgroundErrorReason::kWriteCallback);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::MemTableInsertStatusCheck(const Status& status) {
|
||||
// A non-OK status here indicates that the state implied by the
|
||||
// WAL has diverged from the in-memory state. This could be
|
||||
@@ -989,9 +961,9 @@ WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
|
||||
// When two_write_queues_ is disabled, this function is called from the only
|
||||
// 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) {
|
||||
Status DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size) {
|
||||
assert(log_size != nullptr);
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
*log_size = log_entry.size();
|
||||
@@ -1006,8 +978,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
IOStatus io_s = log_writer->AddRecord(log_entry);
|
||||
|
||||
Status status = log_writer->AddRecord(log_entry);
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -1019,14 +990,15 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
// since alive_log_files_ might be modified concurrently
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
log_empty_ = false;
|
||||
return io_s;
|
||||
return status;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence) {
|
||||
IOStatus io_s;
|
||||
Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence) {
|
||||
Status status;
|
||||
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
size_t write_with_wal = 0;
|
||||
@@ -1044,13 +1016,13 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
uint64_t log_size;
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
status = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
}
|
||||
|
||||
if (io_s.ok() && need_log_sync) {
|
||||
if (status.ok() && need_log_sync) {
|
||||
StopWatch sw(env_, stats_, WAL_FILE_SYNC_MICROS);
|
||||
// It's safe to access logs_ with unlocked mutex_ here because:
|
||||
// - we've set getting_synced=true for all logs,
|
||||
@@ -1060,24 +1032,23 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
// - as long as other threads don't modify it, it's safe to read
|
||||
// from std::deque from multiple threads concurrently.
|
||||
for (auto& log : logs_) {
|
||||
io_s = log.writer->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
status = log.writer->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (io_s.ok() && need_log_dir_sync) {
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
// We only sync WAL directory the first time WAL syncing is
|
||||
// requested, so that in case users never turn on WAL sync,
|
||||
// we can avoid the disk I/O in the write code path.
|
||||
io_s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
status = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (merged_batch == &tmp_batch_) {
|
||||
tmp_batch_.Clear();
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
if (status.ok()) {
|
||||
auto stats = default_cf_internal_stats_;
|
||||
if (need_log_sync) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileSynced, 1);
|
||||
@@ -1088,13 +1059,14 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return io_s;
|
||||
return status;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
const WriteThread::WriteGroup& write_group, uint64_t* log_used,
|
||||
SequenceNumber* last_sequence, size_t seq_inc) {
|
||||
IOStatus io_s;
|
||||
Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used,
|
||||
SequenceNumber* last_sequence,
|
||||
size_t seq_inc) {
|
||||
Status status;
|
||||
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
@@ -1120,14 +1092,14 @@ 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);
|
||||
status = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
|
||||
if (io_s.ok()) {
|
||||
if (status.ok()) {
|
||||
const bool concurrent = true;
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileBytes, log_size,
|
||||
@@ -1137,7 +1109,7 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
concurrent);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return io_s;
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteRecoverableState() {
|
||||
@@ -1820,8 +1792,6 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice* ts = opt.timestamp;
|
||||
assert(nullptr != ts);
|
||||
size_t ts_sz = ts->size();
|
||||
assert(column_family->GetComparator());
|
||||
assert(ts_sz == column_family->GetComparator()->timestamp_size());
|
||||
WriteBatch batch(key.size() + ts_sz + value.size() + 24, /*max_bytes=*/0,
|
||||
ts_sz);
|
||||
Status s = batch.Put(column_family, key, value);
|
||||
|
||||
@@ -45,8 +45,6 @@ class DBSecondaryTest : public DBTestBase {
|
||||
|
||||
void OpenSecondary(const Options& options);
|
||||
|
||||
Status TryOpenSecondary(const Options& options);
|
||||
|
||||
void OpenSecondaryWithColumnFamilies(
|
||||
const std::vector<std::string>& column_families, const Options& options);
|
||||
|
||||
@@ -72,13 +70,9 @@ class DBSecondaryTest : public DBTestBase {
|
||||
};
|
||||
|
||||
void DBSecondaryTest::OpenSecondary(const Options& options) {
|
||||
ASSERT_OK(TryOpenSecondary(options));
|
||||
}
|
||||
|
||||
Status DBSecondaryTest::TryOpenSecondary(const Options& options) {
|
||||
Status s =
|
||||
DB::OpenAsSecondary(options, dbname_, secondary_path_, &db_secondary_);
|
||||
return s;
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
void DBSecondaryTest::OpenSecondaryWithColumnFamilies(
|
||||
@@ -864,56 +858,6 @@ TEST_F(DBSecondaryTest, CheckConsistencyWhenOpen) {
|
||||
thread.join();
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, StartFromInconsistent) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options1;
|
||||
Status s = TryOpenSecondary(options1);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, InconsistencyDuringCatchUp) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Options options1;
|
||||
OpenSecondary(options1);
|
||||
|
||||
{
|
||||
std::string value;
|
||||
ASSERT_OK(db_secondary_->Get(ReadOptions(), "foo", &value));
|
||||
ASSERT_EQ("value", value);
|
||||
}
|
||||
|
||||
ASSERT_OK(Put("bar", "value1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = db_secondary_->TryCatchUpWithPrimary();
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+26
-63
@@ -73,7 +73,6 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
cfd_(cfd),
|
||||
start_seqnum_(read_options.iter_start_seqnum),
|
||||
timestamp_ub_(read_options.timestamp),
|
||||
timestamp_lb_(read_options.iter_start_ts),
|
||||
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0) {
|
||||
RecordTick(statistics_, NO_ITERATOR_CREATED);
|
||||
if (pin_thru_lifetime_) {
|
||||
@@ -247,31 +246,23 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
|
||||
assert(ikey_.user_key.size() >= timestamp_size_);
|
||||
Slice ts;
|
||||
bool more_recent = false;
|
||||
if (timestamp_size_ > 0) {
|
||||
ts = ExtractTimestampFromUserKey(ikey_.user_key, timestamp_size_);
|
||||
}
|
||||
if (IsVisible(ikey_.sequence, ts, &more_recent)) {
|
||||
if (IsVisible(ikey_.sequence, ts)) {
|
||||
// If the previous entry is of seqnum 0, the current entry will not
|
||||
// possibly be skipped. This condition can potentially be relaxed to
|
||||
// prev_key.seq <= ikey_.sequence. We are cautious because it will be more
|
||||
// prone to bugs causing the same user key with the same sequence number.
|
||||
// Note that with current timestamp implementation, the same user key can
|
||||
// have different timestamps and zero sequence number on the bottommost
|
||||
// level. This will change in the future.
|
||||
if ((!is_prev_key_seqnum_zero || timestamp_size_ > 0) &&
|
||||
skipping_saved_key &&
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) <= 0) {
|
||||
if (!is_prev_key_seqnum_zero && skipping_saved_key &&
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, saved_key_.GetUserKey()) <= 0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
} else {
|
||||
assert(!skipping_saved_key ||
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) > 0);
|
||||
if (!iter_.PrepareValue()) {
|
||||
assert(!iter_.status().ok());
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, saved_key_.GetUserKey()) > 0);
|
||||
num_skipped = 0;
|
||||
reseek_done = false;
|
||||
switch (ikey_.type) {
|
||||
@@ -372,13 +363,11 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (more_recent) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
}
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
|
||||
// This key was inserted after our snapshot was taken or skipped by
|
||||
// timestamp range. If this happens too many times in a row for the same
|
||||
// user key, we want to seek to the target sequence number.
|
||||
// This key was inserted after our snapshot was taken.
|
||||
// If this happens too many times in a row for the same user key, we want
|
||||
// to seek to the target sequence number.
|
||||
int cmp = user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, saved_key_.GetUserKey());
|
||||
if (cmp == 0 || (skipping_saved_key && cmp < 0)) {
|
||||
@@ -457,7 +446,6 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// Scan from the newer entries to older entries.
|
||||
// PRE: iter_.key() points to the first merge type entry
|
||||
// saved_key_ stores the user key
|
||||
// iter_.PrepareValue() has been called
|
||||
// POST: saved_value_ has the merged value for the user key
|
||||
// iter_ points to the next entry (or invalid)
|
||||
bool DBIter::MergeValuesNewToOld() {
|
||||
@@ -487,21 +475,14 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
if (!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
// hit the next user key, stop right here
|
||||
break;
|
||||
}
|
||||
if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type ||
|
||||
} else if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelPositioningMode::kForwardTraversal)) {
|
||||
// hit a delete with the same user key, stop right here
|
||||
// iter_ is positioned after delete
|
||||
iter_.Next();
|
||||
break;
|
||||
}
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kTypeValue == ikey.type) {
|
||||
} else if (kTypeValue == ikey.type) {
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
const Slice val = iter_.value();
|
||||
@@ -773,11 +754,6 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
return FindValueForCurrentKeyUsingSeek();
|
||||
}
|
||||
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
last_key_entry_type = ikey.type;
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeValue:
|
||||
@@ -955,10 +931,6 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeBlobIndex) {
|
||||
assert(iter_.iter()->IsValuePinned());
|
||||
pinned_value_ = iter_.value();
|
||||
@@ -990,17 +962,12 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
if (!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelPositioningMode::kForwardTraversal)) {
|
||||
break;
|
||||
}
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeValue) {
|
||||
} else if (ikey.type == kTypeValue) {
|
||||
const Slice val = iter_.value();
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), &val,
|
||||
@@ -1134,24 +1101,20 @@ bool DBIter::TooManyInternalKeysSkipped(bool increment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DBIter::IsVisible(SequenceNumber sequence, const Slice& ts,
|
||||
bool* more_recent) {
|
||||
bool DBIter::IsVisible(SequenceNumber sequence, const Slice& ts) {
|
||||
// Remember that comparator orders preceding timestamp as larger.
|
||||
// TODO(yanqin): support timestamp in read_callback_.
|
||||
bool visible_by_seq = (read_callback_ == nullptr)
|
||||
? sequence <= sequence_
|
||||
: read_callback_->IsVisible(sequence);
|
||||
|
||||
bool visible_by_ts =
|
||||
(timestamp_ub_ == nullptr ||
|
||||
user_comparator_.CompareTimestamp(ts, *timestamp_ub_) <= 0) &&
|
||||
(timestamp_lb_ == nullptr ||
|
||||
user_comparator_.CompareTimestamp(ts, *timestamp_lb_) >= 0);
|
||||
|
||||
if (more_recent) {
|
||||
*more_recent = !visible_by_seq;
|
||||
int cmp_ts = timestamp_ub_ != nullptr
|
||||
? user_comparator_.CompareTimestamp(ts, *timestamp_ub_)
|
||||
: 0;
|
||||
if (cmp_ts > 0) {
|
||||
return false;
|
||||
}
|
||||
if (read_callback_ == nullptr) {
|
||||
return sequence <= sequence_;
|
||||
} else {
|
||||
// TODO(yanqin): support timestamp in read_callback_.
|
||||
return read_callback_->IsVisible(sequence);
|
||||
}
|
||||
return visible_by_seq && visible_by_ts;
|
||||
}
|
||||
|
||||
void DBIter::SetSavedKeyToSeekTarget(const Slice& target) {
|
||||
|
||||
+1
-12
@@ -231,8 +231,7 @@ class DBIter final : public Iterator {
|
||||
// entry can be found within the prefix.
|
||||
void PrevInternal(const Slice* prefix);
|
||||
bool TooManyInternalKeysSkipped(bool increment = true);
|
||||
bool IsVisible(SequenceNumber sequence, const Slice& ts,
|
||||
bool* more_recent = nullptr);
|
||||
bool IsVisible(SequenceNumber sequence, const Slice& ts);
|
||||
|
||||
// Temporarily pin the blocks that we encounter until ReleaseTempPinnedData()
|
||||
// is called
|
||||
@@ -271,15 +270,6 @@ class DBIter final : public Iterator {
|
||||
return expect_total_order_inner_iter_;
|
||||
}
|
||||
|
||||
// If lower bound of timestamp is given by ReadOptions.iter_start_ts, we need
|
||||
// to return versions of the same key. We cannot just skip if the key value
|
||||
// is the same but timestamps are different but fall in timestamp range.
|
||||
inline int CompareKeyForSkip(const Slice& a, const Slice& b) {
|
||||
return timestamp_lb_ != nullptr
|
||||
? user_comparator_.Compare(a, b)
|
||||
: user_comparator_.CompareWithoutTimestamp(a, b);
|
||||
}
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
Env* const env_;
|
||||
Logger* logger_;
|
||||
@@ -348,7 +338,6 @@ class DBIter final : public Iterator {
|
||||
// if this value > 0 iterator will return internal keys
|
||||
SequenceNumber start_seqnum_;
|
||||
const Slice* const timestamp_ub_;
|
||||
const Slice* const timestamp_lb_;
|
||||
const size_t timestamp_size_;
|
||||
};
|
||||
|
||||
|
||||
+10
-40
@@ -1167,62 +1167,32 @@ TEST_P(DBIteratorTest, IndexWithFirstKey) {
|
||||
ropt.tailing = tailing;
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ropt));
|
||||
|
||||
ropt.read_tier = ReadTier::kBlockCacheTier;
|
||||
std::unique_ptr<Iterator> nonblocking_iter(NewIterator(ropt));
|
||||
|
||||
iter->Seek("b10");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b2", iter->key().ToString());
|
||||
EXPECT_EQ("y2", iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
// The cache-only iterator should succeed too, using the blocks pulled into
|
||||
// the cache by the previous iterator.
|
||||
nonblocking_iter->Seek("b10");
|
||||
ASSERT_TRUE(nonblocking_iter->Valid());
|
||||
EXPECT_EQ("b2", nonblocking_iter->key().ToString());
|
||||
EXPECT_EQ("y2", nonblocking_iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
// ... but it shouldn't be able to step forward since the next block is
|
||||
// not in cache yet.
|
||||
nonblocking_iter->Next();
|
||||
ASSERT_FALSE(nonblocking_iter->Valid());
|
||||
ASSERT_TRUE(nonblocking_iter->status().IsIncomplete());
|
||||
|
||||
// ... nor should a seek to the next key succeed.
|
||||
nonblocking_iter->Seek("b20");
|
||||
ASSERT_FALSE(nonblocking_iter->Valid());
|
||||
ASSERT_TRUE(nonblocking_iter->status().IsIncomplete());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b3", iter->key().ToString());
|
||||
EXPECT_EQ("y3", iter->value().ToString());
|
||||
EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
// After the blocking iterator loaded the next block, the nonblocking
|
||||
// iterator's seek should succeed.
|
||||
nonblocking_iter->Seek("b20");
|
||||
ASSERT_TRUE(nonblocking_iter->Valid());
|
||||
EXPECT_EQ("b3", nonblocking_iter->key().ToString());
|
||||
EXPECT_EQ("y3", nonblocking_iter->value().ToString());
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
iter->Seek("c0");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("c0", iter->key().ToString());
|
||||
EXPECT_EQ("z1,z2", iter->value().ToString());
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(6, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("c3", iter->key().ToString());
|
||||
EXPECT_EQ("z3", iter->value().ToString());
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(7, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter.reset();
|
||||
|
||||
@@ -1237,13 +1207,13 @@ TEST_P(DBIteratorTest, IndexWithFirstKey) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b2", iter->key().ToString());
|
||||
EXPECT_EQ("y2", iter->value().ToString());
|
||||
EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(7, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(7, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-83
@@ -33,14 +33,13 @@ class DBOptionsTest : public DBTestBase {
|
||||
std::unordered_map<std::string, std::string> GetMutableDBOptionsMap(
|
||||
const DBOptions& options) {
|
||||
std::string options_str;
|
||||
ConfigOptions config_options;
|
||||
config_options.delimiter = "; ";
|
||||
GetStringFromDBOptions(config_options, options, &options_str);
|
||||
GetStringFromDBOptions(&options_str, options);
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : db_options_type_info) {
|
||||
if (opt.second.IsMutable() && opt.second.ShouldSerialize()) {
|
||||
if (opt.second.is_mutable &&
|
||||
opt.second.verification != OptionVerificationType::kDeprecated) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
}
|
||||
@@ -50,15 +49,13 @@ class DBOptionsTest : public DBTestBase {
|
||||
std::unordered_map<std::string, std::string> GetMutableCFOptionsMap(
|
||||
const ColumnFamilyOptions& options) {
|
||||
std::string options_str;
|
||||
ConfigOptions config_options;
|
||||
config_options.delimiter = "; ";
|
||||
|
||||
GetStringFromColumnFamilyOptions(config_options, options, &options_str);
|
||||
GetStringFromColumnFamilyOptions(&options_str, options);
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : cf_options_type_info) {
|
||||
if (opt.second.IsMutable() && opt.second.ShouldSerialize()) {
|
||||
if (opt.second.is_mutable &&
|
||||
opt.second.verification != OptionVerificationType::kDeprecated) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
}
|
||||
@@ -407,20 +404,6 @@ TEST_F(DBOptionsTest, SetBackgroundCompactionThreads) {
|
||||
ASSERT_EQ(3, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetBackgroundFlushThreads) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.max_background_flushes = 1;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGFlushesAllowed());
|
||||
ASSERT_EQ(1, env_->GetBackgroundThreads(Env::Priority::HIGH));
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"max_background_flushes", "3"}}));
|
||||
ASSERT_EQ(3, env_->GetBackgroundThreads(Env::Priority::HIGH));
|
||||
ASSERT_EQ(3, dbfull()->TEST_BGFlushesAllowed());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBOptionsTest, SetBackgroundJobs) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -876,66 +859,6 @@ TEST_F(DBOptionsTest, FIFOTtlBackwardCompatible) {
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 191);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, ChangeCompression) {
|
||||
if (!Snappy_Supported() || !LZ4_Supported()) {
|
||||
return;
|
||||
}
|
||||
Options options;
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
options.compression = CompressionType::kLZ4Compression;
|
||||
options.bottommost_compression = CompressionType::kNoCompression;
|
||||
options.bottommost_compression_opts.level = 2;
|
||||
options.bottommost_compression_opts.parallel_threads = 1;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
CompressionType compression_used = CompressionType::kLZ4Compression;
|
||||
CompressionOptions compression_opt_used;
|
||||
bool compacted = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* c = reinterpret_cast<Compaction*>(arg);
|
||||
compression_used = c->output_compression();
|
||||
compression_opt_used = c->output_compression_opts();
|
||||
compacted = true;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(compacted);
|
||||
ASSERT_EQ(CompressionType::kNoCompression, compression_used);
|
||||
ASSERT_EQ(options.compression_opts.level, compression_opt_used.level);
|
||||
ASSERT_EQ(options.compression_opts.parallel_threads,
|
||||
compression_opt_used.parallel_threads);
|
||||
|
||||
compression_used = CompressionType::kLZ4Compression;
|
||||
compacted = false;
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"bottommost_compression", "kSnappyCompression"},
|
||||
{"bottommost_compression_opts", "0:6:0:0:4:true"}}));
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(compacted);
|
||||
ASSERT_EQ(CompressionType::kSnappyCompression, compression_used);
|
||||
ASSERT_EQ(6, compression_opt_used.level);
|
||||
// Right now parallel_level is not yet allowed to be changed.
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+19
-7
@@ -356,25 +356,37 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
// Turn timed wait into a simulated sleep
|
||||
uint64_t* abs_time_us = static_cast<uint64_t*>(arg);
|
||||
uint64_t cur_time = env_->NowMicros();
|
||||
if (*abs_time_us > cur_time) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us - cur_time);
|
||||
int64_t cur_time = 0;
|
||||
env_->GetCurrentTime(&cur_time);
|
||||
if (*abs_time_us > static_cast<uint64_t>(cur_time)) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us -
|
||||
static_cast<uint64_t>(cur_time));
|
||||
}
|
||||
|
||||
// Randomly sleep shortly
|
||||
env_->addon_time_.fetch_add(
|
||||
static_cast<uint64_t>(Random::GetTLSInstance()->Uniform(10)));
|
||||
|
||||
// Set wait until time to before (actual) current time to force not
|
||||
// to sleep
|
||||
*abs_time_us = Env::Default()->NowMicros();
|
||||
// Set wait until time to before current to force not to sleep.
|
||||
int64_t real_cur_time = 0;
|
||||
Env::Default()->GetCurrentTime(&real_cur_time);
|
||||
*abs_time_us = static_cast<uint64_t>(real_cur_time);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
env_->no_slowdown_ = true;
|
||||
env_->time_elapse_only_sleep_ = true;
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
options.disable_auto_compactions = true;
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, one of whose member variables is of type
|
||||
// InstrumentedCondVar. The callback for
|
||||
// InstrumentedCondVar::TimedWaitInternal can be triggered by stats dumping
|
||||
// and persisting threads and cause time_spent_deleting measurement to become
|
||||
// incorrect.
|
||||
options.stats_dump_period_sec = 0;
|
||||
options.stats_persist_period_sec = 0;
|
||||
options.env = env_;
|
||||
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
|
||||
@@ -45,8 +45,7 @@ void VerifyTableProperties(DB* db, uint64_t expected_entries_size) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class DBTablePropertiesTest : public DBTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
class DBTablePropertiesTest : public DBTestBase {
|
||||
public:
|
||||
DBTablePropertiesTest() : DBTestBase("/db_table_properties_test") {}
|
||||
TablePropertiesCollection TestGetPropertiesOfTablesInRange(
|
||||
@@ -252,20 +251,7 @@ TEST_F(DBTablePropertiesTest, GetColumnFamilyNameProperty) {
|
||||
}
|
||||
}
|
||||
|
||||
class DeletionTriggeredCompactionTestListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionBegin(DB* , const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.compaction_reason,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
|
||||
void OnCompactionCompleted(DB* , const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.compaction_reason,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
int kNumKeys = 1000;
|
||||
int kWindowSize = 100;
|
||||
int kNumDelsTrigger = 90;
|
||||
@@ -274,10 +260,6 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
|
||||
if(GetParam() == "kCompactionStyleUniversal") {
|
||||
opts.compaction_style = kCompactionStyleUniversal;
|
||||
}
|
||||
Reopen(opts);
|
||||
|
||||
// add an L1 file to prevent tombstones from dropping due to obsolescence
|
||||
@@ -286,11 +268,6 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
Flush();
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
DeletionTriggeredCompactionTestListener *listener =
|
||||
new DeletionTriggeredCompactionTestListener();
|
||||
opts.listeners.emplace_back(listener);
|
||||
Reopen(opts);
|
||||
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
if (i >= kNumKeys - kWindowSize &&
|
||||
i < kNumKeys - kWindowSize + kNumDelsTrigger) {
|
||||
@@ -303,6 +280,7 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
|
||||
// Change the window size and deletion trigger and ensure new values take
|
||||
// effect
|
||||
@@ -324,6 +302,7 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
|
||||
// Change the window size to disable delete triggered compaction
|
||||
kWindowSize = 0;
|
||||
@@ -343,15 +322,8 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTablePropertiesTest,
|
||||
DBTablePropertiesTest,
|
||||
::testing::Values(
|
||||
"kCompactionStyleLevel",
|
||||
"kCompactionStyleUniversal"
|
||||
));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
+4
-5
@@ -1421,7 +1421,7 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
keys[i * 3 + 1] = i * 5 + 1;
|
||||
keys[i * 3 + 2] = i * 5 + 2;
|
||||
}
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
for (int i = 0; i < N * 3; i++) {
|
||||
ASSERT_OK(Put(Key(keys[i] + 1000), RandomString(&rnd, 1024)));
|
||||
@@ -4530,7 +4530,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
Random rnd(301);
|
||||
Options options;
|
||||
@@ -4613,7 +4613,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
for (int i = 0; i < kNKeys; i++) {
|
||||
keys[i] = i;
|
||||
}
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
std::random_shuffle(std::begin(keys), std::end(keys));
|
||||
|
||||
Random rnd(301);
|
||||
Options options;
|
||||
@@ -5503,7 +5503,7 @@ TEST_F(DBTest, EmptyCompactedDB) {
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, DISABLED_SuggestCompactRangeTest) {
|
||||
TEST_F(DBTest, SuggestCompactRangeTest) {
|
||||
class CompactionFilterFactoryGetContext : public CompactionFilterFactory {
|
||||
public:
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
@@ -5611,7 +5611,6 @@ TEST_F(DBTest, DISABLED_SuggestCompactRangeTest) {
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBTest, PromoteL0) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
+11
-139
@@ -1288,10 +1288,6 @@ TEST_F(DBTest2, CompressionOptions) {
|
||||
const int kValSize = 20;
|
||||
Random rnd(301);
|
||||
|
||||
std::vector<uint32_t> compression_parallel_threads = {1, 4};
|
||||
|
||||
std::map<std::string, std::string> key_value_written;
|
||||
|
||||
for (int iter = 0; iter <= 2; iter++) {
|
||||
listener->max_level_checked = 0;
|
||||
|
||||
@@ -1316,37 +1312,19 @@ TEST_F(DBTest2, CompressionOptions) {
|
||||
options.bottommost_compression = kDisableCompressionOption;
|
||||
}
|
||||
|
||||
for (auto num_threads : compression_parallel_threads) {
|
||||
options.compression_opts.parallel_threads = num_threads;
|
||||
options.bottommost_compression_opts.parallel_threads = num_threads;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Write 10 random files
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
std::string key = RandomString(&rnd, kKeySize);
|
||||
std::string value = RandomString(&rnd, kValSize);
|
||||
key_value_written[key] = value;
|
||||
ASSERT_OK(Put(key, value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
DestroyAndReopen(options);
|
||||
// Write 10 random files
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
ASSERT_OK(
|
||||
Put(RandomString(&rnd, kKeySize), RandomString(&rnd, kValSize)));
|
||||
}
|
||||
|
||||
// Make sure that we wrote enough to check all 7 levels
|
||||
ASSERT_EQ(listener->max_level_checked, 6);
|
||||
|
||||
// Make sure database content is the same as key_value_written
|
||||
std::unique_ptr<Iterator> db_iter(db_->NewIterator(ReadOptions()));
|
||||
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
|
||||
std::string key = db_iter->key().ToString();
|
||||
std::string value = db_iter->value().ToString();
|
||||
ASSERT_NE(key_value_written.find(key), key_value_written.end());
|
||||
ASSERT_EQ(key_value_written[key], value);
|
||||
key_value_written.erase(key);
|
||||
}
|
||||
ASSERT_EQ(0, key_value_written.size());
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
// Make sure that we wrote enough to check all 7 levels
|
||||
ASSERT_EQ(listener->max_level_checked, 6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2688,94 +2666,6 @@ TEST_F(DBTest2, OptimizeForSmallDB) {
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, IterRaceFlush1) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::NewIterator:1", "DBTest2::IterRaceFlush:1"},
|
||||
{"DBTest2::IterRaceFlush:2", "DBImpl::NewIterator:2"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread t1([&] {
|
||||
TEST_SYNC_POINT("DBTest2::IterRaceFlush:1");
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
Flush();
|
||||
TEST_SYNC_POINT("DBTest2::IterRaceFlush:2");
|
||||
});
|
||||
|
||||
// iterator is created after the first Put(), so it should see either
|
||||
// "v1" or "v2".
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
it->Seek("foo");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("foo", it->key().ToString());
|
||||
}
|
||||
|
||||
t1.join();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, IterRaceFlush2) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::NewIterator:3", "DBTest2::IterRaceFlush2:1"},
|
||||
{"DBTest2::IterRaceFlush2:2", "DBImpl::NewIterator:4"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread t1([&] {
|
||||
TEST_SYNC_POINT("DBTest2::IterRaceFlush2:1");
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
Flush();
|
||||
TEST_SYNC_POINT("DBTest2::IterRaceFlush2:2");
|
||||
});
|
||||
|
||||
// iterator is created after the first Put(), so it should see either
|
||||
// "v1" or "v2".
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
it->Seek("foo");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("foo", it->key().ToString());
|
||||
}
|
||||
|
||||
t1.join();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, IterRefreshRaceFlush) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ArenaWrappedDBIter::Refresh:1", "DBTest2::IterRefreshRaceFlush:1"},
|
||||
{"DBTest2::IterRefreshRaceFlush:2", "ArenaWrappedDBIter::Refresh:2"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread t1([&] {
|
||||
TEST_SYNC_POINT("DBTest2::IterRefreshRaceFlush:1");
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
Flush();
|
||||
TEST_SYNC_POINT("DBTest2::IterRefreshRaceFlush:2");
|
||||
});
|
||||
|
||||
// iterator is created after the first Put(), so it should see either
|
||||
// "v1" or "v2".
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
it->Refresh();
|
||||
it->Seek("foo");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("foo", it->key().ToString());
|
||||
}
|
||||
|
||||
t1.join();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, GetRaceFlush1) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
@@ -4416,24 +4306,6 @@ TEST_F(DBTest2, SameSmallestInSameLevel) {
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", Get("key"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, FileConsistencyCheckInOpen) {
|
||||
Put("foo", "bar");
|
||||
Flush();
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
Status* ret_s = static_cast<Status*>(arg);
|
||||
*ret_s = Status::Corruption("fcc");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
ASSERT_NOK(TryReopen(options));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, BlockBasedTablePrefixIndexSeekForPrev) {
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
+14
-10
@@ -14,19 +14,10 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
int64_t MaybeCurrentTime(Env* env) {
|
||||
int64_t time = 1337346000; // arbitrary fallback default
|
||||
(void)env->GetCurrentTime(&time);
|
||||
return time;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Special Env used to delay background operations
|
||||
|
||||
SpecialEnv::SpecialEnv(Env* base)
|
||||
: EnvWrapper(base),
|
||||
maybe_starting_time_(MaybeCurrentTime(base)),
|
||||
rnd_(301),
|
||||
sleep_counter_(this),
|
||||
addon_time_(0),
|
||||
@@ -408,7 +399,20 @@ Options DBTestBase::GetOptions(
|
||||
options.use_direct_reads = true;
|
||||
options.use_direct_io_for_flush_and_compaction = true;
|
||||
options.compaction_readahead_size = 2 * 1024 * 1024;
|
||||
test::SetupSyncPointsToMockDirectIO();
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && \
|
||||
!defined(OS_AIX) && !defined(OS_OPENBSD)
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile:O_DIRECT", [&](void* arg) {
|
||||
int* val = static_cast<int*>(arg);
|
||||
*val &= ~O_DIRECT;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewRandomAccessFile:O_DIRECT", [&](void* arg) {
|
||||
int* val = static_cast<int*>(arg);
|
||||
*val &= ~O_DIRECT;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
+1
-85
@@ -502,14 +502,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* unix_time) override {
|
||||
Status s;
|
||||
if (time_elapse_only_sleep_) {
|
||||
*unix_time = maybe_starting_time_;
|
||||
} else {
|
||||
if (!time_elapse_only_sleep_) {
|
||||
s = target()->GetCurrentTime(unix_time);
|
||||
}
|
||||
if (s.ok()) {
|
||||
// FIXME: addon_time_ sometimes used to mean seconds (here) and
|
||||
// sometimes microseconds
|
||||
*unix_time += addon_time_.load();
|
||||
}
|
||||
return s;
|
||||
@@ -535,20 +531,6 @@ class SpecialEnv : public EnvWrapper {
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
void SetTimeElapseOnlySleep(Options* options) {
|
||||
time_elapse_only_sleep_ = true;
|
||||
no_slowdown_ = true;
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, which uses InstrumentedCondVar::TimedWaitInternal.
|
||||
// With time_elapse_only_sleep_, this can hang on some platforms.
|
||||
// TODO: why? investigate/fix
|
||||
options->stats_dump_period_sec = 0;
|
||||
options->stats_persist_period_sec = 0;
|
||||
}
|
||||
|
||||
// Something to return when mocking current time
|
||||
const int64_t maybe_starting_time_;
|
||||
|
||||
Random rnd_;
|
||||
port::Mutex rnd_mutex_; // Lock to pretect rnd_
|
||||
|
||||
@@ -665,72 +647,6 @@ class TestPutOperator : public MergeOperator {
|
||||
virtual const char* Name() const override { return "TestPutOperator"; }
|
||||
};
|
||||
|
||||
// A wrapper around Cache that can easily be extended with instrumentation,
|
||||
// etc.
|
||||
class CacheWrapper : public Cache {
|
||||
public:
|
||||
explicit CacheWrapper(std::shared_ptr<Cache> target)
|
||||
: target_(std::move(target)) {}
|
||||
|
||||
const char* Name() const override { return target_->Name(); }
|
||||
|
||||
Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
return target_->Insert(key, value, charge, deleter, handle, priority);
|
||||
}
|
||||
|
||||
Handle* Lookup(const Slice& key, Statistics* stats = nullptr) override {
|
||||
return target_->Lookup(key, stats);
|
||||
}
|
||||
|
||||
bool Ref(Handle* handle) override { return target_->Ref(handle); }
|
||||
|
||||
bool Release(Handle* handle, bool force_erase = false) override {
|
||||
return target_->Release(handle, force_erase);
|
||||
}
|
||||
|
||||
void* Value(Handle* handle) override { return target_->Value(handle); }
|
||||
|
||||
void Erase(const Slice& key) override { target_->Erase(key); }
|
||||
uint64_t NewId() override { return target_->NewId(); }
|
||||
|
||||
void SetCapacity(size_t capacity) override { target_->SetCapacity(capacity); }
|
||||
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit) override {
|
||||
target_->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
bool HasStrictCapacityLimit() const override {
|
||||
return target_->HasStrictCapacityLimit();
|
||||
}
|
||||
|
||||
size_t GetCapacity() const override { return target_->GetCapacity(); }
|
||||
|
||||
size_t GetUsage() const override { return target_->GetUsage(); }
|
||||
|
||||
size_t GetUsage(Handle* handle) const override {
|
||||
return target_->GetUsage(handle);
|
||||
}
|
||||
|
||||
size_t GetPinnedUsage() const override { return target_->GetPinnedUsage(); }
|
||||
|
||||
size_t GetCharge(Handle* handle) const override {
|
||||
return target_->GetCharge(handle);
|
||||
}
|
||||
|
||||
void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override {
|
||||
target_->ApplyToAllCacheEntries(callback, thread_safe);
|
||||
}
|
||||
|
||||
void EraseUnRefEntries() override { target_->EraseUnRefEntries(); }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Cache> target_;
|
||||
};
|
||||
|
||||
class DBTestBase : public testing::Test {
|
||||
public:
|
||||
// Sequence of option configurations to try
|
||||
|
||||
@@ -1953,7 +1953,6 @@ TEST_F(DBTestUniversalCompaction2, BasicL0toL1) {
|
||||
ASSERT_GT(NumTableFilesAtLevel(6), 0);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_SINGLE_LEVEL_DTC)
|
||||
TEST_F(DBTestUniversalCompaction2, SingleLevel) {
|
||||
const int kNumKeys = 3000;
|
||||
const int kWindowSize = 100;
|
||||
@@ -1992,7 +1991,6 @@ TEST_F(DBTestUniversalCompaction2, SingleLevel) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
#endif // ENABLE_SINGLE_LEVEL_DTC
|
||||
|
||||
TEST_F(DBTestUniversalCompaction2, MultipleLevels) {
|
||||
const int kWindowSize = 100;
|
||||
|
||||
@@ -26,8 +26,22 @@ class DBBasicTestWithTimestampBase : public DBTestBase {
|
||||
|
||||
protected:
|
||||
static std::string Key1(uint64_t k) {
|
||||
uint32_t x = 1;
|
||||
const bool is_little_endian = (*reinterpret_cast<char*>(&x) != 0);
|
||||
std::string ret;
|
||||
PutFixed64(&ret, k);
|
||||
if (is_little_endian) {
|
||||
ret.assign(reinterpret_cast<char*>(&k), sizeof(k));
|
||||
} else {
|
||||
ret.resize(sizeof(k));
|
||||
ret[0] = k & 0xff;
|
||||
ret[1] = (k >> 8) & 0xff;
|
||||
ret[2] = (k >> 16) & 0xff;
|
||||
ret[3] = (k >> 24) & 0xff;
|
||||
ret[4] = (k >> 32) & 0xff;
|
||||
ret[5] = (k >> 40) & 0xff;
|
||||
ret[6] = (k >> 48) & 0xff;
|
||||
ret[7] = (k >> 56) & 0xff;
|
||||
}
|
||||
std::reverse(ret.begin(), ret.end());
|
||||
return ret;
|
||||
}
|
||||
@@ -159,6 +173,8 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
// TODO(yanqin) re-enable auto compaction
|
||||
options.disable_auto_compactions = true;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
@@ -219,56 +235,6 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterateLowerTsBound) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
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;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<std::string> write_timestamps = {Timestamp(1, 0),
|
||||
Timestamp(3, 0)};
|
||||
const std::vector<std::string> read_timestamps = {Timestamp(2, 0),
|
||||
Timestamp(4, 0)};
|
||||
const std::vector<std::string> read_timestamps_lb = {Timestamp(1, 0),
|
||||
Timestamp(1, 0)};
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
WriteOptions write_opts;
|
||||
Slice write_ts = write_timestamps[i];
|
||||
write_opts.timestamp = &write_ts;
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < read_timestamps.size(); ++i) {
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts = read_timestamps[i];
|
||||
Slice read_ts_lb = read_timestamps_lb[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
read_opts.iter_start_ts = &read_ts_lb;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
int count = 0;
|
||||
uint64_t key = 0;
|
||||
for (it->Seek(Key1(0)), key = 0; it->Valid(); it->Next(), ++count, ++key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), "value" + std::to_string(i),
|
||||
write_timestamps[i]);
|
||||
if (i > 0) {
|
||||
it->Next();
|
||||
CheckIterUserEntry(it.get(), Key1(key), "value" + std::to_string(i - 1),
|
||||
write_timestamps[i - 1]);
|
||||
}
|
||||
}
|
||||
size_t expected_count = kMaxKey + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, ForwardIterateStartSeqnum) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 0xffffffffffffffff;
|
||||
@@ -276,9 +242,7 @@ TEST_F(DBBasicTestWithTimestamp, ForwardIterateStartSeqnum) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
// Need to disable compaction to bottommost level when sequence number will be
|
||||
// zeroed out, causing the verification of sequence number to fail in this
|
||||
// test.
|
||||
// TODO(yanqin) re-enable auto compaction
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
@@ -336,6 +300,8 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToTargetTimestamp) {
|
||||
constexpr size_t kNumKeys = 16;
|
||||
options.max_sequential_skip_in_iterations = kNumKeys / 2;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
// TODO(yanqin) re-enable auto compaction
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
@@ -371,6 +337,8 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToNextUserKey) {
|
||||
constexpr size_t kNumKeys = 16;
|
||||
options.max_sequential_skip_in_iterations = kNumKeys / 2;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
// TODO(yanqin) re-enable auto compaction
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
@@ -451,9 +419,8 @@ TEST_F(DBBasicTestWithTimestamp, MaxKeysSkipped) {
|
||||
|
||||
class DBBasicTestWithTimestampCompressionSettings
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<std::shared_ptr<const FilterPolicy>, CompressionType,
|
||||
uint32_t, uint32_t>> {
|
||||
public testing::WithParamInterface<std::tuple<
|
||||
std::shared_ptr<const FilterPolicy>, CompressionType, uint32_t>> {
|
||||
public:
|
||||
DBBasicTestWithTimestampCompressionSettings()
|
||||
: DBBasicTestWithTimestampBase(
|
||||
@@ -493,7 +460,6 @@ TEST_P(DBBasicTestWithTimestampCompressionSettings, PutAndGet) {
|
||||
if (comp_type == kZSTD) {
|
||||
options.compression_opts.zstd_max_train_bytes = std::get<2>(GetParam());
|
||||
}
|
||||
options.compression_opts.parallel_threads = std::get<3>(GetParam());
|
||||
options.target_file_size_base = 1 << 26; // 64MB
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -606,7 +572,6 @@ TEST_P(DBBasicTestWithTimestampCompressionSettings, PutAndGetWithCompaction) {
|
||||
if (comp_type == kZSTD) {
|
||||
options.compression_opts.zstd_max_train_bytes = std::get<2>(GetParam());
|
||||
}
|
||||
options.compression_opts.parallel_threads = std::get<3>(GetParam());
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
@@ -616,7 +581,7 @@ TEST_P(DBBasicTestWithTimestampCompressionSettings, PutAndGetWithCompaction) {
|
||||
std::vector<std::string> read_ts_list;
|
||||
|
||||
const auto& verify_records_func = [&](size_t i, size_t begin, size_t end,
|
||||
ColumnFamilyHandle* cfh) {
|
||||
ColumnFamilyHandle* cfh) {
|
||||
std::string value;
|
||||
std::string timestamp;
|
||||
|
||||
@@ -654,9 +619,9 @@ TEST_P(DBBasicTestWithTimestampCompressionSettings, PutAndGetWithCompaction) {
|
||||
// higherlevel[0].largest.userkey
|
||||
ASSERT_OK(Flush(cf));
|
||||
|
||||
// compact files (2 at each level) to a lower level such that all
|
||||
// keys with the same timestamp is at one level, with newer versions
|
||||
// at higher levels.
|
||||
// compact files (2 at each level) to a lower level such that all keys
|
||||
// with the same timestamp is at one level, with newer versions at
|
||||
// higher levels.
|
||||
CompactionOptions compact_opt;
|
||||
compact_opt.compression = kNoCompression;
|
||||
db_->CompactFiles(compact_opt, handles_[cf],
|
||||
@@ -774,55 +739,6 @@ TEST_F(DBBasicTestWithTimestamp, BatchWriteAndMultiGet) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetNoReturnTs) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, "foo", "value"));
|
||||
ASSERT_OK(db_->Put(write_opts, "bar", "value"));
|
||||
ASSERT_OK(db_->Put(write_opts, "fooxxxxxxxxxxxxxxxx", "value"));
|
||||
ASSERT_OK(db_->Put(write_opts, "barxxxxxxxxxxxxxxxx", "value"));
|
||||
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &ts;
|
||||
{
|
||||
ColumnFamilyHandle* column_families[] = {cfh, cfh};
|
||||
Slice keys[] = {"foo", "bar"};
|
||||
PinnableSlice values[] = {PinnableSlice(), PinnableSlice()};
|
||||
Status statuses[] = {Status::OK(), Status::OK()};
|
||||
dbfull()->MultiGet(read_opts, /*num_keys=*/2, &column_families[0], &keys[0],
|
||||
&values[0], &statuses[0], /*sorted_input=*/false);
|
||||
for (const auto& s : statuses) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
{
|
||||
ColumnFamilyHandle* column_families[] = {cfh, cfh, cfh, cfh};
|
||||
// Make user keys longer than configured timestamp size (16 bytes) to
|
||||
// verify RocksDB does not use the trailing bytes 'x' as timestamp.
|
||||
Slice keys[] = {"fooxxxxxxxxxxxxxxxx", "barxxxxxxxxxxxxxxxx", "foo", "bar"};
|
||||
PinnableSlice values[] = {PinnableSlice(), PinnableSlice(), PinnableSlice(),
|
||||
PinnableSlice()};
|
||||
Status statuses[] = {Status::OK(), Status::OK(), Status::OK(),
|
||||
Status::OK()};
|
||||
dbfull()->MultiGet(read_opts, /*num_keys=*/4, &column_families[0], &keys[0],
|
||||
&values[0], &statuses[0], /*sorted_input=*/false);
|
||||
for (const auto& s : statuses) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
@@ -833,7 +749,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
NewBloomFilterPolicy(10, false))),
|
||||
::testing::Values(kNoCompression, kZlibCompression, kLZ4Compression,
|
||||
kLZ4HCCompression, kZSTD),
|
||||
::testing::Values(0, 1 << 14), ::testing::Values(1, 4)));
|
||||
::testing::Values(0, 1 << 14)));
|
||||
|
||||
class DBBasicTestWithTimestampPrefixSeek
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
@@ -851,6 +767,8 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
// TODO(yanqin): re-enable auto compactions
|
||||
options.disable_auto_compactions = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
std::string Key1(uint64_t key) {
|
||||
std::string ret;
|
||||
PutFixed64(&ret, key);
|
||||
std::reverse(ret.begin(), ret.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Timestamp(uint64_t ts) {
|
||||
std::string ret;
|
||||
PutFixed64(&ret, ts);
|
||||
return ret;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
class TimestampCompatibleCompactionTest : public DBTestBase {
|
||||
public:
|
||||
TimestampCompatibleCompactionTest()
|
||||
: DBTestBase("/ts_compatible_compaction_test") {}
|
||||
|
||||
std::string Get(const std::string& key, uint64_t ts) {
|
||||
ReadOptions read_opts;
|
||||
std::string ts_str = Timestamp(ts);
|
||||
Slice ts_slice = ts_str;
|
||||
read_opts.timestamp = &ts_slice;
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, key, &value);
|
||||
if (s.IsNotFound()) {
|
||||
value.assign("NOT_FOUND");
|
||||
} else if (!s.ok()) {
|
||||
value.assign(s.ToString());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TimestampCompatibleCompactionTest, UserKeyCrossFileBoundary) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.comparator = test::ComparatorWithU64Ts();
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
constexpr size_t kNumKeysPerFile = 101;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
DestroyAndReopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
const auto* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_NE(nullptr, compaction);
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(1, compaction->num_input_levels());
|
||||
// Check that all 3 L0 ssts are picked for level compaction.
|
||||
ASSERT_EQ(3, compaction->num_input_files(0));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
// Write a L0 with keys 0, 1, ..., 99 with ts from 100 to 199.
|
||||
uint64_t ts = 100;
|
||||
uint64_t key = 0;
|
||||
WriteOptions write_opts;
|
||||
for (; key < kNumKeysPerFile - 1; ++key, ++ts) {
|
||||
std::string ts_str = Timestamp(ts);
|
||||
Slice ts_slice = ts_str;
|
||||
write_opts.timestamp = &ts_slice;
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(key), "foo_" + std::to_string(key)));
|
||||
}
|
||||
// Write another L0 with keys 99 with newer ts.
|
||||
ASSERT_OK(Flush());
|
||||
uint64_t saved_read_ts1 = ts++;
|
||||
key = 99;
|
||||
for (int i = 0; i < 4; ++i, ++ts) {
|
||||
std::string ts_str = Timestamp(ts);
|
||||
Slice ts_slice = ts_str;
|
||||
write_opts.timestamp = &ts_slice;
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(key), "bar_" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
uint64_t saved_read_ts2 = ts++;
|
||||
// Write another L0 with keys 99, 100, 101, ..., 150
|
||||
for (; key <= 150; ++key, ++ts) {
|
||||
std::string ts_str = Timestamp(ts);
|
||||
Slice ts_slice = ts_str;
|
||||
write_opts.timestamp = &ts_slice;
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(key), "foo1_" + std::to_string(key)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
// Wait for compaction to finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
uint64_t read_ts = ts;
|
||||
ASSERT_EQ("foo_99", Get(Key1(99), saved_read_ts1));
|
||||
ASSERT_EQ("bar_99", Get(Key1(99), saved_read_ts2));
|
||||
ASSERT_EQ("foo1_99", Get(Key1(99), read_ts));
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -238,46 +238,6 @@ Status ErrorHandler::SetBGError(const Status& bg_err, BackgroundErrorReason reas
|
||||
return bg_error_;
|
||||
}
|
||||
|
||||
Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
if (bg_io_err.ok()) {
|
||||
return Status::OK();
|
||||
}
|
||||
if (recovery_in_prog_ && recovery_error_.ok()) {
|
||||
recovery_error_ = bg_io_err;
|
||||
}
|
||||
Status new_bg_io_err = bg_io_err;
|
||||
Status s;
|
||||
if (bg_io_err.GetDataLoss()) {
|
||||
// FIrst, data loss is treated as unrecoverable error. So it can directly
|
||||
// overwrite any existing bg_error_.
|
||||
bool auto_recovery = false;
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kUnrecoverableError);
|
||||
bg_error_ = bg_err;
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason, &s,
|
||||
db_mutex_, &auto_recovery);
|
||||
return bg_error_;
|
||||
} else if (bg_io_err.GetRetryable()) {
|
||||
// Second, check if the error is a retryable IO error or not. if it is
|
||||
// retryable error and its severity is higher than bg_error_, overwrite
|
||||
// the bg_error_ with new error.
|
||||
// In current stage, treat retryable error as HardError. No automatic
|
||||
// recovery.
|
||||
bool auto_recovery = false;
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kHardError);
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason, &s,
|
||||
db_mutex_, &auto_recovery);
|
||||
if (bg_err.severity() > bg_error_.severity()) {
|
||||
bg_error_ = bg_err;
|
||||
}
|
||||
return bg_error_;
|
||||
} else {
|
||||
s = SetBGError(new_bg_io_err, reason);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status ErrorHandler::OverrideNoSpaceError(Status bg_error,
|
||||
bool* auto_recovery) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
#include "options/db_options.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
@@ -35,8 +34,6 @@ class ErrorHandler {
|
||||
|
||||
Status SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
|
||||
Status SetBGError(const IOStatus& bg_io_err, BackgroundErrorReason reason);
|
||||
|
||||
Status GetBGError() { return bg_error_; }
|
||||
|
||||
Status GetRecoveryError() { return recovery_error_; }
|
||||
|
||||
@@ -181,69 +181,6 @@ TEST_F(DBErrorHandlingFSTest, FLushWriteError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
Put(Key(1), "val1");
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
|
||||
Put(Key(2), "val2");
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeSyncTable",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
|
||||
Put(Key(3), "val3");
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeCloseTableFile",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, ManifestWriteError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
@@ -287,51 +224,6 @@ TEST_F(DBErrorHandlingFSTest, ManifestWriteError) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, ManifestWriteRetryableError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
Status s;
|
||||
std::string old_manifest;
|
||||
std::string new_manifest;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
old_manifest = GetManifestNameFromLiveFiles();
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
Put(Key(0), "val");
|
||||
Flush();
|
||||
Put(Key(1), "val");
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
|
||||
new_manifest = GetManifestNameFromLiveFiles();
|
||||
ASSERT_NE(new_manifest, old_manifest);
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
ASSERT_EQ("val", Get(Key(1)));
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, DoubleManifestWriteError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
@@ -455,80 +347,6 @@ TEST_F(DBErrorHandlingFSTest, CompactionManifestWriteError) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, CompactionManifestWriteRetryableError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.listeners.emplace_back(listener);
|
||||
Status s;
|
||||
std::string old_manifest;
|
||||
std::string new_manifest;
|
||||
std::atomic<bool> fail_manifest(false);
|
||||
DestroyAndReopen(options);
|
||||
old_manifest = GetManifestNameFromLiveFiles();
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
Put(Key(0), "val");
|
||||
Put(Key(2), "val");
|
||||
s = Flush();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
|
||||
listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError));
|
||||
listener->EnableAutoRecovery(false);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
// Wait for flush of 2nd L0 file before starting compaction
|
||||
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
|
||||
"BackgroundCallCompaction:0"},
|
||||
// Wait for compaction to detect manifest write error
|
||||
{"BackgroundCallCompaction:1", "CompactionManifestWriteError:0"},
|
||||
// Make compaction thread wait for error to be cleared
|
||||
{"CompactionManifestWriteError:1",
|
||||
"DBImpl::BackgroundCallCompaction:FoundObsoleteFiles"}});
|
||||
// trigger manifest write failure in compaction thread
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BackgroundCallCompaction:0", [&](void*) { fail_manifest.store(true); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest", [&](void*) {
|
||||
if (fail_manifest.load()) {
|
||||
fault_fs->SetFilesystemActive(false, error_msg);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put(Key(1), "val");
|
||||
s = Flush();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
|
||||
TEST_SYNC_POINT("CompactionManifestWriteError:0");
|
||||
TEST_SYNC_POINT("CompactionManifestWriteError:1");
|
||||
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
|
||||
new_manifest = GetManifestNameFromLiveFiles();
|
||||
ASSERT_NE(new_manifest, old_manifest);
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
ASSERT_EQ("val", Get(Key(1)));
|
||||
ASSERT_EQ("val", Get(Key(2)));
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, CompactionWriteError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
@@ -573,53 +391,6 @@ TEST_F(DBErrorHandlingFSTest, CompactionWriteError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, CompactionWriteRetryableError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.listeners.emplace_back(listener);
|
||||
Status s;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
Put(Key(0), "va;");
|
||||
Put(Key(2), "va;");
|
||||
s = Flush();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
|
||||
listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError));
|
||||
listener->EnableAutoRecovery(false);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BackgroundCallCompaction:0",
|
||||
[&](void*) { fault_fs->SetFilesystemActive(false, error_msg); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put(Key(1), "val");
|
||||
s = Flush();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, CorruptionError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
@@ -795,101 +566,6 @@ TEST_F(DBErrorHandlingFSTest, WALWriteError) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, WALWriteRetryableError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_fs_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.writable_file_max_buffer_size = 32768;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.paranoid_checks = true;
|
||||
Status s;
|
||||
Random rnd(301);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
// For the first batch, write is successful, require sync
|
||||
{
|
||||
WriteBatch batch;
|
||||
|
||||
for (auto i = 0; i < 100; ++i) {
|
||||
batch.Put(Key(i), RandomString(&rnd, 1024));
|
||||
}
|
||||
|
||||
WriteOptions wopts;
|
||||
wopts.sync = true;
|
||||
ASSERT_EQ(dbfull()->Write(wopts, &batch), Status::OK());
|
||||
};
|
||||
|
||||
// For the second batch, the first 2 file Append are successful, then the
|
||||
// following Append fails due to file system retryable IOError.
|
||||
{
|
||||
WriteBatch batch;
|
||||
int write_error = 0;
|
||||
|
||||
for (auto i = 100; i < 200; ++i) {
|
||||
batch.Put(Key(i), RandomString(&rnd, 1024));
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::Append:BeforePrepareWrite", [&](void*) {
|
||||
write_error++;
|
||||
if (write_error > 2) {
|
||||
fault_fs->SetFilesystemActive(false, error_msg);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
WriteOptions wopts;
|
||||
wopts.sync = true;
|
||||
s = dbfull()->Write(wopts, &batch);
|
||||
ASSERT_EQ(true, s.IsIOError());
|
||||
}
|
||||
fault_fs->SetFilesystemActive(true);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Data in corrupted WAL are not stored
|
||||
for (auto i = 0; i < 199; ++i) {
|
||||
if (i < 100) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
} else {
|
||||
ASSERT_EQ(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
}
|
||||
|
||||
// Resume and write a new batch, should be in the WAL
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
{
|
||||
WriteBatch batch;
|
||||
|
||||
for (auto i = 200; i < 300; ++i) {
|
||||
batch.Put(Key(i), RandomString(&rnd, 1024));
|
||||
}
|
||||
|
||||
WriteOptions wopts;
|
||||
wopts.sync = true;
|
||||
ASSERT_EQ(dbfull()->Write(wopts, &batch), Status::OK());
|
||||
};
|
||||
|
||||
Reopen(options);
|
||||
for (auto i = 0; i < 300; ++i) {
|
||||
if (i < 100 || i >= 200) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
} else {
|
||||
ASSERT_EQ(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, MultiCFWALWriteError) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "file/filename.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -2801,47 +2799,6 @@ TEST_P(ExternalSSTFileTest, IngestFilesTriggerFlushingWithTwoWriteQueue) {
|
||||
GenerateAndAddExternalFile(options, data);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, DeltaEncodingWhileGlobalSeqnoPresents) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
constexpr size_t kValueSize = 8;
|
||||
Random rnd(301);
|
||||
std::string value(RandomString(&rnd, kValueSize));
|
||||
|
||||
// Write some key to make global seqno larger than zero
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put("ab" + Key(i), value));
|
||||
}
|
||||
// Get a Snapshot to make RocksDB assign global seqno to ingested sst files.
|
||||
auto snap = dbfull()->GetSnapshot();
|
||||
|
||||
std::string fname = sst_files_dir_ + "test_file";
|
||||
rocksdb::SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.Open(fname));
|
||||
std::string key1 = "ab";
|
||||
std::string key2 = "ab";
|
||||
|
||||
// Make the prefix of key2 is same with key1 add zero seqno. The tail of every
|
||||
// key is composed as (seqno << 8 | value_type), and here `1` represents
|
||||
// ValueType::kTypeValue
|
||||
|
||||
PutFixed64(&key2, PackSequenceAndType(0, kTypeValue));
|
||||
key2 += "cdefghijkl";
|
||||
|
||||
ASSERT_OK(writer.Put(key1, value));
|
||||
ASSERT_OK(writer.Put(key2, value));
|
||||
|
||||
ExternalSstFileInfo info;
|
||||
ASSERT_OK(writer.Finish(&info));
|
||||
|
||||
ASSERT_OK(dbfull()->IngestExternalFile({info.file_path},
|
||||
IngestExternalFileOptions()));
|
||||
dbfull()->ReleaseSnapshot(snap);
|
||||
ASSERT_EQ(value, Get(key1));
|
||||
// You will get error here
|
||||
ASSERT_EQ(value, Get(key2));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
|
||||
+6
-18
@@ -238,14 +238,10 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
} else if (write_manifest_) {
|
||||
TEST_SYNC_POINT("FlushJob::InstallResults");
|
||||
// Replace immutable memtable with the generated Table
|
||||
IOStatus tmp_io_s;
|
||||
s = cfd_->imm()->TryInstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems_, prep_tracker, versions_, db_mutex_,
|
||||
meta_.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_, &committed_flush_jobs_info_, &tmp_io_s);
|
||||
if (!tmp_io_s.ok()) {
|
||||
io_status_ = tmp_io_s;
|
||||
}
|
||||
log_buffer_, &committed_flush_jobs_info_);
|
||||
}
|
||||
|
||||
if (s.ok() && file_meta != nullptr) {
|
||||
@@ -375,12 +371,6 @@ Status FlushJob::WriteLevel0Table() {
|
||||
meta_.oldest_ancester_time = std::min(current_time, oldest_key_time);
|
||||
meta_.file_creation_time = current_time;
|
||||
|
||||
uint64_t creation_time = (cfd_->ioptions()->compaction_style ==
|
||||
CompactionStyle::kCompactionStyleFIFO)
|
||||
? current_time
|
||||
: meta_.oldest_ancester_time;
|
||||
|
||||
IOStatus io_s;
|
||||
s = BuildTable(
|
||||
dbname_, db_options_.env, db_options_.fs.get(), *cfd_->ioptions(),
|
||||
mutable_cf_options_, file_options_, cfd_->table_cache(), iter.get(),
|
||||
@@ -389,14 +379,12 @@ Status FlushJob::WriteLevel0Table() {
|
||||
cfd_->GetName(), existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, snapshot_checker_,
|
||||
output_compression_, mutable_cf_options_.sample_for_compression,
|
||||
mutable_cf_options_.compression_opts,
|
||||
cfd_->ioptions()->compression_opts,
|
||||
mutable_cf_options_.paranoid_file_checks, cfd_->internal_stats(),
|
||||
TableFileCreationReason::kFlush, &io_s, event_logger_,
|
||||
job_context_->job_id, Env::IO_HIGH, &table_properties_, 0 /* level */,
|
||||
creation_time, oldest_key_time, write_hint, current_time);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
}
|
||||
TableFileCreationReason::kFlush, event_logger_, job_context_->job_id,
|
||||
Env::IO_HIGH, &table_properties_, 0 /* level */,
|
||||
meta_.oldest_ancester_time, oldest_key_time, write_hint,
|
||||
current_time);
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
|
||||
@@ -90,9 +90,6 @@ class FlushJob {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Return the IO status
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
private:
|
||||
void ReportStartedFlush();
|
||||
void ReportFlushInputSize(const autovector<MemTable*>& mems);
|
||||
@@ -157,7 +154,6 @@ class FlushJob {
|
||||
Version* base_;
|
||||
bool pick_memtable_called;
|
||||
Env::Priority thread_pri_;
|
||||
IOStatus io_status_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -108,7 +108,7 @@ class FlushJobTest : public testing::Test {
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
@@ -181,8 +181,8 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
// Note: the first two blob references will not be considered when resolving
|
||||
// the oldest blob file referenced (the first one is inlined TTL, while the
|
||||
// second one is TTL and thus points to a TTL blob file).
|
||||
constexpr std::array<uint64_t, 6> blob_file_numbers{{
|
||||
kInvalidBlobFileNumber, 5, 103, 17, 102, 101}};
|
||||
constexpr std::array<uint64_t, 6> blob_file_numbers{
|
||||
kInvalidBlobFileNumber, 5, 103, 17, 102, 101};
|
||||
for (size_t i = 0; i < blob_file_numbers.size(); ++i) {
|
||||
std::string key(ToString(i + 10001));
|
||||
std::string blob_index;
|
||||
|
||||
+8
-44
@@ -36,8 +36,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
ForwardLevelIterator(const ColumnFamilyData* const cfd,
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const SliceTransform* prefix_extractor,
|
||||
bool allow_unprepared_value)
|
||||
const SliceTransform* prefix_extractor)
|
||||
: cfd_(cfd),
|
||||
read_options_(read_options),
|
||||
files_(files),
|
||||
@@ -45,8 +44,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
file_index_(std::numeric_limits<uint32_t>::max()),
|
||||
file_iter_(nullptr),
|
||||
pinned_iters_mgr_(nullptr),
|
||||
prefix_extractor_(prefix_extractor),
|
||||
allow_unprepared_value_(allow_unprepared_value) {}
|
||||
prefix_extractor_(prefix_extractor) {}
|
||||
|
||||
~ForwardLevelIterator() override {
|
||||
// Reset current pointer
|
||||
@@ -85,8 +83,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
/*file_read_hist=*/nullptr, TableReaderCaller::kUserIterator,
|
||||
/*arena=*/nullptr, /*skip_filters=*/false, /*level=*/-1,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value_);
|
||||
/*largest_compaction_key=*/nullptr);
|
||||
file_iter_->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
valid_ = false;
|
||||
if (!range_del_agg.IsEmpty()) {
|
||||
@@ -174,16 +171,6 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
bool PrepareValue() override {
|
||||
assert(valid_);
|
||||
if (file_iter_->PrepareValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(!file_iter_->Valid());
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
bool IsKeyPinned() const override {
|
||||
return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
|
||||
file_iter_->IsKeyPinned();
|
||||
@@ -210,19 +197,16 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
InternalIterator* file_iter_;
|
||||
PinnedIteratorsManager* pinned_iters_mgr_;
|
||||
const SliceTransform* prefix_extractor_;
|
||||
const bool allow_unprepared_value_;
|
||||
};
|
||||
|
||||
ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* current_sv,
|
||||
bool allow_unprepared_value)
|
||||
SuperVersion* current_sv)
|
||||
: db_(db),
|
||||
read_options_(read_options),
|
||||
cfd_(cfd),
|
||||
prefix_extractor_(current_sv->mutable_cf_options.prefix_extractor.get()),
|
||||
user_comparator_(cfd->user_comparator()),
|
||||
allow_unprepared_value_(allow_unprepared_value),
|
||||
immutable_min_heap_(MinIterComparator(&cfd_->internal_comparator())),
|
||||
sv_(current_sv),
|
||||
mutable_iter_(nullptr),
|
||||
@@ -576,22 +560,6 @@ Status ForwardIterator::status() const {
|
||||
return immutable_status_;
|
||||
}
|
||||
|
||||
bool ForwardIterator::PrepareValue() {
|
||||
assert(valid_);
|
||||
if (current_->PrepareValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(!current_->Valid());
|
||||
assert(!current_->status().ok());
|
||||
assert(current_ != mutable_iter_); // memtable iterator can't fail
|
||||
assert(immutable_status_.ok());
|
||||
|
||||
valid_ = false;
|
||||
immutable_status_ = current_->status();
|
||||
return false;
|
||||
}
|
||||
|
||||
Status ForwardIterator::GetProperty(std::string prop_name, std::string* prop) {
|
||||
assert(prop != nullptr);
|
||||
if (prop_name == "rocksdb.iterator.super-version-number") {
|
||||
@@ -687,8 +655,7 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value_));
|
||||
/*largest_compaction_key=*/nullptr));
|
||||
}
|
||||
BuildLevelIterators(vstorage);
|
||||
current_ = nullptr;
|
||||
@@ -765,8 +732,7 @@ void ForwardIterator::RenewIterators() {
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value_));
|
||||
/*largest_compaction_key=*/nullptr));
|
||||
}
|
||||
|
||||
for (auto* f : l0_iters_) {
|
||||
@@ -809,8 +775,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.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -831,8 +796,7 @@ void ForwardIterator::ResetIncompleteIterators() {
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value_);
|
||||
/*largest_compaction_key=*/nullptr);
|
||||
l0_iters_[i]->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,7 @@ typedef std::priority_queue<InternalIterator*, std::vector<InternalIterator*>,
|
||||
class ForwardIterator : public InternalIterator {
|
||||
public:
|
||||
ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd, SuperVersion* current_sv = nullptr,
|
||||
bool allow_unprepared_value = false);
|
||||
ColumnFamilyData* cfd, SuperVersion* current_sv = nullptr);
|
||||
virtual ~ForwardIterator();
|
||||
|
||||
void SeekForPrev(const Slice& /*target*/) override {
|
||||
@@ -76,7 +75,6 @@ class ForwardIterator : public InternalIterator {
|
||||
virtual Slice key() const override;
|
||||
virtual Slice value() const override;
|
||||
virtual Status status() const override;
|
||||
virtual bool PrepareValue() override;
|
||||
virtual Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
virtual void SetPinnedItersMgr(
|
||||
PinnedIteratorsManager* pinned_iters_mgr) override;
|
||||
@@ -122,7 +120,6 @@ class ForwardIterator : public InternalIterator {
|
||||
ColumnFamilyData* const cfd_;
|
||||
const SliceTransform* const prefix_extractor_;
|
||||
const Comparator* user_comparator_;
|
||||
const bool allow_unprepared_value_;
|
||||
MinIterHeap immutable_min_heap_;
|
||||
|
||||
SuperVersion* sv_;
|
||||
|
||||
@@ -33,6 +33,7 @@ Status ImportColumnFamilyJob::Prepare(uint64_t next_file_number,
|
||||
files_to_import_.push_back(file_to_import);
|
||||
}
|
||||
|
||||
const auto ucmp = cfd_->internal_comparator().user_comparator();
|
||||
auto num_files = files_to_import_.size();
|
||||
if (num_files == 0) {
|
||||
return Status::InvalidArgument("The list of files is empty");
|
||||
@@ -54,18 +55,17 @@ Status ImportColumnFamilyJob::Prepare(uint64_t next_file_number,
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(
|
||||
sorted_files.begin(), sorted_files.end(),
|
||||
[this](const IngestedFileInfo* info1, const IngestedFileInfo* info2) {
|
||||
return cfd_->internal_comparator().Compare(
|
||||
info1->smallest_internal_key,
|
||||
info2->smallest_internal_key) < 0;
|
||||
});
|
||||
std::sort(sorted_files.begin(), sorted_files.end(),
|
||||
[&ucmp](const IngestedFileInfo* info1,
|
||||
const IngestedFileInfo* info2) {
|
||||
return sstableKeyCompare(ucmp, info1->smallest_internal_key,
|
||||
info2->smallest_internal_key) < 0;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i + 1 < sorted_files.size(); i++) {
|
||||
if (cfd_->internal_comparator().Compare(
|
||||
sorted_files[i]->largest_internal_key,
|
||||
sorted_files[i + 1]->smallest_internal_key) >= 0) {
|
||||
for (size_t i = 0; i < sorted_files.size() - 1; i++) {
|
||||
if (sstableKeyCompare(ucmp, sorted_files[i]->largest_internal_key,
|
||||
sorted_files[i + 1]->smallest_internal_key) >=
|
||||
0) {
|
||||
return Status::InvalidArgument("Files have overlapping ranges");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,68 +430,6 @@ TEST_F(ImportColumnFamilyTest, ImportExportedSSTFromAnotherDB) {
|
||||
test::DestroyDir(env_, dbname_ + "/db_copy");
|
||||
}
|
||||
|
||||
TEST_F(ImportColumnFamilyTest, LevelFilesOverlappingAtEndpoints) {
|
||||
// Imports a column family containing a level where two files overlap at their
|
||||
// endpoints. "Overlap" means the largest user key in one file is the same as
|
||||
// the smallest user key in the second file.
|
||||
const int kFileBytes = 128 << 10; // 128KB
|
||||
const int kValueBytes = 1 << 10; // 1KB
|
||||
const int kNumFiles = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 2;
|
||||
CreateAndReopenWithCF({"koko"}, options);
|
||||
|
||||
Random rnd(301);
|
||||
// Every key is snapshot protected to ensure older versions will not be
|
||||
// dropped during compaction.
|
||||
std::vector<const Snapshot*> snapshots;
|
||||
snapshots.reserve(kFileBytes / kValueBytes * kNumFiles);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kFileBytes / kValueBytes; ++j) {
|
||||
auto value = RandomString(&rnd, kValueBytes);
|
||||
ASSERT_OK(Put(1, "key", value));
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
}
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
|
||||
// Compact to create overlapping L1 files.
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1, 1), 1);
|
||||
|
||||
Checkpoint* checkpoint;
|
||||
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
|
||||
ASSERT_OK(checkpoint->ExportColumnFamily(handles_[1], export_files_dir_,
|
||||
&metadata_ptr_));
|
||||
ASSERT_NE(metadata_ptr_, nullptr);
|
||||
delete checkpoint;
|
||||
|
||||
// Create a new db and import the files.
|
||||
DB* db_copy;
|
||||
test::DestroyDir(env_, dbname_ + "/db_copy");
|
||||
ASSERT_OK(DB::Open(options, dbname_ + "/db_copy", &db_copy));
|
||||
ColumnFamilyHandle* cfh = nullptr;
|
||||
ASSERT_OK(db_copy->CreateColumnFamilyWithImport(ColumnFamilyOptions(), "yoyo",
|
||||
ImportColumnFamilyOptions(),
|
||||
*metadata_ptr_, &cfh));
|
||||
ASSERT_NE(cfh, nullptr);
|
||||
|
||||
{
|
||||
std::string value;
|
||||
ASSERT_OK(db_copy->Get(ReadOptions(), cfh, "key", &value));
|
||||
}
|
||||
db_copy->DropColumnFamily(cfh);
|
||||
db_copy->DestroyColumnFamilyHandle(cfh);
|
||||
delete db_copy;
|
||||
test::DestroyDir(env_, dbname_ + "/db_copy");
|
||||
for (const Snapshot* snapshot : snapshots) {
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ImportColumnFamilyTest, ImportColumnFamilyNegativeTest) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"koko"}, options);
|
||||
|
||||
+2
-6
@@ -102,9 +102,8 @@ struct SuperVersionContext {
|
||||
|
||||
struct JobContext {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return !(full_scan_candidate_files.empty() && sst_delete_files.empty() &&
|
||||
blob_delete_files.empty() && log_delete_files.empty() &&
|
||||
manifest_delete_files.empty());
|
||||
return full_scan_candidate_files.size() || sst_delete_files.size() ||
|
||||
log_delete_files.size() || manifest_delete_files.size();
|
||||
}
|
||||
|
||||
inline bool HaveSomethingToClean() const {
|
||||
@@ -146,9 +145,6 @@ struct JobContext {
|
||||
// a list of sst files that we need to delete
|
||||
std::vector<ObsoleteFileInfo> sst_delete_files;
|
||||
|
||||
// the list of blob files that we need to delete
|
||||
std::vector<ObsoleteBlobFileInfo> blob_delete_files;
|
||||
|
||||
// a list of log files that we need to delete
|
||||
std::vector<uint64_t> log_delete_files;
|
||||
|
||||
|
||||
+1
-1
@@ -11,11 +11,11 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include "file/sequence_file_reader.h"
|
||||
#include "port/lang.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace log {
|
||||
|
||||
@@ -49,6 +49,7 @@ class Reader {
|
||||
//
|
||||
// If "checksum" is true, verify checksums if available.
|
||||
Reader(std::shared_ptr<Logger> info_log,
|
||||
// @lint-ignore TXT2 T25377293 Grandfathered in
|
||||
std::unique_ptr<SequentialFileReader>&& file, Reporter* reporter,
|
||||
bool checksum, uint64_t log_num);
|
||||
// No copying allowed
|
||||
@@ -158,6 +159,7 @@ class Reader {
|
||||
class FragmentBufferedReader : public Reader {
|
||||
public:
|
||||
FragmentBufferedReader(std::shared_ptr<Logger> info_log,
|
||||
// @lint-ignore TXT2 T25377293 Grandfathered in
|
||||
std::unique_ptr<SequentialFileReader>&& _file,
|
||||
Reporter* reporter, bool checksum, uint64_t log_num)
|
||||
: Reader(info_log, std::move(_file), reporter, checksum, log_num),
|
||||
|
||||
+7
-9
@@ -37,10 +37,10 @@ Writer::~Writer() {
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus Writer::WriteBuffer() { return dest_->Flush(); }
|
||||
Status Writer::WriteBuffer() { return dest_->Flush(); }
|
||||
|
||||
IOStatus Writer::Close() {
|
||||
IOStatus s;
|
||||
Status Writer::Close() {
|
||||
Status s;
|
||||
if (dest_) {
|
||||
s = dest_->Close();
|
||||
dest_.reset();
|
||||
@@ -48,7 +48,7 @@ IOStatus Writer::Close() {
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus Writer::AddRecord(const Slice& slice) {
|
||||
Status Writer::AddRecord(const Slice& slice) {
|
||||
const char* ptr = slice.data();
|
||||
size_t left = slice.size();
|
||||
|
||||
@@ -59,7 +59,7 @@ IOStatus Writer::AddRecord(const Slice& slice) {
|
||||
// Fragment the record if necessary and emit it. Note that if slice
|
||||
// is empty, we still want to iterate once to emit a single
|
||||
// zero-length record
|
||||
IOStatus s;
|
||||
Status s;
|
||||
bool begin = true;
|
||||
do {
|
||||
const int64_t leftover = kBlockSize - block_offset_;
|
||||
@@ -114,7 +114,7 @@ IOStatus Writer::AddRecord(const Slice& slice) {
|
||||
|
||||
bool Writer::TEST_BufferIsEmpty() { return dest_->TEST_BufferIsEmpty(); }
|
||||
|
||||
IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
assert(n <= 0xffff); // Must fit in two bytes
|
||||
|
||||
size_t header_size;
|
||||
@@ -147,12 +147,10 @@ IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
// Compute the crc of the record type and the payload.
|
||||
crc = crc32c::Extend(crc, ptr, n);
|
||||
crc = crc32c::Mask(crc); // Adjust for storage
|
||||
TEST_SYNC_POINT_CALLBACK("LogWriter::EmitPhysicalRecord:BeforeEncodeChecksum",
|
||||
&crc);
|
||||
EncodeFixed32(buf, crc);
|
||||
|
||||
// Write the header and the payload
|
||||
IOStatus s = dest_->Append(Slice(buf, header_size));
|
||||
Status s = dest_->Append(Slice(buf, header_size));
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(ptr, n));
|
||||
}
|
||||
|
||||
+4
-5
@@ -13,7 +13,6 @@
|
||||
#include <memory>
|
||||
|
||||
#include "db/log_format.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
@@ -80,16 +79,16 @@ class Writer {
|
||||
|
||||
~Writer();
|
||||
|
||||
IOStatus AddRecord(const Slice& slice);
|
||||
Status AddRecord(const Slice& slice);
|
||||
|
||||
WritableFileWriter* file() { return dest_.get(); }
|
||||
const WritableFileWriter* file() const { return dest_.get(); }
|
||||
|
||||
uint64_t get_log_number() const { return log_number_; }
|
||||
|
||||
IOStatus WriteBuffer();
|
||||
Status WriteBuffer();
|
||||
|
||||
IOStatus Close();
|
||||
Status Close();
|
||||
|
||||
bool TEST_BufferIsEmpty();
|
||||
|
||||
@@ -104,7 +103,7 @@ class Writer {
|
||||
// record type stored in the header.
|
||||
uint32_t type_crc_[kMaxRecordType + 1];
|
||||
|
||||
IOStatus EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
|
||||
Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
|
||||
|
||||
// If true, it does not flush after each write. Instead it relies on the upper
|
||||
// layer to manually does the flush by calling ::WriteBuffer()
|
||||
|
||||
+1
-1
@@ -23,7 +23,6 @@
|
||||
#include "memory/memory_usage.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "port/lang.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -37,6 +36,7 @@
|
||||
#include "util/autovector.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
|
||||
+1
-4
@@ -390,8 +390,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
VersionSet* vset, InstrumentedMutex* mu, uint64_t file_number,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info,
|
||||
IOStatus* io_s) {
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
@@ -472,10 +471,8 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
}
|
||||
|
||||
// this can release and reacquire the mutex.
|
||||
vset->SetIOStatusOK();
|
||||
s = vset->LogAndApply(cfd, mutable_cf_options, edit_list, mu,
|
||||
db_directory);
|
||||
*io_s = vset->io_status();
|
||||
|
||||
// we will be changing the version in the next code path,
|
||||
// so we better create a new one, since versions are immutable
|
||||
|
||||
+1
-2
@@ -266,8 +266,7 @@ class MemTableList {
|
||||
VersionSet* vset, InstrumentedMutex* mu, uint64_t file_number,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info,
|
||||
IOStatus* io_s);
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info);
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
// Takes ownership of the referenced held on *m by the caller of Add().
|
||||
|
||||
@@ -114,14 +114,13 @@ class MemTableListTest : public testing::Test {
|
||||
auto cfd = column_family_set->GetDefault();
|
||||
EXPECT_TRUE(nullptr != cfd);
|
||||
uint64_t file_num = file_number.fetch_add(1);
|
||||
IOStatus io_s;
|
||||
// Create dummy mutex.
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
std::list<std::unique_ptr<FlushJobInfo>> flush_jobs_info;
|
||||
Status s = list->TryInstallMemtableFlushResults(
|
||||
cfd, mutable_cf_options, m, &dummy_prep_tracker, &versions, &mutex,
|
||||
file_num, to_delete, nullptr, &log_buffer, &flush_jobs_info, &io_s);
|
||||
file_num, to_delete, nullptr, &log_buffer, &flush_jobs_info);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
}
|
||||
|
||||
if (FLAGS_random_key) {
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 1U);
|
||||
@@ -524,7 +524,7 @@ TEST_F(PerfContextTest, SeekKeyComparison) {
|
||||
}
|
||||
|
||||
if (FLAGS_random_key) {
|
||||
RandomShuffle(std::begin(keys), std::end(keys));
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
}
|
||||
|
||||
HistogramImpl hist_put_time;
|
||||
|
||||
+2
-4
@@ -424,7 +424,6 @@ class Repairer {
|
||||
}
|
||||
|
||||
LegacyFileSystemWrapper fs(env_);
|
||||
IOStatus io_s;
|
||||
status = BuildTable(
|
||||
dbname_, env_, &fs, *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(), env_options_, table_cache_,
|
||||
@@ -433,7 +432,7 @@ class Repairer {
|
||||
cfd->GetID(), cfd->GetName(), {}, kMaxSequenceNumber,
|
||||
snapshot_checker, kNoCompression, 0 /* sample_for_compression */,
|
||||
CompressionOptions(), false, nullptr /* internal_stats */,
|
||||
TableFileCreationReason::kRecovery, &io_s, nullptr /* event_logger */,
|
||||
TableFileCreationReason::kRecovery, nullptr /* event_logger */,
|
||||
0 /* job_id */, Env::IO_HIGH, nullptr /* table_properties */,
|
||||
-1 /* level */, current_time, write_hint);
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
@@ -529,8 +528,7 @@ class Repairer {
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kRepair, /*arena=*/nullptr, /*skip_filters=*/false,
|
||||
/*level=*/-1, /*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
/*largest_compaction_key=*/nullptr);
|
||||
ParsedInternalKey parsed;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
Slice key = iter->key();
|
||||
|
||||
+5
-15
@@ -62,8 +62,6 @@ void AppendVarint64(IterKey* key, uint64_t v) {
|
||||
|
||||
} // namespace
|
||||
|
||||
const int kLoadConcurency = 128;
|
||||
|
||||
TableCache::TableCache(const ImmutableCFOptions& ioptions,
|
||||
const FileOptions& file_options, Cache* const cache,
|
||||
BlockCacheTracer* const block_cache_tracer)
|
||||
@@ -71,8 +69,7 @@ TableCache::TableCache(const ImmutableCFOptions& ioptions,
|
||||
file_options_(file_options),
|
||||
cache_(cache),
|
||||
immortal_tables_(false),
|
||||
block_cache_tracer_(block_cache_tracer),
|
||||
loader_mutex_(kLoadConcurency, GetSliceNPHash64) {
|
||||
block_cache_tracer_(block_cache_tracer) {
|
||||
if (ioptions_.row_cache) {
|
||||
// If the same cache is shared by multiple instances, we need to
|
||||
// disambiguate its entries.
|
||||
@@ -158,13 +155,6 @@ Status TableCache::FindTable(const FileOptions& file_options,
|
||||
if (no_io) { // Don't do IO and return a not-found status
|
||||
return Status::Incomplete("Table not found in table_cache, no_io is set");
|
||||
}
|
||||
MutexLock load_lock(loader_mutex_.get(key));
|
||||
// We check the cache again under loading mutex
|
||||
*handle = cache_->Lookup(key);
|
||||
if (*handle != nullptr) {
|
||||
return s;
|
||||
}
|
||||
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
s = GetTableReader(file_options, internal_comparator, fd,
|
||||
false /* sequential mode */, record_read_stats,
|
||||
@@ -194,7 +184,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
TableReader** table_reader_ptr, HistogramImpl* file_read_hist,
|
||||
TableReaderCaller caller, Arena* arena, bool skip_filters, int level,
|
||||
const InternalKey* smallest_compaction_key,
|
||||
const InternalKey* largest_compaction_key, bool allow_unprepared_value) {
|
||||
const InternalKey* largest_compaction_key) {
|
||||
PERF_TIMER_GUARD(new_table_iterator_nanos);
|
||||
|
||||
Status s;
|
||||
@@ -223,8 +213,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
} else {
|
||||
result = table_reader->NewIterator(options, prefix_extractor, arena,
|
||||
skip_filters, caller,
|
||||
file_options.compaction_readahead_size,
|
||||
allow_unprepared_value);
|
||||
file_options.compaction_readahead_size);
|
||||
}
|
||||
if (handle != nullptr) {
|
||||
result->RegisterCleanup(&UnrefEntry, cache_, handle);
|
||||
@@ -314,7 +303,8 @@ void TableCache::CreateRowCacheKeyPrefix(const ReadOptions& options,
|
||||
// Maybe we can include the whole file ifsnapshot == fd.largest_seqno.
|
||||
if (options.snapshot != nullptr &&
|
||||
(get_context->has_callback() ||
|
||||
static_cast_with_check<const SnapshotImpl>(options.snapshot)
|
||||
static_cast_with_check<const SnapshotImpl, const Snapshot>(
|
||||
options.snapshot)
|
||||
->GetSequenceNumber() <= fd.largest_seqno)) {
|
||||
// We should consider to use options.snapshot->GetSequenceNumber()
|
||||
// instead of GetInternalKeySeqno(k), which will make the code
|
||||
|
||||
+1
-2
@@ -73,7 +73,7 @@ class TableCache {
|
||||
const SliceTransform* prefix_extractor, TableReader** table_reader_ptr,
|
||||
HistogramImpl* file_read_hist, TableReaderCaller caller, Arena* arena,
|
||||
bool skip_filters, int level, const InternalKey* smallest_compaction_key,
|
||||
const InternalKey* largest_compaction_key, bool allow_unprepared_value);
|
||||
const InternalKey* largest_compaction_key);
|
||||
|
||||
// If a seek to internal key "k" in specified file finds an entry,
|
||||
// call get_context->SaveValue() repeatedly until
|
||||
@@ -221,7 +221,6 @@ class TableCache {
|
||||
std::string row_cache_id_;
|
||||
bool immortal_tables_;
|
||||
BlockCacheTracer* const block_cache_tracer_;
|
||||
Striped<port::Mutex, Slice> loader_mutex_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+15
-289
@@ -14,16 +14,13 @@
|
||||
#include <cinttypes>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_file_meta.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/table_cache.h"
|
||||
@@ -87,10 +84,9 @@ class VersionBuilder::Rep {
|
||||
};
|
||||
|
||||
const FileOptions& file_options_;
|
||||
const ImmutableCFOptions* const ioptions_;
|
||||
Logger* info_log_;
|
||||
TableCache* table_cache_;
|
||||
VersionStorageInfo* base_vstorage_;
|
||||
VersionSet* version_set_;
|
||||
int num_levels_;
|
||||
LevelState* levels_;
|
||||
// Store states of levels larger than num_levels_. We do this instead of
|
||||
@@ -104,22 +100,16 @@ class VersionBuilder::Rep {
|
||||
FileComparator level_zero_cmp_;
|
||||
FileComparator level_nonzero_cmp_;
|
||||
|
||||
// Metadata for all blob files affected by the series of version edits.
|
||||
std::map<uint64_t, std::shared_ptr<BlobFileMetaData>> changed_blob_files_;
|
||||
|
||||
public:
|
||||
Rep(const FileOptions& file_options, const ImmutableCFOptions* ioptions,
|
||||
TableCache* table_cache, VersionStorageInfo* base_vstorage,
|
||||
VersionSet* version_set)
|
||||
Rep(const FileOptions& file_options, Logger* info_log,
|
||||
TableCache* table_cache,
|
||||
VersionStorageInfo* base_vstorage)
|
||||
: file_options_(file_options),
|
||||
ioptions_(ioptions),
|
||||
info_log_(info_log),
|
||||
table_cache_(table_cache),
|
||||
base_vstorage_(base_vstorage),
|
||||
version_set_(version_set),
|
||||
num_levels_(base_vstorage->num_levels()),
|
||||
has_invalid_levels_(false) {
|
||||
assert(ioptions_);
|
||||
|
||||
levels_ = new LevelState[num_levels_];
|
||||
level_zero_cmp_.sort_method = FileComparator::kLevel0;
|
||||
level_nonzero_cmp_.sort_method = FileComparator::kLevelNon0;
|
||||
@@ -150,57 +140,6 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<BlobFileMetaData> GetBlobFileMetaData(
|
||||
uint64_t blob_file_number) const {
|
||||
auto changed_it = changed_blob_files_.find(blob_file_number);
|
||||
if (changed_it != changed_blob_files_.end()) {
|
||||
const auto& meta = changed_it->second;
|
||||
assert(meta);
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
assert(base_vstorage_);
|
||||
|
||||
const auto& base_blob_files = base_vstorage_->GetBlobFiles();
|
||||
|
||||
auto base_it = base_blob_files.find(blob_file_number);
|
||||
if (base_it != base_blob_files.end()) {
|
||||
const auto& meta = base_it->second;
|
||||
assert(meta);
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
return std::shared_ptr<BlobFileMetaData>();
|
||||
}
|
||||
|
||||
Status CheckConsistencyOfOldestBlobFileReference(
|
||||
const VersionStorageInfo* vstorage, uint64_t blob_file_number) const {
|
||||
assert(vstorage);
|
||||
|
||||
// TODO: remove this check once we actually start recoding metadata for
|
||||
// blob files in the MANIFEST.
|
||||
if (vstorage->GetBlobFiles().empty()) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
if (blob_file_number == kInvalidBlobFileNumber) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
const auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (!meta) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number
|
||||
<< " is not part of this version";
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CheckConsistency(VersionStorageInfo* vstorage) {
|
||||
#ifdef NDEBUG
|
||||
if (!vstorage->force_consistency_checks()) {
|
||||
@@ -209,31 +148,10 @@ class VersionBuilder::Rep {
|
||||
return Status::OK();
|
||||
}
|
||||
#endif
|
||||
// Make sure the files are sorted correctly and that the oldest blob file
|
||||
// reference for each table file points to a valid blob file in this
|
||||
// version.
|
||||
// make sure the files are sorted correctly
|
||||
for (int level = 0; level < num_levels_; level++) {
|
||||
auto& level_files = vstorage->LevelFiles(level);
|
||||
|
||||
if (level_files.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(level_files[0]);
|
||||
Status s = CheckConsistencyOfOldestBlobFileReference(
|
||||
vstorage, level_files[0]->oldest_blob_file_number);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < level_files.size(); i++) {
|
||||
assert(level_files[i]);
|
||||
s = CheckConsistencyOfOldestBlobFileReference(
|
||||
vstorage, level_files[i]->oldest_blob_file_number);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
auto f1 = level_files[i - 1];
|
||||
auto f2 = level_files[i];
|
||||
#ifndef NDEBUG
|
||||
@@ -299,27 +217,7 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that all blob files in the version have non-garbage data.
|
||||
const auto& blob_files = vstorage->GetBlobFiles();
|
||||
for (const auto& pair : blob_files) {
|
||||
const auto& blob_file_meta = pair.second;
|
||||
assert(blob_file_meta);
|
||||
|
||||
if (blob_file_meta->GetGarbageBlobCount() >=
|
||||
blob_file_meta->GetTotalBlobCount()) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_meta->GetBlobFileNumber()
|
||||
<< " consists entirely of garbage";
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
Status ret_s;
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistencyBeforeReturn",
|
||||
&ret_s);
|
||||
return ret_s;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CheckConsistencyForDeletes(VersionEdit* /*edit*/, uint64_t number,
|
||||
@@ -384,74 +282,6 @@ class VersionBuilder::Rep {
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ApplyBlobFileAddition(const BlobFileAddition& blob_file_addition) {
|
||||
const uint64_t blob_file_number = blob_file_addition.GetBlobFileNumber();
|
||||
|
||||
auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (meta) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number << " already added";
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
// Note: we use C++11 for now but in C++14, this could be done in a more
|
||||
// elegant way using generalized lambda capture.
|
||||
VersionSet* const vs = version_set_;
|
||||
const ImmutableCFOptions* const ioptions = ioptions_;
|
||||
|
||||
auto deleter = [vs, ioptions](SharedBlobFileMetaData* shared_meta) {
|
||||
if (vs) {
|
||||
assert(ioptions);
|
||||
assert(!ioptions->cf_paths.empty());
|
||||
assert(shared_meta);
|
||||
|
||||
vs->AddObsoleteBlobFile(shared_meta->GetBlobFileNumber(),
|
||||
ioptions->cf_paths.front().path);
|
||||
}
|
||||
|
||||
delete shared_meta;
|
||||
};
|
||||
|
||||
auto shared_meta = SharedBlobFileMetaData::Create(
|
||||
blob_file_number, blob_file_addition.GetTotalBlobCount(),
|
||||
blob_file_addition.GetTotalBlobBytes(),
|
||||
blob_file_addition.GetChecksumMethod(),
|
||||
blob_file_addition.GetChecksumValue(), deleter);
|
||||
|
||||
constexpr uint64_t garbage_blob_count = 0;
|
||||
constexpr uint64_t garbage_blob_bytes = 0;
|
||||
auto new_meta = BlobFileMetaData::Create(
|
||||
std::move(shared_meta), garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
changed_blob_files_.emplace(blob_file_number, std::move(new_meta));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ApplyBlobFileGarbage(const BlobFileGarbage& blob_file_garbage) {
|
||||
const uint64_t blob_file_number = blob_file_garbage.GetBlobFileNumber();
|
||||
|
||||
auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (!meta) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number << " not found";
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
assert(meta->GetBlobFileNumber() == blob_file_number);
|
||||
|
||||
auto new_meta = BlobFileMetaData::Create(
|
||||
meta->GetSharedMeta(),
|
||||
meta->GetGarbageBlobCount() + blob_file_garbage.GetGarbageBlobCount(),
|
||||
meta->GetGarbageBlobBytes() + blob_file_garbage.GetGarbageBlobBytes());
|
||||
|
||||
changed_blob_files_[blob_file_number] = std::move(new_meta);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Apply all of the edits in *edit to the current state.
|
||||
Status Apply(VersionEdit* edit) {
|
||||
Status s = CheckConsistency(base_vstorage_);
|
||||
@@ -466,10 +296,7 @@ class VersionBuilder::Rep {
|
||||
const auto number = del_file.second;
|
||||
if (level < num_levels_) {
|
||||
levels_[level].deleted_files.insert(number);
|
||||
s = CheckConsistencyForDeletes(edit, number, level);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
CheckConsistencyForDeletes(edit, number, level);
|
||||
|
||||
auto exising = levels_[level].added_files.find(number);
|
||||
if (exising != levels_[level].added_files.end()) {
|
||||
@@ -506,104 +333,9 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new blob files
|
||||
for (const auto& blob_file_addition : edit->GetBlobFileAdditions()) {
|
||||
s = ApplyBlobFileAddition(blob_file_addition);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Increase the amount of garbage for blob files affected by GC
|
||||
for (const auto& blob_file_garbage : edit->GetBlobFileGarbages()) {
|
||||
s = ApplyBlobFileGarbage(blob_file_garbage);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void AddBlobFileIfNeeded(
|
||||
VersionStorageInfo* vstorage,
|
||||
const std::shared_ptr<BlobFileMetaData>& meta) const {
|
||||
assert(vstorage);
|
||||
assert(meta);
|
||||
|
||||
if (meta->GetGarbageBlobCount() < meta->GetTotalBlobCount()) {
|
||||
vstorage->AddBlobFile(meta);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the blob file metadata from the base version with the changes (edits)
|
||||
// applied, and save the result into *vstorage.
|
||||
void SaveBlobFilesTo(VersionStorageInfo* vstorage) const {
|
||||
assert(base_vstorage_);
|
||||
assert(vstorage);
|
||||
|
||||
const auto& base_blob_files = base_vstorage_->GetBlobFiles();
|
||||
auto base_it = base_blob_files.begin();
|
||||
const auto base_it_end = base_blob_files.end();
|
||||
|
||||
auto changed_it = changed_blob_files_.begin();
|
||||
const auto changed_it_end = changed_blob_files_.end();
|
||||
|
||||
while (base_it != base_it_end && changed_it != changed_it_end) {
|
||||
const uint64_t base_blob_file_number = base_it->first;
|
||||
const uint64_t changed_blob_file_number = changed_it->first;
|
||||
|
||||
const auto& base_meta = base_it->second;
|
||||
const auto& changed_meta = changed_it->second;
|
||||
|
||||
assert(base_meta);
|
||||
assert(changed_meta);
|
||||
|
||||
if (base_blob_file_number < changed_blob_file_number) {
|
||||
assert(base_meta->GetGarbageBlobCount() <
|
||||
base_meta->GetTotalBlobCount());
|
||||
|
||||
vstorage->AddBlobFile(base_meta);
|
||||
|
||||
++base_it;
|
||||
} else if (changed_blob_file_number < base_blob_file_number) {
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
|
||||
++changed_it;
|
||||
} else {
|
||||
assert(base_blob_file_number == changed_blob_file_number);
|
||||
assert(base_meta->GetSharedMeta() == changed_meta->GetSharedMeta());
|
||||
assert(base_meta->GetGarbageBlobCount() <=
|
||||
changed_meta->GetGarbageBlobCount());
|
||||
assert(base_meta->GetGarbageBlobBytes() <=
|
||||
changed_meta->GetGarbageBlobBytes());
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
|
||||
++base_it;
|
||||
++changed_it;
|
||||
}
|
||||
}
|
||||
|
||||
while (base_it != base_it_end) {
|
||||
const auto& base_meta = base_it->second;
|
||||
assert(base_meta);
|
||||
assert(base_meta->GetGarbageBlobCount() < base_meta->GetTotalBlobCount());
|
||||
|
||||
vstorage->AddBlobFile(base_meta);
|
||||
++base_it;
|
||||
}
|
||||
|
||||
while (changed_it != changed_it_end) {
|
||||
const auto& changed_meta = changed_it->second;
|
||||
assert(changed_meta);
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
++changed_it;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the current state in *v.
|
||||
Status SaveTo(VersionStorageInfo* vstorage) {
|
||||
Status s = CheckConsistency(base_vstorage_);
|
||||
@@ -658,8 +390,6 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
|
||||
SaveBlobFilesTo(vstorage);
|
||||
|
||||
s = CheckConsistency(vstorage);
|
||||
return s;
|
||||
}
|
||||
@@ -766,19 +496,16 @@ class VersionBuilder::Rep {
|
||||
// f is to-be-deleted table file
|
||||
vstorage->RemoveCurrentStats(f);
|
||||
} else {
|
||||
assert(ioptions_);
|
||||
vstorage->AddFile(level, f, ioptions_->info_log);
|
||||
vstorage->AddFile(level, f, info_log_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VersionBuilder::VersionBuilder(const FileOptions& file_options,
|
||||
const ImmutableCFOptions* ioptions,
|
||||
TableCache* table_cache,
|
||||
VersionStorageInfo* base_vstorage,
|
||||
VersionSet* version_set)
|
||||
: rep_(new Rep(file_options, ioptions, table_cache, base_vstorage,
|
||||
version_set)) {}
|
||||
Logger* info_log)
|
||||
: rep_(new Rep(file_options, info_log, table_cache, base_vstorage)) {}
|
||||
|
||||
VersionBuilder::~VersionBuilder() = default;
|
||||
|
||||
@@ -804,9 +531,8 @@ Status VersionBuilder::LoadTableHandlers(
|
||||
BaseReferencedVersionBuilder::BaseReferencedVersionBuilder(
|
||||
ColumnFamilyData* cfd)
|
||||
: version_builder_(new VersionBuilder(
|
||||
cfd->current()->version_set()->file_options(), cfd->ioptions(),
|
||||
cfd->table_cache(), cfd->current()->storage_info(),
|
||||
cfd->current()->version_set())),
|
||||
cfd->current()->version_set()->file_options(), cfd->table_cache(),
|
||||
cfd->current()->storage_info(), cfd->ioptions()->info_log)),
|
||||
version_(cfd->current()) {
|
||||
version_->Ref();
|
||||
}
|
||||
@@ -814,8 +540,8 @@ BaseReferencedVersionBuilder::BaseReferencedVersionBuilder(
|
||||
BaseReferencedVersionBuilder::BaseReferencedVersionBuilder(
|
||||
ColumnFamilyData* cfd, Version* v)
|
||||
: version_builder_(new VersionBuilder(
|
||||
cfd->current()->version_set()->file_options(), cfd->ioptions(),
|
||||
cfd->table_cache(), v->storage_info(), v->version_set())),
|
||||
cfd->current()->version_set()->file_options(), cfd->table_cache(),
|
||||
v->storage_info(), cfd->ioptions()->info_log)),
|
||||
version_(v) {
|
||||
assert(version_ != cfd->current());
|
||||
}
|
||||
|
||||
@@ -16,14 +16,12 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
struct ImmutableCFOptions;
|
||||
class TableCache;
|
||||
class VersionStorageInfo;
|
||||
class VersionEdit;
|
||||
struct FileMetaData;
|
||||
class InternalStats;
|
||||
class Version;
|
||||
class VersionSet;
|
||||
class ColumnFamilyData;
|
||||
|
||||
// A helper class so we can efficiently apply a whole sequence
|
||||
@@ -31,9 +29,8 @@ class ColumnFamilyData;
|
||||
// Versions that contain full copies of the intermediate state.
|
||||
class VersionBuilder {
|
||||
public:
|
||||
VersionBuilder(const FileOptions& file_options,
|
||||
const ImmutableCFOptions* ioptions, TableCache* table_cache,
|
||||
VersionStorageInfo* base_vstorage, VersionSet* version_set);
|
||||
VersionBuilder(const FileOptions& file_options, TableCache* table_cache,
|
||||
VersionStorageInfo* base_vstorage, Logger* info_log = nullptr);
|
||||
~VersionBuilder();
|
||||
|
||||
bool CheckConsistencyForNumLevels();
|
||||
|
||||
+7
-557
@@ -3,8 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "db/version_edit.h"
|
||||
#include "db/version_set.h"
|
||||
@@ -59,13 +57,12 @@ class VersionBuilderTest : public testing::Test {
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
uint64_t num_entries = 0, uint64_t num_deletions = 0,
|
||||
bool sampled = false, SequenceNumber smallest_seqno = 0,
|
||||
SequenceNumber largest_seqno = 0,
|
||||
uint64_t oldest_blob_file_number = kInvalidBlobFileNumber) {
|
||||
SequenceNumber largest_seqno = 0) {
|
||||
assert(level < vstorage_.num_levels());
|
||||
FileMetaData* f = new FileMetaData(
|
||||
file_number, path_id, file_size, GetInternalKey(smallest, smallest_seq),
|
||||
GetInternalKey(largest, largest_seq), smallest_seqno, largest_seqno,
|
||||
/* marked_for_compact */ false, oldest_blob_file_number,
|
||||
/* marked_for_compact */ false, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
f->compensated_file_size = file_size;
|
||||
@@ -78,34 +75,6 @@ class VersionBuilderTest : public testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
void AddBlob(uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value, uint64_t garbage_blob_count,
|
||||
uint64_t garbage_blob_bytes) {
|
||||
auto shared_meta = SharedBlobFileMetaData::Create(
|
||||
blob_file_number, total_blob_count, total_blob_bytes,
|
||||
std::move(checksum_method), std::move(checksum_value));
|
||||
auto meta = BlobFileMetaData::Create(
|
||||
std::move(shared_meta), garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
vstorage_.AddBlobFile(std::move(meta));
|
||||
}
|
||||
|
||||
static std::shared_ptr<BlobFileMetaData> GetBlobFileMetaData(
|
||||
const VersionStorageInfo::BlobFiles& blob_files,
|
||||
uint64_t blob_file_number) {
|
||||
const auto it = blob_files.find(blob_file_number);
|
||||
|
||||
if (it == blob_files.end()) {
|
||||
return std::shared_ptr<BlobFileMetaData>();
|
||||
}
|
||||
|
||||
const auto& meta = it->second;
|
||||
assert(meta);
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
vstorage_.UpdateFilesByCompactionPri(ioptions_.compaction_pri);
|
||||
vstorage_.UpdateNumNonEmptyLevels();
|
||||
@@ -152,11 +121,8 @@ TEST_F(VersionBuilderTest, ApplyAndSaveTo) {
|
||||
version_edit.DeleteFile(3, 27U);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder version_builder(env_options, &ioptions_, table_cache,
|
||||
&vstorage_, version_set);
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr, false);
|
||||
@@ -193,11 +159,8 @@ TEST_F(VersionBuilderTest, ApplyAndSaveToDynamic) {
|
||||
version_edit.DeleteFile(0, 88U);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder version_builder(env_options, &ioptions_, table_cache,
|
||||
&vstorage_, version_set);
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr, false);
|
||||
@@ -239,11 +202,8 @@ TEST_F(VersionBuilderTest, ApplyAndSaveToDynamic2) {
|
||||
version_edit.DeleteFile(4, 8U);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder version_builder(env_options, &ioptions_, table_cache,
|
||||
&vstorage_, version_set);
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr, false);
|
||||
@@ -288,11 +248,8 @@ TEST_F(VersionBuilderTest, ApplyMultipleAndSaveTo) {
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder version_builder(env_options, &ioptions_, table_cache,
|
||||
&vstorage_, version_set);
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr, false);
|
||||
@@ -308,12 +265,7 @@ TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder version_builder(env_options, &ioptions_, table_cache,
|
||||
&vstorage_, version_set);
|
||||
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr, false);
|
||||
|
||||
@@ -367,508 +319,6 @@ TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyBlobFileAddition) {
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t blob_file_number = 1234;
|
||||
constexpr uint64_t total_blob_count = 5678;
|
||||
constexpr uint64_t total_blob_bytes = 999999;
|
||||
constexpr char checksum_method[] = "SHA1";
|
||||
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd";
|
||||
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
constexpr bool force_consistency_checks = false;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
|
||||
const auto& new_blob_files = new_vstorage.GetBlobFiles();
|
||||
ASSERT_EQ(new_blob_files.size(), 1);
|
||||
|
||||
const auto new_meta = GetBlobFileMetaData(new_blob_files, blob_file_number);
|
||||
|
||||
ASSERT_NE(new_meta, nullptr);
|
||||
ASSERT_EQ(new_meta->GetBlobFileNumber(), blob_file_number);
|
||||
ASSERT_EQ(new_meta->GetTotalBlobCount(), total_blob_count);
|
||||
ASSERT_EQ(new_meta->GetTotalBlobBytes(), total_blob_bytes);
|
||||
ASSERT_EQ(new_meta->GetChecksumMethod(), checksum_method);
|
||||
ASSERT_EQ(new_meta->GetChecksumValue(), checksum_value);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobCount(), 0);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobBytes(), 0);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyBlobFileAdditionAlreadyInBase) {
|
||||
// Attempt to add a blob file that is already present in the base version.
|
||||
|
||||
constexpr uint64_t blob_file_number = 1234;
|
||||
constexpr uint64_t total_blob_count = 5678;
|
||||
constexpr uint64_t total_blob_bytes = 999999;
|
||||
constexpr char checksum_method[] = "SHA1";
|
||||
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd";
|
||||
constexpr uint64_t garbage_blob_count = 123;
|
||||
constexpr uint64_t garbage_blob_bytes = 456789;
|
||||
|
||||
AddBlob(blob_file_number, total_blob_count, total_blob_bytes, checksum_method,
|
||||
checksum_value, garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "Blob file #1234 already added"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyBlobFileAdditionAlreadyApplied) {
|
||||
// Attempt to add the same blob file twice using version edits.
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t blob_file_number = 1234;
|
||||
constexpr uint64_t total_blob_count = 5678;
|
||||
constexpr uint64_t total_blob_bytes = 999999;
|
||||
constexpr char checksum_method[] = "SHA1";
|
||||
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd";
|
||||
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "Blob file #1234 already added"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileInBase) {
|
||||
// Increase the amount of garbage for a blob file present in the base version.
|
||||
|
||||
constexpr uint64_t blob_file_number = 1234;
|
||||
constexpr uint64_t total_blob_count = 5678;
|
||||
constexpr uint64_t total_blob_bytes = 999999;
|
||||
constexpr char checksum_method[] = "SHA1";
|
||||
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd";
|
||||
constexpr uint64_t garbage_blob_count = 123;
|
||||
constexpr uint64_t garbage_blob_bytes = 456789;
|
||||
|
||||
AddBlob(blob_file_number, total_blob_count, total_blob_bytes, checksum_method,
|
||||
checksum_value, garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
const auto meta =
|
||||
GetBlobFileMetaData(vstorage_.GetBlobFiles(), blob_file_number);
|
||||
ASSERT_NE(meta, nullptr);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t new_garbage_blob_count = 456;
|
||||
constexpr uint64_t new_garbage_blob_bytes = 111111;
|
||||
|
||||
edit.AddBlobFileGarbage(blob_file_number, new_garbage_blob_count,
|
||||
new_garbage_blob_bytes);
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
constexpr bool force_consistency_checks = false;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
|
||||
const auto& new_blob_files = new_vstorage.GetBlobFiles();
|
||||
ASSERT_EQ(new_blob_files.size(), 1);
|
||||
|
||||
const auto new_meta = GetBlobFileMetaData(new_blob_files, blob_file_number);
|
||||
|
||||
ASSERT_NE(new_meta, nullptr);
|
||||
ASSERT_EQ(new_meta->GetSharedMeta(), meta->GetSharedMeta());
|
||||
ASSERT_EQ(new_meta->GetBlobFileNumber(), blob_file_number);
|
||||
ASSERT_EQ(new_meta->GetTotalBlobCount(), total_blob_count);
|
||||
ASSERT_EQ(new_meta->GetTotalBlobBytes(), total_blob_bytes);
|
||||
ASSERT_EQ(new_meta->GetChecksumMethod(), checksum_method);
|
||||
ASSERT_EQ(new_meta->GetChecksumValue(), checksum_value);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobCount(),
|
||||
garbage_blob_count + new_garbage_blob_count);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobBytes(),
|
||||
garbage_blob_bytes + new_garbage_blob_bytes);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileAdditionApplied) {
|
||||
// Increase the amount of garbage for a blob file added using a version edit.
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit addition;
|
||||
|
||||
constexpr uint64_t blob_file_number = 1234;
|
||||
constexpr uint64_t total_blob_count = 5678;
|
||||
constexpr uint64_t total_blob_bytes = 999999;
|
||||
constexpr char checksum_method[] = "SHA1";
|
||||
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd";
|
||||
|
||||
addition.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
ASSERT_OK(builder.Apply(&addition));
|
||||
|
||||
constexpr uint64_t garbage_blob_count = 123;
|
||||
constexpr uint64_t garbage_blob_bytes = 456789;
|
||||
|
||||
VersionEdit garbage;
|
||||
|
||||
garbage.AddBlobFileGarbage(blob_file_number, garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
|
||||
ASSERT_OK(builder.Apply(&garbage));
|
||||
|
||||
constexpr bool force_consistency_checks = false;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
|
||||
const auto& new_blob_files = new_vstorage.GetBlobFiles();
|
||||
ASSERT_EQ(new_blob_files.size(), 1);
|
||||
|
||||
const auto new_meta = GetBlobFileMetaData(new_blob_files, blob_file_number);
|
||||
|
||||
ASSERT_NE(new_meta, nullptr);
|
||||
ASSERT_EQ(new_meta->GetBlobFileNumber(), blob_file_number);
|
||||
ASSERT_EQ(new_meta->GetTotalBlobCount(), total_blob_count);
|
||||
ASSERT_EQ(new_meta->GetTotalBlobBytes(), total_blob_bytes);
|
||||
ASSERT_EQ(new_meta->GetChecksumMethod(), checksum_method);
|
||||
ASSERT_EQ(new_meta->GetChecksumValue(), checksum_value);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobCount(), garbage_blob_count);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobBytes(), garbage_blob_bytes);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileNotFound) {
|
||||
// Attempt to increase the amount of garbage for a blob file that is
|
||||
// neither in the base version, nor was it added using a version edit.
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t blob_file_number = 1234;
|
||||
constexpr uint64_t garbage_blob_count = 5678;
|
||||
constexpr uint64_t garbage_blob_bytes = 999999;
|
||||
|
||||
edit.AddBlobFileGarbage(blob_file_number, garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "Blob file #1234 not found"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, SaveBlobFilesTo) {
|
||||
// Add three blob files to base version.
|
||||
for (uint64_t i = 1; i <= 3; ++i) {
|
||||
const uint64_t blob_file_number = i;
|
||||
const uint64_t total_blob_count = i * 1000;
|
||||
const uint64_t total_blob_bytes = i * 1000000;
|
||||
const uint64_t garbage_blob_count = i * 100;
|
||||
const uint64_t garbage_blob_bytes = i * 20000;
|
||||
|
||||
AddBlob(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(), garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
}
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
// Add some garbage to the second and third blob files. The second blob file
|
||||
// remains valid since it does not consist entirely of garbage yet. The third
|
||||
// blob file is all garbage after the edit and will not be part of the new
|
||||
// version.
|
||||
edit.AddBlobFileGarbage(/* blob_file_number */ 2,
|
||||
/* garbage_blob_count */ 200,
|
||||
/* garbage_blob_bytes */ 100000);
|
||||
edit.AddBlobFileGarbage(/* blob_file_number */ 3,
|
||||
/* garbage_blob_count */ 2700,
|
||||
/* garbage_blob_bytes */ 2940000);
|
||||
|
||||
// Add a fourth blob file.
|
||||
edit.AddBlobFile(/* blob_file_number */ 4, /* total_blob_count */ 4000,
|
||||
/* total_blob_bytes */ 4000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string());
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
constexpr bool force_consistency_checks = false;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
|
||||
const auto& new_blob_files = new_vstorage.GetBlobFiles();
|
||||
ASSERT_EQ(new_blob_files.size(), 3);
|
||||
|
||||
const auto meta1 = GetBlobFileMetaData(new_blob_files, 1);
|
||||
|
||||
ASSERT_NE(meta1, nullptr);
|
||||
ASSERT_EQ(meta1->GetBlobFileNumber(), 1);
|
||||
ASSERT_EQ(meta1->GetTotalBlobCount(), 1000);
|
||||
ASSERT_EQ(meta1->GetTotalBlobBytes(), 1000000);
|
||||
ASSERT_EQ(meta1->GetGarbageBlobCount(), 100);
|
||||
ASSERT_EQ(meta1->GetGarbageBlobBytes(), 20000);
|
||||
|
||||
const auto meta2 = GetBlobFileMetaData(new_blob_files, 2);
|
||||
|
||||
ASSERT_NE(meta2, nullptr);
|
||||
ASSERT_EQ(meta2->GetBlobFileNumber(), 2);
|
||||
ASSERT_EQ(meta2->GetTotalBlobCount(), 2000);
|
||||
ASSERT_EQ(meta2->GetTotalBlobBytes(), 2000000);
|
||||
ASSERT_EQ(meta2->GetGarbageBlobCount(), 400);
|
||||
ASSERT_EQ(meta2->GetGarbageBlobBytes(), 140000);
|
||||
|
||||
const auto meta4 = GetBlobFileMetaData(new_blob_files, 4);
|
||||
|
||||
ASSERT_NE(meta4, nullptr);
|
||||
ASSERT_EQ(meta4->GetBlobFileNumber(), 4);
|
||||
ASSERT_EQ(meta4->GetTotalBlobCount(), 4000);
|
||||
ASSERT_EQ(meta4->GetTotalBlobBytes(), 4000000);
|
||||
ASSERT_EQ(meta4->GetGarbageBlobCount(), 0);
|
||||
ASSERT_EQ(meta4->GetGarbageBlobBytes(), 0);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
|
||||
// Initialize base version. The first table file points to a valid blob file
|
||||
// in this version; the second one does not refer to any blob files.
|
||||
|
||||
Add(/* level */ 1, /* file_number */ 1, /* smallest */ "150",
|
||||
/* largest */ "200", /* file_size */ 100,
|
||||
/* path_id */ 0, /* smallest_seq */ 100, /* largest_seq */ 100,
|
||||
/* num_entries */ 0, /* num_deletions */ 0,
|
||||
/* sampled */ false, /* smallest_seqno */ 100, /* largest_seqno */ 100,
|
||||
/* oldest_blob_file_number */ 16);
|
||||
Add(/* level */ 1, /* file_number */ 23, /* smallest */ "201",
|
||||
/* largest */ "300", /* file_size */ 100,
|
||||
/* path_id */ 0, /* smallest_seq */ 200, /* largest_seq */ 200,
|
||||
/* num_entries */ 0, /* num_deletions */ 0,
|
||||
/* sampled */ false, /* smallest_seqno */ 200, /* largest_seqno */ 200,
|
||||
kInvalidBlobFileNumber);
|
||||
|
||||
AddBlob(/* blob_file_number */ 16, /* total_blob_count */ 1000,
|
||||
/* total_blob_bytes */ 1000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(),
|
||||
/* garbage_blob_count */ 500, /* garbage_blob_bytes */ 300000);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// Add a new table file that points to the existing blob file, and add a
|
||||
// new table file--blob file pair.
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
edit.AddFile(/* level */ 1, /* file_number */ 606, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("701"),
|
||||
/* largest */ GetInternalKey("750"), /* smallest_seqno */ 200,
|
||||
/* largest_seqno */ 200, /* marked_for_compaction */ false,
|
||||
/* oldest_blob_file_number */ 16, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
edit.AddFile(/* level */ 1, /* file_number */ 700, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("801"),
|
||||
/* largest */ GetInternalKey("850"), /* smallest_seqno */ 200,
|
||||
/* largest_seqno */ 200, /* marked_for_compaction */ false,
|
||||
/* oldest_blob_file_number */ 1000, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
edit.AddBlobFile(/* blob_file_number */ 1000, /* total_blob_count */ 2000,
|
||||
/* total_blob_bytes */ 200000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string());
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
// Save to a new version in order to trigger consistency checks.
|
||||
constexpr bool force_consistency_checks = true;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesNotInVersion) {
|
||||
// Initialize base version. The table file points to a blob file that is
|
||||
// not in this version.
|
||||
|
||||
Add(/* level */ 1, /* file_number */ 1, /* smallest */ "150",
|
||||
/* largest */ "200", /* file_size */ 100,
|
||||
/* path_id */ 0, /* smallest_seq */ 100, /* largest_seq */ 100,
|
||||
/* num_entries */ 0, /* num_deletions */ 0,
|
||||
/* sampled */ false, /* smallest_seqno */ 100, /* largest_seqno */ 100,
|
||||
/* oldest_blob_file_number */ 256);
|
||||
|
||||
AddBlob(/* blob_file_number */ 16, /* total_blob_count */ 1000,
|
||||
/* total_blob_bytes */ 1000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(),
|
||||
/* garbage_blob_count */ 500, /* garbage_blob_bytes */ 300000);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
// Save to a new version in order to trigger consistency checks.
|
||||
constexpr bool force_consistency_checks = true;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
const Status s = builder.SaveTo(&new_vstorage);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(
|
||||
std::strstr(s.getState(), "Blob file #256 is not part of this version"));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesAllGarbage) {
|
||||
// Initialize base version. The table file points to a blob file that is
|
||||
// all garbage.
|
||||
|
||||
Add(/* level */ 1, /* file_number */ 1, /* smallest */ "150",
|
||||
/* largest */ "200", /* file_size */ 100,
|
||||
/* path_id */ 0, /* smallest_seq */ 100, /* largest_seq */ 100,
|
||||
/* num_entries */ 0, /* num_deletions */ 0,
|
||||
/* sampled */ false, /* smallest_seqno */ 100, /* largest_seqno */ 100,
|
||||
/* oldest_blob_file_number */ 16);
|
||||
|
||||
AddBlob(/* blob_file_number */ 16, /* total_blob_count */ 1000,
|
||||
/* total_blob_bytes */ 1000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(),
|
||||
/* garbage_blob_count */ 1000, /* garbage_blob_bytes */ 1000000);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
// Save to a new version in order to trigger consistency checks.
|
||||
constexpr bool force_consistency_checks = true;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
const Status s = builder.SaveTo(&new_vstorage);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(
|
||||
std::strstr(s.getState(), "Blob file #16 consists entirely of garbage"));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForFileDeletedTwice) {
|
||||
Add(0, 1U, "150", "200", 100U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.DeleteFile(0, 1U);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder version_builder(env_options, &ioptions_, table_cache,
|
||||
&vstorage_, version_set);
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr,
|
||||
true /* force_consistency_checks */);
|
||||
ASSERT_OK(version_builder.Apply(&version_edit));
|
||||
ASSERT_OK(version_builder.SaveTo(&new_vstorage));
|
||||
|
||||
VersionBuilder version_builder2(env_options, &ioptions_, table_cache,
|
||||
&new_vstorage, version_set);
|
||||
VersionStorageInfo new_vstorage2(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr,
|
||||
true /* force_consistency_checks */);
|
||||
ASSERT_NOK(version_builder2.Apply(&version_edit));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
UnrefFilesInVersion(&new_vstorage2);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, EstimatedActiveKeys) {
|
||||
const uint32_t kTotalSamples = 20;
|
||||
const uint32_t kNumLevels = 5;
|
||||
|
||||
+24
-44
@@ -27,17 +27,12 @@ VersionEditHandler::VersionEditHandler(
|
||||
assert(version_set_ != nullptr);
|
||||
}
|
||||
|
||||
void VersionEditHandler::Iterate(log::Reader& reader, Status* log_read_status,
|
||||
std::string* db_id) {
|
||||
Status VersionEditHandler::Iterate(log::Reader& reader, std::string* db_id) {
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
assert(log_read_status);
|
||||
assert(log_read_status->ok());
|
||||
|
||||
size_t recovered_edits = 0;
|
||||
Status s = Initialize();
|
||||
while (s.ok() && reader.ReadRecord(&record, &scratch) &&
|
||||
log_read_status->ok()) {
|
||||
while (reader.ReadRecord(&record, &scratch) && s.ok()) {
|
||||
VersionEdit edit;
|
||||
s = edit.DecodeFrom(record);
|
||||
if (!s.ok()) {
|
||||
@@ -75,15 +70,13 @@ void VersionEditHandler::Iterate(log::Reader& reader, Status* log_read_status,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!log_read_status->ok()) {
|
||||
s = *log_read_status;
|
||||
}
|
||||
|
||||
CheckIterationResult(reader, &s);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status VersionEditHandler::Initialize() {
|
||||
@@ -384,7 +377,6 @@ Status VersionEditHandler::MaybeCreateVersion(const VersionEdit& /*edit*/,
|
||||
ColumnFamilyData* cfd,
|
||||
bool force_create_version) {
|
||||
assert(cfd->initialized());
|
||||
Status s;
|
||||
if (force_create_version) {
|
||||
auto builder_iter = builders_.find(cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
@@ -392,18 +384,13 @@ Status VersionEditHandler::MaybeCreateVersion(const VersionEdit& /*edit*/,
|
||||
auto* v = new Version(cfd, version_set_, version_set_->file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
version_set_->current_version_number_++);
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
if (s.ok()) {
|
||||
// Install new version
|
||||
v->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!(version_set_->db_options_->skip_stats_update_on_db_open));
|
||||
version_set_->AppendVersion(cfd, v);
|
||||
} else {
|
||||
delete v;
|
||||
}
|
||||
builder->SaveTo(v->storage_info());
|
||||
// Install new version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(version_set_->db_options_->skip_stats_update_on_db_open));
|
||||
version_set_->AppendVersion(cfd, v);
|
||||
}
|
||||
return s;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status VersionEditHandler::LoadTables(ColumnFamilyData* cfd,
|
||||
@@ -411,18 +398,18 @@ Status VersionEditHandler::LoadTables(ColumnFamilyData* cfd,
|
||||
bool is_initial_load) {
|
||||
assert(cfd != nullptr);
|
||||
assert(!cfd->IsDropped());
|
||||
Status s;
|
||||
auto builder_iter = builders_.find(cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
assert(builder_iter->second != nullptr);
|
||||
VersionBuilder* builder = builder_iter->second->version_builder();
|
||||
assert(builder);
|
||||
Status s = builder->LoadTableHandlers(
|
||||
s = builder->LoadTableHandlers(
|
||||
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());
|
||||
if ((s.IsPathNotFound() || s.IsCorruption()) &&
|
||||
no_error_if_table_files_missing_) {
|
||||
if (s.IsPathNotFound() && no_error_if_table_files_missing_) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok() && !version_set_->db_options_->paranoid_checks) {
|
||||
@@ -553,11 +540,9 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
const std::string fpath =
|
||||
MakeTableFileName(cfd->ioptions()->cf_paths[0].path, file_num);
|
||||
s = version_set_->VerifyFileMetadata(fpath, meta);
|
||||
if (s.IsPathNotFound() || s.IsNotFound() || s.IsCorruption()) {
|
||||
if (s.IsPathNotFound() || s.IsNotFound()) {
|
||||
missing_files.insert(file_num);
|
||||
s = Status::OK();
|
||||
} else if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool missing_info = !version_edit_params_.has_log_number_ ||
|
||||
@@ -565,29 +550,24 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
!version_edit_params_.has_last_sequence_;
|
||||
|
||||
// Create version before apply edit
|
||||
if (s.ok() && !missing_info &&
|
||||
((!missing_files.empty() && !prev_has_missing_files) ||
|
||||
(missing_files.empty() && force_create_version))) {
|
||||
if (!missing_info && ((!missing_files.empty() && !prev_has_missing_files) ||
|
||||
(missing_files.empty() && force_create_version))) {
|
||||
auto builder_iter = builders_.find(cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
auto* builder = builder_iter->second->version_builder();
|
||||
auto* version = new Version(cfd, version_set_, version_set_->file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
version_set_->current_version_number_++);
|
||||
s = builder->SaveTo(version->storage_info());
|
||||
if (s.ok()) {
|
||||
version->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!version_set_->db_options_->skip_stats_update_on_db_open);
|
||||
auto v_iter = versions_.find(cfd->GetID());
|
||||
if (v_iter != versions_.end()) {
|
||||
delete v_iter->second;
|
||||
v_iter->second = version;
|
||||
} else {
|
||||
versions_.emplace(cfd->GetID(), version);
|
||||
}
|
||||
builder->SaveTo(version->storage_info());
|
||||
version->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!version_set_->db_options_->skip_stats_update_on_db_open);
|
||||
auto v_iter = versions_.find(cfd->GetID());
|
||||
if (v_iter != versions_.end()) {
|
||||
delete v_iter->second;
|
||||
v_iter->second = version;
|
||||
} else {
|
||||
delete version;
|
||||
versions_.emplace(cfd->GetID(), version);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
|
||||
@@ -40,8 +40,7 @@ class VersionEditHandler {
|
||||
|
||||
virtual ~VersionEditHandler() {}
|
||||
|
||||
void Iterate(log::Reader& reader, Status* log_read_status,
|
||||
std::string* db_id);
|
||||
Status Iterate(log::Reader& reader, std::string* db_id);
|
||||
|
||||
const Status& status() const { return status_; }
|
||||
|
||||
|
||||
+86
-218
@@ -10,7 +10,6 @@
|
||||
#include "db/version_set.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
@@ -20,7 +19,6 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "compaction/compaction.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/log_reader.h"
|
||||
@@ -52,7 +50,6 @@
|
||||
#include "table/table_reader.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -864,8 +861,7 @@ class LevelIterator final : public InternalIterator {
|
||||
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)
|
||||
compaction_boundaries = nullptr)
|
||||
: table_cache_(table_cache),
|
||||
read_options_(read_options),
|
||||
file_options_(file_options),
|
||||
@@ -877,7 +873,6 @@ class LevelIterator final : public InternalIterator {
|
||||
should_sample_(should_sample),
|
||||
caller_(caller),
|
||||
skip_filters_(skip_filters),
|
||||
allow_unprepared_value_(allow_unprepared_value),
|
||||
file_index_(flevel_->num_files),
|
||||
level_(level),
|
||||
range_del_agg_(range_del_agg),
|
||||
@@ -912,10 +907,6 @@ class LevelIterator final : public InternalIterator {
|
||||
return file_iter_.iter() ? file_iter_.status() : Status::OK();
|
||||
}
|
||||
|
||||
bool PrepareValue() override {
|
||||
return file_iter_.PrepareValue();
|
||||
}
|
||||
|
||||
inline bool MayBeOutOfLowerBound() override {
|
||||
assert(Valid());
|
||||
return may_be_out_of_lower_bound_ && file_iter_.MayBeOutOfLowerBound();
|
||||
@@ -988,7 +979,7 @@ class LevelIterator final : public InternalIterator {
|
||||
range_del_agg_, prefix_extractor_,
|
||||
nullptr /* don't need reference to table */, file_read_hist_, caller_,
|
||||
/*arena=*/nullptr, skip_filters_, level_, smallest_compaction_key,
|
||||
largest_compaction_key, allow_unprepared_value_);
|
||||
largest_compaction_key);
|
||||
}
|
||||
|
||||
// Check if current file being fully within iterate_lower_bound.
|
||||
@@ -999,9 +990,9 @@ class LevelIterator final : public InternalIterator {
|
||||
if (read_options_.iterate_lower_bound != nullptr &&
|
||||
file_index_ < flevel_->num_files) {
|
||||
may_be_out_of_lower_bound_ =
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
ExtractUserKey(file_smallest_key(file_index_)), /*a_has_ts=*/true,
|
||||
*read_options_.iterate_lower_bound, /*b_has_ts=*/false) < 0;
|
||||
user_comparator_.Compare(
|
||||
ExtractUserKey(file_smallest_key(file_index_)),
|
||||
*read_options_.iterate_lower_bound) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,7 +1012,6 @@ class LevelIterator final : public InternalIterator {
|
||||
bool should_sample_;
|
||||
TableReaderCaller caller_;
|
||||
bool skip_filters_;
|
||||
bool allow_unprepared_value_;
|
||||
bool may_be_out_of_lower_bound_ = true;
|
||||
size_t file_index_;
|
||||
int level_;
|
||||
@@ -1127,7 +1117,6 @@ bool LevelIterator::NextAndGetResult(IterateResult* result) {
|
||||
if (is_valid) {
|
||||
result->key = key();
|
||||
result->may_be_out_of_upper_bound = MayBeOutOfUpperBound();
|
||||
result->value_prepared = !allow_unprepared_value_;
|
||||
}
|
||||
return is_valid;
|
||||
}
|
||||
@@ -1544,13 +1533,12 @@ double VersionStorageInfo::GetEstimatedCompressionRatioAtLevel(
|
||||
void Version::AddIterators(const ReadOptions& read_options,
|
||||
const FileOptions& soptions,
|
||||
MergeIteratorBuilder* merge_iter_builder,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
bool allow_unprepared_value) {
|
||||
RangeDelAggregator* range_del_agg) {
|
||||
assert(storage_info_.finalized_);
|
||||
|
||||
for (int level = 0; level < storage_info_.num_non_empty_levels(); level++) {
|
||||
AddIteratorsForLevel(read_options, soptions, merge_iter_builder, level,
|
||||
range_del_agg, allow_unprepared_value);
|
||||
range_del_agg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1558,8 +1546,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
const FileOptions& soptions,
|
||||
MergeIteratorBuilder* merge_iter_builder,
|
||||
int level,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
bool allow_unprepared_value) {
|
||||
RangeDelAggregator* range_del_agg) {
|
||||
assert(storage_info_.finalized_);
|
||||
if (level >= storage_info_.num_non_empty_levels()) {
|
||||
// This is an empty level
|
||||
@@ -1584,8 +1571,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
TableReaderCaller::kUserIterator, arena,
|
||||
/*skip_filters=*/false, /*level=*/0,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value));
|
||||
/*largest_compaction_key=*/nullptr));
|
||||
}
|
||||
if (should_sample) {
|
||||
// Count ones for every L0 files. This is done per iterator creation
|
||||
@@ -1607,8 +1593,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
mutable_cf_options_.prefix_extractor.get(), should_sample_file_read(),
|
||||
cfd_->internal_stats()->GetFileReadHist(level),
|
||||
TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
|
||||
range_del_agg, /*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value));
|
||||
range_del_agg, /*largest_compaction_key=*/nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1644,8 +1629,7 @@ Status Version::OverlapWithLevelIterator(const ReadOptions& read_options,
|
||||
TableReaderCaller::kUserIterator, &arena,
|
||||
/*skip_filters=*/false, /*level=*/0,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false));
|
||||
/*largest_compaction_key=*/nullptr));
|
||||
status = OverlapWithIterator(
|
||||
ucmp, smallest_user_key, largest_user_key, iter.get(), overlap);
|
||||
if (!status.ok() || *overlap) {
|
||||
@@ -2090,9 +2074,6 @@ void VersionStorageInfo::GenerateLevelFilesBrief() {
|
||||
void Version::PrepareApply(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
bool update_stats) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"Version::PrepareApply:forced_check",
|
||||
reinterpret_cast<void*>(&storage_info_.force_consistency_checks_));
|
||||
UpdateAccumulatedStats(update_stats);
|
||||
storage_info_.UpdateNumNonEmptyLevels();
|
||||
storage_info_.CalculateBaseBytes(*cfd_->ioptions(), mutable_cf_options);
|
||||
@@ -2394,11 +2375,6 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
// compaction score for the whole DB. Adding other levels as if
|
||||
// they are L0 files.
|
||||
for (int i = 1; i < num_levels(); i++) {
|
||||
// Its possible that a subset of the files in a level may be in a
|
||||
// compaction, due to delete triggered compaction or trivial move.
|
||||
// In that case, the below check may not catch a level being
|
||||
// compacted as it only checks the first file. The worst that can
|
||||
// happen is a scheduled compaction thread will find nothing to do.
|
||||
if (!files_[i].empty() && !files_[i][0]->being_compacted) {
|
||||
num_sorted_runs++;
|
||||
}
|
||||
@@ -2623,19 +2599,6 @@ void VersionStorageInfo::AddFile(int level, FileMetaData* f, Logger* info_log) {
|
||||
level_files->push_back(f);
|
||||
}
|
||||
|
||||
void VersionStorageInfo::AddBlobFile(
|
||||
std::shared_ptr<BlobFileMetaData> blob_file_meta) {
|
||||
assert(blob_file_meta);
|
||||
|
||||
const uint64_t blob_file_number = blob_file_meta->GetBlobFileNumber();
|
||||
|
||||
auto it = blob_files_.lower_bound(blob_file_number);
|
||||
assert(it == blob_files_.end() || it->first != blob_file_number);
|
||||
|
||||
blob_files_.insert(
|
||||
it, BlobFiles::value_type(blob_file_number, std::move(blob_file_meta)));
|
||||
}
|
||||
|
||||
// Version::PrepareApply() need to be called before calling the function, or
|
||||
// following functions called:
|
||||
// 1. UpdateNumNonEmptyLevels();
|
||||
@@ -3478,21 +3441,6 @@ std::string Version::DebugString(bool hex, bool print_stats) const {
|
||||
r.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
const auto& blob_files = storage_info_.GetBlobFiles();
|
||||
if (!blob_files.empty()) {
|
||||
r.append("--- blob files --- version# ");
|
||||
AppendNumberTo(&r, version_number_);
|
||||
r.append(" ---\n");
|
||||
for (const auto& pair : blob_files) {
|
||||
const auto& blob_file_meta = pair.second;
|
||||
assert(blob_file_meta);
|
||||
|
||||
r.append(blob_file_meta->DebugString());
|
||||
r.push_back('\n');
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -3820,7 +3768,6 @@ Status VersionSet::ProcessManifestWrites(
|
||||
|
||||
uint64_t new_manifest_file_size = 0;
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
|
||||
assert(pending_manifest_file_number_ == 0);
|
||||
if (!descriptor_log_ ||
|
||||
@@ -3931,19 +3878,15 @@ Status VersionSet::ProcessManifestWrites(
|
||||
}
|
||||
++idx;
|
||||
#endif /* !NDEBUG */
|
||||
io_s = descriptor_log_->AddRecord(record);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
s = io_s;
|
||||
s = descriptor_log_->AddRecord(record);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
io_s = SyncManifest(env_, db_options_, descriptor_log_->file());
|
||||
s = SyncManifest(env_, db_options_, descriptor_log_->file());
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
s = io_s;
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_options_->info_log, "MANIFEST write %s\n",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
@@ -3952,12 +3895,8 @@ Status VersionSet::ProcessManifestWrites(
|
||||
// If we just created a new descriptor file, install it by writing a
|
||||
// new CURRENT file that points to it.
|
||||
if (s.ok() && new_descriptor_log) {
|
||||
io_s = SetCurrentFile(fs_, dbname_, pending_manifest_file_number_,
|
||||
db_directory);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
s = io_s;
|
||||
}
|
||||
s = SetCurrentFile(env_, dbname_, pending_manifest_file_number_,
|
||||
db_directory);
|
||||
TEST_SYNC_POINT("VersionSet::ProcessManifestWrites:AfterNewManifest");
|
||||
}
|
||||
|
||||
@@ -4386,26 +4325,24 @@ Status VersionSet::GetCurrentManifestPath(const std::string& dbname,
|
||||
if (dbname.back() != '/') {
|
||||
manifest_path->push_back('/');
|
||||
}
|
||||
manifest_path->append(fname);
|
||||
*manifest_path += fname;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status VersionSet::ReadAndRecover(
|
||||
log::Reader& reader, AtomicGroupReadBuffer* read_buffer,
|
||||
log::Reader* reader, AtomicGroupReadBuffer* read_buffer,
|
||||
const std::unordered_map<std::string, ColumnFamilyOptions>& name_to_options,
|
||||
std::unordered_map<int, std::string>& column_families_not_found,
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>&
|
||||
builders,
|
||||
Status* log_read_status, VersionEditParams* version_edit_params,
|
||||
std::string* db_id) {
|
||||
VersionEditParams* version_edit_params, std::string* db_id) {
|
||||
assert(reader != nullptr);
|
||||
assert(read_buffer != nullptr);
|
||||
assert(log_read_status != nullptr);
|
||||
Status s;
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
size_t recovered_edits = 0;
|
||||
while (s.ok() && reader.ReadRecord(&record, &scratch) &&
|
||||
log_read_status->ok()) {
|
||||
while (reader->ReadRecord(&record, &scratch) && s.ok()) {
|
||||
VersionEdit edit;
|
||||
s = edit.DecodeFrom(record);
|
||||
if (!s.ok()) {
|
||||
@@ -4449,9 +4386,6 @@ Status VersionSet::ReadAndRecover(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!log_read_status->ok()) {
|
||||
s = *log_read_status;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
// Clear the buffer if we fail to decode/apply an edit.
|
||||
read_buffer->Clear();
|
||||
@@ -4498,7 +4432,8 @@ Status VersionSet::Recover(
|
||||
db_options_->log_readahead_size));
|
||||
}
|
||||
|
||||
VersionBuilderMap builders;
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>
|
||||
builders;
|
||||
|
||||
// add default column family
|
||||
auto default_cf_iter = cf_name_to_options.find(kDefaultColumnFamilyName);
|
||||
@@ -4520,13 +4455,12 @@ Status VersionSet::Recover(
|
||||
VersionEditParams version_edit_params;
|
||||
{
|
||||
VersionSet::LogReporter reporter;
|
||||
Status log_read_status;
|
||||
reporter.status = &log_read_status;
|
||||
reporter.status = &s;
|
||||
log::Reader reader(nullptr, std::move(manifest_file_reader), &reporter,
|
||||
true /* checksum */, 0 /* log_number */);
|
||||
AtomicGroupReadBuffer read_buffer;
|
||||
s = ReadAndRecover(reader, &read_buffer, cf_name_to_options,
|
||||
column_families_not_found, builders, &log_read_status,
|
||||
s = ReadAndRecover(&reader, &read_buffer, cf_name_to_options,
|
||||
column_families_not_found, builders,
|
||||
&version_edit_params, db_id);
|
||||
current_manifest_file_size = reader.GetReadOffset();
|
||||
assert(current_manifest_file_size != 0);
|
||||
@@ -4610,11 +4544,7 @@ Status VersionSet::Recover(
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
if (!s.ok()) {
|
||||
delete v;
|
||||
return s;
|
||||
}
|
||||
builder->SaveTo(v->storage_info());
|
||||
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
@@ -4791,20 +4721,21 @@ Status VersionSet::TryRecoverFromOneManifest(
|
||||
db_options_->log_readahead_size));
|
||||
}
|
||||
|
||||
assert(s.ok());
|
||||
VersionSet::LogReporter reporter;
|
||||
reporter.status = &s;
|
||||
log::Reader reader(nullptr, std::move(manifest_file_reader), &reporter,
|
||||
/*checksum=*/true, /*log_num=*/0);
|
||||
VersionEditHandlerPointInTime handler_pit(read_only, column_families,
|
||||
const_cast<VersionSet*>(this));
|
||||
{
|
||||
VersionEditHandlerPointInTime handler_pit(read_only, column_families,
|
||||
const_cast<VersionSet*>(this));
|
||||
|
||||
handler_pit.Iterate(reader, &s, db_id);
|
||||
s = handler_pit.Iterate(reader, db_id);
|
||||
|
||||
assert(nullptr != has_missing_table_file);
|
||||
*has_missing_table_file = handler_pit.HasMissingFiles();
|
||||
assert(nullptr != has_missing_table_file);
|
||||
*has_missing_table_file = handler_pit.HasMissingFiles();
|
||||
}
|
||||
|
||||
return handler_pit.status();
|
||||
return s;
|
||||
}
|
||||
|
||||
Status VersionSet::ListColumnFamilies(std::vector<std::string>* column_families,
|
||||
@@ -5164,7 +5095,7 @@ Status VersionSet::DumpManifest(Options& options, std::string& dscname,
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
builder->SaveTo(v->storage_info());
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(), false);
|
||||
|
||||
printf("--------------- Column family \"%s\" (ID %" PRIu32
|
||||
@@ -5235,16 +5166,13 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
return Status::Corruption("Unable to Encode VersionEdit:" +
|
||||
edit_for_db_id.DebugString(true));
|
||||
}
|
||||
IOStatus io_s = log->AddRecord(db_id_record);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
return std::move(io_s);
|
||||
Status add_record = log->AddRecord(db_id_record);
|
||||
if (!add_record.ok()) {
|
||||
return add_record;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cfd : *column_family_set_) {
|
||||
assert(cfd);
|
||||
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
@@ -5265,10 +5193,9 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
return Status::Corruption(
|
||||
"Unable to Encode VersionEdit:" + edit.DebugString(true));
|
||||
}
|
||||
IOStatus io_s = log->AddRecord(record);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
return std::move(io_s);
|
||||
Status s = log->AddRecord(record);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5277,9 +5204,6 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
|
||||
assert(cfd->current());
|
||||
assert(cfd->current()->storage_info());
|
||||
|
||||
for (int level = 0; level < cfd->NumberLevels(); level++) {
|
||||
for (const auto& f :
|
||||
cfd->current()->storage_info()->LevelFiles(level)) {
|
||||
@@ -5291,24 +5215,6 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
f->file_checksum, f->file_checksum_func_name);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& blob_files = cfd->current()->storage_info()->GetBlobFiles();
|
||||
for (const auto& pair : blob_files) {
|
||||
const uint64_t blob_file_number = pair.first;
|
||||
const auto& meta = pair.second;
|
||||
|
||||
assert(meta);
|
||||
assert(blob_file_number == meta->GetBlobFileNumber());
|
||||
|
||||
edit.AddBlobFile(blob_file_number, meta->GetTotalBlobCount(),
|
||||
meta->GetTotalBlobBytes(), meta->GetChecksumMethod(),
|
||||
meta->GetChecksumValue());
|
||||
if (meta->GetGarbageBlobCount() > 0) {
|
||||
edit.AddBlobFileGarbage(blob_file_number, meta->GetGarbageBlobCount(),
|
||||
meta->GetGarbageBlobBytes());
|
||||
}
|
||||
}
|
||||
|
||||
const auto iter = curr_state.find(cfd->GetID());
|
||||
assert(iter != curr_state.end());
|
||||
uint64_t log_number = iter->second.log_number;
|
||||
@@ -5318,10 +5224,9 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
return Status::Corruption(
|
||||
"Unable to Encode VersionEdit:" + edit.DebugString(true));
|
||||
}
|
||||
IOStatus io_s = log->AddRecord(record);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
return std::move(io_s);
|
||||
Status s = log->AddRecord(record);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5604,8 +5509,7 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
/*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/static_cast<int>(which),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
/*largest_compaction_key=*/nullptr);
|
||||
}
|
||||
} else {
|
||||
// Create concatenating iterator for the files from this level
|
||||
@@ -5744,37 +5648,19 @@ void VersionSet::GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) {
|
||||
}
|
||||
|
||||
void VersionSet::GetObsoleteFiles(std::vector<ObsoleteFileInfo>* files,
|
||||
std::vector<ObsoleteBlobFileInfo>* blob_files,
|
||||
std::vector<std::string>* manifest_filenames,
|
||||
uint64_t min_pending_output) {
|
||||
assert(files);
|
||||
assert(blob_files);
|
||||
assert(manifest_filenames);
|
||||
assert(files->empty());
|
||||
assert(blob_files->empty());
|
||||
assert(manifest_filenames->empty());
|
||||
|
||||
obsolete_manifests_.swap(*manifest_filenames);
|
||||
std::vector<ObsoleteFileInfo> pending_files;
|
||||
for (auto& f : obsolete_files_) {
|
||||
if (f.metadata->fd.GetNumber() < min_pending_output) {
|
||||
files->emplace_back(std::move(f));
|
||||
files->push_back(std::move(f));
|
||||
} else {
|
||||
pending_files.emplace_back(std::move(f));
|
||||
pending_files.push_back(std::move(f));
|
||||
}
|
||||
}
|
||||
obsolete_files_.swap(pending_files);
|
||||
|
||||
std::vector<ObsoleteBlobFileInfo> pending_blob_files;
|
||||
for (auto& blob_file : obsolete_blob_files_) {
|
||||
if (blob_file.GetBlobFileNumber() < min_pending_output) {
|
||||
blob_files->emplace_back(std::move(blob_file));
|
||||
} else {
|
||||
pending_blob_files.emplace_back(std::move(blob_file));
|
||||
}
|
||||
}
|
||||
obsolete_blob_files_.swap(pending_blob_files);
|
||||
|
||||
obsolete_manifests_.swap(*manifest_filenames);
|
||||
}
|
||||
|
||||
ColumnFamilyData* VersionSet::CreateColumnFamily(
|
||||
@@ -5885,7 +5771,8 @@ Status ReactiveVersionSet::Recover(
|
||||
// In recovery, nobody else can access it, so it's fine to set it to be
|
||||
// initialized earlier.
|
||||
default_cfd->set_initialized();
|
||||
VersionBuilderMap builders;
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>
|
||||
builders;
|
||||
std::unordered_map<int, std::string> column_families_not_found;
|
||||
builders.insert(
|
||||
std::make_pair(0, std::unique_ptr<BaseReferencedVersionBuilder>(
|
||||
@@ -5893,7 +5780,7 @@ Status ReactiveVersionSet::Recover(
|
||||
|
||||
manifest_reader_status->reset(new Status());
|
||||
manifest_reporter->reset(new LogReporter());
|
||||
static_cast_with_check<LogReporter>(manifest_reporter->get())->status =
|
||||
static_cast<LogReporter*>(manifest_reporter->get())->status =
|
||||
manifest_reader_status->get();
|
||||
Status s = MaybeSwitchManifest(manifest_reporter->get(), manifest_reader);
|
||||
log::Reader* reader = manifest_reader->get();
|
||||
@@ -5902,9 +5789,10 @@ Status ReactiveVersionSet::Recover(
|
||||
VersionEdit version_edit;
|
||||
while (s.ok() && retry < 1) {
|
||||
assert(reader != nullptr);
|
||||
s = ReadAndRecover(*reader, &read_buffer_, cf_name_to_options,
|
||||
column_families_not_found, builders,
|
||||
manifest_reader_status->get(), &version_edit);
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
s = ReadAndRecover(reader, &read_buffer_, cf_name_to_options,
|
||||
column_families_not_found, builders, &version_edit);
|
||||
if (s.ok()) {
|
||||
bool enough = version_edit.has_next_file_number_ &&
|
||||
version_edit.has_log_number_ &&
|
||||
@@ -5983,23 +5871,13 @@ Status ReactiveVersionSet::Recover(
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
builder->SaveTo(v->storage_info());
|
||||
|
||||
if (s.ok()) {
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(db_options_->skip_stats_update_on_db_open));
|
||||
AppendVersion(cfd, v);
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(db_options_->info_log,
|
||||
"[%s]: inconsistent version: %s\n",
|
||||
cfd->GetName().c_str(), s.ToString().c_str());
|
||||
delete v;
|
||||
break;
|
||||
}
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(db_options_->skip_stats_update_on_db_open));
|
||||
AppendVersion(cfd, v);
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
next_file_number_.store(version_edit.next_file_number_ + 1);
|
||||
last_allocated_sequence_ = version_edit.last_sequence_;
|
||||
last_published_sequence_ = version_edit.last_sequence_;
|
||||
@@ -6076,8 +5954,6 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
s = ApplyOneVersionEditToBuilder(edit, cfds_changed, &temp_edit);
|
||||
if (s.ok()) {
|
||||
applied_edits++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6087,14 +5963,13 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
}
|
||||
// It's possible that:
|
||||
// 1) s.IsCorruption(), indicating the current MANIFEST is corrupted.
|
||||
// Or the version(s) rebuilt from tailing the MANIFEST is inconsistent.
|
||||
// 2) we have finished reading the current MANIFEST.
|
||||
// 3) we have encountered an IOError reading the current MANIFEST.
|
||||
// We need to look for the next MANIFEST and start from there. If we cannot
|
||||
// find the next MANIFEST, we should exit the loop.
|
||||
Status tmp_s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
|
||||
s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
|
||||
reader = manifest_reader->get();
|
||||
if (tmp_s.ok()) {
|
||||
if (s.ok()) {
|
||||
if (reader->file()->file_name() == old_manifest_path) {
|
||||
// Still processing the same MANIFEST, thus no need to continue this
|
||||
// loop since no record is available if we have reached here.
|
||||
@@ -6124,7 +5999,6 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
number_of_edits_to_skip_ += 2;
|
||||
}
|
||||
}
|
||||
s = tmp_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6217,16 +6091,12 @@ Status ReactiveVersionSet::ApplyOneVersionEditToBuilder(
|
||||
auto version = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
s = builder->SaveTo(version->storage_info());
|
||||
if (s.ok()) {
|
||||
version->PrepareApply(*cfd->GetLatestMutableCFOptions(), true);
|
||||
AppendVersion(cfd, version);
|
||||
active_version_builders_.erase(builder_iter);
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
}
|
||||
} else {
|
||||
delete version;
|
||||
builder->SaveTo(version->storage_info());
|
||||
version->PrepareApply(*cfd->GetLatestMutableCFOptions(), true);
|
||||
AppendVersion(cfd, version);
|
||||
active_version_builders_.erase(builder_iter);
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
}
|
||||
} else if (s.IsPathNotFound()) {
|
||||
s = Status::OK();
|
||||
@@ -6234,25 +6104,23 @@ Status ReactiveVersionSet::ApplyOneVersionEditToBuilder(
|
||||
// Some other error has occurred during LoadTableHandlers.
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
if (version_edit->HasNextFile()) {
|
||||
next_file_number_.store(version_edit->next_file_number_ + 1);
|
||||
}
|
||||
if (version_edit->has_last_sequence_) {
|
||||
last_allocated_sequence_ = version_edit->last_sequence_;
|
||||
last_published_sequence_ = version_edit->last_sequence_;
|
||||
last_sequence_ = version_edit->last_sequence_;
|
||||
}
|
||||
if (version_edit->has_prev_log_number_) {
|
||||
prev_log_number_ = version_edit->prev_log_number_;
|
||||
MarkFileNumberUsed(version_edit->prev_log_number_);
|
||||
}
|
||||
if (version_edit->has_log_number_) {
|
||||
MarkFileNumberUsed(version_edit->log_number_);
|
||||
}
|
||||
column_family_set_->UpdateMaxColumnFamily(version_edit->max_column_family_);
|
||||
MarkMinLogNumberToKeep2PC(version_edit->min_log_number_to_keep_);
|
||||
if (version_edit->HasNextFile()) {
|
||||
next_file_number_.store(version_edit->next_file_number_ + 1);
|
||||
}
|
||||
if (version_edit->has_last_sequence_) {
|
||||
last_allocated_sequence_ = version_edit->last_sequence_;
|
||||
last_published_sequence_ = version_edit->last_sequence_;
|
||||
last_sequence_ = version_edit->last_sequence_;
|
||||
}
|
||||
if (version_edit->has_prev_log_number_) {
|
||||
prev_log_number_ = version_edit->prev_log_number_;
|
||||
MarkFileNumberUsed(version_edit->prev_log_number_);
|
||||
}
|
||||
if (version_edit->has_log_number_) {
|
||||
MarkFileNumberUsed(version_edit->log_number_);
|
||||
}
|
||||
column_family_set_->UpdateMaxColumnFamily(version_edit->max_column_family_);
|
||||
MarkMinLogNumberToKeep2PC(version_edit->min_log_number_to_keep_);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+16
-59
@@ -11,9 +11,8 @@
|
||||
// newest version is called "current". Older versions may be kept
|
||||
// around to provide a consistent view to live iterators.
|
||||
//
|
||||
// Each Version keeps track of a set of table files per level, as well as a
|
||||
// set of blob files. The entire set of versions is maintained in a
|
||||
// VersionSet.
|
||||
// Each Version keeps track of a set of Table files per level. The
|
||||
// entire set of versions is maintained in a VersionSet.
|
||||
//
|
||||
// Version,VersionSet are thread-compatible, but require external
|
||||
// synchronization on all accesses.
|
||||
@@ -29,7 +28,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_file_meta.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
@@ -104,7 +102,7 @@ extern void DoGenerateLevelFilesBrief(LevelFilesBrief* file_level,
|
||||
|
||||
// Information of the storage associated with each Version, including number of
|
||||
// levels of LSM tree, files information at each level, files marked for
|
||||
// compaction, blob files, etc.
|
||||
// compaction, etc.
|
||||
class VersionStorageInfo {
|
||||
public:
|
||||
VersionStorageInfo(const InternalKeyComparator* internal_comparator,
|
||||
@@ -121,8 +119,6 @@ class VersionStorageInfo {
|
||||
|
||||
void AddFile(int level, FileMetaData* f, Logger* info_log = nullptr);
|
||||
|
||||
void AddBlobFile(std::shared_ptr<BlobFileMetaData> blob_file_meta);
|
||||
|
||||
void SetFinalized();
|
||||
|
||||
// Update num_non_empty_levels_.
|
||||
@@ -283,10 +279,6 @@ class VersionStorageInfo {
|
||||
return files_[level];
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
using BlobFiles = std::map<uint64_t, std::shared_ptr<BlobFileMetaData>>;
|
||||
const BlobFiles& GetBlobFiles() const { return blob_files_; }
|
||||
|
||||
const ROCKSDB_NAMESPACE::LevelFilesBrief& LevelFilesBrief(int level) const {
|
||||
assert(level < static_cast<int>(level_files_brief_.size()));
|
||||
return level_files_brief_[level];
|
||||
@@ -461,9 +453,6 @@ class VersionStorageInfo {
|
||||
// in increasing order of keys
|
||||
std::vector<FileMetaData*>* files_;
|
||||
|
||||
// Map of blob files in version by number.
|
||||
BlobFiles blob_files_;
|
||||
|
||||
// Level that L0 data should be compacted to. All levels < base_level_ should
|
||||
// be empty. -1 if it is not level-compaction so it's not applicable.
|
||||
int base_level_;
|
||||
@@ -564,8 +553,8 @@ class VersionStorageInfo {
|
||||
};
|
||||
|
||||
using MultiGetRange = MultiGetContext::Range;
|
||||
// A column family's version consists of the table and blob files owned by
|
||||
// the column family at a certain point in time.
|
||||
// A column family's version consists of the SST files owned by the column
|
||||
// family at a certain point in time.
|
||||
class Version {
|
||||
public:
|
||||
// Append to *iters a sequence of iterators that will
|
||||
@@ -573,13 +562,11 @@ class Version {
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
void AddIterators(const ReadOptions&, const FileOptions& soptions,
|
||||
MergeIteratorBuilder* merger_iter_builder,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
bool allow_unprepared_value);
|
||||
RangeDelAggregator* range_del_agg);
|
||||
|
||||
void AddIteratorsForLevel(const ReadOptions&, const FileOptions& soptions,
|
||||
MergeIteratorBuilder* merger_iter_builder,
|
||||
int level, RangeDelAggregator* range_del_agg,
|
||||
bool allow_unprepared_value);
|
||||
int level, RangeDelAggregator* range_del_agg);
|
||||
|
||||
Status OverlapWithLevelIterator(const ReadOptions&, const FileOptions&,
|
||||
const Slice& smallest_user_key,
|
||||
@@ -711,6 +698,10 @@ class Version {
|
||||
return storage_info_.user_comparator_;
|
||||
}
|
||||
|
||||
bool PrefixMayMatch(const ReadOptions& read_options,
|
||||
InternalIterator* level_iter,
|
||||
const Slice& internal_prefix) const;
|
||||
|
||||
// Returns true if the filter blocks in the specified level will not be
|
||||
// checked during read operations. In certain cases (trivial move or preload),
|
||||
// the filter block may already be cached, but we still do not access it such
|
||||
@@ -789,19 +780,6 @@ struct ObsoleteFileInfo {
|
||||
}
|
||||
};
|
||||
|
||||
class ObsoleteBlobFileInfo {
|
||||
public:
|
||||
ObsoleteBlobFileInfo(uint64_t blob_file_number, std::string path)
|
||||
: blob_file_number_(blob_file_number), path_(std::move(path)) {}
|
||||
|
||||
uint64_t GetBlobFileNumber() const { return blob_file_number_; }
|
||||
const std::string& GetPath() const { return path_; }
|
||||
|
||||
private:
|
||||
uint64_t blob_file_number_;
|
||||
std::string path_;
|
||||
};
|
||||
|
||||
class BaseReferencedVersionBuilder;
|
||||
|
||||
class AtomicGroupReadBuffer {
|
||||
@@ -1073,12 +1051,7 @@ class VersionSet {
|
||||
// This function doesn't support leveldb SST filenames
|
||||
void GetLiveFilesMetaData(std::vector<LiveFileMetaData> *metadata);
|
||||
|
||||
void AddObsoleteBlobFile(uint64_t blob_file_number, std::string path) {
|
||||
obsolete_blob_files_.emplace_back(blob_file_number, std::move(path));
|
||||
}
|
||||
|
||||
void GetObsoleteFiles(std::vector<ObsoleteFileInfo>* files,
|
||||
std::vector<ObsoleteBlobFileInfo>* blob_files,
|
||||
std::vector<std::string>* manifest_filenames,
|
||||
uint64_t min_pending_output);
|
||||
|
||||
@@ -1095,17 +1068,7 @@ class VersionSet {
|
||||
|
||||
static uint64_t GetTotalSstFilesSize(Version* dummy_versions);
|
||||
|
||||
// Get the IO Status returned by written Manifest.
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
// Set the IO Status to OK. Called before Manifest write if needed.
|
||||
void SetIOStatusOK() { io_status_ = IOStatus::OK(); }
|
||||
|
||||
protected:
|
||||
using VersionBuilderMap =
|
||||
std::unordered_map<uint32_t,
|
||||
std::unique_ptr<BaseReferencedVersionBuilder>>;
|
||||
|
||||
struct ManifestWriter;
|
||||
|
||||
friend class Version;
|
||||
@@ -1117,9 +1080,7 @@ class VersionSet {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Status* status;
|
||||
virtual void Corruption(size_t /*bytes*/, const Status& s) override {
|
||||
if (status->ok()) {
|
||||
*status = s;
|
||||
}
|
||||
if (this->status->ok()) *this->status = s;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1150,14 +1111,13 @@ class VersionSet {
|
||||
const VersionEdit* edit);
|
||||
|
||||
Status ReadAndRecover(
|
||||
log::Reader& reader, AtomicGroupReadBuffer* read_buffer,
|
||||
log::Reader* reader, AtomicGroupReadBuffer* read_buffer,
|
||||
const std::unordered_map<std::string, ColumnFamilyOptions>&
|
||||
name_to_options,
|
||||
std::unordered_map<int, std::string>& column_families_not_found,
|
||||
std::unordered_map<
|
||||
uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>& builders,
|
||||
Status* log_read_status, VersionEditParams* version_edit,
|
||||
std::string* db_id = nullptr);
|
||||
VersionEditParams* version_edit, std::string* db_id = nullptr);
|
||||
|
||||
// REQUIRES db mutex
|
||||
Status ApplyOneVersionEditToBuilder(
|
||||
@@ -1219,7 +1179,6 @@ class VersionSet {
|
||||
uint64_t manifest_file_size_;
|
||||
|
||||
std::vector<ObsoleteFileInfo> obsolete_files_;
|
||||
std::vector<ObsoleteBlobFileInfo> obsolete_blob_files_;
|
||||
std::vector<std::string> obsolete_manifests_;
|
||||
|
||||
// env options for all reads and writes except compactions
|
||||
@@ -1227,9 +1186,6 @@ class VersionSet {
|
||||
|
||||
BlockCacheTracer* const block_cache_tracer_;
|
||||
|
||||
// Store the IO status when Manifest is written
|
||||
IOStatus io_status_;
|
||||
|
||||
private:
|
||||
// REQUIRES db mutex at beginning. may release and re-acquire db mutex
|
||||
Status ProcessManifestWrites(std::deque<ManifestWriter>& writers,
|
||||
@@ -1286,7 +1242,8 @@ class ReactiveVersionSet : public VersionSet {
|
||||
std::unique_ptr<log::FragmentBufferedReader>* manifest_reader);
|
||||
|
||||
private:
|
||||
VersionBuilderMap active_version_builders_;
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>
|
||||
active_version_builders_;
|
||||
AtomicGroupReadBuffer read_buffer_;
|
||||
// Number of version edits to skip by ReadAndApply at the beginning of a new
|
||||
// MANIFEST created by primary.
|
||||
|
||||
+23
-240
@@ -96,7 +96,7 @@ Options GetOptionsWithNumLevels(int num_levels,
|
||||
return opt;
|
||||
}
|
||||
|
||||
class VersionStorageInfoTestBase : public testing::Test {
|
||||
class VersionStorageInfoTest : public testing::Test {
|
||||
public:
|
||||
const Comparator* ucmp_;
|
||||
InternalKeyComparator icmp_;
|
||||
@@ -111,19 +111,17 @@ class VersionStorageInfoTestBase : public testing::Test {
|
||||
return InternalKey(ukey, smallest_seq, kTypeValue);
|
||||
}
|
||||
|
||||
explicit VersionStorageInfoTestBase(const Comparator* ucmp)
|
||||
: ucmp_(ucmp),
|
||||
VersionStorageInfoTest()
|
||||
: ucmp_(BytewiseComparator()),
|
||||
icmp_(ucmp_),
|
||||
logger_(new CountingLogger()),
|
||||
options_(GetOptionsWithNumLevels(6, logger_)),
|
||||
ioptions_(options_),
|
||||
mutable_cf_options_(options_),
|
||||
vstorage_(&icmp_, ucmp_, 6, kCompactionStyleLevel,
|
||||
/*src_vstorage=*/nullptr,
|
||||
/*_force_consistency_checks=*/false) {}
|
||||
vstorage_(&icmp_, ucmp_, 6, kCompactionStyleLevel, nullptr, false) {}
|
||||
|
||||
~VersionStorageInfoTestBase() override {
|
||||
for (int i = 0; i < vstorage_.num_levels(); ++i) {
|
||||
~VersionStorageInfoTest() override {
|
||||
for (int i = 0; i < vstorage_.num_levels(); i++) {
|
||||
for (auto* f : vstorage_.LevelFiles(i)) {
|
||||
if (--f->refs == 0) {
|
||||
delete f;
|
||||
@@ -174,13 +172,6 @@ class VersionStorageInfoTestBase : public testing::Test {
|
||||
}
|
||||
};
|
||||
|
||||
class VersionStorageInfoTest : public VersionStorageInfoTestBase {
|
||||
public:
|
||||
VersionStorageInfoTest() : VersionStorageInfoTestBase(BytewiseComparator()) {}
|
||||
|
||||
~VersionStorageInfoTest() override {}
|
||||
};
|
||||
|
||||
TEST_F(VersionStorageInfoTest, MaxBytesForLevelStatic) {
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10;
|
||||
@@ -421,54 +412,6 @@ TEST_F(VersionStorageInfoTest, GetOverlappingInputs) {
|
||||
1, {"i", 0, kTypeValue}, {"j", 0, kTypeValue}));
|
||||
}
|
||||
|
||||
class VersionStorageInfoTimestampTest : public VersionStorageInfoTestBase {
|
||||
public:
|
||||
VersionStorageInfoTimestampTest()
|
||||
: VersionStorageInfoTestBase(test::ComparatorWithU64Ts()) {}
|
||||
~VersionStorageInfoTimestampTest() override {}
|
||||
std::string Timestamp(uint64_t ts) const {
|
||||
std::string ret;
|
||||
PutFixed64(&ret, ts);
|
||||
return ret;
|
||||
}
|
||||
std::string PackUserKeyAndTimestamp(const Slice& ukey, uint64_t ts) const {
|
||||
std::string ret;
|
||||
ret.assign(ukey.data(), ukey.size());
|
||||
PutFixed64(&ret, ts);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(VersionStorageInfoTimestampTest, GetOverlappingInputs) {
|
||||
Add(/*level=*/1, /*file_number=*/1, /*smallest=*/
|
||||
{PackUserKeyAndTimestamp("a", /*ts=*/9), /*s=*/0, kTypeValue},
|
||||
/*largest=*/
|
||||
{PackUserKeyAndTimestamp("a", /*ts=*/8), /*s=*/0, kTypeValue},
|
||||
/*file_size=*/100);
|
||||
Add(/*level=*/1, /*file_number=*/2, /*smallest=*/
|
||||
{PackUserKeyAndTimestamp("a", /*ts=*/5), /*s=*/0, kTypeValue},
|
||||
/*largest=*/
|
||||
{PackUserKeyAndTimestamp("b", /*ts=*/10), /*s=*/0, kTypeValue},
|
||||
/*file_size=*/100);
|
||||
Add(/*level=*/1, /*file_number=*/3, /*smallest=*/
|
||||
{PackUserKeyAndTimestamp("c", /*ts=*/12), /*s=*/0, kTypeValue},
|
||||
/*largest=*/
|
||||
{PackUserKeyAndTimestamp("d", /*ts=*/1), /*s=*/0, kTypeValue},
|
||||
/*file_size=*/100);
|
||||
vstorage_.UpdateNumNonEmptyLevels();
|
||||
vstorage_.GenerateLevelFilesBrief();
|
||||
ASSERT_EQ(
|
||||
"1,2",
|
||||
GetOverlappingFiles(
|
||||
/*level=*/1,
|
||||
{PackUserKeyAndTimestamp("a", /*ts=*/12), /*s=*/0, kTypeValue},
|
||||
{PackUserKeyAndTimestamp("a", /*ts=*/11), /*s=*/0, kTypeValue}));
|
||||
ASSERT_EQ("3",
|
||||
GetOverlappingFiles(
|
||||
/*level=*/1,
|
||||
{PackUserKeyAndTimestamp("c", /*ts=*/15), /*s=*/0, kTypeValue},
|
||||
{PackUserKeyAndTimestamp("c", /*ts=*/2), /*s=*/0, kTypeValue}));
|
||||
}
|
||||
|
||||
class FindLevelFileTest : public testing::Test {
|
||||
public:
|
||||
@@ -801,7 +744,7 @@ class VersionSetTestBase {
|
||||
PrepareManifest(&column_families, &last_seqno, &log_writer);
|
||||
log_writer.reset();
|
||||
// Make "CURRENT" file point to the new manifest file.
|
||||
Status s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
Status s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
ASSERT_OK(s);
|
||||
|
||||
EXPECT_OK(versions_->Recover(column_families, false));
|
||||
@@ -884,164 +827,6 @@ TEST_F(VersionSetTest, SameColumnFamilyGroupCommit) {
|
||||
EXPECT_EQ(kGroupSize - 1, count);
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTest, PersistBlobFileStateInNewManifest) {
|
||||
// Initialize the database and add a couple of blob files, one with some
|
||||
// garbage in it, and one without any garbage.
|
||||
NewDB();
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
{
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
constexpr uint64_t total_blob_count = 456;
|
||||
constexpr uint64_t total_blob_bytes = 77777777;
|
||||
constexpr char checksum_method[] = "SHA1";
|
||||
constexpr char checksum_value[] =
|
||||
"bdb7f34a59dfa1592ce7f52e99f98c570c525cbd";
|
||||
constexpr uint64_t garbage_blob_count = 89;
|
||||
constexpr uint64_t garbage_blob_bytes = 1000000;
|
||||
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
edit.AddBlobFileGarbage(blob_file_number, garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
}
|
||||
|
||||
{
|
||||
constexpr uint64_t blob_file_number = 234;
|
||||
constexpr uint64_t total_blob_count = 555;
|
||||
constexpr uint64_t total_blob_bytes = 66666;
|
||||
constexpr char checksum_method[] = "CRC32";
|
||||
constexpr char checksum_value[] = "3d87ff57";
|
||||
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
}
|
||||
|
||||
assert(versions_);
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
|
||||
mutex_.Lock();
|
||||
Status s =
|
||||
versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options_, &edit, &mutex_);
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Force the creation of a new manifest file and make sure metadata for
|
||||
// the blob files is re-persisted.
|
||||
size_t addition_encoded = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileAddition::EncodeTo::CustomFields",
|
||||
[&](void* /* arg */) { ++addition_encoded; });
|
||||
|
||||
size_t garbage_encoded = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileGarbage::EncodeTo::CustomFields",
|
||||
[&](void* /* arg */) { ++garbage_encoded; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
VersionEdit dummy;
|
||||
|
||||
mutex_.Lock();
|
||||
constexpr FSDirectory* db_directory = nullptr;
|
||||
constexpr bool new_descriptor_log = true;
|
||||
s = versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options_, &dummy, &mutex_, db_directory,
|
||||
new_descriptor_log);
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(addition_encoded, 2);
|
||||
ASSERT_EQ(garbage_encoded, 1);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTest, ObsoleteBlobFile) {
|
||||
// Initialize the database and add a blob file (with no garbage just yet).
|
||||
NewDB();
|
||||
|
||||
VersionEdit addition;
|
||||
|
||||
constexpr uint64_t blob_file_number = 234;
|
||||
constexpr uint64_t total_blob_count = 555;
|
||||
constexpr uint64_t total_blob_bytes = 66666;
|
||||
constexpr char checksum_method[] = "CRC32";
|
||||
constexpr char checksum_value[] = "3d87ff57";
|
||||
|
||||
addition.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
assert(versions_);
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
|
||||
mutex_.Lock();
|
||||
Status s =
|
||||
versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options_, &addition, &mutex_);
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Mark the entire blob file garbage.
|
||||
VersionEdit garbage;
|
||||
|
||||
garbage.AddBlobFileGarbage(blob_file_number, total_blob_count,
|
||||
total_blob_bytes);
|
||||
|
||||
mutex_.Lock();
|
||||
s = versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options_, &garbage, &mutex_);
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Make sure blob files from the pending number range are not returned
|
||||
// as obsolete.
|
||||
{
|
||||
std::vector<ObsoleteFileInfo> table_files;
|
||||
std::vector<ObsoleteBlobFileInfo> blob_files;
|
||||
std::vector<std::string> manifest_files;
|
||||
constexpr uint64_t min_pending_output = blob_file_number;
|
||||
|
||||
versions_->GetObsoleteFiles(&table_files, &blob_files, &manifest_files,
|
||||
min_pending_output);
|
||||
|
||||
ASSERT_TRUE(blob_files.empty());
|
||||
}
|
||||
|
||||
// Make sure the blob file is returned as obsolete if it's not in the pending
|
||||
// range.
|
||||
{
|
||||
std::vector<ObsoleteFileInfo> table_files;
|
||||
std::vector<ObsoleteBlobFileInfo> blob_files;
|
||||
std::vector<std::string> manifest_files;
|
||||
constexpr uint64_t min_pending_output = blob_file_number + 1;
|
||||
|
||||
versions_->GetObsoleteFiles(&table_files, &blob_files, &manifest_files,
|
||||
min_pending_output);
|
||||
|
||||
ASSERT_EQ(blob_files.size(), 1);
|
||||
ASSERT_EQ(blob_files[0].GetBlobFileNumber(), blob_file_number);
|
||||
}
|
||||
|
||||
// Make sure it's not returned a second time.
|
||||
{
|
||||
std::vector<ObsoleteFileInfo> table_files;
|
||||
std::vector<ObsoleteBlobFileInfo> blob_files;
|
||||
std::vector<std::string> manifest_files;
|
||||
constexpr uint64_t min_pending_output = blob_file_number + 1;
|
||||
|
||||
versions_->GetObsoleteFiles(&table_files, &blob_files, &manifest_files,
|
||||
min_pending_output);
|
||||
|
||||
ASSERT_TRUE(blob_files.empty());
|
||||
}
|
||||
}
|
||||
|
||||
class VersionSetAtomicGroupTest : public VersionSetTestBase,
|
||||
public testing::Test {
|
||||
public:
|
||||
@@ -1062,7 +847,7 @@ class VersionSetAtomicGroupTest : public VersionSetTestBase,
|
||||
edits_[i].MarkAtomicGroup(--remaining);
|
||||
edits_[i].SetLastSequence(last_seqno_++);
|
||||
}
|
||||
ASSERT_OK(SetCurrentFile(fs_.get(), dbname_, 1, nullptr));
|
||||
ASSERT_OK(SetCurrentFile(env_, dbname_, 1, nullptr));
|
||||
}
|
||||
|
||||
void SetupIncompleteTrailingAtomicGroup(int atomic_group_size) {
|
||||
@@ -1074,7 +859,7 @@ class VersionSetAtomicGroupTest : public VersionSetTestBase,
|
||||
edits_[i].MarkAtomicGroup(--remaining);
|
||||
edits_[i].SetLastSequence(last_seqno_++);
|
||||
}
|
||||
ASSERT_OK(SetCurrentFile(fs_.get(), dbname_, 1, nullptr));
|
||||
ASSERT_OK(SetCurrentFile(env_, dbname_, 1, nullptr));
|
||||
}
|
||||
|
||||
void SetupCorruptedAtomicGroup(int atomic_group_size) {
|
||||
@@ -1088,7 +873,7 @@ class VersionSetAtomicGroupTest : public VersionSetTestBase,
|
||||
}
|
||||
edits_[i].SetLastSequence(last_seqno_++);
|
||||
}
|
||||
ASSERT_OK(SetCurrentFile(fs_.get(), dbname_, 1, nullptr));
|
||||
ASSERT_OK(SetCurrentFile(env_, dbname_, 1, nullptr));
|
||||
}
|
||||
|
||||
void SetupIncorrectAtomicGroup(int atomic_group_size) {
|
||||
@@ -1104,7 +889,7 @@ class VersionSetAtomicGroupTest : public VersionSetTestBase,
|
||||
}
|
||||
edits_[i].SetLastSequence(last_seqno_++);
|
||||
}
|
||||
ASSERT_OK(SetCurrentFile(fs_.get(), dbname_, 1, nullptr));
|
||||
ASSERT_OK(SetCurrentFile(env_, dbname_, 1, nullptr));
|
||||
}
|
||||
|
||||
void SetupTestSyncPoints() {
|
||||
@@ -1370,7 +1155,7 @@ TEST_F(VersionSetAtomicGroupTest,
|
||||
// Write the corrupted edits.
|
||||
AddNewEditsToLog(kAtomicGroupSize);
|
||||
mu.Lock();
|
||||
EXPECT_NOK(
|
||||
EXPECT_OK(
|
||||
reactive_versions_->ReadAndApply(&mu, &manifest_reader, &cfds_changed));
|
||||
mu.Unlock();
|
||||
EXPECT_EQ(edits_[kAtomicGroupSize / 2].DebugString(),
|
||||
@@ -1420,7 +1205,7 @@ TEST_F(VersionSetAtomicGroupTest,
|
||||
&manifest_reader_status));
|
||||
AddNewEditsToLog(kAtomicGroupSize);
|
||||
mu.Lock();
|
||||
EXPECT_NOK(
|
||||
EXPECT_OK(
|
||||
reactive_versions_->ReadAndApply(&mu, &manifest_reader, &cfds_changed));
|
||||
mu.Unlock();
|
||||
EXPECT_EQ(edits_[1].DebugString(),
|
||||
@@ -1456,7 +1241,7 @@ TEST_P(VersionSetTestDropOneCF, HandleDroppedColumnFamilyInAtomicGroup) {
|
||||
SequenceNumber last_seqno;
|
||||
std::unique_ptr<log::Writer> log_writer;
|
||||
PrepareManifest(&column_families, &last_seqno, &log_writer);
|
||||
Status s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
Status s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
ASSERT_OK(s);
|
||||
|
||||
EXPECT_OK(versions_->Recover(column_families, false /* read_only */));
|
||||
@@ -1593,8 +1378,7 @@ class EmptyDefaultCfNewManifest : public VersionSetTestBase,
|
||||
TEST_F(EmptyDefaultCfNewManifest, Recover) {
|
||||
PrepareManifest(nullptr, nullptr, &log_writer_);
|
||||
log_writer_.reset();
|
||||
Status s =
|
||||
SetCurrentFile(fs_.get(), dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
Status s = SetCurrentFile(env_, dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
ASSERT_OK(s);
|
||||
std::string manifest_path;
|
||||
VerifyManifest(&manifest_path);
|
||||
@@ -1656,8 +1440,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromIncompleteManifest0) {
|
||||
db_options_.write_dbid_to_manifest = std::get<0>(GetParam());
|
||||
PrepareManifest(nullptr, nullptr, &log_writer_);
|
||||
log_writer_.reset();
|
||||
Status s =
|
||||
SetCurrentFile(fs_.get(), dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
Status s = SetCurrentFile(env_, dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
ASSERT_OK(s);
|
||||
|
||||
std::string manifest_path;
|
||||
@@ -1700,7 +1483,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromIncompleteManifest1) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
log_writer_.reset();
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
s = SetCurrentFile(env_, dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
ASSERT_OK(s);
|
||||
|
||||
std::string manifest_path;
|
||||
@@ -1746,7 +1529,7 @@ TEST_P(VersionSetTestEmptyDb, OpenFromInCompleteManifest2) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
log_writer_.reset();
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
s = SetCurrentFile(env_, dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
ASSERT_OK(s);
|
||||
|
||||
std::string manifest_path;
|
||||
@@ -1803,7 +1586,7 @@ TEST_P(VersionSetTestEmptyDb, OpenManifestWithUnknownCF) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
log_writer_.reset();
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
s = SetCurrentFile(env_, dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
ASSERT_OK(s);
|
||||
|
||||
std::string manifest_path;
|
||||
@@ -1859,7 +1642,7 @@ TEST_P(VersionSetTestEmptyDb, OpenCompleteManifest) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
log_writer_.reset();
|
||||
s = SetCurrentFile(fs_.get(), dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
s = SetCurrentFile(env_, dbname_, 1, /*directory_to_fsync=*/nullptr);
|
||||
ASSERT_OK(s);
|
||||
|
||||
std::string manifest_path;
|
||||
@@ -2118,7 +1901,7 @@ TEST_F(VersionSetTestMissingFiles, ManifestFarBehindSst) {
|
||||
WriteFileAdditionAndDeletionToManifest(
|
||||
/*cf=*/0, std::vector<std::pair<int, FileMetaData>>(), deleted_files);
|
||||
log_writer_.reset();
|
||||
Status s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
Status s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
ASSERT_OK(s);
|
||||
std::string manifest_path;
|
||||
VerifyManifest(&manifest_path);
|
||||
@@ -2169,7 +1952,7 @@ TEST_F(VersionSetTestMissingFiles, ManifestAheadofSst) {
|
||||
WriteFileAdditionAndDeletionToManifest(
|
||||
/*cf=*/0, added_files, std::vector<std::pair<int, uint64_t>>());
|
||||
log_writer_.reset();
|
||||
Status s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
Status s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
ASSERT_OK(s);
|
||||
std::string manifest_path;
|
||||
VerifyManifest(&manifest_path);
|
||||
@@ -2218,7 +2001,7 @@ TEST_F(VersionSetTestMissingFiles, NoFileMissing) {
|
||||
WriteFileAdditionAndDeletionToManifest(
|
||||
/*cf=*/0, std::vector<std::pair<int, FileMetaData>>(), deleted_files);
|
||||
log_writer_.reset();
|
||||
Status s = SetCurrentFile(fs_.get(), dbname_, 1, nullptr);
|
||||
Status s = SetCurrentFile(env_, dbname_, 1, nullptr);
|
||||
ASSERT_OK(s);
|
||||
std::string manifest_path;
|
||||
VerifyManifest(&manifest_path);
|
||||
|
||||
+4
-2
@@ -334,8 +334,10 @@ Status WalManager::GetSortedWalsOfType(const std::string& path,
|
||||
std::sort(
|
||||
log_files.begin(), log_files.end(),
|
||||
[](const std::unique_ptr<LogFile>& a, const std::unique_ptr<LogFile>& b) {
|
||||
LogFileImpl* a_impl = static_cast_with_check<LogFileImpl>(a.get());
|
||||
LogFileImpl* b_impl = static_cast_with_check<LogFileImpl>(b.get());
|
||||
LogFileImpl* a_impl =
|
||||
static_cast_with_check<LogFileImpl, LogFile>(a.get());
|
||||
LogFileImpl* b_impl =
|
||||
static_cast_with_check<LogFileImpl, LogFile>(b.get());
|
||||
return *a_impl < *b_impl;
|
||||
});
|
||||
return status;
|
||||
|
||||
Loaded 100 of 349 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user