mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
764bfcd19b | ||
|
|
1fdb34fe99 | ||
|
|
5d2b9971e8 | ||
|
|
e8830cab4b | ||
|
|
73d59f235e | ||
|
|
91b2c6d1f4 | ||
|
|
f445eadba6 | ||
|
|
f438b98e55 | ||
|
|
fb4063749b | ||
|
|
05c9d53a40 | ||
|
|
832ab1424d | ||
|
|
516c7df05d | ||
|
|
bb3b2eb960 | ||
|
|
ae5305ee70 | ||
|
|
f37ea82191 | ||
|
|
c954445ac8 | ||
|
|
619f754816 | ||
|
|
f81fe96eea | ||
|
|
7d3eb006ff | ||
|
|
02d47c1ff4 | ||
|
|
0f17fb9622 | ||
|
|
9552660ecc | ||
|
|
016437d1cd | ||
|
|
b6b72687c6 |
No files matched your search
+2
-5
@@ -71,10 +71,7 @@ install:
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
mkdir cmake-dist && curl --silent --fail --show-error --location https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == java_test ]]; then
|
||||
java -version && echo "JAVA_HOME=${JAVA_HOME}";
|
||||
mkdir cmake-dist && curl -sfSL https://cmake.org/files/v3.8/cmake-3.8.1-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
@@ -93,7 +90,7 @@ script:
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=db_block_cache_test ROCKSDBTESTS_END=full_filter_block_test make -j4 check_some
|
||||
;;
|
||||
2)
|
||||
OPT=-DTRAVIS V=1 make -j4 tools && OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=full_filter_block_test ROCKSDBTESTS_END=write_batch_with_index_test make -j4 check_some
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=full_filter_block_test ROCKSDBTESTS_END=write_batch_with_index_test make -j4 check_some
|
||||
;;
|
||||
3)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=write_batch_with_index_test ROCKSDBTESTS_END=write_prepared_transaction_test make -j4 check_some
|
||||
|
||||
+7
-16
@@ -49,10 +49,6 @@ option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
option(WITH_LZ4 "build with lz4" OFF)
|
||||
option(WITH_ZLIB "build with zlib" OFF)
|
||||
option(WITH_ZSTD "build with zstd" OFF)
|
||||
option(WITH_WINDOWS_UTF8_FILENAMES "use UTF8 as characterset for opening files, regardles of the system code page" OFF)
|
||||
if (WITH_WINDOWS_UTF8_FILENAMES)
|
||||
add_definitions(-DROCKSDB_WINDOWS_UTF8_FILENAMES)
|
||||
endif()
|
||||
if(MSVC)
|
||||
# Defaults currently different for GFLAGS.
|
||||
# We will address find_package work a little later
|
||||
@@ -366,7 +362,7 @@ endif()
|
||||
option(ROCKSDB_LITE "Build RocksDBLite version" OFF)
|
||||
if(ROCKSDB_LITE)
|
||||
add_definitions(-DROCKSDB_LITE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -Os")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Cygwin")
|
||||
@@ -471,7 +467,6 @@ set(SOURCES
|
||||
db/compaction_iterator.cc
|
||||
db/compaction_job.cc
|
||||
db/compaction_picker.cc
|
||||
db/compaction_picker_fifo.cc
|
||||
db/compaction_picker_universal.cc
|
||||
db/convenience.cc
|
||||
db/db_filesnapshot.cc
|
||||
@@ -504,7 +499,6 @@ set(SOURCES
|
||||
db/merge_helper.cc
|
||||
db/merge_operator.cc
|
||||
db/range_del_aggregator.cc
|
||||
db/range_tombstone_fragmenter.cc
|
||||
db/repair.cc
|
||||
db/snapshot_impl.cc
|
||||
db/table_cache.cc
|
||||
@@ -578,7 +572,6 @@ set(SOURCES
|
||||
table/plain_table_index.cc
|
||||
table/plain_table_key_coding.cc
|
||||
table/plain_table_reader.cc
|
||||
table/sst_file_reader.cc
|
||||
table/sst_file_writer.cc
|
||||
table/table_properties.cc
|
||||
table/two_level_iterator.cc
|
||||
@@ -605,7 +598,6 @@ set(SOURCES
|
||||
util/filename.cc
|
||||
util/filter_policy.cc
|
||||
util/hash.cc
|
||||
util/jemalloc_nodump_allocator.cc
|
||||
util/log_buffer.cc
|
||||
util/murmurhash.cc
|
||||
util/random.cc
|
||||
@@ -687,10 +679,12 @@ set(SOURCES
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc
|
||||
$<TARGET_OBJECTS:build_version>)
|
||||
|
||||
if(HAVE_SSE42 AND NOT MSVC)
|
||||
set_source_files_properties(
|
||||
util/crc32c.cc
|
||||
PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
|
||||
if(HAVE_SSE42 AND NOT FORCE_SSE42)
|
||||
if(NOT MSVC)
|
||||
set_source_files_properties(
|
||||
util/crc32c.cc
|
||||
PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_POWER8)
|
||||
@@ -905,8 +899,6 @@ if(WITH_TESTS)
|
||||
db/perf_context_test.cc
|
||||
db/plain_table_db_test.cc
|
||||
db/prefix_test.cc
|
||||
db/range_del_aggregator_test.cc
|
||||
db/range_tombstone_fragmenter_test.cc
|
||||
db/repair_test.cc
|
||||
db/table_properties_collector_test.cc
|
||||
db/version_builder_test.cc
|
||||
@@ -935,7 +927,6 @@ if(WITH_TESTS)
|
||||
table/data_block_hash_index_test.cc
|
||||
table/full_filter_block_test.cc
|
||||
table/merger_test.cc
|
||||
table/sst_file_reader_test.cc
|
||||
table/table_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
|
||||
+14
-43
@@ -1,55 +1,26 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.18.4 (3/3/2020)
|
||||
* Various fixes for ARM64 support (#6250)
|
||||
* Fix JEMALLOC_CXX_THROW macro missing from older Jemalloc versions, causing build failures on some platforms.
|
||||
|
||||
## 5.18.3 (2/11/2019)
|
||||
## 5.17.4 (12/05/2018)
|
||||
### Bug Fixes
|
||||
* Fix possible LSM corruption when both range deletions and subcompactions are used. The symptom of this corruption is L1+ files overlapping in the user key space.
|
||||
|
||||
## 5.18.2 (01/31/2019)
|
||||
|
||||
### Public API Change
|
||||
* Change time resolution in FileOperationInfo.
|
||||
* Deleting Blob files also go through SStFileManager.
|
||||
|
||||
## 5.18.0 (11/30/2018)
|
||||
### New Features
|
||||
* Introduced `JemallocNodumpAllocator` memory allocator. When being use, block cache will be excluded from core dump.
|
||||
* Introduced `PerfContextByLevel` as part of `PerfContext` which allows storing perf context at each level. Also replaced `__thread` with `thread_local` keyword for perf_context. Added per-level perf context for bloom filter and `Get` query.
|
||||
* With level_compaction_dynamic_level_bytes = true, level multiplier may be adjusted automatically when Level 0 to 1 compaction is lagged behind.
|
||||
* Introduced DB option `atomic_flush`. If true, RocksDB supports flushing multiple column families and atomically committing the result to MANIFEST. Useful when WAL is disabled.
|
||||
* Added `num_deletions` and `num_merge_operands` members to `TableProperties`.
|
||||
* Added "rocksdb.min-obsolete-sst-number-to-keep" DB property that reports the lower bound on SST file numbers that are being kept from deletion, even if the SSTs are obsolete.
|
||||
* Add xxhash64 checksum support
|
||||
* Introduced `MemoryAllocator`, which lets the user specify custom memory allocator for block based table.
|
||||
* Improved `DeleteRange` to prevent read performance degradation. The feature is no longer marked as experimental.
|
||||
* Enabled checkpoint on readonly db (DBImplReadOnly).
|
||||
|
||||
### Public API Change
|
||||
* `DBOptions::use_direct_reads` now affects reads issued by `BackupEngine` on the database's SSTs.
|
||||
* `NO_ITERATORS` is divided into two counters `NO_ITERATOR_CREATED` and `NO_ITERATOR_DELETE`. Both of them are only increasing now, just as other counters.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix corner case where a write group leader blocked due to write stall blocks other writers in queue with WriteOptions::no_slowdown set.
|
||||
* Fix in-memory range tombstone truncation to avoid erroneously covering newer keys at a lower level, and include range tombstones in compacted files whose largest key is the range tombstone's start key.
|
||||
* Properly set the stop key for a truncated manual CompactRange
|
||||
* Fix slow flush/compaction when DB contains many snapshots. The problem became noticeable to us in DBs with 100,000+ snapshots, though it will affect others at different thresholds.
|
||||
* Fix the bug that WriteBatchWithIndex's SeekForPrev() doesn't see the entries with the same key.
|
||||
* Fix the bug where user comparator was sometimes fed with InternalKey instead of the user key. The bug manifests when during GenerateBottommostFiles.
|
||||
* Fix a bug in WritePrepared txns where if the number of old snapshots goes beyond the snapshot cache size (128 default) the rest will not be checked when evicting a commit entry from the commit cache.
|
||||
* Fixed Get correctness bug in the presence of range tombstones where merge operands covered by a range tombstone always result in NotFound.
|
||||
* Start populating `NO_FILE_CLOSES` ticker statistic, which was always zero previously.
|
||||
* The default value of NewBloomFilterPolicy()'s argument use_block_based_builder is changed to false. Note that this new default may cause large temp memory usage when building very large SST files.
|
||||
* Fix a deadlock caused by compaction and file ingestion waiting for each other in the event of write stalls.
|
||||
* Make DB ignore dropped column families while committing results of atomic flush.
|
||||
|
||||
# 5.17.2 (10/24/2018)
|
||||
### Bug Fixes
|
||||
* Fix the bug that WriteBatchWithIndex's SeekForPrev() doesn't see the entries with the same key.
|
||||
|
||||
# 5.17.1 (10/16/2018)
|
||||
### Bug Fixes
|
||||
* Fix slow flush/compaction when DB contains many snapshots. The problem became noticeable to us in DBs with 100,000+ snapshots, though it will affect others at different thresholds.
|
||||
* Properly set the stop key for a truncated manual CompactRange
|
||||
* Fix corner case where a write group leader blocked due to write stall blocks other writers in queue with WriteOptions::no_slowdown set.
|
||||
|
||||
### New Features
|
||||
* Introduced CacheAllocator, which lets the user specify custom allocator for memory in block cache.
|
||||
|
||||
## 5.17.0 (10/05/2018)
|
||||
### Public API Change
|
||||
* `OnTableFileCreated` will now be called for empty files generated during compaction. In that case, `TableFileCreationInfo::file_path` will be "(nil)" and `TableFileCreationInfo::file_size` will be zero.
|
||||
* Add `FlushOptions::allow_write_stall`, which controls whether Flush calls start working immediately, even if it causes user writes to stall, or will wait until flush can be performed without causing write stall (similar to `CompactRangeOptions::allow_write_stall`). Note that the default value is false, meaning we add delay to Flush calls until stalling can be avoided when possible. This is behavior change compared to previous RocksDB versions, where Flush calls didn't check if they might cause stall or not.
|
||||
* Application using PessimisticTransactionDB is expected to rollback/commit recovered transactions before starting new ones. This assumption is used to skip concurrency control during recovery.
|
||||
* Expose column family id to `OnCompactionCompleted`.
|
||||
|
||||
### New Features
|
||||
* TransactionOptions::skip_concurrency_control allows pessimistic transactions to skip the overhead of concurrency control. Could be used for optimizing certain transactions or during recovery.
|
||||
|
||||
@@ -43,8 +43,6 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
command line flags processing. You can compile rocksdb library even
|
||||
if you don't have gflags installed.
|
||||
|
||||
* If you wish to build the RocksJava static target, then cmake is required for building Snappy.
|
||||
|
||||
## Supported platforms
|
||||
|
||||
* **Linux - Ubuntu**
|
||||
|
||||
@@ -93,38 +93,19 @@ ifeq ($(MAKECMDGOALS),rocksdbjavastaticpublish)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
# Lite build flag.
|
||||
LITE ?= 0
|
||||
ifeq ($(LITE), 0)
|
||||
ifneq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
# Be backward compatible and support older format where OPT=-DROCKSDB_LITE is
|
||||
# specified instead of LITE=1 on the command line.
|
||||
LITE=1
|
||||
endif
|
||||
else ifeq ($(LITE), 1)
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
OPT += -DROCKSDB_LITE
|
||||
endif
|
||||
endif
|
||||
|
||||
# Figure out optimize level.
|
||||
ifneq ($(DEBUG_LEVEL), 2)
|
||||
ifeq ($(LITE), 0)
|
||||
OPT += -O2
|
||||
else
|
||||
OPT += -Os
|
||||
endif
|
||||
endif
|
||||
|
||||
# compile with -O2 if debug level is not 2
|
||||
ifneq ($(DEBUG_LEVEL), 2)
|
||||
OPT += -fno-omit-frame-pointer
|
||||
OPT += -O2 -fno-omit-frame-pointer
|
||||
# Skip for archs that don't support -momit-leaf-frame-pointer
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -momit-leaf-frame-pointer -xc /dev/null 2>&1))
|
||||
OPT += -momit-leaf-frame-pointer
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -faligned-new -xc++ /dev/null 2>&1))
|
||||
CXXFLAGS += -faligned-new -DHAVE_ALIGNED_NEW
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -maltivec -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -DHAS_ALTIVEC
|
||||
CFLAGS += -DHAS_ALTIVEC
|
||||
@@ -137,12 +118,6 @@ CFLAGS += -DHAVE_POWER8
|
||||
HAVE_POWER8=1
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -march=armv8-a+crc -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -march=armv8-a+crc
|
||||
CFLAGS += -march=armv8-a+crc
|
||||
ARMCRC_SOURCE=1
|
||||
endif
|
||||
|
||||
# if we're compiling for release, compile without debug code (-DNDEBUG)
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
OPT += -DNDEBUG
|
||||
@@ -347,7 +322,7 @@ endif
|
||||
ifeq ("$(wildcard $(LUA_LIB))", "") # LUA_LIB does not exist
|
||||
$(error $(LUA_LIB) does not exist. Try to specify both LUA_PATH and LUA_LIB manually)
|
||||
endif
|
||||
EXEC_LDFLAGS += $(LUA_LIB)
|
||||
LDFLAGS += $(LUA_LIB)
|
||||
|
||||
endif
|
||||
|
||||
@@ -549,6 +524,7 @@ TESTS = \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
lua_test \
|
||||
range_del_aggregator_test \
|
||||
lru_cache_test \
|
||||
object_registry_test \
|
||||
repair_test \
|
||||
@@ -558,9 +534,6 @@ TESTS = \
|
||||
db_universal_compaction_test \
|
||||
trace_analyzer_test \
|
||||
repeatable_thread_test \
|
||||
range_tombstone_fragmenter_test \
|
||||
range_del_aggregator_test \
|
||||
sst_file_reader_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -911,7 +884,6 @@ crash_test: whitebox_crash_test blackbox_crash_test
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
@@ -920,8 +892,6 @@ endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -1590,12 +1560,6 @@ blob_db_test: utilities/blob_db/blob_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
repeatable_thread_test: util/repeatable_thread_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
range_tombstone_fragmenter_test: db/range_tombstone_fragmenter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sst_file_reader_test: table/sst_file_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
@@ -1641,7 +1605,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
else ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ifneq (,$(filter $(MACHINE), amd64 arm64 sparc64))
|
||||
ARCH := 64
|
||||
else
|
||||
ARCH := 32
|
||||
@@ -1650,10 +1614,10 @@ else
|
||||
ARCH := $(shell getconf LONG_BIT)
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ppc% arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
ifeq (,$(findstring ppc,$(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
endif
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux$(ARCH).jar
|
||||
ROCKSDB_JAR_ALL = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
@@ -1666,15 +1630,15 @@ ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.6
|
||||
BZIP2_SHA256 ?= a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
|
||||
BZIP2_DOWNLOAD_BASE ?= https://downloads.sourceforge.net/project/bzip2
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.2
|
||||
LZ4_SHA256 ?= 658ba6191fa44c92280d4aa2c271b0f4fbc0e34d249578dd05e50e76d0e5efcc
|
||||
BZIP2_DOWNLOAD_BASE ?= https://web.archive.org/web/20180624184835/http://www.bzip.org
|
||||
SNAPPY_VER ?= 1.1.4
|
||||
SNAPPY_SHA256 ?= 134bfe122fd25599bb807bb8130e7ba6d9bdb851e0b16efcb83ac4f5d0b70057
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/releases/download
|
||||
LZ4_VER ?= 1.8.0
|
||||
LZ4_SHA256 ?= 2ca482ea7a9bb103603108b5a7510b7592b90158c151ff50a28f1ca8389fccf6
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.4.4
|
||||
ZSTD_SHA256 ?= a364f5162c7d1a455cc915e8e3cf5f4bd8b75d09bc0f53965b0c9ca1383c52c8
|
||||
ZSTD_VER ?= 1.3.3
|
||||
ZSTD_SHA256 ?= a77c47153ee7de02626c5b2a097005786b71688be61e9fb81806a011f90b297b
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -1713,9 +1677,7 @@ endif
|
||||
|
||||
libz.a:
|
||||
-rm -rf zlib-$(ZLIB_VER)
|
||||
ifeq (,$(wildcard ./zlib-$(ZLIB_VER).tar.gz))
|
||||
curl --fail --output zlib-$(ZLIB_VER).tar.gz --location ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
endif
|
||||
curl -O -L ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZLIB_SHA256)" != "$$ZLIB_SHA256_ACTUAL" ]; then \
|
||||
echo zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected=\"$(ZLIB_SHA256)\" actual=\"$$ZLIB_SHA256_ACTUAL\"; \
|
||||
@@ -1727,9 +1689,7 @@ endif
|
||||
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-$(BZIP2_VER)
|
||||
ifeq (,$(wildcard ./bzip2-$(BZIP2_VER).tar.gz))
|
||||
curl --fail --output bzip2-$(BZIP2_VER).tar.gz --location ${CURL_SSL_OPTS} ${BZIP2_DOWNLOAD_BASE}/bzip2-$(BZIP2_VER).tar.gz
|
||||
endif
|
||||
curl -O -L ${BZIP2_DOWNLOAD_BASE}/$(BZIP2_VER)/bzip2-$(BZIP2_VER).tar.gz
|
||||
BZIP2_SHA256_ACTUAL=`$(SHA256_CMD) bzip2-$(BZIP2_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(BZIP2_SHA256)" != "$$BZIP2_SHA256_ACTUAL" ]; then \
|
||||
echo bzip2-$(BZIP2_VER).tar.gz checksum mismatch, expected=\"$(BZIP2_SHA256)\" actual=\"$$BZIP2_SHA256_ACTUAL\"; \
|
||||
@@ -1741,24 +1701,21 @@ endif
|
||||
|
||||
libsnappy.a:
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
ifeq (,$(wildcard ./snappy-$(SNAPPY_VER).tar.gz))
|
||||
curl --fail --output snappy-$(SNAPPY_VER).tar.gz --location ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER).tar.gz
|
||||
endif
|
||||
curl -O -L ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER)/snappy-$(SNAPPY_VER).tar.gz
|
||||
SNAPPY_SHA256_ACTUAL=`$(SHA256_CMD) snappy-$(SNAPPY_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(SNAPPY_SHA256)" != "$$SNAPPY_SHA256_ACTUAL" ]; then \
|
||||
echo snappy-$(SNAPPY_VER).tar.gz checksum mismatch, expected=\"$(SNAPPY_SHA256)\" actual=\"$$SNAPPY_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
mkdir snappy-$(SNAPPY_VER)/build
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='${EXTRA_CFLAGS}' CXXFLAGS='${EXTRA_CXXFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
|
||||
cd snappy-$(SNAPPY_VER) && CFLAGS='${EXTRA_CFLAGS}' CXXFLAGS='${EXTRA_CXXFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' ./configure --with-pic --enable-static --disable-shared
|
||||
cd snappy-$(SNAPPY_VER) && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/.libs/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
-rm -rf lz4-$(LZ4_VER)
|
||||
ifeq (,$(wildcard ./lz4-$(LZ4_VER).tar.gz))
|
||||
curl --fail --output lz4-$(LZ4_VER).tar.gz --location ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
|
||||
endif
|
||||
curl -O -L ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
|
||||
mv v$(LZ4_VER).tar.gz lz4-$(LZ4_VER).tar.gz
|
||||
LZ4_SHA256_ACTUAL=`$(SHA256_CMD) lz4-$(LZ4_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(LZ4_SHA256)" != "$$LZ4_SHA256_ACTUAL" ]; then \
|
||||
echo lz4-$(LZ4_VER).tar.gz checksum mismatch, expected=\"$(LZ4_SHA256)\" actual=\"$$LZ4_SHA256_ACTUAL\"; \
|
||||
@@ -1770,9 +1727,8 @@ endif
|
||||
|
||||
libzstd.a:
|
||||
-rm -rf zstd-$(ZSTD_VER)
|
||||
ifeq (,$(wildcard ./zstd-$(ZSTD_VER).tar.gz))
|
||||
curl --fail --output zstd-$(ZSTD_VER).tar.gz --location ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(ZSTD_VER).tar.gz
|
||||
endif
|
||||
curl -O -L ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(ZSTD_VER).tar.gz
|
||||
mv v$(ZSTD_VER).tar.gz zstd-$(ZSTD_VER).tar.gz
|
||||
ZSTD_SHA256_ACTUAL=`$(SHA256_CMD) zstd-$(ZSTD_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZSTD_SHA256)" != "$$ZSTD_SHA256_ACTUAL" ]; then \
|
||||
echo zstd-$(ZSTD_VER).tar.gz checksum mismatch, expected=\"$(ZSTD_SHA256)\" actual=\"$$ZSTD_SHA256_ACTUAL\"; \
|
||||
@@ -1863,14 +1819,6 @@ rocksdbjavastaticdockerppc64le:
|
||||
fi
|
||||
docker start -a rocksdb_linux_ppc64le-be
|
||||
|
||||
rocksdbjavastaticdockerarm64v8:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_ARM64V8_CONTAINER=`docker ps -aqf name=rocksdb_linux_arm64v8-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_ARM64V8_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_arm64v8-be evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_arm64v8-be
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
rocksdbjavastaticpublishdocker: rocksdbjavastaticreleasedocker rocksdbjavastaticpublishcentral
|
||||
|
||||
@@ -67,14 +67,6 @@ is_opt_mode = build_mode.startswith("opt")
|
||||
if is_opt_mode:
|
||||
rocksdb_compiler_flags.append("-DNDEBUG")
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
if sanitizer == "":
|
||||
rocksdb_compiler_flags.append("-DROCKSDB_JEMALLOC")
|
||||
rocksdb_external_deps.append(("jemalloc", None, "headers"))
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
srcs = [
|
||||
@@ -89,7 +81,6 @@ cpp_library(
|
||||
"db/compaction_iterator.cc",
|
||||
"db/compaction_job.cc",
|
||||
"db/compaction_picker.cc",
|
||||
"db/compaction_picker_fifo.cc",
|
||||
"db/compaction_picker_universal.cc",
|
||||
"db/convenience.cc",
|
||||
"db/db_filesnapshot.cc",
|
||||
@@ -122,7 +113,6 @@ cpp_library(
|
||||
"db/merge_helper.cc",
|
||||
"db/merge_operator.cc",
|
||||
"db/range_del_aggregator.cc",
|
||||
"db/range_tombstone_fragmenter.cc",
|
||||
"db/repair.cc",
|
||||
"db/snapshot_impl.cc",
|
||||
"db/table_cache.cc",
|
||||
@@ -200,7 +190,6 @@ cpp_library(
|
||||
"table/plain_table_index.cc",
|
||||
"table/plain_table_key_coding.cc",
|
||||
"table/plain_table_reader.cc",
|
||||
"table/sst_file_reader.cc",
|
||||
"table/sst_file_writer.cc",
|
||||
"table/table_properties.cc",
|
||||
"table/two_level_iterator.cc",
|
||||
@@ -226,7 +215,6 @@ cpp_library(
|
||||
"util/filename.cc",
|
||||
"util/filter_policy.cc",
|
||||
"util/hash.cc",
|
||||
"util/jemalloc_nodump_allocator.cc",
|
||||
"util/log_buffer.cc",
|
||||
"util/murmurhash.cc",
|
||||
"util/random.cc",
|
||||
@@ -932,11 +920,6 @@ ROCKS_TESTS = [
|
||||
"db/range_del_aggregator_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"range_tombstone_fragmenter_test",
|
||||
"db/range_tombstone_fragmenter_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"rate_limiter_test",
|
||||
"util/rate_limiter_test.cc",
|
||||
@@ -982,11 +965,6 @@ ROCKS_TESTS = [
|
||||
"tools/sst_dump_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"sst_file_reader_test",
|
||||
"table/sst_file_reader_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"statistics_test",
|
||||
"monitoring/statistics_test.cc",
|
||||
|
||||
+5
-65
@@ -1,75 +1,15 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
|
||||
THIRDPARTY_HOME: $(APPVEYOR_BUILD_FOLDER)\thirdparty
|
||||
SNAPPY_HOME: $(THIRDPARTY_HOME)\snappy-1.1.7
|
||||
SNAPPY_INCLUDE: $(SNAPPY_HOME);$(SNAPPY_HOME)\build
|
||||
SNAPPY_LIB_DEBUG: $(SNAPPY_HOME)\build\Debug\snappy.lib
|
||||
SNAPPY_LIB_RELEASE: $(SNAPPY_HOME)\build\Release\snappy.lib
|
||||
LZ4_HOME: $(THIRDPARTY_HOME)\lz4-1.8.3
|
||||
LZ4_INCLUDE: $(LZ4_HOME)\lib
|
||||
LZ4_LIB_DEBUG: $(LZ4_HOME)\visual\VS2010\bin\x64_Debug\liblz4_static.lib
|
||||
LZ4_LIB_RELEASE: $(LZ4_HOME)\visual\VS2010\bin\x64_Release\liblz4_static.lib
|
||||
ZSTD_HOME: $(THIRDPARTY_HOME)\zstd-1.4.0
|
||||
ZSTD_INCLUDE: $(ZSTD_HOME)\lib;$(ZSTD_HOME)\lib\dictBuilder
|
||||
ZSTD_LIB_DEBUG: $(ZSTD_HOME)\build\VS2010\bin\x64_Debug\libzstd_static.lib
|
||||
ZSTD_LIB_RELEASE: $(ZSTD_HOME)\build\VS2010\bin\x64_Release\libzstd_static.lib
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CMAKE_GENERATOR: Visual Studio 14 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.com
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
- echo "Building Snappy dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output snappy-1.1.7.zip --location https://github.com/google/snappy/archive/1.1.7.zip
|
||||
- unzip snappy-1.1.7.zip
|
||||
- cd snappy-1.1.7
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -G "%CMAKE_GENERATOR%" ..
|
||||
- msbuild Snappy.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild Snappy.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building LZ4 dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output lz4-1.8.3.zip --location https://github.com/lz4/lz4/archive/v1.8.3.zip
|
||||
- unzip lz4-1.8.3.zip
|
||||
- cd lz4-1.8.3\visual\VS2010
|
||||
- ps: $CMD="$Env:DEV_ENV"; & $CMD lz4.sln /upgrade
|
||||
- msbuild lz4.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild lz4.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building ZStd dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output zstd-1.4.0.zip --location https://github.com/facebook/zstd/archive/v1.4.0.zip
|
||||
- unzip zstd-1.4.0.zip
|
||||
- cd zstd-1.4.0\build\VS2010
|
||||
- ps: $CMD="$Env:DEV_ENV"; & $CMD zstd.sln /upgrade
|
||||
- msbuild zstd.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild zstd.sln /p:Configuration=Release /p:Platform=x64
|
||||
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
parallel: true
|
||||
verbosity: normal
|
||||
|
||||
test:
|
||||
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test,db_merge_operand_test -Concurrency 8
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a build-failed.zip %APPVEYOR_BUILD_FOLDER%\build\ && appveyor PushArtifact build-failed.zip
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
@@ -70,14 +70,6 @@ is_opt_mode = build_mode.startswith("opt")
|
||||
# doesn't harm and avoid forgetting to add it.
|
||||
if is_opt_mode:
|
||||
rocksdb_compiler_flags.append("-DNDEBUG")
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
if sanitizer == "":
|
||||
rocksdb_compiler_flags.append("-DROCKSDB_JEMALLOC")
|
||||
rocksdb_external_deps.append(("jemalloc", None, "headers"))
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
// Name of the environment variables which need to be set by the entity which
|
||||
// triggers continuous runs so that code at the end of the file gets executed
|
||||
// and Sandcastle run starts.
|
||||
const ENV_POST_RECEIVE_HOOK = "POST_RECEIVE_HOOK";
|
||||
const ENV_HTTPS_APP_VALUE = "HTTPS_APP_VALUE";
|
||||
const ENV_HTTPS_TOKEN_VALUE = "HTTPS_TOKEN_VALUE";
|
||||
define("ENV_POST_RECEIVE_HOOK", "POST_RECEIVE_HOOK");
|
||||
define("ENV_HTTPS_APP_VALUE", "HTTPS_APP_VALUE");
|
||||
define("ENV_HTTPS_TOKEN_VALUE", "HTTPS_TOKEN_VALUE");
|
||||
|
||||
const PRIMARY_TOKEN_FILE = '/home/krad/.sandcastle';
|
||||
const CONT_RUN_ALIAS = "leveldb";
|
||||
define("PRIMARY_TOKEN_FILE", '/home/krad/.sandcastle');
|
||||
define("CONT_RUN_ALIAS", "leveldb");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* Run tests in sandcastle */
|
||||
|
||||
@@ -476,17 +476,6 @@ EOF
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_SCHED_GETCPU_PRESENT"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ALIGNED_NEW; then
|
||||
# Test whether c++17 aligned-new is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -faligned-new -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
struct alignas(1024) t {int a;};
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS -faligned-new -DHAVE_ALIGNED_NEW"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
@@ -527,8 +516,6 @@ if test -z "$PORTABLE"; then
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^arm`"; then
|
||||
# TODO: Handle this with approprite options.
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^aarch64`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" != "AIX" ] && [ "$TARGET_OS" != "SunOS" ]; then
|
||||
|
||||
@@ -5082,8 +5082,8 @@ sub openoutputfiles {
|
||||
# Set reading FD if using --group (--ungroup does not need)
|
||||
for my $fdno (1,2) {
|
||||
# Re-open the file for reading
|
||||
# so fdw can be closed separately
|
||||
# and fdr can be seeked separately (for --line-buffer)
|
||||
# so fdw can be closed seperately
|
||||
# and fdr can be seeked seperately (for --line-buffer)
|
||||
open(my $fdr,"<", $self->fh($fdno,'name')) ||
|
||||
::die_bug("fdr: Cannot open ".$self->fh($fdno,'name'));
|
||||
$self->set_fh($fdno,'r',$fdr);
|
||||
|
||||
@@ -377,7 +377,7 @@ function send_to_ods {
|
||||
echo >&2 "ERROR: Key $key doesn't have a value."
|
||||
return
|
||||
fi
|
||||
curl --silent "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build$git_br&key=$key&value=$value" \
|
||||
curl -s "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build$git_br&key=$key&value=$value" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
|
||||
@@ -346,7 +346,7 @@ LITE_BUILD_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'make J=1 LITE=1 all check || $CONTRUN_NAME=lite $TASK_CREATION_TOOL',
|
||||
'shell':'$LITE make J=1 all check || $CONTRUN_NAME=lite $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -646,7 +646,7 @@ run_regression()
|
||||
|
||||
# parameters: $1 -- key, $2 -- value
|
||||
function send_size_to_ods {
|
||||
curl --silent "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=rocksdb.build_size.$1&value=$2" \
|
||||
curl -s "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=rocksdb.build_size.$1&value=$2" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
@@ -664,12 +664,12 @@ run_regression()
|
||||
|
||||
# === lite build ===
|
||||
make clean
|
||||
make LITE=1 -j$(nproc) static_lib
|
||||
OPT=-DROCKSDB_LITE make -j$(nproc) static_lib
|
||||
send_size_to_ods static_lib_lite $(stat --printf="%s" librocksdb.a)
|
||||
strip librocksdb.a
|
||||
send_size_to_ods static_lib_lite_stripped $(stat --printf="%s" librocksdb.a)
|
||||
|
||||
make LITE=1 -j$(nproc) shared_lib
|
||||
OPT=-DROCKSDB_LITE make -j$(nproc) shared_lib
|
||||
send_size_to_ods shared_lib_lite $(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
strip `readlink -f librocksdb.so`
|
||||
send_size_to_ods shared_lib_lite_stripped $(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
|
||||
Vendored
+4
-4
@@ -64,8 +64,8 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
|
||||
std::vector<int> deleted_keys_;
|
||||
std::vector<int> deleted_values_;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<Cache> cache2_;
|
||||
shared_ptr<Cache> cache_;
|
||||
shared_ptr<Cache> cache2_;
|
||||
|
||||
CacheTest()
|
||||
: cache_(NewCache(kCacheSize, kNumShardBits, false)),
|
||||
@@ -145,7 +145,7 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
CacheTest* CacheTest::current_;
|
||||
|
||||
TEST_P(CacheTest, UsageTest) {
|
||||
// cache is std::shared_ptr and will be automatically cleaned up.
|
||||
// cache is shared_ptr and will be automatically cleaned up.
|
||||
const uint64_t kCapacity = 100000;
|
||||
auto cache = NewCache(kCapacity, 8, false);
|
||||
|
||||
@@ -173,7 +173,7 @@ TEST_P(CacheTest, UsageTest) {
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, PinnedUsageTest) {
|
||||
// cache is std::shared_ptr and will be automatically cleaned up.
|
||||
// cache is shared_ptr and will be automatically cleaned up.
|
||||
const uint64_t kCapacity = 100000;
|
||||
auto cache = NewCache(kCapacity, 8, false);
|
||||
|
||||
|
||||
Vendored
+7
-12
@@ -461,10 +461,8 @@ std::string LRUCacheShard::GetPrintableOptions() const {
|
||||
}
|
||||
|
||||
LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit, double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> allocator)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
std::move(allocator)) {
|
||||
bool strict_capacity_limit, double high_pri_pool_ratio)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit) {
|
||||
num_shards_ = 1 << num_shard_bits;
|
||||
shards_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard) * num_shards_));
|
||||
@@ -539,14 +537,12 @@ double LRUCache::GetHighPriPoolRatio() {
|
||||
std::shared_ptr<Cache> NewLRUCache(const LRUCacheOptions& cache_opts) {
|
||||
return NewLRUCache(cache_opts.capacity, cache_opts.num_shard_bits,
|
||||
cache_opts.strict_capacity_limit,
|
||||
cache_opts.high_pri_pool_ratio,
|
||||
cache_opts.memory_allocator);
|
||||
cache_opts.high_pri_pool_ratio);
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator) {
|
||||
std::shared_ptr<Cache> NewLRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio) {
|
||||
if (num_shard_bits >= 20) {
|
||||
return nullptr; // the cache cannot be sharded into too many fine pieces
|
||||
}
|
||||
@@ -558,8 +554,7 @@ std::shared_ptr<Cache> NewLRUCache(
|
||||
num_shard_bits = GetDefaultCacheShardBits(capacity);
|
||||
}
|
||||
return std::make_shared<LRUCache>(capacity, num_shard_bits,
|
||||
strict_capacity_limit, high_pri_pool_ratio,
|
||||
std::move(memory_allocator));
|
||||
strict_capacity_limit, high_pri_pool_ratio);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
Vendored
+1
-2
@@ -279,8 +279,7 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard {
|
||||
class LRUCache : public ShardedCache {
|
||||
public:
|
||||
LRUCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr);
|
||||
double high_pri_pool_ratio);
|
||||
virtual ~LRUCache();
|
||||
virtual const char* Name() const override { return "LRUCache"; }
|
||||
virtual CacheShard* GetShard(int shard) override;
|
||||
|
||||
Vendored
+2
-7
@@ -20,10 +20,8 @@
|
||||
namespace rocksdb {
|
||||
|
||||
ShardedCache::ShardedCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
std::shared_ptr<MemoryAllocator> allocator)
|
||||
: Cache(std::move(allocator)),
|
||||
num_shard_bits_(num_shard_bits),
|
||||
bool strict_capacity_limit)
|
||||
: num_shard_bits_(num_shard_bits),
|
||||
capacity_(capacity),
|
||||
strict_capacity_limit_(strict_capacity_limit),
|
||||
last_id_(1) {}
|
||||
@@ -144,9 +142,6 @@ std::string ShardedCache::GetPrintableOptions() const {
|
||||
strict_capacity_limit_);
|
||||
ret.append(buffer);
|
||||
}
|
||||
snprintf(buffer, kBufferSize, " memory_allocator : %s\n",
|
||||
memory_allocator() ? memory_allocator()->Name() : "None");
|
||||
ret.append(buffer);
|
||||
ret.append(GetShard(0)->GetPrintableOptions());
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vendored
+1
-2
@@ -47,8 +47,7 @@ class CacheShard {
|
||||
// Keys are sharded by the highest num_shard_bits bits of hash value.
|
||||
class ShardedCache : public Cache {
|
||||
public:
|
||||
ShardedCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr);
|
||||
ShardedCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit);
|
||||
virtual ~ShardedCache() = default;
|
||||
virtual const char* Name() const override = 0;
|
||||
virtual CacheShard* GetShard(int shard) = 0;
|
||||
|
||||
+14
-17
@@ -18,7 +18,6 @@
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
@@ -66,9 +65,8 @@ 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::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, const InternalKeyComparator& internal_comparator,
|
||||
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,
|
||||
@@ -88,10 +86,12 @@ Status BuildTable(
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
iter->SeekToFirst();
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg(
|
||||
new CompactionRangeDelAggregator(&internal_comparator, snapshots));
|
||||
for (auto& range_del_iter : range_del_iters) {
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg(
|
||||
new RangeDelAggregator(internal_comparator, snapshots));
|
||||
s = range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
if (!s.ok()) {
|
||||
// may be non-ok if a range tombstone key is unparsable
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string fname = TableFileName(ioptions.cf_paths, meta->fd.GetNumber(),
|
||||
@@ -104,9 +104,9 @@ Status BuildTable(
|
||||
|
||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||
TableBuilder* builder;
|
||||
std::unique_ptr<WritableFileWriter> file_writer;
|
||||
unique_ptr<WritableFileWriter> file_writer;
|
||||
{
|
||||
std::unique_ptr<WritableFile> file;
|
||||
unique_ptr<WritableFile> file;
|
||||
#ifndef NDEBUG
|
||||
bool use_direct_writes = env_options.use_direct_writes;
|
||||
TEST_SYNC_POINT_CALLBACK("BuildTable:create_file", &use_direct_writes);
|
||||
@@ -121,9 +121,8 @@ Status BuildTable(
|
||||
file->SetIOPriority(io_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
|
||||
file_writer.reset(new WritableFileWriter(std::move(file), fname,
|
||||
env_options, ioptions.statistics,
|
||||
ioptions.listeners));
|
||||
file_writer.reset(new WritableFileWriter(
|
||||
std::move(file), fname, env_options, ioptions.statistics));
|
||||
builder = NewTableBuilder(
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
int_tbl_prop_collector_factories, column_family_id,
|
||||
@@ -158,10 +157,8 @@ Status BuildTable(
|
||||
}
|
||||
}
|
||||
|
||||
auto range_del_it = range_del_agg->NewIterator();
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
for (auto it = range_del_agg->NewIterator(); it->Valid(); it->Next()) {
|
||||
auto tombstone = it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone.SerializeEndKey(),
|
||||
|
||||
+2
-4
@@ -9,7 +9,6 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
@@ -66,9 +65,8 @@ extern Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& options,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, const InternalKeyComparator& internal_comparator,
|
||||
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,
|
||||
|
||||
@@ -151,20 +151,14 @@ struct rocksdb_writablefile_t { WritableFile* rep; };
|
||||
struct rocksdb_wal_iterator_t { TransactionLogIterator* rep; };
|
||||
struct rocksdb_wal_readoptions_t { TransactionLogIterator::ReadOptions rep; };
|
||||
struct rocksdb_filelock_t { FileLock* rep; };
|
||||
struct rocksdb_logger_t {
|
||||
std::shared_ptr<Logger> rep;
|
||||
};
|
||||
struct rocksdb_cache_t {
|
||||
std::shared_ptr<Cache> rep;
|
||||
};
|
||||
struct rocksdb_logger_t { shared_ptr<Logger> rep; };
|
||||
struct rocksdb_cache_t { shared_ptr<Cache> rep; };
|
||||
struct rocksdb_livefiles_t { std::vector<LiveFileMetaData> rep; };
|
||||
struct rocksdb_column_family_handle_t { ColumnFamilyHandle* rep; };
|
||||
struct rocksdb_envoptions_t { EnvOptions rep; };
|
||||
struct rocksdb_ingestexternalfileoptions_t { IngestExternalFileOptions rep; };
|
||||
struct rocksdb_sstfilewriter_t { SstFileWriter* rep; };
|
||||
struct rocksdb_ratelimiter_t {
|
||||
std::shared_ptr<RateLimiter> rep;
|
||||
};
|
||||
struct rocksdb_ratelimiter_t { shared_ptr<RateLimiter> rep; };
|
||||
struct rocksdb_perfcontext_t { PerfContext* rep; };
|
||||
struct rocksdb_pinnableslice_t {
|
||||
PinnableSlice rep;
|
||||
@@ -3550,18 +3544,6 @@ const char* rocksdb_livefiles_largestkey(
|
||||
return lf->rep[index].largestkey.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_livefiles_entries(
|
||||
const rocksdb_livefiles_t* lf,
|
||||
int index) {
|
||||
return lf->rep[index].num_entries;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_livefiles_deletions(
|
||||
const rocksdb_livefiles_t* lf,
|
||||
int index) {
|
||||
return lf->rep[index].num_deletions;
|
||||
}
|
||||
|
||||
extern void rocksdb_livefiles_destroy(
|
||||
const rocksdb_livefiles_t* lf) {
|
||||
delete lf;
|
||||
|
||||
+21
-10
@@ -20,12 +20,10 @@
|
||||
#include <limits>
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#include "db/compaction_picker_universal.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
@@ -107,6 +105,9 @@ void GetIntTblPropCollectorFactory(
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
new UserKeyTablePropertiesCollectorFactory(collector_factories[i]));
|
||||
}
|
||||
// Add collector to collect internal key statistics
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
new InternalKeyPropertiesCollectorFactory);
|
||||
}
|
||||
|
||||
Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
|
||||
@@ -944,16 +945,26 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
super_version->imm->AddIterators(read_opts, &merge_iter_builder);
|
||||
ScopedArenaIterator memtable_iter(merge_iter_builder.Finish());
|
||||
|
||||
auto read_seq = super_version->current->version_set()->LastSequence();
|
||||
ReadRangeDelAggregator range_del_agg(&internal_comparator_, read_seq);
|
||||
std::vector<InternalIterator*> memtable_range_del_iters;
|
||||
auto* active_range_del_iter =
|
||||
super_version->mem->NewRangeTombstoneIterator(read_opts, read_seq);
|
||||
range_del_agg.AddTombstones(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(active_range_del_iter));
|
||||
super_version->imm->AddRangeTombstoneIterators(read_opts, nullptr /* arena */,
|
||||
&range_del_agg);
|
||||
|
||||
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();
|
||||
|
||||
+58
-68
@@ -47,7 +47,7 @@ class EnvCounter : public EnvWrapper {
|
||||
int GetNumberOfNewWritableFileCalls() {
|
||||
return num_new_writable_file_;
|
||||
}
|
||||
Status NewWritableFile(const std::string& f, std::unique_ptr<WritableFile>* r,
|
||||
Status NewWritableFile(const std::string& f, unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
++num_new_writable_file_;
|
||||
return EnvWrapper::NewWritableFile(f, r, soptions);
|
||||
@@ -486,9 +486,9 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
void CopyFile(const std::string& source, const std::string& destination,
|
||||
uint64_t size = 0) {
|
||||
const EnvOptions soptions;
|
||||
std::unique_ptr<SequentialFile> srcfile;
|
||||
unique_ptr<SequentialFile> srcfile;
|
||||
ASSERT_OK(env_->NewSequentialFile(source, &srcfile, soptions));
|
||||
std::unique_ptr<WritableFile> destfile;
|
||||
unique_ptr<WritableFile> destfile;
|
||||
ASSERT_OK(env_->NewWritableFile(destination, &destfile, soptions));
|
||||
|
||||
if (size == 0) {
|
||||
@@ -513,16 +513,6 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
return static_cast<int>(files.size());
|
||||
}
|
||||
|
||||
void RecalculateWriteStallConditions(ColumnFamilyData* cfd,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
// add lock to avoid race condition between
|
||||
// `RecalculateWriteStallConditions` which writes to CFStats and
|
||||
// background `DBImpl::DumpStats()` threads which read CFStats
|
||||
dbfull()->TEST_LockMutex();
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
dbfull()-> TEST_UnlockMutex();
|
||||
}
|
||||
|
||||
std::vector<ColumnFamilyHandle*> handles_;
|
||||
std::vector<std::string> names_;
|
||||
std::vector<std::set<std::string>> keys_;
|
||||
@@ -2510,139 +2500,139 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(450);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(205);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(202);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(198);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(399);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(599);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(2001);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(390);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(100);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(100);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(101);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(101);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(200);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(0);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
mutable_cf_options.disable_auto_compactions = true;
|
||||
dbfull()->TEST_write_controler().set_delayed_write_rate(kBaseRate);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(0, GetDbDelayedWriteRate());
|
||||
@@ -2650,7 +2640,7 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(60);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(0, GetDbDelayedWriteRate());
|
||||
@@ -2659,14 +2649,14 @@ TEST_P(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
vstorage->set_l0_delay_trigger_count(70);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(71);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
@@ -2691,31 +2681,31 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(7);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(9);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(6);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
// Speed up threshold = min(4 * 2, 4 + (12 - 4)/4) = 6
|
||||
@@ -2724,15 +2714,15 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
mutable_cf_options.level0_stop_writes_trigger = 30;
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(5);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(7);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(3);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
@@ -2759,53 +2749,53 @@ TEST_P(ColumnFamilyTest, WriteStallTwoColumnFamilies) {
|
||||
mutable_cf_options1.soft_pending_compaction_bytes_limit = 500;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(201);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(800);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(700);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25 / 1.25, GetDbDelayedWriteRate());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(600);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!IsDbWriteStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.25, GetDbDelayedWriteRate());
|
||||
@@ -2838,41 +2828,41 @@ TEST_P(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
mutable_cf_options1.level0_slowdown_writes_trigger = 16;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(9);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->set_l0_delay_trigger_count(2);
|
||||
RecalculateWriteStallConditions(cfd1, mutable_cf_options);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
RecalculateWriteStallConditions(cfd, mutable_cf_options);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,6 @@ Status CompactedDBImpl::Open(const Options& options,
|
||||
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
||||
Status s = db->Init(options);
|
||||
if (s.ok()) {
|
||||
db->StartTimedTasks();
|
||||
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
|
||||
@@ -67,11 +67,10 @@ class CompactedDBImpl : public DBImpl {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
return DBImpl::GetLiveFiles(ret, manifest_file_size,
|
||||
false /* flush_memtable */);
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
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*/,
|
||||
|
||||
+1
-83
@@ -23,43 +23,6 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
const uint64_t kRangeTombstoneSentinel =
|
||||
PackSequenceAndType(kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b) {
|
||||
auto c = user_cmp->Compare(a.user_key(), b.user_key());
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
auto a_footer = ExtractInternalKeyFooter(a.Encode());
|
||||
auto b_footer = ExtractInternalKeyFooter(b.Encode());
|
||||
if (a_footer == kRangeTombstoneSentinel) {
|
||||
if (b_footer != kRangeTombstoneSentinel) {
|
||||
return -1;
|
||||
}
|
||||
} else if (b_footer == kRangeTombstoneSentinel) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey* a,
|
||||
const InternalKey& b) {
|
||||
if (a == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
return sstableKeyCompare(user_cmp, *a, b);
|
||||
}
|
||||
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey* b) {
|
||||
if (b == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
return sstableKeyCompare(user_cmp, a, *b);
|
||||
}
|
||||
|
||||
uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
|
||||
uint64_t sum = 0;
|
||||
for (size_t i = 0; i < files.size() && files[i]; i++) {
|
||||
@@ -118,49 +81,6 @@ void Compaction::GetBoundaryKeys(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> Compaction::PopulateWithAtomicBoundaries(
|
||||
VersionStorageInfo* vstorage, std::vector<CompactionInputFiles> inputs) {
|
||||
const Comparator* ucmp = vstorage->InternalComparator()->user_comparator();
|
||||
for (size_t i = 0; i < inputs.size(); i++) {
|
||||
if (inputs[i].level == 0 || inputs[i].files.empty()) {
|
||||
continue;
|
||||
}
|
||||
inputs[i].atomic_compaction_unit_boundaries.reserve(inputs[i].files.size());
|
||||
AtomicCompactionUnitBoundary cur_boundary;
|
||||
size_t first_atomic_idx = 0;
|
||||
auto add_unit_boundary = [&](size_t to) {
|
||||
if (first_atomic_idx == to) return;
|
||||
for (size_t k = first_atomic_idx; k < to; k++) {
|
||||
inputs[i].atomic_compaction_unit_boundaries.push_back(cur_boundary);
|
||||
}
|
||||
first_atomic_idx = to;
|
||||
};
|
||||
for (size_t j = 0; j < inputs[i].files.size(); j++) {
|
||||
const auto* f = inputs[i].files[j];
|
||||
if (j == 0) {
|
||||
// First file in a level.
|
||||
cur_boundary.smallest = &f->smallest;
|
||||
cur_boundary.largest = &f->largest;
|
||||
} else if (sstableKeyCompare(ucmp, *cur_boundary.largest, f->smallest) ==
|
||||
0) {
|
||||
// SSTs overlap but the end key of the previous file was not
|
||||
// artificially extended by a range tombstone. Extend the current
|
||||
// boundary.
|
||||
cur_boundary.largest = &f->largest;
|
||||
} else {
|
||||
// Atomic compaction unit has ended.
|
||||
add_unit_boundary(j);
|
||||
cur_boundary.smallest = &f->smallest;
|
||||
cur_boundary.largest = &f->largest;
|
||||
}
|
||||
}
|
||||
add_unit_boundary(inputs[i].files.size());
|
||||
assert(inputs[i].files.size() ==
|
||||
inputs[i].atomic_compaction_unit_boundaries.size());
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
||||
// helper function to determine if compaction is creating files at the
|
||||
// bottommost level
|
||||
bool Compaction::IsBottommostLevel(
|
||||
@@ -235,7 +155,7 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
output_compression_(_compression),
|
||||
output_compression_opts_(_compression_opts),
|
||||
deletion_compaction_(_deletion_compaction),
|
||||
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
|
||||
inputs_(std::move(_inputs)),
|
||||
grandparents_(std::move(_grandparents)),
|
||||
score_(_score),
|
||||
bottommost_level_(IsBottommostLevel(output_level_, vstorage, inputs_)),
|
||||
@@ -411,14 +331,12 @@ const char* Compaction::InputLevelSummary(
|
||||
if (!is_first) {
|
||||
len +=
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, " + ");
|
||||
len = std::min(len, static_cast<int>(sizeof(scratch->buffer)));
|
||||
} else {
|
||||
is_first = false;
|
||||
}
|
||||
len += snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
|
||||
"%" ROCKSDB_PRIszt "@%d", input_level.size(),
|
||||
input_level.level);
|
||||
len = std::min(len, static_cast<int>(sizeof(scratch->buffer)));
|
||||
}
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
|
||||
" files to L%d", output_level());
|
||||
|
||||
@@ -15,43 +15,11 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Utility for comparing sstable boundary keys. Returns -1 if either a or b is
|
||||
// null which provides the property that a==null indicates a key that is less
|
||||
// than any key and b==null indicates a key that is greater than any key. Note
|
||||
// that the comparison is performed primarily on the user-key portion of the
|
||||
// key. If the user-keys compare equal, an additional test is made to sort
|
||||
// range tombstone sentinel keys before other keys with the same user-key. The
|
||||
// result is that 2 user-keys will compare equal if they differ purely on
|
||||
// their sequence number and value, but the range tombstone sentinel for that
|
||||
// user-key will compare not equal. This is necessary because the range
|
||||
// tombstone sentinel key is set as the largest key for an sstable even though
|
||||
// that key never appears in the database. We don't want adjacent sstables to
|
||||
// be considered overlapping if they are separated by the range tombstone
|
||||
// sentinel.
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b);
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey* a,
|
||||
const InternalKey& b);
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey* b);
|
||||
|
||||
// An AtomicCompactionUnitBoundary represents a range of keys [smallest,
|
||||
// largest] that exactly spans one ore more neighbouring SSTs on the same
|
||||
// level. Every pair of SSTs in this range "overlap" (i.e., the largest
|
||||
// user key of one file is the smallest user key of the next file). These
|
||||
// boundaries are propagated down to RangeDelAggregator during compaction
|
||||
// to provide safe truncation boundaries for range tombstones.
|
||||
struct AtomicCompactionUnitBoundary {
|
||||
const InternalKey* smallest = nullptr;
|
||||
const InternalKey* largest = nullptr;
|
||||
};
|
||||
|
||||
// The structure that manages compaction input files associated
|
||||
// with the same physical level.
|
||||
struct CompactionInputFiles {
|
||||
int level;
|
||||
std::vector<FileMetaData*> files;
|
||||
std::vector<AtomicCompactionUnitBoundary> atomic_compaction_unit_boundaries;
|
||||
inline bool empty() const { return files.empty(); }
|
||||
inline size_t size() const { return files.size(); }
|
||||
inline void clear() { files.clear(); }
|
||||
@@ -128,12 +96,6 @@ class Compaction {
|
||||
return inputs_[compaction_input_level][i];
|
||||
}
|
||||
|
||||
const std::vector<AtomicCompactionUnitBoundary>* boundaries(
|
||||
size_t compaction_input_level) const {
|
||||
assert(compaction_input_level < inputs_.size());
|
||||
return &inputs_[compaction_input_level].atomic_compaction_unit_boundaries;
|
||||
}
|
||||
|
||||
// Returns the list of file meta data of the specified compaction
|
||||
// input level.
|
||||
// REQUIREMENT: "compaction_input_level" must be >= 0 and
|
||||
@@ -300,13 +262,6 @@ class Compaction {
|
||||
const std::vector<CompactionInputFiles>& inputs,
|
||||
Slice* smallest_key, Slice* largest_key);
|
||||
|
||||
// Get the atomic file boundaries for all files in the compaction. Necessary
|
||||
// in order to avoid the scenario described in
|
||||
// https://github.com/facebook/rocksdb/pull/4432#discussion_r221072219 and plumb
|
||||
// down appropriate key boundaries to RangeDelAggregator during compaction.
|
||||
static std::vector<CompactionInputFiles> PopulateWithAtomicBoundaries(
|
||||
VersionStorageInfo* vstorage, std::vector<CompactionInputFiles> inputs);
|
||||
|
||||
// helper function to determine if compaction with inputs and storage is
|
||||
// bottommost
|
||||
static bool IsBottommostLevel(
|
||||
|
||||
@@ -18,7 +18,7 @@ CompactionIterator::CompactionIterator(
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg, const Compaction* compaction,
|
||||
RangeDelAggregator* range_del_agg, const Compaction* compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum)
|
||||
@@ -36,7 +36,7 @@ CompactionIterator::CompactionIterator(
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
|
||||
@@ -64,7 +64,7 @@ class CompactionIterator {
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
@@ -77,7 +77,7 @@ class CompactionIterator {
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
@@ -141,7 +141,7 @@ class CompactionIterator {
|
||||
Env* env_;
|
||||
bool report_detailed_time_;
|
||||
bool expect_valid_internal_key_;
|
||||
CompactionRangeDelAggregator* range_del_agg_;
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
|
||||
@@ -221,15 +221,10 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
MergeOperator* merge_op = nullptr, CompactionFilter* filter = nullptr,
|
||||
bool bottommost_level = false,
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber) {
|
||||
std::unique_ptr<InternalIterator> unfragmented_range_del_iter(
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
new test::VectorIterator(range_del_ks, range_del_vs));
|
||||
auto tombstone_list = std::make_shared<FragmentedRangeTombstoneList>(
|
||||
std::move(unfragmented_range_del_iter), icmp_);
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
new FragmentedRangeTombstoneIterator(tombstone_list, icmp_,
|
||||
kMaxSequenceNumber));
|
||||
range_del_agg_.reset(new CompactionRangeDelAggregator(&icmp_, snapshots_));
|
||||
range_del_agg_->AddTombstones(std::move(range_del_iter));
|
||||
range_del_agg_.reset(new RangeDelAggregator(icmp_, snapshots_));
|
||||
ASSERT_OK(range_del_agg_->AddTombstones(std::move(range_del_iter)));
|
||||
|
||||
std::unique_ptr<CompactionIterator::CompactionProxy> compaction;
|
||||
if (filter || bottommost_level) {
|
||||
@@ -297,7 +292,7 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
std::unique_ptr<MergeHelper> merge_helper_;
|
||||
std::unique_ptr<LoggingForwardVectorIterator> iter_;
|
||||
std::unique_ptr<CompactionIterator> c_iter_;
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg_;
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg_;
|
||||
std::unique_ptr<SnapshotChecker> snapshot_checker_;
|
||||
std::atomic<bool> shutting_down_{false};
|
||||
FakeCompaction* compaction_proxy_;
|
||||
|
||||
+26
-78
@@ -36,7 +36,6 @@
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_set.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
@@ -201,7 +200,7 @@ struct CompactionJob::SubcompactionState {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Because member std::unique_ptrs do not have these.
|
||||
// Because member unique_ptrs do not have these.
|
||||
SubcompactionState(const SubcompactionState&) = delete;
|
||||
|
||||
SubcompactionState& operator=(const SubcompactionState&) = delete;
|
||||
@@ -512,10 +511,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// size of data covered by keys in that range
|
||||
uint64_t sum = 0;
|
||||
std::vector<RangeWithSize> ranges;
|
||||
// Get input version from CompactionState since it's already referenced
|
||||
// earlier in SetInputVersioCompaction::SetInputVersion and will not change
|
||||
// when db_mutex_ is released below
|
||||
auto* v = compact_->compaction->input_version();
|
||||
auto* v = cfd->current();
|
||||
for (auto it = bounds.begin();;) {
|
||||
const Slice a = *it;
|
||||
it++;
|
||||
@@ -525,13 +521,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
}
|
||||
|
||||
const Slice b = *it;
|
||||
|
||||
// ApproximateSize could potentially create table reader iterator to seek
|
||||
// to the index block and may incur I/O cost in the process. Unlock db
|
||||
// mutex to reduce contention
|
||||
db_mutex_->Unlock();
|
||||
uint64_t size = versions_->ApproximateSize(v, a, b, start_lvl, out_lvl + 1);
|
||||
db_mutex_->Lock();
|
||||
ranges.emplace_back(a, b, size);
|
||||
sum += size;
|
||||
}
|
||||
@@ -805,13 +795,10 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact != nullptr);
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
CompactionRangeDelAggregator range_del_agg(&cfd->internal_comparator(),
|
||||
existing_snapshots_);
|
||||
|
||||
// Although the v2 aggregator is what the level iterator(s) know about,
|
||||
// the AddTombstones calls will be propagated down to the v1 aggregator.
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg(
|
||||
new RangeDelAggregator(cfd->internal_comparator(), existing_snapshots_));
|
||||
std::unique_ptr<InternalIterator> input(versions_->MakeInputIterator(
|
||||
sub_compact->compaction, &range_del_agg, env_optiosn_for_read_));
|
||||
sub_compact->compaction, range_del_agg.get(), env_optiosn_for_read_));
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -900,7 +887,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_), false,
|
||||
&range_del_agg, sub_compact->compaction, compaction_filter,
|
||||
range_del_agg.get(), sub_compact->compaction, compaction_filter,
|
||||
shutting_down_, preserve_deletes_seqnum_));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
@@ -1038,9 +1025,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
next_key = &c_iter->key();
|
||||
}
|
||||
CompactionIterationStats range_del_out_stats;
|
||||
status =
|
||||
FinishCompactionOutputFile(input_status, sub_compact, &range_del_agg,
|
||||
&range_del_out_stats, next_key);
|
||||
status = FinishCompactionOutputFile(input_status, sub_compact,
|
||||
range_del_agg.get(),
|
||||
&range_del_out_stats, next_key);
|
||||
RecordDroppedKeys(range_del_out_stats,
|
||||
&sub_compact->compaction_job_stats);
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
@@ -1090,7 +1077,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
if (status.ok() && sub_compact->builder == nullptr &&
|
||||
sub_compact->outputs.size() == 0 && !range_del_agg.IsEmpty()) {
|
||||
sub_compact->outputs.size() == 0 &&
|
||||
!range_del_agg->IsEmpty()) {
|
||||
// handle subcompaction containing only range deletions
|
||||
status = OpenCompactionOutputFile(sub_compact);
|
||||
}
|
||||
@@ -1099,8 +1087,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
// close the output file.
|
||||
if (sub_compact->builder != nullptr) {
|
||||
CompactionIterationStats range_del_out_stats;
|
||||
Status s = FinishCompactionOutputFile(status, sub_compact, &range_del_agg,
|
||||
&range_del_out_stats);
|
||||
Status s = FinishCompactionOutputFile(
|
||||
status, sub_compact, range_del_agg.get(), &range_del_out_stats);
|
||||
if (status.ok()) {
|
||||
status = s;
|
||||
}
|
||||
@@ -1165,7 +1153,7 @@ void CompactionJob::RecordDroppedKeys(
|
||||
|
||||
Status CompactionJob::FinishCompactionOutputFile(
|
||||
const Status& input_status, SubcompactionState* sub_compact,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionIterationStats* range_del_out_stats,
|
||||
const Slice* next_table_min_key /* = nullptr */) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
@@ -1204,19 +1192,10 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
lower_bound = nullptr;
|
||||
}
|
||||
if (next_table_min_key != nullptr) {
|
||||
// This may be the last file in the subcompaction in some cases, so we
|
||||
// need to compare the end key of subcompaction with the next file start
|
||||
// key. When the end key is chosen by the subcompaction, we know that
|
||||
// it must be the biggest key in output file. Therefore, it is safe to
|
||||
// use the smaller key as the upper bound of the output file, to ensure
|
||||
// that there is no overlapping between different output files.
|
||||
// This isn't the last file in the subcompaction, so extend until the next
|
||||
// file starts.
|
||||
upper_bound_guard = ExtractUserKey(*next_table_min_key);
|
||||
if (sub_compact->end != nullptr &&
|
||||
ucmp->Compare(upper_bound_guard, *sub_compact->end) >= 0) {
|
||||
upper_bound = sub_compact->end;
|
||||
} else {
|
||||
upper_bound = &upper_bound_guard;
|
||||
}
|
||||
upper_bound = &upper_bound_guard;
|
||||
} else {
|
||||
// This is the last file in the subcompaction, so extend until the
|
||||
// subcompaction ends.
|
||||
@@ -1226,45 +1205,18 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
if (existing_snapshots_.size() > 0) {
|
||||
earliest_snapshot = existing_snapshots_[0];
|
||||
}
|
||||
bool has_overlapping_endpoints;
|
||||
if (upper_bound != nullptr && meta->largest.size() > 0) {
|
||||
has_overlapping_endpoints =
|
||||
ucmp->Compare(meta->largest.user_key(), *upper_bound) == 0;
|
||||
} else {
|
||||
has_overlapping_endpoints = false;
|
||||
}
|
||||
|
||||
// The end key of the subcompaction must be bigger or equal to the upper
|
||||
// bound. If the end of subcompaction is null or the upper bound is null,
|
||||
// it means that this file is the last file in the compaction. So there
|
||||
// will be no overlapping between this file and others.
|
||||
assert(sub_compact->end == nullptr ||
|
||||
upper_bound == nullptr ||
|
||||
ucmp->Compare(*upper_bound , *sub_compact->end) <= 0);
|
||||
auto it = range_del_agg->NewIterator(lower_bound, upper_bound,
|
||||
has_overlapping_endpoints);
|
||||
// Position the range tombstone output iterator. There may be tombstone
|
||||
// fragments that are entirely out of range, so make sure that we do not
|
||||
// include those.
|
||||
auto it = range_del_agg->NewIterator();
|
||||
if (lower_bound != nullptr) {
|
||||
it->Seek(*lower_bound);
|
||||
} else {
|
||||
it->SeekToFirst();
|
||||
}
|
||||
for (; it->Valid(); it->Next()) {
|
||||
auto tombstone = it->Tombstone();
|
||||
if (upper_bound != nullptr) {
|
||||
int cmp = ucmp->Compare(*upper_bound, tombstone.start_key_);
|
||||
if ((has_overlapping_endpoints && cmp < 0) ||
|
||||
(!has_overlapping_endpoints && cmp <= 0)) {
|
||||
// Tombstones starting after upper_bound only need to be included in
|
||||
// the next table. If the current SST ends before upper_bound, i.e.,
|
||||
// `has_overlapping_endpoints == false`, we can also skip over range
|
||||
// tombstones that start exactly at upper_bound. Such range tombstones
|
||||
// will be included in the next file and are not relevant to the point
|
||||
// keys or endpoints of the current file.
|
||||
break;
|
||||
}
|
||||
if (upper_bound != nullptr &&
|
||||
ucmp->Compare(*upper_bound, tombstone.start_key_) <= 0) {
|
||||
// Tombstones starting at upper_bound or later only need to be included
|
||||
// in the next table. Break because subsequent tombstones will start
|
||||
// even later.
|
||||
break;
|
||||
}
|
||||
|
||||
if (bottommost_level_ && tombstone.seq_ <= earliest_snapshot) {
|
||||
@@ -1276,8 +1228,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
}
|
||||
|
||||
auto kv = tombstone.Serialize();
|
||||
assert(lower_bound == nullptr ||
|
||||
ucmp->Compare(*lower_bound, kv.second) < 0);
|
||||
sub_compact->builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey smallest_candidate = std::move(kv.first);
|
||||
if (lower_bound != nullptr &&
|
||||
@@ -1481,7 +1431,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
TableFileCreationReason::kCompaction);
|
||||
#endif // !ROCKSDB_LITE
|
||||
// Make the output file
|
||||
std::unique_ptr<WritableFile> writable_file;
|
||||
unique_ptr<WritableFile> writable_file;
|
||||
#ifndef NDEBUG
|
||||
bool syncpoint_arg = env_options_.use_direct_writes;
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::OpenCompactionOutputFile",
|
||||
@@ -1513,11 +1463,9 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
writable_file->SetWriteLifeTimeHint(write_hint_);
|
||||
writable_file->SetPreallocationBlockSize(static_cast<size_t>(
|
||||
sub_compact->compaction->OutputFilePreallocationSize()));
|
||||
const auto& listeners =
|
||||
sub_compact->compaction->immutable_cf_options()->listeners;
|
||||
sub_compact->outfile.reset(
|
||||
new WritableFileWriter(std::move(writable_file), fname, env_options_,
|
||||
db_options_.statistics.get(), listeners));
|
||||
db_options_.statistics.get()));
|
||||
|
||||
// If the Column family flag is to only optimize filters for hits,
|
||||
// we can skip creating filters if this is the bottommost_level where
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "options/db_options.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/compaction_job_stats.h"
|
||||
@@ -104,7 +104,7 @@ class CompactionJob {
|
||||
|
||||
Status FinishCompactionOutputFile(
|
||||
const Status& input_status, SubcompactionState* sub_compact,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionIterationStats* range_del_out_stats,
|
||||
const Slice* next_table_min_key = nullptr);
|
||||
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
@@ -200,11 +200,11 @@ class CompactionJobTest : public testing::Test {
|
||||
new_db.SetLastSequence(0);
|
||||
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
std::unique_ptr<WritableFile> file;
|
||||
unique_ptr<WritableFile> file;
|
||||
Status s = env_->NewWritableFile(
|
||||
manifest, &file, env_->OptimizeForManifestWrite(env_options_));
|
||||
ASSERT_OK(s);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), manifest, env_options_));
|
||||
{
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
|
||||
+216
-4
@@ -18,7 +18,6 @@
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "util/filename.h"
|
||||
@@ -37,7 +36,6 @@ uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
size_t min_files_to_compact,
|
||||
@@ -71,6 +69,7 @@ bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
// Determine compression type, based on user options, level of the output
|
||||
// file and whether compression is disabled.
|
||||
@@ -944,8 +943,8 @@ Status CompactionPicker::SanitizeCompactionInputFiles(
|
||||
// any currently-existing files.
|
||||
for (auto file_num : *input_files) {
|
||||
bool found = false;
|
||||
for (const auto& level_meta : cf_meta.levels) {
|
||||
for (const auto& file_meta : level_meta.files) {
|
||||
for (auto level_meta : cf_meta.levels) {
|
||||
for (auto file_meta : level_meta.files) {
|
||||
if (file_num == TableFileNameToNumber(file_meta.name)) {
|
||||
if (file_meta.being_compacted) {
|
||||
return Status::Aborted("Specified compaction input file " +
|
||||
@@ -1548,4 +1547,217 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
bool FIFOCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
const int kLevel0 = 0;
|
||||
return vstorage->CompactionScore(kLevel0) >= 1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
uint64_t GetTotalFilesSize(
|
||||
const std::vector<FileMetaData*>& files) {
|
||||
uint64_t total_size = 0;
|
||||
for (const auto& f : files) {
|
||||
total_size += f->fd.file_size;
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(mutable_cf_options.compaction_options_fifo.ttl > 0);
|
||||
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
uint64_t total_size = GetTotalFilesSize(level_files);
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions_.env->GetCurrentTime(&_current_time);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: Couldn't get current time: %s. "
|
||||
"Not doing compactions based on TTL. ",
|
||||
cf_name.c_str(), status.ToString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
inputs.emplace_back();
|
||||
inputs[0].level = 0;
|
||||
|
||||
// avoid underflow
|
||||
if (current_time > mutable_cf_options.compaction_options_fifo.ttl) {
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
if (f->fd.table_reader != nullptr &&
|
||||
f->fd.table_reader->GetTableProperties() != nullptr) {
|
||||
auto creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time == 0 ||
|
||||
creation_time >= (current_time -
|
||||
mutable_cf_options.compaction_options_fifo.ttl)) {
|
||||
break;
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return a nullptr and proceed to size-based FIFO compaction if:
|
||||
// 1. there are no files older than ttl OR
|
||||
// 2. there are a few files older than ttl, but deleting them will not bring
|
||||
// the total size to be less than max_table_files_size threshold.
|
||||
if (inputs[0].files.empty() ||
|
||||
total_size >
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto& f : inputs[0].files) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with creation time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(),
|
||||
f->fd.table_reader->GetTableProperties()->creation_time);
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, ioptions_.compression_opts, /* max_subcompactions */ 0,
|
||||
{}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
uint64_t total_size = GetTotalFilesSize(level_files);
|
||||
|
||||
if (total_size <=
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size ||
|
||||
level_files.size() == 0) {
|
||||
// total size not exceeded
|
||||
if (mutable_cf_options.compaction_options_fifo.allow_compaction &&
|
||||
level_files.size() > 0) {
|
||||
CompactionInputFiles comp_inputs;
|
||||
// try to prevent same files from being compacted multiple times, which
|
||||
// could produce large files that may never TTL-expire. Achieve this by
|
||||
// disallowing compactions with files larger than memtable (inflate its
|
||||
// size by 10% to account for uncompressed L0 files that may have size
|
||||
// slightly greater than memtable size limit).
|
||||
size_t max_compact_bytes_per_del_file =
|
||||
static_cast<size_t>(MultiplyCheckOverflow(
|
||||
static_cast<uint64_t>(mutable_cf_options.write_buffer_size),
|
||||
1.1));
|
||||
if (FindIntraL0Compaction(
|
||||
level_files,
|
||||
mutable_cf_options
|
||||
.level0_file_num_compaction_trigger /* min_files_to_compact */
|
||||
,
|
||||
max_compact_bytes_per_del_file, &comp_inputs)) {
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, {comp_inputs}, 0,
|
||||
16 * 1024 * 1024 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
ioptions_.compression_opts, 0 /* max_subcompactions */, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: nothing to do. Total size %" PRIu64
|
||||
", max size %" PRIu64 "\n",
|
||||
cf_name.c_str(), total_size,
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!level0_compactions_in_progress_.empty()) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: Already executing compaction. No need "
|
||||
"to run parallel compactions since compactions are very fast",
|
||||
cf_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
inputs.emplace_back();
|
||||
inputs[0].level = 0;
|
||||
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
char tmp_fsize[16];
|
||||
AppendHumanBytes(f->fd.GetFileSize(), tmp_fsize, sizeof(tmp_fsize));
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with size %s for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), tmp_fsize);
|
||||
if (total_size <=
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, ioptions_.compression_opts, /* max_subcompactions */ 0,
|
||||
{}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(vstorage->num_levels() == 1);
|
||||
|
||||
Compaction* c = nullptr;
|
||||
if (mutable_cf_options.compaction_options_fifo.ttl > 0) {
|
||||
c = PickTTLCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
if (c == nullptr) {
|
||||
c = PickSizeCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
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*/, uint32_t /*max_subcompactions*/,
|
||||
const InternalKey* /*begin*/, const InternalKey* /*end*/,
|
||||
InternalKey** compaction_end, bool* /*manual_conflict*/) {
|
||||
#ifdef NDEBUG
|
||||
(void)input_level;
|
||||
(void)output_level;
|
||||
#endif
|
||||
assert(input_level == 0);
|
||||
assert(output_level == 0);
|
||||
*compaction_end = nullptr;
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, ioptions_.info_log);
|
||||
Compaction* c =
|
||||
PickCompaction(cf_name, mutable_cf_options, vstorage, &log_buffer);
|
||||
log_buffer.FlushBufferToLog();
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
+36
-5
@@ -235,6 +235,42 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class FIFOCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
FIFOCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual 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, uint32_t max_subcompactions,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict) override;
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
virtual int MaxOutputLevel() const override { return 0; }
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
private:
|
||||
Compaction* PickTTLCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
Compaction* PickSizeCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
};
|
||||
|
||||
class NullCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
NullCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
@@ -272,11 +308,6 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
};
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
size_t min_files_to_compact,
|
||||
uint64_t max_compact_bytes_per_del_file,
|
||||
CompactionInputFiles* comp_inputs);
|
||||
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
const VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace {
|
||||
uint64_t GetTotalFilesSize(const std::vector<FileMetaData*>& files) {
|
||||
uint64_t total_size = 0;
|
||||
for (const auto& f : files) {
|
||||
total_size += f->fd.file_size;
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
bool FIFOCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
const int kLevel0 = 0;
|
||||
return vstorage->CompactionScore(kLevel0) >= 1;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(mutable_cf_options.compaction_options_fifo.ttl > 0);
|
||||
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
uint64_t total_size = GetTotalFilesSize(level_files);
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions_.env->GetCurrentTime(&_current_time);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: Couldn't get current time: %s. "
|
||||
"Not doing compactions based on TTL. ",
|
||||
cf_name.c_str(), status.ToString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
inputs.emplace_back();
|
||||
inputs[0].level = 0;
|
||||
|
||||
// avoid underflow
|
||||
if (current_time > mutable_cf_options.compaction_options_fifo.ttl) {
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
if (f->fd.table_reader != nullptr &&
|
||||
f->fd.table_reader->GetTableProperties() != nullptr) {
|
||||
auto creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time == 0 ||
|
||||
creation_time >= (current_time -
|
||||
mutable_cf_options.compaction_options_fifo.ttl)) {
|
||||
break;
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return a nullptr and proceed to size-based FIFO compaction if:
|
||||
// 1. there are no files older than ttl OR
|
||||
// 2. there are a few files older than ttl, but deleting them will not bring
|
||||
// the total size to be less than max_table_files_size threshold.
|
||||
if (inputs[0].files.empty() ||
|
||||
total_size >
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto& f : inputs[0].files) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with creation time %" PRIu64 " for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(),
|
||||
f->fd.table_reader->GetTableProperties()->creation_time);
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, ioptions_.compression_opts, /* max_subcompactions */ 0,
|
||||
{}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
uint64_t total_size = GetTotalFilesSize(level_files);
|
||||
|
||||
if (total_size <=
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size ||
|
||||
level_files.size() == 0) {
|
||||
// total size not exceeded
|
||||
if (mutable_cf_options.compaction_options_fifo.allow_compaction &&
|
||||
level_files.size() > 0) {
|
||||
CompactionInputFiles comp_inputs;
|
||||
// try to prevent same files from being compacted multiple times, which
|
||||
// could produce large files that may never TTL-expire. Achieve this by
|
||||
// disallowing compactions with files larger than memtable (inflate its
|
||||
// size by 10% to account for uncompressed L0 files that may have size
|
||||
// slightly greater than memtable size limit).
|
||||
size_t max_compact_bytes_per_del_file =
|
||||
static_cast<size_t>(MultiplyCheckOverflow(
|
||||
static_cast<uint64_t>(mutable_cf_options.write_buffer_size),
|
||||
1.1));
|
||||
if (FindIntraL0Compaction(
|
||||
level_files,
|
||||
mutable_cf_options
|
||||
.level0_file_num_compaction_trigger /* min_files_to_compact */
|
||||
,
|
||||
max_compact_bytes_per_del_file, &comp_inputs)) {
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, {comp_inputs}, 0,
|
||||
16 * 1024 * 1024 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
ioptions_.compression_opts, 0 /* max_subcompactions */, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: nothing to do. Total size %" PRIu64
|
||||
", max size %" PRIu64 "\n",
|
||||
cf_name.c_str(), total_size,
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!level0_compactions_in_progress_.empty()) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: Already executing compaction. No need "
|
||||
"to run parallel compactions since compactions are very fast",
|
||||
cf_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
inputs.emplace_back();
|
||||
inputs[0].level = 0;
|
||||
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
total_size -= f->compensated_file_size;
|
||||
inputs[0].files.push_back(f);
|
||||
char tmp_fsize[16];
|
||||
AppendHumanBytes(f->fd.GetFileSize(), tmp_fsize, sizeof(tmp_fsize));
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with size %s for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), tmp_fsize);
|
||||
if (total_size <=
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, ioptions_.compression_opts, /* max_subcompactions */ 0,
|
||||
{}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(vstorage->num_levels() == 1);
|
||||
|
||||
Compaction* c = nullptr;
|
||||
if (mutable_cf_options.compaction_options_fifo.ttl > 0) {
|
||||
c = PickTTLCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
if (c == nullptr) {
|
||||
c = PickSizeCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
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*/, uint32_t /*max_subcompactions*/,
|
||||
const InternalKey* /*begin*/, const InternalKey* /*end*/,
|
||||
InternalKey** compaction_end, bool* /*manual_conflict*/) {
|
||||
#ifdef NDEBUG
|
||||
(void)input_level;
|
||||
(void)output_level;
|
||||
#endif
|
||||
assert(input_level == 0);
|
||||
assert(output_level == 0);
|
||||
*compaction_end = nullptr;
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, ioptions_.info_log);
|
||||
Compaction* c =
|
||||
PickCompaction(cf_name, mutable_cf_options, vstorage, &log_buffer);
|
||||
log_buffer.FlushBufferToLog();
|
||||
return c;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
|
||||
namespace rocksdb {
|
||||
class FIFOCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
FIFOCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual 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, uint32_t max_subcompactions,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict) override;
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
virtual int MaxOutputLevel() const override { return 0; }
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
private:
|
||||
Compaction* PickTTLCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
Compaction* PickSizeCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
};
|
||||
} // namespace rocksdb
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -4,12 +4,10 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#include "db/compaction_picker_universal.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
const EnvOptions& env_options,
|
||||
const std::string& file_path) {
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
unique_ptr<RandomAccessFile> file;
|
||||
uint64_t file_size;
|
||||
InternalKeyComparator internal_comparator(options.comparator);
|
||||
ImmutableCFOptions ioptions(options);
|
||||
@@ -46,7 +46,7 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
unique_ptr<TableReader> table_reader;
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), file_path));
|
||||
const bool kImmortal = true;
|
||||
|
||||
@@ -37,7 +37,7 @@ class CorruptionTest : public testing::Test {
|
||||
public:
|
||||
test::ErrorEnv env_;
|
||||
std::string dbname_;
|
||||
std::shared_ptr<Cache> tiny_cache_;
|
||||
shared_ptr<Cache> tiny_cache_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
|
||||
@@ -485,7 +485,7 @@ TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
db_ = nullptr;
|
||||
|
||||
if (iter == 0) { // corrupt file size
|
||||
std::unique_ptr<WritableFile> file;
|
||||
unique_ptr<WritableFile> file;
|
||||
env_.NewWritableFile(filename, &file, EnvOptions());
|
||||
file->Append(Slice("corrupted sst"));
|
||||
file.reset();
|
||||
|
||||
+3
-3
@@ -794,7 +794,7 @@ TEST_F(DBBasicTest, ChecksumTest) {
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
// change when new checksum type added
|
||||
int max_checksum = static_cast<int>(kxxHash64);
|
||||
int max_checksum = static_cast<int>(kxxHash);
|
||||
const int kNumPerFile = 2;
|
||||
|
||||
// generate one table with each type of checksum
|
||||
@@ -809,7 +809,7 @@ TEST_F(DBBasicTest, ChecksumTest) {
|
||||
}
|
||||
|
||||
// verify data with each type of checksum
|
||||
for (int i = 0; i <= kxxHash64; ++i) {
|
||||
for (int i = 0; i <= kxxHash; ++i) {
|
||||
table_options.checksum = static_cast<ChecksumType>(i);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
@@ -881,7 +881,7 @@ class TestEnv : public EnvWrapper {
|
||||
int GetCloseCount() { return close_count; }
|
||||
|
||||
virtual Status NewLogger(const std::string& /*fname*/,
|
||||
std::shared_ptr<Logger>* result) {
|
||||
shared_ptr<Logger>* result) {
|
||||
result->reset(new TestLogger(this));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
// 200 bytes are enough to hold the first two blocks
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(200, 0, false);
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20, true));
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
|
||||
@@ -147,7 +147,6 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloomCustomPrefixExtractor) {
|
||||
options.prefix_extractor =
|
||||
std::make_shared<SliceTransformLimitedDomainGeneric>();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
get_perf_context()->EnablePerLevelPerfContext();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
if (partition_filters) {
|
||||
@@ -172,39 +171,20 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloomCustomPrefixExtractor) {
|
||||
|
||||
ASSERT_EQ("foo", Get("barbarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
ASSERT_EQ("foo2", Get("barbarbar2"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
ASSERT_EQ("NOT_FOUND", Get("barbarbar3"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("barfoofoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ(
|
||||
1,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
ASSERT_EQ(
|
||||
2,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
|
||||
ro.total_order_seek = true;
|
||||
ASSERT_TRUE(db_->Get(ro, "foobarbar", &value).IsNotFound());
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
ASSERT_EQ(
|
||||
2,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +193,6 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
|
||||
Options options = last_options_;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
get_perf_context()->EnablePerLevelPerfContext();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
if (partition_filters) {
|
||||
@@ -252,10 +231,6 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
|
||||
ro.total_order_seek = true;
|
||||
ASSERT_TRUE(db_->Get(ro, "foobarbar", &value).IsNotFound());
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
ASSERT_EQ(
|
||||
2,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +239,6 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
Options options = last_options_;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
get_perf_context()->EnablePerLevelPerfContext();
|
||||
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
@@ -412,14 +386,6 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
uint64_t bloom_filter_useful_all_levels = 0;
|
||||
for (auto& kv : (*(get_perf_context()->level_to_perf_context))) {
|
||||
if (kv.second.bloom_filter_useful > 0) {
|
||||
bloom_filter_useful_all_levels += kv.second.bloom_filter_useful;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(12, bloom_filter_useful_all_levels);
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,7 +465,6 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatDef, DBBloomFilterTestDefFormatVersion,
|
||||
::testing::Values(std::make_tuple(true, false, test::kDefaultFormatVersion),
|
||||
@@ -520,13 +485,11 @@ INSTANTIATE_TEST_CASE_P(
|
||||
std::make_tuple(false, true, test::kLatestFormatVersion),
|
||||
std::make_tuple(false, false,
|
||||
test::kLatestFormatVersion)));
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
while (ChangeFilterOptions()) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
get_perf_context()->EnablePerLevelPerfContext();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
const int maxKey = 10000;
|
||||
@@ -548,10 +511,6 @@ TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, Key(i + 33333)));
|
||||
}
|
||||
ASSERT_GE(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), maxKey * 0.98);
|
||||
ASSERT_GE(
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful,
|
||||
maxKey * 0.98);
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -897,7 +856,7 @@ TEST_P(BloomStatsTestWithParam, BloomStatsTestWithIter) {
|
||||
ASSERT_OK(Put(key1, value1, WriteOptions()));
|
||||
ASSERT_OK(Put(key3, value3, WriteOptions()));
|
||||
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ReadOptions()));
|
||||
unique_ptr<Iterator> iter(dbfull()->NewIterator(ReadOptions()));
|
||||
|
||||
// check memtable bloom stats
|
||||
iter->Seek(key1);
|
||||
@@ -1073,7 +1032,6 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.optimize_filters_for_hits = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
get_perf_context()->EnablePerLevelPerfContext();
|
||||
CreateAndReopenWithCF({"mypikachu"}, options);
|
||||
|
||||
int numkeys = 200000;
|
||||
@@ -1120,14 +1078,6 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
// no bloom filter. Most keys be checked bloom filters twice.
|
||||
ASSERT_GT(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 65000 * 2);
|
||||
ASSERT_LT(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 120000 * 2);
|
||||
uint64_t bloom_filter_useful_all_levels = 0;
|
||||
for (auto& kv : (*(get_perf_context()->level_to_perf_context))) {
|
||||
if (kv.second.bloom_filter_useful > 0) {
|
||||
bloom_filter_useful_all_levels += kv.second.bloom_filter_useful;
|
||||
}
|
||||
}
|
||||
ASSERT_GT(bloom_filter_useful_all_levels, 65000 * 2);
|
||||
ASSERT_LT(bloom_filter_useful_all_levels, 120000 * 2);
|
||||
|
||||
for (int i = 0; i < numkeys; i += 2) {
|
||||
ASSERT_EQ(Get(1, Key(i)), "val");
|
||||
@@ -1237,7 +1187,6 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(2 /* index and data block */,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
|
||||
int CountIter(std::unique_ptr<Iterator>& iter, const Slice& key) {
|
||||
|
||||
@@ -340,10 +340,9 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
Arena arena;
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber, handles_[1]));
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
ScopedArenaIterator iter(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
@@ -430,10 +429,9 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
count = 0;
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber, handles_[1]));
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
ScopedArenaIterator iter(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
@@ -648,10 +646,9 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
int total = 0;
|
||||
Arena arena;
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* snapshots */);
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
&arena, &range_del_agg, kMaxSequenceNumber));
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
ScopedArenaIterator iter(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
@@ -851,7 +848,7 @@ TEST_F(DBTestCompactionFilter, SkipUntilWithBloomFilter) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Put("0000000010", "v10");
|
||||
Put("0000000020", "v20"); // skipped
|
||||
Put("0000000020", "v20"); // skipped
|
||||
Put("0000000050", "v50");
|
||||
Flush();
|
||||
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/utilities/convenience.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// SYNC_POINT is not supported in released Windows mode.
|
||||
@@ -2955,12 +2953,6 @@ TEST_F(DBCompactionTest, SuggestCompactRangeNoTwoLevel0Compactions) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
static std::string ShortKey(int i) {
|
||||
assert(i < 10000);
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "key%04d", i);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
int32_t trivial_move = 0;
|
||||
@@ -2973,28 +2965,10 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
[&](void* /*arg*/) { non_trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// The key size is guaranteed to be <= 8
|
||||
class ShortKeyComparator : public Comparator {
|
||||
int Compare(const rocksdb::Slice& a,
|
||||
const rocksdb::Slice& b) const override {
|
||||
assert(a.size() <= 8);
|
||||
assert(b.size() <= 8);
|
||||
return BytewiseComparator()->Compare(a, b);
|
||||
}
|
||||
const char* Name() const override { return "ShortKeyComparator"; }
|
||||
void FindShortestSeparator(std::string* start,
|
||||
const rocksdb::Slice& limit) const override {
|
||||
return BytewiseComparator()->FindShortestSeparator(start, limit);
|
||||
}
|
||||
void FindShortSuccessor(std::string* key) const override {
|
||||
return BytewiseComparator()->FindShortSuccessor(key);
|
||||
}
|
||||
} short_key_cmp;
|
||||
Options options = CurrentOptions();
|
||||
options.target_file_size_base = 100000000;
|
||||
options.write_buffer_size = 100000000;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
options.comparator = &short_key_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
@@ -3004,7 +2978,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
// File with keys [ 0 => 99 ]
|
||||
for (int i = 0; i < 100; i++) {
|
||||
values.push_back(RandomString(&rnd, value_size));
|
||||
ASSERT_OK(Put(ShortKey(i), values[i]));
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -3021,7 +2995,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
// File with keys [ 100 => 199 ]
|
||||
for (int i = 100; i < 200; i++) {
|
||||
values.push_back(RandomString(&rnd, value_size));
|
||||
ASSERT_OK(Put(ShortKey(i), values[i]));
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -3039,7 +3013,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
// File with keys [ 200 => 299 ]
|
||||
for (int i = 200; i < 300; i++) {
|
||||
values.push_back(RandomString(&rnd, value_size));
|
||||
ASSERT_OK(Put(ShortKey(i), values[i]));
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
@@ -3057,7 +3031,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
ASSERT_EQ(non_trivial_move, 0);
|
||||
|
||||
for (int i = 0; i < 300; i++) {
|
||||
ASSERT_EQ(Get(ShortKey(i)), values[i]);
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -4031,46 +4005,6 @@ TEST_F(DBCompactionTest, PartialManualCompaction) {
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ManualCompactionFailsInReadOnlyMode) {
|
||||
// Regression test for bug where manual compaction hangs forever when the DB
|
||||
// is in read-only mode. Verify it now at least returns, despite failing.
|
||||
const int kNumL0Files = 4;
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.env = mock_env.get();
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
// Make sure files are overlapping in key-range to prevent trivial move.
|
||||
Put("key1", RandomString(&rnd, 1024));
|
||||
Put("key2", RandomString(&rnd, 1024));
|
||||
Flush();
|
||||
}
|
||||
ASSERT_EQ(kNumL0Files, NumTableFilesAtLevel(0));
|
||||
|
||||
// Enter read-only mode by failing a write.
|
||||
mock_env->SetFilesystemActive(false);
|
||||
// Make sure this is outside `CompactRange`'s range so that it doesn't fail
|
||||
// early trying to flush memtable.
|
||||
ASSERT_NOK(Put("key3", RandomString(&rnd, 1024)));
|
||||
|
||||
// In the bug scenario, the first manual compaction would fail and forget to
|
||||
// unregister itself, causing the second one to hang forever due to conflict
|
||||
// with a non-running compaction.
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
Slice begin_key("key1");
|
||||
Slice end_key("key2");
|
||||
ASSERT_NOK(dbfull()->CompactRange(cro, &begin_key, &end_key));
|
||||
ASSERT_NOK(dbfull()->CompactRange(cro, &begin_key, &end_key));
|
||||
|
||||
// Close before mock_env destruct.
|
||||
Close();
|
||||
}
|
||||
|
||||
// FixFileIngestionCompactionDeadlock tests and verifies that compaction and
|
||||
// file ingestion do not cause deadlock in the event of write stall triggered
|
||||
// by number of L0 files reaching level0_stop_writes_trigger.
|
||||
|
||||
+26
-23
@@ -27,7 +27,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
|
||||
return;
|
||||
}
|
||||
// Use InMemoryEnv, or it would be too slow.
|
||||
std::unique_ptr<Env> env(new MockEnv(env_));
|
||||
unique_ptr<Env> env(new MockEnv(env_));
|
||||
|
||||
const int kNKeys = 1000;
|
||||
int keys[kNKeys];
|
||||
@@ -125,7 +125,6 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
int kMaxKey = 1000000;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 20480;
|
||||
options.max_write_buffer_number = 2;
|
||||
@@ -168,8 +167,8 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
||||
ASSERT_EQ(4U, int_prop);
|
||||
|
||||
// Insert extra about 28K to L0. After they are compacted to L4, the base
|
||||
// level should be changed to L3.
|
||||
// Insert extra about 28K to L0. After they are compacted to L4, base level
|
||||
// should be changed to L3.
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "true"},
|
||||
}));
|
||||
@@ -190,7 +189,13 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level2", &str_prop));
|
||||
ASSERT_EQ("0", str_prop);
|
||||
|
||||
// Write even more data while leaving the base level at L3.
|
||||
// Trigger parallel compaction, and the first one would change the base
|
||||
// level.
|
||||
// Hold compaction jobs to make sure
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Start",
|
||||
[&](void* /*arg*/) { env_->SleepForMicroseconds(100000); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "true"},
|
||||
}));
|
||||
@@ -203,12 +208,18 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
Flush();
|
||||
// Wait for 200 milliseconds before proceeding compactions to make sure two
|
||||
// parallel ones are executed.
|
||||
env_->SleepForMicroseconds(200000);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
||||
ASSERT_EQ(3U, int_prop);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Fill up L0, and then run an (auto) L0->Lmax compaction to raise the base
|
||||
// level to 2.
|
||||
// Trigger a condition that the compaction changes base level and L0->Lbase
|
||||
// happens at the same time.
|
||||
// We try to make last levels' targets to be 40K, 160K, 640K, add triggers
|
||||
// another compaction from 40K->160K.
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "true"},
|
||||
}));
|
||||
@@ -218,31 +229,23 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
|
||||
RandomString(&rnd, 380)));
|
||||
}
|
||||
|
||||
// Make sure that the compaction starts before the last bit of data is
|
||||
// flushed, so that the base level isn't raised to L1.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:0"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:0");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
||||
ASSERT_EQ(2U, int_prop);
|
||||
|
||||
// A manual compaction will trigger the base level to become L2
|
||||
// Keep Writing data until base level changed 2->1. There will be L0->L2
|
||||
// compaction going on at the same time.
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
// Write more data until the base level changes to L1. There will be
|
||||
// a manual compaction going on at the same time.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:1"},
|
||||
{"DynamicLevelMaxBytesBase2:2", "CompactionJob::Run():End"},
|
||||
{"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:0"},
|
||||
{"DynamicLevelMaxBytesBase2:1", "CompactionJob::Run():End"},
|
||||
{"DynamicLevelMaxBytesBase2:compact_range_finish",
|
||||
"FlushJob::WriteLevel0Table"},
|
||||
});
|
||||
@@ -254,12 +257,12 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_finish");
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:1");
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:0");
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
|
||||
RandomString(&rnd, 380)));
|
||||
}
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:2");
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:1");
|
||||
|
||||
Flush();
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
continue;
|
||||
}
|
||||
auto filePath = dbname_ + "/" + *it;
|
||||
std::unique_ptr<SequentialFile> seqFile;
|
||||
unique_ptr<SequentialFile> seqFile;
|
||||
auto envOptions = EnvOptions(CurrentOptions());
|
||||
status = defaultEnv->NewSequentialFile(filePath, &seqFile, envOptions);
|
||||
ASSERT_OK(status);
|
||||
|
||||
+21
-29
@@ -44,7 +44,7 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
bool file_deletion_enabled = false;
|
||||
bool should_purge_files = false;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (force) {
|
||||
@@ -54,18 +54,19 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
if (disable_delete_obsolete_files_ == 0) {
|
||||
file_deletion_enabled = true;
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Enabled");
|
||||
should_purge_files = true;
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
bg_cv_.SignalAll();
|
||||
} else {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
}
|
||||
if (file_deletion_enabled) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Enabled");
|
||||
if (should_purge_files) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
job_context.Clean();
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
@@ -86,28 +87,19 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
if (flush_memtable) {
|
||||
// flush all dirty data to disk.
|
||||
Status status;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(cfds, FlushOptions(),
|
||||
FlushReason::kGetLiveFiles);
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
cfd->Unref();
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
cfd->Unref();
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
@@ -134,7 +126,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
// create names of the live files. The names are not absolute
|
||||
// paths, instead they are relative to dbname_;
|
||||
for (const auto& live_file : live) {
|
||||
for (auto live_file : live) {
|
||||
ret.push_back(MakeTableFileName("", live_file.GetNumber()));
|
||||
}
|
||||
|
||||
|
||||
+5
-281
@@ -25,12 +25,6 @@ class DBFlushDirectIOTest : public DBFlushTest,
|
||||
DBFlushDirectIOTest() : DBFlushTest() {}
|
||||
};
|
||||
|
||||
class DBAtomicFlushTest : public DBFlushTest,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBAtomicFlushTest() : DBFlushTest() {}
|
||||
};
|
||||
|
||||
// We had issue when two background threads trying to flush at the same time,
|
||||
// only one of them get committed. The test verifies the issue is fixed.
|
||||
TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
@@ -46,7 +40,7 @@ TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"VersionSet::LogAndApply:WriteManifest",
|
||||
"DBFlushTest::FlushWhileWritingManifest:1"},
|
||||
{"MemTableList::TryInstallMemtableFlushResults:InProgress",
|
||||
{"MemTableList::InstallMemtableFlushResults:InProgress",
|
||||
"VersionSet::LogAndApply:WriteManifestDone"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -62,6 +56,7 @@ TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
#ifndef TRAVIS
|
||||
// Disable this test temporarily on Travis as it fails intermittently.
|
||||
// Github issue: #4151
|
||||
TEST_F(DBFlushTest, SyncFail) {
|
||||
@@ -72,15 +67,11 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncFail:GetVersionRefCount:1",
|
||||
"DBImpl::FlushMemTableToOutputFile:BeforePickMemtables"},
|
||||
{"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables",
|
||||
"DBFlushTest::SyncFail:GetVersionRefCount:2"},
|
||||
{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{"DBImpl::SyncClosedLogs:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Reopen(options);
|
||||
Put("key", "value");
|
||||
auto* cfd =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
@@ -91,10 +82,6 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
// Flush installs a new super-version. Get the ref count after that.
|
||||
auto current_before = cfd->current();
|
||||
int refs_before = cfd->current()->TEST_refs();
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:GetVersionRefCount:1");
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:GetVersionRefCount:2");
|
||||
int refs_after_picking_memtables = cfd->current()->TEST_refs();
|
||||
ASSERT_EQ(refs_before + 1, refs_after_picking_memtables);
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:1");
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:2");
|
||||
@@ -109,30 +96,7 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
ASSERT_EQ(refs_before, cfd->current()->TEST_refs());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, SyncSkip) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncSkip:1", "DBImpl::SyncClosedLogs:Skip"},
|
||||
{"DBImpl::SyncClosedLogs:Skip", "DBFlushTest::SyncSkip:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
Put("key", "value");
|
||||
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = false;
|
||||
ASSERT_OK(dbfull()->Flush(flush_options));
|
||||
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncSkip:1");
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncSkip:2");
|
||||
|
||||
// Now the background job will do the flush; wait for it.
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
#endif // TRAVIS
|
||||
|
||||
TEST_F(DBFlushTest, FlushInLowPriThreadPool) {
|
||||
// Verify setting an empty high-pri (flush) thread pool causes flushes to be
|
||||
@@ -250,249 +214,9 @@ TEST_F(DBFlushTest, FlushError) {
|
||||
ASSERT_NE(s, Status::OK());
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, ManualFlushFailsInReadOnlyMode) {
|
||||
// Regression test for bug where manual flush hangs forever when the DB
|
||||
// is in read-only mode. Verify it now at least returns, despite failing.
|
||||
Options options;
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
options.env = fault_injection_env.get();
|
||||
options.max_write_buffer_number = 2;
|
||||
Reopen(options);
|
||||
|
||||
// Trigger a first flush but don't let it run
|
||||
ASSERT_OK(db_->PauseBackgroundWork());
|
||||
ASSERT_OK(Put("key1", "value1"));
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
ASSERT_OK(db_->Flush(flush_opts));
|
||||
|
||||
// Write a key to the second memtable so we have something to flush later
|
||||
// after the DB is in read-only mode.
|
||||
ASSERT_OK(Put("key2", "value2"));
|
||||
|
||||
// Let the first flush continue, hit an error, and put the DB in read-only
|
||||
// mode.
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
ASSERT_OK(db_->ContinueBackgroundWork());
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
#ifndef ROCKSDB_LITE
|
||||
uint64_t num_bg_errors;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBackgroundErrors,
|
||||
&num_bg_errors));
|
||||
ASSERT_GT(num_bg_errors, 0);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// In the bug scenario, triggering another flush would cause the second flush
|
||||
// to hang forever. After the fix we expect it to return an error.
|
||||
ASSERT_NOK(db_->Flush(FlushOptions()));
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = GetParam();
|
||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
WriteOptions wopts;
|
||||
wopts.disableWAL = true;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
ASSERT_OK(Put(static_cast<int>(i) /*cf*/, "key", "value", wopts));
|
||||
}
|
||||
std::vector<int> cf_ids;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
cf_ids.emplace_back(static_cast<int>(i));
|
||||
}
|
||||
ASSERT_OK(Flush(cf_ids));
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, AtomicFlushTriggeredByMemTableFull) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = GetParam();
|
||||
// 4KB so that we can easily trigger auto flush.
|
||||
options.write_buffer_size = 4096;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCallFlush:FlushFinish:0",
|
||||
"DBAtomicFlushTest::AtomicFlushTriggeredByMemTableFull:BeforeCheck"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
WriteOptions wopts;
|
||||
wopts.disableWAL = true;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
ASSERT_OK(Put(static_cast<int>(i) /*cf*/, "key", "value", wopts));
|
||||
}
|
||||
// Keep writing to one of them column families to trigger auto flush.
|
||||
for (int i = 0; i != 4000; ++i) {
|
||||
ASSERT_OK(Put(static_cast<int>(num_cfs) - 1 /*cf*/,
|
||||
"key" + std::to_string(i), "value" + std::to_string(i),
|
||||
wopts));
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::AtomicFlushTriggeredByMemTableFull:BeforeCheck");
|
||||
if (options.atomic_flush) {
|
||||
for (size_t i = 0; i != num_cfs - 1; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i != num_cfs - 1; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_FALSE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, AtomicFlushRollbackSomeJobs) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
options.env = fault_injection_env.get();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::AtomicFlushMemTablesToOutputFiles:SomeFlushJobsComplete:1",
|
||||
"DBAtomicFlushTest::AtomicFlushRollbackSomeJobs:1"},
|
||||
{"DBAtomicFlushTest::AtomicFlushRollbackSomeJobs:2",
|
||||
"DBImpl::AtomicFlushMemTablesToOutputFiles:SomeFlushJobsComplete:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
WriteOptions wopts;
|
||||
wopts.disableWAL = true;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_OK(Put(cf_id, "key", "value", wopts));
|
||||
}
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts, handles_));
|
||||
TEST_SYNC_POINT("DBAtomicFlushTest::AtomicFlushRollbackSomeJobs:1");
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT("DBAtomicFlushTest::AtomicFlushRollbackSomeJobs:2");
|
||||
for (auto* cfh : handles_) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(cfh);
|
||||
}
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(1, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, FlushMultipleCFs_DropSomeBeforeRequestFlush) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
WriteOptions wopts;
|
||||
wopts.disableWAL = true;
|
||||
std::vector<int> cf_ids;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_OK(Put(cf_id, "key", "value", wopts));
|
||||
cf_ids.push_back(cf_id);
|
||||
}
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_TRUE(Flush(cf_ids).IsShutdownInProgress());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest,
|
||||
FlushMultipleCFs_DropSomeAfterScheduleFlushBeforeFlushJobRun) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
|
||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::AtomicFlushMemTables:AfterScheduleFlush",
|
||||
"DBAtomicFlushTest::BeforeDropCF"},
|
||||
{"DBAtomicFlushTest::AfterDropCF",
|
||||
"DBImpl::BackgroundCallFlush:start"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(3, num_cfs);
|
||||
WriteOptions wopts;
|
||||
wopts.disableWAL = true;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_OK(Put(cf_id, "key", "value", wopts));
|
||||
}
|
||||
port::Thread user_thread([&]() {
|
||||
TEST_SYNC_POINT("DBAtomicFlushTest::BeforeDropCF");
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
TEST_SYNC_POINT("DBAtomicFlushTest::AfterDropCF");
|
||||
});
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = true;
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts, handles_));
|
||||
user_thread.join();
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_EQ("value", Get(cf_id, "key"));
|
||||
}
|
||||
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "eevee"}, options);
|
||||
num_cfs = handles_.size();
|
||||
ASSERT_EQ(2, num_cfs);
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
int cf_id = static_cast<int>(i);
|
||||
ASSERT_EQ("value", Get(cf_id, "key"));
|
||||
}
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBFlushDirectIOTest, DBFlushDirectIOTest,
|
||||
testing::Bool());
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBAtomicFlushTest, DBAtomicFlushTest, testing::Bool());
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+113
-180
@@ -45,7 +45,7 @@
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/transaction_log_impl.h"
|
||||
@@ -219,8 +219,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
own_sfm_(options.sst_file_manager == nullptr),
|
||||
preserve_deletes_(options.preserve_deletes),
|
||||
closed_(false),
|
||||
error_handler_(this, immutable_db_options_, &mutex_),
|
||||
atomic_flush_install_cv_(&mutex_) {
|
||||
error_handler_(this, immutable_db_options_, &mutex_) {
|
||||
// !batch_per_trx_ implies seq_per_batch_ because it is only unset for
|
||||
// WriteUnprepared, which should use seq_per_batch_.
|
||||
assert(batch_per_txn_ || seq_per_batch_);
|
||||
@@ -305,30 +304,7 @@ Status DBImpl::ResumeImpl() {
|
||||
// We cannot guarantee consistency of the WAL. So force flush Memtables of
|
||||
// all the column families
|
||||
if (s.ok()) {
|
||||
FlushOptions flush_opts;
|
||||
// We allow flush to stall write since we are trying to resume from error.
|
||||
flush_opts.allow_write_stall = true;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
mutex_.Unlock();
|
||||
s = AtomicFlushMemTables(cfds, flush_opts, FlushReason::kErrorRecovery);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
s = FlushMemTable(cfd, flush_opts, FlushReason::kErrorRecovery);
|
||||
mutex_.Lock();
|
||||
cfd->Unref();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
s = FlushAllCFs(FlushReason::kErrorRecovery);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"DB resume requested but failed due to Flush failure [%s]",
|
||||
@@ -383,38 +359,21 @@ void DBImpl::WaitForBackgroundWork() {
|
||||
|
||||
// Will lock the mutex_, will wait for completion if wait is true
|
||||
void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Shutdown: canceling all background work");
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// To avoid deadlock, `thread_dump_stats_->cancel()` needs to be called
|
||||
// before grabbing db mutex because the actual worker function
|
||||
// `DBImpl::DumpStats()` also holds db mutex
|
||||
if (thread_dump_stats_ != nullptr) {
|
||||
mutex_.Unlock();
|
||||
thread_dump_stats_->cancel();
|
||||
mutex_.Lock();
|
||||
thread_dump_stats_.reset();
|
||||
}
|
||||
if (!shutting_down_.load(std::memory_order_acquire) &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed) &&
|
||||
!mutable_db_options_.avoid_flush_during_shutdown) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
mutex_.Unlock();
|
||||
AtomicFlushMemTables(cfds, FlushOptions(), FlushReason::kShutDown);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!cfd->IsDropped() && cfd->initialized() && !cfd->mem()->IsEmpty()) {
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
FlushMemTable(cfd, FlushOptions(), FlushReason::kShutDown);
|
||||
mutex_.Lock();
|
||||
cfd->Unref();
|
||||
}
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (!cfd->IsDropped() && cfd->initialized() && !cfd->mem()->IsEmpty()) {
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
FlushMemTable(cfd, FlushOptions(), FlushReason::kShutDown);
|
||||
mutex_.Lock();
|
||||
cfd->Unref();
|
||||
}
|
||||
}
|
||||
versions_->GetColumnFamilySet()->FreeDeadColumnFamilies();
|
||||
@@ -618,68 +577,66 @@ void DBImpl::PrintStatistics() {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::StartTimedTasks() {
|
||||
unsigned int stats_dump_period_sec = 0;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
stats_dump_period_sec = mutable_db_options_.stats_dump_period_sec;
|
||||
if (stats_dump_period_sec > 0) {
|
||||
if (!thread_dump_stats_) {
|
||||
thread_dump_stats_.reset(new rocksdb::RepeatableThread(
|
||||
[this]() { DBImpl::DumpStats(); }, "dump_st", env_,
|
||||
stats_dump_period_sec * 1000000));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void DBImpl::MaybeDumpStats() {
|
||||
mutex_.Lock();
|
||||
unsigned int stats_dump_period_sec =
|
||||
mutable_db_options_.stats_dump_period_sec;
|
||||
mutex_.Unlock();
|
||||
if (stats_dump_period_sec == 0) return;
|
||||
|
||||
const uint64_t now_micros = env_->NowMicros();
|
||||
|
||||
if (last_stats_dump_time_microsec_ + stats_dump_period_sec * 1000000 <=
|
||||
now_micros) {
|
||||
// Multiple threads could race in here simultaneously.
|
||||
// However, the last one will update last_stats_dump_time_microsec_
|
||||
// atomically. We could see more than one dump during one dump
|
||||
// period in rare cases.
|
||||
last_stats_dump_time_microsec_ = now_micros;
|
||||
|
||||
void DBImpl::DumpStats() {
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:1");
|
||||
#ifndef ROCKSDB_LITE
|
||||
const DBPropertyInfo* cf_property_info =
|
||||
GetPropertyInfo(DB::Properties::kCFStats);
|
||||
assert(cf_property_info != nullptr);
|
||||
const DBPropertyInfo* db_property_info =
|
||||
GetPropertyInfo(DB::Properties::kDBStats);
|
||||
assert(db_property_info != nullptr);
|
||||
const DBPropertyInfo* cf_property_info =
|
||||
GetPropertyInfo(DB::Properties::kCFStats);
|
||||
assert(cf_property_info != nullptr);
|
||||
const DBPropertyInfo* db_property_info =
|
||||
GetPropertyInfo(DB::Properties::kDBStats);
|
||||
assert(db_property_info != nullptr);
|
||||
|
||||
std::string stats;
|
||||
if (shutdown_initiated_) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
default_cf_internal_stats_->GetStringProperty(
|
||||
*db_property_info, DB::Properties::kDBStats, &stats);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
cfd->internal_stats()->GetStringProperty(
|
||||
*cf_property_info, DB::Properties::kCFStatsNoFileHistogram, &stats);
|
||||
std::string stats;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
default_cf_internal_stats_->GetStringProperty(
|
||||
*db_property_info, DB::Properties::kDBStats, &stats);
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
cfd->internal_stats()->GetStringProperty(
|
||||
*cf_property_info, DB::Properties::kCFStatsNoFileHistogram,
|
||||
&stats);
|
||||
}
|
||||
}
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
cfd->internal_stats()->GetStringProperty(
|
||||
*cf_property_info, DB::Properties::kCFFileHistogram, &stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->initialized()) {
|
||||
cfd->internal_stats()->GetStringProperty(
|
||||
*cf_property_info, DB::Properties::kCFFileHistogram, &stats);
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"------- DUMPING STATS -------");
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "%s", stats.c_str());
|
||||
if (immutable_db_options_.dump_malloc_stats) {
|
||||
stats.clear();
|
||||
DumpMallocStats(&stats);
|
||||
if (!stats.empty()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"------- Malloc STATS -------");
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "%s", stats.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::DumpStats:2");
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"------- DUMPING STATS -------");
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "%s", stats.c_str());
|
||||
if (immutable_db_options_.dump_malloc_stats) {
|
||||
stats.clear();
|
||||
DumpMallocStats(&stats);
|
||||
if (!stats.empty()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"------- Malloc STATS -------");
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "%s", stats.c_str());
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
PrintStatistics();
|
||||
PrintStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::ScheduleBgLogWriterClose(JobContext* job_context) {
|
||||
@@ -730,6 +687,7 @@ Status DBImpl::SetOptions(
|
||||
MutableCFOptions new_options;
|
||||
Status s;
|
||||
Status persist_options_status;
|
||||
WriteThread::Writer w;
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -791,6 +749,7 @@ Status DBImpl::SetDBOptions(
|
||||
Status s;
|
||||
Status persist_options_status;
|
||||
bool wal_changed = false;
|
||||
WriteThread::Writer w;
|
||||
WriteContext write_context;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -803,22 +762,7 @@ Status DBImpl::SetDBOptions(
|
||||
new_options.max_background_compactions, Env::Priority::LOW);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
if (new_options.stats_dump_period_sec !=
|
||||
mutable_db_options_.stats_dump_period_sec) {
|
||||
if (thread_dump_stats_) {
|
||||
mutex_.Unlock();
|
||||
thread_dump_stats_->cancel();
|
||||
mutex_.Lock();
|
||||
}
|
||||
if (new_options.stats_dump_period_sec > 0) {
|
||||
thread_dump_stats_.reset(new rocksdb::RepeatableThread(
|
||||
[this]() { DBImpl::DumpStats(); }, "dump_st", env_,
|
||||
new_options.stats_dump_period_sec * 1000000));
|
||||
}
|
||||
else {
|
||||
thread_dump_stats_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
write_controller_.set_max_delayed_write_rate(
|
||||
new_options.delayed_write_rate);
|
||||
table_cache_.get()->SetCapacity(new_options.max_open_files == -1
|
||||
@@ -839,7 +783,6 @@ Status DBImpl::SetDBOptions(
|
||||
env_options_for_compaction_, immutable_db_options_);
|
||||
env_options_for_compaction_.compaction_readahead_size =
|
||||
mutable_db_options_.compaction_readahead_size;
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
if (total_log_size_ > GetMaxTotalWalSize() || wal_changed) {
|
||||
Status purge_wal_status = SwitchWAL(&write_context);
|
||||
@@ -1032,7 +975,7 @@ bool DBImpl::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) {
|
||||
}
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
Arena* arena, RangeDelAggregator* range_del_agg,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
@@ -1046,8 +989,8 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
SuperVersion* super_version = cfd->GetSuperVersion()->Ref();
|
||||
mutex_.Unlock();
|
||||
ReadOptions roptions;
|
||||
return NewInternalIterator(roptions, cfd, super_version, arena, range_del_agg,
|
||||
sequence);
|
||||
return NewInternalIterator(roptions, cfd, super_version, arena,
|
||||
range_del_agg);
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePurge() {
|
||||
@@ -1149,12 +1092,10 @@ static void CleanupIteratorState(void* arg1, void* /*arg2*/) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber sequence) {
|
||||
InternalIterator* DBImpl::NewInternalIterator(
|
||||
const ReadOptions& read_options, ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version, Arena* arena,
|
||||
RangeDelAggregator* range_del_agg) {
|
||||
InternalIterator* internal_iter;
|
||||
assert(arena != nullptr);
|
||||
assert(range_del_agg != nullptr);
|
||||
@@ -1166,12 +1107,12 @@ InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
// Collect iterator for mutable mem
|
||||
merge_iter_builder.AddIterator(
|
||||
super_version->mem->NewIterator(read_options, arena));
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter;
|
||||
std::unique_ptr<InternalIterator> range_del_iter;
|
||||
Status s;
|
||||
if (!read_options.ignore_range_deletions) {
|
||||
range_del_iter.reset(
|
||||
super_version->mem->NewRangeTombstoneIterator(read_options, sequence));
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
super_version->mem->NewRangeTombstoneIterator(read_options));
|
||||
s = range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
}
|
||||
// Collect all needed child iterators for immutable memtables
|
||||
if (s.ok()) {
|
||||
@@ -1272,7 +1213,7 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
|
||||
// Prepare to store a list of merge operations if merge occurs.
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
RangeDelAggregator range_del_agg(cfd->internal_comparator(), snapshot);
|
||||
|
||||
Status s;
|
||||
// First look in the memtable, then in the immutable memtable (if any).
|
||||
@@ -1286,14 +1227,13 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
bool done = false;
|
||||
if (!skip_memtable) {
|
||||
if (sv->mem->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, callback,
|
||||
is_blob_index)) {
|
||||
&range_del_agg, read_options, callback, is_blob_index)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
sv->imm->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, callback,
|
||||
&range_del_agg, read_options, callback,
|
||||
is_blob_index)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
@@ -1307,8 +1247,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
sv->current->Get(read_options, lkey, pinnable_val, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, value_found, nullptr, nullptr,
|
||||
callback, is_blob_index);
|
||||
&range_del_agg, value_found, nullptr, nullptr, callback,
|
||||
is_blob_index);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
|
||||
@@ -1390,7 +1330,8 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
|
||||
LookupKey lkey(keys[i], snapshot);
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family[i]);
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
RangeDelAggregator range_del_agg(cfh->cfd()->internal_comparator(),
|
||||
snapshot);
|
||||
auto mgd_iter = multiget_cf_data.find(cfh->cfd()->GetID());
|
||||
assert(mgd_iter != multiget_cf_data.end());
|
||||
auto mgd = mgd_iter->second;
|
||||
@@ -1401,12 +1342,11 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
bool done = false;
|
||||
if (!skip_memtable) {
|
||||
if (super_version->mem->Get(lkey, value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
&range_del_agg, read_options)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if (super_version->imm->Get(lkey, value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq,
|
||||
read_options)) {
|
||||
&range_del_agg, read_options)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
@@ -1415,7 +1355,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
PinnableSlice pinnable_val;
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(read_options, lkey, &pinnable_val, &s,
|
||||
&merge_context, &max_covering_tombstone_seq);
|
||||
&merge_context, &range_del_agg);
|
||||
value->assign(pinnable_val.data(), pinnable_val.size());
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
@@ -1843,7 +1783,7 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot);
|
||||
db_iter->GetRangeDelAggregator());
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
|
||||
return db_iter;
|
||||
@@ -2214,18 +2154,17 @@ ColumnFamilyHandle* DBImpl::GetColumnFamilyHandle(uint32_t column_family_id) {
|
||||
}
|
||||
|
||||
// REQUIRED: mutex is NOT held.
|
||||
std::unique_ptr<ColumnFamilyHandle> DBImpl::GetColumnFamilyHandleUnlocked(
|
||||
ColumnFamilyHandle* DBImpl::GetColumnFamilyHandleUnlocked(
|
||||
uint32_t column_family_id) {
|
||||
ColumnFamilyMemTables* cf_memtables = column_family_memtables_.get();
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
auto* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(column_family_id);
|
||||
if (cfd == nullptr) {
|
||||
if (!cf_memtables->Seek(column_family_id)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<ColumnFamilyHandleImpl>(
|
||||
new ColumnFamilyHandleImpl(cfd, this, &mutex_));
|
||||
return cf_memtables->GetColumnFamilyHandle();
|
||||
}
|
||||
|
||||
void DBImpl::GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
@@ -2296,8 +2235,9 @@ void DBImpl::ReleaseFileNumberFromPendingOutputs(
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status DBImpl::GetUpdatesSince(
|
||||
SequenceNumber seq, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
SequenceNumber seq, unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options) {
|
||||
|
||||
RecordTick(stats_, GET_UPDATES_SINCE_CALLS);
|
||||
if (seq > versions_->LastSequence()) {
|
||||
return Status::NotFound("Requested sequence not yet written in the db");
|
||||
@@ -2545,10 +2485,10 @@ Status DBImpl::CheckConsistency() {
|
||||
Status DBImpl::GetDbIdentity(std::string& identity) const {
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
const EnvOptions soptions;
|
||||
std::unique_ptr<SequentialFileReader> id_file_reader;
|
||||
unique_ptr<SequentialFileReader> id_file_reader;
|
||||
Status s;
|
||||
{
|
||||
std::unique_ptr<SequentialFile> idfile;
|
||||
unique_ptr<SequentialFile> idfile;
|
||||
s = env_->NewSequentialFile(idfilename, &idfile, soptions);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -2945,7 +2885,8 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
bool* is_blob_index) {
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
RangeDelAggregator range_del_agg(sv->mem->GetInternalKeyComparator(),
|
||||
kMaxSequenceNumber);
|
||||
|
||||
ReadOptions read_options;
|
||||
SequenceNumber current_seq = versions_->LastSequence();
|
||||
@@ -2955,8 +2896,8 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
*found_record_for_key = false;
|
||||
|
||||
// Check if there is a record for this key in the latest memtable
|
||||
sv->mem->Get(lkey, nullptr, &s, &merge_context, &max_covering_tombstone_seq,
|
||||
seq, read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
sv->mem->Get(lkey, nullptr, &s, &merge_context, &range_del_agg, seq,
|
||||
read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading memtable.
|
||||
@@ -2974,8 +2915,8 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
}
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->Get(lkey, nullptr, &s, &merge_context, &max_covering_tombstone_seq,
|
||||
seq, read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
sv->imm->Get(lkey, nullptr, &s, &merge_context, &range_del_agg, seq,
|
||||
read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading memtable.
|
||||
@@ -2993,9 +2934,8 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
}
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->GetFromHistory(lkey, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, seq, read_options,
|
||||
is_blob_index);
|
||||
sv->imm->GetFromHistory(lkey, nullptr, &s, &merge_context, &range_del_agg,
|
||||
seq, read_options, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading memtable.
|
||||
@@ -3018,7 +2958,7 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
if (!cache_only) {
|
||||
// Check tables
|
||||
sv->current->Get(read_options, lkey, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, nullptr /* value_found */,
|
||||
&range_del_agg, nullptr /* value_found */,
|
||||
found_record_for_key, seq, nullptr /*read_callback*/,
|
||||
is_blob_index);
|
||||
|
||||
@@ -3027,10 +2967,12 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Unexpected status returned from Version::Get: %s\n",
|
||||
s.ToString().c_str());
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::IngestExternalFile(
|
||||
@@ -3132,21 +3074,12 @@ Status DBImpl::IngestExternalFile(
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::IngestExternalFile:NeedFlush",
|
||||
&need_flush);
|
||||
if (status.ok() && need_flush) {
|
||||
mutex_.Unlock();
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(cfds, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
} else {
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
}
|
||||
status = FlushMemTable(cfd, flush_opts,
|
||||
FlushReason::kExternalFileIngestion,
|
||||
true /* writes_stopped */);
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
@@ -3280,10 +3213,10 @@ void DBImpl::WaitForIngestFile() {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::StartTrace(const TraceOptions& trace_options,
|
||||
Status DBImpl::StartTrace(const TraceOptions& /* options */,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
tracer_.reset(new Tracer(env_, trace_options, std::move(trace_writer)));
|
||||
tracer_.reset(new Tracer(env_, std::move(trace_writer)));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
+25
-81
@@ -31,7 +31,6 @@
|
||||
#include "db/log_writer.h"
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
#include "db/pre_release_callback.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
@@ -54,7 +53,6 @@
|
||||
#include "util/autovector.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/repeatable_thread.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "util/trace_replay.h"
|
||||
@@ -229,9 +227,6 @@ class DBImpl : public DB {
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status Flush(
|
||||
const FlushOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
virtual Status FlushWAL(bool sync) override;
|
||||
bool TEST_WALBufferIsEmpty();
|
||||
virtual Status SyncWAL() override;
|
||||
@@ -260,9 +255,9 @@ class DBImpl : public DB {
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& files) override;
|
||||
|
||||
virtual Status GetUpdatesSince(
|
||||
SequenceNumber seq_number, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions& read_options =
|
||||
TransactionLogIterator::ReadOptions()) override;
|
||||
SequenceNumber seq_number, unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions&
|
||||
read_options = TransactionLogIterator::ReadOptions()) override;
|
||||
virtual Status DeleteFile(std::string name) override;
|
||||
Status DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
@@ -374,7 +369,7 @@ class DBImpl : public DB {
|
||||
// The keys of this iterator are internal keys (see format.h).
|
||||
// The returned iterator should be deleted when no longer needed.
|
||||
InternalIterator* NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence,
|
||||
Arena* arena, RangeDelAggregator* range_del_agg,
|
||||
ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
LogsWithPrepTracker* logs_with_prep_tracker() {
|
||||
@@ -470,7 +465,6 @@ class DBImpl : public DB {
|
||||
int TEST_BGCompactionsAllowed() const;
|
||||
int TEST_BGFlushesAllowed() const;
|
||||
size_t TEST_GetWalPreallocateBlockSize(uint64_t write_buffer_size) const;
|
||||
void TEST_WaitForTimedTaskRun(std::function<void()> callback) const;
|
||||
|
||||
#endif // NDEBUG
|
||||
|
||||
@@ -492,14 +486,6 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t MinLogNumberToKeep();
|
||||
|
||||
// Returns the lower bound file number for SSTs that won't be deleted, even if
|
||||
// they're obsolete. This lower bound is used internally to prevent newly
|
||||
// created flush/compaction output files from being deleted before they're
|
||||
// installed. This technique avoids the need for tracking the exact numbers of
|
||||
// files pending creation, although it prevents more files than necessary from
|
||||
// being deleted.
|
||||
uint64_t MinObsoleteSstNumberToKeep();
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
// of all files in the filesystem in 'candidate_files'.
|
||||
// If force == false and the last call was less than
|
||||
@@ -559,8 +545,7 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle(uint32_t column_family_id);
|
||||
|
||||
// Same as above, should called without mutex held and not on write thread.
|
||||
std::unique_ptr<ColumnFamilyHandle> GetColumnFamilyHandleUnlocked(
|
||||
uint32_t column_family_id);
|
||||
ColumnFamilyHandle* GetColumnFamilyHandleUnlocked(uint32_t column_family_id);
|
||||
|
||||
// Returns the number of currently running flushes.
|
||||
// REQUIREMENT: mutex_ must be held when calling this function.
|
||||
@@ -578,9 +563,11 @@ class DBImpl : public DB {
|
||||
|
||||
const WriteController& write_controller() { return write_controller_; }
|
||||
|
||||
InternalIterator* NewInternalIterator(
|
||||
const ReadOptions&, ColumnFamilyData* cfd, SuperVersion* super_version,
|
||||
Arena* arena, RangeDelAggregator* range_del_agg, SequenceNumber sequence);
|
||||
InternalIterator* NewInternalIterator(const ReadOptions&,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg);
|
||||
|
||||
// hollow transactions shell used for recovery.
|
||||
// these will then be passed to TransactionDB so that
|
||||
@@ -713,7 +700,7 @@ class DBImpl : public DB {
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
const DBOptions initial_db_options_;
|
||||
@@ -745,11 +732,6 @@ class DBImpl : public DB {
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, TableProperties prop);
|
||||
|
||||
void NotifyOnCompactionBegin(ColumnFamilyData* cfd,
|
||||
Compaction *c, const Status &st,
|
||||
const CompactionJobStats& job_stats,
|
||||
int job_id);
|
||||
|
||||
void NotifyOnCompactionCompleted(ColumnFamilyData* cfd,
|
||||
Compaction *c, const Status &st,
|
||||
const CompactionJobStats& job_stats,
|
||||
@@ -918,18 +900,18 @@ class DBImpl : public DB {
|
||||
// Argument required by background flush thread.
|
||||
struct BGFlushArg {
|
||||
BGFlushArg()
|
||||
: cfd_(nullptr), max_memtable_id_(0), superversion_context_(nullptr) {}
|
||||
BGFlushArg(ColumnFamilyData* cfd, uint64_t max_memtable_id,
|
||||
: cfd_(nullptr), memtable_id_(0), superversion_context_(nullptr) {}
|
||||
BGFlushArg(ColumnFamilyData* cfd, uint64_t memtable_id,
|
||||
SuperVersionContext* superversion_context)
|
||||
: cfd_(cfd),
|
||||
max_memtable_id_(max_memtable_id),
|
||||
memtable_id_(memtable_id),
|
||||
superversion_context_(superversion_context) {}
|
||||
|
||||
// Column family to flush.
|
||||
ColumnFamilyData* cfd_;
|
||||
// Maximum ID of memtable to flush. In this column family, memtables with
|
||||
// IDs smaller than this value must be flushed before this flush completes.
|
||||
uint64_t max_memtable_id_;
|
||||
uint64_t memtable_id_;
|
||||
// Pointer to a SuperVersionContext object. After flush completes, RocksDB
|
||||
// installs a new superversion for the column family. This operation
|
||||
// requires a SuperVersionContext object (currently embedded in JobContext).
|
||||
@@ -942,10 +924,6 @@ class DBImpl : public DB {
|
||||
const autovector<BGFlushArg>& bg_flush_args, bool* made_progress,
|
||||
JobContext* job_context, LogBuffer* log_buffer);
|
||||
|
||||
Status AtomicFlushMemTablesToOutputFiles(
|
||||
const autovector<BGFlushArg>& bg_flush_args, bool* made_progress,
|
||||
JobContext* job_context, LogBuffer* log_buffer);
|
||||
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only);
|
||||
@@ -975,17 +953,10 @@ class DBImpl : public DB {
|
||||
|
||||
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context);
|
||||
|
||||
void SelectColumnFamiliesForAtomicFlush(autovector<ColumnFamilyData*>* cfds);
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
FlushReason flush_reason, bool writes_stopped = false);
|
||||
|
||||
Status AtomicFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& column_family_datas,
|
||||
const FlushOptions& options, FlushReason flush_reason,
|
||||
bool writes_stopped = false);
|
||||
|
||||
// Wait until flushing this column family won't stall writes
|
||||
Status WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
bool* flush_needed);
|
||||
@@ -994,22 +965,14 @@ class DBImpl : public DB {
|
||||
// If flush_memtable_id is non-null, wait until the memtable with the ID
|
||||
// gets flush. Otherwise, wait until the column family don't have any
|
||||
// memtable pending flush.
|
||||
// resuming_from_bg_err indicates whether the caller is attempting to resume
|
||||
// from background error.
|
||||
Status WaitForFlushMemTable(ColumnFamilyData* cfd,
|
||||
const uint64_t* flush_memtable_id = nullptr,
|
||||
bool resuming_from_bg_err = false) {
|
||||
return WaitForFlushMemTables({cfd}, {flush_memtable_id},
|
||||
resuming_from_bg_err);
|
||||
const uint64_t* flush_memtable_id = nullptr) {
|
||||
return WaitForFlushMemTables({cfd}, {flush_memtable_id});
|
||||
}
|
||||
// Wait for memtables to be flushed for multiple column families.
|
||||
Status WaitForFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const uint64_t*>& flush_memtable_ids,
|
||||
bool resuming_from_bg_err);
|
||||
|
||||
// REQUIRES: mutex locked and in write thread.
|
||||
void AssignAtomicFlushSeq(const autovector<ColumnFamilyData*>& cfds);
|
||||
const autovector<const uint64_t*>& flush_memtable_ids);
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status SwitchWAL(WriteContext* write_context);
|
||||
@@ -1074,9 +1037,6 @@ class DBImpl : public DB {
|
||||
// column families in this request, this flush is considered complete.
|
||||
typedef std::vector<std::pair<ColumnFamilyData*, uint64_t>> FlushRequest;
|
||||
|
||||
void GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
FlushRequest* req);
|
||||
|
||||
void SchedulePendingFlush(const FlushRequest& req, FlushReason flush_reason);
|
||||
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
@@ -1103,13 +1063,10 @@ class DBImpl : public DB {
|
||||
const std::vector<CompactionInputFiles>& inputs,
|
||||
bool* sfm_bookkeeping, LogBuffer* log_buffer);
|
||||
|
||||
// Schedule background tasks
|
||||
void StartTimedTasks();
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
// dump rocksdb.stats to LOG
|
||||
void DumpStats();
|
||||
void MaybeDumpStats();
|
||||
|
||||
// Return the minimum empty level that could hold the total data in the
|
||||
// input level. Return the input level, if such level could not be found.
|
||||
@@ -1137,6 +1094,8 @@ class DBImpl : public DB {
|
||||
|
||||
Status CloseHelper();
|
||||
|
||||
Status FlushAllCFs(FlushReason flush_reason);
|
||||
|
||||
void WaitForBackgroundWork();
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
@@ -1189,7 +1148,7 @@ class DBImpl : public DB {
|
||||
bool log_empty_;
|
||||
ColumnFamilyHandleImpl* default_cf_handle_;
|
||||
InternalStats* default_cf_internal_stats_;
|
||||
std::unique_ptr<ColumnFamilyMemTablesImpl> column_family_memtables_;
|
||||
unique_ptr<ColumnFamilyMemTablesImpl> column_family_memtables_;
|
||||
struct LogFileNumberSize {
|
||||
explicit LogFileNumberSize(uint64_t _number)
|
||||
: number(_number) {}
|
||||
@@ -1217,7 +1176,7 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t number;
|
||||
// Visual Studio doesn't support deque's member to be noncopyable because
|
||||
// of a std::unique_ptr as a member.
|
||||
// of a unique_ptr as a member.
|
||||
log::Writer* writer; // own
|
||||
// true for some prefix of logs_
|
||||
bool getting_synced = false;
|
||||
@@ -1303,7 +1262,7 @@ class DBImpl : public DB {
|
||||
|
||||
WriteController write_controller_;
|
||||
|
||||
std::unique_ptr<RateLimiter> low_pri_write_rate_limiter_;
|
||||
unique_ptr<RateLimiter> low_pri_write_rate_limiter_;
|
||||
|
||||
// Size of the last batch group. In slowdown mode, next write needs to
|
||||
// sleep if it uses up the quota.
|
||||
@@ -1510,10 +1469,6 @@ class DBImpl : public DB {
|
||||
// Only to be set during initialization
|
||||
std::unique_ptr<PreReleaseCallback> recoverable_state_pre_release_callback_;
|
||||
|
||||
// handle for scheduling jobs at fixed intervals
|
||||
// REQUIRES: mutex locked
|
||||
std::unique_ptr<rocksdb::RepeatableThread> thread_dump_stats_;
|
||||
|
||||
// No copying allowed
|
||||
DBImpl(const DBImpl&);
|
||||
void operator=(const DBImpl&);
|
||||
@@ -1593,7 +1548,7 @@ class DBImpl : public DB {
|
||||
// error recovery from going on in parallel. The latter, shutting_down_,
|
||||
// is set a little later during the shutdown after scheduling memtable
|
||||
// flushes
|
||||
std::atomic<bool> shutdown_initiated_;
|
||||
bool shutdown_initiated_;
|
||||
// Flag to indicate whether sst_file_manager object was allocated in
|
||||
// DB::Open() or passed to us
|
||||
bool own_sfm_;
|
||||
@@ -1609,17 +1564,6 @@ class DBImpl : public DB {
|
||||
bool closed_;
|
||||
|
||||
ErrorHandler error_handler_;
|
||||
|
||||
// Conditional variable to coordinate installation of atomic flush results.
|
||||
// With atomic flush, each bg thread installs the result of flushing multiple
|
||||
// column families, and different threads can flush different column
|
||||
// families. It's difficult to rely on one thread to perform batch
|
||||
// installation for all threads. This is different from the non-atomic flush
|
||||
// case.
|
||||
// atomic_flush_install_cv_ makes sure that threads install atomic flush
|
||||
// results sequentially. Flush results of memtables with lower IDs get
|
||||
// installed to MANIFEST first.
|
||||
InstrumentedCondVar atomic_flush_install_cv_;
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
|
||||
+93
-571
@@ -125,19 +125,15 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
nullptr /* memtable_id */, env_options_for_compaction_, versions_.get(),
|
||||
&mutex_, &shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, job_context, log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(cfd, 0U),
|
||||
env_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
job_context, log_buffer, directories_.GetDbDir(), GetDataDir(cfd, 0U),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */);
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats);
|
||||
|
||||
FileMetaData file_meta;
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforePickMemtables");
|
||||
flush_job.PickMemTable();
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:AfterPickMemtables");
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
@@ -147,7 +143,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
|
||||
Status s;
|
||||
if (logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1) {
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 0) {
|
||||
// If there are more than one column families, we need to make sure that
|
||||
// all the log files except the most recent one are synced. Otherwise if
|
||||
// the host crashes after flushing and before WAL is persistent, the
|
||||
@@ -155,8 +151,6 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// other column families are missing.
|
||||
// SyncClosedLogs() may unlock and re-lock the db_mutex.
|
||||
s = SyncClosedLogs(job_context);
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Skip");
|
||||
}
|
||||
|
||||
// Within flush_job.Run, rocksdb may call event listener to notify
|
||||
@@ -175,7 +169,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
InstallSuperVersionAndScheduleWork(cfd, superversion_context,
|
||||
mutable_cf_options);
|
||||
if (made_progress) {
|
||||
*made_progress = true;
|
||||
*made_progress = 1;
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Level summary: %s\n",
|
||||
@@ -215,297 +209,19 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
const autovector<BGFlushArg>& bg_flush_args, bool* made_progress,
|
||||
JobContext* job_context, LogBuffer* log_buffer) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
return AtomicFlushMemTablesToOutputFiles(bg_flush_args, made_progress,
|
||||
job_context, log_buffer);
|
||||
}
|
||||
Status status;
|
||||
Status s;
|
||||
for (auto& arg : bg_flush_args) {
|
||||
ColumnFamilyData* cfd = arg.cfd_;
|
||||
MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
const MutableCFOptions& mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
SuperVersionContext* superversion_context = arg.superversion_context_;
|
||||
Status s = FlushMemTableToOutputFile(cfd, mutable_cf_options, made_progress,
|
||||
job_context, superversion_context,
|
||||
log_buffer);
|
||||
s = FlushMemTableToOutputFile(cfd, mutable_cf_options, made_progress,
|
||||
job_context, superversion_context,
|
||||
log_buffer);
|
||||
if (!s.ok()) {
|
||||
status = s;
|
||||
if (!s.IsShutdownInProgress()) {
|
||||
// At this point, DB is not shutting down, nor is cfd dropped.
|
||||
// Something is wrong, thus we break out of the loop.
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Atomically flushes multiple column families.
|
||||
*
|
||||
* For each column family, all memtables with ID smaller than or equal to the
|
||||
* ID specified in bg_flush_args will be flushed. Only after all column
|
||||
* families finish flush will this function commit to MANIFEST. If any of the
|
||||
* column families are not flushed successfully, this function does not have
|
||||
* any side-effect on the state of the database.
|
||||
*/
|
||||
Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
const autovector<BGFlushArg>& bg_flush_args, bool* made_progress,
|
||||
JobContext* job_context, LogBuffer* log_buffer) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
for (const auto& arg : bg_flush_args) {
|
||||
cfds.emplace_back(arg.cfd_);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (const auto cfd : cfds) {
|
||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||
assert(cfd->imm()->IsFlushPending());
|
||||
}
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
autovector<Directory*> distinct_output_dirs;
|
||||
std::vector<FlushJob> jobs;
|
||||
std::vector<MutableCFOptions> all_mutable_cf_options;
|
||||
int num_cfs = static_cast<int>(cfds.size());
|
||||
all_mutable_cf_options.reserve(num_cfs);
|
||||
for (int i = 0; i < num_cfs; ++i) {
|
||||
auto cfd = cfds[i];
|
||||
Directory* data_dir = GetDataDir(cfd, 0U);
|
||||
|
||||
// Add to distinct output directories if eligible. Use linear search. Since
|
||||
// the number of elements in the vector is not large, performance should be
|
||||
// tolerable.
|
||||
bool found = false;
|
||||
for (const auto dir : distinct_output_dirs) {
|
||||
if (dir == data_dir) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
distinct_output_dirs.emplace_back(data_dir);
|
||||
}
|
||||
|
||||
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
|
||||
const uint64_t* max_memtable_id = &(bg_flush_args[i].max_memtable_id_);
|
||||
jobs.emplace_back(
|
||||
dbname_, cfds[i], immutable_db_options_, mutable_cf_options,
|
||||
max_memtable_id, env_options_for_compaction_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, job_context, log_buffer, directories_.GetDbDir(),
|
||||
data_dir, GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
stats_, &event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */);
|
||||
jobs.back().PickMemTable();
|
||||
}
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
Status s;
|
||||
assert(num_cfs == static_cast<int>(jobs.size()));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.at(i);
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfds[i], &file_meta[i], mutable_cf_options,
|
||||
job_context->job_id, jobs[i].GetTableProperties());
|
||||
}
|
||||
#endif /* !ROCKSDB_LITE */
|
||||
|
||||
if (logfile_number_ > 0) {
|
||||
// TODO (yanqin) investigate whether we should sync the closed logs for
|
||||
// single column family case.
|
||||
s = SyncClosedLogs(job_context);
|
||||
}
|
||||
|
||||
// exec_status stores the execution status of flush_jobs as
|
||||
// <bool /* executed */, Status /* status code */>
|
||||
autovector<std::pair<bool, Status>> exec_status;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
// Initially all jobs are not executed, with status OK.
|
||||
exec_status.emplace_back(false, Status::OK());
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
// TODO (yanqin): parallelize jobs with threads.
|
||||
for (int i = 1; i != num_cfs; ++i) {
|
||||
exec_status[i].second =
|
||||
jobs[i].Run(&logs_with_prep_tracker_, &file_meta[i]);
|
||||
exec_status[i].first = true;
|
||||
}
|
||||
if (num_cfs > 1) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::AtomicFlushMemTablesToOutputFiles:SomeFlushJobsComplete:1");
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::AtomicFlushMemTablesToOutputFiles:SomeFlushJobsComplete:2");
|
||||
}
|
||||
exec_status[0].second =
|
||||
jobs[0].Run(&logs_with_prep_tracker_, &file_meta[0]);
|
||||
exec_status[0].first = true;
|
||||
|
||||
Status error_status;
|
||||
for (const auto& e : exec_status) {
|
||||
if (!e.second.ok()) {
|
||||
s = e.second;
|
||||
if (!e.second.IsShutdownInProgress()) {
|
||||
// If a flush job did not return OK, and the CF is not dropped, and
|
||||
// the DB is not shutting down, then we have to return this result to
|
||||
// caller later.
|
||||
error_status = e.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s = error_status.ok() ? s : error_status;
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsShutdownInProgress()) {
|
||||
// Sync on all distinct output directories.
|
||||
for (auto dir : distinct_output_dirs) {
|
||||
if (dir != nullptr) {
|
||||
s = dir->Fsync();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
auto wait_to_install_func = [&]() {
|
||||
bool ready = true;
|
||||
for (size_t i = 0; i != cfds.size(); ++i) {
|
||||
const auto& mems = jobs[i].GetMemTables();
|
||||
if (cfds[i]->IsDropped()) {
|
||||
// If the column family is dropped, then do not wait.
|
||||
continue;
|
||||
} else if (!mems.empty() &&
|
||||
cfds[i]->imm()->GetEarliestMemTableID() < mems[0]->GetID()) {
|
||||
// If a flush job needs to install the flush result for mems and
|
||||
// mems[0] is not the earliest memtable, it means another thread must
|
||||
// be installing flush results for the same column family, then the
|
||||
// current thread needs to wait.
|
||||
ready = false;
|
||||
break;
|
||||
} else if (mems.empty() && cfds[i]->imm()->GetEarliestMemTableID() <=
|
||||
bg_flush_args[i].max_memtable_id_) {
|
||||
// If a flush job does not need to install flush results, then it has
|
||||
// to wait until all memtables up to max_memtable_id_ (inclusive) are
|
||||
// installed.
|
||||
ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ready;
|
||||
};
|
||||
|
||||
bool resuming_from_bg_err = error_handler_.IsDBStopped();
|
||||
while ((!error_handler_.IsDBStopped() ||
|
||||
error_handler_.GetRecoveryError().ok()) &&
|
||||
!wait_to_install_func()) {
|
||||
atomic_flush_install_cv_.Wait();
|
||||
}
|
||||
|
||||
s = resuming_from_bg_err ? error_handler_.GetRecoveryError()
|
||||
: error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
autovector<ColumnFamilyData*> tmp_cfds;
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<FileMetaData*> tmp_file_meta;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const auto& mems = jobs[i].GetMemTables();
|
||||
if (!cfds[i]->IsDropped() && !mems.empty()) {
|
||||
tmp_cfds.emplace_back(cfds[i]);
|
||||
mems_list.emplace_back(&mems);
|
||||
mutable_cf_options_list.emplace_back(&all_mutable_cf_options[i]);
|
||||
tmp_file_meta.emplace_back(&file_meta[i]);
|
||||
}
|
||||
}
|
||||
|
||||
s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, tmp_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), &mutex_, tmp_file_meta,
|
||||
&job_context->memtables_to_free, directories_.GetDbDir(), log_buffer);
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsShutdownInProgress()) {
|
||||
assert(num_cfs ==
|
||||
static_cast<int>(job_context->superversion_contexts.size()));
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
InstallSuperVersionAndScheduleWork(cfds[i],
|
||||
&job_context->superversion_contexts[i],
|
||||
all_mutable_cf_options[i]);
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Level summary: %s\n",
|
||||
cfds[i]->GetName().c_str(),
|
||||
cfds[i]->current()->storage_info()->LevelSummary(&tmp));
|
||||
}
|
||||
if (made_progress) {
|
||||
*made_progress = true;
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
NotifyOnFlushCompleted(cfds[i], &file_meta[i], all_mutable_cf_options[i],
|
||||
job_context->job_id, jobs[i].GetTableProperties());
|
||||
if (sfm) {
|
||||
std::string file_path = MakeTableFileName(
|
||||
cfds[i]->ioptions()->cf_paths[0].path, file_meta[i].fd.GetNumber());
|
||||
sfm->OnAddFile(file_path);
|
||||
if (sfm->IsMaxAllowedSpaceReached() &&
|
||||
error_handler_.GetBGError().ok()) {
|
||||
Status new_bg_error =
|
||||
Status::SpaceLimit("Max allowed space was reached");
|
||||
error_handler_.SetBGError(new_bg_error,
|
||||
BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
// Have to cancel the flush jobs that have NOT executed because we need to
|
||||
// unref the versions.
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (!exec_status[i].first) {
|
||||
jobs[i].Cancel();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (exec_status[i].first && exec_status[i].second.ok()) {
|
||||
auto& mems = jobs[i].GetMemTables();
|
||||
cfds[i]->imm()->RollbackMemtableFlush(mems,
|
||||
file_meta[i].fd.GetNumber());
|
||||
}
|
||||
}
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -637,17 +353,8 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
if (flush_needed) {
|
||||
FlushOptions fo;
|
||||
fo.allow_write_stall = options.allow_write_stall;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
mutex_.Lock();
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
mutex_.Unlock();
|
||||
s = AtomicFlushMemTables(cfds, fo, FlushReason::kManualCompaction,
|
||||
false /* writes_stopped */);
|
||||
} else {
|
||||
s = FlushMemTable(cfd, fo, FlushReason::kManualCompaction,
|
||||
false /* writes_stopped*/);
|
||||
}
|
||||
s = FlushMemTable(cfd, fo, FlushReason::kManualCompaction,
|
||||
false /* writes_stopped*/);
|
||||
if (!s.ok()) {
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return s;
|
||||
@@ -778,7 +485,7 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
immutable_db_options_.info_log.get());
|
||||
|
||||
// Perform CompactFiles
|
||||
TEST_SYNC_POINT("TestCompactFiles::IngestExternalFile2");
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
@@ -786,16 +493,15 @@ Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
// IngestExternalFile() calls to finish.
|
||||
WaitForIngestFile();
|
||||
|
||||
// We need to get current after `WaitForIngestFile`, because
|
||||
// `IngestExternalFile` may add files that overlap with `input_file_names`
|
||||
auto* current = cfd->current();
|
||||
current->Ref();
|
||||
|
||||
s = CompactFilesImpl(compact_options, cfd, current, input_file_names,
|
||||
s = CompactFilesImpl(compact_options, cfd, sv->current, input_file_names,
|
||||
output_file_names, output_level, output_path_id,
|
||||
&job_context, &log_buffer);
|
||||
|
||||
current->Unref();
|
||||
}
|
||||
if (sv->Unref()) {
|
||||
mutex_.Lock();
|
||||
sv->Cleanup();
|
||||
mutex_.Unlock();
|
||||
delete sv;
|
||||
}
|
||||
|
||||
// Find and delete obsolete files
|
||||
@@ -841,7 +547,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
|
||||
std::unordered_set<uint64_t> input_set;
|
||||
for (const auto& file_name : input_file_names) {
|
||||
for (auto file_name : input_file_names) {
|
||||
input_set.insert(TableFileNameToNumber(file_name));
|
||||
}
|
||||
|
||||
@@ -873,7 +579,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
return s;
|
||||
}
|
||||
|
||||
for (const auto& inputs : input_files) {
|
||||
for (auto inputs : input_files) {
|
||||
if (cfd->compaction_picker()->AreFilesInCompaction(inputs.files)) {
|
||||
return Status::Aborted(
|
||||
"Some of the necessary compaction input "
|
||||
@@ -894,7 +600,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
// At this point, CompactFiles will be run.
|
||||
bg_compaction_scheduled_++;
|
||||
|
||||
std::unique_ptr<Compaction> c;
|
||||
unique_ptr<Compaction> c;
|
||||
assert(cfd->compaction_picker());
|
||||
c.reset(cfd->compaction_picker()->CompactFiles(
|
||||
compact_options, input_files, output_level, version->storage_info(),
|
||||
@@ -1038,69 +744,6 @@ Status DBImpl::ContinueBackgroundWork() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd,
|
||||
Compaction *c, const Status &st,
|
||||
const CompactionJobStats& job_stats,
|
||||
int job_id) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (immutable_db_options_.listeners.empty()) {
|
||||
return;
|
||||
}
|
||||
mutex_.AssertHeld();
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
Version* current = cfd->current();
|
||||
current->Ref();
|
||||
// release lock while notifying events
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("DBImpl::NotifyOnCompactionBegin::UnlockMutex");
|
||||
{
|
||||
CompactionJobInfo info;
|
||||
info.cf_name = cfd->GetName();
|
||||
info.status = st;
|
||||
info.thread_id = env_->GetThreadID();
|
||||
info.job_id = job_id;
|
||||
info.base_input_level = c->start_level();
|
||||
info.output_level = c->output_level();
|
||||
info.stats = job_stats;
|
||||
info.table_properties = c->GetOutputTableProperties();
|
||||
info.compaction_reason = c->compaction_reason();
|
||||
info.compression = c->output_compression();
|
||||
for (size_t i = 0; i < c->num_input_levels(); ++i) {
|
||||
for (const auto fmd : *c->inputs(i)) {
|
||||
auto fn = TableFileName(c->immutable_cf_options()->cf_paths,
|
||||
fmd->fd.GetNumber(), fmd->fd.GetPathId());
|
||||
info.input_files.push_back(fn);
|
||||
if (info.table_properties.count(fn) == 0) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
auto s = current->GetTableProperties(&tp, fmd, &fn);
|
||||
if (s.ok()) {
|
||||
info.table_properties[fn] = tp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto newf : c->edit()->GetNewFiles()) {
|
||||
info.output_files.push_back(TableFileName(
|
||||
c->immutable_cf_options()->cf_paths, newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()));
|
||||
}
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionBegin(this, info);
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
current->Unref();
|
||||
#else
|
||||
(void)cfd;
|
||||
(void)c;
|
||||
(void)st;
|
||||
(void)job_stats;
|
||||
(void)job_id;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnCompactionCompleted(
|
||||
ColumnFamilyData* cfd, Compaction* c, const Status& st,
|
||||
const CompactionJobStats& compaction_job_stats, const int job_id) {
|
||||
@@ -1119,7 +762,6 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
TEST_SYNC_POINT("DBImpl::NotifyOnCompactionCompleted::UnlockMutex");
|
||||
{
|
||||
CompactionJobInfo info;
|
||||
info.cf_id = cfd->GetID();
|
||||
info.cf_name = cfd->GetName();
|
||||
info.status = st;
|
||||
info.thread_id = env_->GetThreadID();
|
||||
@@ -1269,59 +911,73 @@ 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;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
s = AtomicFlushMemTables({cfh->cfd()}, flush_options,
|
||||
FlushReason::kManualFlush);
|
||||
} else {
|
||||
s = FlushMemTable(cfh->cfd(), flush_options, FlushReason::kManualFlush);
|
||||
}
|
||||
|
||||
Status s =
|
||||
FlushMemTable(cfh->cfd(), flush_options, FlushReason::kManualFlush);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] Manual flush finished, status: %s\n",
|
||||
cfh->GetName().c_str(), s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::Flush(const FlushOptions& flush_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) {
|
||||
|
||||
Status DBImpl::FlushAllCFs(FlushReason flush_reason) {
|
||||
Status s;
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
for (auto cfh : column_families) {
|
||||
s = Flush(flush_options, cfh);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
WriteContext context;
|
||||
WriteThread::Writer w;
|
||||
|
||||
mutex_.AssertHeld();
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
|
||||
FlushRequest flush_req;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->imm()->NumNotFlushed() == 0 && cfd->mem()->IsEmpty() &&
|
||||
cached_recoverable_state_empty_.load()) {
|
||||
// Nothing to flush
|
||||
continue;
|
||||
}
|
||||
|
||||
// SwitchMemtable() will release and reacquire mutex during execution
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
cfd->imm()->FlushRequested();
|
||||
|
||||
flush_req.emplace_back(cfd, cfd->imm()->GetLatestMemTableID());
|
||||
}
|
||||
|
||||
// schedule flush
|
||||
if (s.ok() && !flush_req.empty()) {
|
||||
SchedulePendingFlush(flush_req, flush_reason);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto& flush : flush_req) {
|
||||
auto cfd = flush.first;
|
||||
auto flush_memtable_id = flush.second;
|
||||
while (cfd->imm()->NumNotFlushed() > 0 &&
|
||||
cfd->imm()->GetEarliestMemTableID() <= flush_memtable_id) {
|
||||
if (!error_handler_.GetRecoveryError().ok()) {
|
||||
break;
|
||||
}
|
||||
if (cfd->IsDropped()) {
|
||||
// FlushJob cannot flush a dropped CF, if we did not break here
|
||||
// we will loop forever since cfd->imm()->NumNotFlushed() will never
|
||||
// drop to zero
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
bg_cv_.Wait();
|
||||
cfd->Unref();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Manual atomic flush start.\n"
|
||||
"=====Column families:=====");
|
||||
for (auto cfh : column_families) {
|
||||
auto cfhi = static_cast<ColumnFamilyHandleImpl*>(cfh);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "%s",
|
||||
cfhi->GetName().c_str());
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"=====End of column families list=====");
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
std::for_each(column_families.begin(), column_families.end(),
|
||||
[&cfds](ColumnFamilyHandle* elem) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(elem);
|
||||
cfds.emplace_back(cfh->cfd());
|
||||
});
|
||||
s = AtomicFlushMemTables(cfds, flush_options, FlushReason::kManualFlush);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Manual atomic flush finished, status: %s\n",
|
||||
"=====Column families:=====", s.ToString().c_str());
|
||||
for (auto cfh : column_families) {
|
||||
auto cfhi = static_cast<ColumnFamilyHandleImpl*>(cfh);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "%s",
|
||||
cfhi->GetName().c_str());
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"=====End of column families list=====");
|
||||
}
|
||||
|
||||
flush_req.clear();
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1455,20 +1111,6 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
return manual.status;
|
||||
}
|
||||
|
||||
void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
FlushRequest* req) {
|
||||
assert(req != nullptr);
|
||||
req->reserve(cfds.size());
|
||||
for (const auto cfd : cfds) {
|
||||
if (nullptr == cfd) {
|
||||
// cfd may be null, see DBImpl::ScheduleFlushes
|
||||
continue;
|
||||
}
|
||||
uint64_t max_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
req->emplace_back(cfd, max_memtable_id);
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
FlushReason flush_reason, bool writes_stopped) {
|
||||
@@ -1492,16 +1134,11 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
}
|
||||
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
|
||||
if (s.ok() && !flush_req.empty()) {
|
||||
@@ -1525,93 +1162,12 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
cfds.push_back(iter.first);
|
||||
flush_memtable_ids.push_back(&(iter.second));
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery));
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids);
|
||||
}
|
||||
TEST_SYNC_POINT("FlushMemTableFinished");
|
||||
return s;
|
||||
}
|
||||
|
||||
// Flush all elments in 'column_family_datas'
|
||||
// and atomically record the result to the MANIFEST.
|
||||
Status DBImpl::AtomicFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& column_family_datas,
|
||||
const FlushOptions& flush_options, FlushReason flush_reason,
|
||||
bool writes_stopped) {
|
||||
Status s;
|
||||
if (!flush_options.allow_write_stall) {
|
||||
int num_cfs_to_flush = 0;
|
||||
for (auto cfd : column_family_datas) {
|
||||
bool flush_needed = true;
|
||||
s = WaitUntilFlushWouldNotStallWrites(cfd, &flush_needed);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
} else if (flush_needed) {
|
||||
++num_cfs_to_flush;
|
||||
}
|
||||
}
|
||||
if (0 == num_cfs_to_flush) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
{
|
||||
WriteContext context;
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
|
||||
WriteThread::Writer w;
|
||||
if (!writes_stopped) {
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
}
|
||||
|
||||
for (auto cfd : column_family_datas) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
cfds.emplace_back(cfd);
|
||||
}
|
||||
}
|
||||
for (auto cfd : cfds) {
|
||||
if (cfd->mem()->IsEmpty() && cached_recoverable_state_empty_.load()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
cfd->Unref();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
for (auto cfd : cfds) {
|
||||
cfd->imm()->FlushRequested();
|
||||
}
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, flush_reason);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
if (!writes_stopped) {
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
|
||||
|
||||
if (s.ok() && flush_options.wait) {
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
for (auto& iter : flush_req) {
|
||||
flush_memtable_ids.push_back(&(iter.second));
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Calling FlushMemTable(), whether from DB::Flush() or from Backup Engine, can
|
||||
// cause write stall, for example if one memtable is being flushed already.
|
||||
// This method tries to avoid write stall (similar to CompactRange() behavior)
|
||||
@@ -1627,14 +1183,6 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
WriteStallCondition write_stall_condition = WriteStallCondition::kNormal;
|
||||
do {
|
||||
if (write_stall_condition != WriteStallCondition::kNormal) {
|
||||
// Same error handling as user writes: Don't wait if there's a
|
||||
// background error, even if it's a soft error. We might wait here
|
||||
// indefinitely as the pending flushes/compactions may never finish
|
||||
// successfully, resulting in the stall condition lasting indefinitely
|
||||
if (error_handler_.IsBGWorkStopped()) {
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait");
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] WaitUntilFlushWouldNotStallWrites"
|
||||
@@ -1691,25 +1239,16 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
// 2) if flush_memtable_ids[i] is null, then all memtables in THIS column
|
||||
// family have to be flushed.
|
||||
// Finish waiting when ALL column families finish flushing memtables.
|
||||
// resuming_from_bg_err indicates whether the caller is trying to resume from
|
||||
// background error or in normal processing.
|
||||
Status DBImpl::WaitForFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const uint64_t*>& flush_memtable_ids,
|
||||
bool resuming_from_bg_err) {
|
||||
const autovector<const uint64_t*>& flush_memtable_ids) {
|
||||
int num = static_cast<int>(cfds.size());
|
||||
// Wait until the compaction completes
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// If the caller is trying to resume from bg error, then
|
||||
// error_handler_.IsDBStopped() is true.
|
||||
while (resuming_from_bg_err || !error_handler_.IsDBStopped()) {
|
||||
while (!error_handler_.IsDBStopped()) {
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
// If an error has occurred during resumption, then no need to wait.
|
||||
if (!error_handler_.GetRecoveryError().ok()) {
|
||||
break;
|
||||
}
|
||||
// Number of column families that have been dropped.
|
||||
int num_dropped = 0;
|
||||
// Number of column families that have finished flush.
|
||||
@@ -1735,9 +1274,7 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
Status s;
|
||||
// If not resuming from bg error, and an error has caused the DB to stop,
|
||||
// then report the bg error to caller.
|
||||
if (!resuming_from_bg_err && error_handler_.IsDBStopped()) {
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
s = error_handler_.GetBGError();
|
||||
}
|
||||
return s;
|
||||
@@ -2077,7 +1614,6 @@ void DBImpl::BackgroundCallFlush() {
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FlushFinish:0");
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
// If flush failed, we want to delete all temporary files that we might have
|
||||
@@ -2100,14 +1636,12 @@ void DBImpl::BackgroundCallFlush() {
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:ContextCleanedUp");
|
||||
|
||||
assert(num_running_flushes_ > 0);
|
||||
num_running_flushes_--;
|
||||
bg_flush_scheduled_--;
|
||||
// See if there's more work to be done
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
atomic_flush_install_cv_.SignalAll();
|
||||
bg_cv_.SignalAll();
|
||||
// IMPORTANT: there should be no code after calling SignalAll. This call may
|
||||
// signal the DB destructor that it's OK to proceed with destruction. In
|
||||
@@ -2121,6 +1655,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
bool made_progress = false;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
TEST_SYNC_POINT("BackgroundCallCompaction:0");
|
||||
MaybeDumpStats();
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
{
|
||||
@@ -2231,7 +1766,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:Start");
|
||||
|
||||
bool is_manual = (manual_compaction != nullptr);
|
||||
std::unique_ptr<Compaction> c;
|
||||
unique_ptr<Compaction> c;
|
||||
if (prepicked_compaction != nullptr &&
|
||||
prepicked_compaction->compaction != nullptr) {
|
||||
c.reset(prepicked_compaction->compaction);
|
||||
@@ -2264,10 +1799,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
manual_compaction->in_progress = false;
|
||||
manual_compaction = nullptr;
|
||||
}
|
||||
if (c) {
|
||||
c->ReleaseCompactionFiles(status);
|
||||
c.reset();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -2414,9 +1945,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
|
||||
compaction_job_stats.num_input_files = c->num_input_files(0);
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
|
||||
for (const auto& f : *c->inputs(0)) {
|
||||
c->edit()->DeleteFile(c->level(), f->fd.GetNumber());
|
||||
}
|
||||
@@ -2441,9 +1969,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
|
||||
compaction_job_stats.num_input_files = c->num_input_files(0);
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
|
||||
// Move files to next level
|
||||
int32_t moved_files = 0;
|
||||
int64_t moved_bytes = 0;
|
||||
@@ -2543,9 +2068,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&compaction_job_stats);
|
||||
compaction_job.Prepare();
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
|
||||
mutex_.Unlock();
|
||||
compaction_job.Run();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:NonTrivial:AfterRun");
|
||||
|
||||
+1
-6
@@ -123,7 +123,7 @@ Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
cfd = cfh->cfd();
|
||||
}
|
||||
return WaitForFlushMemTable(cfd, nullptr, false);
|
||||
return WaitForFlushMemTable(cfd);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForCompact(bool wait_unscheduled) {
|
||||
@@ -243,10 +243,5 @@ size_t DBImpl::TEST_GetWalPreallocateBlockSize(
|
||||
return GetWalPreallocateBlockSize(write_buffer_size);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_WaitForTimedTaskRun(std::function<void()> callback) const {
|
||||
if (thread_dump_stats_ != nullptr) {
|
||||
thread_dump_stats_->TEST_WaitForRun(callback);
|
||||
}
|
||||
}
|
||||
} // namespace rocksdb
|
||||
#endif // NDEBUG
|
||||
+1
-16
@@ -20,7 +20,6 @@
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
if (allow_2pc()) {
|
||||
return versions_->min_log_number_to_keep_2pc();
|
||||
@@ -29,14 +28,6 @@ uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
mutex_.AssertHeld();
|
||||
if (!pending_outputs_.empty()) {
|
||||
return *pending_outputs_.begin();
|
||||
}
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
// * Returns the list of live files in 'sst_live'
|
||||
// If it's doing full scan:
|
||||
// * Returns the list of all files in the filesystem in
|
||||
@@ -465,13 +456,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
} else {
|
||||
dir_to_sync =
|
||||
(type == kLogFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
fname = dir_to_sync
|
||||
+ (
|
||||
(!dir_to_sync.empty() && dir_to_sync.back() == '/') ||
|
||||
(!to_delete.empty() && to_delete.front() == '/')
|
||||
? "" : "/"
|
||||
)
|
||||
+ to_delete;
|
||||
fname = dir_to_sync + "/" + to_delete;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
+36
-66
@@ -23,7 +23,8 @@
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
Options SanitizeOptions(const std::string& dbname, const Options& src) {
|
||||
Options SanitizeOptions(const std::string& dbname,
|
||||
const Options& src) {
|
||||
auto db_options = SanitizeOptions(dbname, DBOptions(src));
|
||||
ImmutableDBOptions immutable_db_options(db_options);
|
||||
auto cf_options =
|
||||
@@ -55,9 +56,10 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
result.write_buffer_manager.reset(
|
||||
new WriteBufferManager(result.db_write_buffer_size));
|
||||
}
|
||||
auto bg_job_limits = DBImpl::GetBGJobLimits(
|
||||
result.max_background_flushes, result.max_background_compactions,
|
||||
result.max_background_jobs, true /* parallelize_compactions */);
|
||||
auto bg_job_limits = DBImpl::GetBGJobLimits(result.max_background_flushes,
|
||||
result.max_background_compactions,
|
||||
result.max_background_jobs,
|
||||
true /* parallelize_compactions */);
|
||||
result.env->IncBackgroundThreadsIfNeeded(bg_job_limits.max_compactions,
|
||||
Env::Priority::LOW);
|
||||
result.env->IncBackgroundThreadsIfNeeded(bg_job_limits.max_flushes,
|
||||
@@ -105,12 +107,14 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
result.db_paths.emplace_back(dbname, std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
if (result.use_direct_reads && result.compaction_readahead_size == 0) {
|
||||
if (result.use_direct_reads &&
|
||||
result.compaction_readahead_size == 0) {
|
||||
TEST_SYNC_POINT_CALLBACK("SanitizeOptions:direct_io", nullptr);
|
||||
result.compaction_readahead_size = 1024 * 1024 * 2;
|
||||
}
|
||||
|
||||
if (result.compaction_readahead_size > 0 || result.use_direct_reads) {
|
||||
if (result.compaction_readahead_size > 0 ||
|
||||
result.use_direct_reads) {
|
||||
result.new_table_reader_for_compaction_inputs = true;
|
||||
}
|
||||
|
||||
@@ -214,7 +218,7 @@ static Status ValidateOptions(
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
Status DBImpl::NewDB() {
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -226,7 +230,7 @@ Status DBImpl::NewDB() {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Creating manifest 1 \n");
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
{
|
||||
std::unique_ptr<WritableFile> file;
|
||||
unique_ptr<WritableFile> file;
|
||||
EnvOptions env_options = env_->OptimizeForManifestWrite(env_options_);
|
||||
s = NewWritableFile(env_, manifest, &file, env_options);
|
||||
if (!s.ok()) {
|
||||
@@ -234,9 +238,8 @@ Status DBImpl::NewDB() {
|
||||
}
|
||||
file->SetPreallocationBlockSize(
|
||||
immutable_db_options_.manifest_preallocation_size);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), manifest, env_options, nullptr /* stats */,
|
||||
immutable_db_options_.listeners));
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), manifest, env_options));
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
new_db.EncodeTo(&record);
|
||||
@@ -254,8 +257,9 @@ Status DBImpl::NewDB() {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory) {
|
||||
Status DBImpl::CreateAndNewDirectory(
|
||||
Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory) {
|
||||
// We call CreateDirIfMissing() as the directory may already exist (if we
|
||||
// are reopening a DB), when this happens we don't want creating the
|
||||
// directory to cause an error. However, we need to check if creating the
|
||||
@@ -336,8 +340,8 @@ Status DBImpl::Recover(
|
||||
}
|
||||
} else if (s.ok()) {
|
||||
if (immutable_db_options_.error_if_exists) {
|
||||
return Status::InvalidArgument(dbname_,
|
||||
"exists (error_if_exists is true)");
|
||||
return Status::InvalidArgument(
|
||||
dbname_, "exists (error_if_exists is true)");
|
||||
}
|
||||
} else {
|
||||
// Unexpected error reading file
|
||||
@@ -357,7 +361,7 @@ Status DBImpl::Recover(
|
||||
}
|
||||
// Verify compatibility of env_options_ and filesystem
|
||||
{
|
||||
std::unique_ptr<RandomAccessFile> idfile;
|
||||
unique_ptr<RandomAccessFile> idfile;
|
||||
EnvOptions customized_env(env_options_);
|
||||
customized_env.use_direct_reads |=
|
||||
immutable_db_options_.use_direct_io_for_flush_and_compaction;
|
||||
@@ -474,28 +478,6 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
|
||||
if (read_only) {
|
||||
// If we are opening as read-only, we need to update options_file_number_
|
||||
// to reflect the most recent OPTIONS file. It does not matter for regular
|
||||
// read-write db instance because options_file_number_ will later be
|
||||
// updated to versions_->NewFileNumber() in RenameTempFileToOptionsFile.
|
||||
std::vector<std::string> file_names;
|
||||
if (s.ok()) {
|
||||
s = env_->GetChildren(GetName(), &file_names);
|
||||
}
|
||||
if (s.ok()) {
|
||||
uint64_t number = 0;
|
||||
uint64_t options_file_number = 0;
|
||||
FileType type;
|
||||
for (const auto& fname : file_names) {
|
||||
if (ParseFileName(fname, &number, &type) && type == kOptionsFile) {
|
||||
options_file_number = std::max(number, options_file_number);
|
||||
}
|
||||
}
|
||||
versions_->options_file_number_ = options_file_number;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -544,9 +526,10 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
std::map<std::string, uint32_t> cf_name_id_map;
|
||||
std::map<uint32_t, uint64_t> cf_lognumber_map;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
cf_name_id_map.insert(std::make_pair(cfd->GetName(), cfd->GetID()));
|
||||
cf_name_id_map.insert(
|
||||
std::make_pair(cfd->GetName(), cfd->GetID()));
|
||||
cf_lognumber_map.insert(
|
||||
std::make_pair(cfd->GetID(), cfd->GetLogNumber()));
|
||||
std::make_pair(cfd->GetID(), cfd->GetLogNumber()));
|
||||
}
|
||||
|
||||
immutable_db_options_.wal_filter->ColumnFamilyLogNumberMap(cf_lognumber_map,
|
||||
@@ -589,9 +572,9 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<SequentialFileReader> file_reader;
|
||||
unique_ptr<SequentialFileReader> file_reader;
|
||||
{
|
||||
std::unique_ptr<SequentialFile> file;
|
||||
unique_ptr<SequentialFile> file;
|
||||
status = env_->NewSequentialFile(fname, &file,
|
||||
env_->OptimizeForLogRead(env_options_));
|
||||
if (!status.ok()) {
|
||||
@@ -624,8 +607,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// to be skipped instead of propagating bad information (like overly
|
||||
// large sequence numbers).
|
||||
log::Reader reader(immutable_db_options_.info_log, std::move(file_reader),
|
||||
&reporter, true /*checksum*/, log_number,
|
||||
false /* retry_after_eof */);
|
||||
&reporter, true /*checksum*/, log_number);
|
||||
|
||||
// Determine if we should tolerate incomplete records at the tail end of the
|
||||
// Read all the records and add to a memtable
|
||||
@@ -896,8 +878,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// VersionSet::next_file_number_ always to be strictly greater than any
|
||||
// log number
|
||||
versions_->MarkFileNumberUsed(max_log_number + 1);
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
edit, &mutex_);
|
||||
status = versions_->LogAndApply(
|
||||
cfd, *cfd->GetLatestMutableCFOptions(), edit, &mutex_);
|
||||
if (!status.ok()) {
|
||||
// Recovery failed
|
||||
break;
|
||||
@@ -1010,17 +992,11 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters;
|
||||
auto range_del_iter =
|
||||
mem->NewRangeTombstoneIterator(ro, kMaxSequenceNumber);
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
s = BuildTable(
|
||||
dbname_, env_, *cfd->ioptions(), mutable_cf_options,
|
||||
env_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::move(range_del_iters), &meta, cfd->internal_comparator(),
|
||||
std::unique_ptr<InternalIterator>(mem->NewRangeTombstoneIterator(ro)),
|
||||
&meta, cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
@@ -1054,8 +1030,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
cfd->internal_stats()->AddCompactionStats(level, stats);
|
||||
cfd->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
meta.fd.GetFileSize());
|
||||
cfd->internal_stats()->AddCFStats(
|
||||
InternalStats::BYTES_FLUSHED, meta.fd.GetFileSize());
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, meta.fd.GetFileSize());
|
||||
return s;
|
||||
}
|
||||
@@ -1151,7 +1127,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
s = impl->Recover(column_families);
|
||||
if (s.ok()) {
|
||||
uint64_t new_log_number = impl->versions_->NewFileNumber();
|
||||
std::unique_ptr<WritableFile> lfile;
|
||||
unique_ptr<WritableFile> lfile;
|
||||
EnvOptions soptions(db_options);
|
||||
EnvOptions opt_env_options =
|
||||
impl->immutable_db_options_.env->OptimizeForLogWrite(
|
||||
@@ -1168,10 +1144,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
{
|
||||
InstrumentedMutexLock wl(&impl->log_write_mutex_);
|
||||
impl->logfile_number_ = new_log_number;
|
||||
const auto& listeners = impl->immutable_db_options_.listeners;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), log_fname, opt_env_options,
|
||||
nullptr /* stats */, listeners));
|
||||
unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(lfile), log_fname, opt_env_options));
|
||||
impl->logs_.emplace_back(
|
||||
new_log_number,
|
||||
new log::Writer(
|
||||
@@ -1248,8 +1222,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
!cfd->mem()->IsMergeOperatorSupported()) {
|
||||
s = Status::InvalidArgument(
|
||||
"The memtable of column family %s does not support merge operator "
|
||||
"its options.merge_operator is non-null",
|
||||
cfd->GetName().c_str());
|
||||
"its options.merge_operator is non-null", cfd->GetName().c_str());
|
||||
}
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
@@ -1322,9 +1295,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
persist_options_status.ToString());
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
impl->StartTimedTasks();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
for (auto* h : *handles) {
|
||||
delete h;
|
||||
|
||||
+15
-17
@@ -9,6 +9,7 @@
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -44,17 +45,17 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
}
|
||||
SuperVersion* super_version = cfd->GetSuperVersion();
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
RangeDelAggregator range_del_agg(cfd->internal_comparator(), snapshot);
|
||||
LookupKey lkey(key, snapshot);
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
&range_del_agg, read_options)) {
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val, &s,
|
||||
&merge_context, &max_covering_tombstone_seq);
|
||||
&merge_context, &range_del_agg);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
@@ -71,20 +72,18 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
auto cfd = cfh->cfd();
|
||||
SuperVersion* super_version = cfd->GetSuperVersion()->Ref();
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
SequenceNumber read_seq =
|
||||
read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot;
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), super_version->mutable_cf_options,
|
||||
read_seq,
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot),
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq);
|
||||
db_iter->GetRangeDelAggregator());
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
@@ -100,22 +99,21 @@ Status DBImplReadOnly::NewIterators(
|
||||
iterators->clear();
|
||||
iterators->reserve(column_families.size());
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
SequenceNumber read_seq =
|
||||
read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot;
|
||||
|
||||
for (auto cfh : column_families) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
auto* sv = cfd->GetSuperVersion()->Ref();
|
||||
auto* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options, read_seq,
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot),
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback);
|
||||
auto* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), read_seq);
|
||||
db_iter->GetRangeDelAggregator());
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
iterators->push_back(db_iter);
|
||||
}
|
||||
|
||||
@@ -89,11 +89,10 @@ class DBImplReadOnly : public DBImpl {
|
||||
virtual Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
virtual Status GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool /*flush_memtable*/) override {
|
||||
return DBImpl::GetLiveFiles(ret, manifest_file_size,
|
||||
false /* flush_memtable */);
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* /*manifest_file_size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Flush;
|
||||
|
||||
+67
-130
@@ -265,19 +265,18 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
// commit. That lets us release our leader status early.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsNumKeysWritten, total_count,
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size,
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1,
|
||||
concurrent_update);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1, concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
auto write_done_by_other = write_group.size - 1;
|
||||
if (write_done_by_other > 0) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
write_done_by_other, concurrent_update);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER, write_done_by_other,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
@@ -339,6 +338,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
}
|
||||
write_group.last_sequence = last_sequence;
|
||||
write_group.running.store(static_cast<uint32_t>(write_group.size),
|
||||
std::memory_order_relaxed);
|
||||
write_thread_.LaunchParallelMemTableWriters(&write_group);
|
||||
in_parallel_group = true;
|
||||
|
||||
@@ -468,9 +469,9 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsNumKeysWritten, total_count);
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
|
||||
@@ -478,10 +479,10 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (w.ShouldWriteToWAL()) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF, 1);
|
||||
if (wal_write_group.size > 1) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER,
|
||||
wal_write_group.size - 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
|
||||
}
|
||||
@@ -592,16 +593,15 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
// commit. That lets us release our leader status early.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size,
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1,
|
||||
concurrent_update);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1, concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
auto write_done_by_other = write_group.size - 1;
|
||||
if (write_done_by_other > 0) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
write_done_by_other, concurrent_update);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER, write_done_by_other,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
@@ -910,12 +910,12 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
if (status.ok()) {
|
||||
auto stats = default_cf_internal_stats_;
|
||||
if (need_log_sync) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileSynced, 1);
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_SYNCED, 1);
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
}
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileBytes, log_size);
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_BYTES, log_size);
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal);
|
||||
stats->AddDBStats(InternalStats::WRITE_WITH_WAL, write_with_wal);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return status;
|
||||
@@ -961,10 +961,9 @@ Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
if (status.ok()) {
|
||||
const bool concurrent = true;
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileBytes, log_size,
|
||||
concurrent);
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_BYTES, log_size, concurrent);
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal,
|
||||
stats->AddDBStats(InternalStats::WRITE_WITH_WAL, write_with_wal,
|
||||
concurrent);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
@@ -1017,28 +1016,6 @@ Status DBImpl::WriteRecoverableState() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::SelectColumnFamiliesForAtomicFlush(
|
||||
autovector<ColumnFamilyData*>* cfds) {
|
||||
for (ColumnFamilyData* cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
cfds->push_back(cfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assign sequence number for atomic flush.
|
||||
void DBImpl::AssignAtomicFlushSeq(const autovector<ColumnFamilyData*>& cfds) {
|
||||
assert(immutable_db_options_.atomic_flush);
|
||||
auto seq = versions_->LastSequence();
|
||||
for (auto cfd : cfds) {
|
||||
cfd->imm()->AssignAtomicFlushSeq(seq);
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(write_context != nullptr);
|
||||
@@ -1087,36 +1064,21 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
oldest_alive_log, total_log_size_.load(), GetMaxTotalWalSize());
|
||||
// no need to refcount because drop is happening in write thread, so can't
|
||||
// happen while we're in the write thread
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (cfd->OldestLogToKeep() <= oldest_alive_log) {
|
||||
cfds.push_back(cfd);
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (const auto cfd : cfds) {
|
||||
cfd->Ref();
|
||||
status = SwitchMemtable(cfd, write_context);
|
||||
cfd->Unref();
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
if (cfd->OldestLogToKeep() <= oldest_alive_log) {
|
||||
status = SwitchMemtable(cfd, write_context);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
flush_req.emplace_back(cfd, cfd->imm()->GetLatestMemTableID());
|
||||
cfd->imm()->FlushRequested();
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
}
|
||||
for (auto cfd : cfds) {
|
||||
cfd->imm()->FlushRequested();
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -1141,52 +1103,41 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
write_buffer_manager_->buffer_size());
|
||||
// no need to refcount because drop is happening in write thread, so can't
|
||||
// happen while we're in the write thread
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
} else {
|
||||
ColumnFamilyData* cfd_picked = nullptr;
|
||||
SequenceNumber seq_num_for_cf_picked = kMaxSequenceNumber;
|
||||
ColumnFamilyData* cfd_picked = nullptr;
|
||||
SequenceNumber seq_num_for_cf_picked = kMaxSequenceNumber;
|
||||
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (!cfd->mem()->IsEmpty()) {
|
||||
// We only consider active mem table, hoping immutable memtable is
|
||||
// already in the process of flushing.
|
||||
uint64_t seq = cfd->mem()->GetCreationSeq();
|
||||
if (cfd_picked == nullptr || seq < seq_num_for_cf_picked) {
|
||||
cfd_picked = cfd;
|
||||
seq_num_for_cf_picked = seq;
|
||||
}
|
||||
}
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (cfd_picked != nullptr) {
|
||||
cfds.push_back(cfd_picked);
|
||||
if (!cfd->mem()->IsEmpty()) {
|
||||
// We only consider active mem table, hoping immutable memtable is
|
||||
// already in the process of flushing.
|
||||
uint64_t seq = cfd->mem()->GetCreationSeq();
|
||||
if (cfd_picked == nullptr || seq < seq_num_for_cf_picked) {
|
||||
cfd_picked = cfd;
|
||||
seq_num_for_cf_picked = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
if (cfd_picked != nullptr) {
|
||||
cfds.push_back(cfd_picked);
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
for (const auto cfd : cfds) {
|
||||
if (cfd->mem()->IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
status = SwitchMemtable(cfd, write_context);
|
||||
cfd->Unref();
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
uint64_t flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
cfd->imm()->FlushRequested();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
if (status.ok()) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
}
|
||||
for (const auto cfd : cfds) {
|
||||
cfd->imm()->FlushRequested();
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -1258,8 +1209,8 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
||||
}
|
||||
assert(!delayed || !write_options.no_slowdown);
|
||||
if (delayed) {
|
||||
default_cf_internal_stats_->AddDBStats(
|
||||
InternalStats::kIntStatsWriteStallMicros, time_delayed);
|
||||
default_cf_internal_stats_->AddDBStats(InternalStats::WRITE_STALL_MICROS,
|
||||
time_delayed);
|
||||
RecordTick(stats_, STALL_MICROS, time_delayed);
|
||||
}
|
||||
|
||||
@@ -1309,38 +1260,25 @@ Status DBImpl::ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds);
|
||||
for (auto cfd : cfds) {
|
||||
cfd->Ref();
|
||||
}
|
||||
flush_scheduler_.Clear();
|
||||
} else {
|
||||
ColumnFamilyData* tmp_cfd;
|
||||
while ((tmp_cfd = flush_scheduler_.TakeNextColumnFamily()) != nullptr) {
|
||||
cfds.push_back(tmp_cfd);
|
||||
}
|
||||
}
|
||||
ColumnFamilyData* cfd;
|
||||
FlushRequest flush_req;
|
||||
Status status;
|
||||
for (auto& cfd : cfds) {
|
||||
if (!cfd->mem()->IsEmpty()) {
|
||||
status = SwitchMemtable(cfd, context);
|
||||
}
|
||||
while ((cfd = flush_scheduler_.TakeNextColumnFamily()) != nullptr) {
|
||||
status = SwitchMemtable(cfd, context);
|
||||
bool should_schedule = true;
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
cfd = nullptr;
|
||||
should_schedule = false;
|
||||
}
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
if (should_schedule) {
|
||||
uint64_t flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -1374,7 +1312,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
|
||||
std::unique_ptr<WritableFile> lfile;
|
||||
unique_ptr<WritableFile> lfile;
|
||||
log::Writer* new_log = nullptr;
|
||||
MemTable* new_mem = nullptr;
|
||||
|
||||
@@ -1458,9 +1396,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
// of calling GetWalPreallocateBlockSize()
|
||||
lfile->SetPreallocationBlockSize(preallocate_block_size);
|
||||
lfile->SetWriteLifeTimeHint(write_hint);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(lfile), log_fname, opt_env_opt, nullptr /* stats */,
|
||||
immutable_db_options_.listeners));
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), log_fname, opt_env_opt));
|
||||
new_log = new log::Writer(
|
||||
std::move(file_writer), new_log_number,
|
||||
immutable_db_options_.recycle_log_file_num > 0, manual_wal_flush_);
|
||||
|
||||
@@ -42,7 +42,7 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
"Error when reading %s dir\n", dbname.c_str());
|
||||
}
|
||||
std::sort(files.begin(), files.end());
|
||||
for (const std::string& file : files) {
|
||||
for (std::string file : files) {
|
||||
if (!ParseFileName(file, &number, &type)) {
|
||||
continue;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
continue;
|
||||
}
|
||||
std::sort(files.begin(), files.end());
|
||||
for (const std::string& file : files) {
|
||||
for (std::string file : files) {
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
if (type == kTableFile && ++file_num < 10) {
|
||||
file_info.append(file).append(" ");
|
||||
@@ -109,7 +109,7 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
return;
|
||||
}
|
||||
wal_info.clear();
|
||||
for (const std::string& file : files) {
|
||||
for (std::string file : files) {
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
if (type == kLogFile) {
|
||||
env->GetFileSize(options.wal_dir + "/" + file, &file_size);
|
||||
|
||||
@@ -88,6 +88,7 @@ TEST_F(DBIOFailureTest, DropWritesFlush) {
|
||||
env_->drop_writes_.store(false, std::memory_order_release);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Check that CompactRange() returns failure if there is not enough space left
|
||||
// on device
|
||||
@@ -115,7 +116,6 @@ TEST_F(DBIOFailureTest, NoSpaceCompactRange) {
|
||||
env_->no_space_.store(false, std::memory_order_release);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBIOFailureTest, NonWritableFileSystem) {
|
||||
do {
|
||||
|
||||
+12
-10
@@ -134,14 +134,15 @@ class DBIter final: public Iterator {
|
||||
prefix_same_as_start_(read_options.prefix_same_as_start),
|
||||
pin_thru_lifetime_(read_options.pin_data),
|
||||
total_order_seek_(read_options.total_order_seek),
|
||||
range_del_agg_(&cf_options.internal_comparator, s),
|
||||
range_del_agg_(cf_options.internal_comparator, s,
|
||||
true /* collapse_deletions */),
|
||||
read_callback_(read_callback),
|
||||
db_impl_(db_impl),
|
||||
cfd_(cfd),
|
||||
allow_blob_(allow_blob),
|
||||
is_blob_(false),
|
||||
start_seqnum_(read_options.iter_start_seqnum) {
|
||||
RecordTick(statistics_, NO_ITERATOR_CREATED);
|
||||
RecordTick(statistics_, NO_ITERATORS);
|
||||
prefix_extractor_ = mutable_cf_options.prefix_extractor.get();
|
||||
max_skip_ = max_sequential_skip_in_iterations;
|
||||
max_skippable_internal_keys_ = read_options.max_skippable_internal_keys;
|
||||
@@ -157,7 +158,9 @@ class DBIter final: public Iterator {
|
||||
if (pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
RecordTick(statistics_, NO_ITERATOR_DELETED);
|
||||
// Compiler warning issue filed:
|
||||
// https://github.com/facebook/rocksdb/issues/3013
|
||||
RecordTick(statistics_, NO_ITERATORS, uint64_t(-1));
|
||||
ResetInternalKeysSkippedCounter();
|
||||
local_stats_.BumpGlobalStatistics(statistics_);
|
||||
if (!arena_mode_) {
|
||||
@@ -171,7 +174,7 @@ class DBIter final: public Iterator {
|
||||
iter_ = iter;
|
||||
iter_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
virtual ReadRangeDelAggregator* GetRangeDelAggregator() {
|
||||
virtual RangeDelAggregator* GetRangeDelAggregator() {
|
||||
return &range_del_agg_;
|
||||
}
|
||||
|
||||
@@ -229,7 +232,7 @@ class DBIter final: public Iterator {
|
||||
*prop = saved_key_.GetUserKey().ToString();
|
||||
return Status::OK();
|
||||
}
|
||||
return Status::InvalidArgument("Unidentified property.");
|
||||
return Status::InvalidArgument("Undentified property.");
|
||||
}
|
||||
|
||||
virtual void Next() override;
|
||||
@@ -341,7 +344,7 @@ class DBIter final: public Iterator {
|
||||
const bool total_order_seek_;
|
||||
// List of operands for merge operator.
|
||||
MergeContext merge_context_;
|
||||
ReadRangeDelAggregator range_del_agg_;
|
||||
RangeDelAggregator range_del_agg_;
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
ReadCallback* read_callback_;
|
||||
@@ -1112,7 +1115,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelPositioningMode::kForwardTraversal)) {
|
||||
ikey, RangeDelPositioningMode::kBackwardTraversal)) {
|
||||
break;
|
||||
} else if (ikey.type == kTypeValue) {
|
||||
const Slice val = iter_->value();
|
||||
@@ -1479,7 +1482,7 @@ Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
|
||||
ArenaWrappedDBIter::~ArenaWrappedDBIter() { db_iter_->~DBIter(); }
|
||||
|
||||
ReadRangeDelAggregator* ArenaWrappedDBIter::GetRangeDelAggregator() {
|
||||
RangeDelAggregator* ArenaWrappedDBIter::GetRangeDelAggregator() {
|
||||
return db_iter_->GetRangeDelAggregator();
|
||||
}
|
||||
|
||||
@@ -1555,8 +1558,7 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq);
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator());
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
// Get the arena to be used to allocate memory for DBIter to be wrapped,
|
||||
// as well as child iterators in it.
|
||||
virtual Arena* GetArena() { return &arena_; }
|
||||
virtual ReadRangeDelAggregator* GetRangeDelAggregator();
|
||||
virtual RangeDelAggregator* GetRangeDelAggregator();
|
||||
|
||||
// Set the internal iterator wrapped inside the DB Iterator. Usually it is
|
||||
// a merging iterator.
|
||||
|
||||
@@ -85,7 +85,7 @@ TEST_P(DBIteratorTest, IteratorProperty) {
|
||||
ReadOptions ropt;
|
||||
ropt.pin_data = false;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(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));
|
||||
@@ -1951,7 +1951,6 @@ TEST_P(DBIteratorTest, ReadAhead) {
|
||||
size_t bytes_read = env_->random_read_bytes_counter_;
|
||||
delete iter;
|
||||
|
||||
int64_t num_file_closes = TestGetTickerCount(options, NO_FILE_CLOSES);
|
||||
env_->random_read_bytes_counter_ = 0;
|
||||
options.statistics->setTickerCount(NO_FILE_OPENS, 0);
|
||||
read_options.readahead_size = 1024 * 10;
|
||||
@@ -1960,10 +1959,7 @@ TEST_P(DBIteratorTest, ReadAhead) {
|
||||
int64_t num_file_opens_readahead = TestGetTickerCount(options, NO_FILE_OPENS);
|
||||
size_t bytes_read_readahead = env_->random_read_bytes_counter_;
|
||||
delete iter;
|
||||
int64_t num_file_closes_readahead =
|
||||
TestGetTickerCount(options, NO_FILE_CLOSES);
|
||||
ASSERT_EQ(num_file_opens + 3, num_file_opens_readahead);
|
||||
ASSERT_EQ(num_file_closes + 3, num_file_closes_readahead);
|
||||
ASSERT_GT(bytes_read_readahead, bytes_read);
|
||||
ASSERT_GT(bytes_read_readahead, read_options.readahead_size * 3);
|
||||
|
||||
@@ -2377,7 +2373,7 @@ TEST_P(DBIteratorTest, SeekAfterHittingManyInternalKeys) {
|
||||
Delete("5");
|
||||
Put("6", "val_6");
|
||||
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ropts));
|
||||
unique_ptr<Iterator> iter(NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -2396,7 +2392,7 @@ TEST_P(DBIteratorTest, SeekAfterHittingManyInternalKeys) {
|
||||
ASSERT_EQ("4", prop_value);
|
||||
|
||||
// Create a new iterator to seek to the internal key.
|
||||
std::unique_ptr<Iterator> iter2(NewIterator(ropts));
|
||||
unique_ptr<Iterator> iter2(NewIterator(ropts));
|
||||
iter2->Seek(prop_value);
|
||||
ASSERT_TRUE(iter2->Valid());
|
||||
ASSERT_OK(iter2->status());
|
||||
@@ -2424,7 +2420,7 @@ TEST_P(DBIteratorTest, NonBlockingIterationBugRepro) {
|
||||
// Create a nonblocking iterator before writing to memtable.
|
||||
ReadOptions ropt;
|
||||
ropt.read_tier = kBlockCacheTier;
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ropt));
|
||||
unique_ptr<Iterator> iter(NewIterator(ropt));
|
||||
|
||||
// Overwrite a key in memtable many times to hit
|
||||
// max_sequential_skip_in_iterations (which is 8 by default).
|
||||
@@ -2434,7 +2430,7 @@ TEST_P(DBIteratorTest, NonBlockingIterationBugRepro) {
|
||||
|
||||
// Load the second block in sst file into the block cache.
|
||||
{
|
||||
std::unique_ptr<Iterator> iter2(NewIterator(ReadOptions()));
|
||||
unique_ptr<Iterator> iter2(NewIterator(ReadOptions()));
|
||||
iter2->Seek("d");
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class DBTestXactLogIterator : public DBTestBase {
|
||||
|
||||
std::unique_ptr<TransactionLogIterator> OpenTransactionLogIter(
|
||||
const SequenceNumber seq) {
|
||||
std::unique_ptr<TransactionLogIterator> iter;
|
||||
unique_ptr<TransactionLogIterator> iter;
|
||||
Status status = dbfull()->GetUpdatesSince(seq, &iter);
|
||||
EXPECT_OK(status);
|
||||
EXPECT_TRUE(iter->Valid());
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
@@ -136,8 +135,7 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
MergeContext merge_context;
|
||||
Options options;
|
||||
InternalKeyComparator ikey_cmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&ikey_cmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
RangeDelAggregator range_del_agg(ikey_cmp, {} /* snapshots */);
|
||||
|
||||
// Create a MemTable
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
|
||||
@@ -514,33 +514,6 @@ TEST_F(DBOptionsTest, SetStatsDumpPeriodSec) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, RunStatsDumpPeriodSec) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_dump_period_sec = 5;
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
int counter = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DumpStats:1", [&](void* /*arg*/) {
|
||||
counter++;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
dbfull()->TEST_WaitForTimedTaskRun([&] { mock_env->set_current_time(5); });
|
||||
ASSERT_GE(counter, 1);
|
||||
|
||||
// Test cacel job through SetOptions
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "0"}}));
|
||||
int old_val = counter;
|
||||
env_->SleepForMicroseconds(10000000);
|
||||
ASSERT_EQ(counter, old_val);
|
||||
Close();
|
||||
}
|
||||
|
||||
static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
dbfull->TEST_LockMutex();
|
||||
JobContext job_context(0);
|
||||
|
||||
+26
-130
@@ -170,8 +170,6 @@ void ResetTableProperties(TableProperties* tp) {
|
||||
tp->raw_value_size = 0;
|
||||
tp->num_data_blocks = 0;
|
||||
tp->num_entries = 0;
|
||||
tp->num_deletions = 0;
|
||||
tp->num_merge_operands = 0;
|
||||
tp->num_range_deletions = 0;
|
||||
}
|
||||
|
||||
@@ -181,19 +179,17 @@ void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
|
||||
std::replace(tp_string.begin(), tp_string.end(), '=', ' ');
|
||||
ResetTableProperties(tp);
|
||||
sscanf(tp_string.c_str(),
|
||||
"# data blocks %" SCNu64 " # entries %" SCNu64 " # deletions %" SCNu64
|
||||
" # merge operands %" SCNu64 " # range deletions %" SCNu64
|
||||
" raw key size %" SCNu64
|
||||
"# data blocks %" SCNu64 " # entries %" SCNu64
|
||||
" # range deletions %" SCNu64 " raw key size %" SCNu64
|
||||
" raw average key size %lf "
|
||||
" raw value size %" SCNu64
|
||||
" raw average value size %lf "
|
||||
" data block size %" SCNu64 " index block size (user-key? %" SCNu64
|
||||
", delta-value? %" SCNu64 ") %" SCNu64 " filter block size %" SCNu64,
|
||||
&tp->num_data_blocks, &tp->num_entries, &tp->num_deletions,
|
||||
&tp->num_merge_operands, &tp->num_range_deletions, &tp->raw_key_size,
|
||||
&dummy_double, &tp->raw_value_size, &dummy_double, &tp->data_size,
|
||||
&tp->index_key_is_user_key, &tp->index_value_is_delta_encoded,
|
||||
&tp->index_size, &tp->filter_size);
|
||||
&tp->num_data_blocks, &tp->num_entries, &tp->num_range_deletions,
|
||||
&tp->raw_key_size, &dummy_double, &tp->raw_value_size, &dummy_double,
|
||||
&tp->data_size, &tp->index_key_is_user_key,
|
||||
&tp->index_value_is_delta_encoded, &tp->index_size, &tp->filter_size);
|
||||
}
|
||||
|
||||
void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
@@ -221,43 +217,28 @@ void VerifyTableProperties(const TableProperties& base_tp,
|
||||
VerifySimilar(base_tp.filter_size, new_tp.filter_size, filter_size_bias);
|
||||
VerifySimilar(base_tp.num_data_blocks, new_tp.num_data_blocks,
|
||||
num_data_blocks_bias);
|
||||
|
||||
ASSERT_EQ(base_tp.raw_key_size, new_tp.raw_key_size);
|
||||
ASSERT_EQ(base_tp.raw_value_size, new_tp.raw_value_size);
|
||||
ASSERT_EQ(base_tp.num_entries, new_tp.num_entries);
|
||||
ASSERT_EQ(base_tp.num_deletions, new_tp.num_deletions);
|
||||
ASSERT_EQ(base_tp.num_range_deletions, new_tp.num_range_deletions);
|
||||
|
||||
// Merge operands may become Puts, so we only have an upper bound the exact
|
||||
// number of merge operands.
|
||||
ASSERT_GE(base_tp.num_merge_operands, new_tp.num_merge_operands);
|
||||
}
|
||||
|
||||
void GetExpectedTableProperties(
|
||||
TableProperties* expected_tp, const int kKeySize, const int kValueSize,
|
||||
const int kPutsPerTable, const int kDeletionsPerTable,
|
||||
const int kMergeOperandsPerTable, const int kRangeDeletionsPerTable,
|
||||
const int kKeysPerTable, const int kRangeDeletionsPerTable,
|
||||
const int kTableCount, const int kBloomBitsPerKey, const size_t kBlockSize,
|
||||
const bool index_key_is_user_key, const bool value_delta_encoding) {
|
||||
const int kKeysPerTable =
|
||||
kPutsPerTable + kDeletionsPerTable + kMergeOperandsPerTable;
|
||||
const int kPutCount = kTableCount * kPutsPerTable;
|
||||
const int kDeletionCount = kTableCount * kDeletionsPerTable;
|
||||
const int kMergeCount = kTableCount * kMergeOperandsPerTable;
|
||||
const int kKeyCount = kTableCount * kKeysPerTable;
|
||||
const int kRangeDeletionCount = kTableCount * kRangeDeletionsPerTable;
|
||||
const int kKeyCount = kPutCount + kDeletionCount + kMergeCount;
|
||||
const int kAvgSuccessorSize = kKeySize / 5;
|
||||
const int kEncodingSavePerKey = kKeySize / 4;
|
||||
expected_tp->raw_key_size =
|
||||
(kKeyCount + kRangeDeletionCount) * (kKeySize + 8);
|
||||
expected_tp->raw_value_size =
|
||||
(kPutCount + kMergeCount + kRangeDeletionCount) * kValueSize;
|
||||
expected_tp->raw_key_size = (kKeyCount + kRangeDeletionCount) * (kKeySize + 8);
|
||||
expected_tp->raw_value_size = (kKeyCount + kRangeDeletionCount) * kValueSize;
|
||||
expected_tp->num_entries = kKeyCount;
|
||||
expected_tp->num_deletions = kDeletionCount;
|
||||
expected_tp->num_merge_operands = kMergeCount;
|
||||
expected_tp->num_range_deletions = kRangeDeletionCount;
|
||||
expected_tp->num_data_blocks =
|
||||
kTableCount * (kKeysPerTable * (kKeySize - kEncodingSavePerKey + kValueSize)) /
|
||||
kTableCount *
|
||||
(kKeysPerTable * (kKeySize - kEncodingSavePerKey + kValueSize)) /
|
||||
kBlockSize;
|
||||
expected_tp->data_size =
|
||||
kTableCount * (kKeysPerTable * (kKeySize + 8 + kValueSize));
|
||||
@@ -317,10 +298,8 @@ TEST_F(DBPropertiesTest, ValidateSampleNumber) {
|
||||
|
||||
TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
for (int kTableCount = 40; kTableCount <= 100; kTableCount += 30) {
|
||||
const int kDeletionsPerTable = 5;
|
||||
const int kMergeOperandsPerTable = 15;
|
||||
const int kRangeDeletionsPerTable = 5;
|
||||
const int kPutsPerTable = 100;
|
||||
const int kKeysPerTable = 100;
|
||||
const int kKeySize = 80;
|
||||
const int kValueSize = 200;
|
||||
const int kBloomBitsPerKey = 20;
|
||||
@@ -329,8 +308,6 @@ TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
options.level0_file_num_compaction_trigger = 8;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.preserve_deletes = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(
|
||||
@@ -346,17 +323,10 @@ TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
|
||||
Random rnd(5632);
|
||||
for (int table = 1; table <= kTableCount; ++table) {
|
||||
for (int i = 0; i < kPutsPerTable; ++i) {
|
||||
for (int i = 0; i < kKeysPerTable; ++i) {
|
||||
db_->Put(WriteOptions(), RandomString(&rnd, kKeySize),
|
||||
RandomString(&rnd, kValueSize));
|
||||
}
|
||||
for (int i = 0; i < kDeletionsPerTable; i++) {
|
||||
db_->Delete(WriteOptions(), RandomString(&rnd, kKeySize));
|
||||
}
|
||||
for (int i = 0; i < kMergeOperandsPerTable; i++) {
|
||||
db_->Merge(WriteOptions(), RandomString(&rnd, kKeySize),
|
||||
RandomString(&rnd, kValueSize));
|
||||
}
|
||||
for (int i = 0; i < kRangeDeletionsPerTable; i++) {
|
||||
std::string start = RandomString(&rnd, kKeySize);
|
||||
std::string end = start;
|
||||
@@ -373,11 +343,11 @@ TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
bool value_is_delta_encoded = output_tp.index_value_is_delta_encoded > 0;
|
||||
|
||||
TableProperties expected_tp;
|
||||
GetExpectedTableProperties(
|
||||
&expected_tp, kKeySize, kValueSize, kPutsPerTable, kDeletionsPerTable,
|
||||
kMergeOperandsPerTable, kRangeDeletionsPerTable, kTableCount,
|
||||
kBloomBitsPerKey, table_options.block_size, index_key_is_user_key,
|
||||
value_is_delta_encoded);
|
||||
GetExpectedTableProperties(&expected_tp, kKeySize, kValueSize,
|
||||
kKeysPerTable, kRangeDeletionsPerTable,
|
||||
kTableCount, kBloomBitsPerKey,
|
||||
table_options.block_size, index_key_is_user_key,
|
||||
value_is_delta_encoded);
|
||||
|
||||
VerifyTableProperties(expected_tp, output_tp);
|
||||
}
|
||||
@@ -446,7 +416,7 @@ TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
for (iter->Seek(Key(0)); iter->Valid(); iter->Next()) {
|
||||
}
|
||||
}
|
||||
@@ -499,10 +469,8 @@ TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
|
||||
|
||||
TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
const int kTableCount = 100;
|
||||
const int kDeletionsPerTable = 2;
|
||||
const int kMergeOperandsPerTable = 2;
|
||||
const int kRangeDeletionsPerTable = 2;
|
||||
const int kPutsPerTable = 10;
|
||||
const int kKeysPerTable = 10;
|
||||
const int kKeySize = 50;
|
||||
const int kValueSize = 400;
|
||||
const int kMaxLevel = 7;
|
||||
@@ -518,8 +486,6 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
// This ensures there no compaction happening when we call GetProperty().
|
||||
options.disable_auto_compactions = true;
|
||||
options.preserve_deletes = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(
|
||||
@@ -537,17 +503,10 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
TableProperties level_tps[kMaxLevel];
|
||||
TableProperties tp, sum_tp, expected_tp;
|
||||
for (int table = 1; table <= kTableCount; ++table) {
|
||||
for (int i = 0; i < kPutsPerTable; ++i) {
|
||||
for (int i = 0; i < kKeysPerTable; ++i) {
|
||||
db_->Put(WriteOptions(), RandomString(&rnd, kKeySize),
|
||||
RandomString(&rnd, kValueSize));
|
||||
}
|
||||
for (int i = 0; i < kDeletionsPerTable; i++) {
|
||||
db_->Delete(WriteOptions(), RandomString(&rnd, kKeySize));
|
||||
}
|
||||
for (int i = 0; i < kMergeOperandsPerTable; i++) {
|
||||
db_->Merge(WriteOptions(), RandomString(&rnd, kKeySize),
|
||||
RandomString(&rnd, kValueSize));
|
||||
}
|
||||
for (int i = 0; i < kRangeDeletionsPerTable; i++) {
|
||||
std::string start = RandomString(&rnd, kKeySize);
|
||||
std::string end = start;
|
||||
@@ -569,8 +528,6 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
sum_tp.raw_value_size += level_tps[level].raw_value_size;
|
||||
sum_tp.num_data_blocks += level_tps[level].num_data_blocks;
|
||||
sum_tp.num_entries += level_tps[level].num_entries;
|
||||
sum_tp.num_deletions += level_tps[level].num_deletions;
|
||||
sum_tp.num_merge_operands += level_tps[level].num_merge_operands;
|
||||
sum_tp.num_range_deletions += level_tps[level].num_range_deletions;
|
||||
}
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &tp_string);
|
||||
@@ -584,15 +541,12 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
ASSERT_EQ(sum_tp.raw_value_size, tp.raw_value_size);
|
||||
ASSERT_EQ(sum_tp.num_data_blocks, tp.num_data_blocks);
|
||||
ASSERT_EQ(sum_tp.num_entries, tp.num_entries);
|
||||
ASSERT_EQ(sum_tp.num_deletions, tp.num_deletions);
|
||||
ASSERT_EQ(sum_tp.num_merge_operands, tp.num_merge_operands);
|
||||
ASSERT_EQ(sum_tp.num_range_deletions, tp.num_range_deletions);
|
||||
if (table > 3) {
|
||||
GetExpectedTableProperties(
|
||||
&expected_tp, kKeySize, kValueSize, kPutsPerTable, kDeletionsPerTable,
|
||||
kMergeOperandsPerTable, kRangeDeletionsPerTable, table,
|
||||
kBloomBitsPerKey, table_options.block_size, index_key_is_user_key,
|
||||
value_is_delta_encoded);
|
||||
GetExpectedTableProperties(&expected_tp, kKeySize, kValueSize,
|
||||
kKeysPerTable, kRangeDeletionsPerTable, table,
|
||||
kBloomBitsPerKey, table_options.block_size,
|
||||
index_key_is_user_key, value_is_delta_encoded);
|
||||
// Gives larger bias here as index block size, filter block size,
|
||||
// and data block size become much harder to estimate in this test.
|
||||
VerifyTableProperties(expected_tp, tp, 0.5, 0.4, 0.4, 0.25);
|
||||
@@ -1529,64 +1483,6 @@ TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
ASSERT_TRUE(listener->callback_triggered);
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, MinObsoleteSstNumberToKeep) {
|
||||
class TestListener : public EventListener {
|
||||
public:
|
||||
void OnTableFileCreated(const TableFileCreationInfo& info) override {
|
||||
if (info.reason == TableFileCreationReason::kCompaction) {
|
||||
// Verify the property indicates that SSTs created by a running
|
||||
// compaction cannot be deleted.
|
||||
uint64_t created_file_num;
|
||||
FileType created_file_type;
|
||||
std::string filename =
|
||||
info.file_path.substr(info.file_path.rfind('/') + 1);
|
||||
ASSERT_TRUE(
|
||||
ParseFileName(filename, &created_file_num, &created_file_type));
|
||||
ASSERT_EQ(kTableFile, created_file_type);
|
||||
|
||||
uint64_t keep_sst_lower_bound;
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kMinObsoleteSstNumberToKeep,
|
||||
&keep_sst_lower_bound));
|
||||
|
||||
ASSERT_LE(keep_sst_lower_bound, created_file_num);
|
||||
validated_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SetDB(DB* db) { db_ = db; }
|
||||
|
||||
int GetNumCompactions() { return num_compactions_; }
|
||||
|
||||
// True if we've verified the property for at least one output file
|
||||
bool Validated() { return validated_; }
|
||||
|
||||
private:
|
||||
int num_compactions_ = 0;
|
||||
bool validated_ = false;
|
||||
DB* db_ = nullptr;
|
||||
};
|
||||
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
std::shared_ptr<TestListener> listener = std::make_shared<TestListener>();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.listeners.push_back(listener);
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
DestroyAndReopen(options);
|
||||
listener->SetDB(db_);
|
||||
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
// Make sure they overlap in keyspace to prevent trivial move
|
||||
Put("key1", "val");
|
||||
Put("key2", "val");
|
||||
Flush();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(listener->Validated());
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, BlockCacheProperties) {
|
||||
Options options;
|
||||
uint64_t value;
|
||||
|
||||
@@ -1041,16 +1041,11 @@ TEST_F(DBRangeDelTest, RangeTombstoneEndKeyAsSstableUpperBound) {
|
||||
// L2:
|
||||
// [key000000#1,1, key000000#1,1]
|
||||
// [key000002#6,1, key000004#72057594037927935,15]
|
||||
//
|
||||
// At the same time, verify the compaction does not cause the key at the
|
||||
// endpoint (key000002#6,1) to disappear.
|
||||
ASSERT_EQ(value, Get(Key(2)));
|
||||
auto begin_str = Key(3);
|
||||
const rocksdb::Slice begin = begin_str;
|
||||
dbfull()->TEST_CompactRange(1, &begin, nullptr);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(2));
|
||||
ASSERT_EQ(value, Get(Key(2)));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1108,386 +1103,6 @@ TEST_F(DBRangeDelTest, UnorderedTombstones) {
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
|
||||
class MockMergeOperator : public MergeOperator {
|
||||
// Mock non-associative operator. Non-associativity is expressed by lack of
|
||||
// implementation for any `PartialMerge*` functions.
|
||||
public:
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
assert(merge_out != nullptr);
|
||||
merge_out->new_value = merge_in.operand_list.back().ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "MockMergeOperator"; }
|
||||
};
|
||||
|
||||
TEST_F(DBRangeDelTest, KeyAtOverlappingEndpointReappears) {
|
||||
// This test uses a non-associative merge operator since that is a convenient
|
||||
// way to get compaction to write out files with overlapping user-keys at the
|
||||
// endpoints. Note, however, overlapping endpoints can also occur with other
|
||||
// value types (Put, etc.), assuming the right snapshots are present.
|
||||
const int kFileBytes = 1 << 20;
|
||||
const int kValueBytes = 1 << 10;
|
||||
const int kNumFiles = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator.reset(new MockMergeOperator());
|
||||
options.target_file_size_base = kFileBytes;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kFileBytes / kValueBytes; ++j) {
|
||||
auto value = RandomString(&rnd, kValueBytes);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "key", value));
|
||||
}
|
||||
if (i == kNumFiles - 1) {
|
||||
// Take snapshot to prevent covered merge operands from being dropped by
|
||||
// compaction.
|
||||
snapshot = db_->GetSnapshot();
|
||||
// The DeleteRange is the last write so all merge operands are covered.
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
"key", "key_"));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(0));
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "key", &value).IsNotFound());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr /* begin_key */,
|
||||
nullptr /* end_key */));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
// Now we have multiple files at L1 all containing a single user key, thus
|
||||
// guaranteeing overlap in the file endpoints.
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 1);
|
||||
|
||||
// Verify no merge operands reappeared after the compaction.
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "key", &value).IsNotFound());
|
||||
|
||||
// Compact and verify again. It's worthwhile because now the files have
|
||||
// tighter endpoints, so we can verify that doesn't mess anything up.
|
||||
dbfull()->TEST_CompactRange(1 /* level */, nullptr /* begin */,
|
||||
nullptr /* end */, nullptr /* column_family */,
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_GT(NumTableFilesAtLevel(2), 1);
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "key", &value).IsNotFound());
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, UntruncatedTombstoneDoesNotDeleteNewerKey) {
|
||||
// Verify a key newer than a range tombstone cannot be deleted by being
|
||||
// compacted to the bottom level (and thus having its seqnum zeroed) before
|
||||
// the range tombstone. This used to happen when range tombstones were
|
||||
// untruncated on reads such that they extended past their file boundaries.
|
||||
//
|
||||
// Test summary:
|
||||
//
|
||||
// - L1 is bottommost.
|
||||
// - A couple snapshots are strategically taken to prevent seqnums from being
|
||||
// zeroed, range tombstone from being dropped, merge operands from being
|
||||
// dropped, and merge operands from being combined.
|
||||
// - Left half of files in L1 all have same user key, ensuring their file
|
||||
// boundaries overlap. In the past this would cause range tombstones to be
|
||||
// untruncated.
|
||||
// - Right half of L1 files all have different keys, ensuring no overlap.
|
||||
// - A range tombstone spans all L1 keys, so it is stored in every L1 file.
|
||||
// - Keys in the right side of the key-range are overwritten. These are
|
||||
// compacted down to L1 after releasing snapshots such that their seqnums
|
||||
// will be zeroed.
|
||||
// - A full range scan is performed. If the tombstone in the left L1 files
|
||||
// were untruncated, it would now cover keys newer than it (but with zeroed
|
||||
// seqnums) in the right L1 files.
|
||||
const int kFileBytes = 1 << 20;
|
||||
const int kValueBytes = 1 << 10;
|
||||
const int kNumFiles = 4;
|
||||
const int kMaxKey = kNumFiles* kFileBytes / kValueBytes;
|
||||
const int kKeysOverwritten = 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator.reset(new MockMergeOperator());
|
||||
options.num_levels = 2;
|
||||
options.target_file_size_base = kFileBytes;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
// - snapshots[0] prevents merge operands from being combined during
|
||||
// compaction.
|
||||
// - snapshots[1] prevents merge operands from being dropped due to the
|
||||
// covering range tombstone.
|
||||
const Snapshot* snapshots[] = {nullptr, nullptr};
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kFileBytes / kValueBytes; ++j) {
|
||||
auto value = RandomString(&rnd, kValueBytes);
|
||||
std::string key;
|
||||
if (i < kNumFiles / 2) {
|
||||
key = Key(0);
|
||||
} else {
|
||||
key = Key(1 + i * kFileBytes / kValueBytes + j);
|
||||
}
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, value));
|
||||
}
|
||||
if (i == 0) {
|
||||
snapshots[0] = db_->GetSnapshot();
|
||||
}
|
||||
if (i == kNumFiles - 1) {
|
||||
snapshots[1] = db_->GetSnapshot();
|
||||
// The DeleteRange is the last write so all merge operands are covered.
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(0), Key(kMaxKey + 1)));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(0));
|
||||
|
||||
auto get_key_count = [this]() -> int {
|
||||
auto* iter = db_->NewIterator(ReadOptions());
|
||||
iter->SeekToFirst();
|
||||
int keys_found = 0;
|
||||
for (; iter->Valid(); iter->Next()) {
|
||||
++keys_found;
|
||||
}
|
||||
delete iter;
|
||||
return keys_found;
|
||||
};
|
||||
|
||||
// All keys should be covered
|
||||
ASSERT_EQ(0, get_key_count());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr /* begin_key */,
|
||||
nullptr /* end_key */));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
// Roughly the left half of L1 files should have overlapping boundary keys,
|
||||
// while the right half should not.
|
||||
ASSERT_GE(NumTableFilesAtLevel(1), kNumFiles);
|
||||
|
||||
// Now overwrite a few keys that are in L1 files that definitely don't have
|
||||
// overlapping boundary keys.
|
||||
for (int i = kMaxKey; i > kMaxKey - kKeysOverwritten; --i) {
|
||||
auto value = RandomString(&rnd, kValueBytes);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), Key(i), value));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
// The overwritten keys are in L0 now, so clearly aren't covered by the range
|
||||
// tombstone in L1.
|
||||
ASSERT_EQ(kKeysOverwritten, get_key_count());
|
||||
|
||||
// Release snapshots so seqnums can be zeroed when L0->L1 happens.
|
||||
db_->ReleaseSnapshot(snapshots[0]);
|
||||
db_->ReleaseSnapshot(snapshots[1]);
|
||||
|
||||
auto begin_key_storage = Key(kMaxKey - kKeysOverwritten + 1);
|
||||
auto end_key_storage = Key(kMaxKey);
|
||||
Slice begin_key(begin_key_storage);
|
||||
Slice end_key(end_key_storage);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &begin_key, &end_key));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GE(NumTableFilesAtLevel(1), kNumFiles);
|
||||
|
||||
ASSERT_EQ(kKeysOverwritten, get_key_count());
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, DeletedMergeOperandReappearsIterPrev) {
|
||||
// Exposes a bug where we were using
|
||||
// `RangeDelPositioningMode::kBackwardTraversal` while scanning merge operands
|
||||
// in the forward direction. Confusingly, this case happened during
|
||||
// `DBIter::Prev`. It could cause assertion failure, or reappearing keys.
|
||||
const int kFileBytes = 1 << 20;
|
||||
const int kValueBytes = 1 << 10;
|
||||
// Need multiple keys so we can get results when calling `Prev()` after
|
||||
// `SeekToLast()`.
|
||||
const int kNumKeys = 3;
|
||||
const int kNumFiles = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator.reset(new MockMergeOperator());
|
||||
options.target_file_size_base = kFileBytes;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kFileBytes / kValueBytes; ++j) {
|
||||
auto value = RandomString(&rnd, kValueBytes);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), Key(j % kNumKeys), value));
|
||||
if (i == 0 && j == kNumKeys) {
|
||||
// Take snapshot to prevent covered merge operands from being dropped or
|
||||
// merged by compaction.
|
||||
snapshot = db_->GetSnapshot();
|
||||
// Do a DeleteRange near the beginning so only the oldest merge operand
|
||||
// for each key is covered. This ensures the sequence of events:
|
||||
//
|
||||
// - `DBIter::Prev()` is called
|
||||
// - After several same versions of the same user key are encountered,
|
||||
// it decides to seek using `DBIter::FindValueForCurrentKeyUsingSeek`.
|
||||
// - Binary searches to the newest version of the key, which is in the
|
||||
// leftmost file containing the user key.
|
||||
// - Scans forwards to collect all merge operands. Eventually reaches
|
||||
// the rightmost file containing the oldest merge operand, which
|
||||
// should be covered by the `DeleteRange`. If `RangeDelAggregator`
|
||||
// were not properly using `kForwardTraversal` here, that operand
|
||||
// would reappear.
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(0), Key(kNumKeys + 1)));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(0));
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr /* begin_key */,
|
||||
nullptr /* end_key */));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 1);
|
||||
|
||||
auto* iter = db_->NewIterator(ReadOptions());
|
||||
iter->SeekToLast();
|
||||
int keys_found = 0;
|
||||
for (; iter->Valid(); iter->Prev()) {
|
||||
++keys_found;
|
||||
}
|
||||
delete iter;
|
||||
ASSERT_EQ(kNumKeys, keys_found);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, SnapshotPreventsDroppedKeys) {
|
||||
const int kFileBytes = 1 << 20;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
options.target_file_size_base = kFileBytes;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "a"));
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(0),
|
||||
Key(10)));
|
||||
|
||||
db_->Flush(FlushOptions());
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.snapshot = snapshot;
|
||||
auto* iter = db_->NewIterator(read_opts);
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(Key(0), iter->key());
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
delete iter;
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, RangeTombstoneWrittenToMinimalSsts) {
|
||||
// Adapted from
|
||||
// https://github.com/cockroachdb/cockroach/blob/de8b3ea603dd1592d9dc26443c2cc92c356fbc2f/pkg/storage/engine/rocksdb_test.go#L1267-L1398.
|
||||
// Regression test for issue where range tombstone was written to more files
|
||||
// than necessary when it began exactly at the begin key in the next
|
||||
// compaction output file.
|
||||
const int kFileBytes = 1 << 20;
|
||||
const int kValueBytes = 4 << 10;
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.disable_auto_compactions = true;
|
||||
// Have a bit of slack in the size limits but we enforce them more strictly
|
||||
// when manually flushing/compacting.
|
||||
options.max_compaction_bytes = 2 * kFileBytes;
|
||||
options.target_file_size_base = 2 * kFileBytes;
|
||||
options.write_buffer_size = 2 * kFileBytes;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (char first_char : {'a', 'b', 'c'}) {
|
||||
for (int i = 0; i < kFileBytes / kValueBytes; ++i) {
|
||||
std::string key(1, first_char);
|
||||
key.append(Key(i));
|
||||
std::string value = RandomString(&rnd, kValueBytes);
|
||||
ASSERT_OK(Put(key, value));
|
||||
}
|
||||
db_->Flush(FlushOptions());
|
||||
MoveFilesToLevel(2);
|
||||
}
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(3, NumTableFilesAtLevel(2));
|
||||
|
||||
// Populate the memtable lightly while spanning the whole key-space. The
|
||||
// setting of `max_compaction_bytes` will cause the L0->L1 to output multiple
|
||||
// files to prevent a large L1->L2 compaction later.
|
||||
ASSERT_OK(Put("a", "val"));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
"c" + Key(1), "d"));
|
||||
// Our compaction output file cutting logic currently only considers point
|
||||
// keys. So, in order for the range tombstone to have a chance at landing at
|
||||
// the start of a new file, we need a point key at the range tombstone's
|
||||
// start.
|
||||
// TODO(ajkr): remove this `Put` after file cutting accounts for range
|
||||
// tombstones (#3977).
|
||||
ASSERT_OK(Put("c" + Key(1), "value"));
|
||||
db_->Flush(FlushOptions());
|
||||
|
||||
// Ensure manual L0->L1 compaction cuts the outputs before the range tombstone
|
||||
// and the range tombstone is only placed in the second SST.
|
||||
std::string begin_key_storage("c" + Key(1));
|
||||
Slice begin_key(begin_key_storage);
|
||||
std::string end_key_storage("d");
|
||||
Slice end_key(end_key_storage);
|
||||
dbfull()->TEST_CompactRange(0 /* level */, &begin_key /* begin */,
|
||||
&end_key /* end */, nullptr /* column_family */,
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(1));
|
||||
|
||||
std::vector<LiveFileMetaData> all_metadata;
|
||||
std::vector<LiveFileMetaData> l1_metadata;
|
||||
db_->GetLiveFilesMetaData(&all_metadata);
|
||||
for (const auto& metadata : all_metadata) {
|
||||
if (metadata.level == 1) {
|
||||
l1_metadata.push_back(metadata);
|
||||
}
|
||||
}
|
||||
std::sort(l1_metadata.begin(), l1_metadata.end(),
|
||||
[&](const LiveFileMetaData& a, const LiveFileMetaData& b) {
|
||||
return options.comparator->Compare(a.smallestkey, b.smallestkey) <
|
||||
0;
|
||||
});
|
||||
ASSERT_EQ("a", l1_metadata[0].smallestkey);
|
||||
ASSERT_EQ("a", l1_metadata[0].largestkey);
|
||||
ASSERT_EQ("c" + Key(1), l1_metadata[1].smallestkey);
|
||||
ASSERT_EQ("d", l1_metadata[1].largestkey);
|
||||
|
||||
TablePropertiesCollection all_table_props;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&all_table_props));
|
||||
int64_t num_range_deletions = 0;
|
||||
for (const auto& name_and_table_props : all_table_props) {
|
||||
const auto& name = name_and_table_props.first;
|
||||
const auto& table_props = name_and_table_props.second;
|
||||
// The range tombstone should only be output to the second L1 SST.
|
||||
if (name.size() >= l1_metadata[1].name.size() &&
|
||||
name.substr(name.size() - l1_metadata[1].name.size()).compare(l1_metadata[1].name) == 0) {
|
||||
ASSERT_EQ(1, table_props->num_range_deletions);
|
||||
++num_range_deletions;
|
||||
} else {
|
||||
ASSERT_EQ(0, table_props->num_range_deletions);
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(1, num_range_deletions);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+36
-66
@@ -93,7 +93,7 @@ class DBTestWithParam
|
||||
};
|
||||
|
||||
TEST_F(DBTest, MockEnvTest) {
|
||||
std::unique_ptr<MockEnv> env{new MockEnv(Env::Default())};
|
||||
unique_ptr<MockEnv> env{new MockEnv(Env::Default())};
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env.get();
|
||||
@@ -143,7 +143,7 @@ TEST_F(DBTest, MockEnvTest) {
|
||||
// defined.
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, MemEnvTest) {
|
||||
std::unique_ptr<Env> env{NewMemEnv(Env::Default())};
|
||||
unique_ptr<Env> env{NewMemEnv(Env::Default())};
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env.get();
|
||||
@@ -2313,9 +2313,6 @@ INSTANTIATE_TEST_CASE_P(
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Group commit test:
|
||||
#if !defined(TRAVIS) && !defined(OS_WIN)
|
||||
// Disable this test temporarily on Travis and appveyor as it fails
|
||||
// intermittently. Github issue: #4151
|
||||
namespace {
|
||||
|
||||
static const int kGCNumThreads = 4;
|
||||
@@ -2342,6 +2339,9 @@ static void GCThreadBody(void* arg) {
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifndef TRAVIS
|
||||
// Disable this test temporarily on Travis as it fails intermittently.
|
||||
// Github issue: #4151
|
||||
TEST_F(DBTest, GroupCommitTest) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
@@ -2351,9 +2351,7 @@ TEST_F(DBTest, GroupCommitTest) {
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"WriteThread::JoinBatchGroup:BeganWaiting",
|
||||
"DBImpl::WriteImpl:BeforeLeaderEnters"},
|
||||
{"WriteThread::AwaitState:BlockingWaiting",
|
||||
"WriteThread::EnterAsBatchGroupLeader:End"}});
|
||||
"DBImpl::WriteImpl:BeforeLeaderEnters"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Start threads
|
||||
@@ -2661,11 +2659,6 @@ class ModelDB : public DB {
|
||||
Status ret;
|
||||
return ret;
|
||||
}
|
||||
virtual Status Flush(
|
||||
const rocksdb::FlushOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_families*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status SyncWAL() override { return Status::OK(); }
|
||||
|
||||
@@ -2689,8 +2682,7 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
virtual Status GetUpdatesSince(
|
||||
rocksdb::SequenceNumber,
|
||||
std::unique_ptr<rocksdb::TransactionLogIterator>*,
|
||||
rocksdb::SequenceNumber, unique_ptr<rocksdb::TransactionLogIterator>*,
|
||||
const TransactionLogIterator::ReadOptions& /*read_options*/ =
|
||||
TransactionLogIterator::ReadOptions()) override {
|
||||
return Status::NotSupported("Not supported in Model DB");
|
||||
@@ -3512,14 +3504,8 @@ TEST_F(DBTest, SanitizeNumThreads) {
|
||||
(i < 4) ? Env::Priority::LOW : Env::Priority::HIGH);
|
||||
}
|
||||
|
||||
// Wait until 10s for they are scheduled.
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
if (options.env->GetThreadPoolQueueLen(Env::Priority::LOW) <= 1 &&
|
||||
options.env->GetThreadPoolQueueLen(Env::Priority::HIGH) <= 2) {
|
||||
break;
|
||||
}
|
||||
env_->SleepForMicroseconds(1000);
|
||||
}
|
||||
// Wait 100 milliseconds for they are scheduled.
|
||||
env_->SleepForMicroseconds(100000);
|
||||
|
||||
// pool size 3, total task 4. Queue size should be 1.
|
||||
ASSERT_EQ(1U, options.env->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
@@ -5655,18 +5641,41 @@ TEST_F(DBTest, HardLimit) {
|
||||
#if !defined(ROCKSDB_LITE) && !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
class WriteStallListener : public EventListener {
|
||||
public:
|
||||
WriteStallListener() : condition_(WriteStallCondition::kNormal) {}
|
||||
WriteStallListener()
|
||||
: cond_(&mutex_),
|
||||
condition_(WriteStallCondition::kNormal),
|
||||
expected_(WriteStallCondition::kNormal),
|
||||
expected_set_(false) {}
|
||||
void OnStallConditionsChanged(const WriteStallInfo& info) override {
|
||||
MutexLock l(&mutex_);
|
||||
condition_ = info.condition.cur;
|
||||
if (expected_set_ && condition_ == expected_) {
|
||||
cond_.Signal();
|
||||
expected_set_ = false;
|
||||
}
|
||||
}
|
||||
bool CheckCondition(WriteStallCondition expected) {
|
||||
MutexLock l(&mutex_);
|
||||
return expected == condition_;
|
||||
if (expected != condition_) {
|
||||
expected_ = expected;
|
||||
expected_set_ = true;
|
||||
while (expected != condition_) {
|
||||
// We bail out on timeout 500 milliseconds
|
||||
const uint64_t timeout_us = 500000;
|
||||
if (cond_.TimedWait(timeout_us)) {
|
||||
expected_set_ = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
port::CondVar cond_;
|
||||
WriteStallCondition condition_;
|
||||
WriteStallCondition expected_;
|
||||
bool expected_set_;
|
||||
};
|
||||
|
||||
TEST_F(DBTest, SoftLimit) {
|
||||
@@ -5687,41 +5696,6 @@ TEST_F(DBTest, SoftLimit) {
|
||||
WriteStallListener* listener = new WriteStallListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
// FlushMemtable with opt.wait=true does not wait for
|
||||
// `OnStallConditionsChanged` being called. The event listener is triggered
|
||||
// on `JobContext::Clean`, which happens after flush result is installed.
|
||||
// We use sync point to create a custom WaitForFlush that waits for
|
||||
// context cleanup.
|
||||
port::Mutex flush_mutex;
|
||||
port::CondVar flush_cv(&flush_mutex);
|
||||
bool flush_finished = false;
|
||||
auto InstallFlushCallback = [&]() {
|
||||
{
|
||||
MutexLock l(&flush_mutex);
|
||||
flush_finished = false;
|
||||
}
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCallFlush:ContextCleanedUp", [&](void*) {
|
||||
{
|
||||
MutexLock l(&flush_mutex);
|
||||
flush_finished = true;
|
||||
}
|
||||
flush_cv.SignalAll();
|
||||
});
|
||||
};
|
||||
auto WaitForFlush = [&]() {
|
||||
{
|
||||
MutexLock l(&flush_mutex);
|
||||
while (!flush_finished) {
|
||||
flush_cv.Wait();
|
||||
}
|
||||
}
|
||||
SyncPoint::GetInstance()->ClearCallBack(
|
||||
"DBImpl::BackgroundCallFlush:ContextCleanedUp");
|
||||
};
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
|
||||
// Generating 360KB in Level 3
|
||||
@@ -5757,9 +5731,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
Put(Key(i), std::string(5000, 'x'));
|
||||
Put(Key(100 - i), std::string(5000, 'x'));
|
||||
// Flush the file. File size is around 30KB.
|
||||
InstallFlushCallback();
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
WaitForFlush();
|
||||
}
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(listener->CheckCondition(WriteStallCondition::kDelayed));
|
||||
@@ -5784,6 +5756,8 @@ TEST_F(DBTest, SoftLimit) {
|
||||
&sleeping_task_low, Env::Priority::LOW);
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
sleeping_task_low.WaitUntilSleeping();
|
||||
@@ -5792,9 +5766,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
Put(Key(10 + i), std::string(5000, 'x'));
|
||||
Put(Key(90 - i), std::string(5000, 'x'));
|
||||
// Flush the file. File size is around 30KB.
|
||||
InstallFlushCallback();
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
WaitForFlush();
|
||||
}
|
||||
|
||||
// Wake up sleep task to enable compaction to run and waits
|
||||
@@ -5815,9 +5787,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
Put(Key(20 + i), std::string(5000, 'x'));
|
||||
Put(Key(80 - i), std::string(5000, 'x'));
|
||||
// Flush the file. File size is around 30KB.
|
||||
InstallFlushCallback();
|
||||
dbfull()->TEST_FlushMemTable(true, true);
|
||||
WaitForFlush();
|
||||
}
|
||||
// Wake up sleep task to enable compaction to run and waits
|
||||
// for it to go to sleep state again to make sure one compaction
|
||||
|
||||
+4
-257
@@ -61,7 +61,7 @@ TEST_P(PrefixFullBloomWithReverseComparator,
|
||||
bbto.block_cache->EraseUnRefEntries();
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->Seek("bar345");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -454,22 +454,6 @@ TEST_F(DBTest2, SharedWriteBufferLimitAcrossDB) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TestWriteBufferNoLimitWithCache) {
|
||||
Options options = CurrentOptions();
|
||||
options.arena_block_size = 4096;
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewLRUCache(LRUCacheOptions(10000000, 1, false, 0.0));
|
||||
options.write_buffer_size = 50000; // this is never hit
|
||||
// Use a write buffer total size so that the soft limit is about
|
||||
// 105000.
|
||||
options.write_buffer_manager.reset(new WriteBufferManager(0, cache));
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
// One dummy entry is 1MB.
|
||||
ASSERT_GT(cache->GetUsage(), 500000);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void ValidateKeyExistence(DB* db, const std::vector<Slice>& keys_must_exist,
|
||||
const std::vector<Slice>& keys_must_not_exist) {
|
||||
@@ -1244,14 +1228,7 @@ TEST_F(DBTest2, CompressionOptions) {
|
||||
|
||||
class CompactionStallTestListener : public EventListener {
|
||||
public:
|
||||
CompactionStallTestListener() : compacting_files_cnt_(0), compacted_files_cnt_(0) {}
|
||||
|
||||
void OnCompactionBegin(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);
|
||||
compacting_files_cnt_ += ci.input_files.size();
|
||||
}
|
||||
CompactionStallTestListener() : compacted_files_cnt_(0) {}
|
||||
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.cf_name, "default");
|
||||
@@ -1259,8 +1236,6 @@ class CompactionStallTestListener : public EventListener {
|
||||
ASSERT_EQ(ci.compaction_reason, CompactionReason::kLevelL0FilesNum);
|
||||
compacted_files_cnt_ += ci.input_files.size();
|
||||
}
|
||||
|
||||
std::atomic<size_t> compacting_files_cnt_;
|
||||
std::atomic<size_t> compacted_files_cnt_;
|
||||
};
|
||||
|
||||
@@ -1269,8 +1244,6 @@ TEST_F(DBTest2, CompactionStall) {
|
||||
{{"DBImpl::BGWorkCompaction", "DBTest2::CompactionStall:0"},
|
||||
{"DBImpl::BGWorkCompaction", "DBTest2::CompactionStall:1"},
|
||||
{"DBTest2::CompactionStall:2",
|
||||
"DBImpl::NotifyOnCompactionBegin::UnlockMutex"},
|
||||
{"DBTest2::CompactionStall:3",
|
||||
"DBImpl::NotifyOnCompactionCompleted::UnlockMutex"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -1312,18 +1285,14 @@ TEST_F(DBTest2, CompactionStall) {
|
||||
// Wait for another compaction to be triggered
|
||||
TEST_SYNC_POINT("DBTest2::CompactionStall:1");
|
||||
|
||||
// Hold NotifyOnCompactionBegin in the unlock mutex section
|
||||
TEST_SYNC_POINT("DBTest2::CompactionStall:2");
|
||||
|
||||
// Hold NotifyOnCompactionCompleted in the unlock mutex section
|
||||
TEST_SYNC_POINT("DBTest2::CompactionStall:3");
|
||||
TEST_SYNC_POINT("DBTest2::CompactionStall:2");
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_LT(NumTableFilesAtLevel(0),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
ASSERT_GT(listener->compacted_files_cnt_.load(),
|
||||
10 - options.level0_file_num_compaction_trigger);
|
||||
ASSERT_EQ(listener->compacting_files_cnt_.load(), listener->compacted_files_cnt_.load());
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
@@ -2531,61 +2500,6 @@ TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TestNumPread) {
|
||||
Options options = CurrentOptions();
|
||||
// disable block cache
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
env_->count_random_reads_ = true;
|
||||
|
||||
env_->random_file_open_counter_.store(0);
|
||||
ASSERT_OK(Put("bar", "foo"));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
// After flush, we'll open the file and read footer, meta block,
|
||||
// property block and index block.
|
||||
ASSERT_EQ(4, env_->random_read_counter_.Read());
|
||||
ASSERT_EQ(1, env_->random_file_open_counter_.load());
|
||||
|
||||
// One pread per a normal data block read
|
||||
env_->random_file_open_counter_.store(0);
|
||||
env_->random_read_counter_.Reset();
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
ASSERT_EQ(1, env_->random_read_counter_.Read());
|
||||
// All files are already opened.
|
||||
ASSERT_EQ(0, env_->random_file_open_counter_.load());
|
||||
|
||||
env_->random_file_open_counter_.store(0);
|
||||
env_->random_read_counter_.Reset();
|
||||
ASSERT_OK(Put("bar2", "foo2"));
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
ASSERT_OK(Flush());
|
||||
// After flush, we'll open the file and read footer, meta block,
|
||||
// property block and index block.
|
||||
ASSERT_EQ(4, env_->random_read_counter_.Read());
|
||||
ASSERT_EQ(1, env_->random_file_open_counter_.load());
|
||||
|
||||
// Compaction needs two input blocks, which requires 2 preads, and
|
||||
// generate a new SST file which needs 4 preads (footer, meta block,
|
||||
// property block and index block). In total 6.
|
||||
env_->random_file_open_counter_.store(0);
|
||||
env_->random_read_counter_.Reset();
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(6, env_->random_read_counter_.Read());
|
||||
// All compactin input files should have already been opened.
|
||||
ASSERT_EQ(1, env_->random_file_open_counter_.load());
|
||||
|
||||
// One pread per a normal data block read
|
||||
env_->random_file_open_counter_.store(0);
|
||||
env_->random_read_counter_.Reset();
|
||||
ASSERT_EQ("foo2", Get("bar2"));
|
||||
ASSERT_EQ(1, env_->random_read_counter_.Read());
|
||||
// SST files are already opened.
|
||||
ASSERT_EQ(0, env_->random_file_open_counter_.load());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TraceAndReplay) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
@@ -2688,74 +2602,6 @@ TEST_F(DBTest2, TraceAndReplay) {
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TraceWithLimit) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
ReadOptions ro;
|
||||
WriteOptions wo;
|
||||
TraceOptions trace_opts;
|
||||
EnvOptions env_opts;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Random rnd(301);
|
||||
|
||||
// test the max trace file size options
|
||||
trace_opts.max_trace_file_size = 5;
|
||||
std::string trace_filename = dbname_ + "/rocksdb.trace1";
|
||||
std::unique_ptr<TraceWriter> trace_writer;
|
||||
ASSERT_OK(NewFileTraceWriter(env_, env_opts, trace_filename, &trace_writer));
|
||||
ASSERT_OK(db_->StartTrace(trace_opts, std::move(trace_writer)));
|
||||
ASSERT_OK(Put(0, "a", "1"));
|
||||
ASSERT_OK(Put(0, "b", "1"));
|
||||
ASSERT_OK(Put(0, "c", "1"));
|
||||
ASSERT_OK(db_->EndTrace());
|
||||
|
||||
std::string dbname2 = test::TmpDir(env_) + "/db_replay2";
|
||||
std::string value;
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
|
||||
// Using a different name than db2, to pacify infer's use-after-lifetime
|
||||
// warnings (http://fbinfer.com).
|
||||
DB* db2_init = nullptr;
|
||||
options.create_if_missing = true;
|
||||
ASSERT_OK(DB::Open(options, dbname2, &db2_init));
|
||||
ColumnFamilyHandle* cf;
|
||||
ASSERT_OK(
|
||||
db2_init->CreateColumnFamily(ColumnFamilyOptions(), "pikachu", &cf));
|
||||
delete cf;
|
||||
delete db2_init;
|
||||
|
||||
DB* db2 = nullptr;
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
column_families.push_back(ColumnFamilyDescriptor("default", cf_options));
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor("pikachu", ColumnFamilyOptions()));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_OK(DB::Open(DBOptions(), dbname2, column_families, &handles, &db2));
|
||||
|
||||
env_->SleepForMicroseconds(100);
|
||||
// Verify that the keys don't already exist
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "a", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "b", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "c", &value).IsNotFound());
|
||||
|
||||
std::unique_ptr<TraceReader> trace_reader;
|
||||
ASSERT_OK(NewFileTraceReader(env_, env_opts, trace_filename, &trace_reader));
|
||||
Replayer replayer(db2, handles_, std::move(trace_reader));
|
||||
ASSERT_OK(replayer.Replay());
|
||||
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "a", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "b", &value).IsNotFound());
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "c", &value).IsNotFound());
|
||||
|
||||
for (auto handle : handles) {
|
||||
delete handle;
|
||||
}
|
||||
delete db2;
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, PinnableSliceAndMmapReads) {
|
||||
@@ -2831,7 +2677,7 @@ TEST_F(DBTest2, DISABLED_IteratorPinnedMemory) {
|
||||
// Verify that iterators don't pin more than one data block in block cache
|
||||
// at each time.
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
iter->SeekToFirst();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -2989,105 +2835,6 @@ TEST_F(DBTest2, TestBBTTailPrefetch) {
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TestGetColumnFamilyHandleUnlocked) {
|
||||
// Setup sync point dependency to reproduce the race condition of
|
||||
// DBImpl::GetColumnFamilyHandleUnlocked
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{ {"TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked1",
|
||||
"TestGetColumnFamilyHandleUnlocked::PreGetColumnFamilyHandleUnlocked2"},
|
||||
{"TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked2",
|
||||
"TestGetColumnFamilyHandleUnlocked::ReadColumnFamilyHandle1"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateColumnFamilies({"test1", "test2"}, Options());
|
||||
ASSERT_EQ(handles_.size(), 2);
|
||||
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
port::Thread user_thread1([&]() {
|
||||
auto cfh = dbi->GetColumnFamilyHandleUnlocked(handles_[0]->GetID());
|
||||
ASSERT_EQ(cfh->GetID(), handles_[0]->GetID());
|
||||
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked1");
|
||||
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::ReadColumnFamilyHandle1");
|
||||
ASSERT_EQ(cfh->GetID(), handles_[0]->GetID());
|
||||
});
|
||||
|
||||
port::Thread user_thread2([&]() {
|
||||
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::PreGetColumnFamilyHandleUnlocked2");
|
||||
auto cfh = dbi->GetColumnFamilyHandleUnlocked(handles_[1]->GetID());
|
||||
ASSERT_EQ(cfh->GetID(), handles_[1]->GetID());
|
||||
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked2");
|
||||
ASSERT_EQ(cfh->GetID(), handles_[1]->GetID());
|
||||
});
|
||||
|
||||
user_thread1.join();
|
||||
user_thread2.join();
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, TestCompactFiles) {
|
||||
// Setup sync point dependency to reproduce the race condition of
|
||||
// DBImpl::GetColumnFamilyHandleUnlocked
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"TestCompactFiles::IngestExternalFile1",
|
||||
"TestCompactFiles::IngestExternalFile2"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options;
|
||||
options.num_levels = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
auto* handle = db_->DefaultColumnFamily();
|
||||
ASSERT_EQ(db_->NumberLevels(handle), 2);
|
||||
|
||||
rocksdb::SstFileWriter sst_file_writer{rocksdb::EnvOptions(), options};
|
||||
std::string external_file1 = dbname_ + "/test_compact_files1.sst_t";
|
||||
std::string external_file2 = dbname_ + "/test_compact_files2.sst_t";
|
||||
std::string external_file3 = dbname_ + "/test_compact_files3.sst_t";
|
||||
|
||||
ASSERT_OK(sst_file_writer.Open(external_file1));
|
||||
ASSERT_OK(sst_file_writer.Put("1", "1"));
|
||||
ASSERT_OK(sst_file_writer.Put("2", "2"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(sst_file_writer.Open(external_file2));
|
||||
ASSERT_OK(sst_file_writer.Put("3", "3"));
|
||||
ASSERT_OK(sst_file_writer.Put("4", "4"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(sst_file_writer.Open(external_file3));
|
||||
ASSERT_OK(sst_file_writer.Put("5", "5"));
|
||||
ASSERT_OK(sst_file_writer.Put("6", "6"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(db_->IngestExternalFile(handle, {external_file1, external_file3},
|
||||
IngestExternalFileOptions()));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), 2);
|
||||
std::vector<std::string> files;
|
||||
GetSstFiles(env_, dbname_, &files);
|
||||
ASSERT_EQ(files.size(), 2);
|
||||
|
||||
port::Thread user_thread1(
|
||||
[&]() { db_->CompactFiles(CompactionOptions(), handle, files, 1); });
|
||||
|
||||
port::Thread user_thread2([&]() {
|
||||
ASSERT_OK(db_->IngestExternalFile(handle, {external_file2},
|
||||
IngestExternalFileOptions()));
|
||||
TEST_SYNC_POINT("TestCompactFiles::IngestExternalFile1");
|
||||
});
|
||||
|
||||
user_thread1.join();
|
||||
user_thread2.join();
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+31
-78
@@ -259,47 +259,6 @@ bool DBTestBase::ChangeFilterOptions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Switch between different DB options for file ingestion tests.
|
||||
bool DBTestBase::ChangeOptionsForFileIngestionTest() {
|
||||
if (option_config_ == kDefault) {
|
||||
option_config_ = kUniversalCompaction;
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
TryReopen(options);
|
||||
return true;
|
||||
} else if (option_config_ == kUniversalCompaction) {
|
||||
option_config_ = kUniversalCompactionMultiLevel;
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
TryReopen(options);
|
||||
return true;
|
||||
} else if (option_config_ == kUniversalCompactionMultiLevel) {
|
||||
option_config_ = kLevelSubcompactions;
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
assert(options.max_subcompactions > 1);
|
||||
TryReopen(options);
|
||||
return true;
|
||||
} else if (option_config_ == kLevelSubcompactions) {
|
||||
option_config_ = kUniversalSubcompactions;
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
assert(options.max_subcompactions > 1);
|
||||
TryReopen(options);
|
||||
return true;
|
||||
} else if (option_config_ == kUniversalSubcompactions) {
|
||||
option_config_ = kDirectIO;
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
TryReopen(options);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the current option configuration.
|
||||
Options DBTestBase::CurrentOptions(
|
||||
const anon::OptionsOverride& options_override) const {
|
||||
@@ -471,10 +430,6 @@ Options DBTestBase::GetOptions(
|
||||
table_options.checksum = kxxHash;
|
||||
break;
|
||||
}
|
||||
case kxxHash64Checksum: {
|
||||
table_options.checksum = kxxHash64;
|
||||
break;
|
||||
}
|
||||
case kFIFOCompaction: {
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
break;
|
||||
@@ -653,19 +608,30 @@ Status DBTestBase::ReadOnlyReopen(const Options& options) {
|
||||
Status DBTestBase::TryReopen(const Options& options) {
|
||||
Close();
|
||||
last_options_.table_factory.reset();
|
||||
// Note: operator= is an unsafe approach here since it destructs
|
||||
// std::shared_ptr in the same order of their creation, in contrast to
|
||||
// destructors which destructs them in the opposite order of creation. One
|
||||
// particular problme is that the cache destructor might invoke callback
|
||||
// functions that use Option members such as statistics. To work around this
|
||||
// problem, we manually call destructor of table_facotry which eventually
|
||||
// clears the block cache.
|
||||
// Note: operator= is an unsafe approach here since it destructs shared_ptr in
|
||||
// the same order of their creation, in contrast to destructors which
|
||||
// destructs them in the opposite order of creation. One particular problme is
|
||||
// that the cache destructor might invoke callback functions that use Option
|
||||
// members such as statistics. To work around this problem, we manually call
|
||||
// destructor of table_facotry which eventually clears the block cache.
|
||||
last_options_ = options;
|
||||
return DB::Open(options, dbname_, &db_);
|
||||
}
|
||||
|
||||
bool DBTestBase::IsDirectIOSupported() {
|
||||
return test::IsDirectIOSupported(env_, dbname_);
|
||||
EnvOptions env_options;
|
||||
env_options.use_mmap_writes = false;
|
||||
env_options.use_direct_writes = true;
|
||||
std::string tmp = TempFileName(dbname_, 999);
|
||||
Status s;
|
||||
{
|
||||
unique_ptr<WritableFile> file;
|
||||
s = env_->NewWritableFile(tmp, &file, env_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = env_->DeleteFile(tmp);
|
||||
}
|
||||
return s.ok();
|
||||
}
|
||||
|
||||
bool DBTestBase::IsMemoryMappedAccessSupported() const {
|
||||
@@ -680,13 +646,6 @@ Status DBTestBase::Flush(int cf) {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBTestBase::Flush(const std::vector<int>& cf_ids) {
|
||||
std::vector<ColumnFamilyHandle*> cfhs;
|
||||
std::for_each(cf_ids.begin(), cf_ids.end(),
|
||||
[&cfhs, this](int id) { cfhs.emplace_back(handles_[id]); });
|
||||
return db_->Flush(FlushOptions(), cfhs);
|
||||
}
|
||||
|
||||
Status DBTestBase::Put(const Slice& k, const Slice& v, WriteOptions wo) {
|
||||
if (kMergePut == option_config_) {
|
||||
return db_->Merge(wo, k, v);
|
||||
@@ -814,15 +773,13 @@ std::string DBTestBase::AllEntriesFor(const Slice& user_key, int cf) {
|
||||
Arena arena;
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
ScopedArenaIterator iter;
|
||||
if (cf == 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
kMaxSequenceNumber));
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg));
|
||||
} else {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
kMaxSequenceNumber, handles_[cf]));
|
||||
iter.set(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[cf]));
|
||||
}
|
||||
InternalKey target(user_key, kMaxSequenceNumber, kTypeValue);
|
||||
iter->Seek(target.Encode());
|
||||
@@ -1227,17 +1184,15 @@ void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
Arena arena;
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
// This should be defined after range_del_agg so that it destructs the
|
||||
// assigned iterator before it range_del_agg is already destructed.
|
||||
ScopedArenaIterator iter;
|
||||
if (cf != 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
kMaxSequenceNumber, handles_[cf]));
|
||||
iter.set(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[cf]));
|
||||
} else {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg,
|
||||
kMaxSequenceNumber));
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg));
|
||||
}
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(iter->status().ok(), true);
|
||||
@@ -1257,9 +1212,9 @@ void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
void DBTestBase::CopyFile(const std::string& source,
|
||||
const std::string& destination, uint64_t size) {
|
||||
const EnvOptions soptions;
|
||||
std::unique_ptr<SequentialFile> srcfile;
|
||||
unique_ptr<SequentialFile> srcfile;
|
||||
ASSERT_OK(env_->NewSequentialFile(source, &srcfile, soptions));
|
||||
std::unique_ptr<WritableFile> destfile;
|
||||
unique_ptr<WritableFile> destfile;
|
||||
ASSERT_OK(env_->NewWritableFile(destination, &destfile, soptions));
|
||||
|
||||
if (size == 0) {
|
||||
@@ -1437,10 +1392,8 @@ void DBTestBase::VerifyDBInternal(
|
||||
std::vector<std::pair<std::string, std::string>> true_data) {
|
||||
Arena arena;
|
||||
InternalKeyComparator icmp(last_options_.comparator);
|
||||
ReadRangeDelAggregator range_del_agg(&icmp,
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
auto iter =
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, kMaxSequenceNumber);
|
||||
RangeDelAggregator range_del_agg(icmp, {});
|
||||
auto iter = dbfull()->NewInternalIterator(&arena, &range_del_agg);
|
||||
iter->SeekToFirst();
|
||||
for (auto p : true_data) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
+46
-22
@@ -46,7 +46,6 @@
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/mock_time_env.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
#include "util/string_util.h"
|
||||
@@ -169,7 +168,7 @@ class SpecialMemTableRep : public MemTableRep {
|
||||
virtual ~SpecialMemTableRep() override {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MemTableRep> memtable_;
|
||||
unique_ptr<MemTableRep> memtable_;
|
||||
int num_entries_flush_;
|
||||
int num_entries_;
|
||||
};
|
||||
@@ -207,15 +206,15 @@ class SpecialEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit SpecialEnv(Env* base);
|
||||
|
||||
Status NewWritableFile(const std::string& f, std::unique_ptr<WritableFile>* r,
|
||||
Status NewWritableFile(const std::string& f, unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
class SSTableFile : public WritableFile {
|
||||
private:
|
||||
SpecialEnv* env_;
|
||||
std::unique_ptr<WritableFile> base_;
|
||||
unique_ptr<WritableFile> base_;
|
||||
|
||||
public:
|
||||
SSTableFile(SpecialEnv* env, std::unique_ptr<WritableFile>&& base)
|
||||
SSTableFile(SpecialEnv* env, unique_ptr<WritableFile>&& base)
|
||||
: env_(env), base_(std::move(base)) {}
|
||||
Status Append(const Slice& data) override {
|
||||
if (env_->table_write_callback_) {
|
||||
@@ -295,7 +294,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
};
|
||||
class ManifestFile : public WritableFile {
|
||||
public:
|
||||
ManifestFile(SpecialEnv* env, std::unique_ptr<WritableFile>&& b)
|
||||
ManifestFile(SpecialEnv* env, unique_ptr<WritableFile>&& b)
|
||||
: env_(env), base_(std::move(b)) {}
|
||||
Status Append(const Slice& data) override {
|
||||
if (env_->manifest_write_error_.load(std::memory_order_acquire)) {
|
||||
@@ -322,11 +321,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
private:
|
||||
SpecialEnv* env_;
|
||||
std::unique_ptr<WritableFile> base_;
|
||||
unique_ptr<WritableFile> base_;
|
||||
};
|
||||
class WalFile : public WritableFile {
|
||||
public:
|
||||
WalFile(SpecialEnv* env, std::unique_ptr<WritableFile>&& b)
|
||||
WalFile(SpecialEnv* env, unique_ptr<WritableFile>&& b)
|
||||
: env_(env), base_(std::move(b)) {
|
||||
env_->num_open_wal_file_.fetch_add(1);
|
||||
}
|
||||
@@ -378,7 +377,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
private:
|
||||
SpecialEnv* env_;
|
||||
std::unique_ptr<WritableFile> base_;
|
||||
unique_ptr<WritableFile> base_;
|
||||
};
|
||||
|
||||
if (non_writeable_rate_.load(std::memory_order_acquire) > 0) {
|
||||
@@ -420,11 +419,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
}
|
||||
|
||||
Status NewRandomAccessFile(const std::string& f,
|
||||
std::unique_ptr<RandomAccessFile>* r,
|
||||
unique_ptr<RandomAccessFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
class CountingFile : public RandomAccessFile {
|
||||
public:
|
||||
CountingFile(std::unique_ptr<RandomAccessFile>&& target,
|
||||
CountingFile(unique_ptr<RandomAccessFile>&& target,
|
||||
anon::AtomicCounter* counter,
|
||||
std::atomic<size_t>* bytes_read)
|
||||
: target_(std::move(target)),
|
||||
@@ -439,7 +438,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<RandomAccessFile> target_;
|
||||
unique_ptr<RandomAccessFile> target_;
|
||||
anon::AtomicCounter* counter_;
|
||||
std::atomic<size_t>* bytes_read_;
|
||||
};
|
||||
@@ -457,11 +456,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
}
|
||||
|
||||
virtual Status NewSequentialFile(const std::string& f,
|
||||
std::unique_ptr<SequentialFile>* r,
|
||||
unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
class CountingFile : public SequentialFile {
|
||||
public:
|
||||
CountingFile(std::unique_ptr<SequentialFile>&& target,
|
||||
CountingFile(unique_ptr<SequentialFile>&& target,
|
||||
anon::AtomicCounter* counter)
|
||||
: target_(std::move(target)), counter_(counter) {}
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
@@ -471,7 +470,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
virtual Status Skip(uint64_t n) override { return target_->Skip(n); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<SequentialFile> target_;
|
||||
unique_ptr<SequentialFile> target_;
|
||||
anon::AtomicCounter* counter_;
|
||||
};
|
||||
|
||||
@@ -574,7 +573,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
std::atomic<int> delete_count_;
|
||||
|
||||
std::atomic<bool> time_elapse_only_sleep_;
|
||||
bool time_elapse_only_sleep_;
|
||||
|
||||
bool no_slowdown_;
|
||||
|
||||
@@ -583,6 +582,37 @@ class SpecialEnv : public EnvWrapper {
|
||||
std::atomic<size_t> compaction_readahead_size_{};
|
||||
};
|
||||
|
||||
class MockTimeEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit MockTimeEnv(Env* base) : EnvWrapper(base) {}
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* time) override {
|
||||
assert(time != nullptr);
|
||||
assert(current_time_ <=
|
||||
static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
|
||||
*time = static_cast<int64_t>(current_time_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual uint64_t NowMicros() override {
|
||||
assert(current_time_ <= std::numeric_limits<uint64_t>::max() / 1000000);
|
||||
return current_time_ * 1000000;
|
||||
}
|
||||
|
||||
virtual uint64_t NowNanos() override {
|
||||
assert(current_time_ <= std::numeric_limits<uint64_t>::max() / 1000000000);
|
||||
return current_time_ * 1000000000;
|
||||
}
|
||||
|
||||
void set_current_time(uint64_t time) {
|
||||
assert(time >= current_time_);
|
||||
current_time_ = time;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<uint64_t> current_time_{0};
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class OnFileDeletionListener : public EventListener {
|
||||
public:
|
||||
@@ -675,7 +705,6 @@ class DBTestBase : public testing::Test {
|
||||
kBlockBasedTableWithPartitionedIndexFormat4,
|
||||
kPartitionedFilterWithNewTableReaderForCompactions,
|
||||
kUniversalSubcompactions,
|
||||
kxxHash64Checksum,
|
||||
// This must be the last line
|
||||
kEnd,
|
||||
};
|
||||
@@ -747,9 +776,6 @@ class DBTestBase : public testing::Test {
|
||||
// Jump from kDefault to kFilter to kFullFilter
|
||||
bool ChangeFilterOptions();
|
||||
|
||||
// Switch between different DB options for file ingestion tests.
|
||||
bool ChangeOptionsForFileIngestionTest();
|
||||
|
||||
// Return the current option configuration.
|
||||
Options CurrentOptions(const anon::OptionsOverride& options_override =
|
||||
anon::OptionsOverride()) const;
|
||||
@@ -803,8 +829,6 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
Status Flush(int cf = 0);
|
||||
|
||||
Status Flush(const std::vector<int>& cf_ids);
|
||||
|
||||
Status Put(const Slice& k, const Slice& v, WriteOptions wo = WriteOptions());
|
||||
|
||||
Status Put(int cf, const Slice& k, const Slice& v,
|
||||
|
||||
@@ -1154,7 +1154,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionCompressRatio2) {
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
ASSERT_LT(TotalSize(), 120000U * 12 * 0.82 + 120000 * 2);
|
||||
ASSERT_LT(TotalSize(), 120000U * 12 * 0.8 + 120000 * 2);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
|
||||
+6
-8
@@ -33,8 +33,7 @@ class DBWALTest : public DBTestBase {
|
||||
class EnrichedSpecialEnv : public SpecialEnv {
|
||||
public:
|
||||
explicit EnrichedSpecialEnv(Env* base) : SpecialEnv(base) {}
|
||||
Status NewSequentialFile(const std::string& f,
|
||||
std::unique_ptr<SequentialFile>* r,
|
||||
Status NewSequentialFile(const std::string& f, unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
InstrumentedMutexLock l(&env_mutex_);
|
||||
if (f == skipped_wal) {
|
||||
@@ -89,7 +88,6 @@ class DBWALTestWithEnrichedEnv : public DBTestBase {
|
||||
enriched_env_ = new EnrichedSpecialEnv(env_->target());
|
||||
auto options = CurrentOptions();
|
||||
options.env = enriched_env_;
|
||||
options.allow_2pc = true;
|
||||
Reopen(options);
|
||||
delete env_;
|
||||
// to be deleted by the parent class
|
||||
@@ -804,12 +802,12 @@ class RecoveryTestHelper {
|
||||
|
||||
*count = 0;
|
||||
|
||||
std::shared_ptr<Cache> table_cache = NewLRUCache(50, 0);
|
||||
shared_ptr<Cache> table_cache = NewLRUCache(50, 0);
|
||||
EnvOptions env_options;
|
||||
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
|
||||
|
||||
std::unique_ptr<VersionSet> versions;
|
||||
std::unique_ptr<WalManager> wal_manager;
|
||||
unique_ptr<VersionSet> versions;
|
||||
unique_ptr<WalManager> wal_manager;
|
||||
WriteController write_controller;
|
||||
|
||||
versions.reset(new VersionSet(test->dbname_, &db_options, env_options,
|
||||
@@ -823,9 +821,9 @@ class RecoveryTestHelper {
|
||||
for (size_t j = kWALFileOffset; j < wal_count + kWALFileOffset; j++) {
|
||||
uint64_t current_log_number = j;
|
||||
std::string fname = LogFileName(test->dbname_, current_log_number);
|
||||
std::unique_ptr<WritableFile> file;
|
||||
unique_ptr<WritableFile> file;
|
||||
ASSERT_OK(db_options.env->NewWritableFile(fname, &file, env_options));
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), fname, env_options));
|
||||
current_log_writer.reset(
|
||||
new log::Writer(std::move(file_writer), current_log_number,
|
||||
|
||||
@@ -674,16 +674,5 @@ int InternalKeyComparator::CompareKeySeq(const Slice& akey,
|
||||
return r;
|
||||
}
|
||||
|
||||
struct ParsedInternalKeyComparator {
|
||||
explicit ParsedInternalKeyComparator(const InternalKeyComparator* c)
|
||||
: cmp(c) {}
|
||||
|
||||
bool operator()(const ParsedInternalKey& a,
|
||||
const ParsedInternalKey& b) const {
|
||||
return cmp->Compare(a, b) < 0;
|
||||
}
|
||||
|
||||
const InternalKeyComparator* cmp;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+1
-1
@@ -157,7 +157,7 @@ void ErrorHandler::CancelErrorRecovery() {
|
||||
// a default one is allocated during DB::Open(), so there will always be
|
||||
// one.
|
||||
// This can also get called as part of a recovery operation. In that case, we
|
||||
// also track the error separately in recovery_error_ so we can tell in the
|
||||
// also track the error seperately in recovery_error_ so we can tell in the
|
||||
// end whether recovery succeeded or not
|
||||
Status ErrorHandler::SetBGError(const Status& bg_err, BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ class ErrorHandler {
|
||||
DBImpl* db_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
Status bg_error_;
|
||||
// A separate Status variable used to record any errors during the
|
||||
// A seperate Status variable used to record any errors during the
|
||||
// recovery process from hard errors
|
||||
Status recovery_error_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
class ExternalSSTFileBasicTest : public DBTestBase {
|
||||
public:
|
||||
ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_test") {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
@@ -42,7 +41,7 @@ class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
const Options options, std::vector<int> keys,
|
||||
const std::vector<ValueType>& value_types,
|
||||
std::vector<std::pair<int, int>> range_deletions, int file_id,
|
||||
bool write_global_seqno, std::map<std::string, std::string>* true_data) {
|
||||
std::map<std::string, std::string>* true_data) {
|
||||
assert(value_types.size() == 1 || keys.size() == value_types.size());
|
||||
std::string file_path = sst_files_dir_ + ToString(file_id);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
@@ -106,7 +105,6 @@ class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
if (s.ok()) {
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.allow_global_seqno = true;
|
||||
ifo.write_global_seqno = write_global_seqno;
|
||||
s = db_->IngestExternalFile({file_path}, ifo);
|
||||
}
|
||||
return s;
|
||||
@@ -115,18 +113,17 @@ class ExternalSSTFileBasicTest : public DBTestBase,
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<int> keys,
|
||||
const std::vector<ValueType>& value_types, int file_id,
|
||||
bool write_global_seqno, std::map<std::string, std::string>* true_data) {
|
||||
std::map<std::string, std::string>* true_data) {
|
||||
return GenerateAndAddExternalFile(options, keys, value_types, {}, file_id,
|
||||
write_global_seqno, true_data);
|
||||
true_data);
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<int> keys, const ValueType value_type,
|
||||
int file_id, bool write_global_seqno,
|
||||
std::map<std::string, std::string>* true_data) {
|
||||
int file_id, std::map<std::string, std::string>* true_data) {
|
||||
return GenerateAndAddExternalFile(options, keys,
|
||||
std::vector<ValueType>(1, value_type),
|
||||
file_id, write_global_seqno, true_data);
|
||||
file_id, true_data);
|
||||
}
|
||||
|
||||
~ExternalSSTFileBasicTest() { test::DestroyDir(env_, sst_files_dir_); }
|
||||
@@ -248,8 +245,7 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
bool write_global_seqno = GetParam();
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -259,37 +255,33 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2, 3, 4, 5, 6},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {10, 11, 12, 13},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 4, 6},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 4, 6}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {11, 15, 19},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {11, 15, 19}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {120, 130},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {120, 130}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 130},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 130}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
@@ -300,21 +292,18 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
}
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {60, 61, 62},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 61, 62}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {40, 41, 42},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {40, 41, 42}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {20, 30, 40},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {20, 30, 40}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
||||
|
||||
@@ -322,39 +311,35 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
|
||||
|
||||
// We will need a seqno for the file regardless if the file overwrite
|
||||
// keys in the DB or not because we have a snapshot
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1000, 1002},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1000, 1002}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2000, 3002},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {2000, 3002}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 20, 40, 100, 150},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {5000, 5001},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {5000, 5001}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// No snapshot anymore, no need to assign a seqno
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
} while (ChangeOptionsForFileIngestionTest());
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
bool write_global_seqno = GetParam();
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
@@ -365,57 +350,52 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2, 3, 4, 5, 6},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {10, 11, 12, 13},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 4, 6},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 4, 6}, ValueType::kTypeMerge, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {11, 15, 19},
|
||||
ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {120, 130},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {120, 130}, ValueType::kTypeMerge, file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 130},
|
||||
ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 130}, ValueType::kTypeDeletion, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {120}, {ValueType::kTypeValue}, {{120, 135}}, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {120},
|
||||
{ValueType::kTypeValue}, {{120, 135}},
|
||||
file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {}, {}, {{110, 120}},
|
||||
file_id++, write_global_seqno,
|
||||
&true_data));
|
||||
file_id++, &true_data));
|
||||
// The range deletion ends on a key, but it doesn't actually delete
|
||||
// this key because the largest key in the range is exclusive. Still,
|
||||
// it counts as an overlap so a new seqno will be assigned.
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {}, {}, {{100, 109}},
|
||||
file_id++, write_global_seqno,
|
||||
&true_data));
|
||||
file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
||||
|
||||
@@ -426,21 +406,19 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
}
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {60, 61, 62},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 61, 62}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {40, 41, 42},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {40, 41, 42}, ValueType::kTypeMerge, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {20, 30, 40},
|
||||
ValueType::kTypeDeletion, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
||||
|
||||
@@ -448,39 +426,35 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
|
||||
|
||||
// We will need a seqno for the file regardless if the file overwrite
|
||||
// keys in the DB or not because we have a snapshot
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1000, 1002},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1000, 1002}, ValueType::kTypeMerge, file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2000, 3002},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {2000, 3002}, ValueType::kTypeMerge, file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 20, 40, 100, 150},
|
||||
ValueType::kTypeMerge, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {5000, 5001},
|
||||
ValueType::kTypeValue, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {5000, 5001}, ValueType::kTypeValue, file_id++, &true_data));
|
||||
// No snapshot anymore, no need to assign a seqno
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
} while (ChangeOptionsForFileIngestionTest());
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
bool write_global_seqno = GetParam();
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
@@ -493,7 +467,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {1, 2, 3, 4, 5, 6},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
|
||||
ValueType::kTypeMerge, ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
@@ -501,35 +475,33 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {10, 11, 12, 13},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
|
||||
ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 4, 6},
|
||||
{ValueType::kTypeDeletion, ValueType::kTypeValue,
|
||||
ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
options, {1, 4, 6}, {ValueType::kTypeDeletion, ValueType::kTypeValue,
|
||||
ValueType::kTypeMerge},
|
||||
file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {11, 15, 19},
|
||||
{ValueType::kTypeDeletion, ValueType::kTypeMerge,
|
||||
ValueType::kTypeValue},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
options, {11, 15, 19}, {ValueType::kTypeDeletion, ValueType::kTypeMerge,
|
||||
ValueType::kTypeValue},
|
||||
file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {120, 130}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1, 130}, {ValueType::kTypeMerge, ValueType::kTypeDeletion},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
@@ -537,14 +509,14 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {150, 151, 152},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge,
|
||||
ValueType::kTypeDeletion},
|
||||
{{150, 160}, {180, 190}}, file_id++, write_global_seqno, &true_data));
|
||||
{{150, 160}, {180, 190}}, file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {150, 151, 152},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
|
||||
{{200, 250}}, file_id++, write_global_seqno, &true_data));
|
||||
{{200, 250}}, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
|
||||
|
||||
@@ -552,7 +524,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {300, 301, 302},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge,
|
||||
ValueType::kTypeDeletion},
|
||||
{{1, 2}, {152, 154}}, file_id++, write_global_seqno, &true_data));
|
||||
{{1, 2}, {152, 154}}, file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
|
||||
|
||||
@@ -566,7 +538,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 61, 62},
|
||||
{ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// File doesn't overwrite any keys, no seqno needed
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
|
||||
@@ -574,7 +546,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {40, 41, 42},
|
||||
{ValueType::kTypeValue, ValueType::kTypeDeletion,
|
||||
ValueType::kTypeDeletion},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
|
||||
|
||||
@@ -582,7 +554,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {20, 30, 40},
|
||||
{ValueType::kTypeDeletion, ValueType::kTypeDeletion,
|
||||
ValueType::kTypeDeletion},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// File overwrites some keys, a seqno will be assigned
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
|
||||
|
||||
@@ -592,13 +564,13 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
// keys in the DB or not because we have a snapshot
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {1000, 1002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {2000, 3002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
|
||||
|
||||
@@ -606,7 +578,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
options, {1, 20, 40, 100, 150},
|
||||
{ValueType::kTypeDeletion, ValueType::kTypeDeletion,
|
||||
ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// A global seqno will be assigned anyway because of the snapshot
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
@@ -614,13 +586,13 @@ TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {5000, 5001}, {ValueType::kTypeValue, ValueType::kTypeMerge},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
// No snapshot anymore, no need to assign a seqno
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
|
||||
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
} while (ChangeOptionsForFileIngestionTest());
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
||||
@@ -663,7 +635,7 @@ TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
int kNumLevels = 7;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -690,13 +662,12 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 1));
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
// overlaps with L0 file but not memtable, so flush is skipped and file is
|
||||
// ingested into L0
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 90}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
{{65, 70}, {70, 85}}, file_id++, write_global_seqno, &true_data));
|
||||
{{65, 70}, {70, 85}}, file_id++, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
@@ -706,7 +677,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
// file is ingested into L5
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {10, 40}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
@@ -715,7 +686,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
// overlaps with L5 file but not memtable or L0 file, so flush is skipped and
|
||||
// file is ingested into L4
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {}, {}, {{5, 15}}, file_id++,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
@@ -727,7 +698,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
// count increases by two.
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {100, 140}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
file_id++, write_global_seqno, &true_data));
|
||||
file_id++, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
@@ -740,16 +711,13 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
// seqnum.
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {151, 175}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
{{160, 200}}, file_id++, write_global_seqno, &true_data));
|
||||
{{160, 200}}, file_id++, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(options.num_levels - 1));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
|
||||
testing::Bool());
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -225,7 +225,7 @@ void ExternalSstFileIngestionJob::UpdateStats() {
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
InternalStats::CompactionStats stats(CompactionReason::kExternalSstIngestion, 1);
|
||||
stats.micros = total_time;
|
||||
// If actual copy occurred for this file, then we need to count the file
|
||||
// If actual copy occured for this file, then we need to count the file
|
||||
// size as the actual bytes written. If the file was linked, then we ignore
|
||||
// the bytes written for file metadata.
|
||||
// TODO (yanqin) maybe account for file metadata bytes for exact accuracy?
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class ExternalSSTFileTest : public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
class ExternalSSTFileTest : public DBTestBase {
|
||||
public:
|
||||
ExternalSSTFileTest() : DBTestBase("/external_sst_file_test") {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
@@ -31,8 +30,7 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options,
|
||||
std::vector<std::pair<std::string, std::string>> data, int file_id = -1,
|
||||
bool allow_global_seqno = false, bool write_global_seqno = false,
|
||||
bool sort_data = false,
|
||||
bool allow_global_seqno = false, bool sort_data = false,
|
||||
std::map<std::string, std::string>* true_data = nullptr,
|
||||
ColumnFamilyHandle* cfh = nullptr) {
|
||||
// Generate a file id if not provided
|
||||
@@ -75,7 +73,6 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
if (s.ok()) {
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.allow_global_seqno = allow_global_seqno;
|
||||
ifo.write_global_seqno = allow_global_seqno ? write_global_seqno : false;
|
||||
if (cfh) {
|
||||
s = db_->IngestExternalFile(cfh, {file_path}, ifo);
|
||||
} else {
|
||||
@@ -152,10 +149,11 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<std::pair<int, std::string>> data,
|
||||
int file_id = -1, bool allow_global_seqno = false,
|
||||
bool write_global_seqno = false, bool sort_data = false,
|
||||
int file_id = -1, bool allow_global_seqno = false, bool sort_data = false,
|
||||
std::map<std::string, std::string>* true_data = nullptr,
|
||||
ColumnFamilyHandle* cfh = nullptr) {
|
||||
std::vector<std::pair<std::string, std::string>> file_data;
|
||||
@@ -163,14 +161,13 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
file_data.emplace_back(Key(entry.first), entry.second);
|
||||
}
|
||||
return GenerateAndAddExternalFile(options, file_data, file_id,
|
||||
allow_global_seqno, write_global_seqno,
|
||||
sort_data, true_data, cfh);
|
||||
allow_global_seqno, sort_data, true_data,
|
||||
cfh);
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<int> keys, int file_id = -1,
|
||||
bool allow_global_seqno = false, bool write_global_seqno = false,
|
||||
bool sort_data = false,
|
||||
bool allow_global_seqno = false, bool sort_data = false,
|
||||
std::map<std::string, std::string>* true_data = nullptr,
|
||||
ColumnFamilyHandle* cfh = nullptr) {
|
||||
std::vector<std::pair<std::string, std::string>> file_data;
|
||||
@@ -178,20 +175,18 @@ class ExternalSSTFileTest : public DBTestBase,
|
||||
file_data.emplace_back(Key(k), Key(k) + ToString(file_id));
|
||||
}
|
||||
return GenerateAndAddExternalFile(options, file_data, file_id,
|
||||
allow_global_seqno, write_global_seqno,
|
||||
sort_data, true_data, cfh);
|
||||
allow_global_seqno, sort_data, true_data,
|
||||
cfh);
|
||||
}
|
||||
|
||||
Status DeprecatedAddFile(const std::vector<std::string>& files,
|
||||
bool move_files = false,
|
||||
bool skip_snapshot_check = false,
|
||||
bool skip_write_global_seqno = false) {
|
||||
bool skip_snapshot_check = false) {
|
||||
IngestExternalFileOptions opts;
|
||||
opts.move_files = move_files;
|
||||
opts.snapshot_consistency = !skip_snapshot_check;
|
||||
opts.allow_global_seqno = false;
|
||||
opts.allow_blocking_flush = false;
|
||||
opts.write_global_seqno = !skip_write_global_seqno;
|
||||
return db_->IngestExternalFile(files, opts);
|
||||
}
|
||||
|
||||
@@ -458,7 +453,6 @@ TEST_F(ExternalSSTFileTest, Basic) {
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction |
|
||||
kRangeDelSkipConfigs));
|
||||
}
|
||||
|
||||
class SstFileWriterCollector : public TablePropertiesCollector {
|
||||
public:
|
||||
explicit SstFileWriterCollector(const std::string prefix) : prefix_(prefix) {
|
||||
@@ -1147,7 +1141,7 @@ TEST_F(ExternalSSTFileTest, OverlappingRanges) {
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, PickedLevel) {
|
||||
TEST_F(ExternalSSTFileTest, PickedLevel) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = false;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
@@ -1158,11 +1152,11 @@ TEST_P(ExternalSSTFileTest, PickedLevel) {
|
||||
|
||||
// File 0 will go to last level (L3)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 10}, -1, false, false,
|
||||
false, &true_data));
|
||||
&true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "0,0,0,1");
|
||||
|
||||
// File 1 will go to level L2 (since it overlap with file 0 in L3)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 9}, -1, false, false, false,
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 9}, -1, false, false,
|
||||
&true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "0,0,1,1");
|
||||
|
||||
@@ -1192,13 +1186,13 @@ TEST_P(ExternalSSTFileTest, PickedLevel) {
|
||||
|
||||
// This file overlaps with file 0 (L3), file 1 (L2) and the
|
||||
// output of compaction going to L1
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {4, 7}, -1, false, false, false,
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {4, 7}, -1, false, false,
|
||||
&true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "5,0,1,1");
|
||||
|
||||
// This file does not overlap with any file or with the running compaction
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {9000, 9001}, -1, false, false,
|
||||
false, &true_data));
|
||||
&true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "5,0,1,2");
|
||||
|
||||
// Hold compaction from finishing
|
||||
@@ -1317,8 +1311,7 @@ TEST_F(ExternalSSTFileTest, IngestNonExistingFile) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
|
||||
|
||||
// After full compaction, there should be only 1 file.
|
||||
std::vector<std::string> files;
|
||||
env_->GetChildren(dbname_, &files);
|
||||
@@ -1429,12 +1422,12 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
|
||||
// This file overlaps with the output of the compaction (going to L3)
|
||||
// so the file will be added to L0 since L3 is the base level
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {31, 32, 33, 34}, -1, false,
|
||||
false, false, &true_data));
|
||||
false, &true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "5");
|
||||
|
||||
// This file does not overlap with the current running compactiong
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {9000, 9001}, -1, false, false,
|
||||
false, &true_data));
|
||||
&true_data));
|
||||
EXPECT_EQ(FilesPerLevel(), "5,0,0,1");
|
||||
|
||||
// Hold compaction from finishing
|
||||
@@ -1449,24 +1442,24 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 15, 19}, -1, false, false,
|
||||
false, &true_data));
|
||||
&true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "1,0,0,3");
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1000, 1001, 1002}, -1, false,
|
||||
false, false, &true_data));
|
||||
false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "1,0,0,4");
|
||||
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {500, 600, 700}, -1, false,
|
||||
false, false, &true_data));
|
||||
false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "1,0,0,5");
|
||||
|
||||
// File 5 overlaps with file 2 (L3 / base level)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {2, 10}, -1, false, false,
|
||||
false, &true_data));
|
||||
&true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "2,0,0,5");
|
||||
|
||||
// File 6 overlaps with file 2 (L3 / base level) and file 5 (L0)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 9}, -1, false, false, false,
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {3, 9}, -1, false, false,
|
||||
&true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "3,0,0,5");
|
||||
|
||||
@@ -1486,7 +1479,7 @@ TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
|
||||
|
||||
// File 7 overlaps with file 4 (L3)
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {650, 651, 652}, -1, false,
|
||||
false, false, &true_data));
|
||||
false, &true_data));
|
||||
ASSERT_EQ(FilesPerLevel(), "5,0,0,5");
|
||||
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
@@ -1620,13 +1613,12 @@ TEST_F(ExternalSSTFileTest, SstFileWriterNonSharedKeys) {
|
||||
ASSERT_OK(DeprecatedAddFile({file_path}));
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
|
||||
TEST_F(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
|
||||
Options options = CurrentOptions();
|
||||
options.IncreaseParallelism(20);
|
||||
options.level0_slowdown_writes_trigger = 256;
|
||||
options.level0_stop_writes_trigger = 256;
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
for (int iter = 0; iter < 2; iter++) {
|
||||
bool write_to_memtable = (iter == 0);
|
||||
DestroyAndReopen(options);
|
||||
@@ -1651,8 +1643,7 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
|
||||
}
|
||||
} else {
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, random_data, -1, true,
|
||||
write_global_seqno, true,
|
||||
&true_data));
|
||||
true, &true_data));
|
||||
}
|
||||
}
|
||||
size_t kcnt = 0;
|
||||
@@ -1662,7 +1653,7 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
TEST_F(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 5;
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -1681,9 +1672,8 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
for (int i = 0; i <= 20; i++) {
|
||||
file_data.emplace_back(Key(i), "L4");
|
||||
}
|
||||
bool write_global_seqno = GetParam();
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true, false,
|
||||
&true_data));
|
||||
|
||||
// This file dont overlap with anything in the DB, will go to L4
|
||||
ASSERT_EQ("0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1693,8 +1683,8 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
for (int i = 80; i <= 130; i++) {
|
||||
file_data.emplace_back(Key(i), "L0");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true, false,
|
||||
&true_data));
|
||||
|
||||
// This file overlap with the memtable, so it will flush it and add
|
||||
// it self to L0
|
||||
@@ -1705,8 +1695,8 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
for (int i = 30; i <= 50; i++) {
|
||||
file_data.emplace_back(Key(i), "L4");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true, false,
|
||||
&true_data));
|
||||
|
||||
// This file dont overlap with anything in the DB and fit in L4 as well
|
||||
ASSERT_EQ("2,0,0,0,2", FilesPerLevel());
|
||||
@@ -1716,8 +1706,8 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
for (int i = 10; i <= 40; i++) {
|
||||
file_data.emplace_back(Key(i), "L3");
|
||||
}
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1, true, false,
|
||||
&true_data));
|
||||
|
||||
// This file overlap with files in L4, we will ingest it in L3
|
||||
ASSERT_EQ("2,0,0,1,2", FilesPerLevel());
|
||||
@@ -1726,7 +1716,7 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
|
||||
TEST_F(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
uint64_t entries_in_memtable;
|
||||
@@ -1740,17 +1730,16 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
|
||||
&entries_in_memtable);
|
||||
ASSERT_GE(entries_in_memtable, 1);
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
// No need for flush
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {90, 100, 110}, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {90, 100, 110}, -1, true, false,
|
||||
&true_data));
|
||||
db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&entries_in_memtable);
|
||||
ASSERT_GE(entries_in_memtable, 1);
|
||||
|
||||
// This file will flush the memtable
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {19, 20, 21}, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {19, 20, 21}, -1, true, false,
|
||||
&true_data));
|
||||
db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&entries_in_memtable);
|
||||
ASSERT_EQ(entries_in_memtable, 0);
|
||||
@@ -1765,14 +1754,14 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
|
||||
|
||||
// No need for flush, this file keys fit between the memtable keys
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {202, 203, 204}, -1, true,
|
||||
write_global_seqno, false, &true_data));
|
||||
false, &true_data));
|
||||
db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&entries_in_memtable);
|
||||
ASSERT_GE(entries_in_memtable, 1);
|
||||
|
||||
// This file will flush the memtable
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {206, 207}, -1, true, false,
|
||||
write_global_seqno, &true_data));
|
||||
&true_data));
|
||||
db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
|
||||
&entries_in_memtable);
|
||||
ASSERT_EQ(entries_in_memtable, 0);
|
||||
@@ -1781,7 +1770,7 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoMemtableFlush) {
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, L0SortingIssue) {
|
||||
TEST_F(ExternalSSTFileTest, L0SortingIssue) {
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 2;
|
||||
DestroyAndReopen(options);
|
||||
@@ -1790,13 +1779,10 @@ TEST_P(ExternalSSTFileTest, L0SortingIssue) {
|
||||
ASSERT_OK(Put(Key(1), "memtable"));
|
||||
ASSERT_OK(Put(Key(10), "memtable"));
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
// No Flush needed, No global seqno needed, Ingest in L1
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {7, 8}, -1, true,
|
||||
write_global_seqno, false));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {7, 8}, -1, true, false));
|
||||
// No Flush needed, but need a global seqno, Ingest in L0
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {7, 8}, -1, true,
|
||||
write_global_seqno, false));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {7, 8}, -1, true, false));
|
||||
printf("%s\n", FilesPerLevel().c_str());
|
||||
|
||||
// Overwrite what we added using external files
|
||||
@@ -2025,17 +2011,15 @@ class TestIngestExternalFileListener : public EventListener {
|
||||
std::vector<ExternalFileIngestionInfo> ingested_files;
|
||||
};
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestionListener) {
|
||||
TEST_F(ExternalSSTFileTest, IngestionListener) {
|
||||
Options options = CurrentOptions();
|
||||
TestIngestExternalFileListener* listener =
|
||||
new TestIngestExternalFileListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
CreateAndReopenWithCF({"koko", "toto"}, options);
|
||||
|
||||
bool write_global_seqno = GetParam();
|
||||
// Ingest into default cf
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
|
||||
write_global_seqno, true, nullptr,
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true, true, nullptr,
|
||||
handles_[0]));
|
||||
ASSERT_EQ(listener->ingested_files.size(), 1);
|
||||
ASSERT_EQ(listener->ingested_files.back().cf_name, "default");
|
||||
@@ -2046,8 +2030,7 @@ TEST_P(ExternalSSTFileTest, IngestionListener) {
|
||||
"default");
|
||||
|
||||
// Ingest into cf1
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
|
||||
write_global_seqno, true, nullptr,
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true, true, nullptr,
|
||||
handles_[1]));
|
||||
ASSERT_EQ(listener->ingested_files.size(), 2);
|
||||
ASSERT_EQ(listener->ingested_files.back().cf_name, "koko");
|
||||
@@ -2058,8 +2041,7 @@ TEST_P(ExternalSSTFileTest, IngestionListener) {
|
||||
"koko");
|
||||
|
||||
// Ingest into cf2
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true,
|
||||
write_global_seqno, true, nullptr,
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {1, 2}, -1, true, true, nullptr,
|
||||
handles_[2]));
|
||||
ASSERT_EQ(listener->ingested_files.size(), 3);
|
||||
ASSERT_EQ(listener->ingested_files.back().cf_name, "toto");
|
||||
@@ -2102,7 +2084,7 @@ TEST_F(ExternalSSTFileTest, SnapshotInconsistencyBug) {
|
||||
db_->ReleaseSnapshot(snap);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestBehind) {
|
||||
TEST_F(ExternalSSTFileTest, IngestBehind) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 3;
|
||||
@@ -2126,7 +2108,6 @@ TEST_P(ExternalSSTFileTest, IngestBehind) {
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.allow_global_seqno = true;
|
||||
ifo.ingest_behind = true;
|
||||
ifo.write_global_seqno = GetParam();
|
||||
|
||||
// Can't ingest behind since allow_ingest_behind isn't set to true
|
||||
ASSERT_NOK(GenerateAndAddExternalFileIngestBehind(options, ifo,
|
||||
@@ -2214,9 +2195,6 @@ TEST_F(ExternalSSTFileTest, SkipBloomFilter) {
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
|
||||
testing::Bool());
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -70,7 +70,7 @@ class FaultInjectionTest
|
||||
std::unique_ptr<Env> base_env_;
|
||||
FaultInjectionTestEnv* env_;
|
||||
std::string dbname_;
|
||||
std::shared_ptr<Cache> tiny_cache_;
|
||||
shared_ptr<Cache> tiny_cache_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
|
||||
|
||||
+16
-19
@@ -24,15 +24,14 @@
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/version_set.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/port.h"
|
||||
#include "db/memtable.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
@@ -86,11 +85,11 @@ const char* GetFlushReasonString (FlushReason flush_reason) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const uint64_t* max_memtable_id,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex,
|
||||
std::atomic<bool>* shutting_down,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
@@ -99,13 +98,11 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
Directory* output_file_directory,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest)
|
||||
EventLogger* event_logger, bool measure_io_stats)
|
||||
: dbname_(dbname),
|
||||
cfd_(cfd),
|
||||
db_options_(db_options),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
max_memtable_id_(max_memtable_id),
|
||||
env_options_(env_options),
|
||||
versions_(versions),
|
||||
db_mutex_(db_mutex),
|
||||
@@ -121,8 +118,6 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
stats_(stats),
|
||||
event_logger_(event_logger),
|
||||
measure_io_stats_(measure_io_stats),
|
||||
sync_output_directory_(sync_output_directory),
|
||||
write_manifest_(write_manifest),
|
||||
edit_(nullptr),
|
||||
base_(nullptr),
|
||||
pick_memtable_called(false) {
|
||||
@@ -167,7 +162,7 @@ void FlushJob::PickMemTable() {
|
||||
assert(!pick_memtable_called);
|
||||
pick_memtable_called = true;
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
cfd_->imm()->PickMemtablesToFlush(max_memtable_id_, &mems_);
|
||||
cfd_->imm()->PickMemtablesToFlush(&mems_);
|
||||
if (mems_.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -231,10 +226,10 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
|
||||
if (!s.ok()) {
|
||||
cfd_->imm()->RollbackMemtableFlush(mems_, meta_.fd.GetNumber());
|
||||
} else if (write_manifest_) {
|
||||
} else {
|
||||
TEST_SYNC_POINT("FlushJob::InstallResults");
|
||||
// Replace immutable memtable with the generated Table
|
||||
s = cfd_->imm()->TryInstallMemtableFlushResults(
|
||||
s = cfd_->imm()->InstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems_, prep_tracker, versions_, db_mutex_,
|
||||
meta_.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_);
|
||||
@@ -296,8 +291,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// memtable and its associated range deletion memtable, respectively, at
|
||||
// corresponding indexes.
|
||||
std::vector<InternalIterator*> memtables;
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters;
|
||||
std::vector<InternalIterator*> range_del_iters;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
Arena arena;
|
||||
@@ -309,10 +303,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
"[%s] [JOB %d] Flushing memtable with next log file: %" PRIu64 "\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, m->GetNextLogNumber());
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
auto* range_del_iter =
|
||||
m->NewRangeTombstoneIterator(ro, kMaxSequenceNumber);
|
||||
auto* range_del_iter = m->NewRangeTombstoneIterator(ro);
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
range_del_iters.push_back(range_del_iter);
|
||||
}
|
||||
total_num_entries += m->num_entries();
|
||||
total_num_deletes += m->num_deletes();
|
||||
@@ -331,6 +324,10 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ScopedArenaIterator iter(
|
||||
NewMergingIterator(&cfd_->internal_comparator(), &memtables[0],
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
std::unique_ptr<InternalIterator> range_del_iter(NewMergingIterator(
|
||||
&cfd_->internal_comparator(),
|
||||
range_del_iters.empty() ? nullptr : &range_del_iters[0],
|
||||
static_cast<int>(range_del_iters.size())));
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), job_context_->job_id,
|
||||
@@ -356,7 +353,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
s = BuildTable(
|
||||
dbname_, db_options_.env, *cfd_->ioptions(), mutable_cf_options_,
|
||||
env_options_, cfd_->table_cache(), iter.get(),
|
||||
std::move(range_del_iters), &meta_, cfd_->internal_comparator(),
|
||||
std::move(range_del_iter), &meta_, cfd_->internal_comparator(),
|
||||
cfd_->int_tbl_prop_collector_factories(), cfd_->GetID(),
|
||||
cfd_->GetName(), existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, snapshot_checker_,
|
||||
@@ -376,7 +373,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
s.ToString().c_str(),
|
||||
meta_.marked_for_compaction ? " (needs compaction)" : "");
|
||||
|
||||
if (s.ok() && output_file_directory_ != nullptr && sync_output_directory_) {
|
||||
if (s.ok() && output_file_directory_ != nullptr) {
|
||||
s = output_file_directory_->Fsync();
|
||||
}
|
||||
TEST_SYNC_POINT("FlushJob::WriteLevel0Table");
|
||||
|
||||
+3
-30
@@ -59,16 +59,14 @@ class FlushJob {
|
||||
FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const uint64_t* max_memtable_id, const EnvOptions& env_options,
|
||||
VersionSet* versions, InstrumentedMutex* db_mutex,
|
||||
std::atomic<bool>* shutting_down,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
Directory* output_file_directory, CompressionType output_compression,
|
||||
Statistics* stats, EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest);
|
||||
Statistics* stats, EventLogger* event_logger, bool measure_io_stats);
|
||||
|
||||
~FlushJob();
|
||||
|
||||
@@ -79,24 +77,16 @@ class FlushJob {
|
||||
FileMetaData* file_meta = nullptr);
|
||||
void Cancel();
|
||||
TableProperties GetTableProperties() const { return table_properties_; }
|
||||
const autovector<MemTable*>& GetMemTables() const { return mems_; }
|
||||
|
||||
private:
|
||||
void ReportStartedFlush();
|
||||
void ReportFlushInputSize(const autovector<MemTable*>& mems);
|
||||
void RecordFlushIOStats();
|
||||
Status WriteLevel0Table();
|
||||
|
||||
const std::string& dbname_;
|
||||
ColumnFamilyData* cfd_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
// Pointer to a variable storing the largest memtable id to flush in this
|
||||
// flush job. RocksDB uses this variable to select the memtables to flush in
|
||||
// this job. All memtables in this column family with an ID smaller than or
|
||||
// equal to *max_memtable_id_ will be selected for flush. If null, then all
|
||||
// memtables in the column family will be selected.
|
||||
const uint64_t* max_memtable_id_;
|
||||
const EnvOptions env_options_;
|
||||
VersionSet* versions_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
@@ -113,23 +103,6 @@ class FlushJob {
|
||||
EventLogger* event_logger_;
|
||||
TableProperties table_properties_;
|
||||
bool measure_io_stats_;
|
||||
// True if this flush job should call fsync on the output directory. False
|
||||
// otherwise.
|
||||
// Usually sync_output_directory_ is true. A flush job needs to call sync on
|
||||
// the output directory before committing to the MANIFEST.
|
||||
// However, an individual flush job does not have to call sync on the output
|
||||
// directory if it is part of an atomic flush. After all flush jobs in the
|
||||
// atomic flush succeed, call sync once on each distinct output directory.
|
||||
const bool sync_output_directory_;
|
||||
// True if this flush job should write to MANIFEST after successfully
|
||||
// flushing memtables. False otherwise.
|
||||
// Usually write_manifest_ is true. A flush job commits to the MANIFEST after
|
||||
// flushing the memtables.
|
||||
// However, an individual flush job cannot rashly write to the MANIFEST
|
||||
// immediately after it finishes the flush if it is part of an atomic flush.
|
||||
// In this case, only after all flush jobs succeed in flush can RocksDB
|
||||
// commit to the MANIFEST.
|
||||
const bool write_manifest_;
|
||||
|
||||
// Variables below are set by PickMemTable():
|
||||
FileMetaData meta_;
|
||||
|
||||
+18
-219
@@ -30,7 +30,6 @@ class FlushJobTest : public testing::Test {
|
||||
dbname_(test::PerThreadDBPath("flush_job_test")),
|
||||
options_(),
|
||||
db_options_(options_),
|
||||
column_family_names_({kDefaultColumnFamilyName, "foo", "bar"}),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_manager_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
@@ -46,9 +45,7 @@ class FlushJobTest : public testing::Test {
|
||||
NewDB();
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
for (const auto& cf_name : column_family_names_) {
|
||||
column_families.emplace_back(cf_name, cf_options_);
|
||||
}
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options_);
|
||||
|
||||
EXPECT_OK(versions_->Recover(column_families, false));
|
||||
}
|
||||
@@ -59,38 +56,18 @@ class FlushJobTest : public testing::Test {
|
||||
new_db.SetNextFile(2);
|
||||
new_db.SetLastSequence(0);
|
||||
|
||||
autovector<VersionEdit> new_cfs;
|
||||
SequenceNumber last_seq = 1;
|
||||
uint32_t cf_id = 1;
|
||||
for (size_t i = 1; i != column_family_names_.size(); ++i) {
|
||||
VersionEdit new_cf;
|
||||
new_cf.AddColumnFamily(column_family_names_[i]);
|
||||
new_cf.SetColumnFamily(cf_id++);
|
||||
new_cf.SetLogNumber(0);
|
||||
new_cf.SetNextFile(2);
|
||||
new_cf.SetLastSequence(last_seq++);
|
||||
new_cfs.emplace_back(new_cf);
|
||||
}
|
||||
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
std::unique_ptr<WritableFile> file;
|
||||
unique_ptr<WritableFile> file;
|
||||
Status s = env_->NewWritableFile(
|
||||
manifest, &file, env_->OptimizeForManifestWrite(env_options_));
|
||||
ASSERT_OK(s);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), manifest, EnvOptions()));
|
||||
{
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
new_db.EncodeTo(&record);
|
||||
s = log.AddRecord(record);
|
||||
|
||||
for (const auto& e : new_cfs) {
|
||||
record.clear();
|
||||
e.EncodeTo(&record);
|
||||
s = log.AddRecord(record);
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
// Make "CURRENT" file that points to the new manifest file.
|
||||
@@ -102,7 +79,6 @@ class FlushJobTest : public testing::Test {
|
||||
EnvOptions env_options_;
|
||||
Options options_;
|
||||
ImmutableDBOptions db_options_;
|
||||
const std::vector<std::string> column_family_names_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
WriteController write_controller_;
|
||||
WriteBufferManager write_buffer_manager_;
|
||||
@@ -120,11 +96,9 @@ TEST_F(FlushJobTest, Empty) {
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), nullptr /* memtable_id */,
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_, {},
|
||||
kMaxSequenceNumber, snapshot_checker, &job_context, nullptr, nullptr,
|
||||
nullptr, kNoCompression, nullptr, &event_logger, false,
|
||||
true /* sync_output_directory */, true /* write_manifest */);
|
||||
*cfd->GetLatestMutableCFOptions(), env_options_, versions_.get(), &mutex_,
|
||||
&shutting_down_, {}, kMaxSequenceNumber, snapshot_checker, &job_context,
|
||||
nullptr, nullptr, nullptr, kNoCompression, nullptr, &event_logger, false);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
flush_job.PickMemTable();
|
||||
@@ -165,13 +139,12 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), nullptr /* memtable_id */,
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_, {},
|
||||
kMaxSequenceNumber, snapshot_checker, &job_context, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */);
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
{}, kMaxSequenceNumber, snapshot_checker, &job_context,
|
||||
nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true);
|
||||
|
||||
HistogramData hist;
|
||||
FileMetaData file_meta;
|
||||
@@ -192,179 +165,6 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
TEST_F(FlushJobTest, FlushMemTablesSingleColumnFamily) {
|
||||
const size_t num_mems = 2;
|
||||
const size_t num_mems_to_flush = 1;
|
||||
const size_t num_keys_per_table = 100;
|
||||
JobContext job_context(0);
|
||||
ColumnFamilyData* cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
std::vector<uint64_t> memtable_ids;
|
||||
std::vector<MemTable*> new_mems;
|
||||
for (size_t i = 0; i != num_mems; ++i) {
|
||||
MemTable* mem = cfd->ConstructNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
kMaxSequenceNumber);
|
||||
mem->SetID(i);
|
||||
mem->Ref();
|
||||
new_mems.emplace_back(mem);
|
||||
memtable_ids.push_back(mem->GetID());
|
||||
|
||||
for (size_t j = 0; j < num_keys_per_table; ++j) {
|
||||
std::string key(ToString(j + i * num_keys_per_table));
|
||||
std::string value("value" + key);
|
||||
mem->Add(SequenceNumber(j + i * num_keys_per_table), kTypeValue, key,
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
autovector<MemTable*> to_delete;
|
||||
for (auto mem : new_mems) {
|
||||
cfd->imm()->Add(mem, &to_delete);
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
|
||||
assert(memtable_ids.size() == num_mems);
|
||||
uint64_t smallest_memtable_id = memtable_ids.front();
|
||||
uint64_t flush_memtable_id = smallest_memtable_id + num_mems_to_flush - 1;
|
||||
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), &flush_memtable_id, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
true /* sync_output_directory */, true /* write_manifest */);
|
||||
HistogramData hist;
|
||||
FileMetaData file_meta;
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run(nullptr /* prep_tracker */, &file_meta));
|
||||
mutex_.Unlock();
|
||||
db_options_.statistics->histogramData(FLUSH_TIME, &hist);
|
||||
ASSERT_GT(hist.average, 0.0);
|
||||
|
||||
ASSERT_EQ(ToString(0), file_meta.smallest.user_key().ToString());
|
||||
ASSERT_EQ("99", file_meta.largest.user_key().ToString());
|
||||
ASSERT_EQ(0, file_meta.fd.smallest_seqno);
|
||||
ASSERT_EQ(SequenceNumber(num_mems_to_flush * num_keys_per_table - 1),
|
||||
file_meta.fd.largest_seqno);
|
||||
|
||||
for (auto m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
to_delete.clear();
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
autovector<ColumnFamilyData*> all_cfds;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
all_cfds.push_back(cfd);
|
||||
}
|
||||
const std::vector<size_t> num_memtables = {2, 1, 3};
|
||||
assert(num_memtables.size() == column_family_names_.size());
|
||||
const size_t num_keys_per_memtable = 1000;
|
||||
JobContext job_context(0);
|
||||
std::vector<uint64_t> memtable_ids;
|
||||
std::vector<SequenceNumber> smallest_seqs;
|
||||
std::vector<SequenceNumber> largest_seqs;
|
||||
autovector<MemTable*> to_delete;
|
||||
SequenceNumber curr_seqno = 0;
|
||||
size_t k = 0;
|
||||
for (auto cfd : all_cfds) {
|
||||
smallest_seqs.push_back(curr_seqno);
|
||||
for (size_t i = 0; i != num_memtables[k]; ++i) {
|
||||
MemTable* mem = cfd->ConstructNewMemtable(
|
||||
*cfd->GetLatestMutableCFOptions(), kMaxSequenceNumber);
|
||||
mem->SetID(i);
|
||||
mem->Ref();
|
||||
|
||||
for (size_t j = 0; j != num_keys_per_memtable; ++j) {
|
||||
std::string key(ToString(j + i * num_keys_per_memtable));
|
||||
std::string value("value" + key);
|
||||
mem->Add(curr_seqno++, kTypeValue, key, value);
|
||||
}
|
||||
|
||||
cfd->imm()->Add(mem, &to_delete);
|
||||
}
|
||||
largest_seqs.push_back(curr_seqno - 1);
|
||||
memtable_ids.push_back(num_memtables[k++] - 1);
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relevant
|
||||
std::vector<FlushJob> flush_jobs;
|
||||
k = 0;
|
||||
for (auto cfd : all_cfds) {
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
flush_jobs.emplace_back(
|
||||
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
&memtable_ids[k], env_options_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshot_seqs, kMaxSequenceNumber, snapshot_checker,
|
||||
&job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true,
|
||||
false /* sync_output_directory */, false /* write_manifest */);
|
||||
k++;
|
||||
}
|
||||
HistogramData hist;
|
||||
std::vector<FileMetaData> file_metas;
|
||||
// Call reserve to avoid auto-resizing
|
||||
file_metas.reserve(flush_jobs.size());
|
||||
mutex_.Lock();
|
||||
for (auto& job : flush_jobs) {
|
||||
job.PickMemTable();
|
||||
}
|
||||
for (auto& job : flush_jobs) {
|
||||
FileMetaData meta;
|
||||
// Run will release and re-acquire mutex
|
||||
ASSERT_OK(job.Run(nullptr /**/, &meta));
|
||||
file_metas.emplace_back(meta);
|
||||
}
|
||||
autovector<FileMetaData*> file_meta_ptrs;
|
||||
for (auto& meta : file_metas) {
|
||||
file_meta_ptrs.push_back(&meta);
|
||||
}
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
for (size_t i = 0; i != all_cfds.size(); ++i) {
|
||||
const auto& mems = flush_jobs[i].GetMemTables();
|
||||
mems_list.push_back(&mems);
|
||||
}
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
for (auto cfd : all_cfds) {
|
||||
mutable_cf_options_list.push_back(cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
|
||||
Status s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, all_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), &mutex_, file_meta_ptrs, &job_context.memtables_to_free,
|
||||
nullptr /* db_directory */, nullptr /* log_buffer */);
|
||||
ASSERT_OK(s);
|
||||
|
||||
mutex_.Unlock();
|
||||
db_options_.statistics->histogramData(FLUSH_TIME, &hist);
|
||||
ASSERT_GT(hist.average, 0.0);
|
||||
k = 0;
|
||||
for (const auto& file_meta : file_metas) {
|
||||
ASSERT_EQ(ToString(0), file_meta.smallest.user_key().ToString());
|
||||
ASSERT_EQ("999", file_meta.largest.user_key()
|
||||
.ToString()); // max key by bytewise comparator
|
||||
ASSERT_EQ(smallest_seqs[k], file_meta.fd.smallest_seqno);
|
||||
ASSERT_EQ(largest_seqs[k], file_meta.fd.largest_seqno);
|
||||
// Verify that imm is empty
|
||||
ASSERT_EQ(std::numeric_limits<uint64_t>::max(),
|
||||
all_cfds[k]->imm()->GetEarliestMemTableID());
|
||||
ASSERT_EQ(0, all_cfds[k]->imm()->GetLatestMemTableID());
|
||||
++k;
|
||||
}
|
||||
|
||||
for (auto m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
to_delete.clear();
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
TEST_F(FlushJobTest, Snapshots) {
|
||||
JobContext job_context(0);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
@@ -413,13 +213,12 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), nullptr /* memtable_id */,
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_, snapshots,
|
||||
kMaxSequenceNumber, snapshot_checker, &job_context, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */);
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
snapshots, kMaxSequenceNumber, snapshot_checker,
|
||||
&job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true);
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
|
||||
+10
-14
@@ -15,8 +15,6 @@
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
@@ -73,8 +71,8 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
delete file_iter_;
|
||||
}
|
||||
|
||||
ReadRangeDelAggregator range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
RangeDelAggregator range_del_agg(
|
||||
cfd_->internal_comparator(), {} /* snapshots */);
|
||||
file_iter_ = cfd_->table_cache()->NewIterator(
|
||||
read_options_, *(cfd_->soptions()), cfd_->internal_comparator(),
|
||||
*files_[file_index_],
|
||||
@@ -610,14 +608,13 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
// New
|
||||
sv_ = cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
}
|
||||
ReadRangeDelAggregator range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
RangeDelAggregator range_del_agg(
|
||||
cfd_->internal_comparator(), {} /* snapshots */);
|
||||
mutable_iter_ = sv_->mem->NewIterator(read_options_, &arena_);
|
||||
sv_->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
sv_->mem->NewRangeTombstoneIterator(
|
||||
read_options_, sv_->current->version_set()->LastSequence()));
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
sv_->mem->NewRangeTombstoneIterator(read_options_));
|
||||
range_del_agg.AddTombstones(std::move(range_del_iter));
|
||||
sv_->imm->AddRangeTombstoneIterators(read_options_, &arena_,
|
||||
&range_del_agg);
|
||||
@@ -669,12 +666,11 @@ void ForwardIterator::RenewIterators() {
|
||||
|
||||
mutable_iter_ = svnew->mem->NewIterator(read_options_, &arena_);
|
||||
svnew->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
ReadRangeDelAggregator range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
RangeDelAggregator range_del_agg(
|
||||
cfd_->internal_comparator(), {} /* snapshots */);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
svnew->mem->NewRangeTombstoneIterator(
|
||||
read_options_, sv_->current->version_set()->LastSequence()));
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
svnew->mem->NewRangeTombstoneIterator(read_options_));
|
||||
range_del_agg.AddTombstones(std::move(range_del_iter));
|
||||
svnew->imm->AddRangeTombstoneIterators(read_options_, &arena_,
|
||||
&range_del_agg);
|
||||
|
||||
+8
-25
@@ -231,8 +231,6 @@ static const std::string current_version_number =
|
||||
"current-super-version-number";
|
||||
static const std::string estimate_live_data_size = "estimate-live-data-size";
|
||||
static const std::string min_log_number_to_keep_str = "min-log-number-to-keep";
|
||||
static const std::string min_obsolete_sst_number_to_keep_str =
|
||||
"min-obsolete-sst-number-to-keep";
|
||||
static const std::string base_level_str = "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";
|
||||
@@ -312,8 +310,6 @@ const std::string DB::Properties::kEstimateLiveDataSize =
|
||||
rocksdb_prefix + estimate_live_data_size;
|
||||
const std::string DB::Properties::kMinLogNumberToKeep =
|
||||
rocksdb_prefix + min_log_number_to_keep_str;
|
||||
const std::string DB::Properties::kMinObsoleteSstNumberToKeep =
|
||||
rocksdb_prefix + min_obsolete_sst_number_to_keep_str;
|
||||
const std::string DB::Properties::kTotalSstFilesSize =
|
||||
rocksdb_prefix + total_sst_files_size;
|
||||
const std::string DB::Properties::kLiveSstFilesSize =
|
||||
@@ -434,9 +430,6 @@ const std::unordered_map<std::string, DBPropertyInfo>
|
||||
{DB::Properties::kMinLogNumberToKeep,
|
||||
{false, nullptr, &InternalStats::HandleMinLogNumberToKeep, nullptr,
|
||||
nullptr}},
|
||||
{DB::Properties::kMinObsoleteSstNumberToKeep,
|
||||
{false, nullptr, &InternalStats::HandleMinObsoleteSstNumberToKeep,
|
||||
nullptr, nullptr}},
|
||||
{DB::Properties::kBaseLevel,
|
||||
{false, nullptr, &InternalStats::HandleBaseLevel, nullptr, nullptr}},
|
||||
{DB::Properties::kTotalSstFilesSize,
|
||||
@@ -833,13 +826,6 @@ bool InternalStats::HandleMinLogNumberToKeep(uint64_t* value, DBImpl* db,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleMinObsoleteSstNumberToKeep(uint64_t* value,
|
||||
DBImpl* db,
|
||||
Version* /*version*/) {
|
||||
*value = db->MinObsoleteSstNumberToKeep();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleActualDelayedWriteRate(uint64_t* value, DBImpl* db,
|
||||
Version* /*version*/) {
|
||||
const WriteController& wc = db->write_controller();
|
||||
@@ -949,17 +935,14 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
seconds_up, interval_seconds_up);
|
||||
value->append(buf);
|
||||
// Cumulative
|
||||
uint64_t user_bytes_written =
|
||||
GetDBStats(InternalStats::kIntStatsBytesWritten);
|
||||
uint64_t num_keys_written =
|
||||
GetDBStats(InternalStats::kIntStatsNumKeysWritten);
|
||||
uint64_t write_other = GetDBStats(InternalStats::kIntStatsWriteDoneByOther);
|
||||
uint64_t write_self = GetDBStats(InternalStats::kIntStatsWriteDoneBySelf);
|
||||
uint64_t wal_bytes = GetDBStats(InternalStats::kIntStatsWalFileBytes);
|
||||
uint64_t wal_synced = GetDBStats(InternalStats::kIntStatsWalFileSynced);
|
||||
uint64_t write_with_wal = GetDBStats(InternalStats::kIntStatsWriteWithWal);
|
||||
uint64_t write_stall_micros =
|
||||
GetDBStats(InternalStats::kIntStatsWriteStallMicros);
|
||||
uint64_t user_bytes_written = GetDBStats(InternalStats::BYTES_WRITTEN);
|
||||
uint64_t num_keys_written = GetDBStats(InternalStats::NUMBER_KEYS_WRITTEN);
|
||||
uint64_t write_other = GetDBStats(InternalStats::WRITE_DONE_BY_OTHER);
|
||||
uint64_t write_self = GetDBStats(InternalStats::WRITE_DONE_BY_SELF);
|
||||
uint64_t wal_bytes = GetDBStats(InternalStats::WAL_FILE_BYTES);
|
||||
uint64_t wal_synced = GetDBStats(InternalStats::WAL_FILE_SYNCED);
|
||||
uint64_t write_with_wal = GetDBStats(InternalStats::WRITE_WITH_WAL);
|
||||
uint64_t write_stall_micros = GetDBStats(InternalStats::WRITE_STALL_MICROS);
|
||||
|
||||
const int kHumanMicrosLen = 32;
|
||||
char human_micros[kHumanMicrosLen];
|
||||
|
||||
+20
-22
@@ -108,15 +108,15 @@ class InternalStats {
|
||||
};
|
||||
|
||||
enum InternalDBStatsType {
|
||||
kIntStatsWalFileBytes,
|
||||
kIntStatsWalFileSynced,
|
||||
kIntStatsBytesWritten,
|
||||
kIntStatsNumKeysWritten,
|
||||
kIntStatsWriteDoneByOther,
|
||||
kIntStatsWriteDoneBySelf,
|
||||
kIntStatsWriteWithWal,
|
||||
kIntStatsWriteStallMicros,
|
||||
kIntStatsNumMax,
|
||||
WAL_FILE_BYTES,
|
||||
WAL_FILE_SYNCED,
|
||||
BYTES_WRITTEN,
|
||||
NUMBER_KEYS_WRITTEN,
|
||||
WRITE_DONE_BY_OTHER,
|
||||
WRITE_DONE_BY_SELF,
|
||||
WRITE_WITH_WAL,
|
||||
WRITE_STALL_MICROS,
|
||||
INTERNAL_DB_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
InternalStats(int num_levels, Env* env, ColumnFamilyData* cfd)
|
||||
@@ -292,7 +292,7 @@ class InternalStats {
|
||||
};
|
||||
|
||||
void Clear() {
|
||||
for (int i = 0; i < kIntStatsNumMax; i++) {
|
||||
for (int i = 0; i < INTERNAL_DB_STATS_ENUM_MAX; i++) {
|
||||
db_stats_[i].store(0);
|
||||
}
|
||||
for (int i = 0; i < INTERNAL_CF_STATS_ENUM_MAX; i++) {
|
||||
@@ -382,7 +382,7 @@ class InternalStats {
|
||||
bool HandleBlockCacheStat(Cache** block_cache);
|
||||
|
||||
// Per-DB stats
|
||||
std::atomic<uint64_t> db_stats_[kIntStatsNumMax];
|
||||
std::atomic<uint64_t> db_stats_[INTERNAL_DB_STATS_ENUM_MAX];
|
||||
// Per-ColumnFamily stats
|
||||
uint64_t cf_stats_value_[INTERNAL_CF_STATS_ENUM_MAX];
|
||||
uint64_t cf_stats_count_[INTERNAL_CF_STATS_ENUM_MAX];
|
||||
@@ -533,8 +533,6 @@ class InternalStats {
|
||||
bool HandleEstimateLiveDataSize(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
bool HandleMinLogNumberToKeep(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleMinObsoleteSstNumberToKeep(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
bool HandleActualDelayedWriteRate(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
bool HandleIsWriteStopped(uint64_t* value, DBImpl* db, Version* version);
|
||||
@@ -580,15 +578,15 @@ class InternalStats {
|
||||
};
|
||||
|
||||
enum InternalDBStatsType {
|
||||
kIntStatsWalFileBytes,
|
||||
kIntStatsWalFileSynced,
|
||||
kIntStatsBytesWritten,
|
||||
kIntStatsNumKeysWritten,
|
||||
kIntStatsWriteDoneByOther,
|
||||
kIntStatsWriteDoneBySelf,
|
||||
kIntStatsWriteWithWal,
|
||||
kIntStatsWriteStallMicros,
|
||||
kIntStatsNumMax,
|
||||
WAL_FILE_BYTES,
|
||||
WAL_FILE_SYNCED,
|
||||
BYTES_WRITTEN,
|
||||
NUMBER_KEYS_WRITTEN,
|
||||
WRITE_DONE_BY_OTHER,
|
||||
WRITE_DONE_BY_SELF,
|
||||
WRITE_WITH_WAL,
|
||||
WRITE_STALL_MICROS,
|
||||
INTERNAL_DB_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
InternalStats(int /*num_levels*/, Env* /*env*/, ColumnFamilyData* /*cfd*/) {}
|
||||
|
||||
+2
-3
@@ -30,8 +30,7 @@ struct SuperVersionContext {
|
||||
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
||||
autovector<WriteStallNotification> write_stall_notifications;
|
||||
#endif
|
||||
std::unique_ptr<SuperVersion>
|
||||
new_superversion; // if nullptr no new superversion
|
||||
unique_ptr<SuperVersion> new_superversion; // if nullptr no new superversion
|
||||
|
||||
explicit SuperVersionContext(bool create_superversion = false)
|
||||
: new_superversion(create_superversion ? new SuperVersion() : nullptr) {}
|
||||
@@ -45,7 +44,7 @@ struct SuperVersionContext {
|
||||
}
|
||||
|
||||
void NewSuperVersion() {
|
||||
new_superversion = std::unique_ptr<SuperVersion>(new SuperVersion());
|
||||
new_superversion = unique_ptr<SuperVersion>(new SuperVersion());
|
||||
}
|
||||
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
|
||||
@@ -891,68 +891,6 @@ TEST_F(EventListenerTest, BackgroundErrorListenerFailedCompactionTest) {
|
||||
ASSERT_LE(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
class TestFileOperationListener : public EventListener {
|
||||
public:
|
||||
TestFileOperationListener() {
|
||||
file_reads_.store(0);
|
||||
file_reads_success_.store(0);
|
||||
file_writes_.store(0);
|
||||
file_writes_success_.store(0);
|
||||
}
|
||||
|
||||
void OnFileReadFinish(const FileOperationInfo& info) override {
|
||||
++file_reads_;
|
||||
if (info.status.ok()) {
|
||||
++file_reads_success_;
|
||||
}
|
||||
ReportDuration(info);
|
||||
}
|
||||
|
||||
void OnFileWriteFinish(const FileOperationInfo& info) override {
|
||||
++file_writes_;
|
||||
if (info.status.ok()) {
|
||||
++file_writes_success_;
|
||||
}
|
||||
ReportDuration(info);
|
||||
}
|
||||
|
||||
bool ShouldBeNotifiedOnFileIO() override { return true; }
|
||||
|
||||
std::atomic<size_t> file_reads_;
|
||||
std::atomic<size_t> file_reads_success_;
|
||||
std::atomic<size_t> file_writes_;
|
||||
std::atomic<size_t> file_writes_success_;
|
||||
|
||||
private:
|
||||
void ReportDuration(const FileOperationInfo& info) const {
|
||||
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
info.finish_timestamp - info.start_timestamp);
|
||||
ASSERT_GT(duration.count(), 0);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(EventListenerTest, OnFileOperationTest) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
|
||||
TestFileOperationListener* listener = new TestFileOperationListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "aaa"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_GE(listener->file_writes_.load(),
|
||||
listener->file_writes_success_.load());
|
||||
ASSERT_GT(listener->file_writes_.load(), 0);
|
||||
Close();
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_GE(listener->file_reads_.load(), listener->file_reads_success_.load());
|
||||
ASSERT_GT(listener->file_reads_.load(), 0);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
+18
-36
@@ -23,9 +23,8 @@ Reader::Reporter::~Reporter() {
|
||||
}
|
||||
|
||||
Reader::Reader(std::shared_ptr<Logger> info_log,
|
||||
std::unique_ptr<SequentialFileReader>&& _file,
|
||||
Reporter* reporter, bool checksum, uint64_t log_num,
|
||||
bool retry_after_eof)
|
||||
unique_ptr<SequentialFileReader>&& _file, Reporter* reporter,
|
||||
bool checksum, uint64_t log_num)
|
||||
: info_log_(info_log),
|
||||
file_(std::move(_file)),
|
||||
reporter_(reporter),
|
||||
@@ -38,8 +37,7 @@ Reader::Reader(std::shared_ptr<Logger> info_log,
|
||||
last_record_offset_(0),
|
||||
end_of_buffer_offset_(0),
|
||||
log_number_(log_num),
|
||||
recycled_(false),
|
||||
retry_after_eof_(retry_after_eof) {}
|
||||
recycled_(false) {}
|
||||
|
||||
Reader::~Reader() {
|
||||
delete[] backing_store_;
|
||||
@@ -124,7 +122,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
// in clean shutdown we don't expect any error in the log files
|
||||
ReportCorruption(drop_size, "truncated header");
|
||||
}
|
||||
FALLTHROUGH_INTENDED;
|
||||
FALLTHROUGH_INTENDED;
|
||||
|
||||
case kEof:
|
||||
if (in_fragmented_record) {
|
||||
@@ -154,7 +152,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
FALLTHROUGH_INTENDED;
|
||||
FALLTHROUGH_INTENDED;
|
||||
|
||||
case kBadRecord:
|
||||
if (in_fragmented_record) {
|
||||
@@ -210,8 +208,7 @@ void Reader::UnmarkEOF() {
|
||||
|
||||
eof_ = false;
|
||||
|
||||
// If retry_after_eof_ is true, we have to proceed to read anyway.
|
||||
if (!retry_after_eof_ && eof_offset_ == 0) {
|
||||
if (eof_offset_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -292,12 +289,8 @@ bool Reader::ReadMore(size_t* drop_size, int *error) {
|
||||
} else if (buffer_.size() < static_cast<size_t>(kBlockSize)) {
|
||||
eof_ = true;
|
||||
eof_offset_ = buffer_.size();
|
||||
TEST_SYNC_POINT("LogReader::ReadMore:FirstEOF");
|
||||
}
|
||||
return true;
|
||||
} else if (retry_after_eof_ && !read_error_) {
|
||||
UnmarkEOF();
|
||||
return !read_error_;
|
||||
} else {
|
||||
// Note that if buffer_ is non-empty, we have a truncated header at the
|
||||
// end of the file, which can be caused by the writer crashing in the
|
||||
@@ -319,10 +312,7 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size) {
|
||||
while (true) {
|
||||
// We need at least the minimum header size
|
||||
if (buffer_.size() < static_cast<size_t>(kHeaderSize)) {
|
||||
// the default value of r is meaningless because ReadMore will overwrite
|
||||
// it if it returns false; in case it returns true, the return value will
|
||||
// not be used anyway
|
||||
int r = kEof;
|
||||
int r;
|
||||
if (!ReadMore(drop_size, &r)) {
|
||||
return r;
|
||||
}
|
||||
@@ -343,7 +333,7 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size) {
|
||||
header_size = kRecyclableHeaderSize;
|
||||
// We need enough for the larger header
|
||||
if (buffer_.size() < static_cast<size_t>(kRecyclableHeaderSize)) {
|
||||
int r = kEof;
|
||||
int r;
|
||||
if (!ReadMore(drop_size, &r)) {
|
||||
return r;
|
||||
}
|
||||
@@ -355,24 +345,16 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size) {
|
||||
}
|
||||
}
|
||||
if (header_size + length > buffer_.size()) {
|
||||
if (!retry_after_eof_) {
|
||||
*drop_size = buffer_.size();
|
||||
buffer_.clear();
|
||||
if (!eof_) {
|
||||
return kBadRecordLen;
|
||||
}
|
||||
// If the end of the file has been reached without reading |length|
|
||||
// bytes of payload, assume the writer died in the middle of writing the
|
||||
// record. Don't report a corruption unless requested.
|
||||
if (*drop_size) {
|
||||
return kBadHeader;
|
||||
}
|
||||
} else {
|
||||
int r = kEof;
|
||||
if (!ReadMore(drop_size, &r)) {
|
||||
return r;
|
||||
}
|
||||
continue;
|
||||
*drop_size = buffer_.size();
|
||||
buffer_.clear();
|
||||
if (!eof_) {
|
||||
return kBadRecordLen;
|
||||
}
|
||||
// If the end of the file has been reached without reading |length| bytes
|
||||
// of payload, assume the writer died in the middle of writing the record.
|
||||
// Don't report a corruption unless requested.
|
||||
if (*drop_size) {
|
||||
return kBadHeader;
|
||||
}
|
||||
return kEof;
|
||||
}
|
||||
|
||||
+3
-8
@@ -52,8 +52,8 @@ class Reader {
|
||||
// If "checksum" is true, verify checksums if available.
|
||||
Reader(std::shared_ptr<Logger> info_log,
|
||||
// @lint-ignore TXT2 T25377293 Grandfathered in
|
||||
std::unique_ptr<SequentialFileReader>&& file, Reporter* reporter,
|
||||
bool checksum, uint64_t log_num, bool retry_after_eof);
|
||||
unique_ptr<SequentialFileReader>&& file, Reporter* reporter,
|
||||
bool checksum, uint64_t log_num);
|
||||
|
||||
~Reader();
|
||||
|
||||
@@ -87,7 +87,7 @@ class Reader {
|
||||
|
||||
private:
|
||||
std::shared_ptr<Logger> info_log_;
|
||||
const std::unique_ptr<SequentialFileReader> file_;
|
||||
const unique_ptr<SequentialFileReader> file_;
|
||||
Reporter* const reporter_;
|
||||
bool const checksum_;
|
||||
char* const backing_store_;
|
||||
@@ -110,11 +110,6 @@ class Reader {
|
||||
// Whether this is a recycled log file
|
||||
bool recycled_;
|
||||
|
||||
// Whether retry after encountering EOF
|
||||
// TODO (yanqin) add support for retry policy, e.g. sleep, max retry limit,
|
||||
// etc.
|
||||
const bool retry_after_eof_;
|
||||
|
||||
// Extend record types with the following special values
|
||||
enum {
|
||||
kEof = kMaxRecordType + 1,
|
||||
|
||||
+4
-176
@@ -145,8 +145,8 @@ class LogTest : public ::testing::TestWithParam<int> {
|
||||
}
|
||||
|
||||
Slice reader_contents_;
|
||||
std::unique_ptr<WritableFileWriter> dest_holder_;
|
||||
std::unique_ptr<SequentialFileReader> source_holder_;
|
||||
unique_ptr<WritableFileWriter> dest_holder_;
|
||||
unique_ptr<SequentialFileReader> source_holder_;
|
||||
ReportCollector report_;
|
||||
Writer writer_;
|
||||
Reader reader_;
|
||||
@@ -164,8 +164,7 @@ class LogTest : public ::testing::TestWithParam<int> {
|
||||
new StringSource(reader_contents_), "" /* file name */)),
|
||||
writer_(std::move(dest_holder_), 123, GetParam()),
|
||||
reader_(nullptr, std::move(source_holder_), &report_,
|
||||
true /* checksum */, 123 /* log_number */,
|
||||
false /* retry_after_eof */) {
|
||||
true /* checksum */, 123 /* log_number */) {
|
||||
int header_size = GetParam() ? kRecyclableHeaderSize : kHeaderSize;
|
||||
initial_offset_last_record_offsets_[0] = 0;
|
||||
initial_offset_last_record_offsets_[1] = header_size + 10000;
|
||||
@@ -639,7 +638,7 @@ TEST_P(LogTest, Recycle) {
|
||||
while (get_reader_contents()->size() < log::kBlockSize * 2) {
|
||||
Write("xxxxxxxxxxxxxxxx");
|
||||
}
|
||||
std::unique_ptr<WritableFileWriter> dest_holder(test::GetWritableFileWriter(
|
||||
unique_ptr<WritableFileWriter> dest_holder(test::GetWritableFileWriter(
|
||||
new test::OverwritingStringSink(get_reader_contents()),
|
||||
"" /* don't care */));
|
||||
Writer recycle_writer(std::move(dest_holder), 123, true);
|
||||
@@ -653,177 +652,6 @@ TEST_P(LogTest, Recycle) {
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(bool, LogTest, ::testing::Values(0, 2));
|
||||
|
||||
class RetriableLogTest : public ::testing::TestWithParam<int> {
|
||||
private:
|
||||
class ReportCollector : public Reader::Reporter {
|
||||
public:
|
||||
size_t dropped_bytes_;
|
||||
std::string message_;
|
||||
|
||||
ReportCollector() : dropped_bytes_(0) {}
|
||||
virtual void Corruption(size_t bytes, const Status& status) override {
|
||||
dropped_bytes_ += bytes;
|
||||
message_.append(status.ToString());
|
||||
}
|
||||
};
|
||||
|
||||
Slice contents_;
|
||||
std::unique_ptr<WritableFileWriter> dest_holder_;
|
||||
std::unique_ptr<Writer> log_writer_;
|
||||
Env* env_;
|
||||
EnvOptions env_options_;
|
||||
const std::string test_dir_;
|
||||
const std::string log_file_;
|
||||
std::unique_ptr<WritableFileWriter> writer_;
|
||||
std::unique_ptr<SequentialFileReader> reader_;
|
||||
ReportCollector report_;
|
||||
std::unique_ptr<Reader> log_reader_;
|
||||
|
||||
public:
|
||||
RetriableLogTest()
|
||||
: contents_(),
|
||||
dest_holder_(nullptr),
|
||||
log_writer_(nullptr),
|
||||
env_(Env::Default()),
|
||||
test_dir_(test::PerThreadDBPath("retriable_log_test")),
|
||||
log_file_(test_dir_ + "/log"),
|
||||
writer_(nullptr),
|
||||
reader_(nullptr),
|
||||
log_reader_(nullptr) {}
|
||||
|
||||
Status SetupTestEnv() {
|
||||
dest_holder_.reset(test::GetWritableFileWriter(
|
||||
new test::StringSink(&contents_), "" /* file name */));
|
||||
assert(dest_holder_ != nullptr);
|
||||
log_writer_.reset(new Writer(std::move(dest_holder_), 123, GetParam()));
|
||||
assert(log_writer_ != nullptr);
|
||||
|
||||
Status s;
|
||||
s = env_->CreateDirIfMissing(test_dir_);
|
||||
std::unique_ptr<WritableFile> writable_file;
|
||||
if (s.ok()) {
|
||||
s = env_->NewWritableFile(log_file_, &writable_file, env_options_);
|
||||
}
|
||||
if (s.ok()) {
|
||||
writer_.reset(new WritableFileWriter(std::move(writable_file), log_file_,
|
||||
env_options_));
|
||||
assert(writer_ != nullptr);
|
||||
}
|
||||
std::unique_ptr<SequentialFile> seq_file;
|
||||
if (s.ok()) {
|
||||
s = env_->NewSequentialFile(log_file_, &seq_file, env_options_);
|
||||
}
|
||||
if (s.ok()) {
|
||||
reader_.reset(new SequentialFileReader(std::move(seq_file), log_file_));
|
||||
assert(reader_ != nullptr);
|
||||
log_reader_.reset(new Reader(nullptr, std::move(reader_), &report_,
|
||||
true /* checksum */, 123 /* log_number */,
|
||||
true /* retry_after_eof */));
|
||||
assert(log_reader_ != nullptr);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string contents() {
|
||||
auto file =
|
||||
dynamic_cast<test::StringSink*>(log_writer_->file()->writable_file());
|
||||
assert(file != nullptr);
|
||||
return file->contents_;
|
||||
}
|
||||
|
||||
void Encode(const std::string& msg) { log_writer_->AddRecord(Slice(msg)); }
|
||||
|
||||
void Write(const Slice& data) {
|
||||
writer_->Append(data);
|
||||
writer_->Sync(true);
|
||||
}
|
||||
|
||||
std::string Read() {
|
||||
auto wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
if (log_reader_->ReadRecord(&record, &scratch, wal_recovery_mode)) {
|
||||
return record.ToString();
|
||||
} else {
|
||||
return "Read error";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(RetriableLogTest, TailLog_PartialHeader) {
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
std::vector<int> remaining_bytes_in_last_record;
|
||||
size_t header_size = GetParam() ? kRecyclableHeaderSize : kHeaderSize;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"RetriableLogTest::TailLog:AfterPart1",
|
||||
"RetriableLogTest::TailLog:BeforeReadRecord"},
|
||||
{"LogReader::ReadMore:FirstEOF",
|
||||
"RetriableLogTest::TailLog:BeforePart2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
size_t delta = header_size - 1;
|
||||
port::Thread log_writer_thread([&]() {
|
||||
size_t old_sz = contents().size();
|
||||
Encode("foo");
|
||||
size_t new_sz = contents().size();
|
||||
std::string part1 = contents().substr(old_sz, delta);
|
||||
std::string part2 =
|
||||
contents().substr(old_sz + delta, new_sz - old_sz - delta);
|
||||
Write(Slice(part1));
|
||||
TEST_SYNC_POINT("RetriableLogTest::TailLog:AfterPart1");
|
||||
TEST_SYNC_POINT("RetriableLogTest::TailLog:BeforePart2");
|
||||
Write(Slice(part2));
|
||||
});
|
||||
|
||||
std::string record;
|
||||
port::Thread log_reader_thread([&]() {
|
||||
TEST_SYNC_POINT("RetriableLogTest::TailLog:BeforeReadRecord");
|
||||
record = Read();
|
||||
});
|
||||
log_reader_thread.join();
|
||||
log_writer_thread.join();
|
||||
ASSERT_EQ("foo", record);
|
||||
}
|
||||
|
||||
TEST_P(RetriableLogTest, TailLog_FullHeader) {
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
std::vector<int> remaining_bytes_in_last_record;
|
||||
size_t header_size = GetParam() ? kRecyclableHeaderSize : kHeaderSize;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"RetriableLogTest::TailLog:AfterPart1",
|
||||
"RetriableLogTest::TailLog:BeforeReadRecord"},
|
||||
{"LogReader::ReadMore:FirstEOF",
|
||||
"RetriableLogTest::TailLog:BeforePart2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
size_t delta = header_size + 1;
|
||||
port::Thread log_writer_thread([&]() {
|
||||
size_t old_sz = contents().size();
|
||||
Encode("foo");
|
||||
size_t new_sz = contents().size();
|
||||
std::string part1 = contents().substr(old_sz, delta);
|
||||
std::string part2 =
|
||||
contents().substr(old_sz + delta, new_sz - old_sz - delta);
|
||||
Write(Slice(part1));
|
||||
TEST_SYNC_POINT("RetriableLogTest::TailLog:AfterPart1");
|
||||
TEST_SYNC_POINT("RetriableLogTest::TailLog:BeforePart2");
|
||||
Write(Slice(part2));
|
||||
});
|
||||
|
||||
std::string record;
|
||||
port::Thread log_reader_thread([&]() {
|
||||
TEST_SYNC_POINT("RetriableLogTest::TailLog:BeforeReadRecord");
|
||||
record = Read();
|
||||
});
|
||||
log_reader_thread.join();
|
||||
log_writer_thread.join();
|
||||
ASSERT_EQ("foo", record);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(bool, RetriableLogTest, ::testing::Values(0, 2));
|
||||
|
||||
} // namespace log
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ class Writer {
|
||||
bool TEST_BufferIsEmpty();
|
||||
|
||||
private:
|
||||
std::unique_ptr<WritableFileWriter> dest_;
|
||||
unique_ptr<WritableFileWriter> dest_;
|
||||
size_t block_offset_; // Current offset in block
|
||||
uint64_t log_number_;
|
||||
bool recycle_log_files_;
|
||||
|
||||
+9
-8
@@ -13,16 +13,17 @@
|
||||
#include <memory>
|
||||
#include <string.h>
|
||||
|
||||
#include "port/jemalloc_helper.h"
|
||||
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <malloc_np.h>
|
||||
#else
|
||||
#include "jemalloc/jemalloc.h"
|
||||
#ifdef JEMALLOC_NO_RENAME
|
||||
#define malloc_stats_print je_malloc_stats_print
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char* cur;
|
||||
@@ -40,10 +41,10 @@ static void GetJemallocStatus(void* mstat_arg, const char* status) {
|
||||
snprintf(mstat->cur, buf_size, "%s", status);
|
||||
mstat->cur += status_len;
|
||||
}
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
void DumpMallocStats(std::string* stats) {
|
||||
if (!HasJemalloc()) {
|
||||
return;
|
||||
}
|
||||
MallocStatus mstat;
|
||||
const unsigned int kMallocStatusLen = 1000000;
|
||||
std::unique_ptr<char[]> buf{new char[kMallocStatusLen + 1]};
|
||||
@@ -55,5 +56,5 @@ void DumpMallocStats(std::string* stats) {
|
||||
#else
|
||||
void DumpMallocStats(std::string*) {}
|
||||
#endif // ROCKSDB_JEMALLOC
|
||||
} // namespace rocksdb
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
+26
-41
@@ -17,7 +17,6 @@
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
@@ -69,13 +68,12 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
refs_(0),
|
||||
kArenaBlockSize(OptimizeBlockSize(moptions_.arena_block_size)),
|
||||
mem_tracker_(write_buffer_manager),
|
||||
arena_(moptions_.arena_block_size,
|
||||
(write_buffer_manager != nullptr &&
|
||||
(write_buffer_manager->enabled() ||
|
||||
write_buffer_manager->cost_to_cache()))
|
||||
? &mem_tracker_
|
||||
: nullptr,
|
||||
mutable_cf_options.memtable_huge_page_size),
|
||||
arena_(
|
||||
moptions_.arena_block_size,
|
||||
(write_buffer_manager != nullptr && write_buffer_manager->enabled())
|
||||
? &mem_tracker_
|
||||
: nullptr,
|
||||
mutable_cf_options.memtable_huge_page_size),
|
||||
table_(ioptions.memtable_factory->CreateMemTableRep(
|
||||
comparator_, &arena_, mutable_cf_options.prefix_extractor.get(),
|
||||
ioptions.info_log, column_family_id)),
|
||||
@@ -103,8 +101,7 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
env_(ioptions.env),
|
||||
insert_with_hint_prefix_extractor_(
|
||||
ioptions.memtable_insert_with_hint_prefix_extractor),
|
||||
oldest_key_time_(std::numeric_limits<uint64_t>::max()),
|
||||
atomic_flush_seqno_(kMaxSequenceNumber) {
|
||||
oldest_key_time_(std::numeric_limits<uint64_t>::max()) {
|
||||
UpdateFlushState();
|
||||
// something went wrong if we need to flush before inserting anything
|
||||
assert(!ShouldScheduleFlush());
|
||||
@@ -412,24 +409,13 @@ InternalIterator* MemTable::NewIterator(const ReadOptions& read_options,
|
||||
return new (mem) MemTableIterator(*this, read_options, arena);
|
||||
}
|
||||
|
||||
FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIterator(
|
||||
const ReadOptions& read_options, SequenceNumber read_seq) {
|
||||
InternalIterator* MemTable::NewRangeTombstoneIterator(
|
||||
const ReadOptions& read_options) {
|
||||
if (read_options.ignore_range_deletions || is_range_del_table_empty_) {
|
||||
return nullptr;
|
||||
}
|
||||
auto* unfragmented_iter = new MemTableIterator(
|
||||
*this, read_options, nullptr /* arena */, true /* use_range_del_table */);
|
||||
if (unfragmented_iter == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto fragmented_tombstone_list =
|
||||
std::make_shared<FragmentedRangeTombstoneList>(
|
||||
std::unique_ptr<InternalIterator>(unfragmented_iter),
|
||||
comparator_.comparator);
|
||||
|
||||
auto* fragmented_iter = new FragmentedRangeTombstoneIterator(
|
||||
fragmented_tombstone_list, comparator_.comparator, read_seq);
|
||||
return fragmented_iter;
|
||||
return new MemTableIterator(*this, read_options, nullptr /* arena */,
|
||||
true /* use_range_del_table */);
|
||||
}
|
||||
|
||||
port::RWMutex* MemTable::GetLock(const Slice& key) {
|
||||
@@ -583,7 +569,7 @@ struct Saver {
|
||||
const MergeOperator* merge_operator;
|
||||
// the merge operations encountered;
|
||||
MergeContext* merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq;
|
||||
RangeDelAggregator* range_del_agg;
|
||||
MemTable* mem;
|
||||
Logger* logger;
|
||||
Statistics* statistics;
|
||||
@@ -605,10 +591,10 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
Saver* s = reinterpret_cast<Saver*>(arg);
|
||||
assert(s != nullptr);
|
||||
MergeContext* merge_context = s->merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = s->max_covering_tombstone_seq;
|
||||
RangeDelAggregator* range_del_agg = s->range_del_agg;
|
||||
const MergeOperator* merge_operator = s->merge_operator;
|
||||
|
||||
assert(merge_context != nullptr);
|
||||
assert(merge_context != nullptr && range_del_agg != nullptr);
|
||||
|
||||
// entry format is:
|
||||
// klength varint32
|
||||
@@ -636,7 +622,7 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
s->seq = seq;
|
||||
|
||||
if ((type == kTypeValue || type == kTypeMerge || type == kTypeBlobIndex) &&
|
||||
max_covering_tombstone_seq > seq) {
|
||||
range_del_agg->ShouldDelete(Slice(key_ptr, key_length))) {
|
||||
type = kTypeRangeDeletion;
|
||||
}
|
||||
switch (type) {
|
||||
@@ -654,7 +640,7 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
*(s->found_final_value) = true;
|
||||
return false;
|
||||
}
|
||||
FALLTHROUGH_INTENDED;
|
||||
FALLTHROUGH_INTENDED;
|
||||
case kTypeValue: {
|
||||
if (s->inplace_update_support) {
|
||||
s->mem->GetLock(s->key->user_key())->ReadLock();
|
||||
@@ -733,9 +719,9 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
|
||||
bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
ReadCallback* callback, bool* is_blob_index) {
|
||||
RangeDelAggregator* range_del_agg, SequenceNumber* seq,
|
||||
const ReadOptions& read_opts, ReadCallback* callback,
|
||||
bool* is_blob_index) {
|
||||
// The sequence number is updated synchronously in version_set.h
|
||||
if (IsEmpty()) {
|
||||
// Avoiding recording stats for speed.
|
||||
@@ -743,13 +729,12 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
}
|
||||
PERF_TIMER_GUARD(get_from_memtable_time);
|
||||
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
NewRangeTombstoneIterator(read_opts,
|
||||
GetInternalKeySeqno(key.internal_key())));
|
||||
if (range_del_iter != nullptr) {
|
||||
*max_covering_tombstone_seq =
|
||||
std::max(*max_covering_tombstone_seq,
|
||||
range_del_iter->MaxCoveringTombstoneSeqnum(key.user_key()));
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
NewRangeTombstoneIterator(read_opts));
|
||||
Status status = range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
if (!status.ok()) {
|
||||
*s = status;
|
||||
return false;
|
||||
}
|
||||
|
||||
Slice user_key = key.user_key();
|
||||
@@ -776,7 +761,7 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
saver.seq = kMaxSequenceNumber;
|
||||
saver.mem = this;
|
||||
saver.merge_context = merge_context;
|
||||
saver.max_covering_tombstone_seq = *max_covering_tombstone_seq;
|
||||
saver.range_del_agg = range_del_agg;
|
||||
saver.merge_operator = moptions_.merge_operator;
|
||||
saver.logger = moptions_.info_log;
|
||||
saver.inplace_update_support = moptions_.inplace_update_support;
|
||||
|
||||
+10
-29
@@ -16,7 +16,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
@@ -158,8 +158,7 @@ class MemTable {
|
||||
// those allocated in arena.
|
||||
InternalIterator* NewIterator(const ReadOptions& read_options, Arena* arena);
|
||||
|
||||
FragmentedRangeTombstoneIterator* NewRangeTombstoneIterator(
|
||||
const ReadOptions& read_options, SequenceNumber read_seq);
|
||||
InternalIterator* NewRangeTombstoneIterator(const ReadOptions& read_options);
|
||||
|
||||
// Add an entry into memtable that maps key to value at the
|
||||
// specified sequence number and with the specified type.
|
||||
@@ -188,19 +187,17 @@ class MemTable {
|
||||
// On success, *s may be set to OK, NotFound, or MergeInProgress. Any other
|
||||
// status returned indicates a corruption or other unexpected error.
|
||||
bool Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq, SequenceNumber* seq,
|
||||
const ReadOptions& read_opts, ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr);
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
ReadCallback* callback = nullptr, bool* is_blob_index = nullptr);
|
||||
|
||||
bool Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
const ReadOptions& read_opts, ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr) {
|
||||
SequenceNumber seq;
|
||||
return Get(key, value, s, merge_context, max_covering_tombstone_seq, &seq,
|
||||
read_opts, callback, is_blob_index);
|
||||
return Get(key, value, s, merge_context, range_del_agg, &seq, read_opts,
|
||||
callback, is_blob_index);
|
||||
}
|
||||
|
||||
// Attempts to update the new_value inplace, else does normal Add
|
||||
@@ -386,16 +383,6 @@ class MemTable {
|
||||
|
||||
uint64_t GetID() const { return id_; }
|
||||
|
||||
void SetFlushCompleted(bool completed) { flush_completed_ = completed; }
|
||||
|
||||
uint64_t GetFileNumber() const { return file_number_; }
|
||||
|
||||
void SetFileNumber(uint64_t file_num) { file_number_ = file_num; }
|
||||
|
||||
void SetFlushInProgress(bool in_progress) {
|
||||
flush_in_progress_ = in_progress;
|
||||
}
|
||||
|
||||
private:
|
||||
enum FlushStateEnum { FLUSH_NOT_REQUESTED, FLUSH_REQUESTED, FLUSH_SCHEDULED };
|
||||
|
||||
@@ -409,8 +396,8 @@ class MemTable {
|
||||
const size_t kArenaBlockSize;
|
||||
AllocTracker mem_tracker_;
|
||||
ConcurrentArena arena_;
|
||||
std::unique_ptr<MemTableRep> table_;
|
||||
std::unique_ptr<MemTableRep> range_del_table_;
|
||||
unique_ptr<MemTableRep> table_;
|
||||
unique_ptr<MemTableRep> range_del_table_;
|
||||
bool is_range_del_table_empty_;
|
||||
|
||||
// Total data size of all data inserted
|
||||
@@ -468,12 +455,6 @@ class MemTable {
|
||||
// Memtable id to track flush.
|
||||
uint64_t id_ = 0;
|
||||
|
||||
// Sequence number of the atomic flush that is responsible for this memtable.
|
||||
// The sequence number of atomic flush is a seq, such that no writes with
|
||||
// sequence numbers greater than or equal to seq are flushed, while all
|
||||
// writes with sequence number smaller than seq are flushed.
|
||||
SequenceNumber atomic_flush_seqno_;
|
||||
|
||||
// Returns a heuristic flush decision
|
||||
bool ShouldFlushNow() const;
|
||||
|
||||
|
||||
+38
-144
@@ -11,11 +11,9 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/version_set.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -105,36 +103,34 @@ int MemTableList::NumFlushed() const {
|
||||
// Operands stores the list of merge operations to apply, so far.
|
||||
bool MemTableListVersion::Get(const LookupKey& key, std::string* value,
|
||||
Status* s, MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
ReadCallback* callback, bool* is_blob_index) {
|
||||
return GetFromList(&memlist_, key, value, s, merge_context,
|
||||
max_covering_tombstone_seq, seq, read_opts, callback,
|
||||
is_blob_index);
|
||||
return GetFromList(&memlist_, key, value, s, merge_context, range_del_agg,
|
||||
seq, read_opts, callback, is_blob_index);
|
||||
}
|
||||
|
||||
bool MemTableListVersion::GetFromHistory(
|
||||
const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context, SequenceNumber* max_covering_tombstone_seq,
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts, bool* is_blob_index) {
|
||||
return GetFromList(&memlist_history_, key, value, s, merge_context,
|
||||
max_covering_tombstone_seq, seq, read_opts,
|
||||
nullptr /*read_callback*/, is_blob_index);
|
||||
range_del_agg, seq, read_opts, nullptr /*read_callback*/,
|
||||
is_blob_index);
|
||||
}
|
||||
|
||||
bool MemTableListVersion::GetFromList(
|
||||
std::list<MemTable*>* list, const LookupKey& key, std::string* value,
|
||||
Status* s, MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq, SequenceNumber* seq,
|
||||
const ReadOptions& read_opts, ReadCallback* callback, bool* is_blob_index) {
|
||||
Status* s, MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts, ReadCallback* callback,
|
||||
bool* is_blob_index) {
|
||||
*seq = kMaxSequenceNumber;
|
||||
|
||||
for (auto& memtable : *list) {
|
||||
SequenceNumber current_seq = kMaxSequenceNumber;
|
||||
|
||||
bool done =
|
||||
memtable->Get(key, value, s, merge_context, max_covering_tombstone_seq,
|
||||
¤t_seq, read_opts, callback, is_blob_index);
|
||||
bool done = memtable->Get(key, value, s, merge_context, range_del_agg,
|
||||
¤t_seq, read_opts, callback, is_blob_index);
|
||||
if (*seq == kMaxSequenceNumber) {
|
||||
// Store the most recent sequence number of any operation on this key.
|
||||
// Since we only care about the most recent change, we only need to
|
||||
@@ -147,7 +143,7 @@ bool MemTableListVersion::GetFromList(
|
||||
}
|
||||
|
||||
if (done) {
|
||||
assert(*seq != kMaxSequenceNumber || s->IsNotFound());
|
||||
assert(*seq != kMaxSequenceNumber);
|
||||
return true;
|
||||
}
|
||||
if (!done && !s->ok() && !s->IsMergeInProgress() && !s->IsNotFound()) {
|
||||
@@ -162,11 +158,24 @@ Status MemTableListVersion::AddRangeTombstoneIterators(
|
||||
RangeDelAggregator* range_del_agg) {
|
||||
assert(range_del_agg != nullptr);
|
||||
for (auto& m : memlist_) {
|
||||
// Using kMaxSequenceNumber is OK because these are immutable memtables.
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
m->NewRangeTombstoneIterator(read_opts,
|
||||
kMaxSequenceNumber /* read_seq */));
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
m->NewRangeTombstoneIterator(read_opts));
|
||||
Status s = range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status MemTableListVersion::AddRangeTombstoneIterators(
|
||||
const ReadOptions& read_opts,
|
||||
std::vector<InternalIterator*>* range_del_iters) {
|
||||
for (auto& m : memlist_) {
|
||||
auto* range_del_iter = m->NewRangeTombstoneIterator(read_opts);
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters->push_back(range_del_iter);
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -263,7 +272,7 @@ void MemTableListVersion::TrimHistory(autovector<MemTable*>* to_delete) {
|
||||
// Returns true if there is at least one memtable on which flush has
|
||||
// not yet started.
|
||||
bool MemTableList::IsFlushPending() const {
|
||||
if ((flush_requested_ && num_flush_not_started_ > 0) ||
|
||||
if ((flush_requested_ && num_flush_not_started_ >= 1) ||
|
||||
(num_flush_not_started_ >= min_write_buffer_number_to_merge_)) {
|
||||
assert(imm_flush_needed.load(std::memory_order_relaxed));
|
||||
return true;
|
||||
@@ -272,16 +281,12 @@ bool MemTableList::IsFlushPending() const {
|
||||
}
|
||||
|
||||
// Returns the memtables that need to be flushed.
|
||||
void MemTableList::PickMemtablesToFlush(const uint64_t* max_memtable_id,
|
||||
autovector<MemTable*>* ret) {
|
||||
void MemTableList::PickMemtablesToFlush(autovector<MemTable*>* ret) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_PICK_MEMTABLES_TO_FLUSH);
|
||||
const auto& memlist = current_->memlist_;
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
if (max_memtable_id != nullptr && m->GetID() > *max_memtable_id) {
|
||||
break;
|
||||
}
|
||||
if (!m->flush_in_progress_) {
|
||||
assert(!m->flush_completed_);
|
||||
num_flush_not_started_--;
|
||||
@@ -315,9 +320,8 @@ void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
// Try record a successful flush in the manifest file. It might just return
|
||||
// Status::OK letting a concurrent flush to do actual the recording..
|
||||
Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// Record a successful flush in the manifest file
|
||||
Status MemTableList::InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& mems, LogsWithPrepTracker* prep_tracker,
|
||||
VersionSet* vset, InstrumentedMutex* mu, uint64_t file_number,
|
||||
@@ -327,9 +331,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
|
||||
// Flush was successful
|
||||
// Record the status on the memtable object. Either this call or a call by a
|
||||
// concurrent flush thread will read the status and write it to manifest.
|
||||
// flush was successful
|
||||
for (size_t i = 0; i < mems.size(); ++i) {
|
||||
// All the edits are associated with the first memtable of this batch.
|
||||
assert(i == 0 || mems[i]->GetEdits()->NumEntries() == 0);
|
||||
@@ -341,7 +343,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// if some other thread is already committing, then return
|
||||
Status s;
|
||||
if (commit_in_progress_) {
|
||||
TEST_SYNC_POINT("MemTableList::TryInstallMemtableFlushResults:InProgress");
|
||||
TEST_SYNC_POINT("MemTableList::InstallMemtableFlushResults:InProgress");
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -352,16 +354,11 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// while the current thread is writing manifest where mutex is released.
|
||||
while (s.ok()) {
|
||||
auto& memlist = current_->memlist_;
|
||||
// The back is the oldest; if flush_completed_ is not set to it, it means
|
||||
// that we were assigned a more recent memtable. The memtables' flushes must
|
||||
// be recorded in manifest in order. A concurrent flush thread, who is
|
||||
// assigned to flush the oldest memtable, will later wake up and does all
|
||||
// the pending writes to manifest, in order.
|
||||
if (memlist.empty() || !memlist.back()->flush_completed_) {
|
||||
break;
|
||||
}
|
||||
// scan all memtables from the earliest, and commit those
|
||||
// (in that order) that have finished flushing. Memtables
|
||||
// (in that order) that have finished flushing. Memetables
|
||||
// are always committed in the order that they were created.
|
||||
uint64_t batch_file_number = 0;
|
||||
size_t batch_count = 0;
|
||||
@@ -384,7 +381,6 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
batch_count++;
|
||||
}
|
||||
|
||||
// TODO(myabandeh): Not sure how batch_count could be 0 here.
|
||||
if (batch_count > 0) {
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
assert(edit_list.size() > 0);
|
||||
@@ -410,7 +406,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// The reason is as follows (refer to
|
||||
// ColumnFamilyTest.FlushAndDropRaceCondition).
|
||||
// If the column family is dropped, then according to LogAndApply, its
|
||||
// corresponding flush operation is NOT written to the MANIFEST. This
|
||||
// corrresponding flush operation is NOT written to the MANIFEST. This
|
||||
// means the DB is not aware of the L0 files generated from the flush.
|
||||
// By committing the new state, we remove the memtable from the memtable
|
||||
// list. Creating an iterator on this column family will not be able to
|
||||
@@ -527,106 +523,4 @@ uint64_t MemTableList::PrecomputeMinLogContainingPrepSection(
|
||||
return min_log;
|
||||
}
|
||||
|
||||
// Commit a successful atomic flush in the manifest file.
|
||||
Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<MemTableList*>* imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list, VersionSet* vset,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData*>& file_metas,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
|
||||
size_t num = mems_list.size();
|
||||
assert(cfds.size() == num);
|
||||
if (imm_lists != nullptr) {
|
||||
assert(imm_lists->size() == num);
|
||||
}
|
||||
for (size_t k = 0; k != num; ++k) {
|
||||
#ifndef NDEBUG
|
||||
const auto* imm =
|
||||
(imm_lists == nullptr) ? cfds[k]->imm() : imm_lists->at(k);
|
||||
if (!mems_list[k]->empty()) {
|
||||
assert((*mems_list[k])[0]->GetID() == imm->GetEarliestMemTableID());
|
||||
}
|
||||
#endif
|
||||
assert(nullptr != file_metas[k]);
|
||||
for (size_t i = 0; i != mems_list[k]->size(); ++i) {
|
||||
assert(i == 0 || (*mems_list[k])[i]->GetEdits()->NumEntries() == 0);
|
||||
(*mems_list[k])[i]->SetFlushCompleted(true);
|
||||
(*mems_list[k])[i]->SetFileNumber(file_metas[k]->fd.GetNumber());
|
||||
}
|
||||
}
|
||||
|
||||
Status s;
|
||||
|
||||
autovector<autovector<VersionEdit*>> edit_lists;
|
||||
uint32_t num_entries = 0;
|
||||
for (const auto mems : mems_list) {
|
||||
assert(mems != nullptr);
|
||||
autovector<VersionEdit*> edits;
|
||||
assert(!mems->empty());
|
||||
edits.emplace_back((*mems)[0]->GetEdits());
|
||||
++num_entries;
|
||||
edit_lists.emplace_back(edits);
|
||||
}
|
||||
// Mark the version edits as an atomic group
|
||||
for (auto& edits : edit_lists) {
|
||||
assert(edits.size() == 1);
|
||||
edits[0]->MarkAtomicGroup(--num_entries);
|
||||
}
|
||||
assert(0 == num_entries);
|
||||
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(cfds, mutable_cf_options_list, edit_lists, mu,
|
||||
db_directory);
|
||||
|
||||
for (size_t k = 0; k != cfds.size(); ++k) {
|
||||
auto* imm = (imm_lists == nullptr) ? cfds[k]->imm() : imm_lists->at(k);
|
||||
imm->InstallNewVersion();
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsShutdownInProgress()) {
|
||||
for (size_t i = 0; i != cfds.size(); ++i) {
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
auto* imm = (imm_lists == nullptr) ? cfds[i]->imm() : imm_lists->at(i);
|
||||
for (auto m : *mems_list[i]) {
|
||||
assert(m->GetFileNumber() > 0);
|
||||
uint64_t mem_id = m->GetID();
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " done",
|
||||
cfds[i]->GetName().c_str(), m->GetFileNumber(),
|
||||
mem_id);
|
||||
imm->current_->Remove(m, to_delete);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i != cfds.size(); ++i) {
|
||||
auto* imm = (imm_lists == nullptr) ? cfds[i]->imm() : imm_lists->at(i);
|
||||
for (auto m : *mems_list[i]) {
|
||||
uint64_t mem_id = m->GetID();
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Level-0 commit table #%" PRIu64
|
||||
": memtable #%" PRIu64 " failed",
|
||||
cfds[i]->GetName().c_str(), m->GetFileNumber(),
|
||||
mem_id);
|
||||
m->SetFlushCompleted(false);
|
||||
m->SetFlushInProgress(false);
|
||||
m->GetEdits()->Clear();
|
||||
m->SetFileNumber(0);
|
||||
imm->num_flush_not_started_++;
|
||||
}
|
||||
imm->imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+22
-75
@@ -31,7 +31,6 @@ class ColumnFamilyData;
|
||||
class InternalKeyComparator;
|
||||
class InstrumentedMutex;
|
||||
class MergeIteratorBuilder;
|
||||
class MemTableList;
|
||||
|
||||
// keeps a list of immutable memtables in a vector. the list is immutable
|
||||
// if refcount is bigger than one. It is used as a state for Get() and
|
||||
@@ -56,19 +55,17 @@ class MemTableListVersion {
|
||||
// will be stored in *seq on success (regardless of whether true/false is
|
||||
// returned). Otherwise, *seq will be set to kMaxSequenceNumber.
|
||||
bool Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq, SequenceNumber* seq,
|
||||
const ReadOptions& read_opts, ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr);
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
ReadCallback* callback = nullptr, bool* is_blob_index = nullptr);
|
||||
|
||||
bool Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
const ReadOptions& read_opts, ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr) {
|
||||
SequenceNumber seq;
|
||||
return Get(key, value, s, merge_context, max_covering_tombstone_seq, &seq,
|
||||
read_opts, callback, is_blob_index);
|
||||
return Get(key, value, s, merge_context, range_del_agg, &seq, read_opts,
|
||||
callback, is_blob_index);
|
||||
}
|
||||
|
||||
// Similar to Get(), but searches the Memtable history of memtables that
|
||||
@@ -77,22 +74,24 @@ class MemTableListVersion {
|
||||
// writes that are also present in the SST files.
|
||||
bool GetFromHistory(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
RangeDelAggregator* range_del_agg, SequenceNumber* seq,
|
||||
const ReadOptions& read_opts,
|
||||
bool* is_blob_index = nullptr);
|
||||
bool GetFromHistory(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const ReadOptions& read_opts,
|
||||
bool* is_blob_index = nullptr) {
|
||||
SequenceNumber seq;
|
||||
return GetFromHistory(key, value, s, merge_context,
|
||||
max_covering_tombstone_seq, &seq, read_opts,
|
||||
is_blob_index);
|
||||
return GetFromHistory(key, value, s, merge_context, range_del_agg, &seq,
|
||||
read_opts, is_blob_index);
|
||||
}
|
||||
|
||||
Status AddRangeTombstoneIterators(const ReadOptions& read_opts, Arena* arena,
|
||||
RangeDelAggregator* range_del_agg);
|
||||
Status AddRangeTombstoneIterators(
|
||||
const ReadOptions& read_opts,
|
||||
std::vector<InternalIterator*>* range_del_iters);
|
||||
|
||||
void AddIterators(const ReadOptions& options,
|
||||
std::vector<InternalIterator*>* iterator_list,
|
||||
@@ -115,18 +114,6 @@ class MemTableListVersion {
|
||||
SequenceNumber GetEarliestSequenceNumber(bool include_history = false) const;
|
||||
|
||||
private:
|
||||
friend class MemTableList;
|
||||
|
||||
friend Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<MemTableList*>* imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// REQUIRE: m is an immutable memtable
|
||||
void Add(MemTable* m, autovector<MemTable*>* to_delete);
|
||||
// REQUIRE: m is an immutable memtable
|
||||
@@ -136,8 +123,8 @@ class MemTableListVersion {
|
||||
|
||||
bool GetFromList(std::list<MemTable*>* list, const LookupKey& key,
|
||||
std::string* value, Status* s, MergeContext* merge_context,
|
||||
SequenceNumber* max_covering_tombstone_seq,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
RangeDelAggregator* range_del_agg, SequenceNumber* seq,
|
||||
const ReadOptions& read_opts,
|
||||
ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr);
|
||||
|
||||
@@ -145,6 +132,8 @@ class MemTableListVersion {
|
||||
|
||||
void UnrefMemTable(autovector<MemTable*>* to_delete, MemTable* m);
|
||||
|
||||
friend class MemTableList;
|
||||
|
||||
// Immutable MemTables that have not yet been flushed.
|
||||
std::list<MemTable*> memlist_;
|
||||
|
||||
@@ -212,17 +201,15 @@ class MemTableList {
|
||||
|
||||
// Returns the earliest memtables that needs to be flushed. The returned
|
||||
// memtables are guaranteed to be in the ascending order of created time.
|
||||
void PickMemtablesToFlush(const uint64_t* max_memtable_id,
|
||||
autovector<MemTable*>* mems);
|
||||
void PickMemtablesToFlush(autovector<MemTable*>* mems);
|
||||
|
||||
// Reset status of the given memtable list back to pending state so that
|
||||
// they can get picked up again on the next round of flush.
|
||||
void RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
uint64_t file_number);
|
||||
|
||||
// Try commit a successful flush in the manifest file. It might just return
|
||||
// Status::OK letting a concurrent flush to do the actual the recording.
|
||||
Status TryInstallMemtableFlushResults(
|
||||
// Commit a successful flush in the manifest file
|
||||
Status InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& m, LogsWithPrepTracker* prep_tracker,
|
||||
VersionSet* vset, InstrumentedMutex* mu, uint64_t file_number,
|
||||
@@ -279,32 +266,7 @@ class MemTableList {
|
||||
return memlist.front()->GetID();
|
||||
}
|
||||
|
||||
void AssignAtomicFlushSeq(const SequenceNumber& seq) {
|
||||
const auto& memlist = current_->memlist_;
|
||||
// Scan the memtable list from new to old
|
||||
for (auto it = memlist.begin(); it != memlist.end(); ++it) {
|
||||
MemTable* mem = *it;
|
||||
if (mem->atomic_flush_seqno_ == kMaxSequenceNumber) {
|
||||
mem->atomic_flush_seqno_ = seq;
|
||||
} else {
|
||||
// Earlier memtables must have been assigned a atomic flush seq, no
|
||||
// need to continue scan.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<MemTableList*>* imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// DB mutex held
|
||||
void InstallNewVersion();
|
||||
|
||||
@@ -318,26 +280,11 @@ class MemTableList {
|
||||
// committing in progress
|
||||
bool commit_in_progress_;
|
||||
|
||||
// Requested a flush of memtables to storage. It's possible to request that
|
||||
// a subset of memtables be flushed.
|
||||
// Requested a flush of all memtables to storage
|
||||
bool flush_requested_;
|
||||
|
||||
// The current memory usage.
|
||||
size_t current_memory_usage_;
|
||||
};
|
||||
|
||||
// Installs memtable atomic flush results.
|
||||
// In most cases, imm_lists is nullptr, and the function simply uses the
|
||||
// immutable memtable lists associated with the cfds. There are unit tests that
|
||||
// installs flush results for external immutable memtable lists other than the
|
||||
// cfds' own immutable memtable lists, e.g. MemTableLIstTest. In this case,
|
||||
// imm_lists parameter is not nullptr.
|
||||
extern Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<MemTableList*>* imm_lists,
|
||||
const autovector<ColumnFamilyData*>& cfds,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list, VersionSet* vset,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData*>& file_meta,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
} // namespace rocksdb
|
||||
+83
-370
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -24,13 +25,9 @@ class MemTableListTest : public testing::Test {
|
||||
std::string dbname;
|
||||
DB* db;
|
||||
Options options;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
std::atomic<uint64_t> file_number;
|
||||
|
||||
MemTableListTest() : db(nullptr), file_number(1) {
|
||||
MemTableListTest() : db(nullptr) {
|
||||
dbname = test::PerThreadDBPath("memtable_list_test");
|
||||
options.create_if_missing = true;
|
||||
DestroyDB(dbname, options);
|
||||
}
|
||||
|
||||
// Create a test db if not yet created
|
||||
@@ -38,49 +35,19 @@ class MemTableListTest : public testing::Test {
|
||||
if (db == nullptr) {
|
||||
options.create_if_missing = true;
|
||||
DestroyDB(dbname, options);
|
||||
// Open DB only with default column family
|
||||
ColumnFamilyOptions cf_options;
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
Status s = DB::Open(options, dbname, cf_descs, &handles, &db);
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
EXPECT_OK(s);
|
||||
|
||||
ColumnFamilyOptions cf_opt1, cf_opt2;
|
||||
cf_opt1.cf_paths.emplace_back(dbname + "_one_1",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
cf_opt2.cf_paths.emplace_back(dbname + "_two_1",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
int sz = static_cast<int>(handles.size());
|
||||
handles.resize(sz + 2);
|
||||
s = db->CreateColumnFamily(cf_opt1, "one", &handles[1]);
|
||||
EXPECT_OK(s);
|
||||
s = db->CreateColumnFamily(cf_opt2, "two", &handles[2]);
|
||||
EXPECT_OK(s);
|
||||
|
||||
cf_descs.emplace_back("one", cf_options);
|
||||
cf_descs.emplace_back("two", cf_options);
|
||||
}
|
||||
}
|
||||
|
||||
~MemTableListTest() {
|
||||
if (db) {
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs(handles.size());
|
||||
for (int i = 0; i != static_cast<int>(handles.size()); ++i) {
|
||||
handles[i]->GetDescriptor(&cf_descs[i]);
|
||||
}
|
||||
for (auto h : handles) {
|
||||
if (h) {
|
||||
db->DestroyColumnFamilyHandle(h);
|
||||
}
|
||||
}
|
||||
handles.clear();
|
||||
delete db;
|
||||
db = nullptr;
|
||||
DestroyDB(dbname, options, cf_descs);
|
||||
DestroyDB(dbname, options);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls MemTableList::TryInstallMemtableFlushResults() and sets up all
|
||||
// Calls MemTableList::InstallMemtableFlushResults() and sets up all
|
||||
// structures needed to call this function.
|
||||
Status Mock_InstallMemtableFlushResults(
|
||||
MemTableList* list, const MutableCFOptions& mutable_cf_options,
|
||||
@@ -89,95 +56,36 @@ class MemTableListTest : public testing::Test {
|
||||
test::NullLogger logger;
|
||||
LogBuffer log_buffer(DEBUG_LEVEL, &logger);
|
||||
|
||||
CreateDB();
|
||||
// Create a mock VersionSet
|
||||
DBOptions db_options;
|
||||
ImmutableDBOptions immutable_db_options(db_options);
|
||||
EnvOptions env_options;
|
||||
std::shared_ptr<Cache> table_cache(NewLRUCache(50000, 16));
|
||||
shared_ptr<Cache> table_cache(NewLRUCache(50000, 16));
|
||||
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
|
||||
WriteController write_controller(10000000u);
|
||||
|
||||
CreateDB();
|
||||
VersionSet versions(dbname, &immutable_db_options, env_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller);
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions());
|
||||
cf_descs.emplace_back("one", ColumnFamilyOptions());
|
||||
cf_descs.emplace_back("two", ColumnFamilyOptions());
|
||||
|
||||
EXPECT_OK(versions.Recover(cf_descs, false));
|
||||
|
||||
// Create mock default ColumnFamilyData
|
||||
ColumnFamilyOptions cf_options;
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
EXPECT_OK(versions.Recover(column_families, false));
|
||||
|
||||
auto column_family_set = versions.GetColumnFamilySet();
|
||||
LogsWithPrepTracker dummy_prep_tracker;
|
||||
auto cfd = column_family_set->GetDefault();
|
||||
EXPECT_TRUE(nullptr != cfd);
|
||||
uint64_t file_num = file_number.fetch_add(1);
|
||||
auto cfd = column_family_set->GetColumnFamily(0);
|
||||
EXPECT_TRUE(cfd != nullptr);
|
||||
|
||||
// Create dummy mutex.
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
return list->TryInstallMemtableFlushResults(
|
||||
cfd, mutable_cf_options, m, &dummy_prep_tracker, &versions, &mutex,
|
||||
file_num, to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
|
||||
// Calls MemTableList::InstallMemtableFlushResults() and sets up all
|
||||
// structures needed to call this function.
|
||||
Status Mock_InstallMemtableAtomicFlushResults(
|
||||
autovector<MemTableList*>& lists, const autovector<uint32_t>& cf_ids,
|
||||
const autovector<const MutableCFOptions*>& mutable_cf_options_list,
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
autovector<MemTable*>* to_delete) {
|
||||
// Create a mock Logger
|
||||
test::NullLogger logger;
|
||||
LogBuffer log_buffer(DEBUG_LEVEL, &logger);
|
||||
|
||||
CreateDB();
|
||||
// Create a mock VersionSet
|
||||
DBOptions db_options;
|
||||
ImmutableDBOptions immutable_db_options(db_options);
|
||||
EnvOptions env_options;
|
||||
std::shared_ptr<Cache> table_cache(NewLRUCache(50000, 16));
|
||||
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
|
||||
WriteController write_controller(10000000u);
|
||||
|
||||
VersionSet versions(dbname, &immutable_db_options, env_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller);
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions());
|
||||
cf_descs.emplace_back("one", ColumnFamilyOptions());
|
||||
cf_descs.emplace_back("two", ColumnFamilyOptions());
|
||||
EXPECT_OK(versions.Recover(cf_descs, false));
|
||||
|
||||
// Create mock default ColumnFamilyData
|
||||
|
||||
auto column_family_set = versions.GetColumnFamilySet();
|
||||
|
||||
LogsWithPrepTracker dummy_prep_tracker;
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
for (int i = 0; i != static_cast<int>(cf_ids.size()); ++i) {
|
||||
cfds.emplace_back(column_family_set->GetColumnFamily(cf_ids[i]));
|
||||
EXPECT_NE(nullptr, cfds[i]);
|
||||
}
|
||||
std::vector<FileMetaData> file_metas;
|
||||
file_metas.reserve(cf_ids.size());
|
||||
for (size_t i = 0; i != cf_ids.size(); ++i) {
|
||||
FileMetaData meta;
|
||||
uint64_t file_num = file_number.fetch_add(1);
|
||||
meta.fd = FileDescriptor(file_num, 0, 0);
|
||||
file_metas.emplace_back(meta);
|
||||
}
|
||||
autovector<FileMetaData*> file_meta_ptrs;
|
||||
for (auto& meta : file_metas) {
|
||||
file_meta_ptrs.push_back(&meta);
|
||||
}
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
return InstallMemtableAtomicFlushResults(
|
||||
&lists, cfds, mutable_cf_options_list, mems_list, &versions, &mutex,
|
||||
file_meta_ptrs, to_delete, nullptr, &log_buffer);
|
||||
return list->InstallMemtableFlushResults(
|
||||
cfd, mutable_cf_options, m, &dummy_prep_tracker, &versions, &mutex, 1,
|
||||
to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -190,7 +98,7 @@ TEST_F(MemTableListTest, Empty) {
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
|
||||
autovector<MemTable*> mems;
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &mems);
|
||||
list.PickMemtablesToFlush(&mems);
|
||||
ASSERT_EQ(0, mems.size());
|
||||
|
||||
autovector<MemTable*> to_delete;
|
||||
@@ -210,12 +118,12 @@ TEST_F(MemTableListTest, GetTest) {
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
InternalKeyComparator ikey_cmp(options.comparator);
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
RangeDelAggregator range_del_agg(ikey_cmp, {} /* snapshots */);
|
||||
autovector<MemTable*> to_delete;
|
||||
|
||||
LookupKey lkey("key1", seq);
|
||||
bool found = list.current()->Get(lkey, &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
&range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
// Create a MemTable
|
||||
@@ -238,19 +146,19 @@ TEST_F(MemTableListTest, GetTest) {
|
||||
// Fetch the newly written keys
|
||||
merge_context.Clear();
|
||||
found = mem->Get(LookupKey("key1", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
&range_del_agg, ReadOptions());
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ(value, "value1");
|
||||
|
||||
merge_context.Clear();
|
||||
found = mem->Get(LookupKey("key1", 2), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
&range_del_agg, ReadOptions());
|
||||
// MemTable found out that this key is *not* found (at this sequence#)
|
||||
ASSERT_TRUE(found && s.IsNotFound());
|
||||
|
||||
merge_context.Clear();
|
||||
found = mem->Get(LookupKey("key2", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
&range_del_agg, ReadOptions());
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ(value, "value2.2");
|
||||
|
||||
@@ -276,28 +184,25 @@ TEST_F(MemTableListTest, GetTest) {
|
||||
|
||||
// Fetch keys via MemTableList
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key1", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key1", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_TRUE(found && s.IsNotFound());
|
||||
|
||||
merge_context.Clear();
|
||||
found = list.current()->Get(LookupKey("key1", saved_seq), &value, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
ReadOptions());
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ("value1", value);
|
||||
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key2", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key2", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ(value, "value2.3");
|
||||
|
||||
merge_context.Clear();
|
||||
found = list.current()->Get(LookupKey("key2", 1), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
&range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
ASSERT_EQ(2, list.NumNotFlushed());
|
||||
@@ -320,12 +225,12 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
InternalKeyComparator ikey_cmp(options.comparator);
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
RangeDelAggregator range_del_agg(ikey_cmp, {} /* snapshots */);
|
||||
autovector<MemTable*> to_delete;
|
||||
|
||||
LookupKey lkey("key1", seq);
|
||||
bool found = list.current()->Get(lkey, &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
&range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
// Create a MemTable
|
||||
@@ -347,13 +252,13 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
// Fetch the newly written keys
|
||||
merge_context.Clear();
|
||||
found = mem->Get(LookupKey("key1", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
&range_del_agg, ReadOptions());
|
||||
// MemTable found out that this key is *not* found (at this sequence#)
|
||||
ASSERT_TRUE(found && s.IsNotFound());
|
||||
|
||||
merge_context.Clear();
|
||||
found = mem->Get(LookupKey("key2", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
&range_del_agg, ReadOptions());
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ(value, "value2.2");
|
||||
|
||||
@@ -363,27 +268,24 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
|
||||
// Fetch keys via MemTableList
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key1", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key1", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_TRUE(found && s.IsNotFound());
|
||||
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key2", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key2", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_TRUE(s.ok() && found);
|
||||
ASSERT_EQ("value2.2", value);
|
||||
|
||||
// Flush this memtable from the list.
|
||||
// (It will then be a part of the memtable history).
|
||||
autovector<MemTable*> to_flush;
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush);
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(1, to_flush.size());
|
||||
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
s = Mock_InstallMemtableFlushResults(&list, mutable_cf_options, to_flush,
|
||||
&to_delete);
|
||||
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options),
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(0, list.NumNotFlushed());
|
||||
ASSERT_EQ(1, list.NumFlushed());
|
||||
@@ -391,28 +293,26 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
|
||||
// Verify keys are no longer in MemTableList
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key1", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key1", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key2", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key2", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
// Verify keys are present in history
|
||||
merge_context.Clear();
|
||||
found = list.current()->GetFromHistory(
|
||||
LookupKey("key1", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->GetFromHistory(LookupKey("key1", seq), &value, &s,
|
||||
&merge_context, &range_del_agg,
|
||||
ReadOptions());
|
||||
ASSERT_TRUE(found && s.IsNotFound());
|
||||
|
||||
merge_context.Clear();
|
||||
found = list.current()->GetFromHistory(
|
||||
LookupKey("key2", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->GetFromHistory(LookupKey("key2", seq), &value, &s,
|
||||
&merge_context, &range_del_agg,
|
||||
ReadOptions());
|
||||
ASSERT_TRUE(found);
|
||||
ASSERT_EQ("value2.2", value);
|
||||
|
||||
@@ -430,12 +330,12 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
ASSERT_EQ(0, to_delete.size());
|
||||
|
||||
to_flush.clear();
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush);
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(1, to_flush.size());
|
||||
|
||||
// Flush second memtable
|
||||
s = Mock_InstallMemtableFlushResults(&list, mutable_cf_options, to_flush,
|
||||
&to_delete);
|
||||
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options),
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(0, list.NumNotFlushed());
|
||||
ASSERT_EQ(2, list.NumFlushed());
|
||||
@@ -453,42 +353,38 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
|
||||
// Verify keys are no longer in MemTableList
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key1", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key1", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key2", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key2", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key3", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key3", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
// Verify that the second memtable's keys are in the history
|
||||
merge_context.Clear();
|
||||
found = list.current()->GetFromHistory(
|
||||
LookupKey("key1", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->GetFromHistory(LookupKey("key1", seq), &value, &s,
|
||||
&merge_context, &range_del_agg,
|
||||
ReadOptions());
|
||||
ASSERT_TRUE(found && s.IsNotFound());
|
||||
|
||||
merge_context.Clear();
|
||||
found = list.current()->GetFromHistory(
|
||||
LookupKey("key3", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->GetFromHistory(LookupKey("key3", seq), &value, &s,
|
||||
&merge_context, &range_del_agg,
|
||||
ReadOptions());
|
||||
ASSERT_TRUE(found);
|
||||
ASSERT_EQ("value3", value);
|
||||
|
||||
// Verify that key2 from the first memtable is no longer in the history
|
||||
merge_context.Clear();
|
||||
found =
|
||||
list.current()->Get(LookupKey("key2", seq), &value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, ReadOptions());
|
||||
found = list.current()->Get(LookupKey("key2", seq), &value, &s,
|
||||
&merge_context, &range_del_agg, ReadOptions());
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
// Cleanup
|
||||
@@ -500,7 +396,7 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
}
|
||||
|
||||
TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
const int num_tables = 6;
|
||||
const int num_tables = 5;
|
||||
SequenceNumber seq = 1;
|
||||
Status s;
|
||||
|
||||
@@ -518,13 +414,11 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
max_write_buffer_number_to_maintain);
|
||||
|
||||
// Create some MemTables
|
||||
uint64_t memtable_id = 0;
|
||||
std::vector<MemTable*> tables;
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
for (int i = 0; i < num_tables; i++) {
|
||||
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
mem->SetID(memtable_id++);
|
||||
mem->Ref();
|
||||
|
||||
std::string value;
|
||||
@@ -543,7 +437,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
autovector<MemTable*> to_flush;
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush);
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(0, to_flush.size());
|
||||
|
||||
// Request a flush even though there is nothing to flush
|
||||
@@ -552,7 +446,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Attempt to 'flush' to clear request for flush
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush);
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(0, to_flush.size());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
@@ -576,7 +470,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Pick tables to flush
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush);
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(2, to_flush.size());
|
||||
ASSERT_EQ(2, list.NumNotFlushed());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
@@ -597,7 +491,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
ASSERT_EQ(0, to_delete.size());
|
||||
|
||||
// Pick tables to flush
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush);
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(3, to_flush.size());
|
||||
ASSERT_EQ(3, list.NumNotFlushed());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
@@ -605,7 +499,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
|
||||
// Pick tables to flush again
|
||||
autovector<MemTable*> to_flush2;
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush2);
|
||||
list.PickMemtablesToFlush(&to_flush2);
|
||||
ASSERT_EQ(0, to_flush2.size());
|
||||
ASSERT_EQ(3, list.NumNotFlushed());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
@@ -623,7 +517,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Pick tables to flush again
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush2);
|
||||
list.PickMemtablesToFlush(&to_flush2);
|
||||
ASSERT_EQ(1, to_flush2.size());
|
||||
ASSERT_EQ(4, list.NumNotFlushed());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
@@ -644,7 +538,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
ASSERT_EQ(0, to_delete.size());
|
||||
|
||||
// Pick tables to flush
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush);
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
// Should pick 4 of 5 since 1 table has been picked in to_flush2
|
||||
ASSERT_EQ(4, to_flush.size());
|
||||
ASSERT_EQ(5, list.NumNotFlushed());
|
||||
@@ -653,21 +547,20 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
|
||||
// Pick tables to flush again
|
||||
autovector<MemTable*> to_flush3;
|
||||
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush3);
|
||||
ASSERT_EQ(0, to_flush3.size()); // nothing not in progress of being flushed
|
||||
ASSERT_EQ(5, list.NumNotFlushed());
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Flush the 4 memtables that were picked in to_flush
|
||||
s = Mock_InstallMemtableFlushResults(&list, mutable_cf_options, to_flush,
|
||||
&to_delete);
|
||||
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options),
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Note: now to_flush contains tables[0,1,2,4]. to_flush2 contains
|
||||
// tables[3].
|
||||
// Current implementation will only commit memtables in the order they were
|
||||
// created. So TryInstallMemtableFlushResults will install the first 3 tables
|
||||
// created. So InstallMemtableFlushResults will install the first 3 tables
|
||||
// in to_flush and stop when it encounters a table not yet flushed.
|
||||
ASSERT_EQ(2, list.NumNotFlushed());
|
||||
int num_in_history = std::min(3, max_write_buffer_number_to_maintain);
|
||||
@@ -681,7 +574,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
|
||||
// Flush the 1 memtable that was picked in to_flush2
|
||||
s = MemTableListTest::Mock_InstallMemtableFlushResults(
|
||||
&list, mutable_cf_options, to_flush2, &to_delete);
|
||||
&list, MutableCFOptions(options), to_flush2, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// This will actually install 2 tables. The 1 we told it to flush, and also
|
||||
@@ -692,7 +585,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
ASSERT_EQ(5 - list.NumNotFlushed() - num_in_history, to_delete.size());
|
||||
|
||||
for (const auto& m : to_delete) {
|
||||
// Refcount should be 0 after calling TryInstallMemtableFlushResults.
|
||||
// Refcount should be 0 after calling InstallMemtableFlushResults.
|
||||
// Verify this, by Ref'ing then UnRef'ing:
|
||||
m->Ref();
|
||||
ASSERT_EQ(m, m->Unref());
|
||||
@@ -700,41 +593,12 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
}
|
||||
to_delete.clear();
|
||||
|
||||
// Add another table
|
||||
list.Add(tables[5], &to_delete);
|
||||
ASSERT_EQ(1, list.NumNotFlushed());
|
||||
ASSERT_EQ(5, list.GetLatestMemTableID());
|
||||
memtable_id = 4;
|
||||
// Pick tables to flush. The tables to pick must have ID smaller than or
|
||||
// equal to 4. Therefore, no table will be selected in this case.
|
||||
autovector<MemTable*> to_flush4;
|
||||
list.FlushRequested();
|
||||
ASSERT_TRUE(list.HasFlushRequested());
|
||||
list.PickMemtablesToFlush(&memtable_id, &to_flush4);
|
||||
ASSERT_TRUE(to_flush4.empty());
|
||||
ASSERT_EQ(1, list.NumNotFlushed());
|
||||
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
ASSERT_FALSE(list.HasFlushRequested());
|
||||
|
||||
// Pick tables to flush. The tables to pick must have ID smaller than or
|
||||
// equal to 5. Therefore, only tables[5] will be selected.
|
||||
memtable_id = 5;
|
||||
list.FlushRequested();
|
||||
list.PickMemtablesToFlush(&memtable_id, &to_flush4);
|
||||
ASSERT_EQ(1, static_cast<int>(to_flush4.size()));
|
||||
ASSERT_EQ(1, list.NumNotFlushed());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
ASSERT_FALSE(list.IsFlushPending());
|
||||
to_delete.clear();
|
||||
|
||||
list.current()->Unref(&to_delete);
|
||||
int to_delete_size =
|
||||
std::min(num_tables, max_write_buffer_number_to_maintain);
|
||||
int to_delete_size = std::min(5, max_write_buffer_number_to_maintain);
|
||||
ASSERT_EQ(to_delete_size, to_delete.size());
|
||||
|
||||
for (const auto& m : to_delete) {
|
||||
// Refcount should be 0 after calling TryInstallMemtableFlushResults.
|
||||
// Refcount should be 0 after calling InstallMemtableFlushResults.
|
||||
// Verify this, by Ref'ing then UnRef'ing:
|
||||
m->Ref();
|
||||
ASSERT_EQ(m, m->Unref());
|
||||
@@ -743,157 +607,6 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
to_delete.clear();
|
||||
}
|
||||
|
||||
TEST_F(MemTableListTest, EmptyAtomicFlusTest) {
|
||||
autovector<MemTableList*> lists;
|
||||
autovector<uint32_t> cf_ids;
|
||||
autovector<const MutableCFOptions*> options_list;
|
||||
autovector<const autovector<MemTable*>*> to_flush;
|
||||
autovector<MemTable*> to_delete;
|
||||
Status s = Mock_InstallMemtableAtomicFlushResults(lists, cf_ids, options_list,
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(to_delete.empty());
|
||||
}
|
||||
|
||||
TEST_F(MemTableListTest, AtomicFlusTest) {
|
||||
const int num_cfs = 3;
|
||||
const int num_tables_per_cf = 2;
|
||||
SequenceNumber seq = 1;
|
||||
|
||||
auto factory = std::make_shared<SkipListFactory>();
|
||||
options.memtable_factory = factory;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
WriteBufferManager wb(options.db_write_buffer_size);
|
||||
|
||||
// Create MemTableLists
|
||||
int min_write_buffer_number_to_merge = 3;
|
||||
int max_write_buffer_number_to_maintain = 7;
|
||||
autovector<MemTableList*> lists;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
lists.emplace_back(new MemTableList(min_write_buffer_number_to_merge,
|
||||
max_write_buffer_number_to_maintain));
|
||||
}
|
||||
|
||||
autovector<uint32_t> cf_ids;
|
||||
std::vector<std::vector<MemTable*>> tables(num_cfs);
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
uint32_t cf_id = 0;
|
||||
for (auto& elem : tables) {
|
||||
mutable_cf_options_list.emplace_back(new MutableCFOptions(options));
|
||||
uint64_t memtable_id = 0;
|
||||
for (int i = 0; i != num_tables_per_cf; ++i) {
|
||||
MemTable* mem =
|
||||
new MemTable(cmp, ioptions, *(mutable_cf_options_list.back()), &wb,
|
||||
kMaxSequenceNumber, cf_id);
|
||||
mem->SetID(memtable_id++);
|
||||
mem->Ref();
|
||||
|
||||
std::string value;
|
||||
|
||||
mem->Add(++seq, kTypeValue, "key1", ToString(i));
|
||||
mem->Add(++seq, kTypeValue, "keyN" + ToString(i), "valueN");
|
||||
mem->Add(++seq, kTypeValue, "keyX" + ToString(i), "value");
|
||||
mem->Add(++seq, kTypeValue, "keyM" + ToString(i), "valueM");
|
||||
mem->Add(++seq, kTypeDeletion, "keyX" + ToString(i), "");
|
||||
|
||||
elem.push_back(mem);
|
||||
}
|
||||
cf_ids.push_back(cf_id++);
|
||||
}
|
||||
|
||||
std::vector<autovector<MemTable*>> flush_candidates(num_cfs);
|
||||
|
||||
// Nothing to flush
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
auto* list = lists[i];
|
||||
ASSERT_FALSE(list->IsFlushPending());
|
||||
ASSERT_FALSE(list->imm_flush_needed.load(std::memory_order_acquire));
|
||||
list->PickMemtablesToFlush(nullptr /* memtable_id */, &flush_candidates[i]);
|
||||
ASSERT_EQ(0, flush_candidates[i].size());
|
||||
}
|
||||
// Request flush even though there is nothing to flush
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
auto* list = lists[i];
|
||||
list->FlushRequested();
|
||||
ASSERT_FALSE(list->IsFlushPending());
|
||||
ASSERT_FALSE(list->imm_flush_needed.load(std::memory_order_acquire));
|
||||
}
|
||||
autovector<MemTable*> to_delete;
|
||||
// Add tables to the immutable memtalbe lists associated with column families
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
for (auto j = 0; j != num_tables_per_cf; ++j) {
|
||||
lists[i]->Add(tables[i][j], &to_delete);
|
||||
}
|
||||
ASSERT_EQ(num_tables_per_cf, lists[i]->NumNotFlushed());
|
||||
ASSERT_TRUE(lists[i]->IsFlushPending());
|
||||
ASSERT_TRUE(lists[i]->imm_flush_needed.load(std::memory_order_acquire));
|
||||
}
|
||||
std::vector<uint64_t> flush_memtable_ids = {1, 1, 0};
|
||||
// +----+
|
||||
// list[0]: |0 1|
|
||||
// list[1]: |0 1|
|
||||
// | +--+
|
||||
// list[2]: |0| 1
|
||||
// +-+
|
||||
// Pick memtables to flush
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
flush_candidates[i].clear();
|
||||
lists[i]->PickMemtablesToFlush(&flush_memtable_ids[i],
|
||||
&flush_candidates[i]);
|
||||
ASSERT_EQ(flush_memtable_ids[i] - 0 + 1,
|
||||
static_cast<uint64_t>(flush_candidates[i].size()));
|
||||
}
|
||||
autovector<MemTableList*> tmp_lists;
|
||||
autovector<uint32_t> tmp_cf_ids;
|
||||
autovector<const MutableCFOptions*> tmp_options_list;
|
||||
autovector<const autovector<MemTable*>*> to_flush;
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
if (!flush_candidates[i].empty()) {
|
||||
to_flush.push_back(&flush_candidates[i]);
|
||||
tmp_lists.push_back(lists[i]);
|
||||
tmp_cf_ids.push_back(i);
|
||||
tmp_options_list.push_back(mutable_cf_options_list[i]);
|
||||
}
|
||||
}
|
||||
Status s = Mock_InstallMemtableAtomicFlushResults(
|
||||
tmp_lists, tmp_cf_ids, tmp_options_list, to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
|
||||
for (auto i = 0; i != num_cfs; ++i) {
|
||||
for (auto j = 0; j != num_tables_per_cf; ++j) {
|
||||
if (static_cast<uint64_t>(j) <= flush_memtable_ids[i]) {
|
||||
ASSERT_LT(0, tables[i][j]->GetFileNumber());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(
|
||||
static_cast<size_t>(num_tables_per_cf) - flush_candidates[i].size(),
|
||||
lists[i]->NumNotFlushed());
|
||||
}
|
||||
|
||||
to_delete.clear();
|
||||
for (auto list : lists) {
|
||||
list->current()->Unref(&to_delete);
|
||||
delete list;
|
||||
}
|
||||
for (auto& mutable_cf_options : mutable_cf_options_list) {
|
||||
if (mutable_cf_options != nullptr) {
|
||||
delete mutable_cf_options;
|
||||
mutable_cf_options = nullptr;
|
||||
}
|
||||
}
|
||||
// All memtables in tables array must have been flushed, thus ready to be
|
||||
// deleted.
|
||||
ASSERT_EQ(to_delete.size(), tables.size() * tables.front().size());
|
||||
for (const auto& m : to_delete) {
|
||||
// Refcount should be 0 after calling InstallMemtableFlushResults.
|
||||
// Verify this by Ref'ing and then Unref'ing.
|
||||
m->Ref();
|
||||
ASSERT_EQ(m, m->Unref());
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+2
-4
@@ -79,8 +79,7 @@ class MergeContext {
|
||||
return GetOperandsDirectionForward();
|
||||
}
|
||||
|
||||
// Return all the operands in the order as they were merged (passed to
|
||||
// FullMerge or FullMergeV2)
|
||||
// Return all the operands in the order as they were merged (passed to FullMerge or FullMergeV2)
|
||||
const std::vector<Slice>& GetOperandsDirectionForward() {
|
||||
if (!operand_list_) {
|
||||
return empty_operand_list;
|
||||
@@ -90,8 +89,7 @@ class MergeContext {
|
||||
return *operand_list_;
|
||||
}
|
||||
|
||||
// Return all the operands in the reversed order relative to how they were
|
||||
// merged (passed to FullMerge or FullMergeV2)
|
||||
// Return all the operands in the reversed order relative to how they were merged (passed to FullMerge or FullMergeV2)
|
||||
const std::vector<Slice>& GetOperandsDirectionBackward() {
|
||||
if (!operand_list_) {
|
||||
return empty_operand_list;
|
||||
|
||||
Loaded 100 of 370 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user