mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
96
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dc9d706be | ||
|
|
ce58659f58 | ||
|
|
7f92556ad7 | ||
|
|
1209b6db5c | ||
|
|
2ac988c67e | ||
|
|
d060421c77 | ||
|
|
bf937cf15b | ||
|
|
ec5843dca9 | ||
|
|
3ae0047278 | ||
|
|
c287c098a4 | ||
|
|
d6336563e5 | ||
|
|
ad05cbb182 | ||
|
|
7eb292da14 | ||
|
|
dfbe52e099 | ||
|
|
30649dc6a1 | ||
|
|
90eca1e616 | ||
|
|
243220d08a | ||
|
|
4624edc440 | ||
|
|
aba3409740 | ||
|
|
f4a030ce81 | ||
|
|
b092977643 | ||
|
|
828211e901 | ||
|
|
8ada876dfe | ||
|
|
e9c31ab159 | ||
|
|
1960e73e21 | ||
|
|
5263da6396 | ||
|
|
ab446dc22d | ||
|
|
c178da053b | ||
|
|
97307d888f | ||
|
|
0454f781c2 | ||
|
|
989d12313c | ||
|
|
8eb1d445c3 | ||
|
|
4e7a182d09 | ||
|
|
c88c57cde1 | ||
|
|
6a30b98fdc | ||
|
|
ba6ee1f749 | ||
|
|
b3c5351335 | ||
|
|
d08d05cb62 | ||
|
|
dadf01672a | ||
|
|
74748611a8 | ||
|
|
3c380fdffd | ||
|
|
b555ed30a4 | ||
|
|
8a04ee4fd1 | ||
|
|
ee1c802675 | ||
|
|
0a0fad447b | ||
|
|
3f1bb07351 | ||
|
|
ef29d2a234 | ||
|
|
fe228da0a9 | ||
|
|
9fc72d6f16 | ||
|
|
d62af7fa4f | ||
|
|
945f618ba5 | ||
|
|
e78715c29a | ||
|
|
a0931b3185 | ||
|
|
821e0b1683 | ||
|
|
1336a7742d | ||
|
|
8feee28020 | ||
|
|
8f8eb4f1c0 | ||
|
|
81736d8afe | ||
|
|
2f29991701 | ||
|
|
88d8b2a2f5 | ||
|
|
4b124fb9d3 | ||
|
|
c3401846ef | ||
|
|
cb5b8f2090 | ||
|
|
cd5092e168 | ||
|
|
a247617e6f | ||
|
|
6e5b341e8b | ||
|
|
e502839e25 | ||
|
|
ba8aa8fdc8 | ||
|
|
b78ed0460b | ||
|
|
813719e952 | ||
|
|
e62a763752 | ||
|
|
1edac32b77 | ||
|
|
5eccf0b9d5 | ||
|
|
6efa8e270c | ||
|
|
5400800a56 | ||
|
|
ab5ab36ac2 | ||
|
|
ec225d2e97 | ||
|
|
ab43ff58b5 | ||
|
|
77dc069eb9 | ||
|
|
f3fe6f883b | ||
|
|
4bdf06e78f | ||
|
|
3073b1c573 | ||
|
|
e6605e5302 | ||
|
|
3fe0937180 | ||
|
|
e2d4b0efb1 | ||
|
|
b8eb32f8cf | ||
|
|
9f7ccc8445 | ||
|
|
3f666f79af | ||
|
|
65cd6cd4b6 | ||
|
|
46acdc9883 | ||
|
|
439855a774 | ||
|
|
4927b4e662 | ||
|
|
7fcc1d0ddf | ||
|
|
0e6e405fec | ||
|
|
d938226af4 | ||
|
|
65d431639b |
No files matched your search
+44
@@ -1,9 +1,53 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### 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.
|
||||
* Remove CompactionEventListener.
|
||||
|
||||
### New Features
|
||||
* Avoid unnecessarily flushing in `CompactRange()` when the range specified by the user does not overlap unflushed memtables.
|
||||
* Add "rocksdb.live-sst-files-size" DB property to return total bytes of all SST files belong to the latest LSM tree.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a leak in prepared_section_completed_ where the zeroed entries would not removed from the map.
|
||||
|
||||
## 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.
|
||||
|
||||
### 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.
|
||||
|
||||
## 5.11.0 (01/08/2018)
|
||||
### Public API Change
|
||||
* Add `autoTune` and `getBytesPerSecond()` to RocksJava RateLimiter
|
||||
|
||||
### New Features
|
||||
* Add a new histogram stat called rocksdb.db.flush.micros for memtable flush.
|
||||
* Add "--use_txn" option to use transactional API in db_stress.
|
||||
* Disable onboard cache for compaction output in Windows platform.
|
||||
* Improve the performance of iterators doing long range scans by using readahead.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a stack-use-after-scope bug in ForwardIterator.
|
||||
* Fix builds on platforms including Linux, Windows, and PowerPC.
|
||||
* Fix buffer overrun in backup engine for DBs with huge number of files.
|
||||
* Fix a mislabel bug for bottom-pri compaction threads.
|
||||
* Fix DB::Flush() keep waiting after flush finish under certain condition.
|
||||
|
||||
## 5.10.0 (12/11/2017)
|
||||
|
||||
@@ -15,3 +15,4 @@ 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,9 +232,13 @@ 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
|
||||
EXEC_LDFLAGS += -fsanitize=undefined
|
||||
PLATFORM_CCFLAGS += -fsanitize=undefined -DROCKSDB_UBSAN_RUN
|
||||
PLATFORM_CXXFLAGS += -fsanitize=undefined -DROCKSDB_UBSAN_RUN
|
||||
# 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
|
||||
endif
|
||||
|
||||
ifdef ROCKSDB_VALGRIND_RUN
|
||||
@@ -523,6 +527,10 @@ 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)/')
|
||||
|
||||
Vendored
+5
@@ -14,6 +14,11 @@ 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 2015
|
||||
image: Visual Studio 2017
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 14 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 ..
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
|
||||
@@ -36,7 +36,7 @@ def parse_src_mk(repo_path):
|
||||
# get all .cc / .c files
|
||||
def get_cc_files(repo_path):
|
||||
cc_files = []
|
||||
for root, dirnames, filenames in os.walk(repo_path):
|
||||
for root, dirnames, filenames in os.walk(repo_path): # noqa: B007 T25377293 Grandfathered in
|
||||
root = root[(len(repo_path) + 1):]
|
||||
if "java" in root:
|
||||
# Skip java
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Create a tmp directory for the test to use
|
||||
TEST_DIR=$(mktemp -d /dev/shm/fbcode_rocksdb_XXXXXXX)
|
||||
# shellcheck disable=SC2068
|
||||
TEST_TMPDIR="$TEST_DIR" $@ && rm -rf "$TEST_DIR"
|
||||
@@ -13,10 +13,12 @@ error=0
|
||||
|
||||
function log {
|
||||
DATE=`date +%Y-%m-%d:%H:%M:%S`
|
||||
# shellcheck disable=SC2068
|
||||
echo $DATE $@
|
||||
}
|
||||
|
||||
function log_err {
|
||||
# shellcheck disable=SC2145
|
||||
log "ERROR: $@ Error code: $error."
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# shellcheck disable=SC2148
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/8219ec1bcedf8ad9da05e121e193364de2cc4f61/5.x/centos6-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/64d8d58e3d84f8bde7a029763d4f5baf39d0d5b9/stable/centos6-native/6aaf4de
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/ba9be983c81de7299b59fe71950c664a84dcb5f8/5.x/gcc-5-glibc-2.23/339d858
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# shellcheck disable=SC2148
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# shellcheck disable=SC1113
|
||||
#/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
@@ -28,12 +29,14 @@ function package() {
|
||||
if dpkg --get-selections | grep --quiet $1; then
|
||||
log "$1 is already installed. skipping."
|
||||
else
|
||||
# shellcheck disable=SC2068
|
||||
apt-get install $@ -y
|
||||
fi
|
||||
elif [[ $OS = "centos" ]]; then
|
||||
if rpm -qa | grep --quiet $1; then
|
||||
log "$1 is already installed. skipping."
|
||||
else
|
||||
# shellcheck disable=SC2068
|
||||
yum install $@ -y
|
||||
fi
|
||||
fi
|
||||
@@ -52,6 +55,7 @@ function gem_install() {
|
||||
if gem list | grep --quiet $1; then
|
||||
log "$1 is already installed. skipping."
|
||||
else
|
||||
# shellcheck disable=SC2068
|
||||
gem install $@
|
||||
fi
|
||||
}
|
||||
@@ -125,4 +129,5 @@ function main() {
|
||||
include $LIB_DIR
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
main $@
|
||||
@@ -352,6 +352,22 @@ 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
|
||||
#
|
||||
@@ -728,6 +744,9 @@ 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
|
||||
;;
|
||||
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/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
+3
-3
@@ -40,9 +40,9 @@ static int DecodeValue(void* v) {
|
||||
const std::string kLRU = "lru";
|
||||
const std::string kClock = "clock";
|
||||
|
||||
void dumbDeleter(const Slice& key, void* value) {}
|
||||
void dumbDeleter(const Slice& /*key*/, void* /*value*/) {}
|
||||
|
||||
void eraseDeleter(const Slice& key, void* value) {
|
||||
void eraseDeleter(const Slice& /*key*/, void* value) {
|
||||
Cache* cache = reinterpret_cast<Cache*>(value);
|
||||
cache->Erase("foo");
|
||||
}
|
||||
@@ -470,7 +470,7 @@ class Value {
|
||||
};
|
||||
|
||||
namespace {
|
||||
void deleter(const Slice& key, void* value) {
|
||||
void deleter(const Slice& /*key*/, void* value) {
|
||||
delete static_cast<Value *>(value);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Vendored
+1
-1
@@ -586,7 +586,7 @@ Status ClockCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Cache::Handle** out_handle,
|
||||
Cache::Priority priority) {
|
||||
Cache::Priority /*priority*/) {
|
||||
CleanupContext context;
|
||||
HashTable::accessor accessor;
|
||||
char* key_data = new char[key.size()];
|
||||
|
||||
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_pro_pool: whether this entry is in high-pri pool.
|
||||
// in_high_pri_pool: whether this entry is in high-pri pool.
|
||||
char flags;
|
||||
|
||||
uint32_t hash; // Hash of key(); used for fast sharding and comparisons
|
||||
|
||||
Vendored
+1
-1
@@ -53,7 +53,7 @@ Status ShardedCache::Insert(const Slice& key, void* value, size_t charge,
|
||||
->Insert(key, hash, value, charge, deleter, handle, priority);
|
||||
}
|
||||
|
||||
Cache::Handle* ShardedCache::Lookup(const Slice& key, Statistics* stats) {
|
||||
Cache::Handle* ShardedCache::Lookup(const Slice& key, Statistics* /*stats*/) {
|
||||
uint32_t hash = HashSlice(key);
|
||||
return GetShard(Shard(hash))->Lookup(key, hash);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ def display_file_coverage(per_file_coverage, total_coverage):
|
||||
header_template = \
|
||||
"%" + str(max_file_name_length) + "s\t%s\t%s"
|
||||
separator = "-" * (max_file_name_length + 10 + 20)
|
||||
print header_template % ("Filename", "Coverage", "Lines")
|
||||
print header_template % ("Filename", "Coverage", "Lines") # noqa: E999 T25377293 Grandfathered in
|
||||
print separator
|
||||
|
||||
# -- Print body
|
||||
|
||||
+6
-5
@@ -61,10 +61,10 @@ TableBuilder* NewTableBuilder(
|
||||
|
||||
Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::unique_ptr<InternalIterator> range_del_iter, FileMetaData* meta,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
const EnvOptions& env_options, TableCache* table_cache,
|
||||
InternalIterator* iter, std::unique_ptr<InternalIterator> range_del_iter,
|
||||
FileMetaData* meta, const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
@@ -131,7 +131,8 @@ 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());
|
||||
snapshots.empty() ? 0 : snapshots.back(),
|
||||
snapshot_checker);
|
||||
|
||||
CompactionIterator c_iter(
|
||||
iter, internal_comparator.user_comparator(), &merge, kMaxSequenceNumber,
|
||||
|
||||
@@ -252,7 +252,7 @@ struct rocksdb_comparator_t : public Comparator {
|
||||
// No-ops since the C binding does not support key shortening methods.
|
||||
virtual void FindShortestSeparator(std::string*,
|
||||
const Slice&) const override {}
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
@@ -367,7 +367,7 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
virtual bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
Logger* /*logger*/) const override {
|
||||
size_t operand_count = operand_list.size();
|
||||
std::vector<const char*> operand_pointers(operand_count);
|
||||
std::vector<size_t> operand_sizes(operand_count);
|
||||
@@ -2166,8 +2166,8 @@ void rocksdb_options_set_level0_stop_writes_trigger(
|
||||
opt->rep.level0_stop_writes_trigger = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_mem_compaction_level(rocksdb_options_t* opt,
|
||||
int n) {}
|
||||
void rocksdb_options_set_max_mem_compaction_level(rocksdb_options_t* /*opt*/,
|
||||
int /*n*/) {}
|
||||
|
||||
void rocksdb_options_set_wal_recovery_mode(rocksdb_options_t* opt,int mode) {
|
||||
opt->rep.wal_recovery_mode = static_cast<WALRecoveryMode>(mode);
|
||||
@@ -2231,8 +2231,8 @@ void rocksdb_options_set_manifest_preallocation_size(
|
||||
}
|
||||
|
||||
// noop
|
||||
void rocksdb_options_set_purge_redundant_kvs_while_flush(rocksdb_options_t* opt,
|
||||
unsigned char v) {}
|
||||
void rocksdb_options_set_purge_redundant_kvs_while_flush(
|
||||
rocksdb_options_t* /*opt*/, unsigned char /*v*/) {}
|
||||
|
||||
void rocksdb_options_set_use_direct_reads(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
@@ -2402,7 +2402,7 @@ void rocksdb_options_set_table_cache_numshardbits(
|
||||
}
|
||||
|
||||
void rocksdb_options_set_table_cache_remove_scan_count_limit(
|
||||
rocksdb_options_t* opt, int v) {
|
||||
rocksdb_options_t* /*opt*/, int /*v*/) {
|
||||
// this option is deprecated
|
||||
}
|
||||
|
||||
@@ -2973,7 +2973,7 @@ rocksdb_sstfilewriter_t* rocksdb_sstfilewriter_create(
|
||||
|
||||
rocksdb_sstfilewriter_t* rocksdb_sstfilewriter_create_with_comparator(
|
||||
const rocksdb_envoptions_t* env, const rocksdb_options_t* io_options,
|
||||
const rocksdb_comparator_t* comparator) {
|
||||
const rocksdb_comparator_t* /*comparator*/) {
|
||||
rocksdb_sstfilewriter_t* writer = new rocksdb_sstfilewriter_t;
|
||||
writer->rep = new SstFileWriter(env->rep, io_options->rep);
|
||||
return writer;
|
||||
|
||||
+149
-46
@@ -31,6 +31,7 @@
|
||||
#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"
|
||||
|
||||
@@ -344,12 +345,13 @@ void SuperVersionUnrefHandle(void* ptr) {
|
||||
// When latter happens, we are in ~ColumnFamilyData(), no get should happen as
|
||||
// well.
|
||||
SuperVersion* sv = static_cast<SuperVersion*>(ptr);
|
||||
if (sv->Unref()) {
|
||||
sv->db_mutex->Lock();
|
||||
sv->Cleanup();
|
||||
sv->db_mutex->Unlock();
|
||||
delete sv;
|
||||
}
|
||||
bool was_last_ref __attribute__((__unused__));
|
||||
was_last_ref = sv->Unref();
|
||||
// Thread-local SuperVersions can't outlive ColumnFamilyData::super_version_.
|
||||
// This is important because we can't do SuperVersion cleanup here.
|
||||
// That would require locking DB mutex, which would deadlock because
|
||||
// SuperVersionUnrefHandle is called with locked ThreadLocalPtr mutex.
|
||||
assert(!was_last_ref);
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -381,6 +383,7 @@ 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),
|
||||
@@ -469,7 +472,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();
|
||||
@@ -480,7 +483,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);
|
||||
}
|
||||
@@ -631,6 +634,41 @@ 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;
|
||||
@@ -640,25 +678,29 @@ 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 (imm()->NumNotFlushed() >= mutable_cf_options.max_write_buffer_number) {
|
||||
if (write_stall_condition == WriteStallCondition::kStopped &&
|
||||
write_stall_cause == WriteStallCause::kMemtableLimit) {
|
||||
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 (!mutable_cf_options.disable_auto_compactions &&
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
} else if (write_stall_condition == WriteStallCondition::kStopped &&
|
||||
write_stall_cause == WriteStallCause::kL0FileCountLimit) {
|
||||
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);
|
||||
@@ -666,28 +708,23 @@ 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 (!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) {
|
||||
} else if (write_stall_condition == WriteStallCondition::kStopped &&
|
||||
write_stall_cause == WriteStallCause::kPendingCompactionBytes) {
|
||||
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 (mutable_cf_options.max_write_buffer_number > 3 &&
|
||||
imm()->NumNotFlushed() >=
|
||||
mutable_cf_options.max_write_buffer_number - 1) {
|
||||
} else if (write_stall_condition == WriteStallCondition::kDelayed &&
|
||||
write_stall_cause == WriteStallCause::kMemtableLimit) {
|
||||
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 "
|
||||
@@ -696,10 +733,8 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number,
|
||||
write_controller->delayed_write_rate());
|
||||
} 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) {
|
||||
} else if (write_stall_condition == WriteStallCondition::kDelayed &&
|
||||
write_stall_cause == WriteStallCause::kL0FileCountLimit) {
|
||||
// L0 is the last two files from stopping.
|
||||
bool near_stop = vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger - 2;
|
||||
@@ -709,7 +744,6 @@ 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);
|
||||
@@ -719,10 +753,8 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"rate %" PRIu64,
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count(),
|
||||
write_controller->delayed_write_rate());
|
||||
} 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) {
|
||||
} else if (write_stall_condition == WriteStallCondition::kDelayed &&
|
||||
write_stall_cause == WriteStallCause::kPendingCompactionBytes) {
|
||||
// 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.
|
||||
@@ -740,7 +772,6 @@ 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 "
|
||||
@@ -748,6 +779,7 @@ 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,
|
||||
@@ -814,6 +846,10 @@ uint64_t ColumnFamilyData::GetTotalSstFilesSize() const {
|
||||
return VersionSet::GetTotalSstFilesSize(dummy_versions_);
|
||||
}
|
||||
|
||||
uint64_t ColumnFamilyData::GetLiveSstFilesSize() const {
|
||||
return current_->GetSstFilesSize();
|
||||
}
|
||||
|
||||
MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
return new MemTable(internal_comparator_, ioptions_, mutable_cf_options,
|
||||
@@ -850,6 +886,68 @@ 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;
|
||||
|
||||
@@ -868,8 +966,7 @@ Compaction* ColumnFamilyData::CompactRange(
|
||||
|
||||
SuperVersion* ColumnFamilyData::GetReferencedSuperVersion(
|
||||
InstrumentedMutex* db_mutex) {
|
||||
SuperVersion* sv = nullptr;
|
||||
sv = GetThreadLocalSuperVersion(db_mutex);
|
||||
SuperVersion* sv = GetThreadLocalSuperVersion(db_mutex);
|
||||
sv->Ref();
|
||||
if (!ReturnThreadLocalSuperVersion(sv)) {
|
||||
// This Unref() corresponds to the Ref() in GetThreadLocalSuperVersion()
|
||||
@@ -883,7 +980,6 @@ 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
|
||||
@@ -902,7 +998,7 @@ SuperVersion* ColumnFamilyData::GetThreadLocalSuperVersion(
|
||||
// should only keep kSVInUse before ReturnThreadLocalSuperVersion call
|
||||
// (if no Scrape happens).
|
||||
assert(ptr != SuperVersion::kSVInUse);
|
||||
sv = static_cast<SuperVersion*>(ptr);
|
||||
SuperVersion* sv = static_cast<SuperVersion*>(ptr);
|
||||
if (sv == SuperVersion::kSVObsolete ||
|
||||
sv->version_number != super_version_number_.load()) {
|
||||
RecordTick(ioptions_.statistics, NUMBER_SUPERVERSION_ACQUIRES);
|
||||
@@ -966,6 +1062,12 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
RecalculateWriteStallConditions(mutable_cf_options);
|
||||
|
||||
if (old_superversion != nullptr) {
|
||||
// Reset SuperVersions cached in thread local storage.
|
||||
// This should be done before old_superversion->Unref(). That's to ensure
|
||||
// that local_sv_ never holds the last reference to SuperVersion, since
|
||||
// it has no means to safely do SuperVersion cleanup.
|
||||
ResetThreadLocalSuperVersions();
|
||||
|
||||
if (old_superversion->mutable_cf_options.write_buffer_size !=
|
||||
mutable_cf_options.write_buffer_size) {
|
||||
mem_->UpdateWriteBufferSize(mutable_cf_options.write_buffer_size);
|
||||
@@ -981,9 +1083,6 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
sv_context->superversions_to_free.push_back(old_superversion);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset SuperVersions cached in thread local storage
|
||||
ResetThreadLocalSuperVersions();
|
||||
}
|
||||
|
||||
void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
@@ -995,10 +1094,12 @@ void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
continue;
|
||||
}
|
||||
auto sv = static_cast<SuperVersion*>(ptr);
|
||||
if (sv->Unref()) {
|
||||
sv->Cleanup();
|
||||
delete sv;
|
||||
}
|
||||
bool was_last_ref __attribute__((__unused__));
|
||||
was_last_ref = sv->Unref();
|
||||
// sv couldn't have been the last reference because
|
||||
// ResetThreadLocalSuperVersions() is called before
|
||||
// unref'ing super_version_.
|
||||
assert(!was_last_ref);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1060,11 +1161,13 @@ 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__)) = cfd->Unref();
|
||||
bool last_ref __attribute__((__unused__));
|
||||
last_ref = cfd->Unref();
|
||||
assert(last_ref);
|
||||
delete cfd;
|
||||
}
|
||||
bool dummy_last_ref __attribute__((__unused__)) = dummy_cfd_->Unref();
|
||||
bool dummy_last_ref __attribute__((__unused__));
|
||||
dummy_last_ref = dummy_cfd_->Unref();
|
||||
assert(dummy_last_ref);
|
||||
delete dummy_cfd_;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace rocksdb {
|
||||
|
||||
class Version;
|
||||
class VersionSet;
|
||||
class VersionStorageInfo;
|
||||
class MemTable;
|
||||
class MemTableListVersion;
|
||||
class CompactionPicker;
|
||||
@@ -202,6 +203,10 @@ 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_; }
|
||||
@@ -239,6 +244,7 @@ class ColumnFamilyData {
|
||||
void SetCurrent(Version* _current);
|
||||
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetLiveSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
void SetMemtable(MemTable* new_mem) {
|
||||
uint64_t memtable_id = last_memtable_id_.fetch_add(1) + 1;
|
||||
new_mem->SetID(memtable_id);
|
||||
@@ -269,6 +275,16 @@ 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;
|
||||
@@ -331,6 +347,17 @@ 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
|
||||
@@ -404,6 +431,8 @@ 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_;
|
||||
|
||||
@@ -1168,13 +1168,14 @@ TEST_F(ColumnFamilyTest, MemtableNotSupportSnapshot) {
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
class TestComparator : public Comparator {
|
||||
int Compare(const rocksdb::Slice& a, const rocksdb::Slice& b) const override {
|
||||
int Compare(const rocksdb::Slice& /*a*/,
|
||||
const rocksdb::Slice& /*b*/) const override {
|
||||
return 0;
|
||||
}
|
||||
const char* Name() const override { return "Test"; }
|
||||
void FindShortestSeparator(std::string* start,
|
||||
const rocksdb::Slice& limit) const override {}
|
||||
void FindShortSuccessor(std::string* key) const override {}
|
||||
void FindShortestSeparator(std::string* /*start*/,
|
||||
const rocksdb::Slice& /*limit*/) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
static TestComparator third_comparator;
|
||||
|
||||
@@ -37,8 +37,7 @@ class FlushedFileCollector : public EventListener {
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
|
||||
virtual void OnFlushCompleted(
|
||||
DB* db, const FlushJobInfo& info) override {
|
||||
virtual void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(info.file_path);
|
||||
}
|
||||
@@ -257,9 +256,9 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
class FilterWithGet : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
if (db_ == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+23
-22
@@ -32,55 +32,56 @@ class CompactedDBImpl : public DBImpl {
|
||||
override;
|
||||
|
||||
using DBImpl::Put;
|
||||
virtual Status Put(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value) override {
|
||||
virtual Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DBImpl::Merge;
|
||||
virtual Status Merge(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value) override {
|
||||
virtual Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DBImpl::Delete;
|
||||
virtual Status Delete(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key) override {
|
||||
virtual Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
virtual Status Write(const WriteOptions& options,
|
||||
WriteBatch* updates) override {
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DBImpl::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) override {
|
||||
virtual Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/,
|
||||
const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
virtual Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
virtual Status EnableFileDeletions(bool force) override {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true) override {
|
||||
uint64_t* /*manifest_file_size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DBImpl::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
virtual Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override {
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
|
||||
+12
-56
@@ -12,31 +12,6 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
CompactionEventListener::CompactionListenerValueType fromInternalValueType(
|
||||
ValueType vt) {
|
||||
switch (vt) {
|
||||
case kTypeDeletion:
|
||||
return CompactionEventListener::CompactionListenerValueType::kDelete;
|
||||
case kTypeValue:
|
||||
return CompactionEventListener::CompactionListenerValueType::kValue;
|
||||
case kTypeMerge:
|
||||
return CompactionEventListener::CompactionListenerValueType::
|
||||
kMergeOperand;
|
||||
case kTypeSingleDeletion:
|
||||
return CompactionEventListener::CompactionListenerValueType::
|
||||
kSingleDelete;
|
||||
case kTypeRangeDeletion:
|
||||
return CompactionEventListener::CompactionListenerValueType::kRangeDelete;
|
||||
case kTypeBlobIndex:
|
||||
return CompactionEventListener::CompactionListenerValueType::kBlobIndex;
|
||||
default:
|
||||
assert(false);
|
||||
return CompactionEventListener::CompactionListenerValueType::kInvalid;
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
@@ -44,7 +19,6 @@ CompactionIterator::CompactionIterator(
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
||||
CompactionEventListener* compaction_listener,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum)
|
||||
: CompactionIterator(
|
||||
@@ -53,21 +27,18 @@ CompactionIterator::CompactionIterator(
|
||||
expect_valid_internal_key, range_del_agg,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new CompactionProxy(compaction) : nullptr),
|
||||
compaction_filter, compaction_listener, shutting_down,
|
||||
preserve_deletes_seqnum) {}
|
||||
compaction_filter, shutting_down, preserve_deletes_seqnum) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber /*last_sequence*/, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
CompactionEventListener* compaction_listener,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum
|
||||
)
|
||||
const SequenceNumber preserve_deletes_seqnum)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
@@ -79,9 +50,6 @@ CompactionIterator::CompactionIterator(
|
||||
range_del_agg_(range_del_agg),
|
||||
compaction_(std::move(compaction)),
|
||||
compaction_filter_(compaction_filter),
|
||||
#ifndef ROCKSDB_LITE
|
||||
compaction_listener_(compaction_listener),
|
||||
#endif // ROCKSDB_LITE
|
||||
shutting_down_(shutting_down),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
ignore_snapshots_(false),
|
||||
@@ -293,28 +261,12 @@ void CompactionIterator::NextFromInput() {
|
||||
(snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(ikey_.sequence, kMaxSequenceNumber));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (compaction_listener_) {
|
||||
compaction_listener_->OnCompaction(compaction_->level(), ikey_.user_key,
|
||||
fromInternalValueType(ikey_.type),
|
||||
value_, ikey_.sequence, true);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Apply the compaction filter to the first committed version of the user
|
||||
// key.
|
||||
if (current_key_committed_) {
|
||||
InvokeFilterIfNeeded(&need_skip, &skip_until);
|
||||
}
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (compaction_listener_) {
|
||||
compaction_listener_->OnCompaction(compaction_->level(), ikey_.user_key,
|
||||
fromInternalValueType(ikey_.type),
|
||||
value_, ikey_.sequence, false);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Update the current key to reflect the new sequence number/type without
|
||||
// copying the user key.
|
||||
// TODO(rven): Compaction filter does not process keys in this path
|
||||
@@ -537,6 +489,11 @@ 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;
|
||||
@@ -554,10 +511,6 @@ 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();
|
||||
@@ -621,9 +574,12 @@ 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() &&
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class CompactionEventListener;
|
||||
|
||||
class CompactionIterator {
|
||||
public:
|
||||
// A wrapper around Compaction. Has a much smaller interface, only what
|
||||
@@ -32,7 +30,7 @@ class CompactionIterator {
|
||||
: compaction_(compaction) {}
|
||||
|
||||
virtual ~CompactionProxy() = default;
|
||||
virtual int level(size_t compaction_input_level = 0) const {
|
||||
virtual int level(size_t /*compaction_input_level*/ = 0) const {
|
||||
return compaction_->level();
|
||||
}
|
||||
virtual bool KeyNotExistsBeyondOutputLevel(
|
||||
@@ -69,7 +67,6 @@ class CompactionIterator {
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
CompactionEventListener* compaction_listener = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0);
|
||||
|
||||
@@ -83,7 +80,6 @@ class CompactionIterator {
|
||||
RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
CompactionEventListener* compaction_listener = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0);
|
||||
|
||||
@@ -147,9 +143,6 @@ class CompactionIterator {
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
#ifndef ROCKSDB_LITE
|
||||
CompactionEventListener* compaction_listener_;
|
||||
#endif // !ROCKSDB_LITE
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
bool bottommost_level_;
|
||||
|
||||
+436
-55
@@ -9,23 +9,25 @@
|
||||
#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 {
|
||||
|
||||
// Expects no merging attempts.
|
||||
class NoMergingMergeOp : public MergeOperator {
|
||||
public:
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* /*merge_out*/) const override {
|
||||
ADD_FAILURE();
|
||||
return false;
|
||||
}
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
bool PartialMergeMulti(const Slice& /*key*/,
|
||||
const std::deque<Slice>& /*operand_list*/,
|
||||
std::string* /*new_value*/,
|
||||
Logger* /*logger*/) const override {
|
||||
ADD_FAILURE();
|
||||
return false;
|
||||
}
|
||||
@@ -39,9 +41,9 @@ class NoMergingMergeOp : public MergeOperator {
|
||||
// Always returns Decition::kRemove.
|
||||
class StallingFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* new_value,
|
||||
std::string* skip_until) const override {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType /*type*/,
|
||||
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()) {
|
||||
@@ -72,6 +74,18 @@ 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 {
|
||||
@@ -112,7 +126,7 @@ class LoggingForwardVectorIterator : public InternalIterator {
|
||||
keys_.begin();
|
||||
}
|
||||
|
||||
virtual void SeekForPrev(const Slice& target) override { assert(false); }
|
||||
virtual void SeekForPrev(const Slice& /*target*/) override { assert(false); }
|
||||
|
||||
virtual void Next() override {
|
||||
assert(Valid());
|
||||
@@ -144,76 +158,149 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
public:
|
||||
FakeCompaction() = default;
|
||||
|
||||
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 {
|
||||
return key_not_exists_beyond_output_level;
|
||||
virtual int level(size_t /*compaction_input_level*/) const override {
|
||||
return 0;
|
||||
}
|
||||
virtual bool bottommost_level() const { return false; }
|
||||
virtual int number_levels() const { return 1; }
|
||||
virtual Slice GetLargestUserKey() const {
|
||||
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;
|
||||
}
|
||||
virtual bool bottommost_level() const override { return is_bottommost_level; }
|
||||
virtual int number_levels() const override { return 1; }
|
||||
virtual Slice GetLargestUserKey() const override {
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
virtual bool allow_ingest_behind() const { return false; }
|
||||
virtual bool allow_ingest_behind() const override { return false; }
|
||||
|
||||
virtual bool preserve_deletes() const {return false; }
|
||||
virtual bool preserve_deletes() const override { return false; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
bool is_bottommost_level = false;
|
||||
};
|
||||
|
||||
class CompactionIteratorTest : public testing::Test {
|
||||
// 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> {
|
||||
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,
|
||||
MergeOperator* merge_op = nullptr,
|
||||
CompactionFilter* filter = nullptr) {
|
||||
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) {
|
||||
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) {
|
||||
if (filter || bottommost_level) {
|
||||
compaction_proxy_ = new FakeCompaction();
|
||||
compaction_proxy_->is_bottommost_level = bottommost_level;
|
||||
compaction.reset(compaction_proxy_);
|
||||
}
|
||||
// TODO(yiwu) add a mock snapshot checker and add test for it.
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
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_));
|
||||
|
||||
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_,
|
||||
kMaxSequenceNumber, snapshot_checker, Env::Default(), false,
|
||||
range_del_agg_.get(), std::move(compaction), filter, nullptr,
|
||||
&shutting_down_));
|
||||
earliest_write_conflict_snapshot, snapshot_checker_.get(),
|
||||
Env::Default(), false, range_del_agg_.get(), std::move(compaction),
|
||||
filter, &shutting_down_));
|
||||
}
|
||||
|
||||
void AddSnapshot(SequenceNumber snapshot) { snapshots_.push_back(snapshot); }
|
||||
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());
|
||||
}
|
||||
|
||||
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_F(CompactionIteratorTest, EmptyResult) {
|
||||
TEST_P(CompactionIteratorTest, EmptyResult) {
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue)},
|
||||
{"", "val"}, {}, {}, 5);
|
||||
@@ -223,7 +310,7 @@ TEST_F(CompactionIteratorTest, EmptyResult) {
|
||||
|
||||
// If there is a corruption after a single deletion, the corrupted key should
|
||||
// be preserved.
|
||||
TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
TEST_P(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue, true),
|
||||
test::KeyStr("b", 10, kTypeValue)},
|
||||
@@ -242,7 +329,7 @@ TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
TEST_P(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValue),
|
||||
test::KeyStr("morning", 2, kTypeValue),
|
||||
test::KeyStr("night", 3, kTypeValue)},
|
||||
@@ -258,7 +345,7 @@ TEST_F(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
TEST_P(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
AddSnapshot(10);
|
||||
std::vector<std::string> ks1;
|
||||
ks1.push_back(test::KeyStr("ma", 28, kTypeRangeDeletion));
|
||||
@@ -279,11 +366,11 @@ TEST_F(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType t,
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
std::string* new_value,
|
||||
std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
@@ -354,7 +441,7 @@ TEST_F(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
test::KeyStr("j", 99, kTypeValue)},
|
||||
{"av50", "am45", "bv60", "bv40", "cv35", "dm70", "em71", "fm65", "fm30",
|
||||
"fv25", "gv90", "hv91", "im95", "jv99"},
|
||||
{}, {}, kMaxSequenceNumber, &merge_op, &filter);
|
||||
{}, {}, kMaxSequenceNumber, kMaxSequenceNumber, &merge_op, &filter);
|
||||
|
||||
// Compaction should output just "a", "e" and "h" keys.
|
||||
c_iter_->SeekToFirst();
|
||||
@@ -389,13 +476,14 @@ TEST_F(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
ASSERT_EQ(expected_actions, iter_->log);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, ShuttingDownInFilter) {
|
||||
TEST_P(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, &merge_op, &filter);
|
||||
{"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
|
||||
&merge_op, &filter);
|
||||
// Don't leave tombstones (kTypeDeletion) for filtered keys.
|
||||
compaction_proxy_->key_not_exists_beyond_output_level = true;
|
||||
|
||||
@@ -426,13 +514,14 @@ TEST_F(CompactionIteratorTest, ShuttingDownInFilter) {
|
||||
|
||||
// Same as ShuttingDownInFilter, but shutdown happens during filter call for
|
||||
// a merge operand, not for a value.
|
||||
TEST_F(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
TEST_P(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, &merge_op, &filter);
|
||||
{"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, kMaxSequenceNumber,
|
||||
&merge_op, &filter);
|
||||
compaction_proxy_->key_not_exists_beyond_output_level = true;
|
||||
|
||||
std::atomic<bool> seek_done{false};
|
||||
@@ -460,12 +549,12 @@ TEST_F(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
EXPECT_EQ(2, filter.last_seen.load());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, SingleMergeOperand) {
|
||||
TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType t,
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
std::string* new_value,
|
||||
std::string* skip_until) const override {
|
||||
std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
|
||||
@@ -516,7 +605,7 @@ TEST_F(CompactionIteratorTest, SingleMergeOperand) {
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
Logger* /*logger*/) const override {
|
||||
std::string string_key = key.ToString();
|
||||
EXPECT_TRUE(string_key == "a" || string_key == "b");
|
||||
|
||||
@@ -552,7 +641,7 @@ TEST_F(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,
|
||||
&merge_op, &filter);
|
||||
kMaxSequenceNumber, &merge_op, &filter);
|
||||
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
@@ -565,6 +654,298 @@ TEST_F(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) {
|
||||
|
||||
+4
-15
@@ -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(),
|
||||
compact_->compaction->level(), db_options_.statistics.get(),
|
||||
shutting_down_);
|
||||
snapshot_checker_, compact_->compaction->level(),
|
||||
db_options_.statistics.get(), shutting_down_);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
|
||||
@@ -762,24 +762,13 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
input->SeekToFirst();
|
||||
}
|
||||
|
||||
// we allow only 1 compaction event listener. Used by blob storage
|
||||
CompactionEventListener* comp_event_listener = nullptr;
|
||||
#ifndef ROCKSDB_LITE
|
||||
for (auto& celitr : cfd->ioptions()->listeners) {
|
||||
comp_event_listener = celitr->GetCompactionEventListener();
|
||||
if (comp_event_listener != nullptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status status;
|
||||
sub_compact->c_iter.reset(new CompactionIterator(
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, false, range_del_agg.get(),
|
||||
sub_compact->compaction, compaction_filter, comp_event_listener,
|
||||
shutting_down_, preserve_deletes_seqnum_));
|
||||
sub_compact->compaction, compaction_filter, shutting_down_,
|
||||
preserve_deletes_seqnum_));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() &&
|
||||
|
||||
@@ -426,7 +426,7 @@ class CompactionJobStatsChecker : public EventListener {
|
||||
// Once a compaction completed, this function will verify the returned
|
||||
// CompactionJobInfo with the oldest CompactionJobInfo added earlier
|
||||
// in "expected_stats_" which has not yet being used for verification.
|
||||
virtual void OnCompactionCompleted(DB *db, const CompactionJobInfo& ci) {
|
||||
virtual void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) {
|
||||
if (verify_next_comp_io_stats_) {
|
||||
ASSERT_GT(ci.stats.file_write_nanos, 0);
|
||||
ASSERT_GT(ci.stats.file_range_sync_nanos, 0);
|
||||
|
||||
@@ -199,7 +199,7 @@ void CompactionPicker::GetRange(const std::vector<CompactionInputFiles>& inputs,
|
||||
assert(initialized);
|
||||
}
|
||||
|
||||
bool CompactionPicker::ExpandInputsToCleanCut(const std::string& cf_name,
|
||||
bool CompactionPicker::ExpandInputsToCleanCut(const std::string& /*cf_name*/,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs) {
|
||||
// This isn't good compaction
|
||||
@@ -309,7 +309,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
Status CompactionPicker::GetCompactionInputsFromFileNumbers(
|
||||
std::vector<CompactionInputFiles>* input_files,
|
||||
std::unordered_set<uint64_t>* input_set, const VersionStorageInfo* vstorage,
|
||||
const CompactionOptions& compact_options) const {
|
||||
const CompactionOptions& /*compact_options*/) const {
|
||||
if (input_set->size() == 0U) {
|
||||
return Status::InvalidArgument(
|
||||
"Compaction must include at least one file.");
|
||||
@@ -1612,8 +1612,9 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
Compaction* FIFOCompactionPicker::CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t output_path_id, const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict) {
|
||||
uint32_t /*output_path_id*/, const InternalKey* /*begin*/,
|
||||
const InternalKey* /*end*/, InternalKey** compaction_end,
|
||||
bool* /*manual_conflict*/) {
|
||||
assert(input_level == 0);
|
||||
assert(output_level == 0);
|
||||
*compaction_end = nullptr;
|
||||
|
||||
+14
-12
@@ -267,27 +267,29 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
virtual ~NullCompactionPicker() {}
|
||||
|
||||
// Always return "nullptr"
|
||||
Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override {
|
||||
Compaction* PickCompaction(const std::string& /*cf_name*/,
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
VersionStorageInfo* /*vstorage*/,
|
||||
LogBuffer* /*log_buffer*/) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Always return "nullptr"
|
||||
Compaction* CompactRange(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level,
|
||||
int output_level, uint32_t output_path_id,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end,
|
||||
bool* manual_conflict) override {
|
||||
Compaction* CompactRange(const std::string& /*cf_name*/,
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
VersionStorageInfo* /*vstorage*/,
|
||||
int /*input_level*/, int /*output_level*/,
|
||||
uint32_t /*output_path_id*/,
|
||||
const InternalKey* /*begin*/,
|
||||
const InternalKey* /*end*/,
|
||||
InternalKey** /*compaction_end*/,
|
||||
bool* /*manual_conflict*/) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Always returns false.
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override {
|
||||
const VersionStorageInfo* /*vstorage*/) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,7 +20,9 @@ namespace rocksdb {
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* format, va_list ap) override { log_count++; }
|
||||
virtual void Logv(const char* /*format*/, va_list /*ap*/) override {
|
||||
log_count++;
|
||||
}
|
||||
size_t log_count;
|
||||
};
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ void UniversalCompactionPicker::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionPicker::SortedRun>
|
||||
UniversalCompactionPicker::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& /*ioptions*/,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
std::vector<UniversalCompactionPicker::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
@@ -603,9 +603,9 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
|
||||
CompactionReason compaction_reason;
|
||||
if (max_number_of_files_to_compact == UINT_MAX) {
|
||||
compaction_reason = CompactionReason::kUniversalSortedRunNum;
|
||||
} else {
|
||||
compaction_reason = CompactionReason::kUniversalSizeRatio;
|
||||
} else {
|
||||
compaction_reason = CompactionReason::kUniversalSortedRunNum;
|
||||
}
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
|
||||
@@ -188,10 +188,10 @@ class DoubleComparator : public Comparator {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override {}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
class HashComparator : public Comparator {
|
||||
@@ -211,10 +211,10 @@ class HashComparator : public Comparator {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override {}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
class TwoStrComparator : public Comparator {
|
||||
@@ -243,10 +243,10 @@ class TwoStrComparator : public Comparator {
|
||||
}
|
||||
return a2.compare(b2);
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override {}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
||||
+10
-2
@@ -19,9 +19,17 @@ void CancelAllBackgroundWork(DB* db, bool wait) {
|
||||
}
|
||||
|
||||
Status DeleteFilesInRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
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) {
|
||||
return (static_cast_with_check<DBImpl, DB>(db->GetRootDB()))
|
||||
->DeleteFilesInRange(column_family, begin, end);
|
||||
->DeleteFilesInRanges(column_family, ranges, n, include_end);
|
||||
}
|
||||
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
|
||||
+68
-10
@@ -9,6 +9,7 @@
|
||||
#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
|
||||
@@ -849,23 +850,44 @@ TEST_F(DBBasicTest, MmapAndBufferOptions) {
|
||||
|
||||
class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv(Env* base) : EnvWrapper(base) { };
|
||||
explicit TestEnv() : EnvWrapper(Env::Default()),
|
||||
close_count(0) { }
|
||||
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char *format, va_list ap) override { };
|
||||
private:
|
||||
virtual Status CloseImpl() override {
|
||||
return Status::NotSupported();
|
||||
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;
|
||||
};
|
||||
|
||||
virtual Status NewLogger(const std::string& fname,
|
||||
void CloseCountInc() { close_count++; }
|
||||
|
||||
int GetCloseCount() { return close_count; }
|
||||
|
||||
virtual Status NewLogger(const std::string& /*fname*/,
|
||||
shared_ptr<Logger>* result) {
|
||||
result->reset(new TestLogger());
|
||||
result->reset(new TestLogger(this));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
int close_count;
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTest, DBClose) {
|
||||
@@ -874,19 +896,29 @@ TEST_F(DBBasicTest, DBClose) {
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
|
||||
DB* db = nullptr;
|
||||
TestEnv *env = new TestEnv();
|
||||
options.create_if_missing = true;
|
||||
options.env = new TestEnv(Env::Default());
|
||||
options.env = env;
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
s = db->Close();
|
||||
ASSERT_EQ(s, Status::NotSupported());
|
||||
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());
|
||||
options.info_log.reset(new TestEnv::TestLogger(env));
|
||||
options.create_if_missing = false;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
@@ -895,9 +927,35 @@ TEST_F(DBBasicTest, DBClose) {
|
||||
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) {
|
||||
|
||||
@@ -47,7 +47,7 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
return options;
|
||||
}
|
||||
|
||||
void InitTable(const Options& options) {
|
||||
void InitTable(const Options& /*options*/) {
|
||||
std::string value(kValueSize, 'a');
|
||||
for (size_t i = 0; i < kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(ToString(i), value.c_str()));
|
||||
@@ -111,6 +111,31 @@ 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();
|
||||
|
||||
@@ -56,9 +56,9 @@ INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value, bool* value_changed) const
|
||||
override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
return false;
|
||||
}
|
||||
@@ -68,9 +68,9 @@ class KeepFilter : public CompactionFilter {
|
||||
|
||||
class DeleteFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value, bool* value_changed) const
|
||||
override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
return true;
|
||||
}
|
||||
@@ -80,9 +80,9 @@ class DeleteFilter : public CompactionFilter {
|
||||
|
||||
class DeleteISFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
virtual bool Filter(int /*level*/, const Slice& key, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
int i = std::stoi(key.ToString());
|
||||
if (i > 5 && i <= 105) {
|
||||
@@ -100,8 +100,10 @@ class DeleteISFilter : public CompactionFilter {
|
||||
// zero-padded to length 10.
|
||||
class SkipEvenFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType value_type,
|
||||
const Slice& existing_value, std::string* new_value,
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key,
|
||||
ValueType /*value_type*/,
|
||||
const Slice& /*existing_value*/,
|
||||
std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
cfilter_count++;
|
||||
int i = std::stoi(key.ToString());
|
||||
@@ -123,9 +125,9 @@ class SkipEvenFilter : public CompactionFilter {
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
@@ -140,9 +142,9 @@ class ConditionalFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ConditionalFilter(const std::string* filtered_value)
|
||||
: filtered_value_(filtered_value) {}
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/, const Slice& value,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return value.ToString() == *filtered_value_;
|
||||
}
|
||||
|
||||
@@ -156,9 +158,9 @@ class ChangeFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ChangeFilter() {}
|
||||
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value, bool* value_changed) const
|
||||
override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
assert(new_value != nullptr);
|
||||
*new_value = NEW_VALUE;
|
||||
*value_changed = true;
|
||||
@@ -247,7 +249,7 @@ class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
@@ -263,7 +265,7 @@ class ConditionalFilterFactory : public CompactionFilterFactory {
|
||||
: filtered_value_(filtered_value.ToString()) {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(
|
||||
new ConditionalFilter(&filtered_value_));
|
||||
}
|
||||
@@ -281,7 +283,7 @@ class ChangeFilterFactory : public CompactionFilterFactory {
|
||||
explicit ChangeFilterFactory() {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new ChangeFilter());
|
||||
}
|
||||
|
||||
|
||||
+387
-1
@@ -53,7 +53,7 @@ class FlushedFileCollector : public EventListener {
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
|
||||
virtual void OnFlushCompleted(DB* db, const FlushJobInfo& info) override {
|
||||
virtual void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(info.file_path);
|
||||
}
|
||||
@@ -1517,6 +1517,122 @@ 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
|
||||
@@ -2989,6 +3105,276 @@ 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),
|
||||
|
||||
@@ -93,7 +93,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions());
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
|
||||
+21
-1
@@ -137,7 +137,7 @@ TEST_F(DBFlushTest, ManualFlushWithMinWriteBufferNumberToMerge) {
|
||||
{{"DBImpl::BGWorkFlush",
|
||||
"DBFlushTest::ManualFlushWithMinWriteBufferNumberToMerge:1"},
|
||||
{"DBFlushTest::ManualFlushWithMinWriteBufferNumberToMerge:2",
|
||||
"DBImpl::FlushMemTableToOutputFile:BeforeInstallSV"}});
|
||||
"FlushJob::WriteLevel0Table"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("key1", "value1"));
|
||||
@@ -185,6 +185,26 @@ 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());
|
||||
|
||||
|
||||
+95
-56
@@ -258,7 +258,7 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
if (!cfd->IsDropped() && cfd->initialized() && !cfd->mem()->IsEmpty()) {
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
FlushMemTable(cfd, FlushOptions());
|
||||
FlushMemTable(cfd, FlushOptions(), FlushReason::kShutDown);
|
||||
mutex_.Lock();
|
||||
cfd->Unref();
|
||||
}
|
||||
@@ -278,7 +278,7 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::CloseImpl() {
|
||||
Status DBImpl::CloseHelper() {
|
||||
// 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,6 +287,7 @@ Status DBImpl::CloseImpl() {
|
||||
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;
|
||||
@@ -349,7 +350,18 @@ Status DBImpl::CloseImpl() {
|
||||
delete l;
|
||||
}
|
||||
for (auto& log : logs_) {
|
||||
log.ClearWriter();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
logs_.clear();
|
||||
|
||||
@@ -383,14 +395,25 @@ Status DBImpl::CloseImpl() {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Shutdown complete");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
Status s = Status::OK();
|
||||
if (immutable_db_options_.info_log && own_info_log_) {
|
||||
s = immutable_db_options_.info_log->Close();
|
||||
Status s = immutable_db_options_.info_log->Close();
|
||||
if (ret.ok()) {
|
||||
ret = s;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
return ret;
|
||||
}
|
||||
|
||||
DBImpl::~DBImpl() { Close(); }
|
||||
Status DBImpl::CloseImpl() {
|
||||
return CloseHelper();
|
||||
}
|
||||
|
||||
DBImpl::~DBImpl() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
CloseHelper();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::MaybeIgnoreError(Status* s) const {
|
||||
if (s->ok() || immutable_db_options_.paranoid_checks) {
|
||||
@@ -535,6 +558,7 @@ 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();
|
||||
@@ -651,8 +675,9 @@ Status DBImpl::SetDBOptions(
|
||||
}
|
||||
|
||||
// return the same level if it cannot be moved
|
||||
int DBImpl::FindMinimumEmptyLevelFitting(ColumnFamilyData* cfd,
|
||||
const MutableCFOptions& mutable_cf_options, int level) {
|
||||
int DBImpl::FindMinimumEmptyLevelFitting(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& /*mutable_cf_options*/,
|
||||
int level) {
|
||||
mutex_.AssertHeld();
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
int minimum_level = level;
|
||||
@@ -873,7 +898,7 @@ struct IterState {
|
||||
bool background_purge;
|
||||
};
|
||||
|
||||
static void CleanupIteratorState(void* arg1, void* arg2) {
|
||||
static void CleanupIteratorState(void* arg1, void* /*arg2*/) {
|
||||
IterState* state = reinterpret_cast<IterState*>(arg1);
|
||||
|
||||
if (state->super_version->Unref()) {
|
||||
@@ -1419,6 +1444,7 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
}
|
||||
is_snapshot_supported_ = new_is_snapshot_supported;
|
||||
}
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -2138,52 +2164,63 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::DeleteFilesInRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end) {
|
||||
Status status;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
VersionEdit edit;
|
||||
std::vector<FileMetaData*> deleted_files;
|
||||
std::set<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 (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) {
|
||||
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)) {
|
||||
continue;
|
||||
}
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.DeleteFile(i, level_file->fd.GetNumber());
|
||||
deleted_files.push_back(level_file);
|
||||
level_file->being_compacted = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (edit.GetDeletedFiles().empty()) {
|
||||
@@ -2299,31 +2336,31 @@ Status DBImpl::GetDbIdentity(std::string& identity) const {
|
||||
}
|
||||
|
||||
// Default implementation -- returns not supported status
|
||||
Status DB::CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family_name,
|
||||
ColumnFamilyHandle** handle) {
|
||||
Status DB::CreateColumnFamily(const ColumnFamilyOptions& /*cf_options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
ColumnFamilyHandle** /*handle*/) {
|
||||
return Status::NotSupported("");
|
||||
}
|
||||
|
||||
Status DB::CreateColumnFamilies(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::vector<std::string>& column_family_names,
|
||||
std::vector<ColumnFamilyHandle*>* handles) {
|
||||
const ColumnFamilyOptions& /*cf_options*/,
|
||||
const std::vector<std::string>& /*column_family_names*/,
|
||||
std::vector<ColumnFamilyHandle*>* /*handles*/) {
|
||||
return Status::NotSupported("");
|
||||
}
|
||||
|
||||
Status DB::CreateColumnFamilies(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles) {
|
||||
const std::vector<ColumnFamilyDescriptor>& /*column_families*/,
|
||||
std::vector<ColumnFamilyHandle*>* /*handles*/) {
|
||||
return Status::NotSupported("");
|
||||
}
|
||||
|
||||
Status DB::DropColumnFamily(ColumnFamilyHandle* column_family) {
|
||||
Status DB::DropColumnFamily(ColumnFamilyHandle* /*column_family*/) {
|
||||
return Status::NotSupported("");
|
||||
}
|
||||
|
||||
Status DB::DropColumnFamilies(
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) {
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_families*/) {
|
||||
return Status::NotSupported("");
|
||||
}
|
||||
|
||||
@@ -2796,12 +2833,14 @@ 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);
|
||||
status = ingestion_job.NeedsFlush(&need_flush, cfd->GetSuperVersion());
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::IngestExternalFile:NeedFlush",
|
||||
&need_flush);
|
||||
if (status.ok() && need_flush) {
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), true /* writes_stopped */);
|
||||
status = FlushMemTable(cfd, FlushOptions(),
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
+47
-26
@@ -14,7 +14,6 @@
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -247,8 +246,9 @@ class DBImpl : public DB {
|
||||
const TransactionLogIterator::ReadOptions&
|
||||
read_options = TransactionLogIterator::ReadOptions()) override;
|
||||
virtual Status DeleteFile(std::string name) override;
|
||||
Status DeleteFilesInRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end);
|
||||
Status DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end = true);
|
||||
|
||||
virtual void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData>* metadata) override;
|
||||
@@ -432,6 +432,8 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t TEST_FindMinLogContainingOutstandingPrep();
|
||||
uint64_t TEST_FindMinPrepLogReferencedByMemTable();
|
||||
size_t TEST_PreparedSectionCompletedSize();
|
||||
size_t TEST_LogsWithPrepSize();
|
||||
|
||||
int TEST_BGCompactionsAllowed() const;
|
||||
int TEST_BGFlushesAllowed() const;
|
||||
@@ -672,10 +674,14 @@ 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,
|
||||
@@ -684,10 +690,13 @@ 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,
|
||||
uint64_t* seq_used = nullptr, size_t batch_cnt = 0,
|
||||
PreReleaseCallback* pre_release_callback = nullptr);
|
||||
|
||||
uint64_t FindMinLogContainingOutstandingPrep();
|
||||
@@ -696,6 +705,9 @@ 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;
|
||||
@@ -811,7 +823,7 @@ class DBImpl : public DB {
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
bool writes_stopped = false);
|
||||
FlushReason flush_reason, bool writes_stopped = false);
|
||||
|
||||
// Wait for memtable flushed.
|
||||
// If flush_memtable_id is non-null, wait until the memtable with the ID
|
||||
@@ -873,7 +885,7 @@ class DBImpl : public DB {
|
||||
ColumnFamilyData* GetColumnFamilyDataByName(const std::string& cf_name);
|
||||
|
||||
void MaybeScheduleFlushOrCompaction();
|
||||
void SchedulePendingFlush(ColumnFamilyData* cfd);
|
||||
void SchedulePendingFlush(ColumnFamilyData* cfd, FlushReason flush_reason);
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingPurge(std::string fname, FileType type, uint64_t number,
|
||||
uint32_t path_id, int job_id);
|
||||
@@ -912,7 +924,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);
|
||||
void AddToFlushQueue(ColumnFamilyData* cfd, FlushReason flush_reason);
|
||||
ColumnFamilyData* PopFirstFromFlushQueue();
|
||||
|
||||
// helper function to call after some of the logs_ were synced
|
||||
@@ -922,8 +934,7 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
// Actual implementation of Close()
|
||||
virtual Status CloseImpl();
|
||||
Status CloseHelper();
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
@@ -955,6 +966,8 @@ class DBImpl : public DB {
|
||||
// * 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,
|
||||
@@ -992,9 +1005,11 @@ class DBImpl : public DB {
|
||||
writer = nullptr;
|
||||
return w;
|
||||
}
|
||||
void ClearWriter() {
|
||||
Status ClearWriter() {
|
||||
Status s = writer->WriteBuffer();
|
||||
delete writer;
|
||||
writer = nullptr;
|
||||
return s;
|
||||
}
|
||||
|
||||
uint64_t number;
|
||||
@@ -1284,27 +1299,33 @@ class DBImpl : public DB {
|
||||
// Indicate DB was opened successfully
|
||||
bool opened_successfully_;
|
||||
|
||||
// minimum log number still containing prepared data.
|
||||
// REQUIRES: logs_with_prep_mutex_ held
|
||||
//
|
||||
// sorted list of log numbers still containing prepared data.
|
||||
// this is used by FindObsoleteFiles to determine which
|
||||
// flushed logs we must keep around because they still
|
||||
// contain prepared data which has not been flushed or rolled back
|
||||
std::priority_queue<uint64_t, std::vector<uint64_t>, std::greater<uint64_t>>
|
||||
min_log_with_prep_;
|
||||
// contain prepared data which has not been committed or rolled back
|
||||
struct LogCnt {
|
||||
uint64_t log; // the log number
|
||||
uint64_t cnt; // number of prepared sections in the log
|
||||
};
|
||||
std::vector<LogCnt> logs_with_prep_;
|
||||
std::mutex logs_with_prep_mutex_;
|
||||
|
||||
// to be used in conjunction with min_log_with_prep_.
|
||||
// REQUIRES: prepared_section_completed_mutex_ held
|
||||
//
|
||||
// to be used in conjunction with logs_with_prep_.
|
||||
// once a transaction with data in log L is committed or rolled back
|
||||
// rather than removing the value from the heap we add that value
|
||||
// to prepared_section_completed_ which maps LOG -> instance_count
|
||||
// since a log could contain multiple prepared sections
|
||||
// rather than updating logs_with_prep_ directly we keep track of that
|
||||
// in prepared_section_completed_ which maps LOG -> instance_count. This helps
|
||||
// avoiding contention between a commit thread and the prepare threads.
|
||||
//
|
||||
// when trying to determine the minimum log still active we first
|
||||
// consult min_log_with_prep_. while that root value maps to
|
||||
// a value > 0 in prepared_section_completed_ we decrement the
|
||||
// instance_count for that log and pop the root value in
|
||||
// min_log_with_prep_. This will work the same as a min_heap
|
||||
// where we are deleteing arbitrary elements and the up heaping.
|
||||
// consult logs_with_prep_. while that root value maps to
|
||||
// an equal value in prepared_section_completed_ we erase the log from
|
||||
// both logs_with_prep_ and prepared_section_completed_.
|
||||
std::unordered_map<uint64_t, uint64_t> prepared_section_completed_;
|
||||
std::mutex prep_heap_mutex_;
|
||||
std::mutex prepared_section_completed_mutex_;
|
||||
|
||||
// Callback for compaction to check if a key is visible to a snapshot.
|
||||
// REQUIRES: mutex held
|
||||
@@ -1353,8 +1374,8 @@ class DBImpl : public DB {
|
||||
return Env::WLTH_SHORT;
|
||||
}
|
||||
|
||||
// When set, we use a seprate queue for writes that dont write to memtable. In
|
||||
// 2PC these are the writes at Prepare phase.
|
||||
// When set, we use a separate 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_;
|
||||
// Increase the sequence number after writing each batch, whether memtable is
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
mutex_.AssertHeld();
|
||||
@@ -134,7 +135,6 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforeInstallSV");
|
||||
InstallSuperVersionAndScheduleWork(cfd, &job_context->superversion_context,
|
||||
mutable_cf_options);
|
||||
if (made_progress) {
|
||||
@@ -223,6 +223,7 @@ 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);
|
||||
}
|
||||
@@ -267,6 +268,7 @@ 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);
|
||||
}
|
||||
@@ -288,10 +290,75 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
auto cfd = cfh->cfd();
|
||||
bool exclusive = options.exclusive_manual_compaction;
|
||||
|
||||
Status s = FlushMemTable(cfd, FlushOptions());
|
||||
if (!s.ok()) {
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return s;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int max_level_with_files = 0;
|
||||
@@ -797,7 +864,7 @@ int DBImpl::NumberLevels(ColumnFamilyHandle* column_family) {
|
||||
return cfh->cfd()->NumberLevels();
|
||||
}
|
||||
|
||||
int DBImpl::MaxMemCompactionLevel(ColumnFamilyHandle* column_family) {
|
||||
int DBImpl::MaxMemCompactionLevel(ColumnFamilyHandle* /*column_family*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -813,7 +880,8 @@ 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);
|
||||
Status s =
|
||||
FlushMemTable(cfh->cfd(), flush_options, FlushReason::kManualCompaction);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] Manual flush finished, status: %s\n",
|
||||
cfh->GetName().c_str(), s.ToString().c_str());
|
||||
@@ -950,7 +1018,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
|
||||
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
bool writes_stopped) {
|
||||
FlushReason flush_reason, bool writes_stopped) {
|
||||
Status s;
|
||||
uint64_t flush_memtable_id = 0;
|
||||
{
|
||||
@@ -979,7 +1047,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
cfd->imm()->FlushRequested();
|
||||
|
||||
// schedule flush
|
||||
SchedulePendingFlush(cfd);
|
||||
SchedulePendingFlush(cfd, flush_reason);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
@@ -1135,11 +1203,12 @@ ColumnFamilyData* DBImpl::PopFirstFromCompactionQueue() {
|
||||
return cfd;
|
||||
}
|
||||
|
||||
void DBImpl::AddToFlushQueue(ColumnFamilyData* cfd) {
|
||||
void DBImpl::AddToFlushQueue(ColumnFamilyData* cfd, FlushReason flush_reason) {
|
||||
assert(!cfd->pending_flush());
|
||||
cfd->Ref();
|
||||
flush_queue_.push_back(cfd);
|
||||
cfd->set_pending_flush(true);
|
||||
cfd->SetFlushReason(flush_reason);
|
||||
}
|
||||
|
||||
ColumnFamilyData* DBImpl::PopFirstFromFlushQueue() {
|
||||
@@ -1148,12 +1217,14 @@ 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) {
|
||||
void DBImpl::SchedulePendingFlush(ColumnFamilyData* cfd,
|
||||
FlushReason flush_reason) {
|
||||
if (!cfd->pending_flush() && cfd->imm()->IsFlushPending()) {
|
||||
AddToFlushQueue(cfd);
|
||||
AddToFlushQueue(cfd, flush_reason);
|
||||
++unscheduled_flushes_;
|
||||
}
|
||||
}
|
||||
@@ -1696,7 +1767,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);
|
||||
@@ -1930,7 +2001,7 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
|
||||
// Whenever we install new SuperVersion, we might need to issue new flushes or
|
||||
// compactions.
|
||||
SchedulePendingFlush(cfd);
|
||||
SchedulePendingFlush(cfd, FlushReason::kSuperVersionChange);
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
|
||||
+7
-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);
|
||||
return FlushMemTable(cfd, fo, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
@@ -186,6 +186,12 @@ uint64_t DBImpl::TEST_FindMinLogContainingOutstandingPrep() {
|
||||
return FindMinLogContainingOutstandingPrep();
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_PreparedSectionCompletedSize() {
|
||||
return prepared_section_completed_.size();
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_LogsWithPrepSize() { return logs_with_prep_.size(); }
|
||||
|
||||
uint64_t DBImpl::TEST_FindMinPrepLogReferencedByMemTable() {
|
||||
return FindMinPrepLogReferencedByMemTable();
|
||||
}
|
||||
|
||||
+42
-39
@@ -48,58 +48,61 @@ uint64_t DBImpl::FindMinPrepLogReferencedByMemTable() {
|
||||
return min_log;
|
||||
}
|
||||
|
||||
// TODO(myabandeh): Avoid using locks
|
||||
void DBImpl::MarkLogAsHavingPrepSectionFlushed(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
std::lock_guard<std::mutex> lock(prepared_section_completed_mutex_);
|
||||
auto it = prepared_section_completed_.find(log);
|
||||
assert(it != prepared_section_completed_.end());
|
||||
it->second += 1;
|
||||
if (UNLIKELY(it == prepared_section_completed_.end())) {
|
||||
prepared_section_completed_[log] = 1;
|
||||
} else {
|
||||
it->second += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(myabandeh): Avoid using locks
|
||||
void DBImpl::MarkLogAsContainingPrepSection(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
min_log_with_prep_.push(log);
|
||||
auto it = prepared_section_completed_.find(log);
|
||||
if (it == prepared_section_completed_.end()) {
|
||||
prepared_section_completed_[log] = 0;
|
||||
std::lock_guard<std::mutex> lock(logs_with_prep_mutex_);
|
||||
|
||||
auto rit = logs_with_prep_.rbegin();
|
||||
bool updated = false;
|
||||
// Most probabely the last log is the one that is being marked for
|
||||
// having a prepare section; so search from the end.
|
||||
for (; rit != logs_with_prep_.rend() && rit->log >= log; ++rit) {
|
||||
if (rit->log == log) {
|
||||
rit->cnt++;
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!updated) {
|
||||
// We are either at the start, or at a position with rit->log < log
|
||||
logs_with_prep_.insert(rit.base(), {log, 1});
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DBImpl::FindMinLogContainingOutstandingPrep() {
|
||||
|
||||
if (!allow_2pc()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
uint64_t min_log = 0;
|
||||
|
||||
// first we look in the prepared heap where we keep
|
||||
// track of transactions that have been prepared (written to WAL)
|
||||
// but not yet committed.
|
||||
while (!min_log_with_prep_.empty()) {
|
||||
min_log = min_log_with_prep_.top();
|
||||
|
||||
auto it = prepared_section_completed_.find(min_log);
|
||||
|
||||
// value was marked as 'deleted' from heap
|
||||
if (it != prepared_section_completed_.end() && it->second > 0) {
|
||||
it->second -= 1;
|
||||
min_log_with_prep_.pop();
|
||||
|
||||
// back to squere one...
|
||||
min_log = 0;
|
||||
continue;
|
||||
} else {
|
||||
// found a valid value
|
||||
break;
|
||||
std::lock_guard<std::mutex> lock(logs_with_prep_mutex_);
|
||||
auto it = logs_with_prep_.begin();
|
||||
// start with the smallest log
|
||||
for (; it != logs_with_prep_.end();) {
|
||||
auto min_log = it->log;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock2(prepared_section_completed_mutex_);
|
||||
auto completed_it = prepared_section_completed_.find(min_log);
|
||||
if (completed_it == prepared_section_completed_.end() ||
|
||||
completed_it->second < it->cnt) {
|
||||
return min_log;
|
||||
}
|
||||
assert(completed_it != prepared_section_completed_.end() &&
|
||||
completed_it->second == it->cnt);
|
||||
prepared_section_completed_.erase(completed_it);
|
||||
}
|
||||
// erase from beigning in vector is not efficient but this function is not
|
||||
// on the fast path.
|
||||
it = logs_with_prep_.erase(it);
|
||||
}
|
||||
|
||||
return min_log;
|
||||
// no such log found
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
|
||||
@@ -356,6 +356,29 @@ 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);
|
||||
|
||||
@@ -107,7 +107,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
}
|
||||
|
||||
Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
DB** dbptr, bool error_if_log_file_exist) {
|
||||
DB** dbptr, bool /*error_if_log_file_exist*/) {
|
||||
*dbptr = nullptr;
|
||||
|
||||
// Try to first open DB as fully compacted DB
|
||||
|
||||
+30
-29
@@ -36,46 +36,47 @@ class DBImplReadOnly : public DBImpl {
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
|
||||
using DBImpl::Put;
|
||||
virtual Status Put(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value) override {
|
||||
virtual Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
using DBImpl::Merge;
|
||||
virtual Status Merge(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value) override {
|
||||
virtual Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
using DBImpl::Delete;
|
||||
virtual Status Delete(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key) override {
|
||||
virtual Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
using DBImpl::SingleDelete;
|
||||
virtual Status SingleDelete(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key) override {
|
||||
virtual Status SingleDelete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
virtual Status Write(const WriteOptions& options,
|
||||
WriteBatch* updates) override {
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
using DBImpl::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) override {
|
||||
virtual Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/,
|
||||
const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DBImpl::CompactFiles;
|
||||
virtual Status CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names,
|
||||
const int output_level, const int output_path_id = -1) override {
|
||||
const CompactionOptions& /*compact_options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*input_file_names*/,
|
||||
const int /*output_level*/, const int /*output_path_id*/ = -1) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
@@ -83,18 +84,18 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
virtual Status EnableFileDeletions(bool force) override {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true) override {
|
||||
uint64_t* /*manifest_file_size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
virtual Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
@@ -105,9 +106,9 @@ class DBImplReadOnly : public DBImpl {
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override {
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
|
||||
+60
-30
@@ -64,7 +64,9 @@ 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!");
|
||||
}
|
||||
@@ -76,6 +78,7 @@ 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");
|
||||
}
|
||||
@@ -93,7 +96,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, pre_release_callback);
|
||||
log_ref, seq_used, batch_cnt, pre_release_callback);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
@@ -103,7 +106,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, pre_release_callback);
|
||||
disable_memtable, batch_cnt, pre_release_callback);
|
||||
|
||||
if (!write_options.disableWAL) {
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
@@ -122,7 +125,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_);
|
||||
true /*concurrent_memtable_writes*/, seq_per_batch_, w.batch_cnt);
|
||||
}
|
||||
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
@@ -214,7 +217,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++;
|
||||
valid_batches += writer->batch_cnt;
|
||||
if (writer->ShouldWriteToMemtable()) {
|
||||
total_count += WriteBatchInternal::Count(writer->batch);
|
||||
parallel = parallel && !writer->batch->HasMerge();
|
||||
@@ -303,7 +306,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
writer->sequence = next_sequence;
|
||||
if (seq_per_batch_) {
|
||||
next_sequence++;
|
||||
assert(writer->batch_cnt);
|
||||
next_sequence += writer->batch_cnt;
|
||||
} else if (writer->ShouldWriteToMemtable()) {
|
||||
next_sequence += WriteBatchInternal::Count(writer->batch);
|
||||
}
|
||||
@@ -323,7 +327,8 @@ 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_);
|
||||
this, true /*concurrent_memtable_writes*/, seq_per_batch_,
|
||||
w.batch_cnt);
|
||||
}
|
||||
}
|
||||
if (seq_used != nullptr) {
|
||||
@@ -515,12 +520,13 @@ 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,
|
||||
uint64_t* seq_used, size_t batch_cnt,
|
||||
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 */, pre_release_callback);
|
||||
true /* disable_memtable */, batch_cnt,
|
||||
pre_release_callback);
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
@@ -576,7 +582,15 @@ 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 = seq_per_batch_ ? write_group.size : 0 /*total_count*/;
|
||||
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;
|
||||
}
|
||||
if (!write_options.disableWAL) {
|
||||
status =
|
||||
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
@@ -591,7 +605,8 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
}
|
||||
writer->sequence = curr_seq;
|
||||
if (seq_per_batch_) {
|
||||
curr_seq++;
|
||||
assert(writer->batch_cnt);
|
||||
curr_seq += writer->batch_cnt;
|
||||
}
|
||||
// else seq advances only by memtable writes
|
||||
}
|
||||
@@ -1004,7 +1019,7 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
break;
|
||||
}
|
||||
cfd->imm()->FlushRequested();
|
||||
SchedulePendingFlush(cfd);
|
||||
SchedulePendingFlush(cfd, FlushReason::kWriteBufferManager);
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -1050,7 +1065,7 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
status = SwitchMemtable(cfd_picked, write_context);
|
||||
if (status.ok()) {
|
||||
cfd_picked->imm()->FlushRequested();
|
||||
SchedulePendingFlush(cfd_picked);
|
||||
SchedulePendingFlush(cfd_picked, FlushReason::kWriteBufferFull);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
}
|
||||
@@ -1160,7 +1175,7 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* cfd,
|
||||
void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* /*cfd*/,
|
||||
const MemTableInfo& mem_table_info) {
|
||||
if (immutable_db_options_.listeners.size() == 0U) {
|
||||
return;
|
||||
@@ -1292,6 +1307,29 @@ 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);
|
||||
@@ -1302,21 +1340,7 @@ 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
|
||||
@@ -1371,7 +1395,10 @@ 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);
|
||||
batch.Put(column_family, key, value);
|
||||
Status s = batch.Put(column_family, key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
@@ -1400,7 +1427,10 @@ Status DB::DeleteRange(const WriteOptions& opt,
|
||||
Status DB::Merge(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& value) {
|
||||
WriteBatch batch;
|
||||
batch.Merge(column_family, key, value);
|
||||
Status s = batch.Merge(column_family, key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
@@ -217,6 +217,9 @@ class DBIter final: public Iterator {
|
||||
*prop = "Iterator is not valid.";
|
||||
}
|
||||
return Status::OK();
|
||||
} else if (prop_name == "rocksdb.iterator.internal-key") {
|
||||
*prop = saved_key_.GetUserKey().ToString();
|
||||
return Status::OK();
|
||||
}
|
||||
return Status::InvalidArgument("Undentified property.");
|
||||
}
|
||||
@@ -611,10 +614,12 @@ 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;
|
||||
|
||||
+182
-102
@@ -18,14 +18,39 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBIteratorTest : public DBTestBase {
|
||||
// A dumb ReadCallback which saying every key is committed.
|
||||
class DummyReadCallback : public ReadCallback {
|
||||
bool IsCommitted(SequenceNumber /*seq*/) { return true; }
|
||||
};
|
||||
|
||||
// Test param:
|
||||
// bool: whether to pass read_callback to NewIterator().
|
||||
class DBIteratorTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBIteratorTest() : DBTestBase("/db_iterator_test") {}
|
||||
|
||||
Iterator* NewIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family = nullptr) {
|
||||
if (column_family == nullptr) {
|
||||
column_family = db_->DefaultColumnFamily();
|
||||
}
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family)->cfd();
|
||||
SequenceNumber seq = read_options.snapshot != nullptr
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: db_->GetLatestSequenceNumber();
|
||||
bool use_read_callback = GetParam();
|
||||
ReadCallback* read_callback = use_read_callback ? &read_callback_ : nullptr;
|
||||
return dbfull()->NewIteratorImpl(read_options, cfd, seq, read_callback);
|
||||
}
|
||||
|
||||
private:
|
||||
DummyReadCallback read_callback_;
|
||||
};
|
||||
|
||||
class FlushBlockEveryKeyPolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
virtual bool Update(const Slice& key, const Slice& value) override {
|
||||
virtual bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (!start_) {
|
||||
start_ = true;
|
||||
return false;
|
||||
@@ -45,34 +70,41 @@ class FlushBlockEveryKeyPolicyFactory : public FlushBlockPolicyFactory {
|
||||
}
|
||||
|
||||
FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& /*data_block_builder*/) const override {
|
||||
return new FlushBlockEveryKeyPolicy;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBIteratorTest, IteratorProperty) {
|
||||
TEST_P(DBIteratorTest, IteratorProperty) {
|
||||
// The test needs to be changed if kPersistedTier is supported in iterator.
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Put(1, "1", "2");
|
||||
Delete(1, "2");
|
||||
ReadOptions ropt;
|
||||
ropt.pin_data = false;
|
||||
{
|
||||
unique_ptr<Iterator> iter(db_->NewIterator(ropt, handles_[1]));
|
||||
unique_ptr<Iterator> iter(NewIterator(ropt, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
std::string prop_value;
|
||||
ASSERT_NOK(iter->GetProperty("non_existing.value", &prop_value));
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
|
||||
ASSERT_EQ("0", prop_value);
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
|
||||
ASSERT_EQ("1", prop_value);
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
|
||||
ASSERT_EQ("Iterator is not valid.", prop_value);
|
||||
|
||||
// Get internal key at which the iteration stopped (tombstone in this case).
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
|
||||
ASSERT_EQ("2", prop_value);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, PersistedTierOnIterator) {
|
||||
TEST_P(DBIteratorTest, PersistedTierOnIterator) {
|
||||
// The test needs to be changed if kPersistedTier is supported in iterator.
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -88,7 +120,7 @@ TEST_F(DBIteratorTest, PersistedTierOnIterator) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, NonBlockingIteration) {
|
||||
TEST_P(DBIteratorTest, NonBlockingIteration) {
|
||||
do {
|
||||
ReadOptions non_blocking_opts, regular_opts;
|
||||
Options options = CurrentOptions();
|
||||
@@ -100,7 +132,7 @@ TEST_F(DBIteratorTest, NonBlockingIteration) {
|
||||
|
||||
// scan using non-blocking iterator. We should find it because
|
||||
// it is in memtable.
|
||||
Iterator* iter = db_->NewIterator(non_blocking_opts, handles_[1]);
|
||||
Iterator* iter = NewIterator(non_blocking_opts, handles_[1]);
|
||||
int count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -117,7 +149,7 @@ TEST_F(DBIteratorTest, NonBlockingIteration) {
|
||||
// kvs. Neither does it do any IOs to storage.
|
||||
uint64_t numopen = TestGetTickerCount(options, NO_FILE_OPENS);
|
||||
uint64_t cache_added = TestGetTickerCount(options, BLOCK_CACHE_ADD);
|
||||
iter = db_->NewIterator(non_blocking_opts, handles_[1]);
|
||||
iter = NewIterator(non_blocking_opts, handles_[1]);
|
||||
count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
count++;
|
||||
@@ -134,7 +166,7 @@ TEST_F(DBIteratorTest, NonBlockingIteration) {
|
||||
// verify that we can find it via a non-blocking scan
|
||||
numopen = TestGetTickerCount(options, NO_FILE_OPENS);
|
||||
cache_added = TestGetTickerCount(options, BLOCK_CACHE_ADD);
|
||||
iter = db_->NewIterator(non_blocking_opts, handles_[1]);
|
||||
iter = NewIterator(non_blocking_opts, handles_[1]);
|
||||
count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -153,7 +185,7 @@ TEST_F(DBIteratorTest, NonBlockingIteration) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBIteratorTest, ManagedNonBlockingIteration) {
|
||||
TEST_P(DBIteratorTest, ManagedNonBlockingIteration) {
|
||||
do {
|
||||
ReadOptions non_blocking_opts, regular_opts;
|
||||
Options options = CurrentOptions();
|
||||
@@ -166,7 +198,7 @@ TEST_F(DBIteratorTest, ManagedNonBlockingIteration) {
|
||||
|
||||
// scan using non-blocking iterator. We should find it because
|
||||
// it is in memtable.
|
||||
Iterator* iter = db_->NewIterator(non_blocking_opts, handles_[1]);
|
||||
Iterator* iter = NewIterator(non_blocking_opts, handles_[1]);
|
||||
int count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -183,7 +215,7 @@ TEST_F(DBIteratorTest, ManagedNonBlockingIteration) {
|
||||
// kvs. Neither does it do any IOs to storage.
|
||||
int64_t numopen = TestGetTickerCount(options, NO_FILE_OPENS);
|
||||
int64_t cache_added = TestGetTickerCount(options, BLOCK_CACHE_ADD);
|
||||
iter = db_->NewIterator(non_blocking_opts, handles_[1]);
|
||||
iter = NewIterator(non_blocking_opts, handles_[1]);
|
||||
count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
count++;
|
||||
@@ -200,7 +232,7 @@ TEST_F(DBIteratorTest, ManagedNonBlockingIteration) {
|
||||
// verify that we can find it via a non-blocking scan
|
||||
numopen = TestGetTickerCount(options, NO_FILE_OPENS);
|
||||
cache_added = TestGetTickerCount(options, BLOCK_CACHE_ADD);
|
||||
iter = db_->NewIterator(non_blocking_opts, handles_[1]);
|
||||
iter = NewIterator(non_blocking_opts, handles_[1]);
|
||||
count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -219,7 +251,7 @@ TEST_F(DBIteratorTest, ManagedNonBlockingIteration) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBIteratorTest, IterSeekBeforePrev) {
|
||||
TEST_P(DBIteratorTest, IterSeekBeforePrev) {
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
@@ -227,7 +259,7 @@ TEST_F(DBIteratorTest, IterSeekBeforePrev) {
|
||||
ASSERT_OK(Put("1", "h"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("2", "j"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
auto iter = NewIterator(ReadOptions());
|
||||
iter->Seek(Slice("c"));
|
||||
iter->Prev();
|
||||
iter->Seek(Slice("a"));
|
||||
@@ -235,7 +267,7 @@ TEST_F(DBIteratorTest, IterSeekBeforePrev) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterSeekForPrevBeforeNext) {
|
||||
TEST_P(DBIteratorTest, IterSeekForPrevBeforeNext) {
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
@@ -243,7 +275,7 @@ TEST_F(DBIteratorTest, IterSeekForPrevBeforeNext) {
|
||||
ASSERT_OK(Put("1", "h"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("2", "j"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
auto iter = NewIterator(ReadOptions());
|
||||
iter->SeekForPrev(Slice("0"));
|
||||
iter->Next();
|
||||
iter->SeekForPrev(Slice("1"));
|
||||
@@ -257,7 +289,7 @@ std::string MakeLongKey(size_t length, char c) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_F(DBIteratorTest, IterLongKeys) {
|
||||
TEST_P(DBIteratorTest, IterLongKeys) {
|
||||
ASSERT_OK(Put(MakeLongKey(20, 0), "0"));
|
||||
ASSERT_OK(Put(MakeLongKey(32, 2), "2"));
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
@@ -265,7 +297,7 @@ TEST_F(DBIteratorTest, IterLongKeys) {
|
||||
ASSERT_OK(Put(MakeLongKey(50, 1), "1"));
|
||||
ASSERT_OK(Put(MakeLongKey(127, 3), "3"));
|
||||
ASSERT_OK(Put(MakeLongKey(64, 4), "4"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
auto iter = NewIterator(ReadOptions());
|
||||
|
||||
// Create a key that needs to be skipped for Seq too new
|
||||
iter->Seek(MakeLongKey(20, 0));
|
||||
@@ -287,7 +319,7 @@ TEST_F(DBIteratorTest, IterLongKeys) {
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(50, 1) + "->1");
|
||||
delete iter;
|
||||
|
||||
iter = db_->NewIterator(ReadOptions());
|
||||
iter = NewIterator(ReadOptions());
|
||||
iter->Seek(MakeLongKey(50, 1));
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(50, 1) + "->1");
|
||||
iter->Next();
|
||||
@@ -297,13 +329,13 @@ TEST_F(DBIteratorTest, IterLongKeys) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterNextWithNewerSeq) {
|
||||
TEST_P(DBIteratorTest, IterNextWithNewerSeq) {
|
||||
ASSERT_OK(Put("0", "0"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
ASSERT_OK(Put("d", "e"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
auto iter = NewIterator(ReadOptions());
|
||||
|
||||
// Create a key that needs to be skipped for Seq too new
|
||||
for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;
|
||||
@@ -323,13 +355,13 @@ TEST_F(DBIteratorTest, IterNextWithNewerSeq) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterPrevWithNewerSeq) {
|
||||
TEST_P(DBIteratorTest, IterPrevWithNewerSeq) {
|
||||
ASSERT_OK(Put("0", "0"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
ASSERT_OK(Put("d", "e"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
auto iter = NewIterator(ReadOptions());
|
||||
|
||||
// Create a key that needs to be skipped for Seq too new
|
||||
for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;
|
||||
@@ -354,14 +386,14 @@ TEST_F(DBIteratorTest, IterPrevWithNewerSeq) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterPrevWithNewerSeq2) {
|
||||
TEST_P(DBIteratorTest, IterPrevWithNewerSeq2) {
|
||||
ASSERT_OK(Put("0", "0"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
ASSERT_OK(Put("e", "f"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
auto iter2 = db_->NewIterator(ReadOptions());
|
||||
auto iter = NewIterator(ReadOptions());
|
||||
auto iter2 = NewIterator(ReadOptions());
|
||||
iter->Seek(Slice("c"));
|
||||
iter2->SeekForPrev(Slice("d"));
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
@@ -383,10 +415,10 @@ TEST_F(DBIteratorTest, IterPrevWithNewerSeq2) {
|
||||
delete iter2;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterEmpty) {
|
||||
TEST_P(DBIteratorTest, IterEmpty) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
Iterator* iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
Iterator* iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
@@ -404,11 +436,11 @@ TEST_F(DBIteratorTest, IterEmpty) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterSingle) {
|
||||
TEST_P(DBIteratorTest, IterSingle) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
ASSERT_OK(Put(1, "a", "va"));
|
||||
Iterator* iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
Iterator* iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
@@ -455,13 +487,13 @@ TEST_F(DBIteratorTest, IterSingle) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterMulti) {
|
||||
TEST_P(DBIteratorTest, IterMulti) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
ASSERT_OK(Put(1, "a", "va"));
|
||||
ASSERT_OK(Put(1, "b", "vb"));
|
||||
ASSERT_OK(Put(1, "c", "vc"));
|
||||
Iterator* iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
Iterator* iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
@@ -554,7 +586,7 @@ TEST_F(DBIteratorTest, IterMulti) {
|
||||
|
||||
// Check that we can skip over a run of user keys
|
||||
// by using reseek rather than sequential scan
|
||||
TEST_F(DBIteratorTest, IterReseek) {
|
||||
TEST_P(DBIteratorTest, IterReseek) {
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
Options options = CurrentOptions(options_override);
|
||||
@@ -571,7 +603,7 @@ TEST_F(DBIteratorTest, IterReseek) {
|
||||
ASSERT_OK(Put(1, "a", "one"));
|
||||
ASSERT_OK(Put(1, "a", "two"));
|
||||
ASSERT_OK(Put(1, "b", "bone"));
|
||||
Iterator* iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
Iterator* iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 0);
|
||||
ASSERT_EQ(IterStatus(iter), "a->two");
|
||||
@@ -583,7 +615,7 @@ TEST_F(DBIteratorTest, IterReseek) {
|
||||
// insert a total of three keys with same userkey and verify
|
||||
// that reseek is still not invoked.
|
||||
ASSERT_OK(Put(1, "a", "three"));
|
||||
iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->three");
|
||||
iter->Next();
|
||||
@@ -594,7 +626,7 @@ TEST_F(DBIteratorTest, IterReseek) {
|
||||
// insert a total of four keys with same userkey and verify
|
||||
// that reseek is invoked.
|
||||
ASSERT_OK(Put(1, "a", "four"));
|
||||
iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->four");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 0);
|
||||
@@ -611,7 +643,7 @@ TEST_F(DBIteratorTest, IterReseek) {
|
||||
|
||||
// Insert another version of b and assert that reseek is not invoked
|
||||
ASSERT_OK(Put(1, "b", "btwo"));
|
||||
iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "b->btwo");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION),
|
||||
@@ -626,7 +658,7 @@ TEST_F(DBIteratorTest, IterReseek) {
|
||||
// of b and 4 versions of a.
|
||||
ASSERT_OK(Put(1, "b", "bthree"));
|
||||
ASSERT_OK(Put(1, "b", "bfour"));
|
||||
iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "b->bfour");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION),
|
||||
@@ -640,7 +672,7 @@ TEST_F(DBIteratorTest, IterReseek) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterSmallAndLargeMix) {
|
||||
TEST_P(DBIteratorTest, IterSmallAndLargeMix) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
ASSERT_OK(Put(1, "a", "va"));
|
||||
@@ -649,7 +681,7 @@ TEST_F(DBIteratorTest, IterSmallAndLargeMix) {
|
||||
ASSERT_OK(Put(1, "d", std::string(100000, 'd')));
|
||||
ASSERT_OK(Put(1, "e", std::string(100000, 'e')));
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
Iterator* iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
@@ -681,7 +713,7 @@ TEST_F(DBIteratorTest, IterSmallAndLargeMix) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterMultiWithDelete) {
|
||||
TEST_P(DBIteratorTest, IterMultiWithDelete) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
ASSERT_OK(Put(1, "ka", "va"));
|
||||
@@ -690,7 +722,7 @@ TEST_F(DBIteratorTest, IterMultiWithDelete) {
|
||||
ASSERT_OK(Delete(1, "kb"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "kb"));
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
Iterator* iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
iter->Seek("kc");
|
||||
ASSERT_EQ(IterStatus(iter), "kc->vc");
|
||||
if (!CurrentOptions().merge_operator) {
|
||||
@@ -707,7 +739,7 @@ TEST_F(DBIteratorTest, IterMultiWithDelete) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterPrevMaxSkip) {
|
||||
TEST_P(DBIteratorTest, IterPrevMaxSkip) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
for (int i = 0; i < 2; i++) {
|
||||
@@ -737,7 +769,7 @@ TEST_F(DBIteratorTest, IterPrevMaxSkip) {
|
||||
} while (ChangeOptions(kSkipMergePut | kSkipNoSeekToLast));
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
TEST_P(DBIteratorTest, IterWithSnapshot) {
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
do {
|
||||
@@ -751,7 +783,7 @@ TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ReadOptions options;
|
||||
options.snapshot = snapshot;
|
||||
Iterator* iter = db_->NewIterator(options, handles_[1]);
|
||||
Iterator* iter = NewIterator(options, handles_[1]);
|
||||
|
||||
ASSERT_OK(Put(1, "key0", "val0"));
|
||||
// Put more values after the snapshot
|
||||
@@ -804,13 +836,13 @@ TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
} while (ChangeOptions(kSkipHashCuckoo));
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IteratorPinsRef) {
|
||||
TEST_P(DBIteratorTest, IteratorPinsRef) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
Put(1, "foo", "hello");
|
||||
|
||||
// Get iterator that will yield the current contents of the DB.
|
||||
Iterator* iter = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
Iterator* iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
|
||||
// Write to force compactions
|
||||
Put(1, "foo", "newvalue1");
|
||||
@@ -830,7 +862,7 @@ TEST_F(DBIteratorTest, IteratorPinsRef) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
TEST_P(DBIteratorTest, DBIteratorBoundTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -847,7 +879,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = nullptr;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ro));
|
||||
|
||||
iter->Seek("foo");
|
||||
|
||||
@@ -884,7 +916,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
Slice prefix("foo2");
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ro));
|
||||
|
||||
iter->Seek("foo");
|
||||
|
||||
@@ -906,7 +938,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
Slice prefix("foo");
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ro));
|
||||
|
||||
iter->SeekToLast();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -930,7 +962,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
Slice upper_bound("g");
|
||||
ro.iterate_upper_bound = &upper_bound;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ro));
|
||||
|
||||
iter->Seek("foo");
|
||||
|
||||
@@ -963,7 +995,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = nullptr;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ro));
|
||||
|
||||
iter->Seek("b");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -983,7 +1015,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
Slice prefix("c");
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
iter.reset(db_->NewIterator(ro));
|
||||
iter.reset(NewIterator(ro));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
|
||||
@@ -1004,7 +1036,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
TEST_P(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
int upper_bound_hits = 0;
|
||||
Options options = CurrentOptions();
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
@@ -1032,7 +1064,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &ub;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ro));
|
||||
|
||||
iter->Seek("foo");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -1050,7 +1082,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
}
|
||||
// TODO(3.13): fix the issue of Seek() + Prev() which might not necessary
|
||||
// return the biggest key which is smaller than the seek key.
|
||||
TEST_F(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
TEST_P(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
@@ -1063,7 +1095,7 @@ TEST_F(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
db_->Merge(wopts, "2", "data2");
|
||||
db_->Merge(wopts, "3", "data3");
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
std::unique_ptr<Iterator> it(NewIterator(ReadOptions()));
|
||||
|
||||
it->Seek("2");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
@@ -1159,7 +1191,7 @@ class DBIteratorTestForPinnedData : public DBIteratorTest {
|
||||
|
||||
ReadOptions ro;
|
||||
ro.pin_data = true;
|
||||
auto iter = db_->NewIterator(ro);
|
||||
auto iter = NewIterator(ro);
|
||||
|
||||
{
|
||||
// Test Seek to random keys
|
||||
@@ -1251,25 +1283,25 @@ class DBIteratorTestForPinnedData : public DBIteratorTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedNormal) {
|
||||
TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedNormal) {
|
||||
PinnedDataIteratorRandomized(TestConfig::NORMAL);
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedCLoseAndOpen) {
|
||||
TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedCLoseAndOpen) {
|
||||
PinnedDataIteratorRandomized(TestConfig::CLOSE_AND_OPEN);
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTestForPinnedData,
|
||||
TEST_P(DBIteratorTestForPinnedData,
|
||||
PinnedDataIteratorRandomizedCompactBeforeRead) {
|
||||
PinnedDataIteratorRandomized(TestConfig::COMPACT_BEFORE_READ);
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedFlush) {
|
||||
TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedFlush) {
|
||||
PinnedDataIteratorRandomized(TestConfig::FLUSH_EVERY_1000);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBIteratorTest, PinnedDataIteratorMultipleFiles) {
|
||||
TEST_P(DBIteratorTest, PinnedDataIteratorMultipleFiles) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.use_delta_encoding = false;
|
||||
@@ -1318,7 +1350,7 @@ TEST_F(DBIteratorTest, PinnedDataIteratorMultipleFiles) {
|
||||
|
||||
ReadOptions ro;
|
||||
ro.pin_data = true;
|
||||
auto iter = db_->NewIterator(ro);
|
||||
auto iter = NewIterator(ro);
|
||||
|
||||
std::vector<std::pair<Slice, std::string>> results;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
@@ -1340,7 +1372,7 @@ TEST_F(DBIteratorTest, PinnedDataIteratorMultipleFiles) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(DBIteratorTest, PinnedDataIteratorMergeOperator) {
|
||||
TEST_P(DBIteratorTest, PinnedDataIteratorMergeOperator) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.use_delta_encoding = false;
|
||||
@@ -1373,7 +1405,7 @@ TEST_F(DBIteratorTest, PinnedDataIteratorMergeOperator) {
|
||||
|
||||
ReadOptions ro;
|
||||
ro.pin_data = true;
|
||||
auto iter = db_->NewIterator(ro);
|
||||
auto iter = NewIterator(ro);
|
||||
|
||||
std::vector<std::pair<Slice, std::string>> results;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
@@ -1400,7 +1432,7 @@ TEST_F(DBIteratorTest, PinnedDataIteratorMergeOperator) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, PinnedDataIteratorReadAfterUpdate) {
|
||||
TEST_P(DBIteratorTest, PinnedDataIteratorReadAfterUpdate) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.use_delta_encoding = false;
|
||||
@@ -1420,7 +1452,7 @@ TEST_F(DBIteratorTest, PinnedDataIteratorReadAfterUpdate) {
|
||||
|
||||
ReadOptions ro;
|
||||
ro.pin_data = true;
|
||||
auto iter = db_->NewIterator(ro);
|
||||
auto iter = NewIterator(ro);
|
||||
|
||||
// Delete 50% of the keys and update the other 50%
|
||||
for (auto& kv : true_data) {
|
||||
@@ -1450,7 +1482,7 @@ TEST_F(DBIteratorTest, PinnedDataIteratorReadAfterUpdate) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterSeekForPrevCrossingFiles) {
|
||||
TEST_P(DBIteratorTest, IterSeekForPrevCrossingFiles) {
|
||||
Options options = CurrentOptions();
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -1479,7 +1511,7 @@ TEST_F(DBIteratorTest, IterSeekForPrevCrossingFiles) {
|
||||
MoveFilesToLevel(1);
|
||||
{
|
||||
ReadOptions ro;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
Iterator* iter = NewIterator(ro);
|
||||
|
||||
iter->SeekForPrev("a4");
|
||||
ASSERT_EQ(iter->key().ToString(), "a3");
|
||||
@@ -1497,14 +1529,14 @@ TEST_F(DBIteratorTest, IterSeekForPrevCrossingFiles) {
|
||||
{
|
||||
ReadOptions ro;
|
||||
ro.prefix_same_as_start = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
Iterator* iter = NewIterator(ro);
|
||||
iter->SeekForPrev("c2");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocks) {
|
||||
TEST_P(DBIteratorTest, IterPrevKeyCrossingBlocks) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1; // every block will contain one entry
|
||||
@@ -1546,7 +1578,7 @@ TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocks) {
|
||||
{
|
||||
ReadOptions ro;
|
||||
ro.fill_cache = false;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
Iterator* iter = NewIterator(ro);
|
||||
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(iter->key().ToString(), "key5");
|
||||
@@ -1572,7 +1604,7 @@ TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocks) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocksRandomized) {
|
||||
TEST_P(DBIteratorTest, IterPrevKeyCrossingBlocksRandomized) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -1648,7 +1680,7 @@ TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocksRandomized) {
|
||||
{
|
||||
ReadOptions ro;
|
||||
ro.fill_cache = false;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
Iterator* iter = NewIterator(ro);
|
||||
auto data_iter = true_data.rbegin();
|
||||
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
@@ -1664,7 +1696,7 @@ TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocksRandomized) {
|
||||
{
|
||||
ReadOptions ro;
|
||||
ro.fill_cache = false;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
Iterator* iter = NewIterator(ro);
|
||||
auto data_iter = true_data.rbegin();
|
||||
|
||||
int entries_right = 0;
|
||||
@@ -1719,7 +1751,7 @@ TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocksRandomized) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
TEST_P(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
@@ -1740,7 +1772,7 @@ TEST_F(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
std::function<void()> reader_func_next = [&]() {
|
||||
SetPerfLevel(kEnableCount);
|
||||
get_perf_context()->Reset();
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
Iterator* iter = NewIterator(ReadOptions());
|
||||
|
||||
iter->SeekToFirst();
|
||||
// Seek will bump ITER_BYTES_READ
|
||||
@@ -1767,7 +1799,7 @@ TEST_F(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
|
||||
std::function<void()> reader_func_prev = [&]() {
|
||||
SetPerfLevel(kEnableCount);
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
Iterator* iter = NewIterator(ReadOptions());
|
||||
|
||||
iter->SeekToLast();
|
||||
// Seek will bump ITER_BYTES_READ
|
||||
@@ -1813,7 +1845,7 @@ TEST_F(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, ReadAhead) {
|
||||
TEST_P(DBIteratorTest, ReadAhead) {
|
||||
Options options;
|
||||
env_->count_random_reads_ = true;
|
||||
options.env = env_;
|
||||
@@ -1850,7 +1882,7 @@ TEST_F(DBIteratorTest, ReadAhead) {
|
||||
env_->random_read_bytes_counter_ = 0;
|
||||
options.statistics->setTickerCount(NO_FILE_OPENS, 0);
|
||||
ReadOptions read_options;
|
||||
auto* iter = db_->NewIterator(read_options);
|
||||
auto* iter = NewIterator(read_options);
|
||||
iter->SeekToFirst();
|
||||
int64_t num_file_opens = TestGetTickerCount(options, NO_FILE_OPENS);
|
||||
size_t bytes_read = env_->random_read_bytes_counter_;
|
||||
@@ -1859,7 +1891,7 @@ TEST_F(DBIteratorTest, ReadAhead) {
|
||||
env_->random_read_bytes_counter_ = 0;
|
||||
options.statistics->setTickerCount(NO_FILE_OPENS, 0);
|
||||
read_options.readahead_size = 1024 * 10;
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter = NewIterator(read_options);
|
||||
iter->SeekToFirst();
|
||||
int64_t num_file_opens_readahead = TestGetTickerCount(options, NO_FILE_OPENS);
|
||||
size_t bytes_read_readahead = env_->random_read_bytes_counter_;
|
||||
@@ -1869,7 +1901,7 @@ TEST_F(DBIteratorTest, ReadAhead) {
|
||||
ASSERT_GT(bytes_read_readahead, read_options.readahead_size * 3);
|
||||
|
||||
// Verify correctness.
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter = NewIterator(read_options);
|
||||
int count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(value, iter->value());
|
||||
@@ -1886,7 +1918,7 @@ TEST_F(DBIteratorTest, ReadAhead) {
|
||||
// Insert a key, create a snapshot iterator, overwrite key lots of times,
|
||||
// seek to a smaller key. Expect DBIter to fall back to a seek instead of
|
||||
// going through all the overwrites linearly.
|
||||
TEST_F(DBIteratorTest, DBIteratorSkipRecentDuplicatesTest) {
|
||||
TEST_P(DBIteratorTest, DBIteratorSkipRecentDuplicatesTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -1901,7 +1933,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipRecentDuplicatesTest) {
|
||||
|
||||
// Create iterator.
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ro));
|
||||
|
||||
// Insert a lot.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
@@ -1939,10 +1971,10 @@ TEST_F(DBIteratorTest, DBIteratorSkipRecentDuplicatesTest) {
|
||||
NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, Refresh) {
|
||||
TEST_P(DBIteratorTest, Refresh) {
|
||||
ASSERT_OK(Put("x", "y"));
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ReadOptions()));
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("x")), 0);
|
||||
@@ -1998,20 +2030,20 @@ TEST_F(DBIteratorTest, Refresh) {
|
||||
iter.reset();
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, CreationFailure) {
|
||||
TEST_P(DBIteratorTest, CreationFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::NewInternalIterator:StatusCallback", [](void* arg) {
|
||||
*(reinterpret_cast<Status*>(arg)) = Status::Corruption("test status");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
Iterator* iter = NewIterator(ReadOptions());
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, TableFilter) {
|
||||
TEST_P(DBIteratorTest, TableFilter) {
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("b", "2"));
|
||||
@@ -2036,7 +2068,7 @@ TEST_F(DBIteratorTest, TableFilter) {
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto iter = db_->NewIterator(opts);
|
||||
auto iter = NewIterator(opts);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->1");
|
||||
iter->Next();
|
||||
@@ -2062,7 +2094,7 @@ TEST_F(DBIteratorTest, TableFilter) {
|
||||
opts.table_filter = [](const TableProperties& props) {
|
||||
return props.num_entries != 2;
|
||||
};
|
||||
auto iter = db_->NewIterator(opts);
|
||||
auto iter = NewIterator(opts);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->1");
|
||||
iter->Next();
|
||||
@@ -2077,7 +2109,7 @@ TEST_F(DBIteratorTest, TableFilter) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, SkipStatistics) {
|
||||
TEST_P(DBIteratorTest, SkipStatistics) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
@@ -2099,7 +2131,7 @@ TEST_F(DBIteratorTest, SkipStatistics) {
|
||||
ASSERT_OK(Delete("e"));
|
||||
ASSERT_OK(Delete("f"));
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
Iterator* iter = NewIterator(ReadOptions());
|
||||
int count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -2110,7 +2142,7 @@ TEST_F(DBIteratorTest, SkipStatistics) {
|
||||
skip_count += 8; // 3 deletes + 3 original keys + 2 lower in sequence
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
|
||||
iter = db_->NewIterator(ReadOptions());
|
||||
iter = NewIterator(ReadOptions());
|
||||
count = 0;
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -2134,7 +2166,7 @@ TEST_F(DBIteratorTest, SkipStatistics) {
|
||||
Slice prefix("b");
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
iter = db_->NewIterator(ro);
|
||||
iter = NewIterator(ro);
|
||||
count = 0;
|
||||
for(iter->Seek("aa"); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -2145,7 +2177,7 @@ TEST_F(DBIteratorTest, SkipStatistics) {
|
||||
skip_count += 6; // 3 deletes + 3 original keys
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
|
||||
iter = db_->NewIterator(ro);
|
||||
iter = NewIterator(ro);
|
||||
count = 0;
|
||||
for(iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -2158,7 +2190,55 @@ TEST_F(DBIteratorTest, SkipStatistics) {
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, ReadCallback) {
|
||||
TEST_P(DBIteratorTest, SeekAfterHittingManyInternalKeys) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ReadOptions ropts;
|
||||
ropts.max_skippable_internal_keys = 2;
|
||||
|
||||
Put("1", "val_1");
|
||||
// Add more tombstones than max_skippable_internal_keys so that Next() fails.
|
||||
Delete("2");
|
||||
Delete("3");
|
||||
Delete("4");
|
||||
Delete("5");
|
||||
Put("6", "val_6");
|
||||
|
||||
unique_ptr<Iterator> iter(NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "1");
|
||||
ASSERT_EQ(iter->value().ToString(), "val_1");
|
||||
|
||||
// This should fail as incomplete due to too many non-visible internal keys on
|
||||
// the way to the next valid user key.
|
||||
iter->Next();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsIncomplete());
|
||||
|
||||
// Get the internal key at which Next() failed.
|
||||
std::string prop_value;
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
|
||||
ASSERT_EQ("4", prop_value);
|
||||
|
||||
// Create a new iterator to seek to the internal key.
|
||||
unique_ptr<Iterator> iter2(NewIterator(ropts));
|
||||
iter2->Seek(prop_value);
|
||||
ASSERT_TRUE(iter2->Valid());
|
||||
ASSERT_OK(iter2->status());
|
||||
|
||||
ASSERT_EQ(iter2->key().ToString(), "6");
|
||||
ASSERT_EQ(iter2->value().ToString(), "val_6");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBIteratorTestInstance, DBIteratorTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
// Tests how DBIter work with ReadCallback
|
||||
class DBIteratorWithReadCallbackTest : public DBIteratorTest {};
|
||||
|
||||
TEST_F(DBIteratorWithReadCallbackTest, ReadCallback) {
|
||||
class TestReadCallback : public ReadCallback {
|
||||
public:
|
||||
explicit TestReadCallback(SequenceNumber last_visible_seq)
|
||||
|
||||
+81
-5
@@ -28,13 +28,11 @@ class MockMemTableRep : public MemTableRep {
|
||||
return rep_->Allocate(len, buf);
|
||||
}
|
||||
|
||||
virtual void Insert(KeyHandle handle) override {
|
||||
return rep_->Insert(handle);
|
||||
}
|
||||
virtual void Insert(KeyHandle handle) override { rep_->Insert(handle); }
|
||||
|
||||
virtual void InsertWithHint(KeyHandle handle, void** hint) override {
|
||||
num_insert_with_hint_++;
|
||||
ASSERT_NE(nullptr, hint);
|
||||
EXPECT_NE(nullptr, hint);
|
||||
last_hint_in_ = *hint;
|
||||
rep_->InsertWithHint(handle, hint);
|
||||
last_hint_out_ = *hint;
|
||||
@@ -121,7 +119,7 @@ class TestPrefixExtractor : public SliceTransform {
|
||||
return separator(key) != nullptr;
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& key) const override { return false; }
|
||||
virtual bool InRange(const Slice& /*key*/) const override { return false; }
|
||||
|
||||
private:
|
||||
const char* separator(const Slice& key) const {
|
||||
@@ -129,6 +127,84 @@ 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,10 +14,43 @@
|
||||
|
||||
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) {
|
||||
@@ -449,8 +482,152 @@ 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) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/perf_level.h"
|
||||
@@ -985,8 +986,9 @@ class CountingUserTblPropCollector : public TablePropertiesCollector {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status AddUserKey(const Slice& user_key, const Slice& value, EntryType type,
|
||||
SequenceNumber seq, uint64_t file_size) override {
|
||||
Status AddUserKey(const Slice& /*user_key*/, const Slice& /*value*/,
|
||||
EntryType /*type*/, SequenceNumber /*seq*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
++count_;
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -1027,8 +1029,9 @@ class CountingDeleteTabPropCollector : public TablePropertiesCollector {
|
||||
public:
|
||||
const char* Name() const override { return "CountingDeleteTabPropCollector"; }
|
||||
|
||||
Status AddUserKey(const Slice& user_key, const Slice& value, EntryType type,
|
||||
SequenceNumber seq, uint64_t file_size) override {
|
||||
Status AddUserKey(const Slice& /*user_key*/, const Slice& /*value*/,
|
||||
EntryType type, SequenceNumber /*seq*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
if (type == kEntryDelete) {
|
||||
num_deletes_++;
|
||||
}
|
||||
@@ -1055,7 +1058,7 @@ class CountingDeleteTabPropCollectorFactory
|
||||
: public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context context) override {
|
||||
TablePropertiesCollectorFactory::Context /*context*/) override {
|
||||
return new CountingDeleteTabPropCollector();
|
||||
}
|
||||
const char* Name() const override {
|
||||
@@ -1383,6 +1386,56 @@ TEST_F(DBPropertiesTest, EstimateOldestKeyTime) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
struct TestListener : public EventListener {
|
||||
void OnCompactionCompleted(DB* db,
|
||||
const CompactionJobInfo& /*info*/) override {
|
||||
assert(callback_triggered == false);
|
||||
assert(size_before_compaction > 0);
|
||||
callback_triggered = true;
|
||||
uint64_t total_sst_size = 0;
|
||||
uint64_t live_sst_size = 0;
|
||||
bool ok = db->GetIntProperty(DB::Properties::kTotalSstFilesSize,
|
||||
&total_sst_size);
|
||||
ASSERT_TRUE(ok);
|
||||
// total_sst_size include files before and after compaction.
|
||||
ASSERT_GT(total_sst_size, size_before_compaction);
|
||||
ok =
|
||||
db->GetIntProperty(DB::Properties::kLiveSstFilesSize, &live_sst_size);
|
||||
ASSERT_TRUE(ok);
|
||||
// live_sst_size only include files after compaction.
|
||||
ASSERT_GT(live_sst_size, 0);
|
||||
ASSERT_LT(live_sst_size, size_before_compaction);
|
||||
}
|
||||
|
||||
uint64_t size_before_compaction = 0;
|
||||
bool callback_triggered = false;
|
||||
};
|
||||
std::shared_ptr<TestListener> listener = std::make_shared<TestListener>();
|
||||
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.listeners.push_back(listener);
|
||||
Reopen(options);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put("key" + ToString(i), std::string(1000, 'v')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ASSERT_OK(Delete("key" + ToString(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
uint64_t sst_size;
|
||||
bool ok = db_->GetIntProperty(DB::Properties::kTotalSstFilesSize, &sst_size);
|
||||
ASSERT_TRUE(ok);
|
||||
ASSERT_GT(sst_size, 0);
|
||||
listener->size_before_compaction = sst_size;
|
||||
// Compact to clean all keys and trigger listener.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_TRUE(listener->callback_triggered);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
+159
-75
@@ -480,6 +480,36 @@ 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.
|
||||
@@ -720,9 +750,9 @@ TEST_F(DBTest, FlushSchedule) {
|
||||
namespace {
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -752,9 +782,9 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
@@ -769,7 +799,7 @@ class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
@@ -2217,17 +2247,17 @@ class ModelDB : public DB {
|
||||
return Write(o, &batch);
|
||||
}
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& options, ColumnFamilyHandle* cf,
|
||||
const Slice& key, PinnableSlice* value) override {
|
||||
virtual Status Get(const ReadOptions& /*options*/, ColumnFamilyHandle* /*cf*/,
|
||||
const Slice& key, PinnableSlice* /*value*/) override {
|
||||
return Status::NotSupported(key);
|
||||
}
|
||||
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const ReadOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_family*/,
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override {
|
||||
std::vector<std::string>* /*values*/) override {
|
||||
std::vector<Status> s(keys.size(),
|
||||
Status::NotSupported("Not implemented."));
|
||||
return s;
|
||||
@@ -2236,9 +2266,9 @@ class ModelDB : public DB {
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& options) override {
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*options*/) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
@@ -2248,22 +2278,22 @@ class ModelDB : public DB {
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
virtual Status GetPropertiesOfAllTables(
|
||||
ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) override {
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
TablePropertiesCollection* /*props*/) override {
|
||||
return Status();
|
||||
}
|
||||
|
||||
virtual Status GetPropertiesOfTablesInRange(
|
||||
ColumnFamilyHandle* column_family, const Range* range, std::size_t n,
|
||||
TablePropertiesCollection* props) override {
|
||||
ColumnFamilyHandle* /*column_family*/, const Range* /*range*/,
|
||||
std::size_t /*n*/, TablePropertiesCollection* /*props*/) override {
|
||||
return Status();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
using DB::KeyMayExist;
|
||||
virtual bool KeyMayExist(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value,
|
||||
virtual bool KeyMayExist(const ReadOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/, std::string* /*value*/,
|
||||
bool* value_found = nullptr) override {
|
||||
if (value_found != nullptr) {
|
||||
*value_found = false;
|
||||
@@ -2271,8 +2301,9 @@ class ModelDB : public DB {
|
||||
return true; // Not Supported directly
|
||||
}
|
||||
using DB::NewIterator;
|
||||
virtual Iterator* NewIterator(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
virtual Iterator* NewIterator(
|
||||
const ReadOptions& options,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
if (options.snapshot == nullptr) {
|
||||
KVMap* saved = new KVMap;
|
||||
*saved = map_;
|
||||
@@ -2284,9 +2315,9 @@ class ModelDB : public DB {
|
||||
}
|
||||
}
|
||||
virtual Status NewIterators(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
std::vector<Iterator*>* iterators) override {
|
||||
const ReadOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_family*/,
|
||||
std::vector<Iterator*>* /*iterators*/) override {
|
||||
return Status::NotSupported("Not supported yet");
|
||||
}
|
||||
virtual const Snapshot* GetSnapshot() override {
|
||||
@@ -2299,7 +2330,7 @@ class ModelDB : public DB {
|
||||
delete reinterpret_cast<const ModelSnapshot*>(snapshot);
|
||||
}
|
||||
|
||||
virtual Status Write(const WriteOptions& options,
|
||||
virtual Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* batch) override {
|
||||
class Handler : public WriteBatch::Handler {
|
||||
public:
|
||||
@@ -2307,7 +2338,8 @@ class ModelDB : public DB {
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
(*map_)[key.ToString()] = value.ToString();
|
||||
}
|
||||
virtual void Merge(const Slice& key, const Slice& value) override {
|
||||
virtual void Merge(const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
// ignore merge for now
|
||||
// (*map_)[key.ToString()] = value.ToString();
|
||||
}
|
||||
@@ -2321,62 +2353,65 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
using DB::GetProperty;
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) override {
|
||||
virtual bool GetProperty(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*property*/,
|
||||
std::string* /*value*/) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetIntProperty;
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, uint64_t* value) override {
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*property*/,
|
||||
uint64_t* /*value*/) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetMapProperty;
|
||||
virtual bool GetMapProperty(
|
||||
ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override {
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*property*/,
|
||||
std::map<std::string, std::string>* /*value*/) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetAggregatedIntProperty;
|
||||
virtual bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* value) override {
|
||||
virtual bool GetAggregatedIntProperty(const Slice& /*property*/,
|
||||
uint64_t* /*value*/) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
uint8_t include_flags
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* /*column_family*/,
|
||||
const Range* /*range*/, int n,
|
||||
uint64_t* sizes,
|
||||
uint8_t /*include_flags*/
|
||||
= INCLUDE_FILES) override {
|
||||
for (int i = 0; i < n; i++) {
|
||||
sizes[i] = 0;
|
||||
}
|
||||
}
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override {
|
||||
virtual void GetApproximateMemTableStats(
|
||||
ColumnFamilyHandle* /*column_family*/, const Range& /*range*/,
|
||||
uint64_t* const count, uint64_t* const size) override {
|
||||
*count = 0;
|
||||
*size = 0;
|
||||
}
|
||||
using DB::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* start, const Slice* end) override {
|
||||
virtual Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*start*/,
|
||||
const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
virtual Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& new_options)
|
||||
const std::unordered_map<std::string, std::string>& /*new_options*/)
|
||||
override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
using DB::CompactFiles;
|
||||
virtual Status CompactFiles(const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names,
|
||||
const int output_level,
|
||||
const int output_path_id = -1) override {
|
||||
virtual Status CompactFiles(
|
||||
const CompactionOptions& /*compact_options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*input_file_names*/,
|
||||
const int /*output_level*/, const int /*output_path_id*/ = -1) override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
@@ -2389,24 +2424,25 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
Status EnableAutoCompaction(
|
||||
const std::vector<ColumnFamilyHandle*>& column_family_handles) override {
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_family_handles*/)
|
||||
override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
using DB::NumberLevels;
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family) override {
|
||||
virtual int NumberLevels(ColumnFamilyHandle* /*column_family*/) override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
using DB::MaxMemCompactionLevel;
|
||||
virtual int MaxMemCompactionLevel(
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
using DB::Level0StopWriteTrigger;
|
||||
virtual int Level0StopWriteTrigger(
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2415,7 +2451,8 @@ class ModelDB : public DB {
|
||||
virtual Env* GetEnv() const override { return nullptr; }
|
||||
|
||||
using DB::GetOptions;
|
||||
virtual Options GetOptions(ColumnFamilyHandle* column_family) const override {
|
||||
virtual Options GetOptions(
|
||||
ColumnFamilyHandle* /*column_family*/) const override {
|
||||
return options_;
|
||||
}
|
||||
|
||||
@@ -2423,8 +2460,8 @@ class ModelDB : public DB {
|
||||
virtual DBOptions GetDBOptions() const override { return options_; }
|
||||
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const rocksdb::FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
virtual Status Flush(const rocksdb::FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
Status ret;
|
||||
return ret;
|
||||
}
|
||||
@@ -2434,39 +2471,42 @@ class ModelDB : public DB {
|
||||
#ifndef ROCKSDB_LITE
|
||||
virtual Status DisableFileDeletions() override { return Status::OK(); }
|
||||
|
||||
virtual Status EnableFileDeletions(bool force) override {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&, uint64_t* size,
|
||||
bool flush_memtable = true) override {
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&, uint64_t* /*size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& files) override {
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& /*files*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(std::string name) override { return Status::OK(); }
|
||||
virtual Status DeleteFile(std::string /*name*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status GetUpdatesSince(
|
||||
rocksdb::SequenceNumber, unique_ptr<rocksdb::TransactionLogIterator>*,
|
||||
const TransactionLogIterator::ReadOptions& read_options =
|
||||
const TransactionLogIterator::ReadOptions& /*read_options*/ =
|
||||
TransactionLogIterator::ReadOptions()) override {
|
||||
return Status::NotSupported("Not supported in Model DB");
|
||||
}
|
||||
|
||||
virtual void GetColumnFamilyMetaData(
|
||||
ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override {}
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
ColumnFamilyMetaData* /*metadata*/) override {}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
virtual Status GetDbIdentity(std::string& identity) const override {
|
||||
virtual Status GetDbIdentity(std::string& /*identity*/) const override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override { return 0; }
|
||||
|
||||
virtual bool SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) override {
|
||||
virtual bool SetPreserveDeletesSequenceNumber(
|
||||
SequenceNumber /*seqnum*/) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4746,7 +4786,7 @@ class DelayedMergeOperator : public MergeOperator {
|
||||
public:
|
||||
explicit DelayedMergeOperator(DBTest* d) : db_test_(d) {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
virtual bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
db_test_->env_->addon_time_.fetch_add(1000);
|
||||
merge_out->new_value = "";
|
||||
@@ -5114,10 +5154,10 @@ TEST_F(DBTest, AutomaticConflictsWithManualCompaction) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
std::thread manual_compaction_thread([this]() {
|
||||
CompactRangeOptions croptions;
|
||||
croptions.exclusive_manual_compaction = true;
|
||||
ASSERT_OK(db_->CompactRange(croptions, nullptr, nullptr));
|
||||
});
|
||||
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) {
|
||||
@@ -5692,6 +5732,50 @@ TEST_F(DBTest, PauseBackgroundWorkTest) {
|
||||
// now it's done
|
||||
ASSERT_TRUE(done.load());
|
||||
}
|
||||
|
||||
// Keep spawning short-living threads that create an iterator and quit.
|
||||
// Meanwhile in another thread keep flushing memtables.
|
||||
// This used to cause a deadlock.
|
||||
TEST_F(DBTest, ThreadLocalPtrDeadlock) {
|
||||
std::atomic<int> flushes_done{0};
|
||||
std::atomic<int> threads_destroyed{0};
|
||||
auto done = [&] {
|
||||
return flushes_done.load() > 10;
|
||||
};
|
||||
|
||||
std::thread flushing_thread([&] {
|
||||
for (int i = 0; !done(); ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Slice("hi"),
|
||||
Slice(std::to_string(i).c_str())));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
int cnt = ++flushes_done;
|
||||
fprintf(stderr, "Flushed %d times\n", cnt);
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<std::thread> thread_spawning_threads(10);
|
||||
for (auto& t: thread_spawning_threads) {
|
||||
t = std::thread([&] {
|
||||
while (!done()) {
|
||||
{
|
||||
std::thread tmp_thread([&] {
|
||||
auto it = db_->NewIterator(ReadOptions());
|
||||
delete it;
|
||||
});
|
||||
tmp_thread.join();
|
||||
}
|
||||
++threads_destroyed;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& t: thread_spawning_threads) {
|
||||
t.join();
|
||||
}
|
||||
flushing_thread.join();
|
||||
fprintf(stderr, "Done. Flushed %d times, destroyed %d threads\n",
|
||||
flushes_done.load(), threads_destroyed.load());
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+19
-14
@@ -498,9 +498,9 @@ TEST_F(DBTest2, WalFilterTest) {
|
||||
apply_option_at_record_index_(apply_option_for_record_index),
|
||||
current_record_index_(0) {}
|
||||
|
||||
virtual WalProcessingOption LogRecord(const WriteBatch& batch,
|
||||
WriteBatch* new_batch,
|
||||
bool* batch_changed) const override {
|
||||
virtual WalProcessingOption LogRecord(
|
||||
const WriteBatch& /*batch*/, WriteBatch* /*new_batch*/,
|
||||
bool* /*batch_changed*/) const override {
|
||||
WalFilter::WalProcessingOption option_to_return;
|
||||
|
||||
if (current_record_index_ == apply_option_at_record_index_) {
|
||||
@@ -874,11 +874,10 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
cf_name_id_map_ = cf_name_id_map;
|
||||
}
|
||||
|
||||
virtual WalProcessingOption LogRecordFound(unsigned long long log_number,
|
||||
const std::string& log_file_name,
|
||||
const WriteBatch& batch,
|
||||
WriteBatch* new_batch,
|
||||
bool* batch_changed) override {
|
||||
virtual WalProcessingOption LogRecordFound(
|
||||
unsigned long long log_number, const std::string& /*log_file_name*/,
|
||||
const WriteBatch& batch, WriteBatch* /*new_batch*/,
|
||||
bool* /*batch_changed*/) override {
|
||||
class LogRecordBatchHandler : public WriteBatch::Handler {
|
||||
private:
|
||||
const std::map<uint32_t, uint64_t> & cf_log_number_map_;
|
||||
@@ -1231,7 +1230,7 @@ class CompactionStallTestListener : public EventListener {
|
||||
public:
|
||||
CompactionStallTestListener() : compacted_files_cnt_(0) {}
|
||||
|
||||
void OnCompactionCompleted(DB* db, const CompactionJobInfo& ci) override {
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.cf_name, "default");
|
||||
ASSERT_EQ(ci.base_input_level, 0);
|
||||
ASSERT_EQ(ci.compaction_reason, CompactionReason::kLevelL0FilesNum);
|
||||
@@ -1820,11 +1819,17 @@ TEST_F(DBTest2, ReadAmpBitmap) {
|
||||
|
||||
#ifndef OS_SOLARIS // GetUniqueIdFromFile is not implemented
|
||||
TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
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;
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
uint32_t bytes_per_bit[2] = {1, 16};
|
||||
for (size_t k = 0; k < 2; k++) {
|
||||
|
||||
+6
-5
@@ -1143,17 +1143,18 @@ UpdateStatus DBTestBase::updateInPlaceSmallerVarintSize(char* prevValue,
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStatus DBTestBase::updateInPlaceLargerSize(char* prevValue,
|
||||
uint32_t* prevSize,
|
||||
UpdateStatus DBTestBase::updateInPlaceLargerSize(char* /*prevValue*/,
|
||||
uint32_t* /*prevSize*/,
|
||||
Slice delta,
|
||||
std::string* newValue) {
|
||||
*newValue = std::string(delta.size(), 'c');
|
||||
return UpdateStatus::UPDATED;
|
||||
}
|
||||
|
||||
UpdateStatus DBTestBase::updateInPlaceNoAction(char* prevValue,
|
||||
uint32_t* prevSize, Slice delta,
|
||||
std::string* newValue) {
|
||||
UpdateStatus DBTestBase::updateInPlaceNoAction(char* /*prevValue*/,
|
||||
uint32_t* /*prevSize*/,
|
||||
Slice /*delta*/,
|
||||
std::string* /*newValue*/) {
|
||||
return UpdateStatus::UPDATE_FAILED;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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 {
|
||||
memtable_->Insert(handle);
|
||||
num_entries_++;
|
||||
memtable_->Insert(handle);
|
||||
}
|
||||
|
||||
// Returns true iff an entry that compares equal to key is in the list.
|
||||
@@ -187,7 +187,7 @@ class SpecialSkipListFactory : public MemTableRepFactory {
|
||||
using MemTableRepFactory::CreateMemTableRep;
|
||||
virtual MemTableRep* CreateMemTableRep(
|
||||
const MemTableRep::KeyComparator& compare, Allocator* allocator,
|
||||
const SliceTransform* transform, Logger* logger) override {
|
||||
const SliceTransform* transform, Logger* /*logger*/) override {
|
||||
return new SpecialMemTableRep(
|
||||
allocator, factory_.CreateMemTableRep(compare, allocator, transform, 0),
|
||||
num_entries_flush_);
|
||||
|
||||
@@ -56,9 +56,9 @@ void VerifyCompactionResult(
|
||||
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value, bool* value_changed) const
|
||||
override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -88,9 +88,9 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
@@ -388,10 +388,16 @@ 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();
|
||||
@@ -445,6 +451,102 @@ 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) {
|
||||
|
||||
@@ -125,6 +125,26 @@ 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,6 +162,8 @@ 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;
|
||||
|
||||
@@ -159,7 +159,7 @@ class DeleteFileTest : public testing::Test {
|
||||
}
|
||||
|
||||
// An empty job to guard all jobs are processed
|
||||
static void GuardFinish(void* arg) {
|
||||
static void GuardFinish(void* /*arg*/) {
|
||||
TEST_SYNC_POINT("DeleteFileTest::GuardFinish");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,11 +132,15 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed) {
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
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 status =
|
||||
IngestedFilesOverlapWithMemtables(super_version, flush_needed);
|
||||
|
||||
cfd_->RangesOverlapWithMemtables(ranges, super_version, flush_needed);
|
||||
if (status.ok() && *flush_needed &&
|
||||
!ingestion_options_.allow_blocking_flush) {
|
||||
status = Status::InvalidArgument("External file requires flush");
|
||||
@@ -148,11 +152,12 @@ 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);
|
||||
status = NeedsFlush(&need_flush, super_version);
|
||||
assert(status.ok() && need_flush == false);
|
||||
#endif
|
||||
|
||||
@@ -167,7 +172,6 @@ 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
|
||||
|
||||
@@ -375,54 +379,6 @@ 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) {
|
||||
@@ -473,7 +429,10 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
return f1->largest_seqno < f2->largest_seqno;
|
||||
}))
|
||||
->largest_seqno;
|
||||
if (level_largest_seqno != 0) {
|
||||
// should only assign seqno to current level's largest seqno when
|
||||
// the file fits
|
||||
if (level_largest_seqno != 0 &&
|
||||
IngestedFileFitInLevel(file_to_ingest, lvl)) {
|
||||
*assigned_seqno = level_largest_seqno;
|
||||
} else {
|
||||
continue;
|
||||
|
||||
@@ -84,8 +84,12 @@ 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.
|
||||
// REQUIRES: Mutex held
|
||||
Status NeedsFlush(bool* flush_needed);
|
||||
//
|
||||
// @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);
|
||||
|
||||
// Will execute the ingestion job and prepare edit() to be applied.
|
||||
// REQUIRES: Mutex held
|
||||
@@ -110,10 +114,6 @@ 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,15 +388,17 @@ 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", std::to_string(count_)},
|
||||
{prefix_ + "_Count", count},
|
||||
};
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status AddUserKey(const Slice& user_key, const Slice& value, EntryType type,
|
||||
SequenceNumber seq, uint64_t file_size) override {
|
||||
Status AddUserKey(const Slice& /*user_key*/, const Slice& /*value*/,
|
||||
EntryType /*type*/, SequenceNumber /*seq*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
++count_;
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -416,7 +418,7 @@ class SstFileWriterCollectorFactory : public TablePropertiesCollectorFactory {
|
||||
explicit SstFileWriterCollectorFactory(std::string prefix)
|
||||
: prefix_(prefix), num_created_(0) {}
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context context) override {
|
||||
TablePropertiesCollectorFactory::Context /*context*/) override {
|
||||
num_created_++;
|
||||
return new SstFileWriterCollector(prefix_);
|
||||
}
|
||||
@@ -1797,7 +1799,7 @@ TEST_F(ExternalSSTFileTest, FileWithCFInfo) {
|
||||
|
||||
class TestIngestExternalFileListener : public EventListener {
|
||||
public:
|
||||
void OnExternalFileIngested(DB* db,
|
||||
void OnExternalFileIngested(DB* /*db*/,
|
||||
const ExternalFileIngestionInfo& info) override {
|
||||
ingested_files.push_back(info);
|
||||
}
|
||||
|
||||
@@ -228,16 +228,9 @@ 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 {
|
||||
int num = i;
|
||||
unsigned long long num = i;
|
||||
if (!sequential_order_) {
|
||||
// random transfer
|
||||
const int m = 0x5bd1e995;
|
||||
@@ -245,7 +238,7 @@ __attribute__((__no_sanitize_undefined__))
|
||||
num ^= num << 24;
|
||||
}
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "%016d", num);
|
||||
snprintf(buf, sizeof(buf), "%016d", static_cast<int>(num));
|
||||
storage->assign(buf, strlen(buf));
|
||||
return Slice(*storage);
|
||||
}
|
||||
|
||||
@@ -36,10 +36,10 @@ class IntComparator : public Comparator {
|
||||
|
||||
const char* Name() const override { return "IntComparator"; }
|
||||
|
||||
void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override {}
|
||||
void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
void FindShortSuccessor(std::string* key) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
class FileIndexerTest : public testing::Test {
|
||||
|
||||
+33
-6
@@ -54,6 +54,32 @@
|
||||
|
||||
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,
|
||||
@@ -278,12 +304,13 @@ 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;
|
||||
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());
|
||||
|
||||
{
|
||||
ScopedArenaIterator iter(
|
||||
|
||||
+71
-51
@@ -25,15 +25,15 @@
|
||||
namespace rocksdb {
|
||||
|
||||
// Usage:
|
||||
// LevelIterator iter;
|
||||
// ForwardLevelIterator iter;
|
||||
// iter.SetFileIndex(file_index);
|
||||
// iter.Seek(target);
|
||||
// iter.Next()
|
||||
class LevelIterator : public InternalIterator {
|
||||
class ForwardLevelIterator : public InternalIterator {
|
||||
public:
|
||||
LevelIterator(const ColumnFamilyData* const cfd,
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files)
|
||||
ForwardLevelIterator(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 LevelIterator : public InternalIterator {
|
||||
file_iter_(nullptr),
|
||||
pinned_iters_mgr_(nullptr) {}
|
||||
|
||||
~LevelIterator() {
|
||||
~ForwardLevelIterator() {
|
||||
// Reset current pointer
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinIterator(file_iter_);
|
||||
@@ -84,11 +84,11 @@ class LevelIterator : public InternalIterator {
|
||||
}
|
||||
}
|
||||
void SeekToLast() override {
|
||||
status_ = Status::NotSupported("LevelIterator::SeekToLast()");
|
||||
status_ = Status::NotSupported("ForwardLevelIterator::SeekToLast()");
|
||||
valid_ = false;
|
||||
}
|
||||
void Prev() override {
|
||||
status_ = Status::NotSupported("LevelIterator::Prev()");
|
||||
status_ = Status::NotSupported("ForwardLevelIterator::Prev()");
|
||||
valid_ = false;
|
||||
}
|
||||
bool Valid() const override {
|
||||
@@ -104,8 +104,8 @@ class LevelIterator : public InternalIterator {
|
||||
file_iter_->Seek(internal_key);
|
||||
valid_ = file_iter_->Valid();
|
||||
}
|
||||
void SeekForPrev(const Slice& internal_key) override {
|
||||
status_ = Status::NotSupported("LevelIterator::SeekForPrev()");
|
||||
void SeekForPrev(const Slice& /*internal_key*/) override {
|
||||
status_ = Status::NotSupported("ForwardLevelIterator::SeekForPrev()");
|
||||
valid_ = false;
|
||||
}
|
||||
void Next() override {
|
||||
@@ -196,38 +196,60 @@ ForwardIterator::~ForwardIterator() {
|
||||
Cleanup(true);
|
||||
}
|
||||
|
||||
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()) {
|
||||
void ForwardIterator::SVCleanup(DBImpl* db, SuperVersion* sv,
|
||||
bool background_purge_on_iterator_cleanup) {
|
||||
if (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 (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_.Lock();
|
||||
sv->Cleanup();
|
||||
db->FindObsoleteFiles(&job_context, false, true);
|
||||
if (background_purge_on_iterator_cleanup) {
|
||||
db->ScheduleBgLogWriterClose(&job_context);
|
||||
}
|
||||
db->mutex_.Unlock();
|
||||
delete sv;
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(
|
||||
job_context, read_options_.background_purge_on_iterator_cleanup);
|
||||
db->PurgeObsoleteFiles(job_context, 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 */);
|
||||
@@ -361,14 +383,13 @@ 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()) {
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,15 +416,14 @@ 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()) {
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -541,7 +561,7 @@ void ForwardIterator::UpdateChildrenPinnedItersMgr() {
|
||||
}
|
||||
|
||||
// Set PinnedIteratorsManager for L1+ levels iterators.
|
||||
for (LevelIterator* child_iter : level_iters_) {
|
||||
for (ForwardLevelIterator* child_iter : level_iters_) {
|
||||
if (child_iter) {
|
||||
child_iter->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
@@ -703,7 +723,7 @@ void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage) {
|
||||
}
|
||||
} else {
|
||||
level_iters_.push_back(
|
||||
new LevelIterator(cfd_, read_options_, level_files));
|
||||
new ForwardLevelIterator(cfd_, read_options_, level_files));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-3
@@ -23,7 +23,7 @@ class DBImpl;
|
||||
class Env;
|
||||
struct SuperVersion;
|
||||
class ColumnFamilyData;
|
||||
class LevelIterator;
|
||||
class ForwardLevelIterator;
|
||||
class VersionStorageInfo;
|
||||
struct FileMetaData;
|
||||
|
||||
@@ -55,7 +55,7 @@ class ForwardIterator : public InternalIterator {
|
||||
ColumnFamilyData* cfd, SuperVersion* current_sv = nullptr);
|
||||
virtual ~ForwardIterator();
|
||||
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
void SeekForPrev(const Slice& /*target*/) override {
|
||||
status_ = Status::NotSupported("ForwardIterator::SeekForPrev()");
|
||||
valid_ = false;
|
||||
}
|
||||
@@ -85,7 +85,14 @@ 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);
|
||||
@@ -119,7 +126,7 @@ class ForwardIterator : public InternalIterator {
|
||||
InternalIterator* mutable_iter_;
|
||||
std::vector<InternalIterator*> imm_iters_;
|
||||
std::vector<InternalIterator*> l0_iters_;
|
||||
std::vector<LevelIterator*> level_iters_;
|
||||
std::vector<ForwardLevelIterator*> level_iters_;
|
||||
InternalIterator* current_;
|
||||
bool valid_;
|
||||
|
||||
|
||||
+73
-55
@@ -233,6 +233,7 @@ static const std::string estimate_live_data_size = "estimate-live-data-size";
|
||||
static const std::string min_log_number_to_keep = "min-log-number-to-keep";
|
||||
static const std::string base_level = "base-level";
|
||||
static const std::string total_sst_files_size = "total-sst-files-size";
|
||||
static const std::string live_sst_files_size = "live-sst-files-size";
|
||||
static const std::string estimate_pending_comp_bytes =
|
||||
"estimate-pending-compaction-bytes";
|
||||
static const std::string aggregated_table_properties =
|
||||
@@ -307,6 +308,8 @@ const std::string DB::Properties::kMinLogNumberToKeep =
|
||||
rocksdb_prefix + min_log_number_to_keep;
|
||||
const std::string DB::Properties::kTotalSstFilesSize =
|
||||
rocksdb_prefix + total_sst_files_size;
|
||||
const std::string DB::Properties::kLiveSstFilesSize =
|
||||
rocksdb_prefix + live_sst_files_size;
|
||||
const std::string DB::Properties::kBaseLevel = rocksdb_prefix + base_level;
|
||||
const std::string DB::Properties::kEstimatePendingCompactionBytes =
|
||||
rocksdb_prefix + estimate_pending_comp_bytes;
|
||||
@@ -405,6 +408,8 @@ const std::unordered_map<std::string, DBPropertyInfo>
|
||||
{false, nullptr, &InternalStats::HandleBaseLevel, nullptr}},
|
||||
{DB::Properties::kTotalSstFilesSize,
|
||||
{false, nullptr, &InternalStats::HandleTotalSstFilesSize, nullptr}},
|
||||
{DB::Properties::kLiveSstFilesSize,
|
||||
{false, nullptr, &InternalStats::HandleLiveSstFilesSize, nullptr}},
|
||||
{DB::Properties::kEstimatePendingCompactionBytes,
|
||||
{false, nullptr, &InternalStats::HandleEstimatePendingCompactionBytes,
|
||||
nullptr}},
|
||||
@@ -442,7 +447,7 @@ bool InternalStats::GetStringProperty(const DBPropertyInfo& property_info,
|
||||
}
|
||||
|
||||
bool InternalStats::GetMapProperty(const DBPropertyInfo& property_info,
|
||||
const Slice& property,
|
||||
const Slice& /*property*/,
|
||||
std::map<std::string, std::string>* value) {
|
||||
assert(value != nullptr);
|
||||
assert(property_info.handle_map != nullptr);
|
||||
@@ -494,7 +499,7 @@ bool InternalStats::HandleCompressionRatioAtLevelPrefix(std::string* value,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleLevelStats(std::string* value, Slice suffix) {
|
||||
bool InternalStats::HandleLevelStats(std::string* value, Slice /*suffix*/) {
|
||||
char buf[1000];
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
snprintf(buf, sizeof(buf),
|
||||
@@ -527,35 +532,36 @@ bool InternalStats::HandleCFMapStats(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCFStats(std::string* value, Slice suffix) {
|
||||
bool InternalStats::HandleCFStats(std::string* value, Slice /*suffix*/) {
|
||||
DumpCFStats(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCFStatsNoFileHistogram(std::string* value,
|
||||
Slice suffix) {
|
||||
Slice /*suffix*/) {
|
||||
DumpCFStatsNoFileHistogram(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCFFileHistogram(std::string* value, Slice suffix) {
|
||||
bool InternalStats::HandleCFFileHistogram(std::string* value,
|
||||
Slice /*suffix*/) {
|
||||
DumpCFFileHistogram(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleDBStats(std::string* value, Slice suffix) {
|
||||
bool InternalStats::HandleDBStats(std::string* value, Slice /*suffix*/) {
|
||||
DumpDBStats(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleSsTables(std::string* value, Slice suffix) {
|
||||
bool InternalStats::HandleSsTables(std::string* value, Slice /*suffix*/) {
|
||||
auto* current = cfd_->current();
|
||||
*value = current->DebugString(true, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleAggregatedTableProperties(std::string* value,
|
||||
Slice suffix) {
|
||||
Slice /*suffix*/) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
auto s = cfd_->current()->GetAggregatedTableProperties(&tp);
|
||||
if (!s.ok()) {
|
||||
@@ -582,34 +588,34 @@ bool InternalStats::HandleAggregatedTablePropertiesAtLevel(std::string* value,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumImmutableMemTable(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleNumImmutableMemTable(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
*value = cfd_->imm()->NumNotFlushed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumImmutableMemTableFlushed(uint64_t* value,
|
||||
DBImpl* db,
|
||||
Version* version) {
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
*value = cfd_->imm()->NumFlushed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleMemTableFlushPending(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleMemTableFlushPending(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Return number of mem tables that are ready to flush (made immutable)
|
||||
*value = (cfd_->imm()->IsFlushPending() ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumRunningFlushes(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
Version* /*version*/) {
|
||||
*value = db->num_running_flushes();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCompactionPending(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleCompactionPending(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// 1 if the system already determines at least one compaction is needed.
|
||||
// 0 otherwise,
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
@@ -618,70 +624,74 @@ bool InternalStats::HandleCompactionPending(uint64_t* value, DBImpl* db,
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumRunningCompactions(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
Version* /*version*/) {
|
||||
*value = db->num_running_compactions_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleBackgroundErrors(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleBackgroundErrors(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Accumulated number of errors in background flushes or compactions.
|
||||
*value = GetBackgroundErrorCount();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCurSizeActiveMemTable(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleCurSizeActiveMemTable(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Current size of the active memtable
|
||||
*value = cfd_->mem()->ApproximateMemoryUsage();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCurSizeAllMemTables(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleCurSizeAllMemTables(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Current size of the active memtable + immutable memtables
|
||||
*value = cfd_->mem()->ApproximateMemoryUsage() +
|
||||
cfd_->imm()->ApproximateUnflushedMemTablesMemoryUsage();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleSizeAllMemTables(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleSizeAllMemTables(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
*value = cfd_->mem()->ApproximateMemoryUsage() +
|
||||
cfd_->imm()->ApproximateMemoryUsage();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumEntriesActiveMemTable(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleNumEntriesActiveMemTable(uint64_t* value,
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Current number of entires in the active memtable
|
||||
*value = cfd_->mem()->num_entries();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumEntriesImmMemTables(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleNumEntriesImmMemTables(uint64_t* value,
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Current number of entries in the immutable memtables
|
||||
*value = cfd_->imm()->current()->GetTotalNumEntries();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumDeletesActiveMemTable(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleNumDeletesActiveMemTable(uint64_t* value,
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Current number of entires in the active memtable
|
||||
*value = cfd_->mem()->num_deletes();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumDeletesImmMemTables(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleNumDeletesImmMemTables(uint64_t* value,
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Current number of entries in the immutable memtables
|
||||
*value = cfd_->imm()->current()->GetTotalNumDeletes();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleEstimateNumKeys(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleEstimateNumKeys(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
// Estimate number of entries in the column family:
|
||||
// Use estimated entries in tables + total entries in memtables.
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
@@ -697,77 +707,85 @@ bool InternalStats::HandleEstimateNumKeys(uint64_t* value, DBImpl* db,
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumSnapshots(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
Version* /*version*/) {
|
||||
*value = db->snapshots().count();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleOldestSnapshotTime(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
Version* /*version*/) {
|
||||
*value = static_cast<uint64_t>(db->snapshots().GetOldestSnapshotTime());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleNumLiveVersions(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleNumLiveVersions(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
*value = cfd_->GetNumLiveVersions();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCurrentSuperVersionNumber(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleCurrentSuperVersionNumber(uint64_t* value,
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
*value = cfd_->GetSuperVersionNumber();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleIsFileDeletionsEnabled(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
Version* /*version*/) {
|
||||
*value = db->IsFileDeletionsEnabled();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleBaseLevel(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleBaseLevel(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
*value = vstorage->base_level();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleTotalSstFilesSize(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleTotalSstFilesSize(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
*value = cfd_->GetTotalSstFilesSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleLiveSstFilesSize(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
*value = cfd_->GetLiveSstFilesSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleEstimatePendingCompactionBytes(uint64_t* value,
|
||||
DBImpl* db,
|
||||
Version* version) {
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
*value = vstorage->estimated_compaction_needed_bytes();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleEstimateTableReadersMem(uint64_t* value, DBImpl* db,
|
||||
bool InternalStats::HandleEstimateTableReadersMem(uint64_t* value,
|
||||
DBImpl* /*db*/,
|
||||
Version* version) {
|
||||
*value = (version == nullptr) ? 0 : version->GetMemoryUsageByTableReaders();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleEstimateLiveDataSize(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
bool InternalStats::HandleEstimateLiveDataSize(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
*value = vstorage->EstimateLiveDataSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleMinLogNumberToKeep(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
Version* /*version*/) {
|
||||
*value = db->MinLogNumberToKeep();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleActualDelayedWriteRate(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
Version* /*version*/) {
|
||||
const WriteController& wc = db->write_controller();
|
||||
if (!wc.NeedsDelay()) {
|
||||
*value = 0;
|
||||
@@ -778,7 +796,7 @@ bool InternalStats::HandleActualDelayedWriteRate(uint64_t* value, DBImpl* db,
|
||||
}
|
||||
|
||||
bool InternalStats::HandleIsWriteStopped(uint64_t* value, DBImpl* db,
|
||||
Version* version) {
|
||||
Version* /*version*/) {
|
||||
*value = db->write_controller().IsStopped() ? 1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -467,6 +467,7 @@ class InternalStats {
|
||||
Version* version);
|
||||
bool HandleBaseLevel(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleTotalSstFilesSize(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleLiveSstFilesSize(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleEstimatePendingCompactionBytes(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
bool HandleEstimateTableReadersMem(uint64_t* value, DBImpl* db,
|
||||
|
||||
+10
-9
@@ -46,11 +46,11 @@ class EventListenerTest : public DBTestBase {
|
||||
};
|
||||
|
||||
struct TestPropertiesCollector : public rocksdb::TablePropertiesCollector {
|
||||
virtual rocksdb::Status AddUserKey(const rocksdb::Slice& key,
|
||||
const rocksdb::Slice& value,
|
||||
rocksdb::EntryType type,
|
||||
rocksdb::SequenceNumber seq,
|
||||
uint64_t file_size) override {
|
||||
virtual rocksdb::Status AddUserKey(const rocksdb::Slice& /*key*/,
|
||||
const rocksdb::Slice& /*value*/,
|
||||
rocksdb::EntryType /*type*/,
|
||||
rocksdb::SequenceNumber /*seq*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
virtual rocksdb::Status Finish(
|
||||
@@ -73,7 +73,7 @@ struct TestPropertiesCollector : public rocksdb::TablePropertiesCollector {
|
||||
class TestPropertiesCollectorFactory : public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector(
|
||||
TablePropertiesCollectorFactory::Context context) override {
|
||||
TablePropertiesCollectorFactory::Context /*context*/) override {
|
||||
return new TestPropertiesCollector;
|
||||
}
|
||||
const char* Name() const override { return "TestTablePropertiesCollector"; }
|
||||
@@ -425,7 +425,7 @@ TEST_F(EventListenerTest, DisableBGCompaction) {
|
||||
|
||||
class TestCompactionReasonListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionCompleted(DB* db, const CompactionJobInfo& ci) override {
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
compaction_reasons_.push_back(ci.compaction_reason);
|
||||
}
|
||||
@@ -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::kUniversalSortedRunNum);
|
||||
ASSERT_EQ(compaction_reason, CompactionReason::kUniversalSizeRatio);
|
||||
}
|
||||
|
||||
options.level0_file_num_compaction_trigger = 8;
|
||||
@@ -807,7 +807,8 @@ class BackgroundErrorListener : public EventListener {
|
||||
public:
|
||||
BackgroundErrorListener(SpecialEnv* env) : env_(env), counter_(0) {}
|
||||
|
||||
void OnBackgroundError(BackgroundErrorReason reason, Status* bg_error) override {
|
||||
void OnBackgroundError(BackgroundErrorReason /*reason*/,
|
||||
Status* bg_error) override {
|
||||
if (counter_ == 0) {
|
||||
// suppress the first error and disable write-dropping such that a retry
|
||||
// can succeed.
|
||||
|
||||
@@ -54,6 +54,7 @@ class Reader {
|
||||
// The Reader will start reading at the first record located at physical
|
||||
// position >= initial_offset within the file.
|
||||
Reader(std::shared_ptr<Logger> info_log,
|
||||
// @lint-ignore TXT2 T25377293 Grandfathered in
|
||||
unique_ptr<SequentialFileReader>&& file,
|
||||
Reporter* reporter, bool checksum, uint64_t initial_offset,
|
||||
uint64_t log_num);
|
||||
|
||||
+4
-2
@@ -41,8 +41,8 @@ static void GetJemallocStatus(void* mstat_arg, const char* status) {
|
||||
}
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
|
||||
void DumpMallocStats(std::string* stats) {
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
void DumpMallocStats(std::string* stats) {
|
||||
MallocStatus mstat;
|
||||
const unsigned int kMallocStatusLen = 1000000;
|
||||
std::unique_ptr<char[]> buf{new char[kMallocStatusLen + 1]};
|
||||
@@ -50,8 +50,10 @@ void DumpMallocStats(std::string* stats) {
|
||||
mstat.end = buf.get() + kMallocStatusLen;
|
||||
je_malloc_stats_print(GetJemallocStatus, &mstat, "");
|
||||
stats->append(buf.get());
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
}
|
||||
#else
|
||||
void DumpMallocStats(std::string*) {}
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -46,9 +46,9 @@ class DestroyAllCompactionFilter : public CompactionFilter {
|
||||
public:
|
||||
DestroyAllCompactionFilter() {}
|
||||
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& existing_value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return existing_value.ToString() == "destroy";
|
||||
}
|
||||
|
||||
|
||||
+30
-16
@@ -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.Compare(k1, k2);
|
||||
return comparator.CompareKeySeq(k1, k2);
|
||||
}
|
||||
|
||||
int MemTable::KeyComparator::operator()(const char* prefix_len_key,
|
||||
@@ -233,16 +233,16 @@ 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.Compare(a, key);
|
||||
return comparator.CompareKeySeq(a, key);
|
||||
}
|
||||
|
||||
void MemTableRep::InsertConcurrently(KeyHandle handle) {
|
||||
void MemTableRep::InsertConcurrently(KeyHandle /*handle*/) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
throw std::runtime_error("concurrent insert not supported");
|
||||
throw std::runtime_error("concurrent insert not supported");
|
||||
#else
|
||||
abort();
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Slice MemTableRep::UserKey(const char* key) const {
|
||||
Slice slice = GetLengthPrefixedSlice(key);
|
||||
@@ -289,8 +289,7 @@ class MemTableIterator : public InternalIterator {
|
||||
#ifndef NDEBUG
|
||||
// Assert that the MemTableIterator is never deleted while
|
||||
// Pinning is Enabled.
|
||||
assert(!pinned_iters_mgr_ ||
|
||||
(pinned_iters_mgr_ && !pinned_iters_mgr_->PinningEnabled()));
|
||||
assert(!pinned_iters_mgr_ || !pinned_iters_mgr_->PinningEnabled());
|
||||
#endif
|
||||
if (arena_mode_) {
|
||||
iter_->~Iterator();
|
||||
@@ -444,7 +443,7 @@ MemTable::MemTableStats MemTable::ApproximateStats(const Slice& start_ikey,
|
||||
return {entry_count * (data_size / n), entry_count};
|
||||
}
|
||||
|
||||
void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
const Slice& key, /* user key */
|
||||
const Slice& value, bool allow_concurrent,
|
||||
MemTablePostProcessInfo* post_process_info) {
|
||||
@@ -479,9 +478,15 @@ void 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);
|
||||
table->InsertWithHint(handle, &insert_hints_[prefix]);
|
||||
bool res = table->InsertKeyWithHint(handle, &insert_hints_[prefix]);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
table->Insert(handle);
|
||||
bool res = table->InsertKey(handle);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a bit ugly, but is the way to avoid locked instructions
|
||||
@@ -514,7 +519,10 @@ void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
assert(post_process_info == nullptr);
|
||||
UpdateFlushState();
|
||||
} else {
|
||||
table->InsertConcurrently(handle);
|
||||
bool res = table->InsertKeyConcurrently(handle);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
assert(post_process_info != nullptr);
|
||||
post_process_info->num_entries++;
|
||||
@@ -544,6 +552,7 @@ void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
is_range_del_table_empty_ = false;
|
||||
}
|
||||
UpdateOldestKeyTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback from MemTable::Get()
|
||||
@@ -579,11 +588,12 @@ 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(s != nullptr && merge_context != nullptr && range_del_agg != nullptr);
|
||||
assert(merge_context != nullptr && range_del_agg != nullptr);
|
||||
|
||||
// entry format is:
|
||||
// klength varint32
|
||||
@@ -799,8 +809,9 @@ void MemTable::Update(SequenceNumber seq,
|
||||
// Correct user key
|
||||
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
|
||||
ValueType type;
|
||||
SequenceNumber unused;
|
||||
UnPackSequenceAndType(tag, &unused, &type);
|
||||
SequenceNumber existing_seq;
|
||||
UnPackSequenceAndType(tag, &existing_seq, &type);
|
||||
assert(existing_seq != seq);
|
||||
if (type == kTypeValue) {
|
||||
Slice prev_value = GetLengthPrefixedSlice(key_ptr + key_length);
|
||||
uint32_t prev_size = static_cast<uint32_t>(prev_value.size());
|
||||
@@ -823,7 +834,10 @@ void MemTable::Update(SequenceNumber seq,
|
||||
}
|
||||
|
||||
// key doesn't exist
|
||||
Add(seq, kTypeValue, key, value);
|
||||
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);
|
||||
}
|
||||
|
||||
bool MemTable::UpdateCallback(SequenceNumber seq,
|
||||
|
||||
+4
-1
@@ -167,7 +167,10 @@ class MemTable {
|
||||
//
|
||||
// REQUIRES: if allow_concurrent = false, external synchronization to prevent
|
||||
// simultaneous operations on the same MemTable.
|
||||
void Add(SequenceNumber seq, ValueType type, const Slice& key,
|
||||
//
|
||||
// Returns false if MemTableRepFactory::CanHandleDuplicatedKey() is true and
|
||||
// the <key, seq> already exists.
|
||||
bool Add(SequenceNumber seq, ValueType type, const Slice& key,
|
||||
const Slice& value, bool allow_concurrent = false,
|
||||
MemTablePostProcessInfo* post_process_info = nullptr);
|
||||
|
||||
|
||||
+2
-2
@@ -154,7 +154,7 @@ bool MemTableListVersion::GetFromList(
|
||||
}
|
||||
|
||||
Status MemTableListVersion::AddRangeTombstoneIterators(
|
||||
const ReadOptions& read_opts, Arena* arena,
|
||||
const ReadOptions& read_opts, Arena* /*arena*/,
|
||||
RangeDelAggregator* range_del_agg) {
|
||||
assert(range_del_agg != nullptr);
|
||||
for (auto& m : memlist_) {
|
||||
@@ -300,7 +300,7 @@ void MemTableList::PickMemtablesToFlush(autovector<MemTable*>* ret) {
|
||||
}
|
||||
|
||||
void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
uint64_t file_number) {
|
||||
uint64_t /*file_number*/) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_ROLLBACK);
|
||||
assert(!mems.empty());
|
||||
|
||||
+8
-3
@@ -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,7 +22,8 @@ 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, int level,
|
||||
SequenceNumber latest_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, int level,
|
||||
Statistics* stats,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
: env_(env),
|
||||
@@ -34,6 +35,7 @@ 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_),
|
||||
@@ -158,7 +160,10 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
// hit a different user key, stop right here
|
||||
hit_the_next_user_key = true;
|
||||
break;
|
||||
} else if (stop_before && ikey.sequence <= stop_before) {
|
||||
} else if (stop_before > 0 && ikey.sequence <= stop_before &&
|
||||
LIKELY(snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(ikey.sequence,
|
||||
stop_before))) {
|
||||
// hit an entry that's visible by the previous snapshot, can't touch that
|
||||
break;
|
||||
}
|
||||
|
||||
+4
-1
@@ -13,6 +13,7 @@
|
||||
#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"
|
||||
@@ -33,7 +34,8 @@ class MergeHelper {
|
||||
const MergeOperator* user_merge_operator,
|
||||
const CompactionFilter* compaction_filter, Logger* logger,
|
||||
bool assert_valid_internal_key, SequenceNumber latest_snapshot,
|
||||
int level = 0, Statistics* stats = nullptr,
|
||||
const SnapshotChecker* snapshot_checker = nullptr, int level = 0,
|
||||
Statistics* stats = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr);
|
||||
|
||||
// Wrapper around MergeOperator::FullMergeV2() that records perf statistics.
|
||||
@@ -145,6 +147,7 @@ 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
|
||||
|
||||
+1
-1
@@ -504,7 +504,7 @@ void runTest(int argc, const std::string& dbname, const bool use_ttl = false) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char* /*argv*/ []) {
|
||||
//TODO: Make this test like a general rocksdb unit-test
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
runTest(argc, test::TmpDir() + "/merge_testdb");
|
||||
|
||||
@@ -327,7 +327,7 @@ class TestPlainTableFactory : public PlainTableFactory {
|
||||
const TableReaderOptions& table_reader_options,
|
||||
unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
unique_ptr<TableReader>* table,
|
||||
bool prefetch_index_and_filter_in_cache) const override {
|
||||
bool /*prefetch_index_and_filter_in_cache*/) const override {
|
||||
TableProperties* props = nullptr;
|
||||
auto s =
|
||||
ReadTableProperties(file.get(), file_size, kPlainTableMagicNumber,
|
||||
|
||||
+3
-3
@@ -126,10 +126,10 @@ class TestKeyComparator : public Comparator {
|
||||
return "TestKeyComparator";
|
||||
}
|
||||
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override {}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -19,8 +19,8 @@ class SnapshotChecker {
|
||||
class DisableGCSnapshotChecker : public SnapshotChecker {
|
||||
public:
|
||||
virtual ~DisableGCSnapshotChecker() {}
|
||||
virtual bool IsInSnapshot(SequenceNumber sequence,
|
||||
SequenceNumber snapshot_sequence) const {
|
||||
virtual bool IsInSnapshot(SequenceNumber /*sequence*/,
|
||||
SequenceNumber /*snapshot_sequence*/) const {
|
||||
// By returning false, we prevent all the values from being GCed
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -30,7 +30,7 @@ namespace rocksdb {
|
||||
namespace {
|
||||
|
||||
template <class T>
|
||||
static void DeleteEntry(const Slice& key, void* value) {
|
||||
static void DeleteEntry(const Slice& /*key*/, void* value) {
|
||||
T* typed_value = reinterpret_cast<T*>(value);
|
||||
delete typed_value;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ static void UnrefEntry(void* arg1, void* arg2) {
|
||||
cache->Release(h);
|
||||
}
|
||||
|
||||
static void DeleteTableReader(void* arg1, void* arg2) {
|
||||
static void DeleteTableReader(void* arg1, void* /*arg2*/) {
|
||||
TableReader* table_reader = reinterpret_cast<TableReader*>(arg1);
|
||||
delete table_reader;
|
||||
}
|
||||
@@ -272,9 +272,8 @@ 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;
|
||||
table_reader = fd.table_reader;
|
||||
TableReader* table_reader = fd.table_reader;
|
||||
if (table_reader == nullptr) {
|
||||
s = FindTable(env_options, icomparator, fd, &handle,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
namespace rocksdb {
|
||||
|
||||
Status InternalKeyPropertiesCollector::InternalAdd(const Slice& key,
|
||||
const Slice& value,
|
||||
uint64_t file_size) {
|
||||
const Slice& /*value*/,
|
||||
uint64_t /*file_size*/) {
|
||||
ParsedInternalKey ikey;
|
||||
if (!ParseInternalKey(key, &ikey)) {
|
||||
return Status::InvalidArgument("Invalid internal key");
|
||||
@@ -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()) {
|
||||
|
||||
@@ -73,7 +73,7 @@ class InternalKeyPropertiesCollectorFactory
|
||||
: public IntTblPropCollectorFactory {
|
||||
public:
|
||||
virtual IntTblPropCollector* CreateIntTblPropCollector(
|
||||
uint32_t column_family_id) override {
|
||||
uint32_t /*column_family_id*/) override {
|
||||
return new InternalKeyPropertiesCollector();
|
||||
}
|
||||
|
||||
|
||||
@@ -82,8 +82,9 @@ class RegularKeysStartWithA: public TablePropertiesCollector {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status AddUserKey(const Slice& user_key, const Slice& value, EntryType type,
|
||||
SequenceNumber seq, uint64_t file_size) override {
|
||||
Status AddUserKey(const Slice& user_key, const Slice& /*value*/,
|
||||
EntryType type, SequenceNumber /*seq*/,
|
||||
uint64_t file_size) override {
|
||||
// simply asssume all user keys are not empty.
|
||||
if (user_key.data()[0] == 'A') {
|
||||
++count_;
|
||||
@@ -133,7 +134,7 @@ class RegularKeysStartWithABackwardCompatible
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Add(const Slice& user_key, const Slice& value) override {
|
||||
Status Add(const Slice& user_key, const Slice& /*value*/) override {
|
||||
// simply asssume all user keys are not empty.
|
||||
if (user_key.data()[0] == 'A') {
|
||||
++count_;
|
||||
@@ -161,8 +162,8 @@ class RegularKeysStartWithAInternal : public IntTblPropCollector {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status InternalAdd(const Slice& user_key, const Slice& value,
|
||||
uint64_t file_size) override {
|
||||
Status InternalAdd(const Slice& user_key, const Slice& /*value*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
// simply asssume all user keys are not empty.
|
||||
if (user_key.data()[0] == 'A') {
|
||||
++count_;
|
||||
@@ -193,7 +194,7 @@ class RegularKeysStartWithAFactory : public IntTblPropCollectorFactory,
|
||||
}
|
||||
}
|
||||
virtual IntTblPropCollector* CreateIntTblPropCollector(
|
||||
uint32_t column_family_id) override {
|
||||
uint32_t /*column_family_id*/) override {
|
||||
return new RegularKeysStartWithAInternal();
|
||||
}
|
||||
const char* Name() const override { return "RegularKeysStartWithA"; }
|
||||
@@ -203,7 +204,7 @@ class RegularKeysStartWithAFactory : public IntTblPropCollectorFactory,
|
||||
|
||||
class FlushBlockEveryThreePolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
virtual bool Update(const Slice& key, const Slice& value) override {
|
||||
virtual bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
return (++count_ % 3U == 0);
|
||||
}
|
||||
|
||||
@@ -220,8 +221,8 @@ class FlushBlockEveryThreePolicyFactory : public FlushBlockPolicyFactory {
|
||||
}
|
||||
|
||||
FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& /*data_block_builder*/) const override {
|
||||
return new FlushBlockEveryThreePolicy;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -269,16 +269,18 @@ void TransactionLogIteratorImpl::UpdateCurrentWriteBatch(const Slice& record) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status PutCF(uint32_t cf, const Slice& key, const Slice& val) override {
|
||||
Status PutCF(uint32_t /*cf*/, const Slice& /*key*/,
|
||||
const Slice& /*val*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status DeleteCF(uint32_t cf, const Slice& key) override {
|
||||
Status DeleteCF(uint32_t /*cf*/, const Slice& /*key*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status SingleDeleteCF(uint32_t cf, const Slice& key) override {
|
||||
Status SingleDeleteCF(uint32_t /*cf*/, const Slice& /*key*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status MergeCF(uint32_t cf, const Slice& key, const Slice& val) override {
|
||||
Status MergeCF(uint32_t /*cf*/, const Slice& /*key*/,
|
||||
const Slice& /*val*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
Status MarkBeginPrepare() override { return Status::OK(); }
|
||||
|
||||
@@ -199,7 +199,7 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
|
||||
void CheckConsistencyForDeletes(VersionEdit* edit, uint64_t number,
|
||||
void CheckConsistencyForDeletes(VersionEdit* /*edit*/, uint64_t number,
|
||||
int level) {
|
||||
#ifdef NDEBUG
|
||||
if (!base_vstorage_->force_consistency_checks()) {
|
||||
|
||||
+1
-1
@@ -198,7 +198,7 @@ static bool GetInternalKey(Slice* input, InternalKey* dst) {
|
||||
}
|
||||
}
|
||||
|
||||
bool VersionEdit::GetLevel(Slice* input, int* level, const char** msg) {
|
||||
bool VersionEdit::GetLevel(Slice* input, int* level, const char** /*msg*/) {
|
||||
uint32_t v;
|
||||
if (GetVarint32(input, &v)) {
|
||||
*level = v;
|
||||
|
||||
+250
-119
@@ -424,129 +424,261 @@ bool SomeFileOverlapsRange(
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// 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 {
|
||||
class LevelIterator final : public InternalIterator {
|
||||
public:
|
||||
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),
|
||||
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),
|
||||
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) {}
|
||||
range_del_agg_(range_del_agg),
|
||||
pinned_iters_mgr_(nullptr) {
|
||||
// Empty level is not supported.
|
||||
assert(flevel_ != nullptr && flevel_->num_files > 0);
|
||||
}
|
||||
|
||||
InternalIterator* NewSecondaryIterator(const Slice& meta_handle) override {
|
||||
if (meta_handle.size() != sizeof(FileDescriptor)) {
|
||||
return NewErrorInternalIterator(
|
||||
Status::Corruption("FileReader invoked with unexpected value"));
|
||||
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();
|
||||
}
|
||||
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_);
|
||||
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();
|
||||
}
|
||||
|
||||
bool PrefixMayMatch(const Slice& internal_key) override {
|
||||
return true;
|
||||
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 KeyReachedUpperBound(const Slice& internal_key) override {
|
||||
bool KeyReachedUpperBound(const Slice& internal_key) {
|
||||
return read_options_.iterate_upper_bound != nullptr &&
|
||||
icomparator_.user_comparator()->Compare(
|
||||
ExtractUserKey(internal_key),
|
||||
*read_options_.iterate_upper_bound) >= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
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_);
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -759,6 +891,15 @@ void Version::GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta) {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Version::GetSstFilesSize() {
|
||||
uint64_t sst_files_size = 0;
|
||||
for (int level = 0; level < storage_info_.num_levels_; level++) {
|
||||
for (const auto& file_meta : storage_info_.LevelFiles(level)) {
|
||||
sst_files_size += file_meta->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
return sst_files_size;
|
||||
}
|
||||
|
||||
uint64_t VersionStorageInfo::GetEstimatedActiveKeys() const {
|
||||
// Estimation will be inaccurate when:
|
||||
@@ -854,24 +995,18 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
sample_file_read_inc(meta);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (storage_info_.LevelFilesBrief(level).num_files > 0) {
|
||||
// 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(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(
|
||||
auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
|
||||
merge_iter_builder->AddIterator(new (mem) LevelIterator(
|
||||
cfd_->table_cache(), read_options, soptions,
|
||||
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
|
||||
should_sample_file_read());
|
||||
merge_iter_builder->AddIterator(
|
||||
NewTwoLevelIterator(state, first_level_iter, arena, false));
|
||||
should_sample_file_read(),
|
||||
cfd_->internal_stats()->GetFileReadHist(level),
|
||||
false /* for_compaction */, IsFilterSkipped(level), level,
|
||||
range_del_agg));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1976,7 +2111,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
|
||||
@@ -2772,7 +2907,7 @@ void VersionSet::LogAndApplyCFHelper(VersionEdit* edit) {
|
||||
}
|
||||
|
||||
void VersionSet::LogAndApplyHelper(ColumnFamilyData* cfd,
|
||||
VersionBuilder* builder, Version* v,
|
||||
VersionBuilder* builder, Version* /*v*/,
|
||||
VersionEdit* edit, InstrumentedMutex* mu) {
|
||||
mu->AssertHeld();
|
||||
assert(!edit->IsColumnFamilyManipulation());
|
||||
@@ -3732,17 +3867,13 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
}
|
||||
} else {
|
||||
// Create concatenating iterator for the files from this level
|
||||
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 */));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -613,6 +613,8 @@ class Version {
|
||||
|
||||
void GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta);
|
||||
|
||||
uint64_t GetSstFilesSize();
|
||||
|
||||
private:
|
||||
Env* env_;
|
||||
friend class VersionSet;
|
||||
@@ -870,7 +872,7 @@ class VersionSet {
|
||||
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Status* status;
|
||||
virtual void Corruption(size_t bytes, const Status& s) override {
|
||||
virtual void Corruption(size_t /*bytes*/, const Status& s) override {
|
||||
if (this->status->ok()) *this->status = s;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -76,7 +76,9 @@ class CountingLogger : public Logger {
|
||||
public:
|
||||
CountingLogger() : log_count(0) {}
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* format, va_list ap) override { log_count++; }
|
||||
virtual void Logv(const char* /*format*/, va_list /*ap*/) override {
|
||||
log_count++;
|
||||
}
|
||||
int log_count;
|
||||
};
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ class WalManagerTest : public testing::Test {
|
||||
}
|
||||
|
||||
// NOT thread safe
|
||||
void RollTheLog(bool archived) {
|
||||
void RollTheLog(bool /*archived*/) {
|
||||
current_log_number_++;
|
||||
std::string fname = ArchivedLogFileName(dbname_, current_log_number_);
|
||||
unique_ptr<WritableFile> file;
|
||||
|
||||
+139
-33
@@ -186,7 +186,7 @@ WriteBatch::~WriteBatch() { delete save_points_; }
|
||||
|
||||
WriteBatch::Handler::~Handler() { }
|
||||
|
||||
void WriteBatch::Handler::LogData(const Slice& blob) {
|
||||
void WriteBatch::Handler::LogData(const Slice& /*blob*/) {
|
||||
// If the user has not specified something to do with blobs, then we ignore
|
||||
// them.
|
||||
}
|
||||
@@ -402,14 +402,22 @@ Status WriteBatch::Iterate(Handler* handler) const {
|
||||
bool empty_batch = true;
|
||||
int found = 0;
|
||||
Status s;
|
||||
while (s.ok() && !input.empty() && handler->Continue()) {
|
||||
char tag = 0;
|
||||
uint32_t column_family = 0; // default
|
||||
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
|
||||
|
||||
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
|
||||
&blob, &xid);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
s = ReadRecordFromWriteBatch(&input, &tag, &column_family, &key, &value,
|
||||
&blob, &xid);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
assert(s.IsTryAgain());
|
||||
s = Status::OK();
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
@@ -418,47 +426,59 @@ 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);
|
||||
empty_batch = false;
|
||||
found++;
|
||||
if (LIKELY(s.ok())) {
|
||||
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);
|
||||
empty_batch = false;
|
||||
found++;
|
||||
if (LIKELY(s.ok())) {
|
||||
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);
|
||||
empty_batch = false;
|
||||
found++;
|
||||
if (LIKELY(s.ok())) {
|
||||
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);
|
||||
empty_batch = false;
|
||||
found++;
|
||||
if (LIKELY(s.ok())) {
|
||||
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);
|
||||
empty_batch = false;
|
||||
found++;
|
||||
if (LIKELY(s.ok())) {
|
||||
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);
|
||||
found++;
|
||||
if (LIKELY(s.ok())) {
|
||||
found++;
|
||||
}
|
||||
break;
|
||||
case kTypeLogData:
|
||||
handler->LogData(blob);
|
||||
@@ -549,12 +569,19 @@ void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) {
|
||||
EncodeFixed64(&b->rep_[0], seq);
|
||||
}
|
||||
|
||||
size_t WriteBatchInternal::GetFirstOffset(WriteBatch* b) {
|
||||
size_t WriteBatchInternal::GetFirstOffset(WriteBatch* /*b*/) {
|
||||
return WriteBatchInternal::kHeader;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -577,8 +604,33 @@ 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) {
|
||||
@@ -795,6 +847,13 @@ 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) {
|
||||
@@ -820,6 +879,11 @@ 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) {
|
||||
@@ -1084,12 +1148,23 @@ 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) {
|
||||
mem->Add(sequence_, value_type, key, value, concurrent_memtable_writes_,
|
||||
get_post_process_info(mem));
|
||||
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);
|
||||
}
|
||||
} else if (moptions->inplace_callback == nullptr) {
|
||||
assert(!concurrent_memtable_writes_);
|
||||
mem->Update(sequence_, key, value);
|
||||
@@ -1125,11 +1200,17 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
value, &merged_value);
|
||||
if (status == UpdateStatus::UPDATED_INPLACE) {
|
||||
// prev_value is updated in-place with final value.
|
||||
mem->Add(sequence_, value_type, key, Slice(prev_buffer, prev_size));
|
||||
bool mem_res __attribute__((__unused__));
|
||||
mem_res = mem->Add(
|
||||
sequence_, value_type, key, Slice(prev_buffer, prev_size));
|
||||
assert(mem_res);
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
} else if (status == UpdateStatus::UPDATED) {
|
||||
// merged_value contains the final value.
|
||||
mem->Add(sequence_, value_type, key, Slice(merged_value));
|
||||
bool mem_res __attribute__((__unused__));
|
||||
mem_res =
|
||||
mem->Add(sequence_, value_type, key, Slice(merged_value));
|
||||
assert(mem_res);
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +1220,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// in memtable add/update.
|
||||
MaybeAdvanceSeq();
|
||||
CheckMemtableFull();
|
||||
return Status::OK();
|
||||
return ret_status;
|
||||
}
|
||||
|
||||
virtual Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
@@ -1147,14 +1228,22 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return PutCFImpl(column_family_id, key, value, kTypeValue);
|
||||
}
|
||||
|
||||
Status DeleteImpl(uint32_t column_family_id, const Slice& key,
|
||||
Status DeleteImpl(uint32_t /*column_family_id*/, const Slice& key,
|
||||
const Slice& value, ValueType delete_type) {
|
||||
Status ret_status;
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
mem->Add(sequence_, delete_type, key, value, concurrent_memtable_writes_,
|
||||
get_post_process_info(mem));
|
||||
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);
|
||||
}
|
||||
MaybeAdvanceSeq();
|
||||
CheckMemtableFull();
|
||||
return Status::OK();
|
||||
return ret_status;
|
||||
}
|
||||
|
||||
virtual Status DeleteCF(uint32_t column_family_id,
|
||||
@@ -1246,6 +1335,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return seek_status;
|
||||
}
|
||||
|
||||
Status ret_status;
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
auto* moptions = mem->GetImmutableMemTableOptions();
|
||||
bool perform_merge = false;
|
||||
@@ -1301,18 +1391,30 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
perform_merge = false;
|
||||
} else {
|
||||
// 3) Add value to memtable
|
||||
mem->Add(sequence_, kTypeValue, key, new_value);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!perform_merge) {
|
||||
// Add merge operator to memtable
|
||||
mem->Add(sequence_, kTypeMerge, key, value);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
MaybeAdvanceSeq();
|
||||
CheckMemtableFull();
|
||||
return Status::OK();
|
||||
return ret_status;
|
||||
}
|
||||
|
||||
virtual Status PutBlobIndexCF(uint32_t column_family_id, const Slice& key,
|
||||
@@ -1496,6 +1598,8 @@ 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();
|
||||
}
|
||||
@@ -1504,7 +1608,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) {
|
||||
bool concurrent_memtable_writes, bool seq_per_batch, size_t batch_cnt) {
|
||||
assert(writer->ShouldWriteToMemtable());
|
||||
MemTableInserter inserter(sequence, memtables, flush_scheduler,
|
||||
ignore_missing_column_families, log_number, db,
|
||||
@@ -1513,6 +1617,8 @@ 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();
|
||||
}
|
||||
|
||||
Loaded 100 of 345 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user