mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
201cf642cc | ||
|
|
fe25901e2a | ||
|
|
1a9412ef4f | ||
|
|
2a1c9ab108 | ||
|
|
06858ac58d | ||
|
|
dbd8fa09b8 | ||
|
|
cda4242396 | ||
|
|
cfbba44c50 | ||
|
|
1983e301e0 | ||
|
|
4c2bda9083 | ||
|
|
67417da28b | ||
|
|
04cf8367af | ||
|
|
5c7fc55012 | ||
|
|
37a5d3d2aa | ||
|
|
25ac8ca06d | ||
|
|
45328eca08 | ||
|
|
ee6f4fa6f5 | ||
|
|
d33f155d8c | ||
|
|
935e834534 | ||
|
|
20269d080a | ||
|
|
8cffe39efc | ||
|
|
df23e80e5f | ||
|
|
1fe7db5fb8 | ||
|
|
7bcf290620 | ||
|
|
22a24c0294 | ||
|
|
c33a483f11 | ||
|
|
880cf72fec | ||
|
|
326389b6b4 |
No files matched your search
+21
-44
@@ -34,15 +34,12 @@
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(rocksdb)
|
||||
enable_language(CXX)
|
||||
enable_language(C)
|
||||
enable_language(ASM)
|
||||
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
||||
|
||||
option(WITH_JEMALLOC "build with JeMalloc" OFF)
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
@@ -132,19 +129,20 @@ endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
set(SH_CMD "sh")
|
||||
execute_process(COMMAND
|
||||
${SH_CMD} -c "build_tools/version.sh full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION}" ROCKSDB_VERSION)
|
||||
execute_process(COMMAND
|
||||
${SH_CMD} -c "build_tools/version.sh major"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION_MAJOR
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
if(NOT WIN32)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION}" ROCKSDB_VERSION)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "major"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION_MAJOR
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
endif()
|
||||
|
||||
option(WITH_MD_LIBRARY "build with MD" ON)
|
||||
if(WIN32 AND MSVC)
|
||||
@@ -180,18 +178,6 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
CHECK_C_COMPILER_FLAG("-maltivec" HAS_ALTIVEC)
|
||||
if(HAS_ALTIVEC)
|
||||
message(STATUS " HAS_ALTIVEC yes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maltivec")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maltivec")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=power8")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=power8")
|
||||
endif(HAS_ALTIVEC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
@@ -204,9 +190,7 @@ else()
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
if(NOT HAVE_POWER8)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -643,12 +627,6 @@ set_source_files_properties(
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_POWER8)
|
||||
list(APPEND SOURCES
|
||||
util/crc32c_ppc.c
|
||||
util/crc32c_ppc_asm.S)
|
||||
endif(HAVE_POWER8)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES
|
||||
port/win/io_win.cc
|
||||
@@ -743,7 +721,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
set(package_config_destination ${CMAKE_INSTALL_LIBDIR}/cmake/rocksdb)
|
||||
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/cmake/RocksDBConfig.cmake.in RocksDBConfig.cmake
|
||||
${CMAKE_SOURCE_DIR}/cmake/RocksDBConfig.cmake.in RocksDBConfig.cmake
|
||||
INSTALL_DESTINATION ${package_config_destination}
|
||||
)
|
||||
|
||||
@@ -973,18 +951,17 @@ if(WITH_TESTS)
|
||||
|
||||
foreach(sourcefile ${TEST_EXES})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
$<TARGET_OBJECTS:testharness>)
|
||||
set_target_properties(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX}
|
||||
set_target_properties(${exename}${ARTIFACT_SUFFIX}
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
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} gtest ${LIBS})
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} gtest ${LIBS})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
endforeach(sourcefile ${TEST_EXES})
|
||||
|
||||
|
||||
+9
-43
@@ -1,51 +1,15 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.12.5 (6/18/2018)
|
||||
### Bug Fixes
|
||||
* Fix regression bug of Prev() with ReadOptions.iterate_upper_bound.
|
||||
|
||||
## 5.12.3 (04/09/2018)
|
||||
### Bug Fixes
|
||||
* Fix memory leak when pin_l0_filter_and_index_blocks_in_cache is used with partitioned filters
|
||||
* Fix memory leak in two_level_iterator
|
||||
|
||||
## 5.12.2 (3/23/2018)
|
||||
### Bug Fixes
|
||||
* Fsync after writing global seq number to the ingestion file in ExternalSstFileIngestionJob.
|
||||
|
||||
### Java API Changes
|
||||
* Add `BlockBasedTableConfig.setBlockCache` to allow sharing a block cache across DB instances.
|
||||
|
||||
## 5.12.1 (3/16/2018)
|
||||
### Public API Change
|
||||
* RocksDBOptionsParser::Parse()'s `ignore_unknown_options` argument will only be effective if the option file shows it is generated using a higher version of RocksDB than the current version.
|
||||
|
||||
### New Features
|
||||
* Avoid unnecessarily flushing in `CompactRange()` when the range specified by the user does not overlap unflushed memtables.
|
||||
|
||||
## 5.11.4 (03/16/2018)
|
||||
### Bug Fixes
|
||||
* Fix WAL corruption caused by race condition between user write thread and backup/checkpoint thread.
|
||||
|
||||
## 5.12.0 (2/14/2018)
|
||||
### Public API Change
|
||||
* Iterator::SeekForPrev is now a pure virtual method. This is to prevent user who implement the Iterator interface fail to implement SeekForPrev by mistake.
|
||||
* Add `include_end` option to make the range end exclusive when `include_end == false` in `DeleteFilesInRange()`.
|
||||
* Add `CompactRangeOptions::allow_write_stall`, which makes `CompactRange` start working immediately, even if it causes user writes to stall. The default value is false, meaning we add delay to `CompactRange` calls until stalling can be avoided when possible. Note this delay is not present in previous RocksDB versions.
|
||||
* Creating checkpoint with empty directory now returns `Status::InvalidArgument`; previously, it returned `Status::IOError`.
|
||||
* Adds a BlockBasedTableOption to turn off index block compression.
|
||||
* Close() method now returns a status when closing a db.
|
||||
|
||||
### New Features
|
||||
* Improve the performance of iterators doing long range scans by using readahead.
|
||||
* Add new function `DeleteFilesInRanges()` to delete files in multiple ranges at once for better performance.
|
||||
* FreeBSD build support for RocksDB and RocksJava.
|
||||
* Improved performance of long range scans with readahead.
|
||||
* Updated to and now continuously tested in Visual Studio 2017.
|
||||
|
||||
## 5.11.2 (02/24/2018)
|
||||
### Bug Fixes
|
||||
* Fix `DisableFileDeletions()` followed by `GetSortedWalFiles()` to not return obsolete WAL files that `PurgeObsoleteFiles()` is going to delete.
|
||||
* Fix Handle error return from WriteBuffer() during WAL file close and DB close.
|
||||
* Fix advance reservation of arena block addresses.
|
||||
* Fix handling of empty string as checkpoint directory.
|
||||
* Fix bug in iterator readahead causing blocks to incorrectly be considered truncated (corrupted).
|
||||
|
||||
## 5.11.1 (02/22/2018)
|
||||
### New Features
|
||||
* Follow rsync-style naming convention for BackupEngine tempfiles. This enables some optimizations when run on GlusterFS.
|
||||
|
||||
## 5.11.0 (01/08/2018)
|
||||
### Public API Change
|
||||
@@ -72,6 +36,8 @@
|
||||
* Provide lifetime hints when writing files on Linux. This reduces hardware write-amp on storage devices supporting multiple streams.
|
||||
* Add a DB stat, `NUMBER_ITER_SKIP`, which returns how many internal keys were skipped during iterations (e.g., due to being tombstones or duplicate versions of a key).
|
||||
* Add PerfContext counters, `key_lock_wait_count` and `key_lock_wait_time`, which measure the number of times transactions wait on key locks and total amount of time waiting.
|
||||
* Support dynamically changing `ColumnFamilyOptions::compaction_options_universal`.
|
||||
* Batch update stats at the end of each `Get`, rather than for each block cache access.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix IOError on WAL write doesn't propagate to write group follower
|
||||
|
||||
@@ -15,4 +15,3 @@ This is the list of all known third-party language bindings for RocksDB. If some
|
||||
* D programming language - https://github.com/b1naryth1ef/rocksdb
|
||||
* Erlang - https://gitlab.com/barrel-db/erlang-rocksdb
|
||||
* Elixir - https://github.com/urbint/rox
|
||||
* Nim - https://github.com/status-im/nim-rocksdb
|
||||
@@ -232,13 +232,9 @@ endif
|
||||
# USAN doesn't work well with jemalloc. If we're compiling with USAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_UBSAN
|
||||
DISABLE_JEMALLOC=1
|
||||
# Suppress alignment warning because murmurhash relies on casting unaligned
|
||||
# memory to integer. Fixing it may cause performance regression. 3-way crc32
|
||||
# relies on it too, although it can be rewritten to eliminate with minimal
|
||||
# performance regression.
|
||||
EXEC_LDFLAGS += -fsanitize=undefined -fno-sanitize-recover=all
|
||||
PLATFORM_CCFLAGS += -fsanitize=undefined -fno-sanitize-recover=all -DROCKSDB_UBSAN_RUN
|
||||
PLATFORM_CXXFLAGS += -fsanitize=undefined -fno-sanitize-recover=all -DROCKSDB_UBSAN_RUN
|
||||
EXEC_LDFLAGS += -fsanitize=undefined
|
||||
PLATFORM_CCFLAGS += -fsanitize=undefined -DROCKSDB_UBSAN_RUN
|
||||
PLATFORM_CXXFLAGS += -fsanitize=undefined -DROCKSDB_UBSAN_RUN
|
||||
endif
|
||||
|
||||
ifdef ROCKSDB_VALGRIND_RUN
|
||||
@@ -527,10 +523,6 @@ PARALLEL_TEST = \
|
||||
transaction_test \
|
||||
write_prepared_transaction_test \
|
||||
|
||||
# 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')
|
||||
endif
|
||||
SUBSET := $(TESTS)
|
||||
ifdef ROCKSDBTESTS_START
|
||||
SUBSET := $(shell echo $(SUBSET) | sed 's/^.*$(ROCKSDBTESTS_START)/$(ROCKSDBTESTS_START)/')
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](http://140.211.168.68:8080/job/Rocksdb)
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
|
||||
Vendored
-5
@@ -14,11 +14,6 @@ Vagrant.configure("2") do |config|
|
||||
box.vm.box = "chef/centos-6.5"
|
||||
end
|
||||
|
||||
config.vm.define "centos7" do |box|
|
||||
box.vm.box = "centos/7"
|
||||
box.vm.provision "shell", path: "build_tools/setup_centos7.sh"
|
||||
end
|
||||
|
||||
config.vm.define "FreeBSD10" do |box|
|
||||
box.vm.guest = :freebsd
|
||||
box.vm.box = "robin/freebsd-10"
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
version: 1.0.{build}
|
||||
image: Visual Studio 2017
|
||||
image: Visual Studio 2015
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 ..
|
||||
- cmake -G "Visual Studio 14 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
|
||||
@@ -353,22 +353,6 @@ LITE_BUILD_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# Report RocksDB lite binary size to scuba
|
||||
REPORT_LITE_BINARY_SIZE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Lite Binary Size',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Report RocksDB Lite binary size to scuba',
|
||||
'shell':'tools/report_lite_binary_size.sh',
|
||||
'user':'root',
|
||||
},
|
||||
],
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB stress/crash test
|
||||
#
|
||||
@@ -745,9 +729,6 @@ case $1 in
|
||||
lite)
|
||||
echo $LITE_BUILD_COMMANDS
|
||||
;;
|
||||
report_lite_binary_size)
|
||||
echo $REPORT_LITE_BINARY_SIZE_COMMANDS
|
||||
;;
|
||||
stress_crash)
|
||||
echo $STRESS_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
ROCKSDB_VERSION="5.10.3"
|
||||
ZSTD_VERSION="1.1.3"
|
||||
|
||||
echo "This script configures CentOS with everything needed to build and run RocksDB"
|
||||
|
||||
yum update -y && yum install epel-release -y
|
||||
|
||||
yum install -y \
|
||||
wget \
|
||||
gcc-c++ \
|
||||
snappy snappy-devel \
|
||||
zlib zlib-devel \
|
||||
bzip2 bzip2-devel \
|
||||
lz4-devel \
|
||||
libasan \
|
||||
gflags
|
||||
|
||||
mkdir -pv /usr/local/rocksdb-${ROCKSDB_VERSION}
|
||||
ln -sfT /usr/local/rocksdb-${ROCKSDB_VERSION} /usr/local/rocksdb
|
||||
|
||||
wget -qO /tmp/zstd-${ZSTD_VERSION}.tar.gz https://github.com/facebook/zstd/archive/v${ZSTD_VERSION}.tar.gz
|
||||
wget -qO /tmp/rocksdb-${ROCKSDB_VERSION}.tar.gz https://github.com/facebook/rocksdb/archive/v${ROCKSDB_VERSION}.tar.gz
|
||||
|
||||
cd /tmp
|
||||
|
||||
tar xzvf zstd-${ZSTD_VERSION}.tar.gz
|
||||
tar xzvf rocksdb-${ROCKSDB_VERSION}.tar.gz -C /usr/local/
|
||||
|
||||
echo "Installing ZSTD..."
|
||||
pushd zstd-${ZSTD_VERSION}
|
||||
make && make install
|
||||
popd
|
||||
|
||||
echo "Compiling RocksDB..."
|
||||
cd /usr/local/rocksdb
|
||||
chown -R vagrant:vagrant /usr/local/rocksdb/
|
||||
sudo -u vagrant make static_lib
|
||||
cd examples/
|
||||
sudo -u vagrant make all
|
||||
sudo -u vagrant ./c_simple_example
|
||||
Vendored
+1
-1
@@ -57,7 +57,7 @@ struct LRUHandle {
|
||||
// Include the following flags:
|
||||
// in_cache: whether this entry is referenced by the hash table.
|
||||
// is_high_pri: whether this entry is high priority entry.
|
||||
// in_high_pri_pool: whether this entry is in high-pri pool.
|
||||
// in_high_pro_pool: whether this entry is in high-pri pool.
|
||||
char flags;
|
||||
|
||||
uint32_t hash; // Hash of key(); used for fast sharding and comparisons
|
||||
|
||||
+1
-2
@@ -131,8 +131,7 @@ Status BuildTable(
|
||||
MergeHelper merge(env, internal_comparator.user_comparator(),
|
||||
ioptions.merge_operator, nullptr, ioptions.info_log,
|
||||
true /* internal key corruption is not ok */,
|
||||
snapshots.empty() ? 0 : snapshots.back(),
|
||||
snapshot_checker);
|
||||
snapshots.empty() ? 0 : snapshots.back());
|
||||
|
||||
CompactionIterator c_iter(
|
||||
iter, internal_comparator.user_comparator(), &merge, kMaxSequenceNumber,
|
||||
|
||||
+33
-128
@@ -31,7 +31,6 @@
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/compression.h"
|
||||
|
||||
@@ -383,7 +382,6 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
next_(nullptr),
|
||||
prev_(nullptr),
|
||||
log_number_(0),
|
||||
flush_reason_(FlushReason::kUnknown),
|
||||
column_family_set_(column_family_set),
|
||||
pending_flush_(false),
|
||||
pending_compaction_(false),
|
||||
@@ -472,7 +470,7 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
local_sv_.reset();
|
||||
super_version_->db_mutex->Lock();
|
||||
|
||||
bool is_last_reference __attribute__((__unused__));
|
||||
bool is_last_reference __attribute__((unused));
|
||||
is_last_reference = super_version_->Unref();
|
||||
assert(is_last_reference);
|
||||
super_version_->Cleanup();
|
||||
@@ -483,7 +481,7 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
if (dummy_versions_ != nullptr) {
|
||||
// List must be empty
|
||||
assert(dummy_versions_->TEST_Next() == dummy_versions_);
|
||||
bool deleted __attribute__((__unused__));
|
||||
bool deleted __attribute__((unused));
|
||||
deleted = dummy_versions_->Unref();
|
||||
assert(deleted);
|
||||
}
|
||||
@@ -634,41 +632,6 @@ int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::pair<WriteStallCondition, ColumnFamilyData::WriteStallCause>
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
int num_unflushed_memtables, int num_l0_files,
|
||||
uint64_t num_compaction_needed_bytes,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
if (num_unflushed_memtables >= mutable_cf_options.max_write_buffer_number) {
|
||||
return {WriteStallCondition::kStopped, WriteStallCause::kMemtableLimit};
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
num_l0_files >= mutable_cf_options.level0_stop_writes_trigger) {
|
||||
return {WriteStallCondition::kStopped, WriteStallCause::kL0FileCountLimit};
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
num_compaction_needed_bytes >=
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit) {
|
||||
return {WriteStallCondition::kStopped,
|
||||
WriteStallCause::kPendingCompactionBytes};
|
||||
} else if (mutable_cf_options.max_write_buffer_number > 3 &&
|
||||
num_unflushed_memtables >=
|
||||
mutable_cf_options.max_write_buffer_number - 1) {
|
||||
return {WriteStallCondition::kDelayed, WriteStallCause::kMemtableLimit};
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
num_l0_files >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
return {WriteStallCondition::kDelayed, WriteStallCause::kL0FileCountLimit};
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
num_compaction_needed_bytes >=
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) {
|
||||
return {WriteStallCondition::kDelayed,
|
||||
WriteStallCause::kPendingCompactionBytes};
|
||||
}
|
||||
return {WriteStallCondition::kNormal, WriteStallCause::kNone};
|
||||
}
|
||||
|
||||
WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
auto write_stall_condition = WriteStallCondition::kNormal;
|
||||
@@ -678,29 +641,25 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
uint64_t compaction_needed_bytes =
|
||||
vstorage->estimated_compaction_needed_bytes();
|
||||
|
||||
auto write_stall_condition_and_cause = GetWriteStallConditionAndCause(
|
||||
imm()->NumNotFlushed(), vstorage->l0_delay_trigger_count(),
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options);
|
||||
write_stall_condition = write_stall_condition_and_cause.first;
|
||||
auto write_stall_cause = write_stall_condition_and_cause.second;
|
||||
|
||||
bool was_stopped = write_controller->IsStopped();
|
||||
bool needed_delay = write_controller->NeedsDelay();
|
||||
|
||||
if (write_stall_condition == WriteStallCondition::kStopped &&
|
||||
write_stall_cause == WriteStallCause::kMemtableLimit) {
|
||||
if (imm()->NumNotFlushed() >= mutable_cf_options.max_write_buffer_number) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_LIMIT_STOPS, 1);
|
||||
write_stall_condition = WriteStallCondition::kStopped;
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d immutable memtables "
|
||||
"(waiting for flush), max_write_buffer_number is set to %d",
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number);
|
||||
} else if (write_stall_condition == WriteStallCondition::kStopped &&
|
||||
write_stall_cause == WriteStallCause::kL0FileCountLimit) {
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::L0_FILE_COUNT_LIMIT_STOPS, 1);
|
||||
write_stall_condition = WriteStallCondition::kStopped;
|
||||
if (compaction_picker_->IsLevel0CompactionInProgress()) {
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::LOCKED_L0_FILE_COUNT_LIMIT_STOPS, 1);
|
||||
@@ -708,23 +667,28 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
ROCKS_LOG_WARN(ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (write_stall_condition == WriteStallCondition::kStopped &&
|
||||
write_stall_cause == WriteStallCause::kPendingCompactionBytes) {
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
compaction_needed_bytes >=
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::PENDING_COMPACTION_BYTES_LIMIT_STOPS, 1);
|
||||
write_stall_condition = WriteStallCondition::kStopped;
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stopping writes because of estimated pending compaction "
|
||||
"bytes %" PRIu64,
|
||||
name_.c_str(), compaction_needed_bytes);
|
||||
} else if (write_stall_condition == WriteStallCondition::kDelayed &&
|
||||
write_stall_cause == WriteStallCause::kMemtableLimit) {
|
||||
} else if (mutable_cf_options.max_write_buffer_number > 3 &&
|
||||
imm()->NumNotFlushed() >=
|
||||
mutable_cf_options.max_write_buffer_number - 1) {
|
||||
write_controller_token_ =
|
||||
SetupDelay(write_controller, compaction_needed_bytes,
|
||||
prev_compaction_needed_bytes_, was_stopped,
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_LIMIT_SLOWDOWNS, 1);
|
||||
write_stall_condition = WriteStallCondition::kDelayed;
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stalling writes because we have %d immutable memtables "
|
||||
@@ -733,8 +697,10 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number,
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (write_stall_condition == WriteStallCondition::kDelayed &&
|
||||
write_stall_cause == WriteStallCause::kL0FileCountLimit) {
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
// L0 is the last two files from stopping.
|
||||
bool near_stop = vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger - 2;
|
||||
@@ -744,6 +710,7 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(InternalStats::L0_FILE_COUNT_LIMIT_SLOWDOWNS,
|
||||
1);
|
||||
write_stall_condition = WriteStallCondition::kDelayed;
|
||||
if (compaction_picker_->IsLevel0CompactionInProgress()) {
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::LOCKED_L0_FILE_COUNT_LIMIT_SLOWDOWNS, 1);
|
||||
@@ -753,8 +720,10 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"rate %" PRIu64,
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count(),
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (write_stall_condition == WriteStallCondition::kDelayed &&
|
||||
write_stall_cause == WriteStallCause::kPendingCompactionBytes) {
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
vstorage->estimated_compaction_needed_bytes() >=
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) {
|
||||
// If the distance to hard limit is less than 1/4 of the gap between soft
|
||||
// and
|
||||
// hard bytes limit, we think it is near stop and speed up the slowdown.
|
||||
@@ -772,6 +741,7 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::PENDING_COMPACTION_BYTES_LIMIT_SLOWDOWNS, 1);
|
||||
write_stall_condition = WriteStallCondition::kDelayed;
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stalling writes because of estimated pending compaction "
|
||||
@@ -779,7 +749,6 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes(),
|
||||
write_controller->delayed_write_rate());
|
||||
} else {
|
||||
assert(write_stall_condition == WriteStallCondition::kNormal);
|
||||
if (vstorage->l0_delay_trigger_count() >=
|
||||
GetL0ThresholdSpeedupCompaction(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger,
|
||||
@@ -882,68 +851,6 @@ bool ColumnFamilyData::RangeOverlapWithCompaction(
|
||||
smallest_user_key, largest_user_key, level);
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
const autovector<Range>& ranges, SuperVersion* super_version,
|
||||
bool* overlap) {
|
||||
assert(overlap != nullptr);
|
||||
*overlap = false;
|
||||
// Create an InternalIterator over all unflushed memtables
|
||||
Arena arena;
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&internal_comparator_, &arena);
|
||||
merge_iter_builder.AddIterator(
|
||||
super_version->mem->NewIterator(read_opts, &arena));
|
||||
super_version->imm->AddIterators(read_opts, &merge_iter_builder);
|
||||
ScopedArenaIterator memtable_iter(merge_iter_builder.Finish());
|
||||
|
||||
std::vector<InternalIterator*> memtable_range_del_iters;
|
||||
auto* active_range_del_iter =
|
||||
super_version->mem->NewRangeTombstoneIterator(read_opts);
|
||||
if (active_range_del_iter != nullptr) {
|
||||
memtable_range_del_iters.push_back(active_range_del_iter);
|
||||
}
|
||||
super_version->imm->AddRangeTombstoneIterators(read_opts,
|
||||
&memtable_range_del_iters);
|
||||
RangeDelAggregator range_del_agg(internal_comparator_, {} /* snapshots */,
|
||||
false /* collapse_deletions */);
|
||||
Status status;
|
||||
{
|
||||
std::unique_ptr<InternalIterator> memtable_range_del_iter(
|
||||
NewMergingIterator(&internal_comparator_,
|
||||
memtable_range_del_iters.empty()
|
||||
? nullptr
|
||||
: &memtable_range_del_iters[0],
|
||||
static_cast<int>(memtable_range_del_iters.size())));
|
||||
status = range_del_agg.AddTombstones(std::move(memtable_range_del_iter));
|
||||
}
|
||||
for (size_t i = 0; i < ranges.size() && status.ok() && !*overlap; ++i) {
|
||||
auto* vstorage = super_version->current->storage_info();
|
||||
auto* ucmp = vstorage->InternalComparator()->user_comparator();
|
||||
InternalKey range_start(ranges[i].start, kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
memtable_iter->Seek(range_start.Encode());
|
||||
status = memtable_iter->status();
|
||||
ParsedInternalKey seek_result;
|
||||
if (status.ok()) {
|
||||
if (memtable_iter->Valid() &&
|
||||
!ParseInternalKey(memtable_iter->key(), &seek_result)) {
|
||||
status = Status::Corruption("DB have corrupted keys");
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
if (memtable_iter->Valid() &&
|
||||
ucmp->Compare(seek_result.user_key, ranges[i].limit) <= 0) {
|
||||
*overlap = true;
|
||||
} else if (range_del_agg.IsRangeOverlapped(ranges[i].start,
|
||||
ranges[i].limit)) {
|
||||
*overlap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
const int ColumnFamilyData::kCompactAllLevels = -1;
|
||||
const int ColumnFamilyData::kCompactToBaseLevel = -2;
|
||||
|
||||
@@ -962,7 +869,8 @@ Compaction* ColumnFamilyData::CompactRange(
|
||||
|
||||
SuperVersion* ColumnFamilyData::GetReferencedSuperVersion(
|
||||
InstrumentedMutex* db_mutex) {
|
||||
SuperVersion* sv = GetThreadLocalSuperVersion(db_mutex);
|
||||
SuperVersion* sv = nullptr;
|
||||
sv = GetThreadLocalSuperVersion(db_mutex);
|
||||
sv->Ref();
|
||||
if (!ReturnThreadLocalSuperVersion(sv)) {
|
||||
// This Unref() corresponds to the Ref() in GetThreadLocalSuperVersion()
|
||||
@@ -976,6 +884,7 @@ SuperVersion* ColumnFamilyData::GetReferencedSuperVersion(
|
||||
|
||||
SuperVersion* ColumnFamilyData::GetThreadLocalSuperVersion(
|
||||
InstrumentedMutex* db_mutex) {
|
||||
SuperVersion* sv = nullptr;
|
||||
// The SuperVersion is cached in thread local storage to avoid acquiring
|
||||
// mutex when SuperVersion does not change since the last use. When a new
|
||||
// SuperVersion is installed, the compaction or flush thread cleans up
|
||||
@@ -994,7 +903,7 @@ SuperVersion* ColumnFamilyData::GetThreadLocalSuperVersion(
|
||||
// should only keep kSVInUse before ReturnThreadLocalSuperVersion call
|
||||
// (if no Scrape happens).
|
||||
assert(ptr != SuperVersion::kSVInUse);
|
||||
SuperVersion* sv = static_cast<SuperVersion*>(ptr);
|
||||
sv = static_cast<SuperVersion*>(ptr);
|
||||
if (sv == SuperVersion::kSVObsolete ||
|
||||
sv->version_number != super_version_number_.load()) {
|
||||
RecordTick(ioptions_.statistics, NUMBER_SUPERVERSION_ACQUIRES);
|
||||
@@ -1157,14 +1066,10 @@ ColumnFamilySet::~ColumnFamilySet() {
|
||||
while (column_family_data_.size() > 0) {
|
||||
// cfd destructor will delete itself from column_family_data_
|
||||
auto cfd = column_family_data_.begin()->second;
|
||||
bool last_ref __attribute__((__unused__));
|
||||
last_ref = cfd->Unref();
|
||||
assert(last_ref);
|
||||
cfd->Unref();
|
||||
delete cfd;
|
||||
}
|
||||
bool dummy_last_ref __attribute__((__unused__));
|
||||
dummy_last_ref = dummy_cfd_->Unref();
|
||||
assert(dummy_last_ref);
|
||||
dummy_cfd_->Unref();
|
||||
delete dummy_cfd_;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace rocksdb {
|
||||
|
||||
class Version;
|
||||
class VersionSet;
|
||||
class VersionStorageInfo;
|
||||
class MemTable;
|
||||
class MemTableListVersion;
|
||||
class CompactionPicker;
|
||||
@@ -203,10 +202,6 @@ class ColumnFamilyData {
|
||||
void SetLogNumber(uint64_t log_number) { log_number_ = log_number; }
|
||||
uint64_t GetLogNumber() const { return log_number_; }
|
||||
|
||||
void SetFlushReason(FlushReason flush_reason) {
|
||||
flush_reason_ = flush_reason;
|
||||
}
|
||||
FlushReason GetFlushReason() const { return flush_reason_; }
|
||||
// thread-safe
|
||||
const EnvOptions* soptions() const;
|
||||
const ImmutableCFOptions* ioptions() const { return &ioptions_; }
|
||||
@@ -274,16 +269,6 @@ class ColumnFamilyData {
|
||||
const Slice& largest_user_key,
|
||||
int level) const;
|
||||
|
||||
// Check if the passed ranges overlap with any unflushed memtables
|
||||
// (immutable or mutable).
|
||||
//
|
||||
// @param super_version A referenced SuperVersion that will be held for the
|
||||
// duration of this function.
|
||||
//
|
||||
// Thread-safe
|
||||
Status RangesOverlapWithMemtables(const autovector<Range>& ranges,
|
||||
SuperVersion* super_version, bool* overlap);
|
||||
|
||||
// A flag to tell a manual compaction is to compact all levels together
|
||||
// instead of a specific level.
|
||||
static const int kCompactAllLevels;
|
||||
@@ -346,17 +331,6 @@ class ColumnFamilyData {
|
||||
bool pending_flush() { return pending_flush_; }
|
||||
bool pending_compaction() { return pending_compaction_; }
|
||||
|
||||
enum class WriteStallCause {
|
||||
kNone,
|
||||
kMemtableLimit,
|
||||
kL0FileCountLimit,
|
||||
kPendingCompactionBytes,
|
||||
};
|
||||
static std::pair<WriteStallCondition, WriteStallCause>
|
||||
GetWriteStallConditionAndCause(int num_unflushed_memtables, int num_l0_files,
|
||||
uint64_t num_compaction_needed_bytes,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Recalculate some small conditions, which are changed only during
|
||||
// compaction, adding new memtable and/or
|
||||
// recalculation of compaction score. These values are used in
|
||||
@@ -430,8 +404,6 @@ class ColumnFamilyData {
|
||||
// recovered from
|
||||
uint64_t log_number_;
|
||||
|
||||
std::atomic<FlushReason> flush_reason_;
|
||||
|
||||
// An object that keeps all the compaction stats
|
||||
// and picks the next compaction
|
||||
std::unique_ptr<CompactionPicker> compaction_picker_;
|
||||
|
||||
+120
-7
@@ -383,9 +383,6 @@ class ColumnFamilyTest : public testing::Test {
|
||||
void AssertFilesPerLevel(const std::string& value, int cf) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(value, FilesPerLevel(cf));
|
||||
#else
|
||||
(void) value;
|
||||
(void) cf;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -400,8 +397,6 @@ class ColumnFamilyTest : public testing::Test {
|
||||
void AssertCountLiveFiles(int expected_value) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(expected_value, CountLiveFiles());
|
||||
#else
|
||||
(void) expected_value;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -450,8 +445,6 @@ class ColumnFamilyTest : public testing::Test {
|
||||
void AssertCountLiveLogFiles(int value) {
|
||||
#ifndef ROCKSDB_LITE // GetSortedWalFiles is not supported
|
||||
ASSERT_EQ(value, CountLiveLogFiles());
|
||||
#else
|
||||
(void) value;
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -1875,6 +1868,126 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks for automatic getting a conflict if there is a
|
||||
// manual which has not yet been scheduled.
|
||||
// The manual compaction waits in NotScheduled
|
||||
// We generate more files and then trigger an automatic compaction
|
||||
// This will wait because there is an unscheduled manual compaction.
|
||||
// Once the conflict is hit, the manual compaction starts and ends
|
||||
// Then another automatic will start and end.
|
||||
TEST_F(ColumnFamilyTest, SameCFManualAutomaticConflict) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ColumnFamilyOptions default_cf, one;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.max_compaction_bytes = default_cf.target_file_size_base * 1100;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
one.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
one.num_levels = 1;
|
||||
// trigger compaction if there are >= 4 files
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
Reopen({default_cf, one});
|
||||
// make sure all background compaction jobs can be scheduled
|
||||
auto stop_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
|
||||
// SETUP column family "one" -- universal style
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger - 2; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
bool cf_1_1 = true;
|
||||
bool cf_1_2 = true;
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction()::Conflict",
|
||||
"ColumnFamilyTest::ManualAutoCon:7"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:9",
|
||||
"ColumnFamilyTest::ManualAutoCon:8"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:2",
|
||||
"ColumnFamilyTest::ManualAutoCon:6"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:4",
|
||||
"ColumnFamilyTest::ManualAutoCon:5"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:1",
|
||||
"ColumnFamilyTest::ManualAutoCon:2"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:1",
|
||||
"ColumnFamilyTest::ManualAutoCon:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:3");
|
||||
} else if (cf_1_2) {
|
||||
cf_1_2 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:2");
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RunManualCompaction:NotScheduled", [&](void* arg) {
|
||||
InstrumentedMutex* mutex = static_cast<InstrumentedMutex*>(arg);
|
||||
mutex->Unlock();
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:9");
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:7");
|
||||
mutex->Lock();
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:6");
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:8");
|
||||
WaitForFlush(1);
|
||||
|
||||
// Add more L0 files and force automatic compaction
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(ToString(one.level0_file_num_compaction_trigger + i),
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:5");
|
||||
// Add more L0 files and force automatic compaction
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
}
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:1");
|
||||
|
||||
threads.join();
|
||||
WaitForCompaction();
|
||||
// VERIFY compaction "one"
|
||||
ASSERT_LE(NumTableFilesAtLevel(0, 1), 3);
|
||||
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
}
|
||||
|
||||
// In this test, we generate enough files to trigger automatic compactions.
|
||||
// The automatic compaction waits in NonTrivial:AfterRun
|
||||
// We generate more files and then trigger an automatic compaction
|
||||
|
||||
@@ -319,7 +319,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as DBImpl::CompactFiles is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
|
||||
+10
-13
@@ -405,23 +405,20 @@ void Compaction::Summary(char* output, int len) {
|
||||
uint64_t Compaction::OutputFilePreallocationSize() const {
|
||||
uint64_t preallocation_size = 0;
|
||||
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
preallocation_size += file->fd.GetFileSize();
|
||||
if (max_output_file_size_ != port::kMaxUint64 &&
|
||||
(cfd_->ioptions()->compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0)) {
|
||||
preallocation_size = max_output_file_size_;
|
||||
} else {
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
preallocation_size += file->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (max_output_file_size_ != port::kMaxUint64 &&
|
||||
(immutable_cf_options_.compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0)) {
|
||||
preallocation_size = std::min(max_output_file_size_, preallocation_size);
|
||||
}
|
||||
|
||||
// Over-estimate slightly so we don't end up just barely crossing
|
||||
// the threshold
|
||||
// No point to prellocate more than 1GB.
|
||||
return std::min(uint64_t{1073741824},
|
||||
preallocation_size + (preallocation_size / 10));
|
||||
return preallocation_size + (preallocation_size / 10);
|
||||
}
|
||||
|
||||
std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
|
||||
|
||||
@@ -537,11 +537,6 @@ void CompactionIterator::NextFromInput() {
|
||||
//
|
||||
// Note: Dropping this Delete will not affect TransactionDB
|
||||
// write-conflict checking since it is earlier than any snapshot.
|
||||
//
|
||||
// It seems that we can also drop deletion later than earliest snapshot
|
||||
// given that:
|
||||
// (1) The deletion is earlier than earliest_write_conflict_snapshot, and
|
||||
// (2) No value exist earlier than the deletion.
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
if (!bottommost_level_) {
|
||||
++iter_stats_.num_optimized_del_drop_obsolete;
|
||||
@@ -559,6 +554,10 @@ void CompactionIterator::NextFromInput() {
|
||||
// have hit (A)
|
||||
// We encapsulate the merge related state machine in a different
|
||||
// object to minimize change to the existing flow.
|
||||
// In case snapshot_checker is present, we can probably merge further
|
||||
// beyond prev_snapshot, since there could be more keys with sequence
|
||||
// smaller than prev_snapshot, but reported by snapshot_checker as not
|
||||
// visible by prev_snapshot. But it will make the logic more complicated.
|
||||
Status s = merge_helper_->MergeUntil(input_, range_del_agg_,
|
||||
prev_snapshot, bottommost_level_);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
@@ -622,12 +621,9 @@ void CompactionIterator::PrepareOutput() {
|
||||
// and the earliest snapshot is larger than this seqno
|
||||
// and the userkey differs from the last userkey in compaction
|
||||
// then we can squash the seqno to zero.
|
||||
//
|
||||
|
||||
// This is safe for TransactionDB write-conflict checking since transactions
|
||||
// only care about sequence number larger than any active snapshots.
|
||||
//
|
||||
// Can we do the same for levels above bottom level as long as
|
||||
// KeyNotExistsBeyondOutputLevel() return true?
|
||||
if ((compaction_ != nullptr &&
|
||||
!compaction_->allow_ingest_behind()) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
|
||||
+41
-419
@@ -9,10 +9,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -41,9 +39,9 @@ class NoMergingMergeOp : public MergeOperator {
|
||||
// Always returns Decition::kRemove.
|
||||
class StallingFilter : public CompactionFilter {
|
||||
public:
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType /*type*/,
|
||||
const Slice& /*existing_value*/, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* new_value,
|
||||
std::string* skip_until) const override {
|
||||
int k = std::atoi(key.ToString().c_str());
|
||||
last_seen.store(k);
|
||||
while (k >= stall_at.load()) {
|
||||
@@ -74,18 +72,6 @@ class StallingFilter : public CompactionFilter {
|
||||
mutable std::atomic<int> last_seen{0};
|
||||
};
|
||||
|
||||
// Compaction filter that filter out all keys.
|
||||
class FilterAllKeysCompactionFilter : public CompactionFilter {
|
||||
public:
|
||||
Decision FilterV2(int /*level*/, const Slice& /*key*/, ValueType /*type*/,
|
||||
const Slice& /*existing_value*/, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
return Decision::kRemove;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "AllKeysCompactionFilter"; }
|
||||
};
|
||||
|
||||
class LoggingForwardVectorIterator : public InternalIterator {
|
||||
public:
|
||||
struct Action {
|
||||
@@ -158,146 +144,76 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
public:
|
||||
FakeCompaction() = default;
|
||||
|
||||
virtual int level(size_t compaction_input_level) const override { return 0; }
|
||||
virtual int level(size_t compaction_input_level) const { return 0; }
|
||||
virtual bool KeyNotExistsBeyondOutputLevel(
|
||||
const Slice& user_key, std::vector<size_t>* level_ptrs) const override {
|
||||
return is_bottommost_level || key_not_exists_beyond_output_level;
|
||||
const Slice& user_key, std::vector<size_t>* level_ptrs) const {
|
||||
return key_not_exists_beyond_output_level;
|
||||
}
|
||||
virtual bool bottommost_level() const override { return is_bottommost_level; }
|
||||
virtual int number_levels() const override { return 1; }
|
||||
virtual Slice GetLargestUserKey() const override {
|
||||
virtual bool bottommost_level() const { return false; }
|
||||
virtual int number_levels() const { return 1; }
|
||||
virtual Slice GetLargestUserKey() const {
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
virtual bool allow_ingest_behind() const override { return false; }
|
||||
virtual bool allow_ingest_behind() const { return false; }
|
||||
|
||||
virtual bool preserve_deletes() const override { return false; }
|
||||
virtual bool preserve_deletes() const {return false; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
bool is_bottommost_level = false;
|
||||
};
|
||||
|
||||
// A simplifed snapshot checker which assumes each snapshot has a global
|
||||
// last visible sequence.
|
||||
class TestSnapshotChecker : public SnapshotChecker {
|
||||
public:
|
||||
explicit TestSnapshotChecker(
|
||||
SequenceNumber last_committed_sequence,
|
||||
const std::unordered_map<SequenceNumber, SequenceNumber>& snapshots = {})
|
||||
: last_committed_sequence_(last_committed_sequence),
|
||||
snapshots_(snapshots) {}
|
||||
|
||||
bool IsInSnapshot(SequenceNumber seq,
|
||||
SequenceNumber snapshot_seq) const override {
|
||||
if (snapshot_seq == kMaxSequenceNumber) {
|
||||
return seq <= last_committed_sequence_;
|
||||
}
|
||||
assert(snapshots_.count(snapshot_seq) > 0);
|
||||
return seq <= snapshots_.at(snapshot_seq);
|
||||
}
|
||||
|
||||
private:
|
||||
SequenceNumber last_committed_sequence_;
|
||||
// A map of valid snapshot to last visible sequence to the snapshot.
|
||||
std::unordered_map<SequenceNumber, SequenceNumber> snapshots_;
|
||||
};
|
||||
|
||||
// Test param:
|
||||
// bool: whether to pass snapshot_checker to compaction iterator.
|
||||
class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
class CompactionIteratorTest : public testing::Test {
|
||||
public:
|
||||
CompactionIteratorTest()
|
||||
: cmp_(BytewiseComparator()), icmp_(cmp_), snapshots_({}) {}
|
||||
|
||||
void InitIterators(
|
||||
const std::vector<std::string>& ks, const std::vector<std::string>& vs,
|
||||
const std::vector<std::string>& range_del_ks,
|
||||
const std::vector<std::string>& range_del_vs,
|
||||
SequenceNumber last_sequence,
|
||||
SequenceNumber last_committed_sequence = kMaxSequenceNumber,
|
||||
MergeOperator* merge_op = nullptr, CompactionFilter* filter = nullptr,
|
||||
bool bottommost_level = false,
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber) {
|
||||
void InitIterators(const std::vector<std::string>& ks,
|
||||
const std::vector<std::string>& vs,
|
||||
const std::vector<std::string>& range_del_ks,
|
||||
const std::vector<std::string>& range_del_vs,
|
||||
SequenceNumber last_sequence,
|
||||
MergeOperator* merge_op = nullptr,
|
||||
CompactionFilter* filter = nullptr) {
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
new test::VectorIterator(range_del_ks, range_del_vs));
|
||||
range_del_agg_.reset(new RangeDelAggregator(icmp_, snapshots_));
|
||||
ASSERT_OK(range_del_agg_->AddTombstones(std::move(range_del_iter)));
|
||||
|
||||
std::unique_ptr<CompactionIterator::CompactionProxy> compaction;
|
||||
if (filter || bottommost_level) {
|
||||
if (filter) {
|
||||
compaction_proxy_ = new FakeCompaction();
|
||||
compaction_proxy_->is_bottommost_level = bottommost_level;
|
||||
compaction.reset(compaction_proxy_);
|
||||
}
|
||||
bool use_snapshot_checker = UseSnapshotChecker() || GetParam();
|
||||
if (use_snapshot_checker || last_committed_sequence < kMaxSequenceNumber) {
|
||||
snapshot_checker_.reset(
|
||||
new TestSnapshotChecker(last_committed_sequence, snapshot_map_));
|
||||
}
|
||||
merge_helper_.reset(
|
||||
new MergeHelper(Env::Default(), cmp_, merge_op, filter, nullptr, false,
|
||||
0 /*latest_snapshot*/, snapshot_checker_.get(),
|
||||
0 /*level*/, nullptr /*statistics*/, &shutting_down_));
|
||||
// TODO(yiwu) add a mock snapshot checker and add test for it.
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
|
||||
merge_helper_.reset(new MergeHelper(Env::Default(), cmp_, merge_op, filter,
|
||||
nullptr, false, 0, 0, nullptr,
|
||||
&shutting_down_));
|
||||
iter_.reset(new LoggingForwardVectorIterator(ks, vs));
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
earliest_write_conflict_snapshot, snapshot_checker_.get(),
|
||||
Env::Default(), false, range_del_agg_.get(), std::move(compaction),
|
||||
filter, nullptr, &shutting_down_));
|
||||
kMaxSequenceNumber, snapshot_checker, Env::Default(), false,
|
||||
range_del_agg_.get(), std::move(compaction), filter, nullptr,
|
||||
&shutting_down_));
|
||||
}
|
||||
|
||||
void AddSnapshot(SequenceNumber snapshot,
|
||||
SequenceNumber last_visible_seq = kMaxSequenceNumber) {
|
||||
snapshots_.push_back(snapshot);
|
||||
snapshot_map_[snapshot] = last_visible_seq;
|
||||
}
|
||||
|
||||
virtual bool UseSnapshotChecker() const { return false; }
|
||||
|
||||
void RunTest(
|
||||
const std::vector<std::string>& input_keys,
|
||||
const std::vector<std::string>& input_values,
|
||||
const std::vector<std::string>& expected_keys,
|
||||
const std::vector<std::string>& expected_values,
|
||||
SequenceNumber last_committed_seq = kMaxSequenceNumber,
|
||||
MergeOperator* merge_operator = nullptr,
|
||||
CompactionFilter* compaction_filter = nullptr,
|
||||
bool bottommost_level = false,
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber) {
|
||||
InitIterators(input_keys, input_values, {}, {}, kMaxSequenceNumber,
|
||||
last_committed_seq, merge_operator, compaction_filter,
|
||||
bottommost_level, earliest_write_conflict_snapshot);
|
||||
c_iter_->SeekToFirst();
|
||||
for (size_t i = 0; i < expected_keys.size(); i++) {
|
||||
std::string info = "i = " + ToString(i);
|
||||
ASSERT_TRUE(c_iter_->Valid()) << info;
|
||||
ASSERT_OK(c_iter_->status()) << info;
|
||||
ASSERT_EQ(expected_keys[i], c_iter_->key().ToString()) << info;
|
||||
ASSERT_EQ(expected_values[i], c_iter_->value().ToString()) << info;
|
||||
c_iter_->Next();
|
||||
}
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
void AddSnapshot(SequenceNumber snapshot) { snapshots_.push_back(snapshot); }
|
||||
|
||||
const Comparator* cmp_;
|
||||
const InternalKeyComparator icmp_;
|
||||
std::vector<SequenceNumber> snapshots_;
|
||||
// A map of valid snapshot to last visible sequence to the snapshot.
|
||||
std::unordered_map<SequenceNumber, SequenceNumber> snapshot_map_;
|
||||
std::unique_ptr<MergeHelper> merge_helper_;
|
||||
std::unique_ptr<LoggingForwardVectorIterator> iter_;
|
||||
std::unique_ptr<CompactionIterator> c_iter_;
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg_;
|
||||
std::unique_ptr<SnapshotChecker> snapshot_checker_;
|
||||
std::atomic<bool> shutting_down_{false};
|
||||
FakeCompaction* compaction_proxy_;
|
||||
};
|
||||
|
||||
// It is possible that the output of the compaction iterator is empty even if
|
||||
// the input is not.
|
||||
TEST_P(CompactionIteratorTest, EmptyResult) {
|
||||
TEST_F(CompactionIteratorTest, EmptyResult) {
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue)},
|
||||
{"", "val"}, {}, {}, 5);
|
||||
@@ -307,7 +223,7 @@ TEST_P(CompactionIteratorTest, EmptyResult) {
|
||||
|
||||
// If there is a corruption after a single deletion, the corrupted key should
|
||||
// be preserved.
|
||||
TEST_P(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue, true),
|
||||
test::KeyStr("b", 10, kTypeValue)},
|
||||
@@ -326,7 +242,7 @@ TEST_P(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
TEST_F(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValue),
|
||||
test::KeyStr("morning", 2, kTypeValue),
|
||||
test::KeyStr("night", 3, kTypeValue)},
|
||||
@@ -342,7 +258,7 @@ TEST_P(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
TEST_F(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
AddSnapshot(10);
|
||||
std::vector<std::string> ks1;
|
||||
ks1.push_back(test::KeyStr("ma", 28, kTypeRangeDeletion));
|
||||
@@ -363,7 +279,7 @@ TEST_P(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
TEST_F(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
@@ -438,7 +354,7 @@ TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
test::KeyStr("j", 99, kTypeValue)},
|
||||
{"av50", "am45", "bv60", "bv40", "cv35", "dm70", "em71", "fm65", "fm30",
|
||||
"fv25", "gv90", "hv91", "im95", "jv99"},
|
||||
{}, {}, kMaxSequenceNumber, kMaxSequenceNumber, &merge_op, &filter);
|
||||
{}, {}, kMaxSequenceNumber, &merge_op, &filter);
|
||||
|
||||
// Compaction should output just "a", "e" and "h" keys.
|
||||
c_iter_->SeekToFirst();
|
||||
@@ -473,14 +389,13 @@ TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
ASSERT_EQ(expected_actions, iter_->log);
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, ShuttingDownInFilter) {
|
||||
TEST_F(CompactionIteratorTest, ShuttingDownInFilter) {
|
||||
NoMergingMergeOp merge_op;
|
||||
StallingFilter filter;
|
||||
InitIterators(
|
||||
{test::KeyStr("1", 1, kTypeValue), test::KeyStr("2", 2, kTypeValue),
|
||||
test::KeyStr("3", 3, kTypeValue), test::KeyStr("4", 4, kTypeValue)},
|
||||
{"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
|
||||
&merge_op, &filter);
|
||||
{"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, &merge_op, &filter);
|
||||
// Don't leave tombstones (kTypeDeletion) for filtered keys.
|
||||
compaction_proxy_->key_not_exists_beyond_output_level = true;
|
||||
|
||||
@@ -511,14 +426,13 @@ TEST_P(CompactionIteratorTest, ShuttingDownInFilter) {
|
||||
|
||||
// Same as ShuttingDownInFilter, but shutdown happens during filter call for
|
||||
// a merge operand, not for a value.
|
||||
TEST_P(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
TEST_F(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
NoMergingMergeOp merge_op;
|
||||
StallingFilter filter;
|
||||
InitIterators(
|
||||
{test::KeyStr("1", 1, kTypeValue), test::KeyStr("2", 2, kTypeMerge),
|
||||
test::KeyStr("3", 3, kTypeMerge), test::KeyStr("4", 4, kTypeValue)},
|
||||
{"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
|
||||
&merge_op, &filter);
|
||||
{"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, &merge_op, &filter);
|
||||
compaction_proxy_->key_not_exists_beyond_output_level = true;
|
||||
|
||||
std::atomic<bool> seek_done{false};
|
||||
@@ -546,7 +460,7 @@ TEST_P(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
EXPECT_EQ(2, filter.last_seen.load());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
TEST_F(CompactionIteratorTest, SingleMergeOperand) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
@@ -638,7 +552,7 @@ TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
// c should invoke FullMerge due to kTypeValue at the beginning.
|
||||
test::KeyStr("c", 90, kTypeMerge), test::KeyStr("c", 80, kTypeValue)},
|
||||
{"av1", "bv2", "bv1", "cv2", "cv1"}, {}, {}, kMaxSequenceNumber,
|
||||
kMaxSequenceNumber, &merge_op, &filter);
|
||||
&merge_op, &filter);
|
||||
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
@@ -651,298 +565,6 @@ TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
ASSERT_EQ("cv1cv2", c_iter_->value().ToString());
|
||||
}
|
||||
|
||||
// In bottommost level, values earlier than earliest snapshot can be output
|
||||
// with sequence = 0.
|
||||
TEST_P(CompactionIteratorTest, ZeroOutSequenceAtBottomLevel) {
|
||||
AddSnapshot(1);
|
||||
RunTest({test::KeyStr("a", 1, kTypeValue), test::KeyStr("b", 2, kTypeValue)},
|
||||
{"v1", "v2"},
|
||||
{test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 2, kTypeValue)},
|
||||
{"v1", "v2"}, kMaxSequenceNumber /*last_commited_seq*/,
|
||||
nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
|
||||
true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
// In bottommost level, deletions earlier than earliest snapshot can be removed
|
||||
// permanently.
|
||||
TEST_P(CompactionIteratorTest, RemoveDeletionAtBottomLevel) {
|
||||
AddSnapshot(1);
|
||||
RunTest({test::KeyStr("a", 1, kTypeDeletion),
|
||||
test::KeyStr("b", 2, kTypeDeletion)},
|
||||
{"", ""}, {test::KeyStr("b", 2, kTypeDeletion)}, {""},
|
||||
kMaxSequenceNumber /*last_commited_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
// In bottommost level, single deletions earlier than earliest snapshot can be
|
||||
// removed permanently.
|
||||
TEST_P(CompactionIteratorTest, RemoveSingleDeletionAtBottomLevel) {
|
||||
AddSnapshot(1);
|
||||
RunTest({test::KeyStr("a", 1, kTypeSingleDeletion),
|
||||
test::KeyStr("b", 2, kTypeSingleDeletion)},
|
||||
{"", ""}, {test::KeyStr("b", 2, kTypeSingleDeletion)}, {""},
|
||||
kMaxSequenceNumber /*last_commited_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionIteratorTestInstance, CompactionIteratorTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
// Tests how CompactionIterator work together with SnapshotChecker.
|
||||
class CompactionIteratorWithSnapshotCheckerTest
|
||||
: public CompactionIteratorTest {
|
||||
public:
|
||||
bool UseSnapshotChecker() const override { return true; }
|
||||
};
|
||||
|
||||
// Uncommitted keys (keys with seq > last_committed_seq) should be output as-is
|
||||
// while committed version of these keys should get compacted as usual.
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
PreserveUncommittedKeys_Value) {
|
||||
RunTest(
|
||||
{test::KeyStr("foo", 3, kTypeValue), test::KeyStr("foo", 2, kTypeValue),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v3", "v2", "v1"},
|
||||
{test::KeyStr("foo", 3, kTypeValue), test::KeyStr("foo", 2, kTypeValue)},
|
||||
{"v3", "v2"}, 2 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
PreserveUncommittedKeys_Deletion) {
|
||||
RunTest({test::KeyStr("foo", 2, kTypeDeletion),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"", "v1"},
|
||||
{test::KeyStr("foo", 2, kTypeDeletion),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"", "v1"}, 1 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
PreserveUncommittedKeys_Merge) {
|
||||
auto merge_op = MergeOperators::CreateStringAppendOperator();
|
||||
RunTest(
|
||||
{test::KeyStr("foo", 3, kTypeMerge), test::KeyStr("foo", 2, kTypeMerge),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v3", "v2", "v1"},
|
||||
{test::KeyStr("foo", 3, kTypeMerge), test::KeyStr("foo", 2, kTypeValue)},
|
||||
{"v3", "v1,v2"}, 2 /*last_committed_seq*/, merge_op.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
PreserveUncommittedKeys_SingleDelete) {
|
||||
RunTest({test::KeyStr("foo", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"", "v1"},
|
||||
{test::KeyStr("foo", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"", "v1"}, 1 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
PreserveUncommittedKeys_BlobIndex) {
|
||||
RunTest({test::KeyStr("foo", 3, kTypeBlobIndex),
|
||||
test::KeyStr("foo", 2, kTypeBlobIndex),
|
||||
test::KeyStr("foo", 1, kTypeBlobIndex)},
|
||||
{"v3", "v2", "v1"},
|
||||
{test::KeyStr("foo", 3, kTypeBlobIndex),
|
||||
test::KeyStr("foo", 2, kTypeBlobIndex)},
|
||||
{"v3", "v2"}, 2 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
// Test compaction iterator dedup keys visible to the same snapshot.
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Value) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest(
|
||||
{test::KeyStr("foo", 4, kTypeValue), test::KeyStr("foo", 3, kTypeValue),
|
||||
test::KeyStr("foo", 2, kTypeValue), test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v4", "v3", "v2", "v1"},
|
||||
{test::KeyStr("foo", 4, kTypeValue), test::KeyStr("foo", 3, kTypeValue),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v4", "v3", "v1"}, 3 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Deletion) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest(
|
||||
{test::KeyStr("foo", 4, kTypeValue),
|
||||
test::KeyStr("foo", 3, kTypeDeletion),
|
||||
test::KeyStr("foo", 2, kTypeValue), test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v4", "", "v2", "v1"},
|
||||
{test::KeyStr("foo", 4, kTypeValue),
|
||||
test::KeyStr("foo", 3, kTypeDeletion),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v4", "", "v1"}, 3 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Merge) {
|
||||
AddSnapshot(2, 1);
|
||||
AddSnapshot(4, 3);
|
||||
auto merge_op = MergeOperators::CreateStringAppendOperator();
|
||||
RunTest(
|
||||
{test::KeyStr("foo", 5, kTypeMerge), test::KeyStr("foo", 4, kTypeMerge),
|
||||
test::KeyStr("foo", 3, kTypeMerge), test::KeyStr("foo", 2, kTypeMerge),
|
||||
test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v5", "v4", "v3", "v2", "v1"},
|
||||
{test::KeyStr("foo", 5, kTypeMerge), test::KeyStr("foo", 4, kTypeMerge),
|
||||
test::KeyStr("foo", 3, kTypeMerge), test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v5", "v4", "v2,v3", "v1"}, 4 /*last_committed_seq*/, merge_op.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
DedupSameSnapshot_SingleDeletion) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest(
|
||||
{test::KeyStr("foo", 4, kTypeValue),
|
||||
test::KeyStr("foo", 3, kTypeSingleDeletion),
|
||||
test::KeyStr("foo", 2, kTypeValue), test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v4", "", "v2", "v1"},
|
||||
{test::KeyStr("foo", 4, kTypeValue), test::KeyStr("foo", 1, kTypeValue)},
|
||||
{"v4", "v1"}, 3 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_BlobIndex) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest({test::KeyStr("foo", 4, kTypeBlobIndex),
|
||||
test::KeyStr("foo", 3, kTypeBlobIndex),
|
||||
test::KeyStr("foo", 2, kTypeBlobIndex),
|
||||
test::KeyStr("foo", 1, kTypeBlobIndex)},
|
||||
{"v4", "v3", "v2", "v1"},
|
||||
{test::KeyStr("foo", 4, kTypeBlobIndex),
|
||||
test::KeyStr("foo", 3, kTypeBlobIndex),
|
||||
test::KeyStr("foo", 1, kTypeBlobIndex)},
|
||||
{"v4", "v3", "v1"}, 3 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
// At bottom level, sequence numbers can be zero out, and deletions can be
|
||||
// removed, but only when they are visible to earliest snapshot.
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
NotZeroOutSequenceIfNotVisibleToEarliestSnapshot) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest({test::KeyStr("a", 1, kTypeValue), test::KeyStr("b", 2, kTypeValue),
|
||||
test::KeyStr("c", 3, kTypeValue)},
|
||||
{"v1", "v2", "v3"},
|
||||
{test::KeyStr("a", 0, kTypeValue), test::KeyStr("b", 2, kTypeValue),
|
||||
test::KeyStr("c", 3, kTypeValue)},
|
||||
{"v1", "v2", "v3"}, kMaxSequenceNumber /*last_commited_seq*/,
|
||||
nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
|
||||
true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
NotRemoveDeletionIfNotVisibleToEarliestSnapshot) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest(
|
||||
{test::KeyStr("a", 1, kTypeDeletion), test::KeyStr("b", 2, kTypeDeletion),
|
||||
test::KeyStr("c", 3, kTypeDeletion)},
|
||||
{"", "", ""},
|
||||
{test::KeyStr("b", 2, kTypeDeletion),
|
||||
test::KeyStr("c", 3, kTypeDeletion)},
|
||||
{"", ""}, kMaxSequenceNumber /*last_commited_seq*/,
|
||||
nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
|
||||
true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
NotRemoveSingleDeletionIfNotVisibleToEarliestSnapshot) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest({test::KeyStr("a", 1, kTypeSingleDeletion),
|
||||
test::KeyStr("b", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("c", 3, kTypeSingleDeletion)},
|
||||
{"", "", ""},
|
||||
{test::KeyStr("b", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("c", 3, kTypeSingleDeletion)},
|
||||
{"", ""}, kMaxSequenceNumber /*last_commited_seq*/,
|
||||
nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
|
||||
true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
// Single delete should not cancel out values that not visible to the
|
||||
// same set of snapshots
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
SingleDeleteAcrossSnapshotBoundary) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest({test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValue)},
|
||||
{"", "v1"},
|
||||
{test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValue)},
|
||||
{"", "v1"}, 2 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
// Single delete should be kept in case it is not visible to the
|
||||
// earliest write conflict snapshot. If a single delete is kept for this reason,
|
||||
// corresponding value can be trimmed to save space.
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
KeepSingleDeletionForWriteConflictChecking) {
|
||||
AddSnapshot(2, 0);
|
||||
RunTest({test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValue)},
|
||||
{"", "v1"},
|
||||
{test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValue)},
|
||||
{"", ""}, 2 /*last_committed_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, false /*bottommost_level*/,
|
||||
2 /*earliest_write_conflict_snapshot*/);
|
||||
}
|
||||
|
||||
// Compaction filter should keep uncommitted key as-is, and
|
||||
// * Convert the latest velue to deletion, and/or
|
||||
// * if latest value is a merge, apply filter to all suequent merges.
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_Value) {
|
||||
std::unique_ptr<CompactionFilter> compaction_filter(
|
||||
new FilterAllKeysCompactionFilter());
|
||||
RunTest(
|
||||
{test::KeyStr("a", 2, kTypeValue), test::KeyStr("a", 1, kTypeValue),
|
||||
test::KeyStr("b", 3, kTypeValue), test::KeyStr("c", 1, kTypeValue)},
|
||||
{"v2", "v1", "v3", "v4"},
|
||||
{test::KeyStr("a", 2, kTypeValue), test::KeyStr("a", 1, kTypeDeletion),
|
||||
test::KeyStr("b", 3, kTypeValue), test::KeyStr("c", 1, kTypeDeletion)},
|
||||
{"v2", "", "v3", ""}, 1 /*last_committed_seq*/,
|
||||
nullptr /*merge_operator*/, compaction_filter.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_Deletion) {
|
||||
std::unique_ptr<CompactionFilter> compaction_filter(
|
||||
new FilterAllKeysCompactionFilter());
|
||||
RunTest(
|
||||
{test::KeyStr("a", 2, kTypeDeletion), test::KeyStr("a", 1, kTypeValue)},
|
||||
{"", "v1"},
|
||||
{test::KeyStr("a", 2, kTypeDeletion),
|
||||
test::KeyStr("a", 1, kTypeDeletion)},
|
||||
{"", ""}, 1 /*last_committed_seq*/, nullptr /*merge_operator*/,
|
||||
compaction_filter.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
CompactionFilter_PartialMerge) {
|
||||
std::shared_ptr<MergeOperator> merge_op =
|
||||
MergeOperators::CreateStringAppendOperator();
|
||||
std::unique_ptr<CompactionFilter> compaction_filter(
|
||||
new FilterAllKeysCompactionFilter());
|
||||
RunTest({test::KeyStr("a", 3, kTypeMerge), test::KeyStr("a", 2, kTypeMerge),
|
||||
test::KeyStr("a", 1, kTypeMerge)},
|
||||
{"v3", "v2", "v1"}, {test::KeyStr("a", 3, kTypeMerge)}, {"v3"},
|
||||
2 /*last_committed_seq*/, merge_op.get(), compaction_filter.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_FullMerge) {
|
||||
std::shared_ptr<MergeOperator> merge_op =
|
||||
MergeOperators::CreateStringAppendOperator();
|
||||
std::unique_ptr<CompactionFilter> compaction_filter(
|
||||
new FilterAllKeysCompactionFilter());
|
||||
RunTest(
|
||||
{test::KeyStr("a", 3, kTypeMerge), test::KeyStr("a", 2, kTypeMerge),
|
||||
test::KeyStr("a", 1, kTypeValue)},
|
||||
{"v3", "v2", "v1"},
|
||||
{test::KeyStr("a", 3, kTypeMerge), test::KeyStr("a", 1, kTypeDeletion)},
|
||||
{"v3", ""}, 2 /*last_committed_seq*/, merge_op.get(),
|
||||
compaction_filter.get());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -747,8 +747,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
compaction_filter, db_options_.info_log.get(),
|
||||
false /* internal key corruption is expected */,
|
||||
existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
|
||||
snapshot_checker_, compact_->compaction->level(),
|
||||
db_options_.statistics.get(), shutting_down_);
|
||||
compact_->compaction->level(), db_options_.statistics.get(),
|
||||
shutting_down_);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
|
||||
|
||||
@@ -1034,7 +1034,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "SKIPPED, not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1043,5 +1043,5 @@ int main(int /*argc*/, char** /*argv*/) {
|
||||
|
||||
#else
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) { return 0; }
|
||||
int main(int argc, char** argv) { return 0; }
|
||||
#endif // !defined(IOS_CROSS_COMPILE)
|
||||
@@ -946,7 +946,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as CompactionJobStats is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
|
||||
@@ -175,8 +175,6 @@ TEST_F(CompactionPickerTest, Level1Trigger) {
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, Level1Trigger2) {
|
||||
mutable_cf_options_.target_file_size_base = 10000000000;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
Add(1, 66U, "150", "200", 1000000001U);
|
||||
Add(1, 88U, "201", "300", 1000000000U);
|
||||
@@ -193,14 +191,13 @@ TEST_F(CompactionPickerTest, Level1Trigger2) {
|
||||
ASSERT_EQ(66U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(6U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(7U, compaction->input(1, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(uint64_t{1073741824}, compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, LevelMaxScore) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
Add(0, 1U, "150", "200", 1000000U);
|
||||
// Level 1 score 1.2
|
||||
Add(1, 66U, "150", "200", 6000000U);
|
||||
@@ -221,9 +218,6 @@ TEST_F(CompactionPickerTest, LevelMaxScore) {
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(7U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(mutable_cf_options_.target_file_size_base +
|
||||
mutable_cf_options_.target_file_size_base / 10,
|
||||
compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NeedsCompactionLevel) {
|
||||
@@ -527,10 +521,9 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.target_file_size_base = 100000000000;
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
|
||||
Add(2, 6U, "150", "179", 50000000U);
|
||||
Add(2, 7U, "180", "220", 50000000U);
|
||||
@@ -550,8 +543,6 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
// Pick file 8 because it overlaps with 0 files on level 3.
|
||||
ASSERT_EQ(8U, compaction->input(0, 0)->fd.GetNumber());
|
||||
// Compaction input size * 1.1
|
||||
ASSERT_GE(uint64_t{55000000}, compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
|
||||
@@ -603,9 +603,9 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
|
||||
CompactionReason compaction_reason;
|
||||
if (max_number_of_files_to_compact == UINT_MAX) {
|
||||
compaction_reason = CompactionReason::kUniversalSizeRatio;
|
||||
} else {
|
||||
compaction_reason = CompactionReason::kUniversalSortedRunNum;
|
||||
} else {
|
||||
compaction_reason = CompactionReason::kUniversalSizeRatio;
|
||||
}
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
|
||||
+2
-10
@@ -19,17 +19,9 @@ void CancelAllBackgroundWork(DB* db, bool wait) {
|
||||
}
|
||||
|
||||
Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end,
|
||||
bool include_end) {
|
||||
RangePtr range(begin, end);
|
||||
return DeleteFilesInRanges(db, column_family, &range, 1, include_end);
|
||||
}
|
||||
|
||||
Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end) {
|
||||
const Slice* begin, const Slice* end) {
|
||||
return (static_cast_with_check<DBImpl, DB>(db->GetRootDB()))
|
||||
->DeleteFilesInRanges(column_family, ranges, n, include_end);
|
||||
->DeleteFilesInRange(column_family, begin, end);
|
||||
}
|
||||
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
|
||||
@@ -510,7 +510,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "SKIPPED as RepairDB() is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "SKIPPED as Cuckoo table is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
+6
-114
@@ -9,7 +9,6 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "util/sync_point.h"
|
||||
#endif
|
||||
@@ -571,19 +570,19 @@ TEST_F(DBBasicTest, CompactBetweenSnapshots) {
|
||||
|
||||
TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
Options options = CurrentOptions();
|
||||
Close();
|
||||
Destroy(options);
|
||||
std::string dbname = test::TmpDir(env_) + "/db_options_test";
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
|
||||
// Does not exist, and create_if_missing == false: error
|
||||
DB* db = nullptr;
|
||||
options.create_if_missing = false;
|
||||
Status s = DB::Open(options, dbname_, &db);
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != nullptr);
|
||||
ASSERT_TRUE(db == nullptr);
|
||||
|
||||
// Does not exist, and create_if_missing == true: OK
|
||||
options.create_if_missing = true;
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
@@ -593,14 +592,14 @@ TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
// Does exist, and error_if_exists == true: error
|
||||
options.create_if_missing = false;
|
||||
options.error_if_exists = true;
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
s = DB::Open(options, dbname, &db);
|
||||
ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != nullptr);
|
||||
ASSERT_TRUE(db == nullptr);
|
||||
|
||||
// Does exist, and error_if_exists == false: OK
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
@@ -848,113 +847,6 @@ TEST_F(DBBasicTest, MmapAndBufferOptions) {
|
||||
}
|
||||
#endif
|
||||
|
||||
class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv() : EnvWrapper(Env::Default()),
|
||||
close_count(0) { }
|
||||
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
TestLogger(TestEnv *env_ptr) : Logger() { env = env_ptr; }
|
||||
~TestLogger() {
|
||||
if (!closed_) {
|
||||
CloseHelper();
|
||||
}
|
||||
}
|
||||
virtual void Logv(const char *format, va_list ap) override { };
|
||||
protected:
|
||||
virtual Status CloseImpl() override {
|
||||
return CloseHelper();
|
||||
}
|
||||
private:
|
||||
Status CloseHelper() {
|
||||
env->CloseCountInc();;
|
||||
return Status::IOError();
|
||||
}
|
||||
TestEnv *env;
|
||||
};
|
||||
|
||||
void CloseCountInc() { close_count++; }
|
||||
|
||||
int GetCloseCount() { return close_count; }
|
||||
|
||||
virtual Status NewLogger(const std::string& fname,
|
||||
shared_ptr<Logger>* result) {
|
||||
result->reset(new TestLogger(this));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
int close_count;
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTest, DBClose) {
|
||||
Options options = GetDefaultOptions();
|
||||
std::string dbname = test::TmpDir(env_) + "/db_close_test";
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
|
||||
DB* db = nullptr;
|
||||
TestEnv *env = new TestEnv();
|
||||
options.create_if_missing = true;
|
||||
options.env = env;
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
s = db->Close();
|
||||
ASSERT_EQ(env->GetCloseCount(), 1);
|
||||
ASSERT_EQ(s, Status::IOError());
|
||||
|
||||
delete db;
|
||||
ASSERT_EQ(env->GetCloseCount(), 1);
|
||||
|
||||
// Do not call DB::Close() and ensure our logger Close() still gets called
|
||||
s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
delete db;
|
||||
ASSERT_EQ(env->GetCloseCount(), 2);
|
||||
|
||||
// Provide our own logger and ensure DB::Close() does not close it
|
||||
options.info_log.reset(new TestEnv::TestLogger(env));
|
||||
options.create_if_missing = false;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
s = db->Close();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
delete db;
|
||||
ASSERT_EQ(env->GetCloseCount(), 2);
|
||||
options.info_log.reset();
|
||||
ASSERT_EQ(env->GetCloseCount(), 3);
|
||||
|
||||
delete options.env;
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DBCloseFlushError) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.manual_wal_flush = true;
|
||||
options.write_buffer_size=100;
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("key1", "value1"));
|
||||
ASSERT_OK(Put("key2", "value2"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
|
||||
ASSERT_OK(Put("key3", "value3"));
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
Status s = dbfull()->Close();
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
ASSERT_NE(s, Status::OK());
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -111,31 +111,6 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBBlockCacheTest, IteratorBlockCacheUsage) {
|
||||
ReadOptions read_options;
|
||||
read_options.fill_cache = false;
|
||||
auto table_options = GetTableOptions();
|
||||
auto options = GetOptions(table_options);
|
||||
InitTable(options);
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(0, 0, false);
|
||||
table_options.block_cache = cache;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
RecordCacheCounters(options);
|
||||
|
||||
std::vector<std::unique_ptr<Iterator>> iterators(kNumBlocks - 1);
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(0));
|
||||
ASSERT_LT(0, cache->GetUsage());
|
||||
delete iter;
|
||||
iter = nullptr;
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
ReadOptions read_options;
|
||||
auto table_options = GetTableOptions();
|
||||
|
||||
@@ -1517,122 +1517,6 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
ASSERT_GT(old_num_files, new_num_files);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DeleteFilesInRanges) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 4;
|
||||
options.max_background_compactions = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
|
||||
Random rnd(301);
|
||||
std::map<int32_t, std::string> values;
|
||||
|
||||
// file [0 => 100), [100 => 200), ... [900, 1000)
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
values[k] = RandomString(&rnd, value_size);
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("10", FilesPerLevel(0));
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = 2;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,10", FilesPerLevel(0));
|
||||
|
||||
// file [0 => 100), [200 => 300), ... [800, 900)
|
||||
for (auto i = 0; i < 10; i+=2) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("5,0,10", FilesPerLevel(0));
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
|
||||
ASSERT_EQ("0,5,10", FilesPerLevel(0));
|
||||
|
||||
// Delete files in range [0, 299] (inclusive)
|
||||
{
|
||||
auto begin_str1 = Key(0), end_str1 = Key(100);
|
||||
auto begin_str2 = Key(100), end_str2 = Key(200);
|
||||
auto begin_str3 = Key(200), end_str3 = Key(299);
|
||||
Slice begin1(begin_str1), end1(end_str1);
|
||||
Slice begin2(begin_str2), end2(end_str2);
|
||||
Slice begin3(begin_str3), end3(end_str3);
|
||||
std::vector<RangePtr> ranges;
|
||||
ranges.push_back(RangePtr(&begin1, &end1));
|
||||
ranges.push_back(RangePtr(&begin2, &end2));
|
||||
ranges.push_back(RangePtr(&begin3, &end3));
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(),
|
||||
ranges.data(), ranges.size()));
|
||||
ASSERT_EQ("0,3,7", FilesPerLevel(0));
|
||||
|
||||
// Keys [0, 300) should not exist.
|
||||
for (auto i = 0; i < 300; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 300; i < 1000; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete files in range [600, 999) (exclusive)
|
||||
{
|
||||
auto begin_str1 = Key(600), end_str1 = Key(800);
|
||||
auto begin_str2 = Key(700), end_str2 = Key(900);
|
||||
auto begin_str3 = Key(800), end_str3 = Key(999);
|
||||
Slice begin1(begin_str1), end1(end_str1);
|
||||
Slice begin2(begin_str2), end2(end_str2);
|
||||
Slice begin3(begin_str3), end3(end_str3);
|
||||
std::vector<RangePtr> ranges;
|
||||
ranges.push_back(RangePtr(&begin1, &end1));
|
||||
ranges.push_back(RangePtr(&begin2, &end2));
|
||||
ranges.push_back(RangePtr(&begin3, &end3));
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(),
|
||||
ranges.data(), ranges.size(), false));
|
||||
ASSERT_EQ("0,1,4", FilesPerLevel(0));
|
||||
|
||||
// Keys [600, 900) should not exist.
|
||||
for (auto i = 600; i < 900; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 300; i < 600; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
for (auto i = 900; i < 1000; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all files.
|
||||
{
|
||||
RangePtr range;
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(), &range, 1));
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
for (auto i = 0; i < 1000; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
// regression test for #2833: groups of files whose user-keys overlap at the
|
||||
// endpoints could be split by `DeleteFilesInRange`. This caused old data to
|
||||
@@ -3105,276 +2989,6 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactRangeDelayedByL0FileCount) {
|
||||
// Verify that, when `CompactRangeOptions::allow_write_stall == false`, manual
|
||||
// compaction only triggers flush after it's sure stall won't be triggered for
|
||||
// L0 file count going too high.
|
||||
const int kNumL0FilesTrigger = 4;
|
||||
const int kNumL0FilesLimit = 8;
|
||||
// i == 0: verifies normal case where stall is avoided by delay
|
||||
// i == 1: verifies no delay in edge case where stall trigger is same as
|
||||
// compaction trigger, so stall can't be avoided
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_slowdown_writes_trigger = kNumL0FilesLimit;
|
||||
if (i == 0) {
|
||||
options.level0_file_num_compaction_trigger = kNumL0FilesTrigger;
|
||||
} else {
|
||||
options.level0_file_num_compaction_trigger = kNumL0FilesLimit;
|
||||
}
|
||||
Reopen(options);
|
||||
|
||||
if (i == 0) {
|
||||
// ensure the auto compaction doesn't finish until manual compaction has
|
||||
// had a chance to be delayed.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWait", "CompactionJob::Run():End"}});
|
||||
} else {
|
||||
// ensure the auto-compaction doesn't finish until manual compaction has
|
||||
// continued without delay.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWaitDone", "CompactionJob::Run():End"}});
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int j = 0; j < kNumL0FilesLimit - 1; ++j) {
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
ASSERT_OK(Put(Key(k), RandomString(&rnd, 1024)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.allow_write_stall = false;
|
||||
db_->CompactRange(cro, nullptr, nullptr);
|
||||
});
|
||||
|
||||
manual_compaction_thread.join();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactRangeDelayedByImmMemTableCount) {
|
||||
// Verify that, when `CompactRangeOptions::allow_write_stall == false`, manual
|
||||
// compaction only triggers flush after it's sure stall won't be triggered for
|
||||
// immutable memtable count going too high.
|
||||
const int kNumImmMemTableLimit = 8;
|
||||
// i == 0: verifies normal case where stall is avoided by delay
|
||||
// i == 1: verifies no delay in edge case where stall trigger is same as flush
|
||||
// trigger, so stall can't be avoided
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
// the delay limit is one less than the stop limit. This test focuses on
|
||||
// avoiding delay limit, but this option sets stop limit, so add one.
|
||||
options.max_write_buffer_number = kNumImmMemTableLimit + 1;
|
||||
if (i == 1) {
|
||||
options.min_write_buffer_number_to_merge = kNumImmMemTableLimit;
|
||||
}
|
||||
Reopen(options);
|
||||
|
||||
if (i == 0) {
|
||||
// ensure the flush doesn't finish until manual compaction has had a
|
||||
// chance to be delayed.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWait", "FlushJob::WriteLevel0Table"}});
|
||||
} else {
|
||||
// ensure the flush doesn't finish until manual compaction has continued
|
||||
// without delay.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWaitDone",
|
||||
"FlushJob::WriteLevel0Table"}});
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int j = 0; j < kNumImmMemTableLimit - 1; ++j) {
|
||||
ASSERT_OK(Put(Key(0), RandomString(&rnd, 1024)));
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
dbfull()->Flush(flush_opts);
|
||||
}
|
||||
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.allow_write_stall = false;
|
||||
db_->CompactRange(cro, nullptr, nullptr);
|
||||
});
|
||||
|
||||
manual_compaction_thread.join();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactRangeShutdownWhileDelayed) {
|
||||
// Verify that, when `CompactRangeOptions::allow_write_stall == false`, delay
|
||||
// does not hang if CF is dropped or DB is closed
|
||||
const int kNumL0FilesTrigger = 4;
|
||||
const int kNumL0FilesLimit = 8;
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0FilesTrigger;
|
||||
options.level0_slowdown_writes_trigger = kNumL0FilesLimit;
|
||||
// i == 0: DB::DropColumnFamily() on CompactRange's target CF unblocks it
|
||||
// i == 1: DB::CancelAllBackgroundWork() unblocks CompactRange. This is to
|
||||
// simulate what happens during Close as we can't call Close (it
|
||||
// blocks on the auto-compaction, making a cycle).
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
CreateAndReopenWithCF({"one"}, options);
|
||||
// The calls to close CF/DB wait until the manual compaction stalls.
|
||||
// The auto-compaction waits until the manual compaction finishes to ensure
|
||||
// the signal comes from closing CF/DB, not from compaction making progress.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWait",
|
||||
"DBCompactionTest::CompactRangeShutdownWhileDelayed:PreShutdown"},
|
||||
{"DBCompactionTest::CompactRangeShutdownWhileDelayed:PostManual",
|
||||
"CompactionJob::Run():End"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int j = 0; j < kNumL0FilesLimit - 1; ++j) {
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
ASSERT_OK(Put(1, Key(k), RandomString(&rnd, 1024)));
|
||||
}
|
||||
Flush(1);
|
||||
}
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.allow_write_stall = false;
|
||||
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
|
||||
.IsShutdownInProgress());
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::CompactRangeShutdownWhileDelayed:PreShutdown");
|
||||
if (i == 0) {
|
||||
ASSERT_OK(db_->DropColumnFamily(handles_[1]));
|
||||
} else {
|
||||
dbfull()->CancelAllBackgroundWork(false /* wait */);
|
||||
}
|
||||
manual_compaction_thread.join();
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::CompactRangeShutdownWhileDelayed:PostManual");
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactRangeSkipFlushAfterDelay) {
|
||||
// Verify that, when `CompactRangeOptions::allow_write_stall == false`,
|
||||
// CompactRange skips its flush if the delay is long enough that the memtables
|
||||
// existing at the beginning of the call have already been flushed.
|
||||
const int kNumL0FilesTrigger = 4;
|
||||
const int kNumL0FilesLimit = 8;
|
||||
Options options = CurrentOptions();
|
||||
options.level0_slowdown_writes_trigger = kNumL0FilesLimit;
|
||||
options.level0_file_num_compaction_trigger = kNumL0FilesTrigger;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
// The manual flush includes the memtable that was active when CompactRange
|
||||
// began. So it unblocks CompactRange and precludes its flush. Throughout the
|
||||
// test, stall conditions are upheld via high L0 file count.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CompactRange:StallWait",
|
||||
"DBCompactionTest::CompactRangeSkipFlushAfterDelay:PreFlush"},
|
||||
{"DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush",
|
||||
"DBImpl::CompactRange:StallWaitDone"},
|
||||
{"DBImpl::CompactRange:StallWaitDone", "CompactionJob::Run():End"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int i = 0; i < kNumL0FilesLimit - 1; ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.allow_write_stall = false;
|
||||
db_->CompactRange(cro, nullptr, nullptr);
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactRangeSkipFlushAfterDelay:PreFlush");
|
||||
Put(ToString(0), RandomString(&rnd, 1024));
|
||||
Flush();
|
||||
Put(ToString(0), RandomString(&rnd, 1024));
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush");
|
||||
manual_compaction_thread.join();
|
||||
|
||||
// If CompactRange's flush was skipped, the final Put above will still be
|
||||
// in the active memtable.
|
||||
std::string num_keys_in_memtable;
|
||||
db_->GetProperty(DB::Properties::kNumEntriesActiveMemTable, &num_keys_in_memtable);
|
||||
ASSERT_EQ(ToString(1), num_keys_in_memtable);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactRangeFlushOverlappingMemtable) {
|
||||
// Verify memtable only gets flushed if it contains data overlapping the range
|
||||
// provided to `CompactRange`. Tests all kinds of overlap/non-overlap.
|
||||
const int kNumEndpointKeys = 5;
|
||||
std::string keys[kNumEndpointKeys] = {"a", "b", "c", "d", "e"};
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
|
||||
// One extra iteration for nullptr, which means left side of interval is
|
||||
// unbounded.
|
||||
for (int i = 0; i <= kNumEndpointKeys; ++i) {
|
||||
Slice begin;
|
||||
Slice* begin_ptr;
|
||||
if (i == 0) {
|
||||
begin_ptr = nullptr;
|
||||
} else {
|
||||
begin = keys[i - 1];
|
||||
begin_ptr = &begin;
|
||||
}
|
||||
// Start at `i` so right endpoint comes after left endpoint. One extra
|
||||
// iteration for nullptr, which means right side of interval is unbounded.
|
||||
for (int j = std::max(0, i - 1); j <= kNumEndpointKeys; ++j) {
|
||||
Slice end;
|
||||
Slice* end_ptr;
|
||||
if (j == kNumEndpointKeys) {
|
||||
end_ptr = nullptr;
|
||||
} else {
|
||||
end = keys[j];
|
||||
end_ptr = &end;
|
||||
}
|
||||
ASSERT_OK(Put("b", "val"));
|
||||
ASSERT_OK(Put("d", "val"));
|
||||
CompactRangeOptions compact_range_opts;
|
||||
ASSERT_OK(db_->CompactRange(compact_range_opts, begin_ptr, end_ptr));
|
||||
|
||||
uint64_t get_prop_tmp, num_memtable_entries = 0;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesImmMemTables,
|
||||
&get_prop_tmp));
|
||||
num_memtable_entries += get_prop_tmp;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&get_prop_tmp));
|
||||
num_memtable_entries += get_prop_tmp;
|
||||
if (begin_ptr == nullptr || end_ptr == nullptr ||
|
||||
(i <= 4 && j >= 1 && (begin != "c" || end != "c"))) {
|
||||
// In this case `CompactRange`'s range overlapped in some way with the
|
||||
// memtable's range, so flush should've happened. Then "b" and "d" won't
|
||||
// be in the memtable.
|
||||
ASSERT_EQ(0, num_memtable_entries);
|
||||
} else {
|
||||
ASSERT_EQ(2, num_memtable_entries);
|
||||
// flush anyways to prepare for next iteration
|
||||
db_->Flush(FlushOptions());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithParam, DBCompactionTestWithParam,
|
||||
::testing::Values(std::make_tuple(1, true),
|
||||
std::make_tuple(1, false),
|
||||
@@ -3481,8 +3095,6 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -501,8 +501,6 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
+1
-14
@@ -57,7 +57,6 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Enabled");
|
||||
should_purge_files = true;
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
bg_cv_.SignalAll();
|
||||
} else {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
@@ -93,7 +92,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
status = FlushMemTable(cfd, FlushOptions());
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
@@ -142,18 +141,6 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
{
|
||||
// If caller disabled deletions, this function should return files that are
|
||||
// guaranteed not to be deleted until deletions are re-enabled. We need to
|
||||
// wait for pending purges to finish since WalManager doesn't know which
|
||||
// files are going to be purged. Additional purges won't be scheduled as
|
||||
// long as deletions are disabled (so the below loop must terminate).
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while (disable_delete_obsolete_files_ > 0 &&
|
||||
pending_purge_obsolete_files_ > 0) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
return wal_manager_.GetSortedWalFiles(files);
|
||||
}
|
||||
|
||||
|
||||
@@ -185,26 +185,6 @@ TEST_P(DBFlushDirectIOTest, DirectIO) {
|
||||
delete options.env;
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, FlushError) {
|
||||
Options options;
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
options.write_buffer_size = 100;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = fault_injection_env.get();
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("key1", "value1"));
|
||||
ASSERT_OK(Put("key2", "value2"));
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
Status s = dbfull()->TEST_SwitchMemtable();
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
Destroy(options);
|
||||
ASSERT_NE(s, Status::OK());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBFlushDirectIOTest, DBFlushDirectIOTest,
|
||||
testing::Bool());
|
||||
|
||||
|
||||
+51
-108
@@ -123,13 +123,13 @@ CompressionType GetCompressionFlush(
|
||||
namespace {
|
||||
void DumpSupportInfo(Logger* logger) {
|
||||
ROCKS_LOG_HEADER(logger, "Compression algorithms supported:");
|
||||
for (auto& compression : OptionsHelper::compression_type_string_map) {
|
||||
if (compression.second != kNoCompression &&
|
||||
compression.second != kDisableCompressionOption) {
|
||||
ROCKS_LOG_HEADER(logger, "\t%s supported: %d", compression.first.c_str(),
|
||||
CompressionTypeSupported(compression.second));
|
||||
}
|
||||
}
|
||||
ROCKS_LOG_HEADER(logger, "\tSnappy supported: %d", Snappy_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tZlib supported: %d", Zlib_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tBzip supported: %d", BZip2_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tLZ4 supported: %d", LZ4_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tZSTDNotFinal supported: %d",
|
||||
ZSTDNotFinal_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tZSTD supported: %d", ZSTD_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "Fast CRC32 supported: %s",
|
||||
crc32c::IsFastCrc32Supported().c_str());
|
||||
}
|
||||
@@ -141,7 +141,6 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
const bool seq_per_batch)
|
||||
: env_(options.env),
|
||||
dbname_(dbname),
|
||||
own_info_log_(options.info_log == nullptr),
|
||||
initial_db_options_(SanitizeOptions(dbname, options)),
|
||||
immutable_db_options_(initial_db_options_),
|
||||
mutable_db_options_(initial_db_options_),
|
||||
@@ -178,7 +177,6 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
num_running_flushes_(0),
|
||||
bg_purge_scheduled_(0),
|
||||
disable_delete_obsolete_files_(0),
|
||||
pending_purge_obsolete_files_(0),
|
||||
delete_obsolete_files_last_run_(env_->NowMicros()),
|
||||
last_stats_dump_time_microsec_(0),
|
||||
next_job_id_(1),
|
||||
@@ -214,8 +212,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
// requires a custom gc for compaction, we use that to set use_custom_gc_
|
||||
// as well.
|
||||
use_custom_gc_(seq_per_batch),
|
||||
preserve_deletes_(options.preserve_deletes),
|
||||
closed_(false) {
|
||||
preserve_deletes_(options.preserve_deletes) {
|
||||
env_->GetAbsolutePath(dbname, &db_absolute_path_);
|
||||
|
||||
// Reserve ten files or so for other uses and give the rest to TableCache.
|
||||
@@ -258,7 +255,7 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
if (!cfd->IsDropped() && cfd->initialized() && !cfd->mem()->IsEmpty()) {
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
FlushMemTable(cfd, FlushOptions(), FlushReason::kShutDown);
|
||||
FlushMemTable(cfd, FlushOptions());
|
||||
mutex_.Lock();
|
||||
cfd->Unref();
|
||||
}
|
||||
@@ -278,7 +275,7 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::CloseHelper() {
|
||||
DBImpl::~DBImpl() {
|
||||
// CancelAllBackgroundWork called with false means we just set the shutdown
|
||||
// marker. After this we do a variant of the waiting and unschedule work
|
||||
// (to consider: moving all the waiting into CancelAllBackgroundWork(true))
|
||||
@@ -287,7 +284,6 @@ Status DBImpl::CloseHelper() {
|
||||
env_->UnSchedule(this, Env::Priority::BOTTOM);
|
||||
int compactions_unscheduled = env_->UnSchedule(this, Env::Priority::LOW);
|
||||
int flushes_unscheduled = env_->UnSchedule(this, Env::Priority::HIGH);
|
||||
Status ret;
|
||||
mutex_.Lock();
|
||||
bg_bottom_compaction_scheduled_ -= bottom_compactions_unscheduled;
|
||||
bg_compaction_scheduled_ -= compactions_unscheduled;
|
||||
@@ -295,8 +291,7 @@ Status DBImpl::CloseHelper() {
|
||||
|
||||
// Wait for background work to finish
|
||||
while (bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ || bg_purge_scheduled_ ||
|
||||
pending_purge_obsolete_files_) {
|
||||
bg_flush_scheduled_ || bg_purge_scheduled_) {
|
||||
TEST_SYNC_POINT("DBImpl::~DBImpl:WaitJob");
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
@@ -350,18 +345,7 @@ Status DBImpl::CloseHelper() {
|
||||
delete l;
|
||||
}
|
||||
for (auto& log : logs_) {
|
||||
uint64_t log_number = log.writer->get_log_number();
|
||||
Status s = log.ClearWriter();
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"Unable to Sync WAL file %s with error -- %s",
|
||||
LogFileName(immutable_db_options_.wal_dir, log_number).c_str(),
|
||||
s.ToString().c_str());
|
||||
// Retain the first error
|
||||
if (ret.ok()) {
|
||||
ret = s;
|
||||
}
|
||||
}
|
||||
log.ClearWriter();
|
||||
}
|
||||
logs_.clear();
|
||||
|
||||
@@ -394,25 +378,6 @@ Status DBImpl::CloseHelper() {
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Shutdown complete");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
if (immutable_db_options_.info_log && own_info_log_) {
|
||||
Status s = immutable_db_options_.info_log->Close();
|
||||
if (ret.ok()) {
|
||||
ret = s;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Status DBImpl::CloseImpl() {
|
||||
return CloseHelper();
|
||||
}
|
||||
|
||||
DBImpl::~DBImpl() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
CloseHelper();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::MaybeIgnoreError(Status* s) const {
|
||||
@@ -558,7 +523,6 @@ Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
|
||||
persist_options_status = WriteOptionsFile(
|
||||
false /*need_mutex_lock*/, true /*need_enter_write_thread*/);
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
}
|
||||
sv_context.Clean();
|
||||
@@ -1443,7 +1407,6 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
}
|
||||
is_snapshot_supported_ = new_is_snapshot_supported;
|
||||
}
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -2084,7 +2047,8 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
name.c_str());
|
||||
return Status::NotSupported("Delete only supported for archived logs");
|
||||
}
|
||||
status = wal_manager_.DeleteFile(name, number);
|
||||
status =
|
||||
env_->DeleteFile(immutable_db_options_.wal_dir + "/" + name.c_str());
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"DeleteFile %s failed -- %s.\n", name.c_str(),
|
||||
@@ -2163,63 +2127,52 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end) {
|
||||
Status DBImpl::DeleteFilesInRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
Status status;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
VersionEdit edit;
|
||||
std::set<FileMetaData*> deleted_files;
|
||||
std::vector<FileMetaData*> deleted_files;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
Version* input_version = cfd->current();
|
||||
|
||||
auto* vstorage = input_version->storage_info();
|
||||
for (size_t r = 0; r < n; r++) {
|
||||
auto begin = ranges[r].start, end = ranges[r].limit;
|
||||
for (int i = 1; i < cfd->NumberLevels(); i++) {
|
||||
if (vstorage->LevelFiles(i).empty() ||
|
||||
!vstorage->OverlapInLevel(i, begin, end)) {
|
||||
for (int i = 1; i < cfd->NumberLevels(); i++) {
|
||||
if (vstorage->LevelFiles(i).empty() ||
|
||||
!vstorage->OverlapInLevel(i, begin, end)) {
|
||||
continue;
|
||||
}
|
||||
std::vector<FileMetaData*> level_files;
|
||||
InternalKey begin_storage, end_storage, *begin_key, *end_key;
|
||||
if (begin == nullptr) {
|
||||
begin_key = nullptr;
|
||||
} else {
|
||||
begin_storage.SetMinPossibleForUserKey(*begin);
|
||||
begin_key = &begin_storage;
|
||||
}
|
||||
if (end == nullptr) {
|
||||
end_key = nullptr;
|
||||
} else {
|
||||
end_storage.SetMaxPossibleForUserKey(*end);
|
||||
end_key = &end_storage;
|
||||
}
|
||||
|
||||
vstorage->GetCleanInputsWithinInterval(i, begin_key, end_key,
|
||||
&level_files, -1 /* hint_index */,
|
||||
nullptr /* file_index */);
|
||||
FileMetaData* level_file;
|
||||
for (uint32_t j = 0; j < level_files.size(); j++) {
|
||||
level_file = level_files[j];
|
||||
if (level_file->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
std::vector<FileMetaData*> level_files;
|
||||
InternalKey begin_storage, end_storage, *begin_key, *end_key;
|
||||
if (begin == nullptr) {
|
||||
begin_key = nullptr;
|
||||
} else {
|
||||
begin_storage.SetMinPossibleForUserKey(*begin);
|
||||
begin_key = &begin_storage;
|
||||
}
|
||||
if (end == nullptr) {
|
||||
end_key = nullptr;
|
||||
} else {
|
||||
end_storage.SetMaxPossibleForUserKey(*end);
|
||||
end_key = &end_storage;
|
||||
}
|
||||
|
||||
vstorage->GetCleanInputsWithinInterval(i, begin_key, end_key,
|
||||
&level_files, -1 /* hint_index */,
|
||||
nullptr /* file_index */);
|
||||
FileMetaData* level_file;
|
||||
for (uint32_t j = 0; j < level_files.size(); j++) {
|
||||
level_file = level_files[j];
|
||||
if (level_file->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
if (deleted_files.find(level_file) != deleted_files.end()) {
|
||||
continue;
|
||||
}
|
||||
if (!include_end && end != nullptr &&
|
||||
cfd->user_comparator()->Compare(level_file->largest.user_key(), *end) == 0) {
|
||||
continue;
|
||||
}
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.DeleteFile(i, level_file->fd.GetNumber());
|
||||
deleted_files.insert(level_file);
|
||||
level_file->being_compacted = true;
|
||||
}
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.DeleteFile(i, level_file->fd.GetNumber());
|
||||
deleted_files.push_back(level_file);
|
||||
level_file->being_compacted = true;
|
||||
}
|
||||
}
|
||||
if (edit.GetDeletedFiles().empty()) {
|
||||
@@ -2368,15 +2321,7 @@ Status DB::DestroyColumnFamilyHandle(ColumnFamilyHandle* column_family) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
DB::~DB() {}
|
||||
|
||||
Status DBImpl::Close() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
return CloseImpl();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
DB::~DB() { }
|
||||
|
||||
Status DB::ListColumnFamilies(const DBOptions& db_options,
|
||||
const std::string& name,
|
||||
@@ -2832,14 +2777,12 @@ Status DBImpl::IngestExternalFile(
|
||||
// Figure out if we need to flush the memtable first
|
||||
if (status.ok()) {
|
||||
bool need_flush = false;
|
||||
status = ingestion_job.NeedsFlush(&need_flush, cfd->GetSuperVersion());
|
||||
status = ingestion_job.NeedsFlush(&need_flush);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::IngestExternalFile:NeedFlush",
|
||||
&need_flush);
|
||||
if (status.ok() && need_flush) {
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(),
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
status = FlushMemTable(cfd, FlushOptions(), true /* writes_stopped */);
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
+8
-40
@@ -247,9 +247,8 @@ class DBImpl : public DB {
|
||||
const TransactionLogIterator::ReadOptions&
|
||||
read_options = TransactionLogIterator::ReadOptions()) override;
|
||||
virtual Status DeleteFile(std::string name) override;
|
||||
Status DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end = true);
|
||||
Status DeleteFilesInRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end);
|
||||
|
||||
virtual void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData>* metadata) override;
|
||||
@@ -469,8 +468,6 @@ class DBImpl : public DB {
|
||||
// belong to live files are posibly removed. Also, removes all the
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
// If FindObsoleteFiles() was run, we need to also run
|
||||
// PurgeObsoleteFiles(), even if disable_delete_obsolete_files_ is true
|
||||
void PurgeObsoleteFiles(const JobContext& background_contet,
|
||||
bool schedule_only = false);
|
||||
|
||||
@@ -614,14 +611,10 @@ class DBImpl : public DB {
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
const bool seq_per_batch);
|
||||
|
||||
virtual Status Close() override;
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
const DBOptions initial_db_options_;
|
||||
const ImmutableDBOptions immutable_db_options_;
|
||||
MutableDBOptions mutable_db_options_;
|
||||
@@ -673,14 +666,10 @@ class DBImpl : public DB {
|
||||
// batch which will be written to memtable later during the commit, and in
|
||||
// WritePrepared it is guaranteed since it will be used only for WAL markers
|
||||
// which will never be written to memtable.
|
||||
// batch_cnt is expected to be non-zero in seq_per_batch mode and indicates
|
||||
// the number of sub-patches. A sub-patch is a subset of the write batch that
|
||||
// does not have duplicate keys.
|
||||
Status WriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false, uint64_t* seq_used = nullptr,
|
||||
size_t batch_cnt = 0,
|
||||
PreReleaseCallback* pre_release_callback = nullptr);
|
||||
|
||||
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
@@ -689,13 +678,10 @@ class DBImpl : public DB {
|
||||
bool disable_memtable = false,
|
||||
uint64_t* seq_used = nullptr);
|
||||
|
||||
// batch_cnt is expected to be non-zero in seq_per_batch mode and indicates
|
||||
// the number of sub-patches. A sub-patch is a subset of the write batch that
|
||||
// does not have duplicate keys.
|
||||
Status WriteImplWALOnly(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
uint64_t* seq_used = nullptr, size_t batch_cnt = 0,
|
||||
uint64_t* seq_used = nullptr,
|
||||
PreReleaseCallback* pre_release_callback = nullptr);
|
||||
|
||||
uint64_t FindMinLogContainingOutstandingPrep();
|
||||
@@ -704,9 +690,6 @@ class DBImpl : public DB {
|
||||
// The writer must be the leader in write_thread_ and holding mutex_
|
||||
Status WriteRecoverableState();
|
||||
|
||||
// Actual implementation of Close()
|
||||
Status CloseImpl();
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
friend class InternalStats;
|
||||
@@ -822,7 +805,7 @@ class DBImpl : public DB {
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
FlushReason flush_reason, bool writes_stopped = false);
|
||||
bool writes_stopped = false);
|
||||
|
||||
// Wait for memtable flushed.
|
||||
// If flush_memtable_id is non-null, wait until the memtable with the ID
|
||||
@@ -884,7 +867,7 @@ class DBImpl : public DB {
|
||||
ColumnFamilyData* GetColumnFamilyDataByName(const std::string& cf_name);
|
||||
|
||||
void MaybeScheduleFlushOrCompaction();
|
||||
void SchedulePendingFlush(ColumnFamilyData* cfd, FlushReason flush_reason);
|
||||
void SchedulePendingFlush(ColumnFamilyData* cfd);
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingPurge(std::string fname, FileType type, uint64_t number,
|
||||
uint32_t path_id, int job_id);
|
||||
@@ -923,7 +906,7 @@ class DBImpl : public DB {
|
||||
// helper functions for adding and removing from flush & compaction queues
|
||||
void AddToCompactionQueue(ColumnFamilyData* cfd);
|
||||
ColumnFamilyData* PopFirstFromCompactionQueue();
|
||||
void AddToFlushQueue(ColumnFamilyData* cfd, FlushReason flush_reason);
|
||||
void AddToFlushQueue(ColumnFamilyData* cfd);
|
||||
ColumnFamilyData* PopFirstFromFlushQueue();
|
||||
|
||||
// helper function to call after some of the logs_ were synced
|
||||
@@ -933,8 +916,6 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
Status CloseHelper();
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
@@ -963,10 +944,6 @@ class DBImpl : public DB {
|
||||
// (i.e. whenever a flush is done, even if it didn't make any progress)
|
||||
// * whenever there is an error in background purge, flush or compaction
|
||||
// * whenever num_running_ingest_file_ goes to 0.
|
||||
// * whenever pending_purge_obsolete_files_ goes to 0.
|
||||
// * whenever disable_delete_obsolete_files_ goes to 0.
|
||||
// * whenever SetOptions successfully updates options.
|
||||
// * whenever a column family is dropped.
|
||||
InstrumentedCondVar bg_cv_;
|
||||
// Writes are protected by locking both mutex_ and log_write_mutex_, and reads
|
||||
// must be under either mutex_ or log_write_mutex_. Since after ::Open,
|
||||
@@ -1004,11 +981,9 @@ class DBImpl : public DB {
|
||||
writer = nullptr;
|
||||
return w;
|
||||
}
|
||||
Status ClearWriter() {
|
||||
Status s = writer->WriteBuffer();
|
||||
void ClearWriter() {
|
||||
delete writer;
|
||||
writer = nullptr;
|
||||
return s;
|
||||
}
|
||||
|
||||
uint64_t number;
|
||||
@@ -1235,10 +1210,6 @@ class DBImpl : public DB {
|
||||
// without any synchronization
|
||||
int disable_delete_obsolete_files_;
|
||||
|
||||
// Number of times FindObsoleteFiles has found deletable files and the
|
||||
// corresponding call to PurgeObsoleteFiles has not yet finished.
|
||||
int pending_purge_obsolete_files_;
|
||||
|
||||
// last time when DeleteObsoleteFiles with full scan was executed. Originaly
|
||||
// initialized with startup time.
|
||||
uint64_t delete_obsolete_files_last_run_;
|
||||
@@ -1367,7 +1338,7 @@ class DBImpl : public DB {
|
||||
return Env::WLTH_SHORT;
|
||||
}
|
||||
|
||||
// When set, we use a separate queue for writes that dont write to memtable. In
|
||||
// When set, we use a seprate queue for writes that dont write to memtable. In
|
||||
// 2PC these are the writes at Prepare phase.
|
||||
const bool two_write_queues_;
|
||||
const bool manual_wal_flush_;
|
||||
@@ -1392,9 +1363,6 @@ class DBImpl : public DB {
|
||||
// is set to false.
|
||||
std::atomic<SequenceNumber> preserve_deletes_seqnum_;
|
||||
const bool preserve_deletes_;
|
||||
|
||||
// Flag to check whether Close() has been called on this DB
|
||||
bool closed_;
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
mutex_.AssertHeld();
|
||||
@@ -223,7 +222,6 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
info.smallest_seqno = file_meta->smallest_seqno;
|
||||
info.largest_seqno = file_meta->largest_seqno;
|
||||
info.table_properties = prop;
|
||||
info.flush_reason = cfd->GetFlushReason();
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushBegin(this, info);
|
||||
}
|
||||
@@ -268,7 +266,6 @@ void DBImpl::NotifyOnFlushCompleted(ColumnFamilyData* cfd,
|
||||
info.smallest_seqno = file_meta->smallest_seqno;
|
||||
info.largest_seqno = file_meta->largest_seqno;
|
||||
info.table_properties = prop;
|
||||
info.flush_reason = cfd->GetFlushReason();
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushCompleted(this, info);
|
||||
}
|
||||
@@ -290,75 +287,10 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
auto cfd = cfh->cfd();
|
||||
bool exclusive = options.exclusive_manual_compaction;
|
||||
|
||||
bool flush_needed = true;
|
||||
if (begin != nullptr && end != nullptr) {
|
||||
// TODO(ajkr): We could also optimize away the flush in certain cases where
|
||||
// one/both sides of the interval are unbounded. But it requires more
|
||||
// changes to RangesOverlapWithMemtables.
|
||||
Range range(*begin, *end);
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
cfd->RangesOverlapWithMemtables({range}, super_version, &flush_needed);
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
|
||||
if (!options.allow_write_stall && flush_needed) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
uint64_t orig_active_memtable_id = cfd->mem()->GetID();
|
||||
WriteStallCondition write_stall_condition = WriteStallCondition::kNormal;
|
||||
do {
|
||||
if (write_stall_condition != WriteStallCondition::kNormal) {
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:StallWait");
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] CompactRange waiting on stall conditions to clear",
|
||||
cfd->GetName().c_str());
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
if (cfd->IsDropped() || shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
|
||||
uint64_t earliest_memtable_id =
|
||||
std::min(cfd->mem()->GetID(), cfd->imm()->GetEarliestMemTableID());
|
||||
if (earliest_memtable_id > orig_active_memtable_id) {
|
||||
// We waited so long that the memtable we were originally waiting on was
|
||||
// flushed.
|
||||
flush_needed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
|
||||
// Skip stalling check if we're below auto-flush and auto-compaction
|
||||
// triggers. If it stalled in these conditions, that'd mean the stall
|
||||
// triggers are so low that stalling is needed for any background work. In
|
||||
// that case we shouldn't wait since background work won't be scheduled.
|
||||
if (cfd->imm()->NumNotFlushed() <
|
||||
cfd->ioptions()->min_write_buffer_number_to_merge &&
|
||||
vstorage->l0_delay_trigger_count() <
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
break;
|
||||
}
|
||||
|
||||
// check whether one extra immutable memtable or an extra L0 file would
|
||||
// cause write stalling mode to be entered. It could still enter stall
|
||||
// mode due to pending compaction bytes, but that's less common
|
||||
write_stall_condition =
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->imm()->NumNotFlushed() + 1,
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options)
|
||||
.first;
|
||||
} while (write_stall_condition != WriteStallCondition::kNormal);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:StallWaitDone");
|
||||
Status s;
|
||||
if (flush_needed) {
|
||||
s = FlushMemTable(cfd, FlushOptions(), FlushReason::kManualCompaction);
|
||||
if (!s.ok()) {
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
Status s = FlushMemTable(cfd, FlushOptions());
|
||||
if (!s.ok()) {
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
|
||||
int max_level_with_files = 0;
|
||||
@@ -509,8 +441,7 @@ Status DBImpl::CompactFiles(
|
||||
} // release the mutex
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
@@ -880,8 +811,7 @@ Status DBImpl::Flush(const FlushOptions& flush_options,
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "[%s] Manual flush start.",
|
||||
cfh->GetName().c_str());
|
||||
Status s =
|
||||
FlushMemTable(cfh->cfd(), flush_options, FlushReason::kManualCompaction);
|
||||
Status s = FlushMemTable(cfh->cfd(), flush_options);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] Manual flush finished, status: %s\n",
|
||||
cfh->GetName().c_str(), s.ToString().c_str());
|
||||
@@ -1018,7 +948,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
|
||||
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
FlushReason flush_reason, bool writes_stopped) {
|
||||
bool writes_stopped) {
|
||||
Status s;
|
||||
uint64_t flush_memtable_id = 0;
|
||||
{
|
||||
@@ -1047,7 +977,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
cfd->imm()->FlushRequested();
|
||||
|
||||
// schedule flush
|
||||
SchedulePendingFlush(cfd, flush_reason);
|
||||
SchedulePendingFlush(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
@@ -1140,7 +1070,6 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
if (HasExclusiveManualCompaction()) {
|
||||
// only manual compactions are allowed to run. don't schedule automatic
|
||||
// compactions
|
||||
TEST_SYNC_POINT("DBImpl::MaybeScheduleFlushOrCompaction:Conflict");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1203,12 +1132,11 @@ ColumnFamilyData* DBImpl::PopFirstFromCompactionQueue() {
|
||||
return cfd;
|
||||
}
|
||||
|
||||
void DBImpl::AddToFlushQueue(ColumnFamilyData* cfd, FlushReason flush_reason) {
|
||||
void DBImpl::AddToFlushQueue(ColumnFamilyData* cfd) {
|
||||
assert(!cfd->pending_flush());
|
||||
cfd->Ref();
|
||||
flush_queue_.push_back(cfd);
|
||||
cfd->set_pending_flush(true);
|
||||
cfd->SetFlushReason(flush_reason);
|
||||
}
|
||||
|
||||
ColumnFamilyData* DBImpl::PopFirstFromFlushQueue() {
|
||||
@@ -1217,14 +1145,12 @@ ColumnFamilyData* DBImpl::PopFirstFromFlushQueue() {
|
||||
flush_queue_.pop_front();
|
||||
assert(cfd->pending_flush());
|
||||
cfd->set_pending_flush(false);
|
||||
// TODO: need to unset flush reason?
|
||||
return cfd;
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingFlush(ColumnFamilyData* cfd,
|
||||
FlushReason flush_reason) {
|
||||
void DBImpl::SchedulePendingFlush(ColumnFamilyData* cfd) {
|
||||
if (!cfd->pending_flush() && cfd->imm()->IsFlushPending()) {
|
||||
AddToFlushQueue(cfd, flush_reason);
|
||||
AddToFlushQueue(cfd);
|
||||
++unscheduled_flushes_;
|
||||
}
|
||||
}
|
||||
@@ -1388,10 +1314,8 @@ void DBImpl::BackgroundCallFlush() {
|
||||
// created. Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FilesFound");
|
||||
// Have to flush the info logs before bg_flush_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
@@ -1471,8 +1395,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
@@ -1591,7 +1514,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
: m->manual_end->DebugString().c_str()));
|
||||
}
|
||||
} else if (!is_prepicked && !compaction_queue_.empty()) {
|
||||
if (HasExclusiveManualCompaction()) {
|
||||
if (HaveManualCompaction(compaction_queue_.front())) {
|
||||
// Can't compact right now, but try again later
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction()::Conflict");
|
||||
|
||||
@@ -1767,7 +1690,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
env_->Schedule(&DBImpl::BGWorkBottomCompaction, ca, Env::Priority::BOTTOM,
|
||||
this, &DBImpl::UnscheduleCallback);
|
||||
} else {
|
||||
int output_level __attribute__((__unused__));
|
||||
int output_level __attribute__((unused));
|
||||
output_level = c->output_level();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:NonTrivial",
|
||||
&output_level);
|
||||
@@ -2001,7 +1924,7 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
|
||||
// Whenever we install new SuperVersion, we might need to issue new flushes or
|
||||
// compactions.
|
||||
SchedulePendingFlush(cfd, FlushReason::kSuperVersionChange);
|
||||
SchedulePendingFlush(cfd);
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ Status DBImpl::TEST_FlushMemTable(bool wait, ColumnFamilyHandle* cfh) {
|
||||
auto cfhi = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh);
|
||||
cfd = cfhi->cfd();
|
||||
}
|
||||
return FlushMemTable(cfd, fo, FlushReason::kTest);
|
||||
return FlushMemTable(cfd, fo);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
|
||||
+6
-13
@@ -285,9 +285,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
job_context->logs_to_free = logs_to_free_;
|
||||
job_context->log_recycle_files.assign(log_recycle_files_.begin(),
|
||||
log_recycle_files_.end());
|
||||
if (job_context->HaveSomethingToDelete()) {
|
||||
++pending_purge_obsolete_files_;
|
||||
}
|
||||
logs_to_free_.clear();
|
||||
}
|
||||
|
||||
@@ -345,12 +342,15 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
TEST_SYNC_POINT("DBImpl::PurgeObsoleteFiles:Begin");
|
||||
// we'd better have sth to delete
|
||||
assert(state.HaveSomethingToDelete());
|
||||
|
||||
// FindObsoleteFiles() should've populated this so nonzero
|
||||
assert(state.manifest_file_number != 0);
|
||||
// this checks if FindObsoleteFiles() was run before. If not, don't do
|
||||
// PurgeObsoleteFiles(). If FindObsoleteFiles() was run, we need to also
|
||||
// run PurgeObsoleteFiles(), even if disable_delete_obsolete_files_ is true
|
||||
if (state.manifest_file_number == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now, convert live list to an unordered map, WITHOUT mutex held;
|
||||
// set is slow.
|
||||
@@ -533,13 +533,6 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
wal_manager_.PurgeObsoleteWALFiles();
|
||||
#endif // ROCKSDB_LITE
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
--pending_purge_obsolete_files_;
|
||||
assert(pending_purge_obsolete_files_ >= 0);
|
||||
if (pending_purge_obsolete_files_ == 0) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::PurgeObsoleteFiles:End");
|
||||
}
|
||||
|
||||
void DBImpl::DeleteObsoleteFiles() {
|
||||
|
||||
@@ -356,29 +356,6 @@ Status DBImpl::Recover(
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
// Verify compatibility of env_options_ and filesystem
|
||||
{
|
||||
unique_ptr<RandomAccessFile> idfile;
|
||||
EnvOptions customized_env(env_options_);
|
||||
customized_env.use_direct_reads |=
|
||||
immutable_db_options_.use_direct_io_for_flush_and_compaction;
|
||||
s = env_->NewRandomAccessFile(IdentityFileName(dbname_), &idfile,
|
||||
customized_env);
|
||||
if (!s.ok()) {
|
||||
const char* error_msg = s.ToString().c_str();
|
||||
// Check if unsupported Direct I/O is the root cause
|
||||
customized_env.use_direct_reads = false;
|
||||
s = env_->NewRandomAccessFile(IdentityFileName(dbname_), &idfile,
|
||||
customized_env);
|
||||
if (s.ok()) {
|
||||
return Status::InvalidArgument(
|
||||
"Direct I/O is not supported by the specified DB.");
|
||||
} else {
|
||||
return Status::InvalidArgument(
|
||||
"Found options incompatible with filesystem", error_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status s = versions_->Recover(column_families, read_only);
|
||||
|
||||
+29
-59
@@ -64,9 +64,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable, uint64_t* seq_used,
|
||||
size_t batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback) {
|
||||
assert(!seq_per_batch_ || batch_cnt != 0);
|
||||
if (my_batch == nullptr) {
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
}
|
||||
@@ -78,7 +76,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
"pipelined_writes is not compatible with concurrent prepares");
|
||||
}
|
||||
if (seq_per_batch_ && immutable_db_options_.enable_pipelined_write) {
|
||||
// TODO(yiwu): update pipeline write with seq_per_batch and batch_cnt
|
||||
return Status::NotSupported(
|
||||
"pipelined_writes is not compatible with seq_per_batch");
|
||||
}
|
||||
@@ -96,7 +93,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (two_write_queues_ && disable_memtable) {
|
||||
return WriteImplWALOnly(write_options, my_batch, callback, log_used,
|
||||
log_ref, seq_used, batch_cnt, pre_release_callback);
|
||||
log_ref, seq_used, pre_release_callback);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
@@ -106,7 +103,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, batch_cnt, pre_release_callback);
|
||||
disable_memtable, pre_release_callback);
|
||||
|
||||
if (!write_options.disableWAL) {
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
@@ -125,7 +122,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
true /*concurrent_memtable_writes*/, seq_per_batch_, w.batch_cnt);
|
||||
true /*concurrent_memtable_writes*/, seq_per_batch_);
|
||||
}
|
||||
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
@@ -217,7 +214,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
uint64_t total_byte_size = 0;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
valid_batches += writer->batch_cnt;
|
||||
valid_batches++;
|
||||
if (writer->ShouldWriteToMemtable()) {
|
||||
total_count += WriteBatchInternal::Count(writer->batch);
|
||||
parallel = parallel && !writer->batch->HasMerge();
|
||||
@@ -306,8 +303,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
writer->sequence = next_sequence;
|
||||
if (seq_per_batch_) {
|
||||
assert(writer->batch_cnt);
|
||||
next_sequence += writer->batch_cnt;
|
||||
next_sequence++;
|
||||
} else if (writer->ShouldWriteToMemtable()) {
|
||||
next_sequence += WriteBatchInternal::Count(writer->batch);
|
||||
}
|
||||
@@ -327,8 +323,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/,
|
||||
this, true /*concurrent_memtable_writes*/, seq_per_batch_,
|
||||
w.batch_cnt);
|
||||
this, true /*concurrent_memtable_writes*/, seq_per_batch_);
|
||||
}
|
||||
}
|
||||
if (seq_used != nullptr) {
|
||||
@@ -520,13 +515,12 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
uint64_t* seq_used, size_t batch_cnt,
|
||||
uint64_t* seq_used,
|
||||
PreReleaseCallback* pre_release_callback) {
|
||||
Status status;
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
true /* disable_memtable */, batch_cnt,
|
||||
pre_release_callback);
|
||||
true /* disable_memtable */, pre_release_callback);
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
@@ -582,15 +576,7 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
// LastAllocatedSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
size_t seq_inc = 0 /* total_count */;
|
||||
if (seq_per_batch_) {
|
||||
size_t total_batch_cnt = 0;
|
||||
for (auto* writer : write_group) {
|
||||
assert(writer->batch_cnt);
|
||||
total_batch_cnt += writer->batch_cnt;
|
||||
}
|
||||
seq_inc = total_batch_cnt;
|
||||
}
|
||||
size_t seq_inc = seq_per_batch_ ? write_group.size : 0 /*total_count*/;
|
||||
if (!write_options.disableWAL) {
|
||||
status =
|
||||
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
@@ -605,8 +591,7 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
}
|
||||
writer->sequence = curr_seq;
|
||||
if (seq_per_batch_) {
|
||||
assert(writer->batch_cnt);
|
||||
curr_seq += writer->batch_cnt;
|
||||
curr_seq++;
|
||||
}
|
||||
// else seq advances only by memtable writes
|
||||
}
|
||||
@@ -1019,7 +1004,7 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
break;
|
||||
}
|
||||
cfd->imm()->FlushRequested();
|
||||
SchedulePendingFlush(cfd, FlushReason::kWriteBufferManager);
|
||||
SchedulePendingFlush(cfd);
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -1065,7 +1050,7 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
status = SwitchMemtable(cfd_picked, write_context);
|
||||
if (status.ok()) {
|
||||
cfd_picked->imm()->FlushRequested();
|
||||
SchedulePendingFlush(cfd_picked, FlushReason::kWriteBufferFull);
|
||||
SchedulePendingFlush(cfd_picked);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
}
|
||||
@@ -1307,29 +1292,6 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
". Immutable memtables: %d.\n",
|
||||
cfd->GetName().c_str(), new_log_number, num_imm_unflushed);
|
||||
mutex_.Lock();
|
||||
if (s.ok() && creating_new_log) {
|
||||
log_write_mutex_.Lock();
|
||||
logfile_number_ = new_log_number;
|
||||
assert(new_log != nullptr);
|
||||
log_empty_ = true;
|
||||
log_dir_synced_ = false;
|
||||
if (!logs_.empty()) {
|
||||
// Alway flush the buffer of the last log before switching to a new one
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
s = cur_log_writer->WriteBuffer();
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] Failed to switch from #%" PRIu64 " to #%" PRIu64
|
||||
" WAL file -- %s\n",
|
||||
cfd->GetName().c_str(), cur_log_writer->get_log_number(),
|
||||
new_log_number);
|
||||
}
|
||||
}
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
// how do we fail if we're not creating new log?
|
||||
assert(creating_new_log);
|
||||
@@ -1340,7 +1302,21 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
if (creating_new_log) {
|
||||
log_write_mutex_.Lock();
|
||||
logfile_number_ = new_log_number;
|
||||
assert(new_log != nullptr);
|
||||
log_empty_ = true;
|
||||
log_dir_synced_ = false;
|
||||
if (!logs_.empty()) {
|
||||
// Alway flush the buffer of the last log before switching to a new one
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
cur_log_writer->WriteBuffer();
|
||||
}
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
for (auto loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
// all this is just optimization to delete logs that
|
||||
// are no longer needed -- if CF is empty, that means it
|
||||
@@ -1395,10 +1371,7 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
// 8 bytes are taken by header, 4 bytes for count, 1 byte for type,
|
||||
// and we allocate 11 extra bytes for key length, as well as value length.
|
||||
WriteBatch batch(key.size() + value.size() + 24);
|
||||
Status s = batch.Put(column_family, key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
batch.Put(column_family, key, value);
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
@@ -1427,10 +1400,7 @@ Status DB::DeleteRange(const WriteOptions& opt,
|
||||
Status DB::Merge(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& value) {
|
||||
WriteBatch batch;
|
||||
Status s = batch.Merge(column_family, key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
batch.Merge(column_family, key, value);
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
@@ -614,12 +614,10 @@ void DBIter::MergeValuesNewToOld() {
|
||||
// Start the merge process by pushing the first operand
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
TEST_SYNC_POINT("DBIter::MergeValuesNewToOld:PushedFirstOperand");
|
||||
|
||||
ParsedInternalKey ikey;
|
||||
Status s;
|
||||
for (iter_->Next(); iter_->Valid(); iter_->Next()) {
|
||||
TEST_SYNC_POINT("DBIter::MergeValuesNewToOld:SteppedToNextOperand");
|
||||
if (!ParseKey(&ikey)) {
|
||||
// skip corrupted key
|
||||
continue;
|
||||
@@ -977,17 +975,6 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
// assume there is at least one parseable key for this user key
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kForward);
|
||||
assert(iter_->Valid());
|
||||
assert(user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()));
|
||||
|
||||
// In case read_callback presents, the value we seek to may not be visible.
|
||||
// Seek for the next value that's visible.
|
||||
while (!IsVisible(ikey.sequence)) {
|
||||
iter_->Next();
|
||||
FindParseableKey(&ikey, kForward);
|
||||
assert(iter_->Valid());
|
||||
assert(user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()));
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
|
||||
+4
-6
@@ -36,9 +36,7 @@ class TestIterator : public InternalIterator {
|
||||
valid_(false),
|
||||
sequence_number_(0),
|
||||
iter_(0),
|
||||
cmp(comparator) {
|
||||
data_.reserve(16);
|
||||
}
|
||||
cmp(comparator) {}
|
||||
|
||||
void AddPut(std::string argkey, std::string argvalue) {
|
||||
Add(argkey, kTypeValue, argvalue);
|
||||
@@ -2551,7 +2549,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace1) {
|
||||
// MergeIterator::Prev() realized the mem table iterator is at its end
|
||||
// and before an SeekToLast() is called.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"MergeIterator::Prev:BeforePrev",
|
||||
"MergeIterator::Prev:BeforeSeekToLast",
|
||||
[&](void* arg) { internal_iter2_->Add("z", kTypeValue, "7", 12u); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -2587,7 +2585,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace2) {
|
||||
// mem table after MergeIterator::Prev() realized the mem tableiterator is at
|
||||
// its end and before an SeekToLast() is called.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"MergeIterator::Prev:BeforePrev", [&](void* arg) {
|
||||
"MergeIterator::Prev:BeforeSeekToLast", [&](void* arg) {
|
||||
internal_iter2_->Add("z", kTypeValue, "7", 12u);
|
||||
internal_iter2_->Add("z", kTypeValue, "7", 11u);
|
||||
});
|
||||
@@ -2625,7 +2623,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace3) {
|
||||
// mem table after MergeIterator::Prev() realized the mem table iterator is at
|
||||
// its end and before an SeekToLast() is called.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"MergeIterator::Prev:BeforePrev", [&](void* arg) {
|
||||
"MergeIterator::Prev:BeforeSeekToLast", [&](void* arg) {
|
||||
internal_iter2_->Add("z", kTypeValue, "7", 16u, true);
|
||||
internal_iter2_->Add("z", kTypeValue, "7", 15u, true);
|
||||
internal_iter2_->Add("z", kTypeValue, "7", 14u, true);
|
||||
|
||||
+101
-346
File diff suppressed because it is too large.
Load diff
@@ -289,8 +289,6 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
+4
-80
@@ -28,11 +28,13 @@ class MockMemTableRep : public MemTableRep {
|
||||
return rep_->Allocate(len, buf);
|
||||
}
|
||||
|
||||
virtual void Insert(KeyHandle handle) override { rep_->Insert(handle); }
|
||||
virtual void Insert(KeyHandle handle) override {
|
||||
return rep_->Insert(handle);
|
||||
}
|
||||
|
||||
virtual void InsertWithHint(KeyHandle handle, void** hint) override {
|
||||
num_insert_with_hint_++;
|
||||
EXPECT_NE(nullptr, hint);
|
||||
ASSERT_NE(nullptr, hint);
|
||||
last_hint_in_ = *hint;
|
||||
rep_->InsertWithHint(handle, hint);
|
||||
last_hint_out_ = *hint;
|
||||
@@ -127,84 +129,6 @@ class TestPrefixExtractor : public SliceTransform {
|
||||
}
|
||||
};
|
||||
|
||||
// Test that ::Add properly returns false when inserting duplicate keys
|
||||
TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
SequenceNumber seq = 123;
|
||||
std::string value;
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
Options options;
|
||||
InternalKeyComparator ikey_cmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(ikey_cmp, {} /* snapshots */);
|
||||
|
||||
// Create a MemTable
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
auto factory = std::make_shared<SkipListFactory>();
|
||||
options.memtable_factory = factory;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
WriteBufferManager wb(options.db_write_buffer_size);
|
||||
MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
|
||||
// Write some keys and make sure it returns false on duplicates
|
||||
bool res;
|
||||
res = mem->Add(seq, kTypeValue, "key", "value2");
|
||||
ASSERT_TRUE(res);
|
||||
res = mem->Add(seq, kTypeValue, "key", "value2");
|
||||
ASSERT_FALSE(res);
|
||||
// Changing the type should still cause the duplicatae key
|
||||
res = mem->Add(seq, kTypeMerge, "key", "value2");
|
||||
ASSERT_FALSE(res);
|
||||
// Changing the seq number will make the key fresh
|
||||
res = mem->Add(seq + 1, kTypeMerge, "key", "value2");
|
||||
ASSERT_TRUE(res);
|
||||
// Test with different types for duplicate keys
|
||||
res = mem->Add(seq, kTypeDeletion, "key", "");
|
||||
ASSERT_FALSE(res);
|
||||
res = mem->Add(seq, kTypeSingleDeletion, "key", "");
|
||||
ASSERT_FALSE(res);
|
||||
|
||||
// Test the duplicate keys under stress
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
bool insert_dup = i % 10 == 1;
|
||||
if (!insert_dup) {
|
||||
seq++;
|
||||
}
|
||||
res = mem->Add(seq, kTypeValue, "foo", "value" + ToString(seq));
|
||||
if (insert_dup) {
|
||||
ASSERT_FALSE(res);
|
||||
} else {
|
||||
ASSERT_TRUE(res);
|
||||
}
|
||||
}
|
||||
delete mem;
|
||||
|
||||
// Test with InsertWithHint
|
||||
options.memtable_insert_with_hint_prefix_extractor.reset(
|
||||
new TestPrefixExtractor()); // which uses _ to extract the prefix
|
||||
ioptions = ImmutableCFOptions(options);
|
||||
mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
// Insert a duplicate key with _ in it
|
||||
res = mem->Add(seq, kTypeValue, "key_1", "value");
|
||||
ASSERT_TRUE(res);
|
||||
res = mem->Add(seq, kTypeValue, "key_1", "value");
|
||||
ASSERT_FALSE(res);
|
||||
delete mem;
|
||||
|
||||
// Test when InsertConcurrently will be invoked
|
||||
options.allow_concurrent_memtable_write = true;
|
||||
ioptions = ImmutableCFOptions(options);
|
||||
mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
MemTablePostProcessInfo post_process_info;
|
||||
res = mem->Add(seq, kTypeValue, "key", "value", true, &post_process_info);
|
||||
ASSERT_TRUE(res);
|
||||
res = mem->Add(seq, kTypeValue, "key", "value", true, &post_process_info);
|
||||
ASSERT_FALSE(res);
|
||||
delete mem;
|
||||
}
|
||||
|
||||
TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
Options options;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
|
||||
@@ -14,43 +14,10 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class TestReadCallback : public ReadCallback {
|
||||
public:
|
||||
TestReadCallback(SnapshotChecker* snapshot_checker,
|
||||
SequenceNumber snapshot_seq)
|
||||
: snapshot_checker_(snapshot_checker), snapshot_seq_(snapshot_seq) {}
|
||||
|
||||
bool IsCommitted(SequenceNumber seq) override {
|
||||
return snapshot_checker_->IsInSnapshot(seq, snapshot_seq_);
|
||||
}
|
||||
|
||||
private:
|
||||
SnapshotChecker* snapshot_checker_;
|
||||
SequenceNumber snapshot_seq_;
|
||||
};
|
||||
|
||||
// Test merge operator functionality.
|
||||
class DBMergeOperatorTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperatorTest() : DBTestBase("/db_merge_operator_test") {}
|
||||
|
||||
std::string GetWithReadCallback(SnapshotChecker* snapshot_checker,
|
||||
const Slice& key,
|
||||
const Snapshot* snapshot = nullptr) {
|
||||
SequenceNumber seq = snapshot == nullptr ? db_->GetLatestSequenceNumber()
|
||||
: snapshot->GetSequenceNumber();
|
||||
TestReadCallback read_callback(snapshot_checker, seq);
|
||||
ReadOptions read_opt;
|
||||
read_opt.snapshot = snapshot;
|
||||
PinnableSlice value;
|
||||
Status s =
|
||||
dbfull()->GetImpl(read_opt, db_->DefaultColumnFamily(), key, &value,
|
||||
nullptr /*value_found*/, &read_callback);
|
||||
if (!s.ok()) {
|
||||
return s.ToString();
|
||||
}
|
||||
return value.ToString();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
@@ -482,152 +449,8 @@ TEST_P(MergeOperatorPinningTest, TailingIterator) {
|
||||
writer_thread.join();
|
||||
reader_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, TailingIteratorMemtableUnrefedBySomeoneElse) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Overview of the test:
|
||||
// * There are two merge operands for the same key: one in an sst file,
|
||||
// another in a memtable.
|
||||
// * Seek a tailing iterator to this key.
|
||||
// * As part of the seek, the iterator will:
|
||||
// (a) first visit the operand in the memtable and tell ForwardIterator
|
||||
// to pin this operand, then
|
||||
// (b) move on to the operand in the sst file, then pass both operands
|
||||
// to merge operator.
|
||||
// * The memtable may get flushed and unreferenced by another thread between
|
||||
// (a) and (b). The test simulates it by flushing the memtable inside a
|
||||
// SyncPoint callback located between (a) and (b).
|
||||
// * In this case it's ForwardIterator's responsibility to keep the memtable
|
||||
// pinned until (b) is complete. There used to be a bug causing
|
||||
// ForwardIterator to not pin it in some circumstances. This test
|
||||
// reproduces it.
|
||||
|
||||
db_->Merge(WriteOptions(), "key", "sst");
|
||||
db_->Flush(FlushOptions()); // Switch to SuperVersion A
|
||||
db_->Merge(WriteOptions(), "key", "memtable");
|
||||
|
||||
// Pin SuperVersion A
|
||||
std::unique_ptr<Iterator> someone_else(db_->NewIterator(ReadOptions()));
|
||||
|
||||
bool pushed_first_operand = false;
|
||||
bool stepped_to_next_operand = false;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBIter::MergeValuesNewToOld:PushedFirstOperand", [&](void*) {
|
||||
EXPECT_FALSE(pushed_first_operand);
|
||||
pushed_first_operand = true;
|
||||
db_->Flush(FlushOptions()); // Switch to SuperVersion B
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBIter::MergeValuesNewToOld:SteppedToNextOperand", [&](void*) {
|
||||
EXPECT_FALSE(stepped_to_next_operand);
|
||||
stepped_to_next_operand = true;
|
||||
someone_else.reset(); // Unpin SuperVersion A
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
iter->Seek("key");
|
||||
|
||||
ASSERT_TRUE(iter->status().ok());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ(std::string("sst,memtable"), iter->value().ToString());
|
||||
EXPECT_TRUE(pushed_first_operand);
|
||||
EXPECT_TRUE(stepped_to_next_operand);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBMergeOperatorTest, SnapshotCheckerAndReadCallback) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
class TestSnapshotChecker : public SnapshotChecker {
|
||||
bool IsInSnapshot(SequenceNumber seq,
|
||||
SequenceNumber snapshot_seq) const override {
|
||||
switch (snapshot_seq) {
|
||||
case 0:
|
||||
return seq == 0;
|
||||
case 1:
|
||||
return seq <= 1;
|
||||
case 2:
|
||||
// seq = 2 not visible to snapshot with seq = 2
|
||||
return seq <= 1;
|
||||
case 3:
|
||||
return seq <= 3;
|
||||
case 4:
|
||||
// seq = 4 not visible to snpahost with seq = 4
|
||||
return seq <= 3;
|
||||
default:
|
||||
// seq >=4 is uncommitted
|
||||
return seq <= 4;
|
||||
};
|
||||
}
|
||||
};
|
||||
TestSnapshotChecker* snapshot_checker = new TestSnapshotChecker();
|
||||
dbfull()->SetSnapshotChecker(snapshot_checker);
|
||||
|
||||
std::string value;
|
||||
ASSERT_OK(Merge("foo", "v1"));
|
||||
ASSERT_EQ(1, db_->GetLatestSequenceNumber());
|
||||
ASSERT_EQ("v1", GetWithReadCallback(snapshot_checker, "foo"));
|
||||
ASSERT_OK(Merge("foo", "v2"));
|
||||
ASSERT_EQ(2, db_->GetLatestSequenceNumber());
|
||||
// v2 is not visible to latest snapshot, which has seq = 2.
|
||||
ASSERT_EQ("v1", GetWithReadCallback(snapshot_checker, "foo"));
|
||||
// Take a snapshot with seq = 2.
|
||||
const Snapshot* snapshot1 = db_->GetSnapshot();
|
||||
ASSERT_EQ(2, snapshot1->GetSequenceNumber());
|
||||
// v2 is not visible to snapshot1, which has seq = 2
|
||||
ASSERT_EQ("v1", GetWithReadCallback(snapshot_checker, "foo", snapshot1));
|
||||
|
||||
// Verify flush doesn't alter the result.
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("v1", GetWithReadCallback(snapshot_checker, "foo", snapshot1));
|
||||
ASSERT_EQ("v1", GetWithReadCallback(snapshot_checker, "foo"));
|
||||
|
||||
ASSERT_OK(Merge("foo", "v3"));
|
||||
ASSERT_EQ(3, db_->GetLatestSequenceNumber());
|
||||
ASSERT_EQ("v1,v2,v3", GetWithReadCallback(snapshot_checker, "foo"));
|
||||
ASSERT_OK(Merge("foo", "v4"));
|
||||
ASSERT_EQ(4, db_->GetLatestSequenceNumber());
|
||||
// v4 is not visible to latest snapshot, which has seq = 4.
|
||||
ASSERT_EQ("v1,v2,v3", GetWithReadCallback(snapshot_checker, "foo"));
|
||||
const Snapshot* snapshot2 = db_->GetSnapshot();
|
||||
ASSERT_EQ(4, snapshot2->GetSequenceNumber());
|
||||
// v4 is not visible to snapshot2, which has seq = 4.
|
||||
ASSERT_EQ("v1,v2,v3",
|
||||
GetWithReadCallback(snapshot_checker, "foo", snapshot2));
|
||||
|
||||
// Verify flush doesn't alter the result.
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("v1", GetWithReadCallback(snapshot_checker, "foo", snapshot1));
|
||||
ASSERT_EQ("v1,v2,v3",
|
||||
GetWithReadCallback(snapshot_checker, "foo", snapshot2));
|
||||
ASSERT_EQ("v1,v2,v3", GetWithReadCallback(snapshot_checker, "foo"));
|
||||
|
||||
ASSERT_OK(Merge("foo", "v5"));
|
||||
ASSERT_EQ(5, db_->GetLatestSequenceNumber());
|
||||
// v5 is uncommitted
|
||||
ASSERT_EQ("v1,v2,v3,v4", GetWithReadCallback(snapshot_checker, "foo"));
|
||||
|
||||
// full manual compaction.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// Verify compaction doesn't alter the result.
|
||||
ASSERT_EQ("v1", GetWithReadCallback(snapshot_checker, "foo", snapshot1));
|
||||
ASSERT_EQ("v1,v2,v3",
|
||||
GetWithReadCallback(snapshot_checker, "foo", snapshot2));
|
||||
ASSERT_EQ("v1,v2,v3,v4", GetWithReadCallback(snapshot_checker, "foo"));
|
||||
|
||||
db_->ReleaseSnapshot(snapshot1);
|
||||
db_->ReleaseSnapshot(snapshot2);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -518,13 +518,8 @@ static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
JobContext job_context(0);
|
||||
dbfull->FindObsoleteFiles(&job_context, false);
|
||||
ASSERT_EQ(empty, job_context.full_scan_candidate_files.empty());
|
||||
dbfull->TEST_UnlockMutex();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
// fulfill the contract of FindObsoleteFiles by calling PurgeObsoleteFiles
|
||||
// afterwards; otherwise the test may hang on shutdown
|
||||
dbfull->PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
job_context.Clean();
|
||||
dbfull->TEST_UnlockMutex();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
|
||||
@@ -809,8 +809,6 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
+36
-102
@@ -480,36 +480,6 @@ TEST_F(DBTest, SingleDeletePutFlush) {
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
}
|
||||
|
||||
// Disable because not all platform can run it.
|
||||
// It requires more than 9GB memory to run it, With single allocation
|
||||
// of more than 3GB.
|
||||
TEST_F(DBTest, DISABLED_SanitizeVeryVeryLargeValue) {
|
||||
const size_t kValueSize = 4 * size_t{1024 * 1024 * 1024}; // 4GB value
|
||||
std::string raw(kValueSize, 'v');
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
options.write_buffer_size = 100000; // Small write buffer
|
||||
options.paranoid_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("boo", "v1"));
|
||||
ASSERT_TRUE(Put("foo", raw).IsInvalidArgument());
|
||||
ASSERT_TRUE(Merge("foo", raw).IsInvalidArgument());
|
||||
|
||||
WriteBatch wb;
|
||||
ASSERT_TRUE(wb.Put("foo", raw).IsInvalidArgument());
|
||||
ASSERT_TRUE(wb.Merge("foo", raw).IsInvalidArgument());
|
||||
|
||||
Slice value_slice = raw;
|
||||
Slice key_slice = "foo";
|
||||
SliceParts sp_key(&key_slice, 1);
|
||||
SliceParts sp_value(&value_slice, 1);
|
||||
|
||||
ASSERT_TRUE(wb.Put(sp_key, sp_value).IsInvalidArgument());
|
||||
ASSERT_TRUE(wb.Merge(sp_key, sp_value).IsInvalidArgument());
|
||||
}
|
||||
|
||||
// Disable because not all platform can run it.
|
||||
// It requires more than 9GB memory to run it, With single allocation
|
||||
// of more than 3GB.
|
||||
@@ -533,9 +503,7 @@ TEST_F(DBTest, DISABLED_VeryLargeValue) {
|
||||
ASSERT_OK(Put(key2, raw));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::string value;
|
||||
Status s = db_->Get(ReadOptions(), key1, &value);
|
||||
@@ -2223,8 +2191,6 @@ class ModelDB : public DB {
|
||||
batch.Put(cf, k, v);
|
||||
return Write(o, &batch);
|
||||
}
|
||||
using DB::Close;
|
||||
virtual Status Close() override { return Status::OK(); }
|
||||
using DB::Delete;
|
||||
virtual Status Delete(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& key) override {
|
||||
@@ -5088,84 +5054,55 @@ TEST_F(DBTest, PromoteL0Failure) {
|
||||
status = experimental::PromoteL0(db_, db_->DefaultColumnFamily());
|
||||
ASSERT_TRUE(status.IsInvalidArgument());
|
||||
}
|
||||
|
||||
// Github issue #596
|
||||
TEST_F(DBTest, CompactRangeWithEmptyBottomLevel) {
|
||||
const int kNumLevels = 2;
|
||||
const int kNumL0Files = 2;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
ASSERT_OK(Put(Key(0), RandomString(&rnd, 1024)));
|
||||
Flush();
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), kNumL0Files);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), kNumL0Files);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, AutomaticConflictsWithManualCompaction) {
|
||||
const int kNumL0Files = 50;
|
||||
// Github issue #596
|
||||
TEST_F(DBTest, HugeNumberOfLevels) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
// never slowdown / stop
|
||||
options.level0_slowdown_writes_trigger = 999999;
|
||||
options.level0_stop_writes_trigger = 999999;
|
||||
options.write_buffer_size = 2 * 1024 * 1024; // 2MB
|
||||
options.max_bytes_for_level_base = 2 * 1024 * 1024; // 2MB
|
||||
options.num_levels = 12;
|
||||
options.max_background_compactions = 10;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// schedule automatic compactions after the manual one starts, but before it
|
||||
// finishes to ensure conflict.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction:Start",
|
||||
"DBTest::AutomaticConflictsWithManualCompaction:PrePuts"},
|
||||
{"DBTest::AutomaticConflictsWithManualCompaction:PostPuts",
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun"}});
|
||||
std::atomic<int> callback_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:Conflict",
|
||||
[&](void* arg) { callback_count.fetch_add(1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, AutomaticConflictsWithManualCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 2 * 1024 * 1024; // 2MB
|
||||
options.max_bytes_for_level_base = 2 * 1024 * 1024; // 2MB
|
||||
options.num_levels = 12;
|
||||
options.max_background_compactions = 10;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// put two keys to ensure no trivial move
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
std::thread manual_compaction_thread([this]() {
|
||||
CompactRangeOptions croptions;
|
||||
croptions.exclusive_manual_compaction = true;
|
||||
ASSERT_OK(db_->CompactRange(croptions, nullptr, nullptr));
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("DBTest::AutomaticConflictsWithManualCompaction:PrePuts");
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
// put two keys to ensure no trivial move
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
TEST_SYNC_POINT("DBTest::AutomaticConflictsWithManualCompaction:PostPuts");
|
||||
|
||||
std::atomic<int> callback_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction()::Conflict",
|
||||
[&](void* arg) { callback_count.fetch_add(1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
CompactRangeOptions croptions;
|
||||
croptions.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(db_->CompactRange(croptions, nullptr, nullptr));
|
||||
ASSERT_GE(callback_count.load(), 1);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(i)));
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
manual_compaction_thread.join();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
// Github issue #595
|
||||
@@ -5385,15 +5322,12 @@ class WriteStallListener : public EventListener {
|
||||
public:
|
||||
WriteStallListener() : condition_(WriteStallCondition::kNormal) {}
|
||||
void OnStallConditionsChanged(const WriteStallInfo& info) override {
|
||||
MutexLock l(&mutex_);
|
||||
condition_ = info.condition.cur;
|
||||
}
|
||||
bool CheckCondition(WriteStallCondition expected) {
|
||||
MutexLock l(&mutex_);
|
||||
return expected == condition_;
|
||||
}
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
WriteStallCondition condition_;
|
||||
};
|
||||
|
||||
|
||||
+5
-65
@@ -1820,17 +1820,11 @@ TEST_F(DBTest2, ReadAmpBitmap) {
|
||||
|
||||
#ifndef OS_SOLARIS // GetUniqueIdFromFile is not implemented
|
||||
TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
{
|
||||
const int kIdBufLen = 100;
|
||||
char id_buf[kIdBufLen];
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
env_->NewRandomAccessFile(dbname_, &file, EnvOptions());
|
||||
if (file->GetUniqueId(id_buf, kIdBufLen) == 0) {
|
||||
// fs holding db directory doesn't support getting a unique file id,
|
||||
// this means that running this test will fail because lru_cache will load
|
||||
// the blocks again regardless of them being already in the cache
|
||||
return;
|
||||
}
|
||||
if (dbname_.find("dev/shm") != std::string::npos) {
|
||||
// /dev/shm dont support getting a unique file id, this mean that
|
||||
// running this test on /dev/shm will fail because lru_cache will load
|
||||
// the blocks again regardless of them being already in the cache
|
||||
return;
|
||||
}
|
||||
uint32_t bytes_per_bit[2] = {1, 16};
|
||||
for (size_t k = 0; k < 2; k++) {
|
||||
@@ -2436,60 +2430,6 @@ TEST_F(DBTest2, ReadCallbackTest) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
// Regression test for race condition where an obsolete file is returned to
|
||||
// user as a "live file" but then deleted, all while file deletions are
|
||||
// disabled.
|
||||
//
|
||||
// It happened like this:
|
||||
//
|
||||
// 1. [flush thread] Log file "x.log" found by FindObsoleteFiles
|
||||
// 2. [user thread] DisableFileDeletions, GetSortedWalFiles are called and the
|
||||
// latter returned "x.log"
|
||||
// 3. [flush thread] PurgeObsoleteFiles deleted "x.log"
|
||||
// 4. [user thread] Reading "x.log" failed
|
||||
//
|
||||
// Unfortunately the only regression test I can come up with involves sleep.
|
||||
// We cannot set SyncPoints to repro since, once the fix is applied, the
|
||||
// SyncPoints would cause a deadlock as the repro's sequence of events is now
|
||||
// prohibited.
|
||||
//
|
||||
// Instead, if we sleep for a second between Find and Purge, and ensure the
|
||||
// read attempt happens after purge, then the sequence of events will almost
|
||||
// certainly happen on the old code.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::BackgroundCallFlush:FilesFound",
|
||||
"DBTest2::LiveFilesOmitObsoleteFiles:FlushTriggered"},
|
||||
{"DBImpl::PurgeObsoleteFiles:End",
|
||||
"DBTest2::LiveFilesOmitObsoleteFiles:LiveFilesCaptured"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::PurgeObsoleteFiles:Begin",
|
||||
[&](void* arg) { env_->SleepForMicroseconds(1000000); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put("key", "val");
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
db_->Flush(flush_opts);
|
||||
TEST_SYNC_POINT("DBTest2::LiveFilesOmitObsoleteFiles:FlushTriggered");
|
||||
|
||||
db_->DisableFileDeletions();
|
||||
VectorLogPtr log_files;
|
||||
db_->GetSortedWalFiles(log_files);
|
||||
TEST_SYNC_POINT("DBTest2::LiveFilesOmitObsoleteFiles:LiveFilesCaptured");
|
||||
for (const auto& log_file : log_files) {
|
||||
ASSERT_OK(env_->FileExists(LogFileName(dbname_, log_file->LogNumber())));
|
||||
}
|
||||
|
||||
db_->EnableFileDeletions();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+1
-3
@@ -867,6 +867,7 @@ size_t DBTestBase::CountLiveFiles() {
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
return metadata.size();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
int DBTestBase::NumTableFilesAtLevel(int level, int cf) {
|
||||
std::string property;
|
||||
@@ -927,7 +928,6 @@ std::string DBTestBase::FilesPerLevel(int cf) {
|
||||
result.resize(last_non_zero_offset);
|
||||
return result;
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
size_t DBTestBase::CountFiles() {
|
||||
std::vector<std::string> files;
|
||||
@@ -997,7 +997,6 @@ void DBTestBase::MoveFilesToLevel(int level, int cf) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DBTestBase::DumpFileCounts(const char* label) {
|
||||
fprintf(stderr, "---\n%s:\n", label);
|
||||
fprintf(stderr, "maxoverlap: %" PRIu64 "\n",
|
||||
@@ -1009,7 +1008,6 @@ void DBTestBase::DumpFileCounts(const char* label) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::string DBTestBase::DumpSSTableList() {
|
||||
std::string property;
|
||||
|
||||
+2
-4
@@ -137,8 +137,8 @@ class SpecialMemTableRep : public MemTableRep {
|
||||
// Insert key into the list.
|
||||
// REQUIRES: nothing that compares equal to key is currently in the list.
|
||||
virtual void Insert(KeyHandle handle) override {
|
||||
num_entries_++;
|
||||
memtable_->Insert(handle);
|
||||
num_entries_++;
|
||||
}
|
||||
|
||||
// Returns true iff an entry that compares equal to key is in the list.
|
||||
@@ -863,13 +863,13 @@ class DBTestBase : public testing::Test {
|
||||
size_t TotalLiveFiles(int cf = 0);
|
||||
|
||||
size_t CountLiveFiles();
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
int NumTableFilesAtLevel(int level, int cf = 0);
|
||||
|
||||
double CompressionRatioAtLevel(int level, int cf = 0);
|
||||
|
||||
int TotalTableFiles(int cf = 0, int levels = -1);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Return spread of files per level
|
||||
std::string FilesPerLevel(int cf = 0);
|
||||
@@ -897,9 +897,7 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
void MoveFilesToLevel(int level, int cf = 0);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DumpFileCounts(const char* label);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
std::string DumpSSTableList();
|
||||
|
||||
|
||||
@@ -388,16 +388,10 @@ TEST_P(DBTestUniversalCompaction, DynamicUniversalCompactionSizeAmplification) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int total_picked_compactions = 0;
|
||||
int total_size_amp_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
if (arg) {
|
||||
total_picked_compactions++;
|
||||
Compaction* c = static_cast<Compaction*>(arg);
|
||||
if (c->compaction_reason() ==
|
||||
CompactionReason::kUniversalSizeAmplification) {
|
||||
total_size_amp_compactions++;
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -451,102 +445,6 @@ TEST_P(DBTestUniversalCompaction, DynamicUniversalCompactionSizeAmplification) {
|
||||
// Verify that size amplification did happen
|
||||
ASSERT_EQ(NumSortedRuns(1), 1);
|
||||
ASSERT_EQ(total_picked_compactions, 1);
|
||||
ASSERT_EQ(total_size_amp_compactions, 1);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, DynamicUniversalCompactionReadAmplification) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 1;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
// Initial setup of compaction_options_universal will prevent universal
|
||||
// compaction from happening
|
||||
options.compaction_options_universal.max_size_amplification_percent = 2000;
|
||||
options.compaction_options_universal.size_ratio = 0;
|
||||
options.compaction_options_universal.min_merge_width = 100;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int total_picked_compactions = 0;
|
||||
int total_size_ratio_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
if (arg) {
|
||||
total_picked_compactions++;
|
||||
Compaction* c = static_cast<Compaction*>(arg);
|
||||
if (c->compaction_reason() == CompactionReason::kUniversalSizeRatio) {
|
||||
total_size_ratio_compactions++;
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
MutableCFOptions mutable_cf_options;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
Random rnd(301);
|
||||
int key_idx = 0;
|
||||
|
||||
// Generate three files in Level 0. All files are approx the same size.
|
||||
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
|
||||
// Write 110KB (11 values, each 10K)
|
||||
for (int i = 0; i < 11; i++) {
|
||||
ASSERT_OK(Put(1, Key(key_idx), RandomString(&rnd, 10000)));
|
||||
key_idx++;
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
ASSERT_EQ(NumSortedRuns(1), num + 1);
|
||||
}
|
||||
ASSERT_EQ(NumSortedRuns(1), options.level0_file_num_compaction_trigger);
|
||||
|
||||
// Flush whatever is remaining in memtable. This is typically small, about
|
||||
// 30KB.
|
||||
ASSERT_OK(Flush(1));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Verify compaction did not happen
|
||||
ASSERT_EQ(NumSortedRuns(1), options.level0_file_num_compaction_trigger + 1);
|
||||
ASSERT_EQ(total_picked_compactions, 0);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
handles_[1],
|
||||
{{"compaction_options_universal",
|
||||
"{min_merge_width=2;max_merge_width=2;size_ratio=100;}"}}));
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions(handles_[1])
|
||||
.compaction_options_universal.min_merge_width,
|
||||
2);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions(handles_[1])
|
||||
.compaction_options_universal.max_merge_width,
|
||||
2);
|
||||
ASSERT_EQ(
|
||||
dbfull()->GetOptions(handles_[1]).compaction_options_universal.size_ratio,
|
||||
100);
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[1],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.size_ratio, 100);
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.min_merge_width, 2);
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.max_merge_width, 2);
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// Files in L0 are approx: 0.3 (30KB), 1, 1, 1.
|
||||
// On compaction: the files are below the size amp threshold, so we
|
||||
// fallthrough to checking read amp conditions. The configured size ratio is
|
||||
// not big enough to take 0.3 into consideration. So the next files 1 and 1
|
||||
// are compacted together first as they satisfy size ratio condition and
|
||||
// (min_merge_width, max_merge_width) condition, to give out a file size of 2.
|
||||
// Next, the newly generated 2 and the last file 1 are compacted together. So
|
||||
// at the end: #sortedRuns = 2, #picked_compactions = 2, and all the picked
|
||||
// ones are size ratio based compactions.
|
||||
ASSERT_EQ(NumSortedRuns(1), 2);
|
||||
// If max_merge_width had not been changed dynamically above, and if it
|
||||
// continued to be the default value of UINIT_MAX, total_picked_compactions
|
||||
// would have been 1.
|
||||
ASSERT_EQ(total_picked_compactions, 2);
|
||||
ASSERT_EQ(total_size_ratio_compactions, 2);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, CompactFilesOnUniversalCompaction) {
|
||||
@@ -1759,8 +1657,6 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -125,26 +125,6 @@ int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const {
|
||||
return r;
|
||||
}
|
||||
|
||||
int InternalKeyComparator::CompareKeySeq(const Slice& akey,
|
||||
const Slice& bkey) const {
|
||||
// Order by:
|
||||
// increasing user key (according to user-supplied comparator)
|
||||
// decreasing sequence number
|
||||
int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey));
|
||||
PERF_COUNTER_ADD(user_key_comparison_count, 1);
|
||||
if (r == 0) {
|
||||
// Shift the number to exclude the last byte which contains the value type
|
||||
const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8) >> 8;
|
||||
const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8) >> 8;
|
||||
if (anum > bnum) {
|
||||
r = -1;
|
||||
} else if (anum < bnum) {
|
||||
r = +1;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int InternalKeyComparator::Compare(const ParsedInternalKey& a,
|
||||
const ParsedInternalKey& b) const {
|
||||
// Order by:
|
||||
|
||||
@@ -162,8 +162,6 @@ class InternalKeyComparator
|
||||
|
||||
virtual const char* Name() const override;
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override;
|
||||
// Same as Compare except that it excludes the value type from comparison
|
||||
virtual int CompareKeySeq(const Slice& a, const Slice& b) const;
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override;
|
||||
virtual void FindShortSuccessor(std::string* key) const override;
|
||||
|
||||
@@ -500,7 +500,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as DBImpl::DeleteFile is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
|
||||
@@ -132,15 +132,11 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed,
|
||||
SuperVersion* super_version) {
|
||||
autovector<Range> ranges;
|
||||
for (const IngestedFileInfo& file_to_ingest : files_to_ingest_) {
|
||||
ranges.emplace_back(file_to_ingest.smallest_user_key,
|
||||
file_to_ingest.largest_user_key);
|
||||
}
|
||||
Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed) {
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
Status status =
|
||||
cfd_->RangesOverlapWithMemtables(ranges, super_version, flush_needed);
|
||||
IngestedFilesOverlapWithMemtables(super_version, flush_needed);
|
||||
|
||||
if (status.ok() && *flush_needed &&
|
||||
!ingestion_options_.allow_blocking_flush) {
|
||||
status = Status::InvalidArgument("External file requires flush");
|
||||
@@ -152,12 +148,11 @@ Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed,
|
||||
// nonmem_write_thread_
|
||||
Status ExternalSstFileIngestionJob::Run() {
|
||||
Status status;
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
#ifndef NDEBUG
|
||||
// We should never run the job with a memtable that is overlapping
|
||||
// with the files we are ingesting
|
||||
bool need_flush = false;
|
||||
status = NeedsFlush(&need_flush, super_version);
|
||||
status = NeedsFlush(&need_flush);
|
||||
assert(status.ok() && need_flush == false);
|
||||
#endif
|
||||
|
||||
@@ -172,6 +167,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
// It is safe to use this instead of LastAllocatedSequence since we are
|
||||
// the only active writer, and hence they are equal
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
edit_.SetColumnFamily(cfd_->GetID());
|
||||
// The levels that the files will be ingested into
|
||||
|
||||
@@ -379,6 +375,54 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::IngestedFilesOverlapWithMemtables(
|
||||
SuperVersion* sv, bool* overlap) {
|
||||
*overlap = false;
|
||||
// Create an InternalIterator over all memtables
|
||||
Arena arena;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&cfd_->internal_comparator(), &arena);
|
||||
merge_iter_builder.AddIterator(sv->mem->NewIterator(ro, &arena));
|
||||
sv->imm->AddIterators(ro, &merge_iter_builder);
|
||||
ScopedArenaIterator memtable_iter(merge_iter_builder.Finish());
|
||||
|
||||
std::vector<InternalIterator*> memtable_range_del_iters;
|
||||
auto* active_range_del_iter = sv->mem->NewRangeTombstoneIterator(ro);
|
||||
if (active_range_del_iter != nullptr) {
|
||||
memtable_range_del_iters.push_back(active_range_del_iter);
|
||||
}
|
||||
sv->imm->AddRangeTombstoneIterators(ro, &memtable_range_del_iters);
|
||||
RangeDelAggregator range_del_agg(cfd_->internal_comparator(),
|
||||
{} /* snapshots */,
|
||||
false /* collapse_deletions */);
|
||||
Status status;
|
||||
{
|
||||
std::unique_ptr<InternalIterator> memtable_range_del_iter(
|
||||
NewMergingIterator(&cfd_->internal_comparator(),
|
||||
memtable_range_del_iters.empty()
|
||||
? nullptr
|
||||
: &memtable_range_del_iters[0],
|
||||
static_cast<int>(memtable_range_del_iters.size())));
|
||||
status = range_del_agg.AddTombstones(std::move(memtable_range_del_iter));
|
||||
}
|
||||
if (status.ok()) {
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
status = IngestedFileOverlapWithIteratorRange(&f, memtable_iter.get(),
|
||||
overlap);
|
||||
if (!status.ok() || *overlap == true) {
|
||||
break;
|
||||
}
|
||||
if (range_del_agg.IsRangeOverlapped(f.smallest_user_key,
|
||||
f.largest_user_key)) {
|
||||
*overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
SuperVersion* sv, bool force_global_seqno, CompactionStyle compaction_style,
|
||||
IngestedFileInfo* file_to_ingest, SequenceNumber* assigned_seqno) {
|
||||
@@ -429,10 +473,7 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
return f1->largest_seqno < f2->largest_seqno;
|
||||
}))
|
||||
->largest_seqno;
|
||||
// should only assign seqno to current level's largest seqno when
|
||||
// the file fits
|
||||
if (level_largest_seqno != 0 &&
|
||||
IngestedFileFitInLevel(file_to_ingest, lvl)) {
|
||||
if (level_largest_seqno != 0) {
|
||||
*assigned_seqno = level_largest_seqno;
|
||||
} else {
|
||||
continue;
|
||||
@@ -509,9 +550,6 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
std::string seqno_val;
|
||||
PutFixed64(&seqno_val, seqno);
|
||||
status = rwfile->Write(file_to_ingest->global_seqno_offset, seqno_val);
|
||||
if (status.ok()) {
|
||||
status = rwfile->Fsync();
|
||||
}
|
||||
if (status.ok()) {
|
||||
file_to_ingest->assigned_seqno = seqno;
|
||||
}
|
||||
|
||||
@@ -84,12 +84,8 @@ class ExternalSstFileIngestionJob {
|
||||
// Check if we need to flush the memtable before running the ingestion job
|
||||
// This will be true if the files we are ingesting are overlapping with any
|
||||
// key range in the memtable.
|
||||
//
|
||||
// @param super_version A referenced SuperVersion that will be held for the
|
||||
// duration of this function.
|
||||
//
|
||||
// Thread-safe
|
||||
Status NeedsFlush(bool* flush_needed, SuperVersion* super_version);
|
||||
// REQUIRES: Mutex held
|
||||
Status NeedsFlush(bool* flush_needed);
|
||||
|
||||
// Will execute the ingestion job and prepare edit() to be applied.
|
||||
// REQUIRES: Mutex held
|
||||
@@ -114,6 +110,10 @@ class ExternalSstFileIngestionJob {
|
||||
Status GetIngestedFileInfo(const std::string& external_file,
|
||||
IngestedFileInfo* file_to_ingest);
|
||||
|
||||
// Check if the files we are ingesting overlap with any memtable.
|
||||
// REQUIRES: Mutex held
|
||||
Status IngestedFilesOverlapWithMemtables(SuperVersion* sv, bool* overlap);
|
||||
|
||||
// Assign `file_to_ingest` the appropriate sequence number and the lowest
|
||||
// possible level that it can be ingested to according to compaction_style.
|
||||
// REQUIRES: Mutex held
|
||||
|
||||
@@ -388,10 +388,9 @@ class SstFileWriterCollector : public TablePropertiesCollector {
|
||||
const char* Name() const override { return name_.c_str(); }
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
std::string count = std::to_string(count_);
|
||||
*properties = UserCollectedProperties{
|
||||
{prefix_ + "_SstFileWriterCollector", "YES"},
|
||||
{prefix_ + "_Count", count},
|
||||
{prefix_ + "_Count", std::to_string(count_)},
|
||||
};
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -1941,55 +1940,6 @@ TEST_F(ExternalSSTFileTest, IngestBehind) {
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, SkipBloomFilter) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
|
||||
// Create external SST file and include bloom filters
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
{
|
||||
std::string file_path = sst_files_dir_ + "sst_with_bloom.sst";
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
ASSERT_OK(sst_file_writer.Put("Key1", "Value1"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(
|
||||
db_->IngestExternalFile({file_path}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get("Key1"), "Value1");
|
||||
ASSERT_GE(
|
||||
options.statistics->getTickerCount(Tickers::BLOCK_CACHE_FILTER_ADD), 1);
|
||||
}
|
||||
|
||||
// Create external SST file but skip bloom filters
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
{
|
||||
std::string file_path = sst_files_dir_ + "sst_with_no_bloom.sst";
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, nullptr, true,
|
||||
Env::IOPriority::IO_TOTAL,
|
||||
true /* skip_filters */);
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
ASSERT_OK(sst_file_writer.Put("Key1", "Value1"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(
|
||||
db_->IngestExternalFile({file_path}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get("Key1"), "Value1");
|
||||
ASSERT_EQ(
|
||||
options.statistics->getTickerCount(Tickers::BLOCK_CACHE_FILTER_ADD), 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -2001,7 +1951,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as External SST File Writer and Ingestion are not supported "
|
||||
"in ROCKSDB_LITE\n");
|
||||
|
||||
@@ -228,9 +228,16 @@ class FaultInjectionTest : public testing::Test,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_UBSAN_RUN
|
||||
#if defined(__clang__)
|
||||
__attribute__((__no_sanitize__("shift"), no_sanitize("signed-integer-overflow")))
|
||||
#elif defined(__GNUC__)
|
||||
__attribute__((__no_sanitize_undefined__))
|
||||
#endif
|
||||
#endif
|
||||
// Return the ith key
|
||||
Slice Key(int i, std::string* storage) const {
|
||||
unsigned long long num = i;
|
||||
int num = i;
|
||||
if (!sequential_order_) {
|
||||
// random transfer
|
||||
const int m = 0x5bd1e995;
|
||||
@@ -238,7 +245,7 @@ class FaultInjectionTest : public testing::Test,
|
||||
num ^= num << 24;
|
||||
}
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "%016d", static_cast<int>(num));
|
||||
snprintf(buf, sizeof(buf), "%016d", num);
|
||||
storage->assign(buf, strlen(buf));
|
||||
return Slice(*storage);
|
||||
}
|
||||
|
||||
+6
-33
@@ -54,32 +54,6 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
const char* GetFlushReasonString (FlushReason flush_reason) {
|
||||
switch (flush_reason) {
|
||||
case FlushReason::kUnknown:
|
||||
return "Unknown";
|
||||
case FlushReason::kGetLiveFiles:
|
||||
return "Get Live Files";
|
||||
case FlushReason::kShutDown:
|
||||
return "Shut down";
|
||||
case FlushReason::kExternalFileIngestion:
|
||||
return "External File Ingestion";
|
||||
case FlushReason::kManualCompaction:
|
||||
return "Manual Compaction";
|
||||
case FlushReason::kWriteBufferManager:
|
||||
return "Write Buffer Manager";
|
||||
case FlushReason::kWriteBufferFull:
|
||||
return "Write Buffer Full";
|
||||
case FlushReason::kTest:
|
||||
return "Test";
|
||||
case FlushReason::kSuperVersionChange:
|
||||
return "SuperVersion Change";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
@@ -304,13 +278,12 @@ Status FlushJob::WriteLevel0Table() {
|
||||
total_memory_usage += m->ApproximateMemoryUsage();
|
||||
}
|
||||
|
||||
event_logger_->Log()
|
||||
<< "job" << job_context_->job_id << "event"
|
||||
<< "flush_started"
|
||||
<< "num_memtables" << mems_.size() << "num_entries" << total_num_entries
|
||||
<< "num_deletes" << total_num_deletes << "memory_usage"
|
||||
<< total_memory_usage << "flush_reason"
|
||||
<< GetFlushReasonString(cfd_->GetFlushReason());
|
||||
event_logger_->Log() << "job" << job_context_->job_id << "event"
|
||||
<< "flush_started"
|
||||
<< "num_memtables" << mems_.size() << "num_entries"
|
||||
<< total_num_entries << "num_deletes"
|
||||
<< total_num_deletes << "memory_usage"
|
||||
<< total_memory_usage;
|
||||
|
||||
{
|
||||
ScopedArenaIterator iter(
|
||||
|
||||
+50
-70
@@ -25,15 +25,15 @@
|
||||
namespace rocksdb {
|
||||
|
||||
// Usage:
|
||||
// ForwardLevelIterator iter;
|
||||
// LevelIterator iter;
|
||||
// iter.SetFileIndex(file_index);
|
||||
// iter.Seek(target);
|
||||
// iter.Next()
|
||||
class ForwardLevelIterator : public InternalIterator {
|
||||
class LevelIterator : public InternalIterator {
|
||||
public:
|
||||
ForwardLevelIterator(const ColumnFamilyData* const cfd,
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files)
|
||||
LevelIterator(const ColumnFamilyData* const cfd,
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files)
|
||||
: cfd_(cfd),
|
||||
read_options_(read_options),
|
||||
files_(files),
|
||||
@@ -42,7 +42,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
file_iter_(nullptr),
|
||||
pinned_iters_mgr_(nullptr) {}
|
||||
|
||||
~ForwardLevelIterator() {
|
||||
~LevelIterator() {
|
||||
// Reset current pointer
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinIterator(file_iter_);
|
||||
@@ -84,11 +84,11 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
}
|
||||
}
|
||||
void SeekToLast() override {
|
||||
status_ = Status::NotSupported("ForwardLevelIterator::SeekToLast()");
|
||||
status_ = Status::NotSupported("LevelIterator::SeekToLast()");
|
||||
valid_ = false;
|
||||
}
|
||||
void Prev() override {
|
||||
status_ = Status::NotSupported("ForwardLevelIterator::Prev()");
|
||||
status_ = Status::NotSupported("LevelIterator::Prev()");
|
||||
valid_ = false;
|
||||
}
|
||||
bool Valid() const override {
|
||||
@@ -105,7 +105,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
valid_ = file_iter_->Valid();
|
||||
}
|
||||
void SeekForPrev(const Slice& internal_key) override {
|
||||
status_ = Status::NotSupported("ForwardLevelIterator::SeekForPrev()");
|
||||
status_ = Status::NotSupported("LevelIterator::SeekForPrev()");
|
||||
valid_ = false;
|
||||
}
|
||||
void Next() override {
|
||||
@@ -196,60 +196,38 @@ ForwardIterator::~ForwardIterator() {
|
||||
Cleanup(true);
|
||||
}
|
||||
|
||||
void ForwardIterator::SVCleanup(DBImpl* db, SuperVersion* sv,
|
||||
bool background_purge_on_iterator_cleanup) {
|
||||
if (sv->Unref()) {
|
||||
namespace {
|
||||
// Used in PinnedIteratorsManager to release pinned SuperVersion
|
||||
static void ReleaseSuperVersionFunc(void* sv) {
|
||||
delete reinterpret_cast<SuperVersion*>(sv);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ForwardIterator::SVCleanup() {
|
||||
if (sv_ != nullptr && sv_->Unref()) {
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
db->mutex_.Lock();
|
||||
sv->Cleanup();
|
||||
db->FindObsoleteFiles(&job_context, false, true);
|
||||
if (background_purge_on_iterator_cleanup) {
|
||||
db->ScheduleBgLogWriterClose(&job_context);
|
||||
db_->mutex_.Lock();
|
||||
sv_->Cleanup();
|
||||
db_->FindObsoleteFiles(&job_context, false, true);
|
||||
if (read_options_.background_purge_on_iterator_cleanup) {
|
||||
db_->ScheduleBgLogWriterClose(&job_context);
|
||||
}
|
||||
db_->mutex_.Unlock();
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinPtr(sv_, &ReleaseSuperVersionFunc);
|
||||
} else {
|
||||
delete sv_;
|
||||
}
|
||||
db->mutex_.Unlock();
|
||||
delete sv;
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
db->PurgeObsoleteFiles(job_context, background_purge_on_iterator_cleanup);
|
||||
db_->PurgeObsoleteFiles(
|
||||
job_context, read_options_.background_purge_on_iterator_cleanup);
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct SVCleanupParams {
|
||||
DBImpl* db;
|
||||
SuperVersion* sv;
|
||||
bool background_purge_on_iterator_cleanup;
|
||||
};
|
||||
}
|
||||
|
||||
// Used in PinnedIteratorsManager to release pinned SuperVersion
|
||||
void ForwardIterator::DeferredSVCleanup(void* arg) {
|
||||
auto d = reinterpret_cast<SVCleanupParams*>(arg);
|
||||
ForwardIterator::SVCleanup(
|
||||
d->db, d->sv, d->background_purge_on_iterator_cleanup);
|
||||
delete d;
|
||||
}
|
||||
|
||||
void ForwardIterator::SVCleanup() {
|
||||
if (sv_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
// pinned_iters_mgr_ tells us to make sure that all visited key-value slices
|
||||
// are alive until pinned_iters_mgr_->ReleasePinnedData() is called.
|
||||
// The slices may point into some memtables owned by sv_, so we need to keep
|
||||
// sv_ referenced until pinned_iters_mgr_ unpins everything.
|
||||
auto p = new SVCleanupParams{
|
||||
db_, sv_, read_options_.background_purge_on_iterator_cleanup};
|
||||
pinned_iters_mgr_->PinPtr(p, &ForwardIterator::DeferredSVCleanup);
|
||||
} else {
|
||||
SVCleanup(db_, sv_, read_options_.background_purge_on_iterator_cleanup);
|
||||
}
|
||||
}
|
||||
|
||||
void ForwardIterator::Cleanup(bool release_sv) {
|
||||
if (mutable_iter_ != nullptr) {
|
||||
DeleteIterator(mutable_iter_, true /* is_arena */);
|
||||
@@ -383,13 +361,14 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
|
||||
if (!l0_iters_[i]->status().ok()) {
|
||||
immutable_status_ = l0_iters_[i]->status();
|
||||
} else if (l0_iters_[i]->Valid() &&
|
||||
!IsOverUpperBound(l0_iters_[i]->key())) {
|
||||
immutable_min_heap_.push(l0_iters_[i]);
|
||||
} else {
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
DeleteIterator(l0_iters_[i]);
|
||||
l0_iters_[i] = nullptr;
|
||||
} else if (l0_iters_[i]->Valid()) {
|
||||
if (!IsOverUpperBound(l0_iters_[i]->key())) {
|
||||
immutable_min_heap_.push(l0_iters_[i]);
|
||||
} else {
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
DeleteIterator(l0_iters_[i]);
|
||||
l0_iters_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,14 +395,15 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
|
||||
if (!level_iters_[level - 1]->status().ok()) {
|
||||
immutable_status_ = level_iters_[level - 1]->status();
|
||||
} else if (level_iters_[level - 1]->Valid() &&
|
||||
!IsOverUpperBound(level_iters_[level - 1]->key())) {
|
||||
immutable_min_heap_.push(level_iters_[level - 1]);
|
||||
} else {
|
||||
// Nothing in this level is interesting. Remove.
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
DeleteIterator(level_iters_[level - 1]);
|
||||
level_iters_[level - 1] = nullptr;
|
||||
} else if (level_iters_[level - 1]->Valid()) {
|
||||
if (!IsOverUpperBound(level_iters_[level - 1]->key())) {
|
||||
immutable_min_heap_.push(level_iters_[level - 1]);
|
||||
} else {
|
||||
// Nothing in this level is interesting. Remove.
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
DeleteIterator(level_iters_[level - 1]);
|
||||
level_iters_[level - 1] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -561,7 +541,7 @@ void ForwardIterator::UpdateChildrenPinnedItersMgr() {
|
||||
}
|
||||
|
||||
// Set PinnedIteratorsManager for L1+ levels iterators.
|
||||
for (ForwardLevelIterator* child_iter : level_iters_) {
|
||||
for (LevelIterator* child_iter : level_iters_) {
|
||||
if (child_iter) {
|
||||
child_iter->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
@@ -723,7 +703,7 @@ void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage) {
|
||||
}
|
||||
} else {
|
||||
level_iters_.push_back(
|
||||
new ForwardLevelIterator(cfd_, read_options_, level_files));
|
||||
new LevelIterator(cfd_, read_options_, level_files));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class DBImpl;
|
||||
class Env;
|
||||
struct SuperVersion;
|
||||
class ColumnFamilyData;
|
||||
class ForwardLevelIterator;
|
||||
class LevelIterator;
|
||||
class VersionStorageInfo;
|
||||
struct FileMetaData;
|
||||
|
||||
@@ -85,14 +85,7 @@ class ForwardIterator : public InternalIterator {
|
||||
|
||||
private:
|
||||
void Cleanup(bool release_sv);
|
||||
// Unreference and, if needed, clean up the current SuperVersion. This is
|
||||
// either done immediately or deferred until this iterator is unpinned by
|
||||
// PinnedIteratorsManager.
|
||||
void SVCleanup();
|
||||
static void SVCleanup(
|
||||
DBImpl* db, SuperVersion* sv, bool background_purge_on_iterator_cleanup);
|
||||
static void DeferredSVCleanup(void* arg);
|
||||
|
||||
void RebuildIterators(bool refresh_sv);
|
||||
void RenewIterators();
|
||||
void BuildLevelIterators(const VersionStorageInfo* vstorage);
|
||||
@@ -126,7 +119,7 @@ class ForwardIterator : public InternalIterator {
|
||||
InternalIterator* mutable_iter_;
|
||||
std::vector<InternalIterator*> imm_iters_;
|
||||
std::vector<InternalIterator*> l0_iters_;
|
||||
std::vector<ForwardLevelIterator*> level_iters_;
|
||||
std::vector<LevelIterator*> level_iters_;
|
||||
InternalIterator* current_;
|
||||
bool valid_;
|
||||
|
||||
|
||||
+2
-5
@@ -81,11 +81,8 @@ struct SuperVersionContext {
|
||||
struct JobContext {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return full_scan_candidate_files.size() || sst_delete_files.size() ||
|
||||
log_delete_files.size() || manifest_delete_files.size();
|
||||
}
|
||||
|
||||
inline bool HaveSomethingToClean() const {
|
||||
return memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
|
||||
log_delete_files.size() || manifest_delete_files.size() ||
|
||||
memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
|
||||
superversion_context.HaveSomethingToDelete();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -528,7 +528,7 @@ TEST_F(EventListenerTest, CompactionReasonUniversal) {
|
||||
|
||||
ASSERT_GT(listener->compaction_reasons_.size(), 0);
|
||||
for (auto compaction_reason : listener->compaction_reasons_) {
|
||||
ASSERT_EQ(compaction_reason, CompactionReason::kUniversalSizeRatio);
|
||||
ASSERT_EQ(compaction_reason, CompactionReason::kUniversalSortedRunNum);
|
||||
}
|
||||
|
||||
options.level0_file_num_compaction_trigger = 8;
|
||||
|
||||
+2
-9
@@ -16,12 +16,7 @@
|
||||
namespace rocksdb {
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
#ifdef __FreeBSD__
|
||||
#include <malloc_np.h>
|
||||
#define je_malloc_stats_print malloc_stats_print
|
||||
#else
|
||||
#include "jemalloc/jemalloc.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char* cur;
|
||||
@@ -41,8 +36,8 @@ static void GetJemallocStatus(void* mstat_arg, const char* status) {
|
||||
}
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
void DumpMallocStats(std::string* stats) {
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
MallocStatus mstat;
|
||||
const unsigned int kMallocStatusLen = 1000000;
|
||||
std::unique_ptr<char[]> buf{new char[kMallocStatusLen + 1]};
|
||||
@@ -50,10 +45,8 @@ void DumpMallocStats(std::string* stats) {
|
||||
mstat.end = buf.get() + kMallocStatusLen;
|
||||
je_malloc_stats_print(GetJemallocStatus, &mstat, "");
|
||||
stats->append(buf.get());
|
||||
}
|
||||
#else
|
||||
void DumpMallocStats(std::string*) {}
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
}
|
||||
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
+12
-34
@@ -225,7 +225,7 @@ int MemTable::KeyComparator::operator()(const char* prefix_len_key1,
|
||||
// Internal keys are encoded as length-prefixed strings.
|
||||
Slice k1 = GetLengthPrefixedSlice(prefix_len_key1);
|
||||
Slice k2 = GetLengthPrefixedSlice(prefix_len_key2);
|
||||
return comparator.CompareKeySeq(k1, k2);
|
||||
return comparator.Compare(k1, k2);
|
||||
}
|
||||
|
||||
int MemTable::KeyComparator::operator()(const char* prefix_len_key,
|
||||
@@ -233,15 +233,7 @@ int MemTable::KeyComparator::operator()(const char* prefix_len_key,
|
||||
const {
|
||||
// Internal keys are encoded as length-prefixed strings.
|
||||
Slice a = GetLengthPrefixedSlice(prefix_len_key);
|
||||
return comparator.CompareKeySeq(a, key);
|
||||
}
|
||||
|
||||
void MemTableRep::InsertConcurrently(KeyHandle handle) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
throw std::runtime_error("concurrent insert not supported");
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
return comparator.Compare(a, key);
|
||||
}
|
||||
|
||||
Slice MemTableRep::UserKey(const char* key) const {
|
||||
@@ -289,7 +281,8 @@ class MemTableIterator : public InternalIterator {
|
||||
#ifndef NDEBUG
|
||||
// Assert that the MemTableIterator is never deleted while
|
||||
// Pinning is Enabled.
|
||||
assert(!pinned_iters_mgr_ || !pinned_iters_mgr_->PinningEnabled());
|
||||
assert(!pinned_iters_mgr_ ||
|
||||
(pinned_iters_mgr_ && !pinned_iters_mgr_->PinningEnabled()));
|
||||
#endif
|
||||
if (arena_mode_) {
|
||||
iter_->~Iterator();
|
||||
@@ -443,7 +436,7 @@ MemTable::MemTableStats MemTable::ApproximateStats(const Slice& start_ikey,
|
||||
return {entry_count * (data_size / n), entry_count};
|
||||
}
|
||||
|
||||
bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
const Slice& key, /* user key */
|
||||
const Slice& value, bool allow_concurrent,
|
||||
MemTablePostProcessInfo* post_process_info) {
|
||||
@@ -478,15 +471,9 @@ bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
if (insert_with_hint_prefix_extractor_ != nullptr &&
|
||||
insert_with_hint_prefix_extractor_->InDomain(key_slice)) {
|
||||
Slice prefix = insert_with_hint_prefix_extractor_->Transform(key_slice);
|
||||
bool res = table->InsertKeyWithHint(handle, &insert_hints_[prefix]);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
table->InsertWithHint(handle, &insert_hints_[prefix]);
|
||||
} else {
|
||||
bool res = table->InsertKey(handle);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
table->Insert(handle);
|
||||
}
|
||||
|
||||
// this is a bit ugly, but is the way to avoid locked instructions
|
||||
@@ -519,10 +506,7 @@ bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
assert(post_process_info == nullptr);
|
||||
UpdateFlushState();
|
||||
} else {
|
||||
bool res = table->InsertKeyConcurrently(handle);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
table->InsertConcurrently(handle);
|
||||
|
||||
assert(post_process_info != nullptr);
|
||||
post_process_info->num_entries++;
|
||||
@@ -552,7 +536,6 @@ bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
is_range_del_table_empty_ = false;
|
||||
}
|
||||
UpdateOldestKeyTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback from MemTable::Get()
|
||||
@@ -588,12 +571,11 @@ struct Saver {
|
||||
|
||||
static bool SaveValue(void* arg, const char* entry) {
|
||||
Saver* s = reinterpret_cast<Saver*>(arg);
|
||||
assert(s != nullptr);
|
||||
MergeContext* merge_context = s->merge_context;
|
||||
RangeDelAggregator* range_del_agg = s->range_del_agg;
|
||||
const MergeOperator* merge_operator = s->merge_operator;
|
||||
|
||||
assert(merge_context != nullptr && range_del_agg != nullptr);
|
||||
assert(s != nullptr && merge_context != nullptr && range_del_agg != nullptr);
|
||||
|
||||
// entry format is:
|
||||
// klength varint32
|
||||
@@ -809,9 +791,8 @@ void MemTable::Update(SequenceNumber seq,
|
||||
// Correct user key
|
||||
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
|
||||
ValueType type;
|
||||
SequenceNumber existing_seq;
|
||||
UnPackSequenceAndType(tag, &existing_seq, &type);
|
||||
assert(existing_seq != seq);
|
||||
SequenceNumber unused;
|
||||
UnPackSequenceAndType(tag, &unused, &type);
|
||||
if (type == kTypeValue) {
|
||||
Slice prev_value = GetLengthPrefixedSlice(key_ptr + key_length);
|
||||
uint32_t prev_size = static_cast<uint32_t>(prev_value.size());
|
||||
@@ -834,10 +815,7 @@ void MemTable::Update(SequenceNumber seq,
|
||||
}
|
||||
|
||||
// key doesn't exist
|
||||
bool add_res __attribute__((__unused__));
|
||||
add_res = Add(seq, kTypeValue, key, value);
|
||||
// We already checked unused != seq above. In that case, Add should not fail.
|
||||
assert(add_res);
|
||||
Add(seq, kTypeValue, key, value);
|
||||
}
|
||||
|
||||
bool MemTable::UpdateCallback(SequenceNumber seq,
|
||||
|
||||
+1
-4
@@ -167,10 +167,7 @@ class MemTable {
|
||||
//
|
||||
// REQUIRES: if allow_concurrent = false, external synchronization to prevent
|
||||
// simultaneous operations on the same MemTable.
|
||||
//
|
||||
// Returns false if MemTableRepFactory::CanHandleDuplicatedKey() is true and
|
||||
// the <key, seq> already exists.
|
||||
bool Add(SequenceNumber seq, ValueType type, const Slice& key,
|
||||
void Add(SequenceNumber seq, ValueType type, const Slice& key,
|
||||
const Slice& value, bool allow_concurrent = false,
|
||||
MemTablePostProcessInfo* post_process_info = nullptr);
|
||||
|
||||
|
||||
+3
-8
@@ -5,12 +5,12 @@
|
||||
|
||||
#include "db/merge_helper.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
@@ -22,8 +22,7 @@ MergeHelper::MergeHelper(Env* env, const Comparator* user_comparator,
|
||||
const MergeOperator* user_merge_operator,
|
||||
const CompactionFilter* compaction_filter,
|
||||
Logger* logger, bool assert_valid_internal_key,
|
||||
SequenceNumber latest_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, int level,
|
||||
SequenceNumber latest_snapshot, int level,
|
||||
Statistics* stats,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
: env_(env),
|
||||
@@ -35,7 +34,6 @@ MergeHelper::MergeHelper(Env* env, const Comparator* user_comparator,
|
||||
assert_valid_internal_key_(assert_valid_internal_key),
|
||||
allow_single_operand_(false),
|
||||
latest_snapshot_(latest_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
level_(level),
|
||||
keys_(),
|
||||
filter_timer_(env_),
|
||||
@@ -160,10 +158,7 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
// hit a different user key, stop right here
|
||||
hit_the_next_user_key = true;
|
||||
break;
|
||||
} else if (stop_before > 0 && ikey.sequence <= stop_before &&
|
||||
LIKELY(snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(ikey.sequence,
|
||||
stop_before))) {
|
||||
} else if (stop_before && ikey.sequence <= stop_before) {
|
||||
// hit an entry that's visible by the previous snapshot, can't touch that
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-4
@@ -13,7 +13,6 @@
|
||||
#include "db/dbformat.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
@@ -34,8 +33,7 @@ class MergeHelper {
|
||||
const MergeOperator* user_merge_operator,
|
||||
const CompactionFilter* compaction_filter, Logger* logger,
|
||||
bool assert_valid_internal_key, SequenceNumber latest_snapshot,
|
||||
const SnapshotChecker* snapshot_checker = nullptr, int level = 0,
|
||||
Statistics* stats = nullptr,
|
||||
int level = 0, Statistics* stats = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr);
|
||||
|
||||
// Wrapper around MergeOperator::FullMergeV2() that records perf statistics.
|
||||
@@ -147,7 +145,6 @@ class MergeHelper {
|
||||
bool assert_valid_internal_key_; // enforce no internal key corruption?
|
||||
bool allow_single_operand_;
|
||||
SequenceNumber latest_snapshot_;
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
int level_;
|
||||
|
||||
// the scratch area that holds the result of MergeUntil
|
||||
|
||||
@@ -112,7 +112,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
printf("Skipped as Options file is not supported in RocksDBLite.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1170,7 +1170,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "SKIPPED as plain table is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -887,7 +887,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as HashSkipList and HashLinkList are not supported in "
|
||||
"ROCKSDB_LITE\n");
|
||||
|
||||
+1
-1
@@ -356,7 +356,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "SKIPPED as RepairDB is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -45,10 +45,6 @@ class SnapshotList {
|
||||
list_.prev_ = &list_;
|
||||
list_.next_ = &list_;
|
||||
list_.number_ = 0xFFFFFFFFL; // placeholder marker, for debugging
|
||||
// Set all the variables to make UBSAN happy.
|
||||
list_.list_ = nullptr;
|
||||
list_.unix_time_ = 0;
|
||||
list_.is_write_conflict_boundary_ = false;
|
||||
count_ = 0;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -272,8 +272,9 @@ InternalIterator* TableCache::NewRangeTombstoneIterator(
|
||||
const InternalKeyComparator& icomparator, const FileDescriptor& fd,
|
||||
HistogramImpl* file_read_hist, bool skip_filters, int level) {
|
||||
Status s;
|
||||
TableReader* table_reader = nullptr;
|
||||
Cache::Handle* handle = nullptr;
|
||||
TableReader* table_reader = fd.table_reader;
|
||||
table_reader = fd.table_reader;
|
||||
if (table_reader == nullptr) {
|
||||
s = FindTable(env_options, icomparator, fd, &handle,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
|
||||
@@ -60,7 +60,7 @@ InternalKeyPropertiesCollector::GetReadableProperties() const {
|
||||
namespace {
|
||||
|
||||
uint64_t GetUint64Property(const UserCollectedProperties& props,
|
||||
const std::string& property_name,
|
||||
const std::string property_name,
|
||||
bool* property_present) {
|
||||
auto pos = props.find(property_name);
|
||||
if (pos == props.end()) {
|
||||
|
||||
+118
-240
@@ -424,261 +424,129 @@ bool SomeFileOverlapsRange(
|
||||
}
|
||||
|
||||
namespace {
|
||||
class LevelIterator final : public InternalIterator {
|
||||
|
||||
// An internal iterator. For a given version/level pair, yields
|
||||
// information about the files in the level. For a given entry, key()
|
||||
// is the largest key that occurs in the file, and value() is an
|
||||
// 16-byte value containing the file number and file size, both
|
||||
// encoded using EncodeFixed64.
|
||||
class LevelFileNumIterator : public InternalIterator {
|
||||
public:
|
||||
LevelIterator(TableCache* table_cache, const ReadOptions& read_options,
|
||||
const EnvOptions& env_options,
|
||||
const InternalKeyComparator& icomparator,
|
||||
const LevelFilesBrief* flevel, bool should_sample,
|
||||
HistogramImpl* file_read_hist, bool for_compaction,
|
||||
bool skip_filters, int level, RangeDelAggregator* range_del_agg)
|
||||
: table_cache_(table_cache),
|
||||
LevelFileNumIterator(const InternalKeyComparator& icmp,
|
||||
const LevelFilesBrief* flevel, bool should_sample)
|
||||
: icmp_(icmp),
|
||||
flevel_(flevel),
|
||||
index_(static_cast<uint32_t>(flevel->num_files)),
|
||||
current_value_(0, 0, 0), // Marks as invalid
|
||||
should_sample_(should_sample) {}
|
||||
virtual bool Valid() const override { return index_ < flevel_->num_files; }
|
||||
virtual void Seek(const Slice& target) override {
|
||||
index_ = FindFile(icmp_, *flevel_, target);
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& target) override {
|
||||
SeekForPrevImpl(target, &icmp_);
|
||||
}
|
||||
|
||||
virtual void SeekToFirst() override { index_ = 0; }
|
||||
virtual void SeekToLast() override {
|
||||
index_ = (flevel_->num_files == 0)
|
||||
? 0
|
||||
: static_cast<uint32_t>(flevel_->num_files) - 1;
|
||||
}
|
||||
virtual void Next() override {
|
||||
assert(Valid());
|
||||
index_++;
|
||||
}
|
||||
virtual void Prev() override {
|
||||
assert(Valid());
|
||||
if (index_ == 0) {
|
||||
index_ = static_cast<uint32_t>(flevel_->num_files); // Marks as invalid
|
||||
} else {
|
||||
index_--;
|
||||
}
|
||||
}
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return flevel_->files[index_].largest_key;
|
||||
}
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
|
||||
auto file_meta = flevel_->files[index_];
|
||||
if (should_sample_) {
|
||||
sample_file_read_inc(file_meta.file_metadata);
|
||||
}
|
||||
current_value_ = file_meta.fd;
|
||||
return Slice(reinterpret_cast<const char*>(¤t_value_),
|
||||
sizeof(FileDescriptor));
|
||||
}
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
const InternalKeyComparator icmp_;
|
||||
const LevelFilesBrief* flevel_;
|
||||
uint32_t index_;
|
||||
mutable FileDescriptor current_value_;
|
||||
bool should_sample_;
|
||||
};
|
||||
|
||||
class LevelFileIteratorState : public TwoLevelIteratorState {
|
||||
public:
|
||||
// @param skip_filters Disables loading/accessing the filter block
|
||||
LevelFileIteratorState(TableCache* table_cache,
|
||||
const ReadOptions& read_options,
|
||||
const EnvOptions& env_options,
|
||||
const InternalKeyComparator& icomparator,
|
||||
HistogramImpl* file_read_hist, bool for_compaction,
|
||||
bool prefix_enabled, bool skip_filters, int level,
|
||||
RangeDelAggregator* range_del_agg)
|
||||
: TwoLevelIteratorState(prefix_enabled),
|
||||
table_cache_(table_cache),
|
||||
read_options_(read_options),
|
||||
env_options_(env_options),
|
||||
icomparator_(icomparator),
|
||||
flevel_(flevel),
|
||||
file_read_hist_(file_read_hist),
|
||||
should_sample_(should_sample),
|
||||
for_compaction_(for_compaction),
|
||||
skip_filters_(skip_filters),
|
||||
file_index_(flevel_->num_files),
|
||||
level_(level),
|
||||
range_del_agg_(range_del_agg),
|
||||
pinned_iters_mgr_(nullptr) {
|
||||
// Empty level is not supported.
|
||||
assert(flevel_ != nullptr && flevel_->num_files > 0);
|
||||
}
|
||||
range_del_agg_(range_del_agg) {}
|
||||
|
||||
virtual ~LevelIterator() { delete file_iter_.Set(nullptr); }
|
||||
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
|
||||
virtual bool Valid() const override { return file_iter_.Valid(); }
|
||||
virtual Slice key() const override {
|
||||
assert(Valid());
|
||||
return file_iter_.key();
|
||||
}
|
||||
virtual Slice value() const override {
|
||||
assert(Valid());
|
||||
return file_iter_.value();
|
||||
}
|
||||
virtual Status status() const override {
|
||||
// It'd be nice if status() returned a const Status& instead of a Status
|
||||
if (!status_.ok()) {
|
||||
return status_;
|
||||
} else if (file_iter_.iter() != nullptr) {
|
||||
return file_iter_.status();
|
||||
InternalIterator* NewSecondaryIterator(const Slice& meta_handle) override {
|
||||
if (meta_handle.size() != sizeof(FileDescriptor)) {
|
||||
return NewErrorInternalIterator(
|
||||
Status::Corruption("FileReader invoked with unexpected value"));
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
virtual void SetPinnedItersMgr(
|
||||
PinnedIteratorsManager* pinned_iters_mgr) override {
|
||||
pinned_iters_mgr_ = pinned_iters_mgr;
|
||||
if (file_iter_.iter()) {
|
||||
file_iter_.SetPinnedItersMgr(pinned_iters_mgr);
|
||||
}
|
||||
}
|
||||
virtual bool IsKeyPinned() const override {
|
||||
return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
|
||||
file_iter_.iter() && file_iter_.IsKeyPinned();
|
||||
}
|
||||
virtual bool IsValuePinned() const override {
|
||||
return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
|
||||
file_iter_.iter() && file_iter_.IsValuePinned();
|
||||
const FileDescriptor* fd =
|
||||
reinterpret_cast<const FileDescriptor*>(meta_handle.data());
|
||||
return table_cache_->NewIterator(
|
||||
read_options_, env_options_, icomparator_, *fd, range_del_agg_,
|
||||
nullptr /* don't need reference to table */, file_read_hist_,
|
||||
for_compaction_, nullptr /* arena */, skip_filters_, level_);
|
||||
}
|
||||
|
||||
private:
|
||||
void SkipEmptyFileForward();
|
||||
void SkipEmptyFileBackward();
|
||||
void SetFileIterator(InternalIterator* iter);
|
||||
void InitFileIterator(size_t new_file_index);
|
||||
|
||||
const Slice& file_smallest_key(size_t file_index) {
|
||||
assert(file_index < flevel_->num_files);
|
||||
return flevel_->files[file_index].smallest_key;
|
||||
bool PrefixMayMatch(const Slice& internal_key) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeyReachedUpperBound(const Slice& internal_key) {
|
||||
bool KeyReachedUpperBound(const Slice& internal_key) override {
|
||||
return read_options_.iterate_upper_bound != nullptr &&
|
||||
icomparator_.user_comparator()->Compare(
|
||||
ExtractUserKey(internal_key),
|
||||
*read_options_.iterate_upper_bound) >= 0;
|
||||
}
|
||||
|
||||
InternalIterator* NewFileIterator() {
|
||||
assert(file_index_ < flevel_->num_files);
|
||||
auto file_meta = flevel_->files[file_index_];
|
||||
if (should_sample_) {
|
||||
sample_file_read_inc(file_meta.file_metadata);
|
||||
}
|
||||
|
||||
return table_cache_->NewIterator(
|
||||
read_options_, env_options_, icomparator_, file_meta.fd, range_del_agg_,
|
||||
nullptr /* don't need reference to table */, file_read_hist_,
|
||||
for_compaction_, nullptr /* arena */, skip_filters_, level_);
|
||||
}
|
||||
|
||||
private:
|
||||
TableCache* table_cache_;
|
||||
const ReadOptions read_options_;
|
||||
const EnvOptions& env_options_;
|
||||
const InternalKeyComparator& icomparator_;
|
||||
const LevelFilesBrief* flevel_;
|
||||
mutable FileDescriptor current_value_;
|
||||
|
||||
HistogramImpl* file_read_hist_;
|
||||
bool should_sample_;
|
||||
bool for_compaction_;
|
||||
bool skip_filters_;
|
||||
size_t file_index_;
|
||||
int level_;
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
IteratorWrapper file_iter_; // May be nullptr
|
||||
PinnedIteratorsManager* pinned_iters_mgr_;
|
||||
Status status_;
|
||||
};
|
||||
|
||||
void LevelIterator::Seek(const Slice& target) {
|
||||
size_t new_file_index = FindFile(icomparator_, *flevel_, target);
|
||||
|
||||
InitFileIterator(new_file_index);
|
||||
if (file_iter_.iter() != nullptr) {
|
||||
file_iter_.Seek(target);
|
||||
}
|
||||
SkipEmptyFileForward();
|
||||
}
|
||||
|
||||
void LevelIterator::SeekForPrev(const Slice& target) {
|
||||
size_t new_file_index = FindFile(icomparator_, *flevel_, target);
|
||||
if (new_file_index >= flevel_->num_files) {
|
||||
new_file_index = flevel_->num_files - 1;
|
||||
}
|
||||
|
||||
InitFileIterator(new_file_index);
|
||||
if (file_iter_.iter() != nullptr) {
|
||||
file_iter_.SeekForPrev(target);
|
||||
SkipEmptyFileBackward();
|
||||
}
|
||||
}
|
||||
|
||||
void LevelIterator::SeekToFirst() {
|
||||
InitFileIterator(0);
|
||||
if (file_iter_.iter() != nullptr) {
|
||||
file_iter_.SeekToFirst();
|
||||
}
|
||||
SkipEmptyFileForward();
|
||||
}
|
||||
|
||||
void LevelIterator::SeekToLast() {
|
||||
InitFileIterator(flevel_->num_files - 1);
|
||||
if (file_iter_.iter() != nullptr) {
|
||||
file_iter_.SeekToLast();
|
||||
}
|
||||
SkipEmptyFileBackward();
|
||||
}
|
||||
|
||||
void LevelIterator::Next() {
|
||||
assert(Valid());
|
||||
file_iter_.Next();
|
||||
SkipEmptyFileForward();
|
||||
}
|
||||
|
||||
void LevelIterator::Prev() {
|
||||
assert(Valid());
|
||||
file_iter_.Prev();
|
||||
SkipEmptyFileBackward();
|
||||
}
|
||||
|
||||
void LevelIterator::SkipEmptyFileForward() {
|
||||
// For an error (IO error, checksum mismatch, etc), we skip the file
|
||||
// and move to the next one and continue reading data.
|
||||
// TODO this behavior is from LevelDB. We should revisit it.
|
||||
while (file_iter_.iter() == nullptr ||
|
||||
(!file_iter_.Valid() && !file_iter_.status().IsIncomplete())) {
|
||||
if (file_iter_.iter() != nullptr && !file_iter_.Valid() &&
|
||||
file_iter_.iter()->IsOutOfBound()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move to next file
|
||||
if (file_index_ >= flevel_->num_files - 1) {
|
||||
// Already at the last file
|
||||
SetFileIterator(nullptr);
|
||||
return;
|
||||
}
|
||||
if (KeyReachedUpperBound(file_smallest_key(file_index_ + 1))) {
|
||||
SetFileIterator(nullptr);
|
||||
return;
|
||||
}
|
||||
InitFileIterator(file_index_ + 1);
|
||||
if (file_iter_.iter() != nullptr) {
|
||||
file_iter_.SeekToFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LevelIterator::SkipEmptyFileBackward() {
|
||||
while (file_iter_.iter() == nullptr ||
|
||||
(!file_iter_.Valid() && !file_iter_.status().IsIncomplete())) {
|
||||
// Move to previous file
|
||||
if (file_index_ == 0) {
|
||||
// Already the first file
|
||||
SetFileIterator(nullptr);
|
||||
return;
|
||||
}
|
||||
InitFileIterator(file_index_ - 1);
|
||||
if (file_iter_.iter() != nullptr) {
|
||||
file_iter_.SeekToLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LevelIterator::SetFileIterator(InternalIterator* iter) {
|
||||
if (file_iter_.iter() != nullptr && status_.ok()) {
|
||||
// TODO right now we don't invalidate the iterator even if the status is
|
||||
// not OK. We should consider to do that so that it is harder for users to
|
||||
// skip errors.
|
||||
status_ = file_iter_.status();
|
||||
}
|
||||
|
||||
if (pinned_iters_mgr_ && iter) {
|
||||
iter->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
InternalIterator* old_iter = file_iter_.Set(iter);
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinIterator(old_iter);
|
||||
} else {
|
||||
delete old_iter;
|
||||
}
|
||||
}
|
||||
|
||||
void LevelIterator::InitFileIterator(size_t new_file_index) {
|
||||
if (new_file_index >= flevel_->num_files) {
|
||||
file_index_ = new_file_index;
|
||||
SetFileIterator(nullptr);
|
||||
return;
|
||||
} else {
|
||||
// If the file iterator shows incomplete, we try it again if users seek
|
||||
// to the same file, as this time we may go to a different data block
|
||||
// which is cached in block cache.
|
||||
//
|
||||
if (file_iter_.iter() != nullptr && !file_iter_.status().IsIncomplete() &&
|
||||
new_file_index == file_index_) {
|
||||
// file_iter_ is already constructed with this iterator, so
|
||||
// no need to change anything
|
||||
} else {
|
||||
file_index_ = new_file_index;
|
||||
InternalIterator* iter = NewFileIterator();
|
||||
SetFileIterator(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A wrapper of version builder which references the current version in
|
||||
// constructor and unref it in the destructor.
|
||||
// Both of the constructor and destructor need to be called inside DB Mutex.
|
||||
@@ -986,18 +854,24 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
sample_file_read_inc(meta);
|
||||
}
|
||||
}
|
||||
} else if (storage_info_.LevelFilesBrief(level).num_files > 0) {
|
||||
} else {
|
||||
// For levels > 0, we can use a concatenating iterator that sequentially
|
||||
// walks through the non-overlapping files in the level, opening them
|
||||
// lazily.
|
||||
auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
|
||||
merge_iter_builder->AddIterator(new (mem) LevelIterator(
|
||||
cfd_->table_cache(), read_options, soptions,
|
||||
auto* mem = arena->AllocateAligned(sizeof(LevelFileIteratorState));
|
||||
auto* state = new (mem)
|
||||
LevelFileIteratorState(cfd_->table_cache(), read_options, soptions,
|
||||
cfd_->internal_comparator(),
|
||||
cfd_->internal_stats()->GetFileReadHist(level),
|
||||
false /* for_compaction */,
|
||||
cfd_->ioptions()->prefix_extractor != nullptr,
|
||||
IsFilterSkipped(level), level, range_del_agg);
|
||||
mem = arena->AllocateAligned(sizeof(LevelFileNumIterator));
|
||||
auto* first_level_iter = new (mem) LevelFileNumIterator(
|
||||
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
|
||||
should_sample_file_read(),
|
||||
cfd_->internal_stats()->GetFileReadHist(level),
|
||||
false /* for_compaction */, IsFilterSkipped(level), level,
|
||||
range_del_agg));
|
||||
should_sample_file_read());
|
||||
merge_iter_builder->AddIterator(
|
||||
NewTwoLevelIterator(state, first_level_iter, arena, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2102,7 +1976,7 @@ void VersionStorageInfo::ExtendFileRangeOverlappingInterval(
|
||||
#endif
|
||||
*start_index = mid_index + 1;
|
||||
*end_index = mid_index;
|
||||
int count __attribute__((__unused__));
|
||||
int count __attribute__((unused));
|
||||
count = 0;
|
||||
|
||||
// check backwards from 'mid' to lower indices
|
||||
@@ -3858,13 +3732,17 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
}
|
||||
} else {
|
||||
// Create concatenating iterator for the files from this level
|
||||
list[num++] = new LevelIterator(
|
||||
cfd->table_cache(), read_options, env_options_compactions,
|
||||
cfd->internal_comparator(), c->input_levels(which),
|
||||
false /* should_sample */,
|
||||
nullptr /* no per level latency histogram */,
|
||||
true /* for_compaction */, false /* skip_filters */,
|
||||
(int)which /* level */, range_del_agg);
|
||||
list[num++] = NewTwoLevelIterator(
|
||||
new LevelFileIteratorState(
|
||||
cfd->table_cache(), read_options, env_options_compactions,
|
||||
cfd->internal_comparator(),
|
||||
nullptr /* no per level latency histogram */,
|
||||
true /* for_compaction */, false /* prefix enabled */,
|
||||
false /* skip_filters */, (int)which /* level */,
|
||||
range_del_agg),
|
||||
new LevelFileNumIterator(cfd->internal_comparator(),
|
||||
c->input_levels(which),
|
||||
false /* don't sample compaction */));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,15 +39,6 @@ namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status WalManager::DeleteFile(const std::string& fname, uint64_t number) {
|
||||
auto s = env_->DeleteFile(db_options_.wal_dir + "/" + fname);
|
||||
if (s.ok()) {
|
||||
MutexLock l(&read_first_record_cache_mutex_);
|
||||
read_first_record_cache_.erase(number);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status WalManager::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// First get sorted files in db dir, then get sorted files from archived
|
||||
// dir, to avoid a race condition where a log file is moved to archived
|
||||
|
||||
@@ -49,8 +49,6 @@ class WalManager {
|
||||
|
||||
void ArchiveWALFile(const std::string& fname, uint64_t number);
|
||||
|
||||
Status DeleteFile(const std::string& fname, uint64_t number);
|
||||
|
||||
Status TEST_ReadFirstRecord(const WalFileType type, const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
return ReadFirstRecord(type, number, sequence);
|
||||
|
||||
@@ -303,7 +303,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "SKIPPED as WalManager is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
+32
-146
@@ -15,12 +15,10 @@
|
||||
// kTypeValue varstring varstring
|
||||
// kTypeDeletion varstring
|
||||
// kTypeSingleDeletion varstring
|
||||
// kTypeRangeDeletion varstring varstring
|
||||
// kTypeMerge varstring varstring
|
||||
// kTypeColumnFamilyValue varint32 varstring varstring
|
||||
// kTypeColumnFamilyDeletion varint32 varstring
|
||||
// kTypeColumnFamilySingleDeletion varint32 varstring
|
||||
// kTypeColumnFamilyRangeDeletion varint32 varstring varstring
|
||||
// kTypeColumnFamilyDeletion varint32 varstring varstring
|
||||
// kTypeColumnFamilySingleDeletion varint32 varstring varstring
|
||||
// kTypeColumnFamilyMerge varint32 varstring varstring
|
||||
// kTypeBeginPrepareXID varstring
|
||||
// kTypeEndPrepareXID
|
||||
@@ -146,12 +144,6 @@ WriteBatch::WriteBatch(const std::string& rep)
|
||||
max_bytes_(0),
|
||||
rep_(rep) {}
|
||||
|
||||
WriteBatch::WriteBatch(std::string&& rep)
|
||||
: save_points_(nullptr),
|
||||
content_flags_(ContentFlags::DEFERRED),
|
||||
max_bytes_(0),
|
||||
rep_(std::move(rep)) {}
|
||||
|
||||
WriteBatch::WriteBatch(const WriteBatch& src)
|
||||
: save_points_(src.save_points_),
|
||||
wal_term_point_(src.wal_term_point_),
|
||||
@@ -402,22 +394,14 @@ Status WriteBatch::Iterate(Handler* handler) const {
|
||||
bool empty_batch = true;
|
||||
int found = 0;
|
||||
Status s;
|
||||
char tag = 0;
|
||||
uint32_t column_family = 0; // default
|
||||
while ((s.ok() || UNLIKELY(s.IsTryAgain())) && !input.empty() &&
|
||||
handler->Continue()) {
|
||||
if (LIKELY(!s.IsTryAgain())) {
|
||||
tag = 0;
|
||||
column_family = 0; // default
|
||||
while (s.ok() && !input.empty() && handler->Continue()) {
|
||||
char tag = 0;
|
||||
uint32_t column_family = 0; // default
|
||||
|
||||
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
|
||||
&blob, &xid);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
assert(s.IsTryAgain());
|
||||
s = Status::OK();
|
||||
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
|
||||
&blob, &xid);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
@@ -426,59 +410,47 @@ Status WriteBatch::Iterate(Handler* handler) const {
|
||||
assert(content_flags_.load(std::memory_order_relaxed) &
|
||||
(ContentFlags::DEFERRED | ContentFlags::HAS_PUT));
|
||||
s = handler->PutCF(column_family, key, value);
|
||||
if (LIKELY(s.ok())) {
|
||||
empty_batch = false;
|
||||
found++;
|
||||
}
|
||||
empty_batch = false;
|
||||
found++;
|
||||
break;
|
||||
case kTypeColumnFamilyDeletion:
|
||||
case kTypeDeletion:
|
||||
assert(content_flags_.load(std::memory_order_relaxed) &
|
||||
(ContentFlags::DEFERRED | ContentFlags::HAS_DELETE));
|
||||
s = handler->DeleteCF(column_family, key);
|
||||
if (LIKELY(s.ok())) {
|
||||
empty_batch = false;
|
||||
found++;
|
||||
}
|
||||
empty_batch = false;
|
||||
found++;
|
||||
break;
|
||||
case kTypeColumnFamilySingleDeletion:
|
||||
case kTypeSingleDeletion:
|
||||
assert(content_flags_.load(std::memory_order_relaxed) &
|
||||
(ContentFlags::DEFERRED | ContentFlags::HAS_SINGLE_DELETE));
|
||||
s = handler->SingleDeleteCF(column_family, key);
|
||||
if (LIKELY(s.ok())) {
|
||||
empty_batch = false;
|
||||
found++;
|
||||
}
|
||||
empty_batch = false;
|
||||
found++;
|
||||
break;
|
||||
case kTypeColumnFamilyRangeDeletion:
|
||||
case kTypeRangeDeletion:
|
||||
assert(content_flags_.load(std::memory_order_relaxed) &
|
||||
(ContentFlags::DEFERRED | ContentFlags::HAS_DELETE_RANGE));
|
||||
s = handler->DeleteRangeCF(column_family, key, value);
|
||||
if (LIKELY(s.ok())) {
|
||||
empty_batch = false;
|
||||
found++;
|
||||
}
|
||||
empty_batch = false;
|
||||
found++;
|
||||
break;
|
||||
case kTypeColumnFamilyMerge:
|
||||
case kTypeMerge:
|
||||
assert(content_flags_.load(std::memory_order_relaxed) &
|
||||
(ContentFlags::DEFERRED | ContentFlags::HAS_MERGE));
|
||||
s = handler->MergeCF(column_family, key, value);
|
||||
if (LIKELY(s.ok())) {
|
||||
empty_batch = false;
|
||||
found++;
|
||||
}
|
||||
empty_batch = false;
|
||||
found++;
|
||||
break;
|
||||
case kTypeColumnFamilyBlobIndex:
|
||||
case kTypeBlobIndex:
|
||||
assert(content_flags_.load(std::memory_order_relaxed) &
|
||||
(ContentFlags::DEFERRED | ContentFlags::HAS_BLOB_INDEX));
|
||||
s = handler->PutBlobIndexCF(column_family, key, value);
|
||||
if (LIKELY(s.ok())) {
|
||||
found++;
|
||||
}
|
||||
found++;
|
||||
break;
|
||||
case kTypeLogData:
|
||||
handler->LogData(blob);
|
||||
@@ -575,13 +547,6 @@ size_t WriteBatchInternal::GetFirstOffset(WriteBatch* b) {
|
||||
|
||||
Status WriteBatchInternal::Put(WriteBatch* b, uint32_t column_family_id,
|
||||
const Slice& key, const Slice& value) {
|
||||
if (key.size() > size_t{port::kMaxUint32}) {
|
||||
return Status::InvalidArgument("key is too large");
|
||||
}
|
||||
if (value.size() > size_t{port::kMaxUint32}) {
|
||||
return Status::InvalidArgument("value is too large");
|
||||
}
|
||||
|
||||
LocalSavePoint save(b);
|
||||
WriteBatchInternal::SetCount(b, WriteBatchInternal::Count(b) + 1);
|
||||
if (column_family_id == 0) {
|
||||
@@ -604,33 +569,8 @@ Status WriteBatch::Put(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
value);
|
||||
}
|
||||
|
||||
Status WriteBatchInternal::CheckSlicePartsLength(const SliceParts& key,
|
||||
const SliceParts& value) {
|
||||
size_t total_key_bytes = 0;
|
||||
for (int i = 0; i < key.num_parts; ++i) {
|
||||
total_key_bytes += key.parts[i].size();
|
||||
}
|
||||
if (total_key_bytes >= size_t{port::kMaxUint32}) {
|
||||
return Status::InvalidArgument("key is too large");
|
||||
}
|
||||
|
||||
size_t total_value_bytes = 0;
|
||||
for (int i = 0; i < value.num_parts; ++i) {
|
||||
total_value_bytes += value.parts[i].size();
|
||||
}
|
||||
if (total_value_bytes >= size_t{port::kMaxUint32}) {
|
||||
return Status::InvalidArgument("value is too large");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status WriteBatchInternal::Put(WriteBatch* b, uint32_t column_family_id,
|
||||
const SliceParts& key, const SliceParts& value) {
|
||||
Status s = CheckSlicePartsLength(key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
LocalSavePoint save(b);
|
||||
WriteBatchInternal::SetCount(b, WriteBatchInternal::Count(b) + 1);
|
||||
if (column_family_id == 0) {
|
||||
@@ -847,13 +787,6 @@ Status WriteBatch::DeleteRange(ColumnFamilyHandle* column_family,
|
||||
|
||||
Status WriteBatchInternal::Merge(WriteBatch* b, uint32_t column_family_id,
|
||||
const Slice& key, const Slice& value) {
|
||||
if (key.size() > size_t{port::kMaxUint32}) {
|
||||
return Status::InvalidArgument("key is too large");
|
||||
}
|
||||
if (value.size() > size_t{port::kMaxUint32}) {
|
||||
return Status::InvalidArgument("value is too large");
|
||||
}
|
||||
|
||||
LocalSavePoint save(b);
|
||||
WriteBatchInternal::SetCount(b, WriteBatchInternal::Count(b) + 1);
|
||||
if (column_family_id == 0) {
|
||||
@@ -879,11 +812,6 @@ Status WriteBatch::Merge(ColumnFamilyHandle* column_family, const Slice& key,
|
||||
Status WriteBatchInternal::Merge(WriteBatch* b, uint32_t column_family_id,
|
||||
const SliceParts& key,
|
||||
const SliceParts& value) {
|
||||
Status s = CheckSlicePartsLength(key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
LocalSavePoint save(b);
|
||||
WriteBatchInternal::SetCount(b, WriteBatchInternal::Count(b) + 1);
|
||||
if (column_family_id == 0) {
|
||||
@@ -1148,23 +1076,12 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
MaybeAdvanceSeq();
|
||||
return seek_status;
|
||||
}
|
||||
Status ret_status;
|
||||
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
auto* moptions = mem->GetImmutableMemTableOptions();
|
||||
// inplace_update_support is inconsistent with snapshots, and therefore with
|
||||
// any kind of transactions including the ones that use seq_per_batch
|
||||
assert(!seq_per_batch_ || !moptions->inplace_update_support);
|
||||
if (!moptions->inplace_update_support) {
|
||||
bool mem_res =
|
||||
mem->Add(sequence_, value_type, key, value,
|
||||
concurrent_memtable_writes_, get_post_process_info(mem));
|
||||
if (UNLIKELY(!mem_res)) {
|
||||
assert(seq_per_batch_);
|
||||
ret_status = Status::TryAgain("key+seq exists");
|
||||
const bool BATCH_BOUNDRY = true;
|
||||
MaybeAdvanceSeq(BATCH_BOUNDRY);
|
||||
}
|
||||
mem->Add(sequence_, value_type, key, value, concurrent_memtable_writes_,
|
||||
get_post_process_info(mem));
|
||||
} else if (moptions->inplace_callback == nullptr) {
|
||||
assert(!concurrent_memtable_writes_);
|
||||
mem->Update(sequence_, key, value);
|
||||
@@ -1200,17 +1117,11 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
value, &merged_value);
|
||||
if (status == UpdateStatus::UPDATED_INPLACE) {
|
||||
// prev_value is updated in-place with final value.
|
||||
bool mem_res __attribute__((__unused__));
|
||||
mem_res = mem->Add(
|
||||
sequence_, value_type, key, Slice(prev_buffer, prev_size));
|
||||
assert(mem_res);
|
||||
mem->Add(sequence_, value_type, key, Slice(prev_buffer, prev_size));
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
} else if (status == UpdateStatus::UPDATED) {
|
||||
// merged_value contains the final value.
|
||||
bool mem_res __attribute__((__unused__));
|
||||
mem_res =
|
||||
mem->Add(sequence_, value_type, key, Slice(merged_value));
|
||||
assert(mem_res);
|
||||
mem->Add(sequence_, value_type, key, Slice(merged_value));
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
}
|
||||
}
|
||||
@@ -1220,7 +1131,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// in memtable add/update.
|
||||
MaybeAdvanceSeq();
|
||||
CheckMemtableFull();
|
||||
return ret_status;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
@@ -1230,20 +1141,12 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
Status DeleteImpl(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value, ValueType delete_type) {
|
||||
Status ret_status;
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
bool mem_res =
|
||||
mem->Add(sequence_, delete_type, key, value,
|
||||
concurrent_memtable_writes_, get_post_process_info(mem));
|
||||
if (UNLIKELY(!mem_res)) {
|
||||
assert(seq_per_batch_);
|
||||
ret_status = Status::TryAgain("key+seq exists");
|
||||
const bool BATCH_BOUNDRY = true;
|
||||
MaybeAdvanceSeq(BATCH_BOUNDRY);
|
||||
}
|
||||
mem->Add(sequence_, delete_type, key, value, concurrent_memtable_writes_,
|
||||
get_post_process_info(mem));
|
||||
MaybeAdvanceSeq();
|
||||
CheckMemtableFull();
|
||||
return ret_status;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status DeleteCF(uint32_t column_family_id,
|
||||
@@ -1335,7 +1238,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return seek_status;
|
||||
}
|
||||
|
||||
Status ret_status;
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
auto* moptions = mem->GetImmutableMemTableOptions();
|
||||
bool perform_merge = false;
|
||||
@@ -1391,30 +1293,18 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
perform_merge = false;
|
||||
} else {
|
||||
// 3) Add value to memtable
|
||||
bool mem_res = mem->Add(sequence_, kTypeValue, key, new_value);
|
||||
if (UNLIKELY(!mem_res)) {
|
||||
assert(seq_per_batch_);
|
||||
ret_status = Status::TryAgain("key+seq exists");
|
||||
const bool BATCH_BOUNDRY = true;
|
||||
MaybeAdvanceSeq(BATCH_BOUNDRY);
|
||||
}
|
||||
mem->Add(sequence_, kTypeValue, key, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!perform_merge) {
|
||||
// Add merge operator to memtable
|
||||
bool mem_res = mem->Add(sequence_, kTypeMerge, key, value);
|
||||
if (UNLIKELY(!mem_res)) {
|
||||
assert(seq_per_batch_);
|
||||
ret_status = Status::TryAgain("key+seq exists");
|
||||
const bool BATCH_BOUNDRY = true;
|
||||
MaybeAdvanceSeq(BATCH_BOUNDRY);
|
||||
}
|
||||
mem->Add(sequence_, kTypeMerge, key, value);
|
||||
}
|
||||
|
||||
MaybeAdvanceSeq();
|
||||
CheckMemtableFull();
|
||||
return ret_status;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status PutBlobIndexCF(uint32_t column_family_id, const Slice& key,
|
||||
@@ -1598,8 +1488,6 @@ Status WriteBatchInternal::InsertInto(
|
||||
if (!w->status.ok()) {
|
||||
return w->status;
|
||||
}
|
||||
assert(!seq_per_batch || w->batch_cnt != 0);
|
||||
assert(!seq_per_batch || inserter.sequence() - w->sequence == w->batch_cnt);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -1608,7 +1496,7 @@ Status WriteBatchInternal::InsertInto(
|
||||
WriteThread::Writer* writer, SequenceNumber sequence,
|
||||
ColumnFamilyMemTables* memtables, FlushScheduler* flush_scheduler,
|
||||
bool ignore_missing_column_families, uint64_t log_number, DB* db,
|
||||
bool concurrent_memtable_writes, bool seq_per_batch, size_t batch_cnt) {
|
||||
bool concurrent_memtable_writes, bool seq_per_batch) {
|
||||
assert(writer->ShouldWriteToMemtable());
|
||||
MemTableInserter inserter(sequence, memtables, flush_scheduler,
|
||||
ignore_missing_column_families, log_number, db,
|
||||
@@ -1617,8 +1505,6 @@ Status WriteBatchInternal::InsertInto(
|
||||
SetSequence(writer->batch, sequence);
|
||||
inserter.set_log_number_ref(writer->log_ref);
|
||||
Status s = writer->batch->Iterate(&inserter);
|
||||
assert(!seq_per_batch || batch_cnt != 0);
|
||||
assert(!seq_per_batch || inserter.sequence() - sequence == batch_cnt);
|
||||
if (concurrent_memtable_writes) {
|
||||
inserter.PostProcess();
|
||||
}
|
||||
|
||||
@@ -138,9 +138,6 @@ class WriteBatchInternal {
|
||||
|
||||
static Status SetContents(WriteBatch* batch, const Slice& contents);
|
||||
|
||||
static Status CheckSlicePartsLength(const SliceParts& key,
|
||||
const SliceParts& value);
|
||||
|
||||
// Inserts batches[i] into memtable, for i in 0..num_batches-1 inclusive.
|
||||
//
|
||||
// If ignore_missing_column_families == true. WriteBatch
|
||||
@@ -185,7 +182,7 @@ class WriteBatchInternal {
|
||||
bool ignore_missing_column_families = false,
|
||||
uint64_t log_number = 0, DB* db = nullptr,
|
||||
bool concurrent_memtable_writes = false,
|
||||
bool seq_per_batch = false, size_t batch_cnt = 0);
|
||||
bool seq_per_batch = false);
|
||||
|
||||
static Status Append(WriteBatch* dst, const WriteBatch* src,
|
||||
const bool WAL_only = false);
|
||||
|
||||
@@ -291,7 +291,7 @@ TEST_F(WriteCallbackTest, WriteWithCallbackTest) {
|
||||
woptions.disableWAL = !enable_WAL;
|
||||
woptions.sync = enable_WAL;
|
||||
Status s;
|
||||
if (seq_per_batch) {
|
||||
if (seq_per_batch && two_queues) {
|
||||
class PublishSeqCallback : public PreReleaseCallback {
|
||||
public:
|
||||
PublishSeqCallback(DBImpl* db_impl_in)
|
||||
@@ -302,13 +302,9 @@ TEST_F(WriteCallbackTest, WriteWithCallbackTest) {
|
||||
}
|
||||
DBImpl* db_impl_;
|
||||
} publish_seq_callback(db_impl);
|
||||
// seq_per_batch requires a natural batch separator or Noop
|
||||
WriteBatchInternal::InsertNoop(&write_op.write_batch_);
|
||||
const size_t ONE_BATCH = 1;
|
||||
s = db_impl->WriteImpl(
|
||||
woptions, &write_op.write_batch_, &write_op.callback_,
|
||||
nullptr, 0, false, nullptr, ONE_BATCH,
|
||||
two_queues ? &publish_seq_callback : nullptr);
|
||||
s = db_impl->WriteImpl(woptions, &write_op.write_batch_,
|
||||
&write_op.callback_, nullptr, 0, false,
|
||||
nullptr, &publish_seq_callback);
|
||||
} else {
|
||||
s = db_impl->WriteWithCallback(
|
||||
woptions, &write_op.write_batch_, &write_op.callback_);
|
||||
@@ -433,7 +429,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as WriteWithCallback is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
|
||||
@@ -118,7 +118,6 @@ class WriteThread {
|
||||
bool no_slowdown;
|
||||
bool disable_wal;
|
||||
bool disable_memtable;
|
||||
size_t batch_cnt; // if non-zero, number of sub-batches in the write batch
|
||||
PreReleaseCallback* pre_release_callback;
|
||||
uint64_t log_used; // log number that this batch was inserted into
|
||||
uint64_t log_ref; // log number that memtable insert should reference
|
||||
@@ -129,7 +128,6 @@ class WriteThread {
|
||||
SequenceNumber sequence; // the sequence number to use for the first key
|
||||
Status status; // status of memtable inserter
|
||||
Status callback_status; // status returned by callback->Callback()
|
||||
|
||||
std::aligned_storage<sizeof(std::mutex)>::type state_mutex_bytes;
|
||||
std::aligned_storage<sizeof(std::condition_variable)>::type state_cv_bytes;
|
||||
Writer* link_older; // read/write only before linking, or as leader
|
||||
@@ -141,7 +139,6 @@ class WriteThread {
|
||||
no_slowdown(false),
|
||||
disable_wal(false),
|
||||
disable_memtable(false),
|
||||
batch_cnt(0),
|
||||
pre_release_callback(nullptr),
|
||||
log_used(0),
|
||||
log_ref(0),
|
||||
@@ -155,14 +152,12 @@ class WriteThread {
|
||||
|
||||
Writer(const WriteOptions& write_options, WriteBatch* _batch,
|
||||
WriteCallback* _callback, uint64_t _log_ref, bool _disable_memtable,
|
||||
size_t _batch_cnt = 0,
|
||||
PreReleaseCallback* _pre_release_callback = nullptr)
|
||||
: batch(_batch),
|
||||
sync(write_options.sync),
|
||||
no_slowdown(write_options.no_slowdown),
|
||||
disable_wal(write_options.disableWAL),
|
||||
disable_memtable(_disable_memtable),
|
||||
batch_cnt(_batch_cnt),
|
||||
pre_release_callback(_pre_release_callback),
|
||||
log_used(0),
|
||||
log_ref(_log_ref),
|
||||
|
||||
+3
-3
@@ -105,13 +105,13 @@ GEM
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9.7)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.3.0)
|
||||
mini_portile2 (2.1.0)
|
||||
minima (2.0.0)
|
||||
minitest (5.9.1)
|
||||
multipart-post (2.0.0)
|
||||
net-dns (0.8.0)
|
||||
nokogiri (1.8.1)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogiri (1.6.8.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
octokit (4.4.1)
|
||||
sawyer (~> 0.7.0, >= 0.5.3)
|
||||
pathutil (0.14.0)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
title: RocksDB 5.10.2 Released!
|
||||
layout: post
|
||||
author: siying
|
||||
category: blog
|
||||
---
|
||||
|
||||
### Public API Change
|
||||
* When running `make` with environment variable `USE_SSE` set and `PORTABLE` unset, will use all machine features available locally. Previously this combination only compiled SSE-related features.
|
||||
|
||||
### New Features
|
||||
* CRC32C is now using the 3-way pipelined SSE algorithm `crc32c_3way` on supported platforms to improve performance. The system will choose to use this algorithm on supported platforms automatically whenever possible. If PCLMULQDQ is not supported it will fall back to the old Fast_CRC32 algorithm.
|
||||
* Provide lifetime hints when writing files on Linux. This reduces hardware write-amp on storage devices supporting multiple streams.
|
||||
* Add a DB stat, `NUMBER_ITER_SKIP`, which returns how many internal keys were skipped during iterations (e.g., due to being tombstones or duplicate versions of a key).
|
||||
* Add PerfContext counters, `key_lock_wait_count` and `key_lock_wait_time`, which measure the number of times transactions wait on key locks and total amount of time waiting.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix IOError on WAL write doesn't propagate to write group follower
|
||||
* Make iterator invalid on merge error.
|
||||
* Fix performance issue in `IngestExternalFile()` affecting databases with large number of SST files.
|
||||
* Fix possible corruption to LSM structure when `DeleteFilesInRange()` deletes a subset of files spanned by a `DeleteRange()` marker.
|
||||
* Fix DB::Flush() keep waiting after flush finish under certain condition.
|
||||
Vendored
+1
-11
@@ -73,19 +73,9 @@ RandomAccessFile::~RandomAccessFile() {
|
||||
WritableFile::~WritableFile() {
|
||||
}
|
||||
|
||||
Logger::~Logger() { }
|
||||
|
||||
Status Logger::Close() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
return CloseImpl();
|
||||
} else {
|
||||
return Status::OK();
|
||||
}
|
||||
Logger::~Logger() {
|
||||
}
|
||||
|
||||
Status Logger::CloseImpl() { return Status::NotSupported(); }
|
||||
|
||||
FileLock::~FileLock() {
|
||||
}
|
||||
|
||||
|
||||
Vendored
+5
-18
@@ -277,21 +277,6 @@ class HdfsLogger : public Logger {
|
||||
HdfsWritableFile* file_;
|
||||
uint64_t (*gettid_)(); // Return the thread id for the current thread
|
||||
|
||||
Status HdfsCloseHelper() {
|
||||
ROCKS_LOG_DEBUG(mylog, "[hdfs] HdfsLogger closed %s\n",
|
||||
file_->getName().c_str());
|
||||
Status s = file_->Close();
|
||||
if (mylog != nullptr && mylog == this) {
|
||||
mylog = nullptr;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual Status CloseImpl() override {
|
||||
return HdfsCloseHelper();
|
||||
}
|
||||
|
||||
public:
|
||||
HdfsLogger(HdfsWritableFile* f, uint64_t (*gettid)())
|
||||
: file_(f), gettid_(gettid) {
|
||||
@@ -300,9 +285,11 @@ class HdfsLogger : public Logger {
|
||||
}
|
||||
|
||||
virtual ~HdfsLogger() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
HdfsCloseHelper();
|
||||
ROCKS_LOG_DEBUG(mylog, "[hdfs] HdfsLogger closed %s\n",
|
||||
file_->getName().c_str());
|
||||
delete file_;
|
||||
if (mylog != nullptr && mylog == this) {
|
||||
mylog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -763,7 +763,7 @@ class PosixEnv : public Env {
|
||||
|
||||
virtual Status GetAbsolutePath(const std::string& db_path,
|
||||
std::string* output_path) override {
|
||||
if (!db_path.empty() && db_path[0] == '/') {
|
||||
if (db_path.find('/') == 0) {
|
||||
*output_path = db_path;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Vendored
-68
@@ -1475,74 +1475,6 @@ TEST_P(EnvPosixTestWithParam, PosixRandomRWFileRandomized) {
|
||||
env_->DeleteFile(path);
|
||||
}
|
||||
|
||||
class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv() : EnvWrapper(Env::Default()),
|
||||
close_count(0) { }
|
||||
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
TestLogger(TestEnv *env_ptr) : Logger() { env = env_ptr; }
|
||||
~TestLogger() {
|
||||
if (!closed_) {
|
||||
CloseHelper();
|
||||
}
|
||||
}
|
||||
virtual void Logv(const char *format, va_list ap) override { };
|
||||
protected:
|
||||
virtual Status CloseImpl() override {
|
||||
return CloseHelper();
|
||||
}
|
||||
private:
|
||||
Status CloseHelper() {
|
||||
env->CloseCountInc();;
|
||||
return Status::OK();
|
||||
}
|
||||
TestEnv *env;
|
||||
};
|
||||
|
||||
void CloseCountInc() { close_count++; }
|
||||
|
||||
int GetCloseCount() { return close_count; }
|
||||
|
||||
virtual Status NewLogger(const std::string& fname,
|
||||
shared_ptr<Logger>* result) {
|
||||
result->reset(new TestLogger(this));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
int close_count;
|
||||
};
|
||||
|
||||
class EnvTest : public testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(EnvTest, Close) {
|
||||
TestEnv *env = new TestEnv();
|
||||
std::shared_ptr<Logger> logger;
|
||||
Status s;
|
||||
|
||||
s = env->NewLogger("", &logger);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
logger.get()->Close();
|
||||
ASSERT_EQ(env->GetCloseCount(), 1);
|
||||
// Call Close() again. CloseHelper() should not be called again
|
||||
logger.get()->Close();
|
||||
ASSERT_EQ(env->GetCloseCount(), 1);
|
||||
logger.reset();
|
||||
ASSERT_EQ(env->GetCloseCount(), 1);
|
||||
|
||||
s = env->NewLogger("", &logger);
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
logger.reset();
|
||||
ASSERT_EQ(env->GetCloseCount(), 2);
|
||||
|
||||
delete env;
|
||||
}
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DefaultEnvWithoutDirectIO, EnvPosixTestWithParam,
|
||||
::testing::Values(std::pair<Env*, bool>(Env::Default(),
|
||||
false)));
|
||||
|
||||
Vendored
+3
-7
@@ -799,7 +799,7 @@ Status PosixWritableFile::Close() {
|
||||
// trim the extra space preallocated at the end of the file
|
||||
// NOTE(ljin): we probably don't want to surface failure as an IOError,
|
||||
// but it will be nice to log these errors.
|
||||
int dummy __attribute__((__unused__));
|
||||
int dummy __attribute__((unused));
|
||||
dummy = ftruncate(fd_, filesize_);
|
||||
#if defined(ROCKSDB_FALLOCATE_PRESENT) && !defined(TRAVIS)
|
||||
// in some file systems, ftruncate only trims trailing space if the
|
||||
@@ -873,12 +873,8 @@ void PosixWritableFile::SetWriteLifeTimeHint(Env::WriteLifeTimeHint hint) {
|
||||
if (fcntl(fd_, F_SET_RW_HINT, &hint) == 0) {
|
||||
write_hint_ = hint;
|
||||
}
|
||||
#else
|
||||
(void)hint;
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
#else
|
||||
(void)hint;
|
||||
#endif // OS_LINUX
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
Status PosixWritableFile::InvalidateCache(size_t offset, size_t length) {
|
||||
|
||||
Vendored
+1
-20
@@ -24,7 +24,6 @@
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include "env/io_posix.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -33,15 +32,6 @@ namespace rocksdb {
|
||||
|
||||
class PosixLogger : public Logger {
|
||||
private:
|
||||
Status PosixCloseHelper() {
|
||||
int ret;
|
||||
|
||||
ret = fclose(file_);
|
||||
if (ret) {
|
||||
return IOError("Unable to close log file", "", ret);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
FILE* file_;
|
||||
uint64_t (*gettid_)(); // Return the thread id for the current thread
|
||||
std::atomic_size_t log_size_;
|
||||
@@ -50,12 +40,6 @@ class PosixLogger : public Logger {
|
||||
std::atomic_uint_fast64_t last_flush_micros_;
|
||||
Env* env_;
|
||||
std::atomic<bool> flush_pending_;
|
||||
|
||||
protected:
|
||||
virtual Status CloseImpl() override {
|
||||
return PosixCloseHelper();
|
||||
}
|
||||
|
||||
public:
|
||||
PosixLogger(FILE* f, uint64_t (*gettid)(), Env* env,
|
||||
const InfoLogLevel log_level = InfoLogLevel::ERROR_LEVEL)
|
||||
@@ -68,10 +52,7 @@ class PosixLogger : public Logger {
|
||||
env_(env),
|
||||
flush_pending_(false) {}
|
||||
virtual ~PosixLogger() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
PosixCloseHelper();
|
||||
}
|
||||
fclose(file_);
|
||||
}
|
||||
virtual void Flush() override {
|
||||
TEST_SYNC_POINT("PosixLogger::Flush:Begin1");
|
||||
|
||||
@@ -329,15 +329,7 @@ void CancelAllBackgroundWork(DB* db, bool wait = false);
|
||||
// in the range.
|
||||
// Snapshots before the delete might not see the data in the given range.
|
||||
Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end,
|
||||
bool include_end = true);
|
||||
|
||||
// Delete files in multiple ranges at once
|
||||
// Delete files in a lot of ranges one at a time can be slow, use this API for
|
||||
// better performance in that case.
|
||||
Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end = true);
|
||||
const Slice* begin, const Slice* end);
|
||||
|
||||
// Verify the checksum of file
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
|
||||
+3
-21
@@ -93,21 +93,13 @@ static const int kMinorVersion = __ROCKSDB_MINOR__;
|
||||
|
||||
// A range of keys
|
||||
struct Range {
|
||||
Slice start;
|
||||
Slice limit;
|
||||
Slice start; // Included in the range
|
||||
Slice limit; // Not included in the range
|
||||
|
||||
Range() { }
|
||||
Range(const Slice& s, const Slice& l) : start(s), limit(l) { }
|
||||
};
|
||||
|
||||
struct RangePtr {
|
||||
const Slice* start;
|
||||
const Slice* limit;
|
||||
|
||||
RangePtr() : start(nullptr), limit(nullptr) { }
|
||||
RangePtr(const Slice* s, const Slice* l) : start(s), limit(l) { }
|
||||
};
|
||||
|
||||
// A collections of table properties objects, where
|
||||
// key: is the table's file name.
|
||||
// value: the table properties object of the given table.
|
||||
@@ -171,16 +163,6 @@ class DB {
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr);
|
||||
|
||||
// Close the DB by releasing resources, closing files etc. This should be
|
||||
// called before calling the desctructor so that the caller can get back a
|
||||
// status in case there are any errors. This will not fsync the WAL files.
|
||||
// If syncing is required, the caller must first call SyncWAL(), or Write()
|
||||
// using an empty write batch with WriteOptions.sync=true.
|
||||
// Regardless of the return status, the DB must be freed. If the return
|
||||
// status is NotSupported(), then the DB implementation does cleanup in the
|
||||
// destructor
|
||||
virtual Status Close() { return Status::NotSupported(); }
|
||||
|
||||
// ListColumnFamilies will open the DB specified by argument name
|
||||
// and return the list of all column families in that DB
|
||||
// through column_families argument. The ordering of
|
||||
@@ -991,7 +973,7 @@ class DB {
|
||||
// the file can fit in, and ingest the file into this level (2). A file that
|
||||
// have a key range that overlap with the memtable key range will require us
|
||||
// to Flush the memtable first before ingesting the file.
|
||||
// In the second mode we will always ingest in the bottom most level (see
|
||||
// In the second mode we will always ingest in the bottom mode level (see
|
||||
// docs to IngestExternalFileOptions::ingest_behind).
|
||||
//
|
||||
// (1) External SST files can be created using SstFileWriter
|
||||
|
||||
+1
-10
@@ -819,14 +819,9 @@ class Logger {
|
||||
size_t kDoNotSupportGetLogFileSize = (std::numeric_limits<size_t>::max)();
|
||||
|
||||
explicit Logger(const InfoLogLevel log_level = InfoLogLevel::INFO_LEVEL)
|
||||
: closed_(false), log_level_(log_level) {}
|
||||
: log_level_(log_level) {}
|
||||
virtual ~Logger();
|
||||
|
||||
// Close the log file. Must be called before destructor. If the return
|
||||
// status is NotSupported(), it means the implementation does cleanup in
|
||||
// the destructor
|
||||
virtual Status Close();
|
||||
|
||||
// Write a header to the log file with the specified format
|
||||
// It is recommended that you log all header information at the start of the
|
||||
// application. But it is not enforced.
|
||||
@@ -853,10 +848,6 @@ class Logger {
|
||||
log_level_ = log_level;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual Status CloseImpl();
|
||||
bool closed_;
|
||||
|
||||
private:
|
||||
// No copying allowed
|
||||
Logger(const Logger&);
|
||||
|
||||
@@ -51,7 +51,7 @@ class Iterator : public Cleanable {
|
||||
// Position at the last key in the source that at or before target
|
||||
// The iterator is Valid() after this call iff the source contains
|
||||
// an entry that comes at or before target.
|
||||
virtual void SeekForPrev(const Slice& target) = 0;
|
||||
virtual void SeekForPrev(const Slice& target) {}
|
||||
|
||||
// Moves to the next entry in the source. After this call, Valid() is
|
||||
// true iff the iterator was not positioned at the last entry in the source.
|
||||
|
||||
@@ -82,18 +82,6 @@ enum class CompactionReason {
|
||||
kBottommostFiles,
|
||||
};
|
||||
|
||||
enum class FlushReason : int {
|
||||
kUnknown = 0x00,
|
||||
kGetLiveFiles = 0x01,
|
||||
kShutDown = 0x02,
|
||||
kExternalFileIngestion = 0x03,
|
||||
kManualCompaction = 0x04,
|
||||
kWriteBufferManager = 0x05,
|
||||
kWriteBufferFull = 0x06,
|
||||
kTest = 0x07,
|
||||
kSuperVersionChange = 0x08,
|
||||
};
|
||||
|
||||
enum class BackgroundErrorReason {
|
||||
kFlush,
|
||||
kCompaction,
|
||||
@@ -155,8 +143,6 @@ struct FlushJobInfo {
|
||||
SequenceNumber largest_seqno;
|
||||
// Table properties of the table being flushed
|
||||
TableProperties table_properties;
|
||||
|
||||
FlushReason flush_reason;
|
||||
};
|
||||
|
||||
struct CompactionJobInfo {
|
||||
|
||||
@@ -83,14 +83,6 @@ class MemTableRep {
|
||||
// collection, and no concurrent modifications to the table in progress
|
||||
virtual void Insert(KeyHandle handle) = 0;
|
||||
|
||||
// Same as ::Insert
|
||||
// Returns false if MemTableRepFactory::CanHandleDuplicatedKey() is true and
|
||||
// the <key, seq> already exists.
|
||||
virtual bool InsertKey(KeyHandle handle) {
|
||||
Insert(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Same as Insert(), but in additional pass a hint to insert location for
|
||||
// the key. If hint points to nullptr, a new hint will be populated.
|
||||
// otherwise the hint will be updated to reflect the last insert location.
|
||||
@@ -102,27 +94,14 @@ class MemTableRep {
|
||||
Insert(handle);
|
||||
}
|
||||
|
||||
// Same as ::InsertWithHint
|
||||
// Returns false if MemTableRepFactory::CanHandleDuplicatedKey() is true and
|
||||
// the <key, seq> already exists.
|
||||
virtual bool InsertKeyWithHint(KeyHandle handle, void** hint) {
|
||||
InsertWithHint(handle, hint);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Like Insert(handle), but may be called concurrent with other calls
|
||||
// to InsertConcurrently for other handles.
|
||||
//
|
||||
// Returns false if MemTableRepFactory::CanHandleDuplicatedKey() is true and
|
||||
// the <key, seq> already exists.
|
||||
virtual void InsertConcurrently(KeyHandle handle);
|
||||
|
||||
// Same as ::InsertConcurrently
|
||||
// Returns false if MemTableRepFactory::CanHandleDuplicatedKey() is true and
|
||||
// the <key, seq> already exists.
|
||||
virtual bool InsertKeyConcurrently(KeyHandle handle) {
|
||||
InsertConcurrently(handle);
|
||||
return true;
|
||||
// to InsertConcurrently for other handles
|
||||
virtual void InsertConcurrently(KeyHandle handle) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
throw std::runtime_error("concurrent insert not supported");
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns true iff an entry that compares equal to key is in the collection.
|
||||
@@ -253,12 +232,6 @@ class MemTableRepFactory {
|
||||
// Return true if the current MemTableRep supports concurrent inserts
|
||||
// Default: false
|
||||
virtual bool IsInsertConcurrentlySupported() const { return false; }
|
||||
|
||||
// Return true if the current MemTableRep supports detecting duplicate
|
||||
// <key,seq> at insertion time. If true, then MemTableRep::Insert* returns
|
||||
// false when if the <key,seq> already exists.
|
||||
// Default: false
|
||||
virtual bool CanHandleDuplicatedKey() const { return false; }
|
||||
};
|
||||
|
||||
// This uses a skip list to store keys. It is the default.
|
||||
@@ -280,8 +253,6 @@ class SkipListFactory : public MemTableRepFactory {
|
||||
|
||||
bool IsInsertConcurrentlySupported() const override { return true; }
|
||||
|
||||
bool CanHandleDuplicatedKey() const override { return true; }
|
||||
|
||||
private:
|
||||
const size_t lookahead_;
|
||||
};
|
||||
|
||||
+10
-14
@@ -274,14 +274,14 @@ struct ColumnFamilyOptions : public AdvancedColumnFamilyOptions {
|
||||
enum class WALRecoveryMode : char {
|
||||
// Original levelDB recovery
|
||||
// We tolerate incomplete record in trailing data on all logs
|
||||
// Use case : This is legacy behavior
|
||||
// Use case : This is legacy behavior (default)
|
||||
kTolerateCorruptedTailRecords = 0x00,
|
||||
// Recover from clean shutdown
|
||||
// We don't expect to find any corruption in the WAL
|
||||
// Use case : This is ideal for unit tests and rare applications that
|
||||
// can require high consistency guarantee
|
||||
kAbsoluteConsistency = 0x01,
|
||||
// Recover to point-in-time consistency (default)
|
||||
// Recover to point-in-time consistency
|
||||
// We stop the WAL playback on discovering WAL inconsistency
|
||||
// Use case : Ideal for systems that have disk controller cache like
|
||||
// hard disk, SSD without super capacitor that store related data
|
||||
@@ -406,14 +406,13 @@ struct DBOptions {
|
||||
// If non-null, then we should collect metrics about database operations
|
||||
std::shared_ptr<Statistics> statistics = nullptr;
|
||||
|
||||
// By default, writes to stable storage use fdatasync (on platforms
|
||||
// where this function is available). If this option is true,
|
||||
// fsync is used instead.
|
||||
//
|
||||
// fsync and fdatasync are equally safe for our purposes and fdatasync is
|
||||
// faster, so it is rarely necessary to set this option. It is provided
|
||||
// as a workaround for kernel/filesystem bugs, such as one that affected
|
||||
// fdatasync with ext4 in kernel versions prior to 3.7.
|
||||
// If true, then every store to stable storage will issue a fsync.
|
||||
// If false, then every store to stable storage will issue a fdatasync.
|
||||
// This parameter should be set to true while storing data to
|
||||
// filesystem like ext3 that can lose files after a reboot.
|
||||
// Default: false
|
||||
// Note: on many platforms fdatasync is defined as fsync, so this parameter
|
||||
// would make no difference. Refer to fdatasync definition in this code base.
|
||||
bool use_fsync = false;
|
||||
|
||||
// A list of paths where SST files can be put into, with its target size.
|
||||
@@ -560,7 +559,7 @@ struct DBOptions {
|
||||
// then WAL_size_limit_MB, they will be deleted starting with the
|
||||
// earliest until size_limit is met. All empty files will be deleted.
|
||||
// 3. If WAL_ttl_seconds is not 0 and WAL_size_limit_MB is 0, then
|
||||
// WAL files will be checked every WAL_ttl_seconds / 2 and those that
|
||||
// WAL files will be checked every WAL_ttl_secondsi / 2 and those that
|
||||
// are older than WAL_ttl_seconds will be deleted.
|
||||
// 4. If both are not 0, WAL files will be checked every 10 min and both
|
||||
// checks will be performed with ttl being first.
|
||||
@@ -1205,9 +1204,6 @@ struct CompactRangeOptions {
|
||||
// if there is a compaction filter
|
||||
BottommostLevelCompaction bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter;
|
||||
// If true, will execute immediately even if doing so would cause the DB to
|
||||
// enter write stall mode. Otherwise, it'll sleep until load is low enough.
|
||||
bool allow_write_stall = false;
|
||||
};
|
||||
|
||||
// IngestExternalFileOptions is used by IngestExternalFile()
|
||||
|
||||
@@ -72,18 +72,16 @@ class SstFileWriter {
|
||||
SstFileWriter(const EnvOptions& env_options, const Options& options,
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool invalidate_page_cache = true,
|
||||
Env::IOPriority io_priority = Env::IOPriority::IO_TOTAL,
|
||||
bool skip_filters = false)
|
||||
Env::IOPriority io_priority = Env::IOPriority::IO_TOTAL)
|
||||
: SstFileWriter(env_options, options, options.comparator, column_family,
|
||||
invalidate_page_cache, io_priority, skip_filters) {}
|
||||
invalidate_page_cache, io_priority) {}
|
||||
|
||||
// Deprecated API
|
||||
SstFileWriter(const EnvOptions& env_options, const Options& options,
|
||||
const Comparator* user_comparator,
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool invalidate_page_cache = true,
|
||||
Env::IOPriority io_priority = Env::IOPriority::IO_TOTAL,
|
||||
bool skip_filters = false);
|
||||
Env::IOPriority io_priority = Env::IOPriority::IO_TOTAL);
|
||||
|
||||
~SstFileWriter();
|
||||
|
||||
|
||||
Loaded 100 of 226 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user