mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 22:55:23 +08:00
Compare commits
200 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 020e575ed1 | |||
| 82089d59c3 | |||
| a35451eaa4 | |||
| aaac6cd16f | |||
| 727eb881a5 | |||
| 4dd80debd0 | |||
| a736255de8 | |||
| 724855c7da | |||
| cf826de3ed | |||
| 03cda531e4 | |||
| 1c1bafa668 | |||
| 402b7aa07f | |||
| 8c3bf0801b | |||
| 45434178ee | |||
| aa53579d6c | |||
| 6e08916eb3 | |||
| 070319f7bb | |||
| bc7e8d472e | |||
| 3db8504cde | |||
| c465509379 | |||
| 7a99c04311 | |||
| 01bcc34896 | |||
| 4011012d9d | |||
| 6c73a46693 | |||
| 4420cb49da | |||
| 7db721b9a6 | |||
| fcb31016e9 | |||
| 3db1ada3bf | |||
| c3ebc75843 | |||
| 263ef52b65 | |||
| 508a09fd62 | |||
| 7b655214d2 | |||
| e410501eeb | |||
| ed4d3393fb | |||
| 26da3676d9 | |||
| a0c7b4d526 | |||
| 17af09fcce | |||
| 1d7ca20f29 | |||
| aed7abbcca | |||
| fa43948cbc | |||
| 7ccb35f653 | |||
| 8bf555f487 | |||
| 46fde6b653 | |||
| 42cb4775c1 | |||
| 12ad711247 | |||
| 3d7dc75b36 | |||
| ebb823f746 | |||
| 718c1c9c1f | |||
| 66c7aa32fb | |||
| 3272bc07c6 | |||
| 072ae671a7 | |||
| d19f568abf | |||
| 3690276e74 | |||
| ddfd2525d2 | |||
| f92cd2feb4 | |||
| cee138c7d7 | |||
| 4bf169f07e | |||
| 1d9f24dc9a | |||
| 9470ee45b8 | |||
| 6d6e01cdbc | |||
| d72a51e9e1 | |||
| 72942ad7a4 | |||
| 4c5a3232e4 | |||
| fc522bdb3e | |||
| 171f415b30 | |||
| 4d40b10e0f | |||
| 6fc1bccef5 | |||
| a703432808 | |||
| 934f96de27 | |||
| a8d77ca381 | |||
| d59549298f | |||
| cfb86659bf | |||
| 5bed8a0065 | |||
| 6cab3184f5 | |||
| acb61b7a52 | |||
| 19fde54841 | |||
| 46152d53bf | |||
| 282099fc0f | |||
| c9ace1d81b | |||
| 6afe22db2e | |||
| 459bb9028f | |||
| ed7a95b28c | |||
| 7dfbe33532 | |||
| 513b5ce618 | |||
| 37cd617b6b | |||
| db36f222d8 | |||
| 7004e45489 | |||
| 63c965cdb4 | |||
| 7e4e381495 | |||
| e5a4dacf6d | |||
| 7c9f23e6db | |||
| 406b95197c | |||
| 13a0bd90ce | |||
| dfc61e7c24 | |||
| 9b89479e64 | |||
| a4fb1f8c04 | |||
| bc0da4b512 | |||
| 090c78a0d7 | |||
| affe01b0d5 | |||
| d5afa73789 | |||
| a8a28da215 | |||
| 2e72a5899b | |||
| bb2a2ec731 | |||
| dbdaa4662e | |||
| c3d1e36cce | |||
| 17e04039dd | |||
| dee95a1afc | |||
| 8a9c7f71c9 | |||
| 69faddb32e | |||
| e1e826b980 | |||
| ad511684b2 | |||
| 3cea61392f | |||
| 6d06be22c0 | |||
| 2ee1496c43 | |||
| 2c2f388897 | |||
| b4f333922a | |||
| 5e48811844 | |||
| af95aecd01 | |||
| 954b496b3f | |||
| 9fcd82e987 | |||
| 81d44f2bc5 | |||
| 28087acd79 | |||
| a0102aa6d7 | |||
| 31ee4bf240 | |||
| 1f5457ef21 | |||
| c81b0abedd | |||
| 3be9b36453 | |||
| d15397ba10 | |||
| 6f5e6445d9 | |||
| d42bd041c5 | |||
| 019d7894eb | |||
| d95014b9df | |||
| 2770a94c42 | |||
| 5ec382b918 | |||
| d2bcd7611f | |||
| 65fe8d6cd6 | |||
| 1c27cbfbd1 | |||
| f3a1d9e049 | |||
| bde1c1a72a | |||
| eb5a295440 | |||
| ca87aef82d | |||
| 74767deec3 | |||
| faba3fb53d | |||
| 6571770030 | |||
| 446b32cfc3 | |||
| 67182678a5 | |||
| 685912d07f | |||
| 147dfc7bdf | |||
| 36a9f22931 | |||
| c827b2dc2a | |||
| 7d9067991e | |||
| 2a62ca1750 | |||
| b058a33705 | |||
| 12b400e814 | |||
| 04c11b867d | |||
| df14424410 | |||
| b225de7e10 | |||
| 1579626d0d | |||
| 44653c7b7a | |||
| 8917eee962 | |||
| d12112d05e | |||
| d9bfb35d31 | |||
| c3eb762bb0 | |||
| 73f21a7b21 | |||
| 89d989ed75 | |||
| 3cb591954e | |||
| 0377ff9dea | |||
| 1f5def1653 | |||
| 90c542347a | |||
| d687670256 | |||
| f9f4d40f93 | |||
| 0999e9b79a | |||
| 35a4469bbf | |||
| 23f9d93f47 | |||
| 3e417a6607 | |||
| d382ae7de6 | |||
| 53d66df0c4 | |||
| a993c0139d | |||
| e80709a33a | |||
| 7ffce2805b | |||
| 09b6bf828a | |||
| 1cbc96d236 | |||
| 82137f0ce8 | |||
| 2e3d407778 | |||
| 4d51feab0b | |||
| 118058ba69 | |||
| 88c3e26cc0 | |||
| 620823f88b | |||
| de6cf95a53 | |||
| 7429b20e39 | |||
| fa8c050e9f | |||
| 7a6353bd1c | |||
| 70282cf876 | |||
| 2ce8f63f81 | |||
| d5585bb605 | |||
| f1b7b790c9 | |||
| 8823487ff7 | |||
| 6383e42362 | |||
| 61785c73ed | |||
| 93d52696bf |
+62
-27
@@ -16,7 +16,7 @@
|
||||
# See thirdparty.inc for more information.
|
||||
# sample command: cmake -G "Visual Studio 15 Win64" -DWITH_GFLAGS=1 -DWITH_SNAPPY=1 -DWITH_JEMALLOC=1 -DWITH_JNI=1 ..
|
||||
# 4. Then build the project in debug mode (you may want to add /m[:<N>] flag to run msbuild in <N> parallel threads
|
||||
# or simply /m ot use all avail cores)
|
||||
# or simply /m to use all avail cores)
|
||||
# msbuild rocksdb.sln
|
||||
#
|
||||
# rocksdb.sln build features exclusions of test only code in Release. If you build ALL_BUILD then everything
|
||||
@@ -64,9 +64,10 @@ else()
|
||||
find_package(JeMalloc REQUIRED)
|
||||
add_definitions(-DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE)
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${JEMALLOC_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# No config file for this
|
||||
option(WITH_GFLAGS "build with GFlags" ON)
|
||||
if(WITH_GFLAGS)
|
||||
@@ -132,19 +133,17 @@ endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
set(SH_CMD "sh")
|
||||
execute_process(COMMAND
|
||||
${SH_CMD} -c "build_tools/version.sh full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION}" ROCKSDB_VERSION)
|
||||
execute_process(COMMAND
|
||||
${SH_CMD} -c "build_tools/version.sh major"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION_MAJOR
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
|
||||
# Read rocksdb version from version.h header file.
|
||||
file(READ include/rocksdb/version.h version_header_file)
|
||||
string(REGEX MATCH "#define ROCKSDB_MAJOR ([0-9]+)" _ ${version_header_file})
|
||||
set(ROCKSDB_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
string(REGEX MATCH "#define ROCKSDB_MINOR ([0-9]+)" _ ${version_header_file})
|
||||
set(ROCKSDB_VERSION_MINOR ${CMAKE_MATCH_1})
|
||||
string(REGEX MATCH "#define ROCKSDB_PATCH ([0-9]+)" _ ${version_header_file})
|
||||
set(ROCKSDB_VERSION_PATCH ${CMAKE_MATCH_1})
|
||||
set(ROCKSDB_VERSION ${ROCKSDB_VERSION_MAJOR}.${ROCKSDB_VERSION_MINOR}.${ROCKSDB_VERSION_PATCH})
|
||||
|
||||
|
||||
option(WITH_MD_LIBRARY "build with MD" ON)
|
||||
if(WIN32 AND MSVC)
|
||||
@@ -229,6 +228,7 @@ int main() {
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
@@ -285,20 +285,28 @@ if(WITH_UBSAN)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(NUMA)
|
||||
if(NUMA_FOUND)
|
||||
option(WITH_NUMA "build with NUMA policy support" OFF)
|
||||
if(WITH_NUMA)
|
||||
find_package(NUMA REQUIRED)
|
||||
add_definitions(-DNUMA)
|
||||
include_directories(${NUMA_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${NUMA_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_package(TBB)
|
||||
if(TBB_FOUND)
|
||||
option(WITH_TBB "build with Threading Building Blocks (TBB)" OFF)
|
||||
if(WITH_TBB)
|
||||
find_package(TBB REQUIRED)
|
||||
add_definitions(-DTBB)
|
||||
include_directories(${TBB_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${TBB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# Stall notifications eat some performance from inserts
|
||||
option(DISABLE_STALL_NOTIF "Build with stall notifications" OFF)
|
||||
if(DISABLE_STALL_NOTIF)
|
||||
add_definitions(-DROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
endif()
|
||||
|
||||
# Used to run CI build and tests so we can run faster
|
||||
set(OPTIMIZE_DEBUG_DEFAULT 0) # Debug build is unoptimized by default use -DOPTDBG=1 to optimize
|
||||
|
||||
@@ -308,6 +316,22 @@ else()
|
||||
set(OPTIMIZE_DEBUG ${OPTIMIZE_DEBUG_DEFAULT})
|
||||
endif()
|
||||
|
||||
if(DEFINED USE_RTTI)
|
||||
if(USE_RTTI)
|
||||
message(STATUS "Enabling RTTI")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DROCKSDB_USE_RTTI")
|
||||
else()
|
||||
message(STATUS "Disabling RTTI")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-rtti")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Enabling RTTI in Debug builds only (default)")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DROCKSDB_USE_RTTI")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-rtti")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
if((${OPTIMIZE_DEBUG} EQUAL 1))
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
@@ -455,6 +479,7 @@ set(SOURCES
|
||||
db/flush_scheduler.cc
|
||||
db/forward_iterator.cc
|
||||
db/internal_stats.cc
|
||||
db/logs_with_prep_tracker.cc
|
||||
db/log_reader.cc
|
||||
db/log_writer.cc
|
||||
db/malloc_stats.cc
|
||||
@@ -569,6 +594,7 @@ set(SOURCES
|
||||
util/status_message.cc
|
||||
util/string_util.cc
|
||||
util/sync_point.cc
|
||||
util/sync_point_impl.cc
|
||||
util/testutil.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
@@ -631,6 +657,8 @@ set(SOURCES
|
||||
utilities/transactions/transaction_util.cc
|
||||
utilities/transactions/write_prepared_txn.cc
|
||||
utilities/transactions/write_prepared_txn_db.cc
|
||||
utilities/transactions/write_unprepared_txn.cc
|
||||
utilities/transactions/write_unprepared_txn_db.cc
|
||||
utilities/ttl/db_ttl_impl.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc
|
||||
@@ -657,8 +685,12 @@ if(WIN32)
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/win_thread.cc
|
||||
port/win/win_thread.cc)
|
||||
|
||||
if(WITH_XPRESS)
|
||||
list(APPEND SOURCES
|
||||
port/win/xpress_win.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_JEMALLOC)
|
||||
list(APPEND SOURCES
|
||||
@@ -675,6 +707,14 @@ endif()
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_SHARED_LIB rocksdb-shared${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_IMPORT_LIB ${ROCKSDB_SHARED_LIB})
|
||||
|
||||
option(WITH_LIBRADOS "Build with librados" OFF)
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND SOURCES
|
||||
utilities/env_librados.cc)
|
||||
list(APPEND THIRDPARTY_LIBS rados)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} Shlwapi.lib Rpcrt4.lib)
|
||||
set(LIBS ${ROCKSDB_STATIC_LIB} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
@@ -693,13 +733,6 @@ else()
|
||||
OUTPUT_NAME "rocksdb")
|
||||
endif()
|
||||
|
||||
option(WITH_LIBRADOS "Build with librados" OFF)
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND SOURCES
|
||||
utilities/env_librados.cc)
|
||||
list(APPEND THIRDPARTY_LIBS rados)
|
||||
endif()
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
@@ -814,6 +847,7 @@ if(WITH_TESTS)
|
||||
db/db_inplace_update_test.cc
|
||||
db/db_io_failure_test.cc
|
||||
db/db_iter_test.cc
|
||||
db/db_iter_stress_test.cc
|
||||
db/db_iterator_test.cc
|
||||
db/db_log_iter_test.cc
|
||||
db/db_memtable_test.cc
|
||||
@@ -832,6 +866,7 @@ if(WITH_TESTS)
|
||||
db/db_write_test.cc
|
||||
db/dbformat_test.cc
|
||||
db/deletefile_test.cc
|
||||
db/obsolete_files_test.cc
|
||||
db/external_sst_file_basic_test.cc
|
||||
db/external_sst_file_test.cc
|
||||
db/fault_injection_test.cc
|
||||
|
||||
+45
@@ -1,6 +1,50 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Public API Change
|
||||
* For users of `Statistics` objects created via `CreateDBStatistics()`, the format of the string returned by its `ToString()` method has changed.
|
||||
* With LRUCache, when high_pri_pool_ratio > 0, midpoint insertion strategy will be enabled to put low-pri items to the tail of low-pri list (the midpoint) when they first inserted into the cache. This is to make cache entries never get hit age out faster, improving cache efficiency when large background scan presents.
|
||||
|
||||
### New Features
|
||||
* Changes the format of index blocks by storing the key in their raw form rather than converting them to InternalKey. This saves 8 bytes per index key. The feature is backward compatbile but not forward compatible. It is disabled by default unless format_version 3 or above is used.
|
||||
|
||||
### Bug Fixes
|
||||
* fix deadlock with enable_pipelined_write=true and max_successive_merges > 0
|
||||
|
||||
## 5.14.0 (5/16/2018)
|
||||
### Public API Change
|
||||
* Add a BlockBasedTableOption to align uncompressed data blocks on the smaller of block size or page size boundary, to reduce flash reads by avoiding reads spanning 4K pages.
|
||||
* The background thread naming convention changed (on supporting platforms) to "rocksdb:<thread pool priority><thread number>", e.g., "rocksdb:low0".
|
||||
* Add a new ticker stat rocksdb.number.multiget.keys.found to count number of keys successfully read in MultiGet calls
|
||||
* Touch-up to write-related counters in PerfContext. New counters added: write_scheduling_flushes_compactions_time, write_thread_wait_nanos. Counters whose behavior was fixed or modified: write_memtable_time, write_pre_and_post_process_time, write_delay_time.
|
||||
* Posix Env's NewRandomRWFile() will fail if the file doesn't exist.
|
||||
* Now, `DBOptions::use_direct_io_for_flush_and_compaction` only applies to background writes, and `DBOptions::use_direct_reads` applies to both user reads and background reads. This conforms with Linux's `open(2)` manpage, which advises against simultaneously reading a file in buffered and direct modes, due to possibly undefined behavior and degraded performance.
|
||||
* Iterator::Valid() always returns false if !status().ok(). So, now when doing a Seek() followed by some Next()s, there's no need to check status() after every operation.
|
||||
* Iterator::Seek()/SeekForPrev()/SeekToFirst()/SeekToLast() always resets status().
|
||||
* Introduced `CompressionOptions::kDefaultCompressionLevel`, which is a generic way to tell RocksDB to use the compression library's default level. It is now the default value for `CompressionOptions::level`. Previously the level defaulted to -1, which gave poor compression ratios in ZSTD.
|
||||
|
||||
### New Features
|
||||
* Introduce TTL for level compaction so that all files older than ttl go through the compaction process to get rid of old data.
|
||||
* TransactionDBOptions::write_policy can be configured to enable WritePrepared 2PC transactions. Read more about them in the wiki.
|
||||
* Add DB properties "rocksdb.block-cache-capacity", "rocksdb.block-cache-usage", "rocksdb.block-cache-pinned-usage" to show block cache usage.
|
||||
* Add `Env::LowerThreadPoolCPUPriority(Priority)` method, which lowers the CPU priority of background (esp. compaction) threads to minimize interference with foreground tasks.
|
||||
* Fsync parent directory after deleting a file in delete scheduler.
|
||||
* In level-based compaction, if bottom-pri thread pool was setup via `Env::SetBackgroundThreads()`, compactions to the bottom level will be delegated to that thread pool.
|
||||
* `prefix_extractor` has been moved from ImmutableCFOptions to MutableCFOptions, meaning it can be dynamically changed without a DB restart.
|
||||
|
||||
### Bug Fixes
|
||||
* Fsync after writing global seq number to the ingestion file in ExternalSstFileIngestionJob.
|
||||
* Fix WAL corruption caused by race condition between user write thread and FlushWAL when two_write_queue is not set.
|
||||
* Fix `BackupableDBOptions::max_valid_backups_to_open` to not delete backup files when refcount cannot be accurately determined.
|
||||
* Fix memory leak when pin_l0_filter_and_index_blocks_in_cache is used with partitioned filters
|
||||
* Disable rollback of merge operands in WritePrepared transactions to work around an issue in MyRocks. It can be enabled back by setting TransactionDBOptions::rollback_merge_operands to true.
|
||||
* Fix wrong results by ReverseBytewiseComparator::FindShortSuccessor()
|
||||
|
||||
### Java API Changes
|
||||
* Add `BlockBasedTableConfig.setBlockCache` to allow sharing a block cache across DB instances.
|
||||
* Added SstFileManager to the Java API to allow managing SST files across DB instances.
|
||||
|
||||
## 5.13.0 (3/20/2018)
|
||||
### Public API Change
|
||||
* RocksDBOptionsParser::Parse()'s `ignore_unknown_options` argument will only be effective if the option file shows it is generated using a higher version of RocksDB than the current version.
|
||||
* Remove CompactionEventListener.
|
||||
|
||||
@@ -9,6 +53,7 @@
|
||||
* Avoid unnecessarily flushing in `CompactRange()` when the range specified by the user does not overlap unflushed memtables.
|
||||
* If `ColumnFamilyOptions::max_subcompactions` is set greater than one, we now parallelize large manual level-based compactions.
|
||||
* Add "rocksdb.live-sst-files-size" DB property to return total bytes of all SST files belong to the latest LSM tree.
|
||||
* NewSstFileManager to add an argument bytes_max_delete_chunk with default 64MB. With this argument, a file larger than 64MB will be ftruncated multiple times based on this size.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a leak in prepared_section_completed_ where the zeroed entries would not removed from the map.
|
||||
|
||||
@@ -76,7 +76,9 @@ ifeq ($(MAKECMDGOALS),install)
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastatic)
|
||||
DEBUG_LEVEL=0
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticrelease)
|
||||
@@ -222,6 +224,9 @@ ifdef COMPILE_WITH_TSAN
|
||||
PROFILING_FLAGS =
|
||||
# LUA is not supported under TSAN
|
||||
LUA_PATH =
|
||||
# Limit keys for crash test under TSAN to avoid error:
|
||||
# "ThreadSanitizer: DenseSlabAllocator overflow. Dying."
|
||||
CRASH_TEST_EXT_ARGS += --max_key=1000000
|
||||
endif
|
||||
|
||||
# AIX doesn't work with -pg
|
||||
@@ -276,7 +281,7 @@ endif
|
||||
default: all
|
||||
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
-Wunused-parameter
|
||||
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
WARNING_FLAGS += -Wno-unused-lambda-capture
|
||||
@@ -397,6 +402,7 @@ TESTS = \
|
||||
db_blob_index_test \
|
||||
db_bloom_filter_test \
|
||||
db_iter_test \
|
||||
db_iter_stress_test \
|
||||
db_log_iter_test \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
@@ -474,6 +480,7 @@ TESTS = \
|
||||
write_batch_with_index_test \
|
||||
write_controller_test\
|
||||
deletefile_test \
|
||||
obsolete_files_test \
|
||||
table_test \
|
||||
geodb_test \
|
||||
delete_scheduler_test \
|
||||
@@ -642,7 +649,7 @@ ifeq ($(HAVE_POWER8),1)
|
||||
shared_all_libobjects = $(shared_libobjects) $(shared-ppc-objects)
|
||||
endif
|
||||
$(SHARED4): $(shared_all_libobjects)
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(shared_libobjects) $(LDFLAGS) -o $@
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(shared_all_libobjects) $(LDFLAGS) -o $@
|
||||
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
|
||||
@@ -795,7 +802,7 @@ check_0:
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
|
||||
valgrind-blacklist-regexp = InlineSkipTest.ConcurrentInsert|TransactionTest.DeadlockStress|DBCompactionTest.SuggestCompactRangeNoTwoLevel0Compactions|BackupableDBTest.RateLimiting|DBTest.CloseSpeedup|DBTest.ThreadStatusFlush|DBTest.RateLimitingTest|DBTest.EncodeDecompressedBlockSizeTest|FaultInjectionTest.UninstalledCompaction|HarnessTest.Randomized|ExternalSSTFileTest.CompactDuringAddFileRandom|ExternalSSTFileTest.IngestFileWithGlobalSeqnoRandomized
|
||||
valgrind-blacklist-regexp = InlineSkipTest.ConcurrentInsert|TransactionTest.DeadlockStress|DBCompactionTest.SuggestCompactRangeNoTwoLevel0Compactions|BackupableDBTest.RateLimiting|DBTest.CloseSpeedup|DBTest.ThreadStatusFlush|DBTest.RateLimitingTest|DBTest.EncodeDecompressedBlockSizeTest|FaultInjectionTest.UninstalledCompaction|HarnessTest.Randomized|ExternalSSTFileTest.CompactDuringAddFileRandom|ExternalSSTFileTest.IngestFileWithGlobalSeqnoRandomized|MySQLStyleTransactionTest.TransactionStressTest
|
||||
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0:
|
||||
@@ -1189,6 +1196,9 @@ db_tailing_iter_test: db/db_tailing_iter_test.o db/db_test_util.o $(LIBOBJECTS)
|
||||
db_iter_test: db/db_iter_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_iter_stress_test: db/db_iter_stress_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_universal_compaction_test: db/db_universal_compaction_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1380,6 +1390,9 @@ options_file_test: db/options_file_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
deletefile_test: db/deletefile_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
obsolete_files_test: db/obsolete_files_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
geodb_test: utilities/geodb/geodb_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1715,7 +1728,9 @@ rocksdbjavastatic: $(java_static_all_libobjects)
|
||||
-o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) \
|
||||
$(java_static_all_libobjects) $(COVERAGEFLAGS) \
|
||||
$(JAVA_COMPRESSIONS) $(JAVA_STATIC_LDFLAGS)
|
||||
cd java/target;strip $(STRIPFLAGS) $(ROCKSDBJNILIB)
|
||||
cd java/target;if [ "$(DEBUG_LEVEL)" == "0" ]; then \
|
||||
strip $(STRIPFLAGS) $(ROCKSDBJNILIB); \
|
||||
fi
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
|
||||
@@ -25,6 +25,10 @@ rocksdb_compiler_flags = [
|
||||
"-DTBB",
|
||||
# Needed to compile in fbcode
|
||||
"-Wno-expansion-to-defined",
|
||||
# Added missing flags from output of build_detect_platform
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
"-Wshorten-64-to-32",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
@@ -99,6 +103,7 @@ cpp_library(
|
||||
"db/internal_stats.cc",
|
||||
"db/log_reader.cc",
|
||||
"db/log_writer.cc",
|
||||
"db/logs_with_prep_tracker.cc",
|
||||
"db/malloc_stats.cc",
|
||||
"db/managed_iterator.cc",
|
||||
"db/memtable.cc",
|
||||
@@ -215,6 +220,7 @@ cpp_library(
|
||||
"util/status_message.cc",
|
||||
"util/string_util.cc",
|
||||
"util/sync_point.cc",
|
||||
"util/sync_point_impl.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/transaction_test_util.cc",
|
||||
@@ -274,6 +280,8 @@ cpp_library(
|
||||
"utilities/transactions/transaction_util.cc",
|
||||
"utilities/transactions/write_prepared_txn.cc",
|
||||
"utilities/transactions/write_prepared_txn_db.cc",
|
||||
"utilities/transactions/write_unprepared_txn.cc",
|
||||
"utilities/transactions/write_unprepared_txn_db.cc",
|
||||
"utilities/ttl/db_ttl_impl.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
@@ -554,6 +562,11 @@ ROCKS_TESTS = [
|
||||
"db/db_iter_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"db_iter_stress_test",
|
||||
"db/db_iter_stress_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"db_iterator_test",
|
||||
"db/db_iterator_test.cc",
|
||||
@@ -824,6 +837,11 @@ ROCKS_TESTS = [
|
||||
"utilities/object_registry_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"obsolete_files_test",
|
||||
"db/obsolete_files_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"optimistic_transaction_test",
|
||||
"utilities/transactions/optimistic_transaction_test.cc",
|
||||
|
||||
@@ -29,6 +29,10 @@ rocksdb_compiler_flags = [
|
||||
"-DTBB",
|
||||
# Needed to compile in fbcode
|
||||
"-Wno-expansion-to-defined",
|
||||
# Added missing flags from output of build_detect_platform
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
"-Wshorten-64-to-32",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Detects OS we're compiling on and outputs a file specified by the first
|
||||
# argument, which in turn gets read while processing Makefile.
|
||||
@@ -66,11 +66,23 @@ rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
if test -z "$CC"; then
|
||||
CC=cc
|
||||
if [ -x "$(command -v cc)" ]; then
|
||||
CC=cc
|
||||
elif [ -x "$(command -v clang)" ]; then
|
||||
CC=clang
|
||||
else
|
||||
CC=cc
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$CXX"; then
|
||||
CXX=g++
|
||||
if [ -x "$(command -v g++)" ]; then
|
||||
CXX=g++
|
||||
elif [ -x "$(command -v clang++)" ]; then
|
||||
CXX=clang++
|
||||
else
|
||||
CXX=g++
|
||||
fi
|
||||
fi
|
||||
|
||||
# Detect OS
|
||||
@@ -87,7 +99,7 @@ if test -z "$CLANG_SCAN_BUILD"; then
|
||||
fi
|
||||
|
||||
if test -z "$CLANG_ANALYZER"; then
|
||||
CLANG_ANALYZER=$(which clang++ 2> /dev/null)
|
||||
CLANG_ANALYZER=$(command -v clang++ 2> /dev/null)
|
||||
fi
|
||||
|
||||
if test -z "$FIND"; then
|
||||
@@ -132,6 +144,8 @@ case "$TARGET_OS" in
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DOS_LINUX"
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
|
||||
else
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
@@ -184,6 +198,8 @@ case "$TARGET_OS" in
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DCYGWIN"
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
|
||||
else
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
@@ -498,9 +514,9 @@ 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 [ "$TARGET_OS" == IOS ]; then
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" != AIX ] && [ "$TARGET_OS" != SunOS ]; then
|
||||
elif [ "$TARGET_OS" != "AIX" ] && [ "$TARGET_OS" != "SunOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
elif test "$USE_SSE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse4.2 -mpclmul"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env bash
|
||||
if [ "$#" = "0" ]; then
|
||||
echo "Usage: $0 major|minor|patch|full"
|
||||
exit 1
|
||||
|
||||
Vendored
+1
-1
@@ -52,7 +52,7 @@ namespace rocksdb {
|
||||
|
||||
class CacheBench;
|
||||
namespace {
|
||||
void deleter(const Slice& key, void* value) {
|
||||
void deleter(const Slice& /*key*/, void* value) {
|
||||
delete reinterpret_cast<char *>(value);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+5
-3
@@ -13,8 +13,8 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
std::shared_ptr<Cache> NewClockCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit) {
|
||||
std::shared_ptr<Cache> NewClockCache(size_t /*capacity*/, int /*num_shard_bits*/,
|
||||
bool /*strict_capacity_limit*/) {
|
||||
// Clock cache not supported.
|
||||
return nullptr;
|
||||
}
|
||||
@@ -367,7 +367,9 @@ ClockCacheShard::~ClockCacheShard() {
|
||||
for (auto& handle : list_) {
|
||||
uint32_t flags = handle.flags.load(std::memory_order_relaxed);
|
||||
if (InCache(flags) || CountRefs(flags) > 0) {
|
||||
(*handle.deleter)(handle.key, handle.value);
|
||||
if (handle.deleter != nullptr) {
|
||||
(*handle.deleter)(handle.key, handle.value);
|
||||
}
|
||||
delete[] handle.key.data();
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+26
-25
@@ -99,14 +99,20 @@ void LRUHandleTable::Resize() {
|
||||
length_ = new_length;
|
||||
}
|
||||
|
||||
LRUCacheShard::LRUCacheShard()
|
||||
: capacity_(0), high_pri_pool_usage_(0), strict_capacity_limit_(false),
|
||||
high_pri_pool_ratio_(0), high_pri_pool_capacity_(0), usage_(0),
|
||||
LRUCacheShard::LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio)
|
||||
: capacity_(0),
|
||||
high_pri_pool_usage_(0),
|
||||
strict_capacity_limit_(strict_capacity_limit),
|
||||
high_pri_pool_ratio_(high_pri_pool_ratio),
|
||||
high_pri_pool_capacity_(0),
|
||||
usage_(0),
|
||||
lru_usage_(0) {
|
||||
// Make empty circular linked list
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
lru_low_pri_ = &lru_;
|
||||
SetCapacity(capacity);
|
||||
}
|
||||
|
||||
LRUCacheShard::~LRUCacheShard() {}
|
||||
@@ -193,7 +199,7 @@ void LRUCacheShard::LRU_Remove(LRUHandle* e) {
|
||||
void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
assert(e->next == nullptr);
|
||||
assert(e->prev == nullptr);
|
||||
if (high_pri_pool_ratio_ > 0 && e->IsHighPri()) {
|
||||
if (high_pri_pool_ratio_ > 0 && (e->IsHighPri() || e->HasHit())) {
|
||||
// Inset "e" to head of LRU list.
|
||||
e->next = &lru_;
|
||||
e->prev = lru_.prev;
|
||||
@@ -240,22 +246,6 @@ void LRUCacheShard::EvictFromLRU(size_t charge,
|
||||
}
|
||||
}
|
||||
|
||||
void* LRUCacheShard::operator new(size_t size) {
|
||||
return port::cacheline_aligned_alloc(size);
|
||||
}
|
||||
|
||||
void* LRUCacheShard::operator new[](size_t size) {
|
||||
return port::cacheline_aligned_alloc(size);
|
||||
}
|
||||
|
||||
void LRUCacheShard::operator delete(void *memblock) {
|
||||
port::cacheline_aligned_free(memblock);
|
||||
}
|
||||
|
||||
void LRUCacheShard::operator delete[](void* memblock) {
|
||||
port::cacheline_aligned_free(memblock);
|
||||
}
|
||||
|
||||
void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
@@ -285,6 +275,7 @@ Cache::Handle* LRUCacheShard::Lookup(const Slice& key, uint32_t hash) {
|
||||
LRU_Remove(e);
|
||||
}
|
||||
e->refs++;
|
||||
e->SetHit();
|
||||
}
|
||||
return reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
@@ -473,15 +464,24 @@ LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit, double high_pri_pool_ratio)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit) {
|
||||
num_shards_ = 1 << num_shard_bits;
|
||||
shards_ = new LRUCacheShard[num_shards_];
|
||||
SetCapacity(capacity);
|
||||
SetStrictCapacityLimit(strict_capacity_limit);
|
||||
shards_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard) * num_shards_));
|
||||
size_t per_shard = (capacity + (num_shards_ - 1)) / num_shards_;
|
||||
for (int i = 0; i < num_shards_; i++) {
|
||||
shards_[i].SetHighPriorityPoolRatio(high_pri_pool_ratio);
|
||||
new (&shards_[i])
|
||||
LRUCacheShard(per_shard, strict_capacity_limit, high_pri_pool_ratio);
|
||||
}
|
||||
}
|
||||
|
||||
LRUCache::~LRUCache() { delete[] shards_; }
|
||||
LRUCache::~LRUCache() {
|
||||
if (shards_ != nullptr) {
|
||||
assert(num_shards_ > 0);
|
||||
for (int i = 0; i < num_shards_; i++) {
|
||||
shards_[i].~LRUCacheShard();
|
||||
}
|
||||
port::cacheline_aligned_free(shards_);
|
||||
}
|
||||
}
|
||||
|
||||
CacheShard* LRUCache::GetShard(int shard) {
|
||||
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
||||
@@ -507,6 +507,7 @@ void LRUCache::DisownData() {
|
||||
// Do not drop data if compile with ASAN to suppress leak warning.
|
||||
#ifndef __SANITIZE_ADDRESS__
|
||||
shards_ = nullptr;
|
||||
num_shards_ = 0;
|
||||
#endif // !__SANITIZE_ADDRESS__
|
||||
}
|
||||
|
||||
|
||||
Vendored
+6
-11
@@ -77,6 +77,7 @@ struct LRUHandle {
|
||||
bool InCache() { return flags & 1; }
|
||||
bool IsHighPri() { return flags & 2; }
|
||||
bool InHighPriPool() { return flags & 4; }
|
||||
bool HasHit() { return flags & 8; }
|
||||
|
||||
void SetInCache(bool in_cache) {
|
||||
if (in_cache) {
|
||||
@@ -102,6 +103,8 @@ struct LRUHandle {
|
||||
}
|
||||
}
|
||||
|
||||
void SetHit() { flags |= 8; }
|
||||
|
||||
void Free() {
|
||||
assert((refs == 1 && InCache()) || (refs == 0 && !InCache()));
|
||||
if (deleter) {
|
||||
@@ -156,7 +159,8 @@ class LRUHandleTable {
|
||||
// A single shard of sharded cache.
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard {
|
||||
public:
|
||||
LRUCacheShard();
|
||||
LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio);
|
||||
virtual ~LRUCacheShard();
|
||||
|
||||
// Separate from constructor so caller can easily make an array of LRUCache
|
||||
@@ -205,15 +209,6 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard {
|
||||
// Retrives high pri pool ratio
|
||||
double GetHighPriPoolRatio();
|
||||
|
||||
// Overloading to aligned it to cache line size
|
||||
void* operator new(size_t);
|
||||
|
||||
void* operator new[](size_t);
|
||||
|
||||
void operator delete(void *);
|
||||
|
||||
void operator delete[](void*);
|
||||
|
||||
private:
|
||||
void LRU_Remove(LRUHandle* e);
|
||||
void LRU_Insert(LRUHandle* e);
|
||||
@@ -300,7 +295,7 @@ class LRUCache : public ShardedCache {
|
||||
double GetHighPriPoolRatio();
|
||||
|
||||
private:
|
||||
LRUCacheShard* shards_;
|
||||
LRUCacheShard* shards_ = nullptr;
|
||||
int num_shards_ = 0;
|
||||
};
|
||||
|
||||
|
||||
Vendored
+45
-21
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "port/port.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -14,22 +15,22 @@ namespace rocksdb {
|
||||
class LRUCacheTest : public testing::Test {
|
||||
public:
|
||||
LRUCacheTest() {}
|
||||
~LRUCacheTest() {}
|
||||
~LRUCacheTest() { DeleteCache(); }
|
||||
|
||||
void DeleteCache() {
|
||||
if (cache_ != nullptr) {
|
||||
cache_->~LRUCacheShard();
|
||||
port::cacheline_aligned_free(cache_);
|
||||
cache_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NewCache(size_t capacity, double high_pri_pool_ratio = 0.0) {
|
||||
cache_.reset(
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4316) // We've validated the alignment with the new operators
|
||||
#endif
|
||||
new LRUCacheShard()
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
);
|
||||
cache_->SetCapacity(capacity);
|
||||
cache_->SetStrictCapacityLimit(false);
|
||||
cache_->SetHighPriorityPoolRatio(high_pri_pool_ratio);
|
||||
DeleteCache();
|
||||
cache_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard)));
|
||||
new (cache_) LRUCacheShard(capacity, false /*strict_capcity_limit*/,
|
||||
high_pri_pool_ratio);
|
||||
}
|
||||
|
||||
void Insert(const std::string& key,
|
||||
@@ -85,7 +86,7 @@ class LRUCacheTest : public testing::Test {
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<LRUCacheShard> cache_;
|
||||
LRUCacheShard* cache_ = nullptr;
|
||||
};
|
||||
|
||||
TEST_F(LRUCacheTest, BasicLRU) {
|
||||
@@ -114,7 +115,30 @@ TEST_F(LRUCacheTest, BasicLRU) {
|
||||
ValidateLRUList({"e", "z", "d", "u", "v"});
|
||||
}
|
||||
|
||||
TEST_F(LRUCacheTest, MidPointInsertion) {
|
||||
TEST_F(LRUCacheTest, MidpointInsertion) {
|
||||
// Allocate 2 cache entries to high-pri pool.
|
||||
NewCache(5, 0.45);
|
||||
|
||||
Insert("a", Cache::Priority::LOW);
|
||||
Insert("b", Cache::Priority::LOW);
|
||||
Insert("c", Cache::Priority::LOW);
|
||||
Insert("x", Cache::Priority::HIGH);
|
||||
Insert("y", Cache::Priority::HIGH);
|
||||
ValidateLRUList({"a", "b", "c", "x", "y"}, 2);
|
||||
|
||||
// Low-pri entries inserted to the tail of low-pri list (the midpoint).
|
||||
// After lookup, it will move to the tail of the full list.
|
||||
Insert("d", Cache::Priority::LOW);
|
||||
ValidateLRUList({"b", "c", "d", "x", "y"}, 2);
|
||||
ASSERT_TRUE(Lookup("d"));
|
||||
ValidateLRUList({"b", "c", "x", "y", "d"}, 2);
|
||||
|
||||
// High-pri entries will be inserted to the tail of full list.
|
||||
Insert("z", Cache::Priority::HIGH);
|
||||
ValidateLRUList({"c", "x", "y", "d", "z"}, 2);
|
||||
}
|
||||
|
||||
TEST_F(LRUCacheTest, EntriesWithPriority) {
|
||||
// Allocate 2 cache entries to high-pri pool.
|
||||
NewCache(5, 0.45);
|
||||
|
||||
@@ -140,15 +164,15 @@ TEST_F(LRUCacheTest, MidPointInsertion) {
|
||||
Insert("a", Cache::Priority::LOW);
|
||||
ValidateLRUList({"v", "X", "a", "Y", "Z"}, 2);
|
||||
|
||||
// Low-pri entries will be inserted to head of low-pri pool after lookup.
|
||||
// Low-pri entries will be inserted to head of high-pri pool after lookup.
|
||||
ASSERT_TRUE(Lookup("v"));
|
||||
ValidateLRUList({"X", "a", "v", "Y", "Z"}, 2);
|
||||
ValidateLRUList({"X", "a", "Y", "Z", "v"}, 2);
|
||||
|
||||
// High-pri entries will be inserted to the head of the list after lookup.
|
||||
ASSERT_TRUE(Lookup("X"));
|
||||
ValidateLRUList({"a", "v", "Y", "Z", "X"}, 2);
|
||||
ValidateLRUList({"a", "Y", "Z", "v", "X"}, 2);
|
||||
ASSERT_TRUE(Lookup("Z"));
|
||||
ValidateLRUList({"a", "v", "Y", "X", "Z"}, 2);
|
||||
ValidateLRUList({"a", "Y", "v", "X", "Z"}, 2);
|
||||
|
||||
Erase("Y");
|
||||
ValidateLRUList({"a", "v", "X", "Z"}, 2);
|
||||
@@ -161,7 +185,7 @@ TEST_F(LRUCacheTest, MidPointInsertion) {
|
||||
Insert("g", Cache::Priority::LOW);
|
||||
ValidateLRUList({"d", "e", "f", "g", "Z"}, 1);
|
||||
ASSERT_TRUE(Lookup("d"));
|
||||
ValidateLRUList({"e", "f", "g", "d", "Z"}, 1);
|
||||
ValidateLRUList({"e", "f", "g", "Z", "d"}, 2);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -5,13 +5,17 @@
|
||||
# TBB_LIBRARIES - List of libraries when using TBB.
|
||||
# TBB_FOUND - True if TBB found.
|
||||
|
||||
if(NOT DEFINED TBB_ROOT_DIR)
|
||||
set(TBB_ROOT_DIR "$ENV{TBBROOT}")
|
||||
endif()
|
||||
|
||||
find_path(TBB_INCLUDE_DIR
|
||||
NAMES tbb/tbb.h
|
||||
HINTS ${TBB_ROOT_DIR}/include)
|
||||
|
||||
find_library(TBB_LIBRARIES
|
||||
NAMES tbb
|
||||
HINTS ${TBB_ROOT_DIR}/lib)
|
||||
HINTS ${TBB_ROOT_DIR}/lib ENV LIBRARY_PATH)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(TBB DEFAULT_MSG TBB_LIBRARIES TBB_INCLUDE_DIR)
|
||||
|
||||
+18
-15
@@ -39,7 +39,7 @@ namespace rocksdb {
|
||||
class TableFactory;
|
||||
|
||||
TableBuilder* NewTableBuilder(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const ImmutableCFOptions& ioptions, const MutableCFOptions& moptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
@@ -52,19 +52,20 @@ TableBuilder* NewTableBuilder(
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
return ioptions.table_factory->NewTableBuilder(
|
||||
TableBuilderOptions(
|
||||
ioptions, internal_comparator, int_tbl_prop_collector_factories,
|
||||
compression_type, compression_opts, compression_dict, skip_filters,
|
||||
column_family_name, level, creation_time, oldest_key_time),
|
||||
TableBuilderOptions(ioptions, moptions, internal_comparator,
|
||||
int_tbl_prop_collector_factories, compression_type,
|
||||
compression_opts, compression_dict, skip_filters,
|
||||
column_family_name, level, creation_time,
|
||||
oldest_key_time),
|
||||
column_family_id, file);
|
||||
}
|
||||
|
||||
Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
const EnvOptions& env_options, TableCache* table_cache,
|
||||
InternalIterator* iter, std::unique_ptr<InternalIterator> range_del_iter,
|
||||
FileMetaData* meta, const InternalKeyComparator& internal_comparator,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::unique_ptr<InternalIterator> range_del_iter, FileMetaData* meta,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
@@ -92,7 +93,7 @@ Status BuildTable(
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string fname = TableFileName(ioptions.db_paths, meta->fd.GetNumber(),
|
||||
std::string fname = TableFileName(ioptions.cf_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
#ifndef ROCKSDB_LITE
|
||||
EventHelpers::NotifyTableFileCreationStarted(
|
||||
@@ -122,10 +123,11 @@ Status BuildTable(
|
||||
file_writer.reset(new WritableFileWriter(std::move(file), env_options,
|
||||
ioptions.statistics));
|
||||
builder = NewTableBuilder(
|
||||
ioptions, internal_comparator, int_tbl_prop_collector_factories,
|
||||
column_family_id, column_family_name, file_writer.get(), compression,
|
||||
compression_opts, level, nullptr /* compression_dict */,
|
||||
false /* skip_filters */, creation_time, oldest_key_time);
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
int_tbl_prop_collector_factories, column_family_id,
|
||||
column_family_name, file_writer.get(), compression, compression_opts,
|
||||
level, nullptr /* compression_dict */, false /* skip_filters */,
|
||||
creation_time, oldest_key_time);
|
||||
}
|
||||
|
||||
MergeHelper merge(env, internal_comparator.user_comparator(),
|
||||
@@ -195,7 +197,8 @@ Status BuildTable(
|
||||
// to cache it here for further user reads
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
ReadOptions(), env_options, internal_comparator, meta->fd,
|
||||
nullptr /* range_del_agg */, nullptr,
|
||||
nullptr /* range_del_agg */,
|
||||
mutable_cf_options.prefix_extractor.get(), nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
false /* for_compaction */, nullptr /* arena */,
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ class InternalIterator;
|
||||
// @param compression_dict Data for presetting the compression library's
|
||||
// dictionary, or nullptr.
|
||||
TableBuilder* NewTableBuilder(
|
||||
const ImmutableCFOptions& options,
|
||||
const ImmutableCFOptions& options, const MutableCFOptions& moptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using rocksdb::BytewiseComparator;
|
||||
@@ -89,6 +90,7 @@ using rocksdb::LiveFileMetaData;
|
||||
using rocksdb::BackupEngine;
|
||||
using rocksdb::BackupableDBOptions;
|
||||
using rocksdb::BackupInfo;
|
||||
using rocksdb::BackupID;
|
||||
using rocksdb::RestoreOptions;
|
||||
using rocksdb::CompactRangeOptions;
|
||||
using rocksdb::RateLimiter;
|
||||
@@ -101,6 +103,10 @@ using rocksdb::OptimisticTransactionDB;
|
||||
using rocksdb::OptimisticTransactionOptions;
|
||||
using rocksdb::Transaction;
|
||||
using rocksdb::Checkpoint;
|
||||
using rocksdb::TransactionLogIterator;
|
||||
using rocksdb::BatchResult;
|
||||
using rocksdb::PerfLevel;
|
||||
using rocksdb::PerfContext;
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
@@ -132,6 +138,8 @@ struct rocksdb_cuckoo_table_options_t { CuckooTableOptions rep; };
|
||||
struct rocksdb_seqfile_t { SequentialFile* rep; };
|
||||
struct rocksdb_randomfile_t { RandomAccessFile* rep; };
|
||||
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 { shared_ptr<Logger> rep; };
|
||||
struct rocksdb_cache_t { shared_ptr<Cache> rep; };
|
||||
@@ -140,7 +148,8 @@ 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 { RateLimiter* rep; };
|
||||
struct rocksdb_ratelimiter_t { shared_ptr<RateLimiter> rep; };
|
||||
struct rocksdb_perfcontext_t { PerfContext* rep; };
|
||||
struct rocksdb_pinnableslice_t {
|
||||
PinnableSlice rep;
|
||||
};
|
||||
@@ -523,10 +532,18 @@ rocksdb_backup_engine_t* rocksdb_backup_engine_open(
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_create_new_backup(rocksdb_backup_engine_t* be,
|
||||
rocksdb_t* db, char** errptr) {
|
||||
rocksdb_t* db,
|
||||
char** errptr) {
|
||||
SaveError(errptr, be->rep->CreateNewBackup(db->rep));
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_create_new_backup_flush(rocksdb_backup_engine_t* be,
|
||||
rocksdb_t* db,
|
||||
unsigned char flush_before_backup,
|
||||
char** errptr) {
|
||||
SaveError(errptr, be->rep->CreateNewBackup(db->rep, flush_before_backup));
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_purge_old_backups(rocksdb_backup_engine_t* be,
|
||||
uint32_t num_backups_to_keep,
|
||||
char** errptr) {
|
||||
@@ -546,6 +563,12 @@ void rocksdb_restore_options_set_keep_log_files(rocksdb_restore_options_t* opt,
|
||||
opt->rep.keep_log_files = v;
|
||||
}
|
||||
|
||||
|
||||
void rocksdb_backup_engine_verify_backup(rocksdb_backup_engine_t* be,
|
||||
uint32_t backup_id, char** errptr) {
|
||||
SaveError(errptr, be->rep->VerifyBackup(static_cast<BackupID>(backup_id)));
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_restore_db_from_latest_backup(
|
||||
rocksdb_backup_engine_t* be, const char* db_dir, const char* wal_dir,
|
||||
const rocksdb_restore_options_t* restore_options, char** errptr) {
|
||||
@@ -926,6 +949,54 @@ rocksdb_iterator_t* rocksdb_create_iterator(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_wal_iterator_t* rocksdb_get_updates_since(
|
||||
rocksdb_t* db, uint64_t seq_number,
|
||||
const rocksdb_wal_readoptions_t* options,
|
||||
char** errptr) {
|
||||
std::unique_ptr<TransactionLogIterator> iter;
|
||||
TransactionLogIterator::ReadOptions ro;
|
||||
if (options!=nullptr) {
|
||||
ro = options->rep;
|
||||
}
|
||||
if (SaveError(errptr, db->rep->GetUpdatesSince(seq_number, &iter, ro))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_wal_iterator_t* result = new rocksdb_wal_iterator_t;
|
||||
result->rep = iter.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_wal_iter_next(rocksdb_wal_iterator_t* iter) {
|
||||
iter->rep->Next();
|
||||
}
|
||||
|
||||
unsigned char rocksdb_wal_iter_valid(const rocksdb_wal_iterator_t* iter) {
|
||||
return iter->rep->Valid();
|
||||
}
|
||||
|
||||
void rocksdb_wal_iter_status (const rocksdb_wal_iterator_t* iter, char** errptr) {
|
||||
SaveError(errptr, iter->rep->status());
|
||||
}
|
||||
|
||||
void rocksdb_wal_iter_destroy (const rocksdb_wal_iterator_t* iter) {
|
||||
delete iter->rep;
|
||||
delete iter;
|
||||
}
|
||||
|
||||
rocksdb_writebatch_t* rocksdb_wal_iter_get_batch (const rocksdb_wal_iterator_t* iter, uint64_t* seq) {
|
||||
rocksdb_writebatch_t* result = rocksdb_writebatch_create();
|
||||
BatchResult wal_batch = iter->rep->GetBatch();
|
||||
result->rep = * wal_batch.writeBatchPtr.release();
|
||||
if (seq != nullptr) {
|
||||
*seq = wal_batch.sequence;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_get_latest_sequence_number (rocksdb_t *db) {
|
||||
return db->rep->GetLatestSequenceNumber();
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_create_iterator_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
@@ -1403,23 +1474,24 @@ void rocksdb_writebatch_put_log_data(
|
||||
b->rep.PutLogData(Slice(blob, len));
|
||||
}
|
||||
|
||||
class H : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
|
||||
void rocksdb_writebatch_iterate(
|
||||
rocksdb_writebatch_t* b,
|
||||
void* state,
|
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
|
||||
void (*deleted)(void*, const char* k, size_t klen)) {
|
||||
class H : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
H handler;
|
||||
handler.state_ = state;
|
||||
handler.put_ = put;
|
||||
@@ -1664,18 +1736,6 @@ void rocksdb_writebatch_wi_iterate(
|
||||
void* state,
|
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
|
||||
void (*deleted)(void*, const char* k, size_t klen)) {
|
||||
class H : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
H handler;
|
||||
handler.state_ = state;
|
||||
handler.put_ = put;
|
||||
@@ -2531,8 +2591,9 @@ char *rocksdb_options_statistics_get_string(rocksdb_options_t *opt) {
|
||||
}
|
||||
|
||||
void rocksdb_options_set_ratelimiter(rocksdb_options_t *opt, rocksdb_ratelimiter_t *limiter) {
|
||||
opt->rep.rate_limiter.reset(limiter->rep);
|
||||
limiter->rep = nullptr;
|
||||
if (limiter) {
|
||||
opt->rep.rate_limiter = limiter->rep;
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_ratelimiter_t* rocksdb_ratelimiter_create(
|
||||
@@ -2540,18 +2601,186 @@ rocksdb_ratelimiter_t* rocksdb_ratelimiter_create(
|
||||
int64_t refill_period_us,
|
||||
int32_t fairness) {
|
||||
rocksdb_ratelimiter_t* rate_limiter = new rocksdb_ratelimiter_t;
|
||||
rate_limiter->rep = NewGenericRateLimiter(rate_bytes_per_sec,
|
||||
refill_period_us, fairness);
|
||||
rate_limiter->rep.reset(
|
||||
NewGenericRateLimiter(rate_bytes_per_sec,
|
||||
refill_period_us, fairness));
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
void rocksdb_ratelimiter_destroy(rocksdb_ratelimiter_t *limiter) {
|
||||
if (limiter->rep) {
|
||||
delete limiter->rep;
|
||||
}
|
||||
delete limiter;
|
||||
}
|
||||
|
||||
void rocksdb_set_perf_level(int v) {
|
||||
PerfLevel level = static_cast<PerfLevel>(v);
|
||||
SetPerfLevel(level);
|
||||
}
|
||||
|
||||
rocksdb_perfcontext_t* rocksdb_perfcontext_create() {
|
||||
rocksdb_perfcontext_t* context = new rocksdb_perfcontext_t;
|
||||
context->rep = rocksdb::get_perf_context();
|
||||
return context;
|
||||
}
|
||||
|
||||
void rocksdb_perfcontext_reset(rocksdb_perfcontext_t* context) {
|
||||
context->rep->Reset();
|
||||
}
|
||||
|
||||
char* rocksdb_perfcontext_report(rocksdb_perfcontext_t* context,
|
||||
unsigned char exclude_zero_counters) {
|
||||
return strdup(context->rep->ToString(exclude_zero_counters).c_str());
|
||||
}
|
||||
|
||||
uint64_t rocksdb_perfcontext_metric(rocksdb_perfcontext_t* context,
|
||||
int metric) {
|
||||
PerfContext* rep = context->rep;
|
||||
switch (metric) {
|
||||
case rocksdb_user_key_comparison_count:
|
||||
return rep->user_key_comparison_count;
|
||||
case rocksdb_block_cache_hit_count:
|
||||
return rep->block_cache_hit_count;
|
||||
case rocksdb_block_read_count:
|
||||
return rep->block_read_count;
|
||||
case rocksdb_block_read_byte:
|
||||
return rep->block_read_byte;
|
||||
case rocksdb_block_read_time:
|
||||
return rep->block_read_time;
|
||||
case rocksdb_block_checksum_time:
|
||||
return rep->block_checksum_time;
|
||||
case rocksdb_block_decompress_time:
|
||||
return rep->block_decompress_time;
|
||||
case rocksdb_get_read_bytes:
|
||||
return rep->get_read_bytes;
|
||||
case rocksdb_multiget_read_bytes:
|
||||
return rep->multiget_read_bytes;
|
||||
case rocksdb_iter_read_bytes:
|
||||
return rep->iter_read_bytes;
|
||||
case rocksdb_internal_key_skipped_count:
|
||||
return rep->internal_key_skipped_count;
|
||||
case rocksdb_internal_delete_skipped_count:
|
||||
return rep->internal_delete_skipped_count;
|
||||
case rocksdb_internal_recent_skipped_count:
|
||||
return rep->internal_recent_skipped_count;
|
||||
case rocksdb_internal_merge_count:
|
||||
return rep->internal_merge_count;
|
||||
case rocksdb_get_snapshot_time:
|
||||
return rep->get_snapshot_time;
|
||||
case rocksdb_get_from_memtable_time:
|
||||
return rep->get_from_memtable_time;
|
||||
case rocksdb_get_from_memtable_count:
|
||||
return rep->get_from_memtable_count;
|
||||
case rocksdb_get_post_process_time:
|
||||
return rep->get_post_process_time;
|
||||
case rocksdb_get_from_output_files_time:
|
||||
return rep->get_from_output_files_time;
|
||||
case rocksdb_seek_on_memtable_time:
|
||||
return rep->seek_on_memtable_time;
|
||||
case rocksdb_seek_on_memtable_count:
|
||||
return rep->seek_on_memtable_count;
|
||||
case rocksdb_next_on_memtable_count:
|
||||
return rep->next_on_memtable_count;
|
||||
case rocksdb_prev_on_memtable_count:
|
||||
return rep->prev_on_memtable_count;
|
||||
case rocksdb_seek_child_seek_time:
|
||||
return rep->seek_child_seek_time;
|
||||
case rocksdb_seek_child_seek_count:
|
||||
return rep->seek_child_seek_count;
|
||||
case rocksdb_seek_min_heap_time:
|
||||
return rep->seek_min_heap_time;
|
||||
case rocksdb_seek_max_heap_time:
|
||||
return rep->seek_max_heap_time;
|
||||
case rocksdb_seek_internal_seek_time:
|
||||
return rep->seek_internal_seek_time;
|
||||
case rocksdb_find_next_user_entry_time:
|
||||
return rep->find_next_user_entry_time;
|
||||
case rocksdb_write_wal_time:
|
||||
return rep->write_wal_time;
|
||||
case rocksdb_write_memtable_time:
|
||||
return rep->write_memtable_time;
|
||||
case rocksdb_write_delay_time:
|
||||
return rep->write_delay_time;
|
||||
case rocksdb_write_pre_and_post_process_time:
|
||||
return rep->write_pre_and_post_process_time;
|
||||
case rocksdb_db_mutex_lock_nanos:
|
||||
return rep->db_mutex_lock_nanos;
|
||||
case rocksdb_db_condition_wait_nanos:
|
||||
return rep->db_condition_wait_nanos;
|
||||
case rocksdb_merge_operator_time_nanos:
|
||||
return rep->merge_operator_time_nanos;
|
||||
case rocksdb_read_index_block_nanos:
|
||||
return rep->read_index_block_nanos;
|
||||
case rocksdb_read_filter_block_nanos:
|
||||
return rep->read_filter_block_nanos;
|
||||
case rocksdb_new_table_block_iter_nanos:
|
||||
return rep->new_table_block_iter_nanos;
|
||||
case rocksdb_new_table_iterator_nanos:
|
||||
return rep->new_table_iterator_nanos;
|
||||
case rocksdb_block_seek_nanos:
|
||||
return rep->block_seek_nanos;
|
||||
case rocksdb_find_table_nanos:
|
||||
return rep->find_table_nanos;
|
||||
case rocksdb_bloom_memtable_hit_count:
|
||||
return rep->bloom_memtable_hit_count;
|
||||
case rocksdb_bloom_memtable_miss_count:
|
||||
return rep->bloom_memtable_miss_count;
|
||||
case rocksdb_bloom_sst_hit_count:
|
||||
return rep->bloom_sst_hit_count;
|
||||
case rocksdb_bloom_sst_miss_count:
|
||||
return rep->bloom_sst_miss_count;
|
||||
case rocksdb_key_lock_wait_time:
|
||||
return rep->key_lock_wait_time;
|
||||
case rocksdb_key_lock_wait_count:
|
||||
return rep->key_lock_wait_count;
|
||||
case rocksdb_env_new_sequential_file_nanos:
|
||||
return rep->env_new_sequential_file_nanos;
|
||||
case rocksdb_env_new_random_access_file_nanos:
|
||||
return rep->env_new_random_access_file_nanos;
|
||||
case rocksdb_env_new_writable_file_nanos:
|
||||
return rep->env_new_writable_file_nanos;
|
||||
case rocksdb_env_reuse_writable_file_nanos:
|
||||
return rep->env_reuse_writable_file_nanos;
|
||||
case rocksdb_env_new_random_rw_file_nanos:
|
||||
return rep->env_new_random_rw_file_nanos;
|
||||
case rocksdb_env_new_directory_nanos:
|
||||
return rep->env_new_directory_nanos;
|
||||
case rocksdb_env_file_exists_nanos:
|
||||
return rep->env_file_exists_nanos;
|
||||
case rocksdb_env_get_children_nanos:
|
||||
return rep->env_get_children_nanos;
|
||||
case rocksdb_env_get_children_file_attributes_nanos:
|
||||
return rep->env_get_children_file_attributes_nanos;
|
||||
case rocksdb_env_delete_file_nanos:
|
||||
return rep->env_delete_file_nanos;
|
||||
case rocksdb_env_create_dir_nanos:
|
||||
return rep->env_create_dir_nanos;
|
||||
case rocksdb_env_create_dir_if_missing_nanos:
|
||||
return rep->env_create_dir_if_missing_nanos;
|
||||
case rocksdb_env_delete_dir_nanos:
|
||||
return rep->env_delete_dir_nanos;
|
||||
case rocksdb_env_get_file_size_nanos:
|
||||
return rep->env_get_file_size_nanos;
|
||||
case rocksdb_env_get_file_modification_time_nanos:
|
||||
return rep->env_get_file_modification_time_nanos;
|
||||
case rocksdb_env_rename_file_nanos:
|
||||
return rep->env_rename_file_nanos;
|
||||
case rocksdb_env_link_file_nanos:
|
||||
return rep->env_link_file_nanos;
|
||||
case rocksdb_env_lock_file_nanos:
|
||||
return rep->env_lock_file_nanos;
|
||||
case rocksdb_env_unlock_file_nanos:
|
||||
return rep->env_unlock_file_nanos;
|
||||
case rocksdb_env_new_logger_nanos:
|
||||
return rep->env_new_logger_nanos;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rocksdb_perfcontext_destroy(rocksdb_perfcontext_t* context) {
|
||||
delete context;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO:
|
||||
DB::OpenForReadOnly
|
||||
@@ -3124,20 +3353,21 @@ void rocksdb_slicetransform_destroy(rocksdb_slicetransform_t* st) {
|
||||
delete st;
|
||||
}
|
||||
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
}
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return rep_->InDomain(src);
|
||||
}
|
||||
bool InRange(const Slice& src) const override { return rep_->InRange(src); }
|
||||
static void DoNothing(void*) { }
|
||||
};
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create_fixed_prefix(size_t prefixLen) {
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
}
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return rep_->InDomain(src);
|
||||
}
|
||||
bool InRange(const Slice& src) const override { return rep_->InRange(src); }
|
||||
static void DoNothing(void*) { }
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
wrapper->rep_ = rocksdb::NewFixedPrefixTransform(prefixLen);
|
||||
wrapper->state_ = nullptr;
|
||||
@@ -3146,19 +3376,6 @@ rocksdb_slicetransform_t* rocksdb_slicetransform_create_fixed_prefix(size_t pref
|
||||
}
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create_noop() {
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
}
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return rep_->InDomain(src);
|
||||
}
|
||||
bool InRange(const Slice& src) const override { return rep_->InRange(src); }
|
||||
static void DoNothing(void*) { }
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
wrapper->rep_ = rocksdb::NewNoopTransform();
|
||||
wrapper->state_ = nullptr;
|
||||
|
||||
+46
-7
@@ -192,10 +192,11 @@ static void CheckDel(void* ptr, const char* k, size_t klen) {
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
static void CmpDestroy(void* arg) { }
|
||||
static void CmpDestroy(void* arg) { (void)arg; }
|
||||
|
||||
static int CmpCompare(void* arg, const char* a, size_t alen,
|
||||
const char* b, size_t blen) {
|
||||
(void)arg;
|
||||
size_t n = (alen < blen) ? alen : blen;
|
||||
int r = memcmp(a, b, n);
|
||||
if (r == 0) {
|
||||
@@ -206,13 +207,15 @@ static int CmpCompare(void* arg, const char* a, size_t alen,
|
||||
}
|
||||
|
||||
static const char* CmpName(void* arg) {
|
||||
(void)arg;
|
||||
return "foo";
|
||||
}
|
||||
|
||||
// Custom filter policy
|
||||
static unsigned char fake_filter_result = 1;
|
||||
static void FilterDestroy(void* arg) { }
|
||||
static void FilterDestroy(void* arg) { (void)arg; }
|
||||
static const char* FilterName(void* arg) {
|
||||
(void)arg;
|
||||
return "TestFilter";
|
||||
}
|
||||
static char* FilterCreate(
|
||||
@@ -220,6 +223,10 @@ static char* FilterCreate(
|
||||
const char* const* key_array, const size_t* key_length_array,
|
||||
int num_keys,
|
||||
size_t* filter_length) {
|
||||
(void)arg;
|
||||
(void)key_array;
|
||||
(void)key_length_array;
|
||||
(void)num_keys;
|
||||
*filter_length = 4;
|
||||
char* result = malloc(4);
|
||||
memcpy(result, "fake", 4);
|
||||
@@ -229,20 +236,30 @@ static unsigned char FilterKeyMatch(
|
||||
void* arg,
|
||||
const char* key, size_t length,
|
||||
const char* filter, size_t filter_length) {
|
||||
(void)arg;
|
||||
(void)key;
|
||||
(void)length;
|
||||
CheckCondition(filter_length == 4);
|
||||
CheckCondition(memcmp(filter, "fake", 4) == 0);
|
||||
return fake_filter_result;
|
||||
}
|
||||
|
||||
// Custom compaction filter
|
||||
static void CFilterDestroy(void* arg) {}
|
||||
static const char* CFilterName(void* arg) { return "foo"; }
|
||||
static void CFilterDestroy(void* arg) { (void)arg; }
|
||||
static const char* CFilterName(void* arg) {
|
||||
(void)arg;
|
||||
return "foo";
|
||||
}
|
||||
static unsigned char CFilterFilter(void* arg, int level, const char* key,
|
||||
size_t key_length,
|
||||
const char* existing_value,
|
||||
size_t value_length, char** new_value,
|
||||
size_t* new_value_length,
|
||||
unsigned char* value_changed) {
|
||||
(void)arg;
|
||||
(void)level;
|
||||
(void)existing_value;
|
||||
(void)value_length;
|
||||
if (key_length == 3) {
|
||||
if (memcmp(key, "bar", key_length) == 0) {
|
||||
return 1;
|
||||
@@ -256,10 +273,15 @@ static unsigned char CFilterFilter(void* arg, int level, const char* key,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void CFilterFactoryDestroy(void* arg) {}
|
||||
static const char* CFilterFactoryName(void* arg) { return "foo"; }
|
||||
static void CFilterFactoryDestroy(void* arg) { (void)arg; }
|
||||
static const char* CFilterFactoryName(void* arg) {
|
||||
(void)arg;
|
||||
return "foo";
|
||||
}
|
||||
static rocksdb_compactionfilter_t* CFilterCreate(
|
||||
void* arg, rocksdb_compactionfiltercontext_t* context) {
|
||||
(void)arg;
|
||||
(void)context;
|
||||
return rocksdb_compactionfilter_create(NULL, CFilterDestroy, CFilterFilter,
|
||||
CFilterName);
|
||||
}
|
||||
@@ -290,8 +312,9 @@ static rocksdb_t* CheckCompaction(rocksdb_t* db, rocksdb_options_t* options,
|
||||
}
|
||||
|
||||
// Custom merge operator
|
||||
static void MergeOperatorDestroy(void* arg) { }
|
||||
static void MergeOperatorDestroy(void* arg) { (void)arg; }
|
||||
static const char* MergeOperatorName(void* arg) {
|
||||
(void)arg;
|
||||
return "TestMergeOperator";
|
||||
}
|
||||
static char* MergeOperatorFullMerge(
|
||||
@@ -301,6 +324,14 @@ static char* MergeOperatorFullMerge(
|
||||
const char* const* operands_list, const size_t* operands_list_length,
|
||||
int num_operands,
|
||||
unsigned char* success, size_t* new_value_length) {
|
||||
(void)arg;
|
||||
(void)key;
|
||||
(void)key_length;
|
||||
(void)existing_value;
|
||||
(void)existing_value_length;
|
||||
(void)operands_list;
|
||||
(void)operands_list_length;
|
||||
(void)num_operands;
|
||||
*new_value_length = 4;
|
||||
*success = 1;
|
||||
char* result = malloc(4);
|
||||
@@ -313,6 +344,12 @@ static char* MergeOperatorPartialMerge(
|
||||
const char* const* operands_list, const size_t* operands_list_length,
|
||||
int num_operands,
|
||||
unsigned char* success, size_t* new_value_length) {
|
||||
(void)arg;
|
||||
(void)key;
|
||||
(void)key_length;
|
||||
(void)operands_list;
|
||||
(void)operands_list_length;
|
||||
(void)num_operands;
|
||||
*new_value_length = 4;
|
||||
*success = 1;
|
||||
char* result = malloc(4);
|
||||
@@ -377,6 +414,8 @@ static void CheckTxnDBGetCF(rocksdb_transactiondb_t* txn_db,
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
rocksdb_t* db;
|
||||
rocksdb_comparator_t* cmp;
|
||||
rocksdb_cache_t* cache;
|
||||
|
||||
+84
-12
@@ -34,6 +34,7 @@
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -54,6 +55,9 @@ ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
#endif // ROCKSDB_LITE
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
// Need to hold some shared pointers owned by the initial_cf_options
|
||||
// before final cleaning up finishes.
|
||||
ColumnFamilyOptions initial_cf_options_copy = cfd_->initial_cf_options();
|
||||
JobContext job_context(0);
|
||||
mutex_->Lock();
|
||||
if (cfd_->Unref()) {
|
||||
@@ -81,6 +85,7 @@ Status ColumnFamilyHandleImpl::GetDescriptor(ColumnFamilyDescriptor* desc) {
|
||||
*desc = ColumnFamilyDescriptor(cfd()->GetName(), cfd()->GetLatestCFOptions());
|
||||
return Status::OK();
|
||||
#else
|
||||
(void)desc;
|
||||
return Status::NotSupported();
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
@@ -156,6 +161,28 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options) {
|
||||
// More than one cf_paths are supported only in universal
|
||||
// and level compaction styles. This function also checks the case
|
||||
// in which cf_paths is not specified, which results in db_paths
|
||||
// being used.
|
||||
if ((cf_options.compaction_style != kCompactionStyleUniversal) &&
|
||||
(cf_options.compaction_style != kCompactionStyleLevel)) {
|
||||
if (cf_options.cf_paths.size() > 1) {
|
||||
return Status::NotSupported(
|
||||
"More than one CF paths are only supported in "
|
||||
"universal and level compaction styles. ");
|
||||
} else if (cf_options.cf_paths.empty() &&
|
||||
db_options.db_paths.size() > 1) {
|
||||
return Status::NotSupported(
|
||||
"More than one DB paths are only supported in "
|
||||
"universal and level compaction styles. ");
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions result = src;
|
||||
@@ -274,9 +301,24 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
result.hard_pending_compaction_bytes_limit;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// When the DB is stopped, it's possible that there are some .trash files that
|
||||
// were not deleted yet, when we open the DB we will find these .trash files
|
||||
// and schedule them to be deleted (or delete immediately if SstFileManager
|
||||
// was not used)
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(db_options.sst_file_manager.get());
|
||||
for (size_t i = 0; i < result.cf_paths.size(); i++) {
|
||||
DeleteScheduler::CleanupDirectory(db_options.env, sfm, result.cf_paths[i].path);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (result.cf_paths.empty()) {
|
||||
result.cf_paths = db_options.db_paths;
|
||||
}
|
||||
|
||||
if (result.level_compaction_dynamic_level_bytes) {
|
||||
if (result.compaction_style != kCompactionStyleLevel ||
|
||||
db_options.db_paths.size() > 1U) {
|
||||
result.cf_paths.size() > 1U) {
|
||||
// 1. level_compaction_dynamic_level_bytes only makes sense for
|
||||
// level-based compaction.
|
||||
// 2. we don't yet know how to make both of this feature and multiple
|
||||
@@ -383,10 +425,10 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
next_(nullptr),
|
||||
prev_(nullptr),
|
||||
log_number_(0),
|
||||
flush_reason_(FlushReason::kUnknown),
|
||||
flush_reason_(FlushReason::kOthers),
|
||||
column_family_set_(column_family_set),
|
||||
pending_flush_(false),
|
||||
pending_compaction_(false),
|
||||
queued_for_flush_(false),
|
||||
queued_for_compaction_(false),
|
||||
prev_compaction_needed_bytes_(0),
|
||||
allow_2pc_(db_options.allow_2pc),
|
||||
last_memtable_id_(0) {
|
||||
@@ -462,8 +504,8 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
|
||||
// It would be wrong if this ColumnFamilyData is in flush_queue_ or
|
||||
// compaction_queue_ and we destroyed it
|
||||
assert(!pending_flush_);
|
||||
assert(!pending_compaction_);
|
||||
assert(!queued_for_flush_);
|
||||
assert(!queued_for_compaction_);
|
||||
|
||||
if (super_version_ != nullptr) {
|
||||
// Release SuperVersion reference kept in ThreadLocalPtr.
|
||||
@@ -516,7 +558,9 @@ uint64_t ColumnFamilyData::OldestLogToKeep() {
|
||||
auto current_log = GetLogNumber();
|
||||
|
||||
if (allow_2pc_) {
|
||||
auto imm_prep_log = imm()->GetMinLogContainingPrepSection();
|
||||
autovector<MemTable*> empty_list;
|
||||
auto imm_prep_log =
|
||||
imm()->PrecomputeMinLogContainingPrepSection(empty_list);
|
||||
auto mem_prep_log = mem()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (imm_prep_log > 0 && imm_prep_log < current_log) {
|
||||
@@ -953,11 +997,13 @@ const int ColumnFamilyData::kCompactToBaseLevel = -2;
|
||||
|
||||
Compaction* ColumnFamilyData::CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options, int input_level,
|
||||
int output_level, uint32_t output_path_id, const InternalKey* begin,
|
||||
const InternalKey* end, InternalKey** compaction_end, bool* conflict) {
|
||||
int output_level, uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* conflict) {
|
||||
auto* result = compaction_picker_->CompactRange(
|
||||
GetName(), mutable_cf_options, current_->storage_info(), input_level,
|
||||
output_level, output_path_id, begin, end, compaction_end, conflict);
|
||||
output_level, output_path_id, max_subcompactions, begin, end,
|
||||
compaction_end, conflict);
|
||||
if (result != nullptr) {
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
@@ -1107,8 +1153,9 @@ void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
Status ColumnFamilyData::SetOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
MutableCFOptions new_mutable_cf_options;
|
||||
Status s = GetMutableOptionsFromStrings(mutable_cf_options_, options_map,
|
||||
&new_mutable_cf_options);
|
||||
Status s =
|
||||
GetMutableOptionsFromStrings(mutable_cf_options_, options_map,
|
||||
ioptions_.info_log, &new_mutable_cf_options);
|
||||
if (s.ok()) {
|
||||
mutable_cf_options_ = new_mutable_cf_options;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
@@ -1135,6 +1182,31 @@ Env::WriteLifeTimeHint ColumnFamilyData::CalculateSSTWriteHint(int level) {
|
||||
static_cast<int>(Env::WLTH_MEDIUM));
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::AddDirectories() {
|
||||
Status s;
|
||||
assert(data_dirs_.empty());
|
||||
for (auto& p : ioptions_.cf_paths) {
|
||||
std::unique_ptr<Directory> path_directory;
|
||||
s = DBImpl::CreateAndNewDirectory(ioptions_.env, p.path, &path_directory);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
assert(path_directory != nullptr);
|
||||
data_dirs_.emplace_back(path_directory.release());
|
||||
}
|
||||
assert(data_dirs_.size() == ioptions_.cf_paths.size());
|
||||
return s;
|
||||
}
|
||||
|
||||
Directory* ColumnFamilyData::GetDataDir(size_t path_id) const {
|
||||
if (data_dirs_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(path_id < data_dirs_.size());
|
||||
return data_dirs_[path_id].get();
|
||||
}
|
||||
|
||||
ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
const ImmutableDBOptions* db_options,
|
||||
const EnvOptions& env_options,
|
||||
|
||||
+23
-9
@@ -139,6 +139,9 @@ extern Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckConcurrentWritesSupported(
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
extern Status CheckCFPathsSupported(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
extern ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
// Wrap user defined table proproties collector factories `from cf_options`
|
||||
@@ -293,9 +296,9 @@ class ColumnFamilyData {
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* CompactRange(const MutableCFOptions& mutable_cf_options,
|
||||
int input_level, int output_level,
|
||||
uint32_t output_path_id, const InternalKey* begin,
|
||||
const InternalKey* end, InternalKey** compaction_end,
|
||||
bool* manual_conflict);
|
||||
uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict);
|
||||
|
||||
CompactionPicker* compaction_picker() { return compaction_picker_.get(); }
|
||||
// thread-safe
|
||||
@@ -342,10 +345,10 @@ class ColumnFamilyData {
|
||||
void ResetThreadLocalSuperVersions();
|
||||
|
||||
// Protected by DB mutex
|
||||
void set_pending_flush(bool value) { pending_flush_ = value; }
|
||||
void set_pending_compaction(bool value) { pending_compaction_ = value; }
|
||||
bool pending_flush() { return pending_flush_; }
|
||||
bool pending_compaction() { return pending_compaction_; }
|
||||
void set_queued_for_flush(bool value) { queued_for_flush_ = value; }
|
||||
void set_queued_for_compaction(bool value) { queued_for_compaction_ = value; }
|
||||
bool queued_for_flush() { return queued_for_flush_; }
|
||||
bool queued_for_compaction() { return queued_for_compaction_; }
|
||||
|
||||
enum class WriteStallCause {
|
||||
kNone,
|
||||
@@ -370,8 +373,16 @@ class ColumnFamilyData {
|
||||
|
||||
bool initialized() const { return initialized_.load(); }
|
||||
|
||||
const ColumnFamilyOptions& initial_cf_options() {
|
||||
return initial_cf_options_;
|
||||
}
|
||||
|
||||
Env::WriteLifeTimeHint CalculateSSTWriteHint(int level);
|
||||
|
||||
Status AddDirectories();
|
||||
|
||||
Directory* GetDataDir(size_t path_id) const;
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
@@ -442,11 +453,11 @@ class ColumnFamilyData {
|
||||
std::unique_ptr<WriteControllerToken> write_controller_token_;
|
||||
|
||||
// If true --> this ColumnFamily is currently present in DBImpl::flush_queue_
|
||||
bool pending_flush_;
|
||||
bool queued_for_flush_;
|
||||
|
||||
// If true --> this ColumnFamily is currently present in
|
||||
// DBImpl::compaction_queue_
|
||||
bool pending_compaction_;
|
||||
bool queued_for_compaction_;
|
||||
|
||||
uint64_t prev_compaction_needed_bytes_;
|
||||
|
||||
@@ -455,6 +466,9 @@ class ColumnFamilyData {
|
||||
|
||||
// Memtable id to track flush.
|
||||
std::atomic<uint64_t> last_memtable_id_;
|
||||
|
||||
// Directories corresponding to cf_paths.
|
||||
std::vector<std::unique_ptr<Directory>> data_dirs_;
|
||||
};
|
||||
|
||||
// ColumnFamilySet has interesting thread-safety requirements
|
||||
|
||||
+156
-28
@@ -69,9 +69,15 @@ class ColumnFamilyTest : public testing::Test {
|
||||
}
|
||||
|
||||
~ColumnFamilyTest() {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (auto h : handles_) {
|
||||
ColumnFamilyDescriptor cfdescriptor;
|
||||
h->GetDescriptor(&cfdescriptor);
|
||||
column_families.push_back(cfdescriptor);
|
||||
}
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
Destroy();
|
||||
Destroy(column_families);
|
||||
delete env_;
|
||||
}
|
||||
|
||||
@@ -236,9 +242,11 @@ class ColumnFamilyTest : public testing::Test {
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
void Destroy(const std::vector<ColumnFamilyDescriptor>& column_families =
|
||||
std::vector<ColumnFamilyDescriptor>()) {
|
||||
Close();
|
||||
ASSERT_OK(DestroyDB(dbname_, Options(db_options_, column_family_options_)));
|
||||
ASSERT_OK(DestroyDB(dbname_, Options(db_options_, column_family_options_),
|
||||
column_families));
|
||||
}
|
||||
|
||||
void CreateColumnFamilies(
|
||||
@@ -291,6 +299,9 @@ class ColumnFamilyTest : public testing::Test {
|
||||
}
|
||||
|
||||
void PutRandomData(int cf, int num, int key_value_size, bool save = false) {
|
||||
if (cf >= static_cast<int>(keys_.size())) {
|
||||
keys_.resize(cf + 1);
|
||||
}
|
||||
for (int i = 0; i < num; ++i) {
|
||||
// 10 bytes for key, rest is value
|
||||
if (!save) {
|
||||
@@ -298,7 +309,7 @@ class ColumnFamilyTest : public testing::Test {
|
||||
RandomString(&rnd_, key_value_size - 10)));
|
||||
} else {
|
||||
std::string key = test::RandomKey(&rnd_, 11);
|
||||
keys_.insert(key);
|
||||
keys_[cf].insert(key);
|
||||
ASSERT_OK(Put(cf, key, RandomString(&rnd_, key_value_size - 10)));
|
||||
}
|
||||
}
|
||||
@@ -383,6 +394,9 @@ class ColumnFamilyTest : public testing::Test {
|
||||
void AssertFilesPerLevel(const std::string& value, int cf) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(value, FilesPerLevel(cf));
|
||||
#else
|
||||
(void) value;
|
||||
(void) cf;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -397,6 +411,8 @@ class ColumnFamilyTest : public testing::Test {
|
||||
void AssertCountLiveFiles(int expected_value) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(expected_value, CountLiveFiles());
|
||||
#else
|
||||
(void) expected_value;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -445,6 +461,8 @@ class ColumnFamilyTest : public testing::Test {
|
||||
void AssertCountLiveLogFiles(int value) {
|
||||
#ifndef ROCKSDB_LITE // GetSortedWalFiles is not supported
|
||||
ASSERT_EQ(value, CountLiveLogFiles());
|
||||
#else
|
||||
(void) value;
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -483,9 +501,15 @@ class ColumnFamilyTest : public testing::Test {
|
||||
ASSERT_OK(destfile->Close());
|
||||
}
|
||||
|
||||
int GetSstFileCount(std::string path) {
|
||||
std::vector<std::string> files;
|
||||
DBTestBase::GetSstFiles(env_, path, &files);
|
||||
return static_cast<int>(files.size());
|
||||
}
|
||||
|
||||
std::vector<ColumnFamilyHandle*> handles_;
|
||||
std::vector<std::string> names_;
|
||||
std::set<std::string> keys_;
|
||||
std::vector<std::set<std::string>> keys_;
|
||||
ColumnFamilyOptions column_family_options_;
|
||||
DBOptions db_options_;
|
||||
std::string dbname_;
|
||||
@@ -1347,7 +1371,7 @@ TEST_F(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
{"ColumnFamilyTest::MultiManual:2", "ColumnFamilyTest::MultiManual:5"},
|
||||
{"ColumnFamilyTest::MultiManual:2", "ColumnFamilyTest::MultiManual:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::MultiManual:4");
|
||||
cf_1_1 = false;
|
||||
@@ -1393,8 +1417,8 @@ TEST_F(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
CompactAll(2);
|
||||
AssertFilesPerLevel("0,1", 2);
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
std::set<std::string>::iterator key_iter = keys_[1].begin();
|
||||
while (key_iter != keys_[1].end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
@@ -1440,7 +1464,7 @@ TEST_F(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
{"ColumnFamilyTest::AutoManual:2", "ColumnFamilyTest::AutoManual:5"},
|
||||
{"ColumnFamilyTest::AutoManual:2", "ColumnFamilyTest::AutoManual:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1) {
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:4");
|
||||
@@ -1487,8 +1511,8 @@ TEST_F(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
CompactAll(2);
|
||||
AssertFilesPerLevel("0,1", 2);
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
std::set<std::string>::iterator key_iter = keys_[1].begin();
|
||||
while (key_iter != keys_[1].end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
@@ -1541,7 +1565,7 @@ TEST_F(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
{"ColumnFamilyTest::ManualAuto:5", "ColumnFamilyTest::ManualAuto:2"},
|
||||
{"ColumnFamilyTest::ManualAuto:2", "ColumnFamilyTest::ManualAuto:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:4");
|
||||
cf_1_1 = false;
|
||||
@@ -1583,8 +1607,8 @@ TEST_F(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
CompactAll(2);
|
||||
AssertFilesPerLevel("0,1", 2);
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
std::set<std::string>::iterator key_iter = keys_[1].begin();
|
||||
while (key_iter != keys_[1].end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
@@ -1634,7 +1658,7 @@ TEST_F(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
{"ColumnFamilyTest::ManualManual:1",
|
||||
"ColumnFamilyTest::ManualManual:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualManual:4");
|
||||
cf_1_1 = false;
|
||||
@@ -1682,8 +1706,8 @@ TEST_F(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
ASSERT_LE(NumTableFilesAtLevel(0, 1), 2);
|
||||
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
std::set<std::string>::iterator key_iter = keys_[1].begin();
|
||||
while (key_iter != keys_[1].end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
@@ -1732,7 +1756,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
{"ColumnFamilyTest::ManualAuto:1", "ColumnFamilyTest::ManualAuto:2"},
|
||||
{"ColumnFamilyTest::ManualAuto:1", "ColumnFamilyTest::ManualAuto:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:4");
|
||||
cf_1_1 = false;
|
||||
@@ -1772,8 +1796,8 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
ASSERT_LE(NumTableFilesAtLevel(0, 1), 2);
|
||||
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
std::set<std::string>::iterator key_iter = keys_[1].begin();
|
||||
while (key_iter != keys_[1].end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
@@ -1824,7 +1848,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
"ColumnFamilyTest::ManualAuto:3"},
|
||||
{"ColumnFamilyTest::ManualAuto:1", "ColumnFamilyTest::ManualAuto:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAuto:4");
|
||||
cf_1_1 = false;
|
||||
@@ -1862,8 +1886,8 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
AssertFilesPerLevel("0,1", 1);
|
||||
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
std::set<std::string>::iterator key_iter = keys_[1].begin();
|
||||
while (key_iter != keys_[1].end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
@@ -1911,7 +1935,7 @@ TEST_F(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
{"CompactionPicker::CompactRange:Conflict",
|
||||
"ColumnFamilyTest::AutoManual:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::AutoManual:4");
|
||||
cf_1_1 = false;
|
||||
@@ -1951,8 +1975,8 @@ TEST_F(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
// VERIFY compaction "one"
|
||||
AssertFilesPerLevel("1", 1);
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
std::set<std::string>::iterator key_iter = keys_[1].begin();
|
||||
while (key_iter != keys_[1].end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
@@ -2358,7 +2382,7 @@ TEST_F(ColumnFamilyTest, CreateAndDropRace) {
|
||||
auto main_thread_id = std::this_thread::get_id();
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack("PersistRocksDBOptions:start",
|
||||
[&](void* arg) {
|
||||
[&](void* /*arg*/) {
|
||||
auto current_thread_id = std::this_thread::get_id();
|
||||
// If it's the main thread hitting this sync-point, then it
|
||||
// will be blocked until some other thread update the test_stage.
|
||||
@@ -2371,7 +2395,7 @@ TEST_F(ColumnFamilyTest, CreateAndDropRace) {
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WriteThread::EnterUnbatched:Wait", [&](void* arg) {
|
||||
"WriteThread::EnterUnbatched:Wait", [&](void* /*arg*/) {
|
||||
// This means a thread doing DropColumnFamily() is waiting for
|
||||
// other thread to finish persisting options.
|
||||
// In such case, we update the test_stage to unblock the main thread.
|
||||
@@ -2791,7 +2815,40 @@ TEST_F(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, CreateAndDestoryOptions) {
|
||||
std::unique_ptr<ColumnFamilyOptions> cfo(new ColumnFamilyOptions());
|
||||
ColumnFamilyHandle* cfh;
|
||||
Open();
|
||||
ASSERT_OK(db_->CreateColumnFamily(*(cfo.get()), "yoyo", &cfh));
|
||||
cfo.reset();
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfh, "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), cfh));
|
||||
ASSERT_OK(db_->DropColumnFamily(cfh));
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh));
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, CreateDropAndDestroy) {
|
||||
ColumnFamilyHandle* cfh;
|
||||
Open();
|
||||
ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "yoyo", &cfh));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfh, "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), cfh));
|
||||
ASSERT_OK(db_->DropColumnFamily(cfh));
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh));
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(ColumnFamilyTest, CreateDropAndDestroyWithoutFileDeletion) {
|
||||
ColumnFamilyHandle* cfh;
|
||||
Open();
|
||||
ASSERT_OK(db_->CreateColumnFamily(ColumnFamilyOptions(), "yoyo", &cfh));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), cfh, "foo", "bar"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), cfh));
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
ASSERT_OK(db_->DropColumnFamily(cfh));
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(cfh));
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, FlushCloseWALFiles) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
@@ -3117,6 +3174,77 @@ TEST_F(ColumnFamilyTest, DISABLED_LogTruncationTest) {
|
||||
// cleanup
|
||||
env_->DeleteDir(backup_logs);
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, DefaultCfPathsTest) {
|
||||
Open();
|
||||
// Leave cf_paths for one column families to be empty.
|
||||
// Files should be generated according to db_paths for that
|
||||
// column family.
|
||||
ColumnFamilyOptions cf_opt1, cf_opt2;
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "_one_1",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
CreateColumnFamilies({"one", "two"}, {cf_opt1, cf_opt2});
|
||||
Reopen({ColumnFamilyOptions(), cf_opt1, cf_opt2});
|
||||
|
||||
// Fill Column family 1.
|
||||
PutRandomData(1, 100, 100);
|
||||
Flush(1);
|
||||
|
||||
ASSERT_EQ(1, GetSstFileCount(cf_opt1.cf_paths[0].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// Fill column family 2
|
||||
PutRandomData(2, 100, 100);
|
||||
Flush(2);
|
||||
|
||||
// SST from Column family 2 should be generated in
|
||||
// db_paths which is dbname_ in this case.
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, MultipleCFPathsTest) {
|
||||
Open();
|
||||
// Configure Column family specific paths.
|
||||
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());
|
||||
CreateColumnFamilies({"one", "two"}, {cf_opt1, cf_opt2});
|
||||
Reopen({ColumnFamilyOptions(), cf_opt1, cf_opt2});
|
||||
|
||||
PutRandomData(1, 100, 100, true /* save */);
|
||||
Flush(1);
|
||||
|
||||
// Check that files are generated in appropriate paths.
|
||||
ASSERT_EQ(1, GetSstFileCount(cf_opt1.cf_paths[0].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
PutRandomData(2, 100, 100, true /* save */);
|
||||
Flush(2);
|
||||
|
||||
ASSERT_EQ(1, GetSstFileCount(cf_opt2.cf_paths[0].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// Re-open and verify the keys.
|
||||
Reopen({ColumnFamilyOptions(), cf_opt1, cf_opt2});
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
for (int cf = 1; cf != 3; ++cf) {
|
||||
ReadOptions read_options;
|
||||
read_options.readahead_size = 0;
|
||||
auto it = dbi->NewIterator(read_options, handles_[cf]);
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
Slice key(it->key());
|
||||
ASSERT_NE(keys_[cf].end(), keys_[cf].find(key.ToString()));
|
||||
}
|
||||
delete it;
|
||||
|
||||
for (const auto& key : keys_[cf]) {
|
||||
ASSERT_NE("NOT_FOUND", Get(cf, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -318,7 +318,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as DBImpl::CompactFiles is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
|
||||
@@ -49,8 +49,8 @@ Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
GetContext::kNotFound, key, value, nullptr, nullptr,
|
||||
nullptr, nullptr);
|
||||
LookupKey lkey(key, kMaxSequenceNumber);
|
||||
files_.files[FindFile(key)].fd.table_reader->Get(
|
||||
options, lkey.internal_key(), &get_context);
|
||||
files_.files[FindFile(key)].fd.table_reader->Get(options, lkey.internal_key(),
|
||||
&get_context, nullptr);
|
||||
if (get_context.State() == GetContext::kFound) {
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -82,7 +82,7 @@ std::vector<Status> CompactedDBImpl::MultiGet(const ReadOptions& options,
|
||||
GetContext::kNotFound, keys[idx], &pinnable_val,
|
||||
nullptr, nullptr, nullptr, nullptr);
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber);
|
||||
r->Get(options, lkey.internal_key(), &get_context);
|
||||
r->Get(options, lkey.internal_key(), &get_context, nullptr);
|
||||
value.assign(pinnable_val.data(), pinnable_val.size());
|
||||
if (get_context.State() == GetContext::kFound) {
|
||||
statuses[idx] = Status::OK();
|
||||
|
||||
+10
-1
@@ -134,6 +134,7 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
int _output_level, uint64_t _target_file_size,
|
||||
uint64_t _max_compaction_bytes, uint32_t _output_path_id,
|
||||
CompressionType _compression,
|
||||
uint32_t _max_subcompactions,
|
||||
std::vector<FileMetaData*> _grandparents,
|
||||
bool _manual_compaction, double _score,
|
||||
bool _deletion_compaction,
|
||||
@@ -143,6 +144,7 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
output_level_(_output_level),
|
||||
max_output_file_size_(_target_file_size),
|
||||
max_compaction_bytes_(_max_compaction_bytes),
|
||||
max_subcompactions_(_max_subcompactions),
|
||||
immutable_cf_options_(_immutable_cf_options),
|
||||
mutable_cf_options_(_mutable_cf_options),
|
||||
input_version_(nullptr),
|
||||
@@ -163,6 +165,9 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
if (is_manual_compaction_) {
|
||||
compaction_reason_ = CompactionReason::kManualCompaction;
|
||||
}
|
||||
if (max_subcompactions_ == 0) {
|
||||
max_subcompactions_ = immutable_cf_options_.max_subcompactions;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i < inputs_.size(); ++i) {
|
||||
@@ -442,7 +447,7 @@ bool Compaction::IsOutputLevelEmpty() const {
|
||||
}
|
||||
|
||||
bool Compaction::ShouldFormSubcompactions() const {
|
||||
if (immutable_cf_options_.max_subcompactions <= 1 || cfd_ == nullptr) {
|
||||
if (max_subcompactions_ <= 1 || cfd_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
@@ -468,4 +473,8 @@ uint64_t Compaction::MaxInputFileCreationTime() const {
|
||||
return max_creation_time;
|
||||
}
|
||||
|
||||
int Compaction::GetInputBaseLevel() const {
|
||||
return input_vstorage_->base_level();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -40,6 +40,7 @@ class Compaction {
|
||||
std::vector<CompactionInputFiles> inputs, int output_level,
|
||||
uint64_t target_file_size, uint64_t max_compaction_bytes,
|
||||
uint32_t output_path_id, CompressionType compression,
|
||||
uint32_t max_subcompactions,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
bool manual_compaction = false, double score = -1,
|
||||
bool deletion_compaction = false,
|
||||
@@ -233,6 +234,8 @@ class Compaction {
|
||||
|
||||
Slice GetLargestUserKey() const { return largest_user_key_; }
|
||||
|
||||
int GetInputBaseLevel() const;
|
||||
|
||||
CompactionReason compaction_reason() { return compaction_reason_; }
|
||||
|
||||
const std::vector<FileMetaData*>& grandparents() const {
|
||||
@@ -241,6 +244,8 @@ class Compaction {
|
||||
|
||||
uint64_t max_compaction_bytes() const { return max_compaction_bytes_; }
|
||||
|
||||
uint32_t max_subcompactions() const { return max_subcompactions_; }
|
||||
|
||||
uint64_t MaxInputFileCreationTime() const;
|
||||
|
||||
private:
|
||||
@@ -267,6 +272,7 @@ class Compaction {
|
||||
const int output_level_; // levels to which output files are stored
|
||||
uint64_t max_output_file_size_;
|
||||
uint64_t max_compaction_bytes_;
|
||||
uint32_t max_subcompactions_;
|
||||
const ImmutableCFOptions immutable_cf_options_;
|
||||
const MutableCFOptions mutable_cf_options_;
|
||||
Version* input_version_;
|
||||
|
||||
+128
-76
@@ -62,6 +62,46 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
const char* GetCompactionReasonString(CompactionReason compaction_reason) {
|
||||
switch (compaction_reason) {
|
||||
case CompactionReason::kUnknown:
|
||||
return "Unknown";
|
||||
case CompactionReason::kLevelL0FilesNum:
|
||||
return "LevelL0FilesNum";
|
||||
case CompactionReason::kLevelMaxLevelSize:
|
||||
return "LevelMaxLevelSize";
|
||||
case CompactionReason::kUniversalSizeAmplification:
|
||||
return "UniversalSizeAmplification";
|
||||
case CompactionReason::kUniversalSizeRatio:
|
||||
return "UniversalSizeRatio";
|
||||
case CompactionReason::kUniversalSortedRunNum:
|
||||
return "UniversalSortedRunNum";
|
||||
case CompactionReason::kFIFOMaxSize:
|
||||
return "FIFOMaxSize";
|
||||
case CompactionReason::kFIFOReduceNumFiles:
|
||||
return "FIFOReduceNumFiles";
|
||||
case CompactionReason::kFIFOTtl:
|
||||
return "FIFOTtl";
|
||||
case CompactionReason::kManualCompaction:
|
||||
return "ManualCompaction";
|
||||
case CompactionReason::kFilesMarkedForCompaction:
|
||||
return "FilesMarkedForCompaction";
|
||||
case CompactionReason::kBottommostFiles:
|
||||
return "BottommostFiles";
|
||||
case CompactionReason::kTtl:
|
||||
return "Ttl";
|
||||
case CompactionReason::kFlush:
|
||||
return "Flush";
|
||||
case CompactionReason::kExternalSstIngestion:
|
||||
return "ExternalSstIngestion";
|
||||
case CompactionReason::kNumOfReasons:
|
||||
// fall through
|
||||
default:
|
||||
assert(false);
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
// Maintains state for each sub-compaction
|
||||
struct CompactionJob::SubcompactionState {
|
||||
const Compaction* compaction;
|
||||
@@ -276,7 +316,7 @@ CompactionJob::CompactionJob(
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_job_stats_(compaction_job_stats),
|
||||
compaction_stats_(1),
|
||||
compaction_stats_(compaction->compaction_reason(), 1),
|
||||
dbname_(dbname),
|
||||
db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
@@ -314,15 +354,13 @@ CompactionJob::~CompactionJob() {
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
}
|
||||
|
||||
void CompactionJob::ReportStartedCompaction(
|
||||
Compaction* compaction) {
|
||||
void CompactionJob::ReportStartedCompaction(Compaction* compaction) {
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
db_options_.enable_thread_tracking);
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_JOB_ID,
|
||||
job_id_);
|
||||
ThreadStatusUtil::SetThreadOperationProperty(ThreadStatus::COMPACTION_JOB_ID,
|
||||
job_id_);
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_INPUT_OUTPUT_LEVEL,
|
||||
@@ -352,8 +390,7 @@ void CompactionJob::ReportStartedCompaction(
|
||||
|
||||
// Set the thread operation after operation properties
|
||||
// to ensure GetThreadList() can always show them all together.
|
||||
ThreadStatusUtil::SetThreadOperation(
|
||||
ThreadStatus::OP_COMPACTION);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
|
||||
|
||||
if (compaction_job_stats_) {
|
||||
compaction_job_stats_->is_manual_compaction =
|
||||
@@ -368,11 +405,11 @@ void CompactionJob::Prepare() {
|
||||
// Generate file_levels_ for compaction berfore making Iterator
|
||||
auto* c = compact_->compaction;
|
||||
assert(c->column_family_data() != nullptr);
|
||||
assert(c->column_family_data()->current()->storage_info()
|
||||
->NumLevelFiles(compact_->compaction->level()) > 0);
|
||||
assert(c->column_family_data()->current()->storage_info()->NumLevelFiles(
|
||||
compact_->compaction->level()) > 0);
|
||||
|
||||
write_hint_ = c->column_family_data()->CalculateSSTWriteHint(
|
||||
c->output_level());
|
||||
write_hint_ =
|
||||
c->column_family_data()->CalculateSSTWriteHint(c->output_level());
|
||||
// Is this compaction producing files at the bottommost level?
|
||||
bottommost_level_ = c->bottommost_level();
|
||||
|
||||
@@ -455,14 +492,18 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
}
|
||||
|
||||
std::sort(bounds.begin(), bounds.end(),
|
||||
[cfd_comparator] (const Slice& a, const Slice& b) -> bool {
|
||||
return cfd_comparator->Compare(ExtractUserKey(a), ExtractUserKey(b)) < 0;
|
||||
});
|
||||
[cfd_comparator](const Slice& a, const Slice& b) -> bool {
|
||||
return cfd_comparator->Compare(ExtractUserKey(a),
|
||||
ExtractUserKey(b)) < 0;
|
||||
});
|
||||
// Remove duplicated entries from bounds
|
||||
bounds.erase(std::unique(bounds.begin(), bounds.end(),
|
||||
[cfd_comparator] (const Slice& a, const Slice& b) -> bool {
|
||||
return cfd_comparator->Compare(ExtractUserKey(a), ExtractUserKey(b)) == 0;
|
||||
}), bounds.end());
|
||||
bounds.erase(
|
||||
std::unique(bounds.begin(), bounds.end(),
|
||||
[cfd_comparator](const Slice& a, const Slice& b) -> bool {
|
||||
return cfd_comparator->Compare(ExtractUserKey(a),
|
||||
ExtractUserKey(b)) == 0;
|
||||
}),
|
||||
bounds.end());
|
||||
|
||||
// Combine consecutive pairs of boundaries into ranges with an approximate
|
||||
// size of data covered by keys in that range
|
||||
@@ -485,12 +526,15 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
|
||||
// Group the ranges into subcompactions
|
||||
const double min_file_fill_percent = 4.0 / 5;
|
||||
uint64_t max_output_files = static_cast<uint64_t>(
|
||||
std::ceil(sum / min_file_fill_percent /
|
||||
c->mutable_cf_options()->MaxFileSizeForLevel(out_lvl)));
|
||||
int base_level = v->storage_info()->base_level();
|
||||
uint64_t max_output_files = static_cast<uint64_t>(std::ceil(
|
||||
sum / min_file_fill_percent /
|
||||
MaxFileSizeForLevel(*(c->mutable_cf_options()), out_lvl,
|
||||
c->immutable_cf_options()->compaction_style, base_level,
|
||||
c->immutable_cf_options()->level_compaction_dynamic_level_bytes)));
|
||||
uint64_t subcompactions =
|
||||
std::min({static_cast<uint64_t>(ranges.size()),
|
||||
static_cast<uint64_t>(db_options_.max_subcompactions),
|
||||
static_cast<uint64_t>(c->max_subcompactions()),
|
||||
max_output_files});
|
||||
|
||||
if (subcompactions > 1) {
|
||||
@@ -566,8 +610,9 @@ Status CompactionJob::Run() {
|
||||
TablePropertiesCollection tp;
|
||||
for (const auto& state : compact_->sub_compact_states) {
|
||||
for (const auto& output : state.outputs) {
|
||||
auto fn = TableFileName(db_options_.db_paths, output.meta.fd.GetNumber(),
|
||||
output.meta.fd.GetPathId());
|
||||
auto fn =
|
||||
TableFileName(state.compaction->immutable_cf_options()->cf_paths,
|
||||
output.meta.fd.GetNumber(), output.meta.fd.GetPathId());
|
||||
tp[fn] = output.table_properties;
|
||||
}
|
||||
}
|
||||
@@ -723,15 +768,18 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
std::set<size_t> sample_begin_offsets;
|
||||
if (bottommost_level_ && kSampleBytes > 0) {
|
||||
const size_t kMaxSamples = kSampleBytes >> kSampleLenShift;
|
||||
const size_t kOutFileLen = static_cast<size_t>(
|
||||
mutable_cf_options->MaxFileSizeForLevel(
|
||||
compact_->compaction->output_level()));
|
||||
const size_t kOutFileLen =
|
||||
static_cast<size_t>(MaxFileSizeForLevel(*mutable_cf_options,
|
||||
compact_->compaction->output_level(),
|
||||
cfd->ioptions()->compaction_style,
|
||||
compact_->compaction->GetInputBaseLevel(),
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes));
|
||||
if (kOutFileLen != port::kMaxSizet) {
|
||||
const size_t kOutFileNumSamples = kOutFileLen >> kSampleLenShift;
|
||||
Random64 generator{versions_->NewFileNumber()};
|
||||
for (size_t i = 0; i < kMaxSamples; ++i) {
|
||||
sample_begin_offsets.insert(
|
||||
static_cast<size_t>(generator.Uniform(kOutFileNumSamples))
|
||||
static_cast<size_t>(generator.Uniform(kOutFileNumSamples))
|
||||
<< kSampleLenShift);
|
||||
}
|
||||
}
|
||||
@@ -773,13 +821,12 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
preserve_deletes_seqnum_));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() &&
|
||||
sub_compact->compaction->output_level() != 0) {
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
// ShouldStopBefore() maintains state based on keys processed so far. The
|
||||
// compaction loop always calls it on the "next" key, thus won't tell it the
|
||||
// first key. So we do that here.
|
||||
sub_compact->ShouldStopBefore(
|
||||
c_iter->key(), sub_compact->current_output_file_size);
|
||||
sub_compact->ShouldStopBefore(c_iter->key(),
|
||||
sub_compact->current_output_file_size);
|
||||
}
|
||||
const auto& c_iter_stats = c_iter->iter_stats();
|
||||
auto sample_begin_offset_iter = sample_begin_offsets.cbegin();
|
||||
@@ -893,8 +940,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
c_iter->Next();
|
||||
if (!output_file_ended && c_iter->Valid() &&
|
||||
sub_compact->compaction->output_level() != 0 &&
|
||||
sub_compact->ShouldStopBefore(
|
||||
c_iter->key(), sub_compact->current_output_file_size) &&
|
||||
sub_compact->ShouldStopBefore(c_iter->key(),
|
||||
sub_compact->current_output_file_size) &&
|
||||
sub_compact->builder != nullptr) {
|
||||
// (2) this key belongs to the next file. For historical reasons, the
|
||||
// iterator status after advancing will be given to
|
||||
@@ -946,8 +993,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
|
||||
RecordCompactionIOStats();
|
||||
|
||||
if (status.ok() && (shutting_down_->load(std::memory_order_relaxed) ||
|
||||
cfd->IsDropped())) {
|
||||
if (status.ok() &&
|
||||
(shutting_down_->load(std::memory_order_relaxed) || cfd->IsDropped())) {
|
||||
status = Status::ShutdownInProgress(
|
||||
"Database shutdown or Column family drop during compaction");
|
||||
}
|
||||
@@ -1111,8 +1158,9 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// If there is nothing to output, no necessary to generate a sst file.
|
||||
// This happens when the output level is bottom level, at the same time
|
||||
// the sub_compact output nothing.
|
||||
std::string fname = TableFileName(
|
||||
db_options_.db_paths, meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
std::string fname =
|
||||
TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
|
||||
meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
env_->DeleteFile(fname);
|
||||
|
||||
// Also need to remove the file from outputs, or it will be added to the
|
||||
@@ -1131,11 +1179,13 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// We set for_compaction to false and don't OptimizeForCompactionTableRead
|
||||
// here because this is a special case after we finish the table building
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// we will regrad this verification as user reads since the goal is
|
||||
// we will regard this verification as user reads since the goal is
|
||||
// to cache it here for further user reads
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
ReadOptions(), env_options_, cfd->internal_comparator(), meta->fd,
|
||||
nullptr /* range_del_agg */, nullptr,
|
||||
nullptr /* range_del_agg */,
|
||||
sub_compact->compaction->mutable_cf_options()->prefix_extractor.get(),
|
||||
nullptr,
|
||||
cfd->internal_stats()->GetFileReadHist(
|
||||
compact_->compaction->output_level()),
|
||||
false, nullptr /* arena */, false /* skip_filters */,
|
||||
@@ -1143,7 +1193,8 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {}
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
}
|
||||
s = iter->status();
|
||||
}
|
||||
|
||||
@@ -1165,8 +1216,9 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
std::string fname;
|
||||
FileDescriptor output_fd;
|
||||
if (meta != nullptr) {
|
||||
fname = TableFileName(db_options_.db_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
fname =
|
||||
TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
|
||||
meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
output_fd = meta->fd;
|
||||
} else {
|
||||
fname = "(nil)";
|
||||
@@ -1180,13 +1232,14 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
auto sfm =
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
if (sfm && meta != nullptr && meta->fd.GetPathId() == 0) {
|
||||
auto fn = TableFileName(cfd->ioptions()->db_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
auto fn =
|
||||
TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
|
||||
meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
sfm->OnAddFile(fn);
|
||||
if (sfm->IsMaxAllowedSpaceReached()) {
|
||||
// TODO(ajkr): should we return OK() if max space was reached by the final
|
||||
// compaction output file (similarly to how flush works when full)?
|
||||
s = Status::IOError("Max allowed space was reached");
|
||||
s = Status::NoSpace("Max allowed space was reached");
|
||||
TEST_SYNC_POINT(
|
||||
"CompactionJob::FinishCompactionOutputFile:"
|
||||
"MaxAllowedSpaceReached");
|
||||
@@ -1236,7 +1289,7 @@ Status CompactionJob::InstallCompactionResults(
|
||||
compaction->InputLevelSummary(&inputs_summary), compact_->total_bytes);
|
||||
}
|
||||
|
||||
// Add compaction outputs
|
||||
// Add compaction inputs
|
||||
compaction->AddInputDeletions(compact_->compaction->edit());
|
||||
|
||||
for (const auto& sub_compact : compact_->sub_compact_states) {
|
||||
@@ -1266,8 +1319,9 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
assert(sub_compact->builder == nullptr);
|
||||
// no need to lock because VersionSet::next_file_number_ is atomic
|
||||
uint64_t file_number = versions_->NewFileNumber();
|
||||
std::string fname = TableFileName(db_options_.db_paths, file_number,
|
||||
sub_compact->compaction->output_path_id());
|
||||
std::string fname =
|
||||
TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
|
||||
file_number, sub_compact->compaction->output_path_id());
|
||||
// Fire events.
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -1334,9 +1388,10 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
}
|
||||
|
||||
sub_compact->builder.reset(NewTableBuilder(
|
||||
*cfd->ioptions(), cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
sub_compact->outfile.get(), sub_compact->compaction->output_compression(),
|
||||
*cfd->ioptions(), *(sub_compact->compaction->mutable_cf_options()),
|
||||
cfd->internal_comparator(), cfd->int_tbl_prop_collector_factories(),
|
||||
cfd->GetID(), cfd->GetName(), sub_compact->outfile.get(),
|
||||
sub_compact->compaction->output_compression(),
|
||||
cfd->ioptions()->compression_opts,
|
||||
sub_compact->compaction->output_level(), &sub_compact->compression_dict,
|
||||
skip_filters, output_file_creation_time));
|
||||
@@ -1369,8 +1424,7 @@ void CompactionJob::CleanupCompaction() {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace {
|
||||
void CopyPrefix(
|
||||
const Slice& src, size_t prefix_length, std::string* dst) {
|
||||
void CopyPrefix(const Slice& src, size_t prefix_length, std::string* dst) {
|
||||
assert(prefix_length > 0);
|
||||
size_t length = src.size() > prefix_length ? prefix_length : src.size();
|
||||
dst->assign(src.data(), length);
|
||||
@@ -1389,13 +1443,11 @@ void CompactionJob::UpdateCompactionStats() {
|
||||
if (compaction->level(input_level) != compaction->output_level()) {
|
||||
UpdateCompactionInputStatsHelper(
|
||||
&compaction_stats_.num_input_files_in_non_output_levels,
|
||||
&compaction_stats_.bytes_read_non_output_levels,
|
||||
input_level);
|
||||
&compaction_stats_.bytes_read_non_output_levels, input_level);
|
||||
} else {
|
||||
UpdateCompactionInputStatsHelper(
|
||||
&compaction_stats_.num_input_files_in_output_level,
|
||||
&compaction_stats_.bytes_read_output_level,
|
||||
input_level);
|
||||
&compaction_stats_.bytes_read_output_level, input_level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1418,8 +1470,9 @@ void CompactionJob::UpdateCompactionStats() {
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionJob::UpdateCompactionInputStatsHelper(
|
||||
int* num_files, uint64_t* bytes_read, int input_level) {
|
||||
void CompactionJob::UpdateCompactionInputStatsHelper(int* num_files,
|
||||
uint64_t* bytes_read,
|
||||
int input_level) {
|
||||
const Compaction* compaction = compact_->compaction;
|
||||
auto num_input_files = compaction->num_input_files(input_level);
|
||||
*num_files += static_cast<int>(num_input_files);
|
||||
@@ -1440,10 +1493,8 @@ void CompactionJob::UpdateCompactionJobStats(
|
||||
|
||||
// input information
|
||||
compaction_job_stats_->total_input_bytes =
|
||||
stats.bytes_read_non_output_levels +
|
||||
stats.bytes_read_output_level;
|
||||
compaction_job_stats_->num_input_records =
|
||||
compact_->num_input_records;
|
||||
stats.bytes_read_non_output_levels + stats.bytes_read_output_level;
|
||||
compaction_job_stats_->num_input_records = compact_->num_input_records;
|
||||
compaction_job_stats_->num_input_files =
|
||||
stats.num_input_files_in_non_output_levels +
|
||||
stats.num_input_files_in_output_level;
|
||||
@@ -1452,21 +1503,20 @@ void CompactionJob::UpdateCompactionJobStats(
|
||||
|
||||
// output information
|
||||
compaction_job_stats_->total_output_bytes = stats.bytes_written;
|
||||
compaction_job_stats_->num_output_records =
|
||||
compact_->num_output_records;
|
||||
compaction_job_stats_->num_output_records = compact_->num_output_records;
|
||||
compaction_job_stats_->num_output_files = stats.num_output_files;
|
||||
|
||||
if (compact_->NumOutputFiles() > 0U) {
|
||||
CopyPrefix(
|
||||
compact_->SmallestUserKey(),
|
||||
CompactionJobStats::kMaxPrefixLength,
|
||||
&compaction_job_stats_->smallest_output_key_prefix);
|
||||
CopyPrefix(
|
||||
compact_->LargestUserKey(),
|
||||
CompactionJobStats::kMaxPrefixLength,
|
||||
&compaction_job_stats_->largest_output_key_prefix);
|
||||
CopyPrefix(compact_->SmallestUserKey(),
|
||||
CompactionJobStats::kMaxPrefixLength,
|
||||
&compaction_job_stats_->smallest_output_key_prefix);
|
||||
CopyPrefix(compact_->LargestUserKey(),
|
||||
CompactionJobStats::kMaxPrefixLength,
|
||||
&compaction_job_stats_->largest_output_key_prefix);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)stats;
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -1489,7 +1539,9 @@ void CompactionJob::LogCompaction() {
|
||||
// build event logger report
|
||||
auto stream = event_logger_->Log();
|
||||
stream << "job" << job_id_ << "event"
|
||||
<< "compaction_started";
|
||||
<< "compaction_started"
|
||||
<< "compaction_reason"
|
||||
<< GetCompactionReasonString(compaction->compaction_reason());
|
||||
for (size_t i = 0; i < compaction->num_input_levels(); ++i) {
|
||||
stream << ("files_L" + ToString(compaction->level(i)));
|
||||
stream.StartArray();
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.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"
|
||||
|
||||
@@ -806,7 +806,7 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
stats_checker->set_verify_next_comp_io_stats(true);
|
||||
std::atomic<bool> first_prepare_write(true);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::Append:BeforePrepareWrite", [&](void* arg) {
|
||||
"WritableFileWriter::Append:BeforePrepareWrite", [&](void* /*arg*/) {
|
||||
if (first_prepare_write.load()) {
|
||||
options.env->SleepForMicroseconds(3);
|
||||
first_prepare_write.store(false);
|
||||
@@ -815,7 +815,7 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
|
||||
std::atomic<bool> first_flush(true);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::Flush:BeforeAppend", [&](void* arg) {
|
||||
"WritableFileWriter::Flush:BeforeAppend", [&](void* /*arg*/) {
|
||||
if (first_flush.load()) {
|
||||
options.env->SleepForMicroseconds(3);
|
||||
first_flush.store(false);
|
||||
@@ -824,7 +824,7 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
|
||||
std::atomic<bool> first_sync(true);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::SyncInternal:0", [&](void* arg) {
|
||||
"WritableFileWriter::SyncInternal:0", [&](void* /*arg*/) {
|
||||
if (first_sync.load()) {
|
||||
options.env->SleepForMicroseconds(3);
|
||||
first_sync.store(false);
|
||||
@@ -833,7 +833,7 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
|
||||
std::atomic<bool> first_range_sync(true);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::RangeSync:0", [&](void* arg) {
|
||||
"WritableFileWriter::RangeSync:0", [&](void* /*arg*/) {
|
||||
if (first_range_sync.load()) {
|
||||
options.env->SleepForMicroseconds(3);
|
||||
first_range_sync.store(false);
|
||||
@@ -1034,7 +1034,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
fprintf(stderr, "SKIPPED, not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1043,5 +1043,5 @@ int main(int argc, char** argv) {
|
||||
|
||||
#else
|
||||
|
||||
int main(int argc, char** argv) { return 0; }
|
||||
int main(int /*argc*/, char** /*argv*/) { return 0; }
|
||||
#endif // !defined(IOS_CROSS_COMPILE)
|
||||
|
||||
@@ -246,7 +246,7 @@ class CompactionJobTest : public testing::Test {
|
||||
Compaction compaction(cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
compaction_input_files, 1, 1024 * 1024,
|
||||
10 * 1024 * 1024, 0, kNoCompression, {}, true);
|
||||
10 * 1024 * 1024, 0, kNoCompression, 0, {}, true);
|
||||
compaction.SetInputVersion(cfd->current());
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
@@ -946,7 +946,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as CompactionJobStats is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
|
||||
+175
-92
@@ -59,7 +59,7 @@ bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
}
|
||||
|
||||
if (span_len >= min_files_to_compact &&
|
||||
new_compact_bytes_per_del_file < max_compact_bytes_per_del_file) {
|
||||
compact_bytes_per_del_file < max_compact_bytes_per_del_file) {
|
||||
assert(comp_inputs != nullptr);
|
||||
comp_inputs->level = 0;
|
||||
for (size_t i = 0; i < span_len; ++i) {
|
||||
@@ -301,7 +301,9 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
new Compaction(vstorage, ioptions_, mutable_cf_options, input_files,
|
||||
output_level, compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id,
|
||||
compact_options.compression, /* grandparents */ {}, true);
|
||||
compact_options.compression,
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
@@ -364,7 +366,7 @@ bool CompactionPicker::IsRangeInCompaction(VersionStorageInfo* vstorage,
|
||||
assert(level < NumberLevels());
|
||||
|
||||
vstorage->GetOverlappingInputs(level, smallest, largest, &inputs,
|
||||
*level_index, level_index);
|
||||
level_index ? *level_index : 0, level_index);
|
||||
return AreFilesInCompaction(inputs);
|
||||
}
|
||||
|
||||
@@ -475,7 +477,7 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
ROCKS_LOG_INFO(ioptions_.info_log,
|
||||
"[%s] Expanding@%d %" ROCKSDB_PRIszt "+%" ROCKSDB_PRIszt
|
||||
"(%" PRIu64 "+%" PRIu64 " bytes) to %" ROCKSDB_PRIszt
|
||||
"+%" ROCKSDB_PRIszt " (%" PRIu64 "+%" PRIu64 "bytes)\n",
|
||||
"+%" ROCKSDB_PRIszt " (%" PRIu64 "+%" PRIu64 " bytes)\n",
|
||||
cf_name.c_str(), input_level, inputs->size(),
|
||||
output_level_inputs->size(), inputs_size,
|
||||
output_level_inputs_size, expanded_inputs.size(),
|
||||
@@ -504,7 +506,8 @@ void CompactionPicker::GetGrandparents(
|
||||
Compaction* CompactionPicker::CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t output_path_id, const InternalKey* begin, const InternalKey* end,
|
||||
uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict) {
|
||||
// CompactionPickerFIFO has its own implementation of compact range
|
||||
assert(ioptions_.compaction_style != kCompactionStyleFIFO);
|
||||
@@ -566,11 +569,12 @@ Compaction* CompactionPicker::CompactRange(
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
output_level, MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
ioptions_.compaction_style),
|
||||
/* max_compaction_bytes */ LLONG_MAX, output_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
/* grandparents */ {}, /* is manual */ true);
|
||||
max_subcompactions, /* grandparents */ {}, /* is manual */ true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
@@ -616,7 +620,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(output_path_id < static_cast<uint32_t>(ioptions_.db_paths.size()));
|
||||
assert(output_path_id < static_cast<uint32_t>(ioptions_.cf_paths.size()));
|
||||
|
||||
if (ExpandInputsToCleanCut(cf_name, vstorage, &inputs) == false) {
|
||||
// manual compaction is now multi-threaded, so it can
|
||||
@@ -673,11 +677,15 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetGrandparents(vstorage, inputs, output_level_inputs, &grandparents);
|
||||
Compaction* compaction = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(compaction_inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
output_level,
|
||||
MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
ioptions_.compaction_style, vstorage->base_level(),
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
std::move(grandparents), /* is manual compaction */ true);
|
||||
/* max_subcompactions */ 0, std::move(grandparents),
|
||||
/* is manual compaction */ true);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::CompactRange:Return", compaction);
|
||||
RegisterCompaction(compaction);
|
||||
@@ -939,8 +947,83 @@ void CompactionPicker::UnregisterCompaction(Compaction* c) {
|
||||
compactions_in_progress_.erase(c);
|
||||
}
|
||||
|
||||
void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
const std::string& cf_name, VersionStorageInfo* vstorage, int* start_level,
|
||||
int* output_level, CompactionInputFiles* start_level_inputs) {
|
||||
if (vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&, cf_name](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
*start_level = level_file.first;
|
||||
*output_level =
|
||||
(*start_level == 0) ? vstorage->base_level() : *start_level + 1;
|
||||
|
||||
if (*start_level == 0 && !level0_compactions_in_progress()->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs->files = {level_file.second};
|
||||
start_level_inputs->level = *start_level;
|
||||
return ExpandInputsToCleanCut(cf_name, vstorage, start_level_inputs);
|
||||
};
|
||||
|
||||
// take a chance on a random file first
|
||||
Random64 rnd(/* seed */ reinterpret_cast<uint64_t>(vstorage));
|
||||
size_t random_file_index = static_cast<size_t>(rnd.Uniform(
|
||||
static_cast<uint64_t>(vstorage->FilesMarkedForCompaction().size())));
|
||||
|
||||
if (continuation(vstorage->FilesMarkedForCompaction()[random_file_index])) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& level_file : vstorage->FilesMarkedForCompaction()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
start_level_inputs->files.clear();
|
||||
}
|
||||
|
||||
bool CompactionPicker::GetOverlappingL0Files(
|
||||
VersionStorageInfo* vstorage, CompactionInputFiles* start_level_inputs,
|
||||
int output_level, int* parent_index) {
|
||||
// Two level 0 compaction won't run at the same time, so don't need to worry
|
||||
// about files on level 0 being compacted.
|
||||
assert(level0_compactions_in_progress()->empty());
|
||||
InternalKey smallest, largest;
|
||||
GetRange(*start_level_inputs, &smallest, &largest);
|
||||
// Note that the next call will discard the file we placed in
|
||||
// c->inputs_[0] earlier and replace it with an overlapping set
|
||||
// which will include the picked file.
|
||||
start_level_inputs->files.clear();
|
||||
vstorage->GetOverlappingInputs(0, &smallest, &largest,
|
||||
&(start_level_inputs->files));
|
||||
|
||||
// If we include more L0 files in the same compaction run it can
|
||||
// cause the 'smallest' and 'largest' key to get extended to a
|
||||
// larger range. So, re-invoke GetRange to get the new key range
|
||||
GetRange(*start_level_inputs, &smallest, &largest);
|
||||
if (IsRangeInCompaction(vstorage, &smallest, &largest, output_level,
|
||||
parent_index)) {
|
||||
return false;
|
||||
}
|
||||
assert(!start_level_inputs->files.empty());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevelCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
if (!vstorage->ExpiredTtlFiles().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->BottommostFilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
@@ -1007,8 +1090,7 @@ class LevelCompactionBuilder {
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
// If there is any file marked for compaction, put put it into inputs.
|
||||
void PickFilesMarkedForCompaction();
|
||||
void PickExpiredTtlFiles();
|
||||
|
||||
const std::string& cf_name_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
@@ -1036,8 +1118,8 @@ class LevelCompactionBuilder {
|
||||
static const int kMinFilesForIntraL0Compaction = 4;
|
||||
};
|
||||
|
||||
void LevelCompactionBuilder::PickFilesMarkedForCompaction() {
|
||||
if (vstorage_->FilesMarkedForCompaction().empty()) {
|
||||
void LevelCompactionBuilder::PickExpiredTtlFiles() {
|
||||
if (vstorage_->ExpiredTtlFiles().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1050,8 +1132,9 @@ void LevelCompactionBuilder::PickFilesMarkedForCompaction() {
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
if ((start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
(start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1061,22 +1144,13 @@ void LevelCompactionBuilder::PickFilesMarkedForCompaction() {
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
// take a chance on a random file first
|
||||
Random64 rnd(/* seed */ reinterpret_cast<uint64_t>(vstorage_));
|
||||
size_t random_file_index = static_cast<size_t>(rnd.Uniform(
|
||||
static_cast<uint64_t>(vstorage_->FilesMarkedForCompaction().size())));
|
||||
|
||||
if (continuation(vstorage_->FilesMarkedForCompaction()[random_file_index])) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& level_file : vstorage_->FilesMarkedForCompaction()) {
|
||||
for (auto& level_file : vstorage_->ExpiredTtlFiles()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
@@ -1131,61 +1205,51 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
// if we didn't find a compaction, check if there are any files marked for
|
||||
// compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
is_manual_ = true;
|
||||
parent_index_ = base_index_ = -1;
|
||||
PickFilesMarkedForCompaction();
|
||||
if (start_level_inputs_.empty()) {
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file =
|
||||
vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
}
|
||||
} else {
|
||||
|
||||
// PickFilesMarkedForCompaction();
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
is_manual_ = true;
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file = vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(start_level_inputs_.empty());
|
||||
PickExpiredTtlFiles();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherL0FilesIfNeeded() {
|
||||
if (start_level_ == 0 && output_level_ != 0) {
|
||||
// Two level 0 compaction won't run at the same time, so don't need to worry
|
||||
// about files on level 0 being compacted.
|
||||
assert(compaction_picker_->level0_compactions_in_progress()->empty());
|
||||
InternalKey smallest, largest;
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
// Note that the next call will discard the file we placed in
|
||||
// c->inputs_[0] earlier and replace it with an overlapping set
|
||||
// which will include the picked file.
|
||||
start_level_inputs_.files.clear();
|
||||
vstorage_->GetOverlappingInputs(0, &smallest, &largest,
|
||||
&start_level_inputs_.files);
|
||||
|
||||
// If we include more L0 files in the same compaction run it can
|
||||
// cause the 'smallest' and 'largest' key to get extended to a
|
||||
// larger range. So, re-invoke GetRange to get the new key range
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
if (compaction_picker_->IsRangeInCompaction(
|
||||
vstorage_, &smallest, &largest, output_level_, &parent_index_)) {
|
||||
return false;
|
||||
}
|
||||
return compaction_picker_->GetOverlappingL0Files(
|
||||
vstorage_, &start_level_inputs_, output_level_, &parent_index_);
|
||||
}
|
||||
assert(!start_level_inputs_.files.empty());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1258,13 +1322,16 @@ Compaction* LevelCompactionBuilder::PickCompaction() {
|
||||
Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
auto c = new Compaction(
|
||||
vstorage_, ioptions_, mutable_cf_options_, std::move(compaction_inputs_),
|
||||
output_level_, mutable_cf_options_.MaxFileSizeForLevel(output_level_),
|
||||
output_level_,
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level_,
|
||||
ioptions_.compaction_style, vstorage_->base_level(),
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
std::move(grandparents_), is_manual_, start_level_score_,
|
||||
false /* deletion_compaction */, compaction_reason_);
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
@@ -1287,10 +1354,10 @@ uint32_t LevelCompactionBuilder::GetPathId(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, int level) {
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.db_paths.empty());
|
||||
assert(!ioptions.cf_paths.empty());
|
||||
|
||||
// size remaining in the most recent path
|
||||
uint64_t current_path_size = ioptions.db_paths[0].target_size;
|
||||
uint64_t current_path_size = ioptions.cf_paths[0].target_size;
|
||||
|
||||
uint64_t level_size;
|
||||
int cur_level = 0;
|
||||
@@ -1300,7 +1367,7 @@ uint32_t LevelCompactionBuilder::GetPathId(
|
||||
level_size = mutable_cf_options.max_bytes_for_level_base;
|
||||
|
||||
// Last path is the fallback
|
||||
while (p < ioptions.db_paths.size() - 1) {
|
||||
while (p < ioptions.cf_paths.size() - 1) {
|
||||
if (level_size <= current_path_size) {
|
||||
if (cur_level == level) {
|
||||
// Does desired level fit in this path?
|
||||
@@ -1327,7 +1394,7 @@ uint32_t LevelCompactionBuilder::GetPathId(
|
||||
}
|
||||
}
|
||||
p++;
|
||||
current_path_size = ioptions.db_paths[p].target_size;
|
||||
current_path_size = ioptions.cf_paths[p].target_size;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -1512,8 +1579,9 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, {}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
kNoCompression, /* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0), /* is deletion compaction */ true,
|
||||
CompactionReason::kFIFOTtl);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -1531,18 +1599,28 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
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 */,
|
||||
mutable_cf_options.write_buffer_size, &comp_inputs)) {
|
||||
.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, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
0 /* max_subcompactions */, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0), /* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
return c;
|
||||
}
|
||||
@@ -1588,8 +1666,9 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, {}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
kNoCompression, /* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0), /* is deletion compaction */ true,
|
||||
CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -1612,9 +1691,13 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
Compaction* FIFOCompactionPicker::CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t /*output_path_id*/, const InternalKey* /*begin*/,
|
||||
const InternalKey* /*end*/, InternalKey** compaction_end,
|
||||
bool* /*manual_conflict*/) {
|
||||
uint32_t /*output_path_id*/, 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;
|
||||
|
||||
+14
-2
@@ -58,7 +58,8 @@ class CompactionPicker {
|
||||
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, const InternalKey* begin, const InternalKey* end,
|
||||
uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict);
|
||||
|
||||
// The maximum allowed output level. Default value is NumberLevels() - 1.
|
||||
@@ -174,6 +175,15 @@ class CompactionPicker {
|
||||
const CompactionInputFiles& output_level_inputs,
|
||||
std::vector<FileMetaData*>* grandparents);
|
||||
|
||||
void PickFilesMarkedForCompaction(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
int* start_level, int* output_level,
|
||||
CompactionInputFiles* start_level_inputs);
|
||||
|
||||
bool GetOverlappingL0Files(VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* start_level_inputs,
|
||||
int output_level, int* parent_index);
|
||||
|
||||
// Register this compaction in the set of running compactions
|
||||
void RegisterCompaction(Compaction* c);
|
||||
|
||||
@@ -238,7 +248,8 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
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, const InternalKey* begin, const InternalKey* end,
|
||||
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.
|
||||
@@ -280,6 +291,7 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
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*/,
|
||||
|
||||
@@ -59,7 +59,7 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_(nullptr) {
|
||||
fifo_options_.max_table_files_size = 1;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
ioptions_.db_paths.emplace_back("dummy",
|
||||
ioptions_.cf_paths.emplace_back("dummy",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
@@ -391,10 +391,10 @@ TEST_F(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
UniversalCompactionPicker universal_compaction_picker(
|
||||
ioptions_, &icmp_);
|
||||
UpdateVersionStorageInfo();
|
||||
// must return false when there's no files.
|
||||
ASSERT_EQ(universal_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
false);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// verify the trigger given different number of L0 files.
|
||||
for (int i = 1;
|
||||
@@ -415,6 +415,7 @@ TEST_F(CompactionPickerTest, CompactionUniversalIngestBehindReservedLevel) {
|
||||
ioptions_.allow_ingest_behind = true;
|
||||
ioptions_.num_levels = 3;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
UpdateVersionStorageInfo();
|
||||
// must return false when there's no files.
|
||||
ASSERT_EQ(universal_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
false);
|
||||
@@ -448,6 +449,7 @@ TEST_F(CompactionPickerTest, CannotTrivialMoveUniversal) {
|
||||
mutable_cf_options_.compaction_options_universal.allow_trivial_move = true;
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
UpdateVersionStorageInfo();
|
||||
// must return false when there's no files.
|
||||
ASSERT_EQ(universal_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
false);
|
||||
|
||||
@@ -162,7 +162,13 @@ bool UniversalCompactionPicker::IsInputFilesNonOverlapping(Compaction* c) {
|
||||
bool UniversalCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
const int kLevel0 = 0;
|
||||
return vstorage->CompactionScore(kLevel0) >= 1;
|
||||
if (vstorage->CompactionScore(kLevel0) >= 1) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UniversalCompactionPicker::SortedRun::Dump(char* out_buf,
|
||||
@@ -257,8 +263,9 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
CalculateSortedRuns(*vstorage, ioptions_, mutable_cf_options);
|
||||
|
||||
if (sorted_runs.size() == 0 ||
|
||||
sorted_runs.size() <
|
||||
(unsigned int)mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
(vstorage->FilesMarkedForCompaction().empty() &&
|
||||
sorted_runs.size() < (unsigned int)mutable_cf_options
|
||||
.level0_file_num_compaction_trigger)) {
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: nothing to do\n",
|
||||
cf_name.c_str());
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
@@ -272,58 +279,73 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
cf_name.c_str(), sorted_runs.size(), vstorage->LevelSummary(&tmp));
|
||||
|
||||
// Check for size amplification first.
|
||||
Compaction* c;
|
||||
if ((c = PickCompactionToReduceSizeAmp(cf_name, mutable_cf_options, vstorage,
|
||||
score, sorted_runs, log_buffer)) !=
|
||||
nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: compacting for size amp\n",
|
||||
cf_name.c_str());
|
||||
} else {
|
||||
// Size amplification is within limits. Try reducing read
|
||||
// amplification while maintaining file size ratios.
|
||||
unsigned int ratio =
|
||||
mutable_cf_options.compaction_options_universal.size_ratio;
|
||||
|
||||
if ((c = PickCompactionToReduceSortedRuns(
|
||||
cf_name, mutable_cf_options, vstorage, score, ratio, UINT_MAX,
|
||||
sorted_runs, log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Universal: compacting for size ratio\n",
|
||||
Compaction* c = nullptr;
|
||||
if (sorted_runs.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger)) {
|
||||
if ((c = PickCompactionToReduceSizeAmp(cf_name, mutable_cf_options,
|
||||
vstorage, score, sorted_runs,
|
||||
log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: compacting for size amp\n",
|
||||
cf_name.c_str());
|
||||
} else {
|
||||
// Size amplification and file size ratios are within configured limits.
|
||||
// If max read amplification is exceeding configured limits, then force
|
||||
// compaction without looking at filesize ratios and try to reduce
|
||||
// the number of files to fewer than level0_file_num_compaction_trigger.
|
||||
// This is guaranteed by NeedsCompaction()
|
||||
assert(sorted_runs.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger));
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs.size(); i++) {
|
||||
if (sorted_runs[i].being_compacted == false) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
// Size amplification is within limits. Try reducing read
|
||||
// amplification while maintaining file size ratios.
|
||||
unsigned int ratio =
|
||||
mutable_cf_options.compaction_options_universal.size_ratio;
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater than
|
||||
// the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted >
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
unsigned int num_files =
|
||||
num_sr_not_compacted -
|
||||
mutable_cf_options.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionToReduceSortedRuns(
|
||||
cf_name, mutable_cf_options, vstorage, score, UINT_MAX,
|
||||
num_files, sorted_runs, log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
if ((c = PickCompactionToReduceSortedRuns(
|
||||
cf_name, mutable_cf_options, vstorage, score, ratio, UINT_MAX,
|
||||
sorted_runs, log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Universal: compacting for size ratio\n",
|
||||
cf_name.c_str());
|
||||
} else {
|
||||
// Size amplification and file size ratios are within configured limits.
|
||||
// If max read amplification is exceeding configured limits, then force
|
||||
// compaction without looking at filesize ratios and try to reduce
|
||||
// the number of files to fewer than level0_file_num_compaction_trigger.
|
||||
// This is guaranteed by NeedsCompaction()
|
||||
assert(sorted_runs.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger));
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs.size(); i++) {
|
||||
if (sorted_runs[i].being_compacted == false) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater
|
||||
// than the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted >
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
unsigned int num_files =
|
||||
num_sr_not_compacted -
|
||||
mutable_cf_options.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionToReduceSortedRuns(
|
||||
cf_name, mutable_cf_options, vstorage, score, UINT_MAX,
|
||||
num_files, sorted_runs, log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr) {
|
||||
if ((c = PickDeleteTriggeredCompaction(cf_name, mutable_cf_options,
|
||||
vstorage, score, sorted_runs,
|
||||
log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Universal: delete triggered compaction\n",
|
||||
cf_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr) {
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
nullptr);
|
||||
@@ -406,9 +428,9 @@ uint32_t UniversalCompactionPicker::GetPathId(
|
||||
file_size *
|
||||
(100 - mutable_cf_options.compaction_options_universal.size_ratio) / 100;
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.db_paths.empty());
|
||||
for (; p < ioptions.db_paths.size() - 1; p++) {
|
||||
uint64_t target_size = ioptions.db_paths[p].target_size;
|
||||
assert(!ioptions.cf_paths.empty());
|
||||
for (; p < ioptions.cf_paths.size() - 1; p++) {
|
||||
uint64_t target_size = ioptions.cf_paths[p].target_size;
|
||||
if (target_size > file_size &&
|
||||
accumulated_size + (target_size - file_size) > future_size) {
|
||||
return p;
|
||||
@@ -609,11 +631,13 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
}
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level), LLONG_MAX, path_id,
|
||||
MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, start_level,
|
||||
1, enable_compression),
|
||||
/* grandparents */ {}, /* is manual */ false, score,
|
||||
false /* deletion_compaction */, compaction_reason);
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score, false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
|
||||
// Look at overall size amplification. If size amplification
|
||||
@@ -742,14 +766,134 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
output_level, MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
/* grandparents */ {}, /* is manual */ false, score,
|
||||
false /* deletion_compaction */,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score, false /* deletion_compaction */,
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
}
|
||||
|
||||
// Pick files marked for compaction. Typically, files are marked by
|
||||
// CompactOnDeleteCollector due to the presence of tombstones.
|
||||
Compaction* UniversalCompactionPicker::PickDeleteTriggeredCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score,
|
||||
const std::vector<SortedRun>& /*sorted_runs*/, LogBuffer* /*log_buffer*/) {
|
||||
CompactionInputFiles start_level_inputs;
|
||||
int output_level;
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
|
||||
if (vstorage->num_levels() == 1) {
|
||||
// This is single level universal. Since we're basically trying to reclaim
|
||||
// space by processing files marked for compaction due to high tombstone
|
||||
// density, let's do the same thing as compaction to reduce size amp which
|
||||
// has the same goals.
|
||||
bool compact = false;
|
||||
|
||||
start_level_inputs.level = 0;
|
||||
start_level_inputs.files.clear();
|
||||
output_level = 0;
|
||||
for (FileMetaData* f : vstorage->LevelFiles(0)) {
|
||||
if (f->marked_for_compaction) {
|
||||
compact = true;
|
||||
}
|
||||
if (compact) {
|
||||
start_level_inputs.files.push_back(f);
|
||||
}
|
||||
}
|
||||
if (start_level_inputs.size() <= 1) {
|
||||
// If only the last file in L0 is marked for compaction, ignore it
|
||||
return nullptr;
|
||||
}
|
||||
inputs.push_back(start_level_inputs);
|
||||
} else {
|
||||
int start_level;
|
||||
|
||||
// For multi-level universal, the strategy is to make this look more like
|
||||
// leveled. We pick one of the files marked for compaction and compact with
|
||||
// overlapping files in the adjacent level.
|
||||
PickFilesMarkedForCompaction(cf_name, vstorage, &start_level, &output_level,
|
||||
&start_level_inputs);
|
||||
if (start_level_inputs.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Pick the first non-empty level after the start_level
|
||||
for (output_level = start_level + 1; output_level < vstorage->num_levels();
|
||||
output_level++) {
|
||||
if (vstorage->NumLevelFiles(output_level) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all higher levels are empty, pick the highest level as output level
|
||||
if (output_level == vstorage->num_levels()) {
|
||||
if (start_level == 0) {
|
||||
output_level = vstorage->num_levels() - 1;
|
||||
} else {
|
||||
// If start level is non-zero and all higher levels are empty, this
|
||||
// compaction will translate into a trivial move. Since the idea is
|
||||
// to reclaim space and trivial move doesn't help with that, we
|
||||
// skip compaction in this case and return nullptr
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (ioptions_.allow_ingest_behind &&
|
||||
output_level == vstorage->num_levels() - 1) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
|
||||
if (output_level != 0) {
|
||||
if (start_level == 0) {
|
||||
if (!GetOverlappingL0Files(vstorage, &start_level_inputs, output_level,
|
||||
nullptr)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CompactionInputFiles output_level_inputs;
|
||||
int parent_index = -1;
|
||||
|
||||
output_level_inputs.level = output_level;
|
||||
if (!SetupOtherInputs(cf_name, mutable_cf_options, vstorage,
|
||||
&start_level_inputs, &output_level_inputs,
|
||||
&parent_index, -1)) {
|
||||
return nullptr;
|
||||
}
|
||||
inputs.push_back(start_level_inputs);
|
||||
if (!output_level_inputs.empty()) {
|
||||
inputs.push_back(output_level_inputs);
|
||||
}
|
||||
if (FilesRangeOverlapWithCompaction(inputs, output_level)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
inputs.push_back(start_level_inputs);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t estimated_total_size = 0;
|
||||
// Use size of the output level as estimated file size
|
||||
for (FileMetaData* f : vstorage->LevelFiles(output_level)) {
|
||||
estimated_total_size += f->fd.GetFileSize();
|
||||
}
|
||||
uint32_t path_id =
|
||||
GetPathId(ioptions_, mutable_cf_options, estimated_total_size);
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
1),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ true,
|
||||
score, false /* deletion_compaction */,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
@@ -73,6 +73,11 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
VersionStorageInfo* vstorage, double score,
|
||||
const std::vector<SortedRun>& sorted_runs, LogBuffer* log_buffer);
|
||||
|
||||
Compaction* PickDeleteTriggeredCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score,
|
||||
const std::vector<SortedRun>& sorted_runs, LogBuffer* log_buffer);
|
||||
|
||||
// Used in universal compaction when the enabled_trivial_move
|
||||
// option is set. Checks whether there are any overlapping files
|
||||
// in the input. Returns true if the input files are non
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
@@ -433,6 +434,145 @@ TEST_F(ComparatorDBTest, TwoStrComparator) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ComparatorDBTest, FindShortestSeparator) {
|
||||
std::string s1 = "abc1xyz";
|
||||
std::string s2 = "abc3xy";
|
||||
|
||||
BytewiseComparator()->FindShortestSeparator(&s1, s2);
|
||||
ASSERT_EQ("abc2", s1);
|
||||
|
||||
s1 = "abc5xyztt";
|
||||
|
||||
ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
|
||||
ASSERT_EQ("abc5", s1);
|
||||
|
||||
s1 = "abc3";
|
||||
s2 = "abc2xy";
|
||||
ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
|
||||
ASSERT_EQ("abc3", s1);
|
||||
|
||||
s1 = "abc3xyz";
|
||||
s2 = "abc2xy";
|
||||
ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
|
||||
ASSERT_EQ("abc3", s1);
|
||||
|
||||
s1 = "abc3xyz";
|
||||
s2 = "abc2";
|
||||
ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
|
||||
ASSERT_EQ("abc3", s1);
|
||||
|
||||
std::string old_s1 = s1 = "abc2xy";
|
||||
s2 = "abc2";
|
||||
ReverseBytewiseComparator()->FindShortestSeparator(&s1, s2);
|
||||
ASSERT_TRUE(old_s1 >= s1);
|
||||
ASSERT_TRUE(s1 > s2);
|
||||
}
|
||||
|
||||
TEST_F(ComparatorDBTest, SeparatorSuccessorRandomizeTest) {
|
||||
// Char list for boundary cases.
|
||||
std::array<unsigned char, 6> char_list{{0, 1, 2, 253, 254, 255}};
|
||||
Random rnd(301);
|
||||
|
||||
for (int attempts = 0; attempts < 1000; attempts++) {
|
||||
uint32_t size1 = rnd.Skewed(4);
|
||||
uint32_t size2;
|
||||
|
||||
if (rnd.OneIn(2)) {
|
||||
// size2 to be random size
|
||||
size2 = rnd.Skewed(4);
|
||||
} else {
|
||||
// size1 is within [-2, +2] of size1
|
||||
int diff = static_cast<int>(rnd.Uniform(5)) - 2;
|
||||
int tmp_size2 = static_cast<int>(size1) + diff;
|
||||
if (tmp_size2 < 0) {
|
||||
tmp_size2 = 0;
|
||||
}
|
||||
size2 = static_cast<uint32_t>(tmp_size2);
|
||||
}
|
||||
|
||||
std::string s1;
|
||||
std::string s2;
|
||||
for (uint32_t i = 0; i < size1; i++) {
|
||||
if (rnd.OneIn(2)) {
|
||||
// Use random byte
|
||||
s1 += static_cast<char>(rnd.Uniform(256));
|
||||
} else {
|
||||
// Use one byte in char_list
|
||||
char c = static_cast<char>(char_list[rnd.Uniform(sizeof(char_list))]);
|
||||
s1 += c;
|
||||
}
|
||||
}
|
||||
|
||||
// First set s2 to be the same as s1, and then modify s2.
|
||||
s2 = s1;
|
||||
s2.resize(size2);
|
||||
// We start from the back of the string
|
||||
if (size2 > 0) {
|
||||
uint32_t pos = size2 - 1;
|
||||
do {
|
||||
if (pos >= size1 || rnd.OneIn(4)) {
|
||||
// For 1/4 chance, use random byte
|
||||
s2[pos] = static_cast<char>(rnd.Uniform(256));
|
||||
} else if (rnd.OneIn(4)) {
|
||||
// In 1/4 chance, stop here.
|
||||
break;
|
||||
} else {
|
||||
// Create a char within [-2, +2] of the matching char of s1.
|
||||
int diff = static_cast<int>(rnd.Uniform(5)) - 2;
|
||||
// char may be signed or unsigned based on platform.
|
||||
int s1_char = static_cast<int>(static_cast<unsigned char>(s1[pos]));
|
||||
int s2_char = s1_char + diff;
|
||||
if (s2_char < 0) {
|
||||
s2_char = 0;
|
||||
}
|
||||
if (s2_char > 255) {
|
||||
s2_char = 255;
|
||||
}
|
||||
s2[pos] = static_cast<char>(s2_char);
|
||||
}
|
||||
} while (pos-- != 0);
|
||||
}
|
||||
|
||||
// Test separators
|
||||
for (int rev = 0; rev < 2; rev++) {
|
||||
if (rev == 1) {
|
||||
// switch s1 and s2
|
||||
std::string t = s1;
|
||||
s1 = s2;
|
||||
s2 = t;
|
||||
}
|
||||
std::string separator = s1;
|
||||
BytewiseComparator()->FindShortestSeparator(&separator, s2);
|
||||
std::string rev_separator = s1;
|
||||
ReverseBytewiseComparator()->FindShortestSeparator(&rev_separator, s2);
|
||||
|
||||
if (s1 == s2) {
|
||||
ASSERT_EQ(s1, separator);
|
||||
ASSERT_EQ(s2, rev_separator);
|
||||
} else if (s1 < s2) {
|
||||
ASSERT_TRUE(s1 <= separator);
|
||||
ASSERT_TRUE(s2 > separator);
|
||||
ASSERT_LE(separator.size(), std::max(s1.size(), s2.size()));
|
||||
ASSERT_EQ(s1, rev_separator);
|
||||
} else {
|
||||
ASSERT_TRUE(s1 >= rev_separator);
|
||||
ASSERT_TRUE(s2 < rev_separator);
|
||||
ASSERT_LE(rev_separator.size(), std::max(s1.size(), s2.size()));
|
||||
ASSERT_EQ(s1, separator);
|
||||
}
|
||||
}
|
||||
|
||||
// Test successors
|
||||
std::string succ = s1;
|
||||
BytewiseComparator()->FindShortSuccessor(&succ);
|
||||
ASSERT_TRUE(succ >= s1);
|
||||
|
||||
succ = s1;
|
||||
ReverseBytewiseComparator()->FindShortSuccessor(&succ);
|
||||
ASSERT_TRUE(succ <= s1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+3
-2
@@ -50,8 +50,9 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), file_path));
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions, env_options, internal_comparator,
|
||||
false /* skip_filters */, -1 /* level */),
|
||||
TableReaderOptions(ioptions, options.prefix_extractor.get(), env_options,
|
||||
internal_comparator, false /* skip_filters */,
|
||||
-1 /* level */),
|
||||
std::move(file_reader), file_size, &table_reader,
|
||||
false /* prefetch_index_and_filter_in_cache */);
|
||||
if (!s.ok()) {
|
||||
|
||||
@@ -333,9 +333,9 @@ TEST_F(CorruptionTest, TableFileIndexData) {
|
||||
Corrupt(kTableFile, -2000, 500);
|
||||
Reopen();
|
||||
dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
// one full file should be readable, since only one was corrupted
|
||||
// one full file may be readable, since only one was corrupted
|
||||
// the other file should be fully non-readable, since index was corrupted
|
||||
Check(5000, 5000);
|
||||
Check(0, 5000);
|
||||
ASSERT_NOK(dbi->VerifyChecksum());
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
fprintf(stderr, "SKIPPED as RepairDB() is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
fprintf(stderr, "SKIPPED as Cuckoo table is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -305,6 +305,41 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksOfNewTableAddedToCache) {
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
|
||||
// With fill_cache = false, fills up the cache, then iterates over the entire
|
||||
// db, verify dummy entries inserted in `BlockBasedTable::NewDataBlockIterator`
|
||||
// does not cause heap-use-after-free errors in COMPILE_WITH_ASAN=1 runs
|
||||
TEST_F(DBBlockCacheTest, FillCacheAndIterateDB) {
|
||||
ReadOptions read_options;
|
||||
read_options.fill_cache = false;
|
||||
auto table_options = GetTableOptions();
|
||||
auto options = GetOptions(table_options);
|
||||
InitTable(options);
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(10, 0, true);
|
||||
table_options.block_cache = cache;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
ASSERT_OK(Put("key2", "val2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("key3", "val3"));
|
||||
ASSERT_OK(Put("key4", "val4"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("key5", "val5"));
|
||||
ASSERT_OK(Put("key6", "val6"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(0));
|
||||
while (iter->Valid()) {
|
||||
iter->Next();
|
||||
}
|
||||
delete iter;
|
||||
iter = nullptr;
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -355,7 +390,10 @@ class MockCache : public LRUCache {
|
||||
static uint32_t high_pri_insert_count;
|
||||
static uint32_t low_pri_insert_count;
|
||||
|
||||
MockCache() : LRUCache(1 << 25, 0, false, 0.0) {}
|
||||
MockCache()
|
||||
: LRUCache((size_t)1 << 25 /*capacity*/, 0 /*num_shard_bits*/,
|
||||
false /*strict_capacity_limit*/, 0.0 /*high_pri_pool_ratio*/) {
|
||||
}
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
|
||||
@@ -1057,10 +1057,10 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial_move++; });
|
||||
[&](void* /*arg*/) { non_trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CompactRangeOptions compact_options;
|
||||
|
||||
@@ -45,6 +45,7 @@ class DBTestCompactionFilterWithCompactParam
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTestCompactionFilterWithCompactOption,
|
||||
DBTestCompactionFilterWithCompactParam,
|
||||
@@ -53,6 +54,12 @@ INSTANTIATE_TEST_CASE_P(
|
||||
DBTestBase::OptionConfig::kUniversalCompactionMultiLevel,
|
||||
DBTestBase::OptionConfig::kLevelSubcompactions,
|
||||
DBTestBase::OptionConfig::kUniversalSubcompactions));
|
||||
#else
|
||||
// Run fewer cases in valgrind
|
||||
INSTANTIATE_TEST_CASE_P(DBTestCompactionFilterWithCompactOption,
|
||||
DBTestCompactionFilterWithCompactParam,
|
||||
::testing::Values(DBTestBase::OptionConfig::kDefault));
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
|
||||
+353
-23
@@ -74,6 +74,48 @@ class FlushedFileCollector : public EventListener {
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
class CompactionStatsCollector : public EventListener {
|
||||
public:
|
||||
CompactionStatsCollector()
|
||||
: compaction_completed_(static_cast<int>(CompactionReason::kNumOfReasons)) {
|
||||
for (auto& v : compaction_completed_) {
|
||||
v.store(0);
|
||||
}
|
||||
}
|
||||
|
||||
~CompactionStatsCollector() {}
|
||||
|
||||
virtual void OnCompactionCompleted(DB* /* db */,
|
||||
const CompactionJobInfo& info) override {
|
||||
int k = static_cast<int>(info.compaction_reason);
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
assert(k >= 0 && k < num_of_reasons);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
|
||||
virtual void OnExternalFileIngested(DB* /* db */,
|
||||
const ExternalFileIngestionInfo& /* info */) override {
|
||||
int k = static_cast<int>(CompactionReason::kExternalSstIngestion);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /* db */,
|
||||
const FlushJobInfo& /* info */) override {
|
||||
int k = static_cast<int>(CompactionReason::kFlush);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
|
||||
int NumberOfCompactions(CompactionReason reason) const {
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
int k = static_cast<int>(reason);
|
||||
assert(k >= 0 && k < num_of_reasons);
|
||||
return compaction_completed_.at(k).load();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::atomic<int>> compaction_completed_;
|
||||
};
|
||||
|
||||
static const int kCDTValueSize = 1000;
|
||||
static const int kCDTKeysPerBuffer = 4;
|
||||
static const int kCDTNumLevels = 8;
|
||||
@@ -154,6 +196,40 @@ void VerifyCompactionResult(
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies compaction stats of cfd are valid.
|
||||
*
|
||||
* For each level of cfd, its compaction stats are valid if
|
||||
* 1) sum(stat.counts) == stat.count, and
|
||||
* 2) stat.counts[i] == collector.NumberOfCompactions(i)
|
||||
*/
|
||||
void VerifyCompactionStats(ColumnFamilyData& cfd,
|
||||
const CompactionStatsCollector& collector) {
|
||||
#ifndef NDEBUG
|
||||
InternalStats* internal_stats_ptr = cfd.internal_stats();
|
||||
ASSERT_TRUE(internal_stats_ptr != nullptr);
|
||||
const std::vector<InternalStats::CompactionStats>& comp_stats =
|
||||
internal_stats_ptr->TEST_GetCompactionStats();
|
||||
const int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
std::vector<int> counts(num_of_reasons, 0);
|
||||
// Count the number of compactions caused by each CompactionReason across
|
||||
// all levels.
|
||||
for (const auto& stat : comp_stats) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < num_of_reasons; i++) {
|
||||
counts[i] += stat.counts[i];
|
||||
sum += stat.counts[i];
|
||||
}
|
||||
ASSERT_EQ(sum, stat.count);
|
||||
}
|
||||
// Verify InternalStats bookkeeping matches that of CompactionStatsCollector,
|
||||
// assuming that all compactions complete.
|
||||
for (int i = 0; i < num_of_reasons; i++) {
|
||||
ASSERT_EQ(collector.NumberOfCompactions(static_cast<CompactionReason>(i)), counts[i]);
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
}
|
||||
|
||||
const SstFileMetaData* PickFileRandomly(
|
||||
const ColumnFamilyMetaData& cf_meta,
|
||||
Random* rand,
|
||||
@@ -175,6 +251,7 @@ const SstFileMetaData* PickFileRandomly(
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
// All the TEST_P tests run once with sub_compactions disabled (i.e.
|
||||
// options.max_subcompactions = 1) and once with it enabled
|
||||
TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
|
||||
@@ -217,6 +294,7 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
|
||||
ASSERT_GT(db_size[0] / 3, db_size[1]);
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, CompactionsPreserveDeletes) {
|
||||
// For each options type we test following
|
||||
@@ -360,7 +438,7 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::GetTableReader:0",
|
||||
[&](void* arg) { num_new_table_reader++; });
|
||||
[&](void* /*arg*/) { num_new_table_reader++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int k = 0; k < options.level0_file_num_compaction_trigger; ++k) {
|
||||
@@ -916,7 +994,7 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveOneFile) {
|
||||
int32_t trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -973,10 +1051,10 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveNonOverlappingFiles) {
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial_move++; });
|
||||
[&](void* /*arg*/) { non_trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -1072,10 +1150,10 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveTargetLevel) {
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial_move++; });
|
||||
[&](void* /*arg*/) { non_trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -1131,10 +1209,10 @@ TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial_move++; });
|
||||
[&](void* /*arg*/) { non_trivial_move++; });
|
||||
bool first = true;
|
||||
// Purpose of dependencies:
|
||||
// 4 -> 1: ensure the order of two non-trivial compactions
|
||||
@@ -1145,7 +1223,7 @@ TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
|
||||
{"DBCompaction::ManualPartial:5", "DBCompaction::ManualPartial:2"},
|
||||
{"DBCompaction::ManualPartial:5", "DBCompaction::ManualPartial:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (first) {
|
||||
first = false;
|
||||
TEST_SYNC_POINT("DBCompaction::ManualPartial:4");
|
||||
@@ -1276,17 +1354,17 @@ TEST_F(DBCompactionTest, DISABLED_ManualPartialFill) {
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial_move++; });
|
||||
[&](void* /*arg*/) { non_trivial_move++; });
|
||||
bool first = true;
|
||||
bool second = true;
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBCompaction::PartialFill:4", "DBCompaction::PartialFill:1"},
|
||||
{"DBCompaction::PartialFill:2", "DBCompaction::PartialFill:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* /*arg*/) {
|
||||
if (first) {
|
||||
TEST_SYNC_POINT("DBCompaction::PartialFill:4");
|
||||
first = false;
|
||||
@@ -1692,10 +1770,10 @@ TEST_P(DBCompactionTestWithParam, TrivialMoveToLastLevelWithFiles) {
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial_move++; });
|
||||
[&](void* /*arg*/) { non_trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -1983,6 +2061,125 @@ TEST_P(DBCompactionTestWithParam, LevelCompactionPathUse) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, LevelCompactionCFPathUse) {
|
||||
Options options = CurrentOptions();
|
||||
options.db_paths.emplace_back(dbname_, 500 * 1024);
|
||||
options.db_paths.emplace_back(dbname_ + "_2", 4 * 1024 * 1024);
|
||||
options.db_paths.emplace_back(dbname_ + "_3", 1024 * 1024 * 1024);
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.write_buffer_size = 110 << 10; // 110KB
|
||||
options.arena_block_size = 4 << 10;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.num_levels = 4;
|
||||
options.max_bytes_for_level_base = 400 * 1024;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
|
||||
std::vector<Options> option_vector;
|
||||
option_vector.emplace_back(options);
|
||||
ColumnFamilyOptions cf_opt1(options), cf_opt2(options);
|
||||
// Configure CF1 specific paths.
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1", 500 * 1024);
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_2", 4 * 1024 * 1024);
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_3", 1024 * 1024 * 1024);
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt1);
|
||||
CreateColumnFamilies({"one"},option_vector[1]);
|
||||
|
||||
// Configura CF2 specific paths.
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2", 500 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_2", 4 * 1024 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_3", 1024 * 1024 * 1024);
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt2);
|
||||
CreateColumnFamilies({"two"},option_vector[2]);
|
||||
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
|
||||
|
||||
Random rnd(301);
|
||||
int key_idx = 0;
|
||||
int key_idx1 = 0;
|
||||
int key_idx2 = 0;
|
||||
|
||||
auto generate_file = [&]() {
|
||||
GenerateNewFile(0, &rnd, &key_idx);
|
||||
GenerateNewFile(1, &rnd, &key_idx1);
|
||||
GenerateNewFile(2, &rnd, &key_idx2);
|
||||
};
|
||||
|
||||
auto check_sstfilecount = [&](int path_id, int expected) {
|
||||
ASSERT_EQ(expected, GetSstFileCount(options.db_paths[path_id].path));
|
||||
ASSERT_EQ(expected, GetSstFileCount(cf_opt1.cf_paths[path_id].path));
|
||||
ASSERT_EQ(expected, GetSstFileCount(cf_opt2.cf_paths[path_id].path));
|
||||
};
|
||||
|
||||
auto check_filesperlevel = [&](const std::string& expected) {
|
||||
ASSERT_EQ(expected, FilesPerLevel(0));
|
||||
ASSERT_EQ(expected, FilesPerLevel(1));
|
||||
ASSERT_EQ(expected, FilesPerLevel(2));
|
||||
};
|
||||
|
||||
auto check_getvalues = [&]() {
|
||||
for (int i = 0; i < key_idx; i++) {
|
||||
auto v = Get(0, Key(i));
|
||||
ASSERT_NE(v, "NOT_FOUND");
|
||||
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
|
||||
}
|
||||
|
||||
for (int i = 0; i < key_idx1; i++) {
|
||||
auto v = Get(1, Key(i));
|
||||
ASSERT_NE(v, "NOT_FOUND");
|
||||
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
|
||||
}
|
||||
|
||||
for (int i = 0; i < key_idx2; i++) {
|
||||
auto v = Get(2, Key(i));
|
||||
ASSERT_NE(v, "NOT_FOUND");
|
||||
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
|
||||
}
|
||||
};
|
||||
|
||||
// Check that default column family uses db_paths.
|
||||
// And Column family "one" uses cf_paths.
|
||||
|
||||
// First three 110KB files are not going to second path.
|
||||
// After that, (100K, 200K)
|
||||
for (int num = 0; num < 3; num++) {
|
||||
generate_file();
|
||||
}
|
||||
|
||||
// Another 110KB triggers a compaction to 400K file to fill up first path
|
||||
generate_file();
|
||||
check_sstfilecount(1, 3);
|
||||
|
||||
// (1, 4)
|
||||
generate_file();
|
||||
check_filesperlevel("1,4");
|
||||
check_sstfilecount(1, 4);
|
||||
check_sstfilecount(0, 1);
|
||||
|
||||
// (1, 4, 1)
|
||||
generate_file();
|
||||
check_filesperlevel("1,4,1");
|
||||
check_sstfilecount(2, 1);
|
||||
check_sstfilecount(1, 4);
|
||||
check_sstfilecount(0, 1);
|
||||
|
||||
// (1, 4, 2)
|
||||
generate_file();
|
||||
check_filesperlevel("1,4,2");
|
||||
check_sstfilecount(2, 2);
|
||||
check_sstfilecount(1, 4);
|
||||
check_sstfilecount(0, 1);
|
||||
|
||||
check_getvalues();
|
||||
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
|
||||
|
||||
check_getvalues();
|
||||
|
||||
Destroy(options, true);
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, ConvertCompactionStyle) {
|
||||
Random rnd(301);
|
||||
int max_key_level_insert = 200;
|
||||
@@ -2573,16 +2770,16 @@ TEST_P(DBCompactionTestWithParam, CompressLevelCompaction) {
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"Compaction::InputCompressionMatchesOutput:Matches",
|
||||
[&](void* arg) { matches++; });
|
||||
[&](void* /*arg*/) { matches++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"Compaction::InputCompressionMatchesOutput:DidntMatch",
|
||||
[&](void* arg) { didnt_match++; });
|
||||
[&](void* /*arg*/) { didnt_match++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial++; });
|
||||
[&](void* /*arg*/) { non_trivial++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
@@ -2744,10 +2941,10 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial_move++; });
|
||||
[&](void* /*arg*/) { non_trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -2933,6 +3130,48 @@ TEST_P(DBCompactionTestWithParam, IntraL0CompactionDoesNotObsoleteDeletions) {
|
||||
ASSERT_TRUE(db_->Get(roptions, Key(0), &result).IsNotFound());
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, FullCompactionInBottomPriThreadPool) {
|
||||
const int kNumFilesTrigger = 3;
|
||||
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
for (bool use_universal_compaction : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
if (use_universal_compaction) {
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
} else {
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
}
|
||||
options.num_levels = 4;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int num_bottom_pri_compactions = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkBottomCompaction",
|
||||
[&](void* /*arg*/) { ++num_bottom_pri_compactions; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
ASSERT_EQ(NumSortedRuns(), num);
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_EQ(1, num_bottom_pri_compactions);
|
||||
|
||||
// Verify that size amplification did occur
|
||||
ASSERT_EQ(NumSortedRuns(), 1);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, OptimizedDeletionObsoleting) {
|
||||
// Deletions can be dropped when compacted to non-last level if they fall
|
||||
// outside the lower-level files' key-ranges.
|
||||
@@ -3091,6 +3330,13 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
// compactions should be triggered, which reduce the size of each bottom-level
|
||||
// file without changing file count.
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() ==
|
||||
CompactionReason::kBottommostFiles);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
db_->GetLiveFilesMetaData(&post_release_metadata);
|
||||
ASSERT_EQ(pre_release_metadata.size(), post_release_metadata.size());
|
||||
@@ -3104,6 +3350,66 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
// deletion markers/deleted keys.
|
||||
ASSERT_LT(post_file.size, pre_file.size);
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
const int kNumKeysPerFile = 32;
|
||||
const int kNumLevelFiles = 2;
|
||||
const int kValueSize = 1024;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.ttl = 24 * 60 * 60; // 24 hours
|
||||
options.max_open_files = -1;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), RandomString(&rnd, kValueSize)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
MoveFilesToLevel(3);
|
||||
ASSERT_EQ("0,0,0,2", FilesPerLevel());
|
||||
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
// Overwrite previous keys with smaller, but predictable, values.
|
||||
ASSERT_OK(Delete(Key(i * kNumKeysPerFile + j)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ("2,0,0,2", FilesPerLevel());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ("0,2,0,2", FilesPerLevel());
|
||||
|
||||
env_->addon_time_.fetch_add(36 * 60 * 60); // 36 hours
|
||||
ASSERT_EQ("0,2,0,2", FilesPerLevel());
|
||||
|
||||
// Just do a siimple write + flush so that the Ttl expired files get
|
||||
// compacted.
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
Flush();
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// All non-L0 files are deleted, as they contained only deleted data.
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactRangeDelayedByL0FileCount) {
|
||||
@@ -3376,6 +3682,28 @@ TEST_F(DBCompactionTest, CompactRangeFlushOverlappingMemtable) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionStatsTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
CompactionStatsCollector* collector = new CompactionStatsCollector();
|
||||
options.listeners.emplace_back(collector);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
for (int j = 0; j < 5000; j++) {
|
||||
Put(std::to_string(j), std::string(1, 'A'));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ColumnFamilyHandleImpl* cfh =
|
||||
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily());
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
|
||||
VerifyCompactionStats(*cfd, *collector);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithParam, DBCompactionTestWithParam,
|
||||
::testing::Values(std::make_tuple(1, true),
|
||||
std::make_tuple(1, false),
|
||||
@@ -3395,7 +3723,7 @@ TEST_P(DBCompactionDirectIOTest, DirectIO) {
|
||||
"TableCache::NewIterator:for_compaction", [&](void* arg) {
|
||||
bool* use_direct_reads = static_cast<bool*>(arg);
|
||||
ASSERT_EQ(*use_direct_reads,
|
||||
options.use_direct_io_for_flush_and_compaction);
|
||||
options.use_direct_reads);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::OpenCompactionOutputFile", [&](void* arg) {
|
||||
@@ -3405,7 +3733,7 @@ TEST_P(DBCompactionDirectIOTest, DirectIO) {
|
||||
});
|
||||
if (options.use_direct_io_for_flush_and_compaction) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions:direct_io", [&](void* arg) {
|
||||
"SanitizeOptions:direct_io", [&](void* /*arg*/) {
|
||||
readahead = true;
|
||||
});
|
||||
}
|
||||
@@ -3414,7 +3742,7 @@ TEST_P(DBCompactionDirectIOTest, DirectIO) {
|
||||
MakeTables(3, "p", "q", 1);
|
||||
ASSERT_EQ("1,1,1", FilesPerLevel(1));
|
||||
Compact(1, "p1", "p9");
|
||||
ASSERT_FALSE(readahead ^ options.use_direct_io_for_flush_and_compaction);
|
||||
ASSERT_EQ(readahead, options.use_direct_reads);
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel(1));
|
||||
Destroy(options);
|
||||
delete options.env;
|
||||
@@ -3482,6 +3810,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
// Hold compaction jobs to make sure
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Start",
|
||||
[&](void* arg) { env_->SleepForMicroseconds(100000); });
|
||||
[&](void* /*arg*/) { env_->SleepForMicroseconds(100000); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "true"},
|
||||
@@ -378,7 +378,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBaseInc) {
|
||||
int non_trivial = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial++; });
|
||||
[&](void* /*arg*/) { non_trivial++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
@@ -501,6 +501,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
}
|
||||
|
||||
int DBImpl::IsFileDeletionsEnabled() const {
|
||||
return disable_delete_obsolete_files_;
|
||||
return !disable_delete_obsolete_files_;
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
+8
-4
@@ -72,19 +72,23 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
auto* cfd =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
->cfd();
|
||||
int refs_before = cfd->current()->TEST_refs();
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = false;
|
||||
ASSERT_OK(dbfull()->Flush(flush_options));
|
||||
// Flush installs a new super-version. Get the ref count after that.
|
||||
auto current_before = cfd->current();
|
||||
int refs_before = cfd->current()->TEST_refs();
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:1");
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:2");
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
// Now the background job will do the flush; wait for it.
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("", FilesPerLevel()); // flush failed.
|
||||
#endif // ROCKSDB_LITE
|
||||
// Flush job should release ref count to current version.
|
||||
// Backgroun flush job should release ref count to current version.
|
||||
ASSERT_EQ(current_before, cfd->current());
|
||||
ASSERT_EQ(refs_before, cfd->current()->TEST_refs());
|
||||
Destroy(options);
|
||||
}
|
||||
@@ -101,7 +105,7 @@ TEST_F(DBFlushTest, FlushInLowPriThreadPool) {
|
||||
std::thread::id tid;
|
||||
int num_flushes = 0, num_compactions = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkFlush", [&](void* arg) {
|
||||
"DBImpl::BGWorkFlush", [&](void* /*arg*/) {
|
||||
if (tid == std::thread::id()) {
|
||||
tid = std::this_thread::get_id();
|
||||
} else {
|
||||
@@ -110,7 +114,7 @@ TEST_F(DBFlushTest, FlushInLowPriThreadPool) {
|
||||
++num_flushes;
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkCompaction", [&](void* arg) {
|
||||
"DBImpl::BGWorkCompaction", [&](void* /*arg*/) {
|
||||
ASSERT_EQ(tid, std::this_thread::get_id());
|
||||
++num_compactions;
|
||||
});
|
||||
|
||||
+189
-127
@@ -97,7 +97,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
const std::string kDefaultColumnFamilyName("default");
|
||||
void DumpRocksDBBuildVersion(Logger * log);
|
||||
void DumpRocksDBBuildVersion(Logger* log);
|
||||
|
||||
CompressionType GetCompressionFlush(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
@@ -135,7 +135,7 @@ void DumpSupportInfo(Logger* logger) {
|
||||
}
|
||||
|
||||
int64_t kDefaultLowPriThrottledRate = 2 * 1024 * 1024;
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
const bool seq_per_batch)
|
||||
@@ -183,7 +183,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
last_stats_dump_time_microsec_(0),
|
||||
next_job_id_(1),
|
||||
has_unpersisted_data_(false),
|
||||
unable_to_flush_oldest_log_(false),
|
||||
unable_to_release_oldest_log_(false),
|
||||
env_options_(BuildDBOptions(immutable_db_options_, mutable_db_options_)),
|
||||
env_options_for_compaction_(env_->OptimizeForCompactionTableWrite(
|
||||
env_options_, immutable_db_options_)),
|
||||
@@ -300,6 +300,8 @@ Status DBImpl::CloseHelper() {
|
||||
TEST_SYNC_POINT("DBImpl::~DBImpl:WaitJob");
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::CloseHelper:PendingPurgeFinished",
|
||||
&files_grabbed_for_purge_);
|
||||
EraseThreadStatusDbInfo();
|
||||
flush_scheduler_.Clear();
|
||||
|
||||
@@ -353,10 +355,11 @@ Status DBImpl::CloseHelper() {
|
||||
uint64_t log_number = log.writer->get_log_number();
|
||||
Status s = log.ClearWriter();
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"Unable to Sync WAL file %s with error -- %s",
|
||||
LogFileName(immutable_db_options_.wal_dir, log_number).c_str(),
|
||||
s.ToString().c_str());
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Unable to Sync WAL file %s with error -- %s",
|
||||
LogFileName(immutable_db_options_.wal_dir, log_number).c_str(),
|
||||
s.ToString().c_str());
|
||||
// Retain the first error
|
||||
if (ret.ok()) {
|
||||
ret = s;
|
||||
@@ -512,7 +515,16 @@ void DBImpl::ScheduleBgLogWriterClose(JobContext* job_context) {
|
||||
}
|
||||
}
|
||||
|
||||
Directory* DBImpl::Directories::GetDataDir(size_t path_id) {
|
||||
Directory* DBImpl::GetDataDir(ColumnFamilyData* cfd, size_t path_id) const {
|
||||
assert(cfd);
|
||||
Directory* ret_dir = cfd->GetDataDir(path_id);
|
||||
if (ret_dir == nullptr) {
|
||||
return directories_.GetDataDir(path_id);
|
||||
}
|
||||
return ret_dir;
|
||||
}
|
||||
|
||||
Directory* DBImpl::Directories::GetDataDir(size_t path_id) const {
|
||||
assert(path_id < data_dirs_.size());
|
||||
Directory* ret_dir = data_dirs_[path_id].get();
|
||||
if (ret_dir == nullptr) {
|
||||
@@ -522,9 +534,12 @@ Directory* DBImpl::Directories::GetDataDir(size_t path_id) {
|
||||
return ret_dir;
|
||||
}
|
||||
|
||||
Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
Status DBImpl::SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
(void)column_family;
|
||||
(void)options_map;
|
||||
return Status::NotSupported("Not supported in ROCKSDB LITE");
|
||||
#else
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family)->cfd();
|
||||
@@ -561,9 +576,9 @@ Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
}
|
||||
sv_context.Clean();
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"SetOptions() on column family [%s], inputs:",
|
||||
cfd->GetName().c_str());
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"SetOptions() on column family [%s], inputs:", cfd->GetName().c_str());
|
||||
for (const auto& o : options_map) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "%s: %s\n", o.first.c_str(),
|
||||
o.second.c_str());
|
||||
@@ -587,6 +602,7 @@ Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
Status DBImpl::SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
(void)options_map;
|
||||
return Status::NotSupported("Not supported in ROCKSDB LITE");
|
||||
#else
|
||||
if (options_map.empty()) {
|
||||
@@ -613,7 +629,8 @@ Status DBImpl::SetDBOptions(
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
write_controller_.set_max_delayed_write_rate(new_options.delayed_write_rate);
|
||||
write_controller_.set_max_delayed_write_rate(
|
||||
new_options.delayed_write_rate);
|
||||
table_cache_.get()->SetCapacity(new_options.max_open_files == -1
|
||||
? TableCache::kInfiniteCapacity
|
||||
: new_options.max_open_files - 10);
|
||||
@@ -693,7 +710,7 @@ int DBImpl::FindMinimumEmptyLevelFitting(
|
||||
}
|
||||
|
||||
Status DBImpl::FlushWAL(bool sync) {
|
||||
{
|
||||
if (manual_wal_flush_) {
|
||||
// We need to lock log_write_mutex_ since logs_ might change concurrently
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
@@ -701,12 +718,20 @@ Status DBImpl::FlushWAL(bool sync) {
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "WAL flush error %s",
|
||||
s.ToString().c_str());
|
||||
// In case there is a fs error we should set it globally to prevent the
|
||||
// future writes
|
||||
WriteStatusCheck(s);
|
||||
// whether sync or not, we should abort the rest of function upon error
|
||||
return s;
|
||||
}
|
||||
if (!sync) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "FlushWAL sync=false");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (!sync) {
|
||||
return Status::OK();
|
||||
}
|
||||
// sync = true
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "FlushWAL sync=true");
|
||||
return SyncWAL();
|
||||
@@ -774,12 +799,10 @@ Status DBImpl::SyncWAL() {
|
||||
return status;
|
||||
}
|
||||
|
||||
void DBImpl::MarkLogsSynced(
|
||||
uint64_t up_to, bool synced_dir, const Status& status) {
|
||||
void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
|
||||
const Status& status) {
|
||||
mutex_.AssertHeld();
|
||||
if (synced_dir &&
|
||||
logfile_number_ == up_to &&
|
||||
status.ok()) {
|
||||
if (synced_dir && logfile_number_ == up_to && status.ok()) {
|
||||
log_dir_synced_ = true;
|
||||
}
|
||||
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;) {
|
||||
@@ -787,6 +810,8 @@ void DBImpl::MarkLogsSynced(
|
||||
assert(log.getting_synced);
|
||||
if (status.ok() && logs_.size() > 1) {
|
||||
logs_to_free_.push_back(log.ReleaseWriter());
|
||||
// To modify logs_ both mutex_ and log_write_mutex_ must be held
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
it = logs_.erase(it);
|
||||
} else {
|
||||
log.getting_synced = false;
|
||||
@@ -853,14 +878,14 @@ void DBImpl::BackgroundCallPurge() {
|
||||
if (!purge_queue_.empty()) {
|
||||
auto purge_file = purge_queue_.begin();
|
||||
auto fname = purge_file->fname;
|
||||
auto dir_to_sync = purge_file->dir_to_sync;
|
||||
auto type = purge_file->type;
|
||||
auto number = purge_file->number;
|
||||
auto path_id = purge_file->path_id;
|
||||
auto job_id = purge_file->job_id;
|
||||
purge_queue_.pop_front();
|
||||
|
||||
mutex_.Unlock();
|
||||
DeleteObsoleteFileImpl(job_id, fname, type, number, path_id);
|
||||
DeleteObsoleteFileImpl(job_id, fname, dir_to_sync, type, number);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
assert(!logs_to_free_queue_.empty());
|
||||
@@ -944,7 +969,7 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
MergeIteratorBuilder merge_iter_builder(
|
||||
&cfd->internal_comparator(), arena,
|
||||
!read_options.total_order_seek &&
|
||||
cfd->ioptions()->prefix_extractor != nullptr);
|
||||
super_version->mutable_cf_options.prefix_extractor != nullptr);
|
||||
// Collect iterator for mutable mem
|
||||
merge_iter_builder.AddIterator(
|
||||
super_version->mem->NewIterator(read_options, arena));
|
||||
@@ -1016,8 +1041,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
// Because prep_seq > snapshot => commit_seq > snapshot so if a snapshot is
|
||||
// specified we should be fine with skipping seq numbers that are greater
|
||||
// than that.
|
||||
snapshot = reinterpret_cast<const SnapshotImpl*>(
|
||||
read_options.snapshot)->number_;
|
||||
snapshot =
|
||||
reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)->number_;
|
||||
} else {
|
||||
// Since we get and reference the super version before getting
|
||||
// the snapshot number, without a mutex protection, it is possible
|
||||
@@ -1095,7 +1120,6 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values) {
|
||||
|
||||
StopWatch sw(env_, stats_, DB_MULTIGET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
@@ -1119,8 +1143,8 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
|
||||
mutex_.Lock();
|
||||
if (read_options.snapshot != nullptr) {
|
||||
snapshot = reinterpret_cast<const SnapshotImpl*>(
|
||||
read_options.snapshot)->number_;
|
||||
snapshot =
|
||||
reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)->number_;
|
||||
} else {
|
||||
snapshot = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
@@ -1148,6 +1172,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
// First look in the memtable, then in the immutable memtable (if any).
|
||||
// s is both in/out. When in, s could either be OK or MergeInProgress.
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t num_found = 0;
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
merge_context.Clear();
|
||||
Status& s = stat_list[i];
|
||||
@@ -1169,11 +1194,11 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
if (super_version->mem->Get(lkey, value, &s, &merge_context,
|
||||
&range_del_agg, read_options)) {
|
||||
done = true;
|
||||
// TODO(?): RecordTick(stats_, MEMTABLE_HIT)?
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if (super_version->imm->Get(lkey, value, &s, &merge_context,
|
||||
&range_del_agg, read_options)) {
|
||||
done = true;
|
||||
// TODO(?): RecordTick(stats_, MEMTABLE_HIT)?
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
@@ -1182,11 +1207,12 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
super_version->current->Get(read_options, lkey, &pinnable_val, &s,
|
||||
&merge_context, &range_del_agg);
|
||||
value->assign(pinnable_val.data(), pinnable_val.size());
|
||||
// TODO(?): RecordTick(stats_, MEMTABLE_MISS)?
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
bytes_read += value->size();
|
||||
num_found++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1214,6 +1240,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
|
||||
RecordTick(stats_, NUMBER_MULTIGET_CALLS);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_KEYS_READ, num_keys);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_KEYS_FOUND, num_found);
|
||||
RecordTick(stats_, NUMBER_MULTIGET_BYTES_READ, bytes_read);
|
||||
MeasureTime(stats_, BYTES_PER_MULTIGET, bytes_read);
|
||||
PERF_COUNTER_ADD(multiget_read_bytes, bytes_read);
|
||||
@@ -1301,6 +1328,17 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
if (s.ok() && immutable_db_options_.allow_concurrent_memtable_write) {
|
||||
s = CheckConcurrentWritesSupported(cf_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = CheckCFPathsSupported(initial_db_options_, cf_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
for (auto& cf_path : cf_options.cf_paths) {
|
||||
s = env_->CreateDirIfMissing(cf_path.path);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1332,13 +1370,19 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
&cf_options);
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
if (s.ok()) {
|
||||
auto* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(column_family_name);
|
||||
assert(cfd != nullptr);
|
||||
s = cfd->AddDirectories();
|
||||
}
|
||||
if (s.ok()) {
|
||||
single_column_family_mode_ = false;
|
||||
auto* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(column_family_name);
|
||||
assert(cfd != nullptr);
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &sv_context, *cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context,
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
|
||||
if (!cfd->mem()->IsSnapshotSupported()) {
|
||||
is_snapshot_supported_ = false;
|
||||
@@ -1420,8 +1464,8 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
// we drop column family from a single write thread
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_);
|
||||
s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(), &edit,
|
||||
&mutex_);
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
if (s.ok()) {
|
||||
@@ -1471,7 +1515,7 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
|
||||
*value_found = true;
|
||||
}
|
||||
ReadOptions roptions = read_options;
|
||||
roptions.read_tier = kBlockCacheTier; // read from block cache only
|
||||
roptions.read_tier = kBlockCacheTier; // read from block cache only
|
||||
PinnableSlice pinnable_val;
|
||||
auto s = GetImpl(roptions, column_family, key, &pinnable_val, value_found);
|
||||
value->assign(pinnable_val.data(), pinnable_val.size());
|
||||
@@ -1494,17 +1538,17 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
if (immutable_db_options_.preserve_deletes &&
|
||||
read_options.iter_start_seqnum > 0 &&
|
||||
read_options.iter_start_seqnum < preserve_deletes_seqnum_.load()) {
|
||||
return NewErrorIterator(Status::InvalidArgument(
|
||||
"Iterator requested internal keys which are too old and are not"
|
||||
" guaranteed to be preserved, try larger iter_start_seqnum opt."));
|
||||
}
|
||||
return NewErrorIterator(Status::InvalidArgument(
|
||||
"Iterator requested internal keys which are too old and are not"
|
||||
" guaranteed to be preserved, try larger iter_start_seqnum opt."));
|
||||
}
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (read_options.managed) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
// not supported in lite version
|
||||
result = NewErrorIterator(Status::InvalidArgument(
|
||||
result = NewErrorIterator(Status::InvalidArgument(
|
||||
"Managed Iterators not supported in RocksDBLite."));
|
||||
#else
|
||||
if ((read_options.tailing) || (read_options.snapshot != nullptr) ||
|
||||
@@ -1513,7 +1557,7 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
} else {
|
||||
// Managed iter not supported
|
||||
result = NewErrorIterator(Status::InvalidArgument(
|
||||
"Managed Iterators not supported without snapshots."));
|
||||
"Managed Iterators not supported without snapshots."));
|
||||
}
|
||||
#endif
|
||||
} else if (read_options.tailing) {
|
||||
@@ -1525,8 +1569,8 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv);
|
||||
result = NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(), iter,
|
||||
kMaxSequenceNumber,
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
read_callback);
|
||||
#endif
|
||||
@@ -1593,7 +1637,7 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
// likely that any iterator pointer is close to the iterator it points to so
|
||||
// that they are likely to be in the same cache line and/or page.
|
||||
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), snapshot,
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options, snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback,
|
||||
((read_options.snapshot != nullptr) ? nullptr : this), cfd, allow_blob,
|
||||
@@ -1644,8 +1688,8 @@ Status DBImpl::NewIterators(
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv);
|
||||
iterators->push_back(NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(), iter,
|
||||
kMaxSequenceNumber,
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
read_callback));
|
||||
}
|
||||
@@ -1658,8 +1702,8 @@ Status DBImpl::NewIterators(
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: versions_->LastSequence();
|
||||
for (size_t i = 0; i < column_families.size(); ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
column_families[i])->cfd();
|
||||
auto* cfd =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(column_families[i])->cfd();
|
||||
iterators->push_back(
|
||||
NewIteratorImpl(read_options, cfd, snapshot, read_callback));
|
||||
}
|
||||
@@ -1676,7 +1720,7 @@ const Snapshot* DBImpl::GetSnapshotForWriteConflictBoundary() {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
const Snapshot* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary) {
|
||||
SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary) {
|
||||
int64_t unix_time = 0;
|
||||
env_->GetCurrentTime(&unix_time); // Ignore error
|
||||
SnapshotImpl* s = new SnapshotImpl;
|
||||
@@ -1766,13 +1810,9 @@ Status DBImpl::GetPropertiesOfTablesInRange(ColumnFamilyHandle* column_family,
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
const std::string& DBImpl::GetName() const {
|
||||
return dbname_;
|
||||
}
|
||||
const std::string& DBImpl::GetName() const { return dbname_; }
|
||||
|
||||
Env* DBImpl::GetEnv() const {
|
||||
return env_;
|
||||
}
|
||||
Env* DBImpl::GetEnv() const { return env_; }
|
||||
|
||||
Options DBImpl::GetOptions(ColumnFamilyHandle* column_family) const {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -2145,9 +2185,9 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
FlushReason::kDeleteFiles);
|
||||
}
|
||||
FindObsoleteFiles(&job_context, false);
|
||||
} // lock released here
|
||||
@@ -2198,9 +2238,9 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
end_key = &end_storage;
|
||||
}
|
||||
|
||||
vstorage->GetCleanInputsWithinInterval(i, begin_key, end_key,
|
||||
&level_files, -1 /* hint_index */,
|
||||
nullptr /* file_index */);
|
||||
vstorage->GetCleanInputsWithinInterval(
|
||||
i, begin_key, end_key, &level_files, -1 /* hint_index */,
|
||||
nullptr /* file_index */);
|
||||
FileMetaData* level_file;
|
||||
for (uint32_t j = 0; j < level_files.size(); j++) {
|
||||
level_file = level_files[j];
|
||||
@@ -2211,7 +2251,8 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
continue;
|
||||
}
|
||||
if (!include_end && end != nullptr &&
|
||||
cfd->user_comparator()->Compare(level_file->largest.user_key(), *end) == 0) {
|
||||
cfd->user_comparator()->Compare(level_file->largest.user_key(),
|
||||
*end) == 0) {
|
||||
continue;
|
||||
}
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
@@ -2229,9 +2270,9 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
FlushReason::kDeleteFiles);
|
||||
}
|
||||
for (auto* deleted_file : deleted_files) {
|
||||
deleted_file->being_compacted = false;
|
||||
@@ -2255,9 +2296,8 @@ void DBImpl::GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) {
|
||||
versions_->GetLiveFilesMetaData(metadata);
|
||||
}
|
||||
|
||||
void DBImpl::GetColumnFamilyMetaData(
|
||||
ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* cf_meta) {
|
||||
void DBImpl::GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* cf_meta) {
|
||||
assert(column_family);
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family)->cfd();
|
||||
auto* sv = GetAndRefSuperVersion(cfd);
|
||||
@@ -2383,10 +2423,10 @@ Status DB::ListColumnFamilies(const DBOptions& db_options,
|
||||
return VersionSet::ListColumnFamilies(column_families, name, db_options.env);
|
||||
}
|
||||
|
||||
Snapshot::~Snapshot() {
|
||||
}
|
||||
Snapshot::~Snapshot() {}
|
||||
|
||||
Status DestroyDB(const std::string& dbname, const Options& options) {
|
||||
Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families) {
|
||||
ImmutableDBOptions soptions(SanitizeOptions(dbname, options));
|
||||
Env* env = soptions.env;
|
||||
std::vector<std::string> filenames;
|
||||
@@ -2404,15 +2444,15 @@ Status DestroyDB(const std::string& dbname, const Options& options) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
InfoLogPrefix info_log_prefix(!soptions.db_log_dir.empty(), dbname);
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, info_log_prefix.prefix, &type) &&
|
||||
type != kDBLockFile) { // Lock file will be deleted at end
|
||||
for (const auto& fname : filenames) {
|
||||
if (ParseFileName(fname, &number, info_log_prefix.prefix, &type) &&
|
||||
type != kDBLockFile) { // Lock file will be deleted at end
|
||||
Status del;
|
||||
std::string path_to_delete = dbname + "/" + filenames[i];
|
||||
std::string path_to_delete = dbname + "/" + fname;
|
||||
if (type == kMetaDatabase) {
|
||||
del = DestroyDB(path_to_delete, options);
|
||||
} else if (type == kTableFile) {
|
||||
del = DeleteSSTFile(&soptions, path_to_delete, 0);
|
||||
del = DeleteSSTFile(&soptions, path_to_delete, dbname);
|
||||
} else {
|
||||
del = env->DeleteFile(path_to_delete);
|
||||
}
|
||||
@@ -2422,59 +2462,82 @@ Status DestroyDB(const std::string& dbname, const Options& options) {
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t path_id = 0; path_id < options.db_paths.size(); path_id++) {
|
||||
const auto& db_path = options.db_paths[path_id];
|
||||
env->GetChildren(db_path.path, &filenames);
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, &type) &&
|
||||
std::vector<std::string> paths;
|
||||
|
||||
for (const auto& path : options.db_paths) {
|
||||
paths.emplace_back(path.path);
|
||||
}
|
||||
for (const auto& cf : column_families) {
|
||||
for (const auto& path : cf.options.cf_paths) {
|
||||
paths.emplace_back(path.path);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicate paths.
|
||||
// Note that we compare only the actual paths but not path ids.
|
||||
// This reason is that same path can appear at different path_ids
|
||||
// for different column families.
|
||||
std::sort(paths.begin(), paths.end());
|
||||
paths.erase(std::unique(paths.begin(), paths.end()), paths.end());
|
||||
|
||||
for (const auto& path : paths) {
|
||||
if (env->GetChildren(path, &filenames).ok()) {
|
||||
for (const auto& fname : filenames) {
|
||||
if (ParseFileName(fname, &number, &type) &&
|
||||
type == kTableFile) { // Lock file will be deleted at end
|
||||
std::string table_path = db_path.path + "/" + filenames[i];
|
||||
Status del = DeleteSSTFile(&soptions, table_path,
|
||||
static_cast<uint32_t>(path_id));
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
std::string table_path = path + "/" + fname;
|
||||
Status del = DeleteSSTFile(&soptions, table_path, dbname);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
}
|
||||
}
|
||||
env->DeleteDir(path);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> walDirFiles;
|
||||
std::string archivedir = ArchivalDirectory(dbname);
|
||||
bool wal_dir_exists = false;
|
||||
if (dbname != soptions.wal_dir) {
|
||||
env->GetChildren(soptions.wal_dir, &walDirFiles);
|
||||
wal_dir_exists = env->GetChildren(soptions.wal_dir, &walDirFiles).ok();
|
||||
archivedir = ArchivalDirectory(soptions.wal_dir);
|
||||
}
|
||||
|
||||
// Delete log files in the WAL dir
|
||||
for (const auto& file : walDirFiles) {
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
Status del = env->DeleteFile(LogFileName(soptions.wal_dir, number));
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Archive dir may be inside wal dir or dbname and should be
|
||||
// processed and removed before those otherwise we have issues
|
||||
// removing them
|
||||
std::vector<std::string> archiveFiles;
|
||||
env->GetChildren(archivedir, &archiveFiles);
|
||||
// Delete archival files.
|
||||
for (size_t i = 0; i < archiveFiles.size(); ++i) {
|
||||
if (ParseFileName(archiveFiles[i], &number, &type) &&
|
||||
if (env->GetChildren(archivedir, &archiveFiles).ok()) {
|
||||
// Delete archival files.
|
||||
for (const auto& file : archiveFiles) {
|
||||
if (ParseFileName(file, &number, &type) &&
|
||||
type == kLogFile) {
|
||||
Status del = env->DeleteFile(archivedir + "/" + archiveFiles[i]);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
Status del = env->DeleteFile(archivedir + "/" + file);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
}
|
||||
}
|
||||
env->DeleteDir(archivedir);
|
||||
}
|
||||
|
||||
// ignore case where no archival directory is present
|
||||
env->DeleteDir(archivedir);
|
||||
// Delete log files in the WAL dir
|
||||
if (wal_dir_exists) {
|
||||
for (const auto& file : walDirFiles) {
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
Status del = env->DeleteFile(LogFileName(soptions.wal_dir, number));
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
}
|
||||
}
|
||||
env->DeleteDir(soptions.wal_dir);
|
||||
}
|
||||
|
||||
env->UnlockFile(lock); // Ignore error since state is already gone
|
||||
env->DeleteFile(lockname);
|
||||
env->DeleteDir(dbname); // Ignore error in case dir contains other files
|
||||
env->DeleteDir(soptions.wal_dir);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2536,6 +2599,9 @@ Status DBImpl::WriteOptionsFile(bool need_mutex_lock,
|
||||
s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)need_mutex_lock;
|
||||
(void)need_enter_write_thread;
|
||||
#endif // !ROCKSDB_LITE
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -2604,22 +2670,21 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
|
||||
DeleteObsoleteOptionsFiles();
|
||||
return s;
|
||||
#else
|
||||
(void)file_name;
|
||||
return Status::OK();
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
|
||||
void DBImpl::NewThreadStatusCfInfo(
|
||||
ColumnFamilyData* cfd) const {
|
||||
void DBImpl::NewThreadStatusCfInfo(ColumnFamilyData* cfd) const {
|
||||
if (immutable_db_options_.enable_thread_tracking) {
|
||||
ThreadStatusUtil::NewColumnFamilyInfo(this, cfd, cfd->GetName(),
|
||||
cfd->ioptions()->env);
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::EraseThreadStatusCfInfo(
|
||||
ColumnFamilyData* cfd) const {
|
||||
void DBImpl::EraseThreadStatusCfInfo(ColumnFamilyData* cfd) const {
|
||||
if (immutable_db_options_.enable_thread_tracking) {
|
||||
ThreadStatusUtil::EraseColumnFamilyInfo(cfd);
|
||||
}
|
||||
@@ -2632,21 +2697,16 @@ void DBImpl::EraseThreadStatusDbInfo() const {
|
||||
}
|
||||
|
||||
#else
|
||||
void DBImpl::NewThreadStatusCfInfo(
|
||||
ColumnFamilyData* cfd) const {
|
||||
}
|
||||
void DBImpl::NewThreadStatusCfInfo(ColumnFamilyData* /*cfd*/) const {}
|
||||
|
||||
void DBImpl::EraseThreadStatusCfInfo(
|
||||
ColumnFamilyData* cfd) const {
|
||||
}
|
||||
void DBImpl::EraseThreadStatusCfInfo(ColumnFamilyData* /*cfd*/) const {}
|
||||
|
||||
void DBImpl::EraseThreadStatusDbInfo() const {
|
||||
}
|
||||
void DBImpl::EraseThreadStatusDbInfo() const {}
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
|
||||
//
|
||||
// A global method that can dump out the build version
|
||||
void DumpRocksDBBuildVersion(Logger * log) {
|
||||
void DumpRocksDBBuildVersion(Logger* log) {
|
||||
#if !defined(IOS_CROSS_COMPILE)
|
||||
// if we compile with Xcode, we don't run build_detect_version, so we don't
|
||||
// generate util/build_version.cc
|
||||
@@ -2783,7 +2843,7 @@ Status DBImpl::IngestExternalFile(
|
||||
if (ingestion_options.ingest_behind) {
|
||||
if (!immutable_db_options_.allow_ingest_behind) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file in DB with allow_ingest_behind=false");
|
||||
"Can't ingest_behind file in DB with allow_ingest_behind=false");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2803,7 +2863,9 @@ Status DBImpl::IngestExternalFile(
|
||||
pending_output_elem = CaptureCurrentFileNumberInPendingOutputs();
|
||||
}
|
||||
|
||||
status = ingestion_job.Prepare(external_files);
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
status = ingestion_job.Prepare(external_files, super_version);
|
||||
CleanupSuperVersion(super_version);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
@@ -2859,8 +2921,8 @@ Status DBImpl::IngestExternalFile(
|
||||
&mutex_, directories_.GetDbDir());
|
||||
}
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context,
|
||||
*mutable_cf_options);
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, *mutable_cf_options,
|
||||
FlushReason::kExternalFileIngestion);
|
||||
}
|
||||
|
||||
// Resume writes to the DB
|
||||
@@ -2916,11 +2978,12 @@ Status DBImpl::VerifyChecksum() {
|
||||
}
|
||||
for (auto& sv : sv_list) {
|
||||
VersionStorageInfo* vstorage = sv->current->storage_info();
|
||||
ColumnFamilyData* cfd = sv->current->cfd();
|
||||
for (int i = 0; i < vstorage->num_non_empty_levels() && s.ok(); i++) {
|
||||
for (size_t j = 0; j < vstorage->LevelFilesBrief(i).num_files && s.ok();
|
||||
j++) {
|
||||
const auto& fd = vstorage->LevelFilesBrief(i).files[j].fd;
|
||||
std::string fname = TableFileName(immutable_db_options_.db_paths,
|
||||
std::string fname = TableFileName(cfd->ioptions()->cf_paths,
|
||||
fd.GetNumber(), fd.GetPathId());
|
||||
s = rocksdb::VerifySstFileChecksum(options, env_options, fname);
|
||||
}
|
||||
@@ -2938,7 +3001,7 @@ Status DBImpl::VerifyChecksum() {
|
||||
}
|
||||
}
|
||||
for (auto cfd : cfd_list) {
|
||||
cfd->Unref();
|
||||
cfd->Unref();
|
||||
}
|
||||
}
|
||||
return s;
|
||||
@@ -2974,5 +3037,4 @@ void DBImpl::WaitForIngestFile() {
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+86
-60
@@ -27,6 +27,7 @@
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
#include "db/pre_release_callback.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
@@ -220,9 +221,12 @@ class DBImpl : public DB {
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status FlushWAL(bool sync) override;
|
||||
bool TEST_WALBufferIsEmpty();
|
||||
virtual Status SyncWAL() override;
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
// REQUIRES: joined the main write queue if two_write_queues is disabled, and
|
||||
// the second write queue otherwise.
|
||||
virtual void SetLastPublishedSequence(SequenceNumber seq);
|
||||
// Returns LastSequence in last_seq_same_as_publish_seq_
|
||||
// mode and LastAllocatedSequence otherwise. This is useful when visiblility
|
||||
@@ -340,6 +344,7 @@ class DBImpl : public DB {
|
||||
|
||||
Status RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
int output_level, uint32_t output_path_id,
|
||||
uint32_t max_subcompactions,
|
||||
const Slice* begin, const Slice* end,
|
||||
bool exclusive,
|
||||
bool disallow_trivial_move = false);
|
||||
@@ -351,6 +356,10 @@ class DBImpl : public DB {
|
||||
Arena* arena, RangeDelAggregator* range_del_agg,
|
||||
ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
LogsWithPrepTracker* logs_with_prep_tracker() {
|
||||
return &logs_with_prep_tracker_;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Extra methods (for testing) that are not in the public DB interface
|
||||
// Implemented in db_impl_debug.cc
|
||||
@@ -362,9 +371,7 @@ class DBImpl : public DB {
|
||||
|
||||
void TEST_SwitchWAL();
|
||||
|
||||
bool TEST_UnableToFlushOldestLog() {
|
||||
return unable_to_flush_oldest_log_;
|
||||
}
|
||||
bool TEST_UnableToReleaseOldestLog() { return unable_to_release_oldest_log_; }
|
||||
|
||||
bool TEST_IsLogGettingFlushed() {
|
||||
return alive_log_files_.begin()->getting_flushed;
|
||||
@@ -476,7 +483,7 @@ class DBImpl : public DB {
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
// If FindObsoleteFiles() was run, we need to also run
|
||||
// PurgeObsoleteFiles(), even if disable_delete_obsolete_files_ is true
|
||||
void PurgeObsoleteFiles(const JobContext& background_contet,
|
||||
void PurgeObsoleteFiles(JobContext& background_contet,
|
||||
bool schedule_only = false);
|
||||
|
||||
void SchedulePurge();
|
||||
@@ -591,7 +598,7 @@ class DBImpl : public DB {
|
||||
size_t batch_cnt) {
|
||||
recovered_transactions_[name] =
|
||||
new RecoveredTransaction(log, name, batch, seq, batch_cnt);
|
||||
MarkLogAsContainingPrepSection(log);
|
||||
logs_with_prep_tracker_.MarkLogAsContainingPrepSection(log);
|
||||
}
|
||||
|
||||
void DeleteRecoveredTransaction(const std::string& name) {
|
||||
@@ -599,7 +606,7 @@ class DBImpl : public DB {
|
||||
assert(it != recovered_transactions_.end());
|
||||
auto* trx = it->second;
|
||||
recovered_transactions_.erase(it);
|
||||
MarkLogAsHavingPrepSectionFlushed(trx->log_number_);
|
||||
logs_with_prep_tracker_.MarkLogAsHavingPrepSectionFlushed(trx->log_number_);
|
||||
delete trx;
|
||||
}
|
||||
|
||||
@@ -611,14 +618,15 @@ class DBImpl : public DB {
|
||||
recovered_transactions_.clear();
|
||||
}
|
||||
|
||||
void MarkLogAsHavingPrepSectionFlushed(uint64_t log);
|
||||
void MarkLogAsContainingPrepSection(uint64_t log);
|
||||
void AddToLogsToFreeQueue(log::Writer* log_writer) {
|
||||
logs_to_free_queue_.push_back(log_writer);
|
||||
}
|
||||
|
||||
void SetSnapshotChecker(SnapshotChecker* snapshot_checker);
|
||||
|
||||
// Not thread-safe.
|
||||
void SetRecoverableStatePreReleaseCallback(PreReleaseCallback* callback);
|
||||
|
||||
InstrumentedMutex* mutex() { return &mutex_; }
|
||||
|
||||
Status NewDB();
|
||||
@@ -631,6 +639,9 @@ class DBImpl : public DB {
|
||||
|
||||
virtual Status Close() override;
|
||||
|
||||
static Status CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory);
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
@@ -683,14 +694,20 @@ class DBImpl : public DB {
|
||||
void EraseThreadStatusDbInfo() const;
|
||||
|
||||
// If disable_memtable is set the application logic must guarantee that the
|
||||
// batch will still be skipped from memtable during the recovery. In
|
||||
// WriteCommitted it is guarnateed since disable_memtable is used for prepare
|
||||
// batch which will be written to memtable later during the commit, and in
|
||||
// WritePrepared it is guaranteed since it will be used only for WAL markers
|
||||
// which will never be written to memtable.
|
||||
// batch_cnt is expected to be non-zero in seq_per_batch mode and indicates
|
||||
// the number of sub-patches. A sub-patch is a subset of the write batch that
|
||||
// does not have duplicate keys.
|
||||
// batch will still be skipped from memtable during the recovery. An excption
|
||||
// to this is seq_per_batch_ mode, in which since each batch already takes one
|
||||
// seq, it is ok for the batch to write to memtable during recovery as long as
|
||||
// it only takes one sequence number: i.e., no duplicate keys.
|
||||
// In WriteCommitted it is guarnateed since disable_memtable is used for
|
||||
// prepare batch which will be written to memtable later during the commit,
|
||||
// and in WritePrepared it is guaranteed since it will be used only for WAL
|
||||
// markers which will never be written to memtable. If the commit marker is
|
||||
// accompanied with CommitTimeWriteBatch that is not written to memtable as
|
||||
// long as it has no duplicate keys, it does not violate the one-seq-per-batch
|
||||
// policy.
|
||||
// batch_cnt is expected to be non-zero in seq_per_batch mode and
|
||||
// indicates the number of sub-patches. A sub-patch is a subset of the write
|
||||
// batch that does not have duplicate keys.
|
||||
Status WriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
@@ -713,8 +730,6 @@ class DBImpl : public DB {
|
||||
uint64_t* seq_used = nullptr, size_t batch_cnt = 0,
|
||||
PreReleaseCallback* pre_release_callback = nullptr);
|
||||
|
||||
uint64_t FindMinLogContainingOutstandingPrep();
|
||||
uint64_t FindMinPrepLogReferencedByMemTable();
|
||||
// write cached_recoverable_state_ to memtable if it is not empty
|
||||
// The writer must be the leader in write_thread_ and holding mutex_
|
||||
Status WriteRecoverableState();
|
||||
@@ -726,6 +741,7 @@ class DBImpl : public DB {
|
||||
friend class DB;
|
||||
friend class InternalStats;
|
||||
friend class PessimisticTransaction;
|
||||
friend class TransactionBaseImpl;
|
||||
friend class WriteCommittedTxn;
|
||||
friend class WritePreparedTxn;
|
||||
friend class WritePreparedTxnDB;
|
||||
@@ -735,6 +751,7 @@ class DBImpl : public DB {
|
||||
#endif
|
||||
friend struct SuperVersion;
|
||||
friend class CompactedDBImpl;
|
||||
friend class DBTest_ConcurrentFlushWAL_Test;
|
||||
#ifndef NDEBUG
|
||||
friend class DBTest2_ReadCallbackTest_Test;
|
||||
friend class WriteCallbackTest_WriteWithCallbackTest_Test;
|
||||
@@ -782,7 +799,8 @@ class DBImpl : public DB {
|
||||
void DeleteObsoleteFiles();
|
||||
// Delete obsolete files and log status and information of file deletion
|
||||
void DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
FileType type, uint64_t number, uint32_t path_id);
|
||||
const std::string& path_to_sync, FileType type,
|
||||
uint64_t number);
|
||||
|
||||
// Background process needs to call
|
||||
// auto x = CaptureCurrentFileNumberInPendingOutputs()
|
||||
@@ -833,7 +851,8 @@ class DBImpl : public DB {
|
||||
|
||||
Status ScheduleFlushes(WriteContext* context);
|
||||
|
||||
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context);
|
||||
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
FlushReason flush_reason = FlushReason::kOthers);
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
@@ -873,7 +892,7 @@ class DBImpl : public DB {
|
||||
size_t seq_inc);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
void WriteCallbackStatusCheck(const Status& status);
|
||||
void WriteStatusCheck(const Status& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
|
||||
void MemTableInsertStatusCheck(const Status& memtable_insert_status);
|
||||
@@ -902,8 +921,8 @@ class DBImpl : public DB {
|
||||
void MaybeScheduleFlushOrCompaction();
|
||||
void SchedulePendingFlush(ColumnFamilyData* cfd, FlushReason flush_reason);
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingPurge(std::string fname, FileType type, uint64_t number,
|
||||
uint32_t path_id, int job_id);
|
||||
void SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
FileType type, uint64_t number, int job_id);
|
||||
static void BGWorkCompaction(void* arg);
|
||||
// Runs a pre-chosen universal compaction involving bottom level in a
|
||||
// separate, bottom-pri thread pool.
|
||||
@@ -921,6 +940,9 @@ class DBImpl : public DB {
|
||||
Status BackgroundFlush(bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
bool EnoughRoomForCompaction(const std::vector<CompactionInputFiles>& inputs,
|
||||
bool* sfm_bookkeeping, LogBuffer* log_buffer);
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
// dump rocksdb.stats to LOG
|
||||
@@ -945,10 +967,12 @@ class DBImpl : public DB {
|
||||
// helper function to call after some of the logs_ were synced
|
||||
void MarkLogsSynced(uint64_t up_to, bool synced_dir, const Status& status);
|
||||
|
||||
const Snapshot* GetSnapshotImpl(bool is_write_conflict_boundary);
|
||||
SnapshotImpl* GetSnapshotImpl(bool is_write_conflict_boundary);
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
Directory* GetDataDir(ColumnFamilyData* cfd, size_t path_id) const;
|
||||
|
||||
Status CloseHelper();
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
@@ -1086,7 +1110,7 @@ class DBImpl : public DB {
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths);
|
||||
|
||||
Directory* GetDataDir(size_t path_id);
|
||||
Directory* GetDataDir(size_t path_id) const;
|
||||
|
||||
Directory* GetWalDir() {
|
||||
if (wal_dir_) {
|
||||
@@ -1101,9 +1125,6 @@ class DBImpl : public DB {
|
||||
std::unique_ptr<Directory> db_dir_;
|
||||
std::vector<std::unique_ptr<Directory>> data_dirs_;
|
||||
std::unique_ptr<Directory> wal_dir_;
|
||||
|
||||
Status CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory) const;
|
||||
};
|
||||
|
||||
Directories directories_;
|
||||
@@ -1145,13 +1166,13 @@ class DBImpl : public DB {
|
||||
// purge_queue_
|
||||
struct PurgeFileInfo {
|
||||
std::string fname;
|
||||
std::string dir_to_sync;
|
||||
FileType type;
|
||||
uint64_t number;
|
||||
uint32_t path_id;
|
||||
int job_id;
|
||||
PurgeFileInfo(std::string fn, FileType t, uint64_t num, uint32_t pid,
|
||||
PurgeFileInfo(std::string fn, std::string d, FileType t, uint64_t num,
|
||||
int jid)
|
||||
: fname(fn), type(t), number(num), path_id(pid), job_id(jid) {}
|
||||
: fname(fn), dir_to_sync(d), type(t), number(num), job_id(jid) {}
|
||||
};
|
||||
|
||||
// flush_queue_ and compaction_queue_ hold column families that we need to
|
||||
@@ -1182,6 +1203,10 @@ class DBImpl : public DB {
|
||||
// A queue to store filenames of the files to be purged
|
||||
std::deque<PurgeFileInfo> purge_queue_;
|
||||
|
||||
// A vector to store the file numbers that have been assigned to certain
|
||||
// JobContext. Current implementation tracks ssts only.
|
||||
std::vector<uint64_t> files_grabbed_for_purge_;
|
||||
|
||||
// A queue to store log writers to close
|
||||
std::deque<log::Writer*> logs_to_free_queue_;
|
||||
int unscheduled_flushes_;
|
||||
@@ -1277,7 +1302,7 @@ class DBImpl : public DB {
|
||||
// We must attempt to free the dependent memtables again
|
||||
// at a later time after the transaction in the oldest
|
||||
// log is fully commited.
|
||||
bool unable_to_flush_oldest_log_;
|
||||
bool unable_to_release_oldest_log_;
|
||||
|
||||
static const int KEEP_LOG_FILE_NUM = 1000;
|
||||
// MSVC version 1800 still does not have constexpr for ::max()
|
||||
@@ -1314,38 +1339,16 @@ class DBImpl : public DB {
|
||||
// Indicate DB was opened successfully
|
||||
bool opened_successfully_;
|
||||
|
||||
// REQUIRES: logs_with_prep_mutex_ held
|
||||
//
|
||||
// sorted list of log numbers still containing prepared data.
|
||||
// this is used by FindObsoleteFiles to determine which
|
||||
// flushed logs we must keep around because they still
|
||||
// contain prepared data which has not been committed or rolled back
|
||||
struct LogCnt {
|
||||
uint64_t log; // the log number
|
||||
uint64_t cnt; // number of prepared sections in the log
|
||||
};
|
||||
std::vector<LogCnt> logs_with_prep_;
|
||||
std::mutex logs_with_prep_mutex_;
|
||||
|
||||
// REQUIRES: prepared_section_completed_mutex_ held
|
||||
//
|
||||
// to be used in conjunction with logs_with_prep_.
|
||||
// once a transaction with data in log L is committed or rolled back
|
||||
// rather than updating logs_with_prep_ directly we keep track of that
|
||||
// in prepared_section_completed_ which maps LOG -> instance_count. This helps
|
||||
// avoiding contention between a commit thread and the prepare threads.
|
||||
//
|
||||
// when trying to determine the minimum log still active we first
|
||||
// consult logs_with_prep_. while that root value maps to
|
||||
// an equal value in prepared_section_completed_ we erase the log from
|
||||
// both logs_with_prep_ and prepared_section_completed_.
|
||||
std::unordered_map<uint64_t, uint64_t> prepared_section_completed_;
|
||||
std::mutex prepared_section_completed_mutex_;
|
||||
LogsWithPrepTracker logs_with_prep_tracker_;
|
||||
|
||||
// Callback for compaction to check if a key is visible to a snapshot.
|
||||
// REQUIRES: mutex held
|
||||
std::unique_ptr<SnapshotChecker> snapshot_checker_;
|
||||
|
||||
// Callback for when the cached_recoverable_state_ is written to memtable
|
||||
// Only to be set during initialization
|
||||
std::unique_ptr<PreReleaseCallback> recoverable_state_pre_release_callback_;
|
||||
|
||||
// No copying allowed
|
||||
DBImpl(const DBImpl&);
|
||||
void operator=(const DBImpl&);
|
||||
@@ -1359,7 +1362,8 @@ class DBImpl : public DB {
|
||||
// state needs flush or compaction.
|
||||
void InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
FlushReason flush_reason = FlushReason::kOthers);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
@@ -1384,6 +1388,9 @@ class DBImpl : public DB {
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
|
||||
|
||||
bool ShouldPurge(uint64_t file_number) const;
|
||||
void MarkAsGrabbedForPurge(uint64_t file_number);
|
||||
|
||||
size_t GetWalPreallocateBlockSize(uint64_t write_buffer_size) const;
|
||||
Env::WriteLifeTimeHint CalculateWALWriteHint() {
|
||||
return Env::WLTH_SHORT;
|
||||
@@ -1428,6 +1435,25 @@ extern CompressionType GetCompressionFlush(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Return the earliest log file to keep after the memtable flush is
|
||||
// finalized.
|
||||
// `cfd_to_flush` is the column family whose memtable (specified in
|
||||
// `memtables_to_flush`) will be flushed and thus will not depend on any WAL
|
||||
// file.
|
||||
// The function is only applicable to 2pc mode.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeep(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
autovector<VersionEdit*> edit_list,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker);
|
||||
|
||||
// `cfd_to_flush` is the column family whose memtable will be flushed and thus
|
||||
// will not depend on any WAL file. nullptr means no memtable is being flushed.
|
||||
// The function is only applicable to 2pc mode.
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const ColumnFamilyData* cfd_to_flush,
|
||||
const autovector<MemTable*>& memtables_to_flush);
|
||||
|
||||
// Fix user-supplied options to be reasonable
|
||||
template <class T, class V>
|
||||
static void ClipToRange(T* ptr, V minvalue, V maxvalue) {
|
||||
|
||||
+225
-130
@@ -24,6 +24,35 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
bool DBImpl::EnoughRoomForCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs,
|
||||
bool* sfm_reserved_compact_space, LogBuffer* log_buffer) {
|
||||
// Check if we have enough room to do the compaction
|
||||
bool enough_room = true;
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
if (sfm) {
|
||||
enough_room = sfm->EnoughRoomForCompaction(inputs);
|
||||
if (enough_room) {
|
||||
*sfm_reserved_compact_space = true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)inputs;
|
||||
(void)sfm_reserved_compact_space;
|
||||
#endif // ROCKSDB_LITE
|
||||
if (!enough_room) {
|
||||
// Just in case tests want to change the value of enough_room
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction():CancelledCompaction", &enough_room);
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"Cancelled compaction because not enough room");
|
||||
RecordTick(stats_, COMPACTION_CANCELLED, 1);
|
||||
}
|
||||
return enough_room;
|
||||
}
|
||||
|
||||
Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
mutex_.AssertHeld();
|
||||
@@ -50,6 +79,9 @@ Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
log->get_log_number());
|
||||
s = log->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = directories_.GetWalDir()->Fsync();
|
||||
@@ -95,8 +127,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
env_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
job_context, log_buffer, directories_.GetDbDir(),
|
||||
directories_.GetDataDir(0U),
|
||||
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);
|
||||
|
||||
@@ -129,7 +160,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// and EventListener callback will be called when the db_mutex
|
||||
// is unlocked by the current thread.
|
||||
if (s.ok()) {
|
||||
s = flush_job.Run(&file_meta);
|
||||
s = flush_job.Run(&logs_with_prep_tracker_, &file_meta);
|
||||
} else {
|
||||
flush_job.Cancel();
|
||||
}
|
||||
@@ -169,10 +200,10 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
if (sfm) {
|
||||
// Notify sst_file_manager that a new file was added
|
||||
std::string file_path = MakeTableFileName(
|
||||
immutable_db_options_.db_paths[0].path, file_meta.fd.GetNumber());
|
||||
cfd->ioptions()->cf_paths[0].path, file_meta.fd.GetNumber());
|
||||
sfm->OnAddFile(file_path);
|
||||
if (sfm->IsMaxAllowedSpaceReached() && bg_error_.ok()) {
|
||||
Status new_bg_error = Status::IOError("Max allowed space was reached");
|
||||
Status new_bg_error = Status::NoSpace("Max allowed space was reached");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::FlushMemTableToOutputFile:MaxAllowedSpaceReached",
|
||||
&new_bg_error);
|
||||
@@ -214,7 +245,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
info.cf_name = cfd->GetName();
|
||||
// TODO(yhchiang): make db_paths dynamic in case flush does not
|
||||
// go to L0 in the future.
|
||||
info.file_path = MakeTableFileName(immutable_db_options_.db_paths[0].path,
|
||||
info.file_path = MakeTableFileName(cfd->ioptions()->cf_paths[0].path,
|
||||
file_meta->fd.GetNumber());
|
||||
info.thread_id = env_->GetThreadID();
|
||||
info.job_id = job_id;
|
||||
@@ -231,6 +262,12 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
mutex_.Lock();
|
||||
// no need to signal bg_cv_ as it will be signaled at the end of the
|
||||
// flush process.
|
||||
#else
|
||||
(void)cfd;
|
||||
(void)file_meta;
|
||||
(void)mutable_cf_options;
|
||||
(void)job_id;
|
||||
(void)prop;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -259,7 +296,7 @@ void DBImpl::NotifyOnFlushCompleted(ColumnFamilyData* cfd,
|
||||
info.cf_name = cfd->GetName();
|
||||
// TODO(yhchiang): make db_paths dynamic in case flush does not
|
||||
// go to L0 in the future.
|
||||
info.file_path = MakeTableFileName(immutable_db_options_.db_paths[0].path,
|
||||
info.file_path = MakeTableFileName(cfd->ioptions()->cf_paths[0].path,
|
||||
file_meta->fd.GetNumber());
|
||||
info.thread_id = env_->GetThreadID();
|
||||
info.job_id = job_id;
|
||||
@@ -276,18 +313,25 @@ void DBImpl::NotifyOnFlushCompleted(ColumnFamilyData* cfd,
|
||||
mutex_.Lock();
|
||||
// no need to signal bg_cv_ as it will be signaled at the end of the
|
||||
// flush process.
|
||||
#else
|
||||
(void)cfd;
|
||||
(void)file_meta;
|
||||
(void)mutable_cf_options;
|
||||
(void)job_id;
|
||||
(void)prop;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
if (options.target_path_id >= immutable_db_options_.db_paths.size()) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
if (options.target_path_id >= cfd->ioptions()->cf_paths.size()) {
|
||||
return Status::InvalidArgument("Invalid target path ID");
|
||||
}
|
||||
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
bool exclusive = options.exclusive_manual_compaction;
|
||||
|
||||
bool flush_needed = true;
|
||||
@@ -384,7 +428,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
}
|
||||
s = RunManualCompaction(cfd, ColumnFamilyData::kCompactAllLevels,
|
||||
final_output_level, options.target_path_id,
|
||||
begin, end, exclusive);
|
||||
options.max_subcompactions, begin, end, exclusive);
|
||||
} else {
|
||||
for (int level = 0; level <= max_level_with_files; level++) {
|
||||
int output_level;
|
||||
@@ -418,7 +462,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
}
|
||||
}
|
||||
s = RunManualCompaction(cfd, level, output_level, options.target_path_id,
|
||||
begin, end, exclusive);
|
||||
options.max_subcompactions, begin, end, exclusive);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -457,14 +501,19 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::CompactFiles(
|
||||
const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names,
|
||||
const int output_level, const int output_path_id,
|
||||
std::vector<std::string>* const output_file_names) {
|
||||
Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names,
|
||||
const int output_level, const int output_path_id,
|
||||
std::vector<std::string>* const output_file_names) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
// not supported in lite version
|
||||
(void)compact_options;
|
||||
(void)column_family;
|
||||
(void)input_file_names;
|
||||
(void)output_level;
|
||||
(void)output_path_id;
|
||||
(void)output_file_names;
|
||||
// not supported in lite version
|
||||
return Status::NotSupported("Not supported in ROCKSDB LITE");
|
||||
#else
|
||||
if (column_family == nullptr) {
|
||||
@@ -488,9 +537,9 @@ Status DBImpl::CompactFiles(
|
||||
// IngestExternalFile() calls to finish.
|
||||
WaitForIngestFile();
|
||||
|
||||
s = CompactFilesImpl(compact_options, cfd, sv->current,
|
||||
input_file_names, output_file_names, output_level,
|
||||
output_path_id, &job_context, &log_buffer);
|
||||
s = CompactFilesImpl(compact_options, cfd, sv->current, input_file_names,
|
||||
output_file_names, output_level, output_path_id,
|
||||
&job_context, &log_buffer);
|
||||
}
|
||||
if (sv->Unref()) {
|
||||
mutex_.Lock();
|
||||
@@ -533,9 +582,8 @@ Status DBImpl::CompactFiles(
|
||||
Status DBImpl::CompactFilesImpl(
|
||||
const CompactionOptions& compact_options, ColumnFamilyData* cfd,
|
||||
Version* version, const std::vector<std::string>& input_file_names,
|
||||
std::vector<std::string>* const output_file_names,
|
||||
const int output_level, int output_path_id, JobContext* job_context,
|
||||
LogBuffer* log_buffer) {
|
||||
std::vector<std::string>* const output_file_names, const int output_level,
|
||||
int output_path_id, JobContext* job_context, LogBuffer* log_buffer) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
@@ -553,7 +601,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
version->GetColumnFamilyMetaData(&cf_meta);
|
||||
|
||||
if (output_path_id < 0) {
|
||||
if (immutable_db_options_.db_paths.size() == 1U) {
|
||||
if (cfd->ioptions()->cf_paths.size() == 1U) {
|
||||
output_path_id = 0;
|
||||
} else {
|
||||
return Status::NotSupported(
|
||||
@@ -582,6 +630,16 @@ Status DBImpl::CompactFilesImpl(
|
||||
"files are already being compacted");
|
||||
}
|
||||
}
|
||||
bool sfm_reserved_compact_space = false;
|
||||
// First check if we have enough room to do the compaction
|
||||
bool enough_room = EnoughRoomForCompaction(
|
||||
input_files, &sfm_reserved_compact_space, log_buffer);
|
||||
|
||||
if (!enough_room) {
|
||||
// m's vars will get set properly at the end of this function,
|
||||
// as long as status == CompactionTooLarge
|
||||
return Status::CompactionTooLarge();
|
||||
}
|
||||
|
||||
// At this point, CompactFiles will be run.
|
||||
bg_compaction_scheduled_++;
|
||||
@@ -615,9 +673,9 @@ Status DBImpl::CompactFilesImpl(
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
env_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
preserve_deletes_seqnum_.load(), log_buffer, directories_.GetDbDir(),
|
||||
directories_.GetDataDir(c->output_path_id()), stats_, &mutex_, &bg_error_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
table_cache_, &event_logger_,
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()), stats_, &mutex_,
|
||||
&bg_error_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
nullptr); // Here we pass a nullptr for CompactionJobStats because
|
||||
@@ -655,9 +713,17 @@ Status DBImpl::CompactFilesImpl(
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
*c->mutable_cf_options());
|
||||
*c->mutable_cf_options(), FlushReason::kManualCompaction);
|
||||
}
|
||||
c->ReleaseCompactionFiles(s);
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Need to make sure SstFileManager does its bookkeeping
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
if (sfm && sfm_reserved_compact_space) {
|
||||
sfm->OnCompactionCompletion(c.get());
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
@@ -684,9 +750,10 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
if (output_file_names != nullptr) {
|
||||
for (const auto newf : c->edit()->GetNewFiles()) {
|
||||
(*output_file_names).push_back(TableFileName(
|
||||
immutable_db_options_.db_paths, newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()) );
|
||||
(*output_file_names)
|
||||
.push_back(TableFileName(c->immutable_cf_options()->cf_paths,
|
||||
newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,6 +763,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
if (bg_compaction_scheduled_ == 0) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
TEST_SYNC_POINT("CompactFilesImpl:End");
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -730,9 +798,8 @@ Status DBImpl::ContinueBackgroundWork() {
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnCompactionCompleted(
|
||||
ColumnFamilyData* cfd, Compaction *c, const Status &st,
|
||||
const CompactionJobStats& compaction_job_stats,
|
||||
const int job_id) {
|
||||
ColumnFamilyData* cfd, Compaction* c, const Status& st,
|
||||
const CompactionJobStats& compaction_job_stats, const int job_id) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (immutable_db_options_.listeners.size() == 0U) {
|
||||
return;
|
||||
@@ -760,7 +827,7 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
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(immutable_db_options_.db_paths,
|
||||
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) {
|
||||
@@ -773,9 +840,9 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
}
|
||||
}
|
||||
for (const auto newf : c->edit()->GetNewFiles()) {
|
||||
info.output_files.push_back(TableFileName(immutable_db_options_.db_paths,
|
||||
newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()));
|
||||
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->OnCompactionCompleted(this, info);
|
||||
@@ -785,6 +852,12 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
current->Unref();
|
||||
// no need to signal bg_cv_ as it will be signaled at the end of the
|
||||
// flush process.
|
||||
#else
|
||||
(void)cfd;
|
||||
(void)c;
|
||||
(void)st;
|
||||
(void)compaction_job_stats;
|
||||
(void)job_id;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -881,8 +954,9 @@ int DBImpl::MaxMemCompactionLevel(ColumnFamilyHandle* /*column_family*/) {
|
||||
int DBImpl::Level0StopWriteTrigger(ColumnFamilyHandle* column_family) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return cfh->cfd()->GetSuperVersion()->
|
||||
mutable_cf_options.level0_stop_writes_trigger;
|
||||
return cfh->cfd()
|
||||
->GetSuperVersion()
|
||||
->mutable_cf_options.level0_stop_writes_trigger;
|
||||
}
|
||||
|
||||
Status DBImpl::Flush(const FlushOptions& flush_options,
|
||||
@@ -891,7 +965,7 @@ Status DBImpl::Flush(const FlushOptions& flush_options,
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "[%s] Manual flush start.",
|
||||
cfh->GetName().c_str());
|
||||
Status s =
|
||||
FlushMemTable(cfh->cfd(), flush_options, FlushReason::kManualCompaction);
|
||||
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());
|
||||
@@ -900,6 +974,7 @@ Status DBImpl::Flush(const FlushOptions& flush_options,
|
||||
|
||||
Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
int output_level, uint32_t output_path_id,
|
||||
uint32_t max_subcompactions,
|
||||
const Slice* begin, const Slice* end,
|
||||
bool exclusive, bool disallow_trivial_move) {
|
||||
assert(input_level == ColumnFamilyData::kCompactAllLevels ||
|
||||
@@ -985,11 +1060,12 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
if (ShouldntRunManualCompaction(&manual) || (manual.in_progress == true) ||
|
||||
scheduled ||
|
||||
(((manual.manual_end = &manual.tmp_storage1) != nullptr) &&
|
||||
((compaction = manual.cfd->CompactRange(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), manual.input_level,
|
||||
manual.output_level, manual.output_path_id, manual.begin,
|
||||
manual.end, &manual.manual_end, &manual_conflict)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
((compaction = manual.cfd->CompactRange(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), manual.input_level,
|
||||
manual.output_level, manual.output_path_id, max_subcompactions,
|
||||
manual.begin, manual.end, &manual.manual_end,
|
||||
&manual_conflict)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
// exclusive manual compactions should not see a conflict during
|
||||
// CompactRange
|
||||
assert(!exclusive || !manual_conflict);
|
||||
@@ -1122,7 +1198,7 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
}
|
||||
auto bg_job_limits = GetBGJobLimits();
|
||||
bool is_flush_pool_empty =
|
||||
env_->GetBackgroundThreads(Env::Priority::HIGH) == 0;
|
||||
env_->GetBackgroundThreads(Env::Priority::HIGH) == 0;
|
||||
while (!is_flush_pool_empty && unscheduled_flushes_ > 0 &&
|
||||
bg_flush_scheduled_ < bg_job_limits.max_flushes) {
|
||||
unscheduled_flushes_--;
|
||||
@@ -1198,26 +1274,26 @@ DBImpl::BGJobLimits DBImpl::GetBGJobLimits(int max_background_flushes,
|
||||
}
|
||||
|
||||
void DBImpl::AddToCompactionQueue(ColumnFamilyData* cfd) {
|
||||
assert(!cfd->pending_compaction());
|
||||
assert(!cfd->queued_for_compaction());
|
||||
cfd->Ref();
|
||||
compaction_queue_.push_back(cfd);
|
||||
cfd->set_pending_compaction(true);
|
||||
cfd->set_queued_for_compaction(true);
|
||||
}
|
||||
|
||||
ColumnFamilyData* DBImpl::PopFirstFromCompactionQueue() {
|
||||
assert(!compaction_queue_.empty());
|
||||
auto cfd = *compaction_queue_.begin();
|
||||
compaction_queue_.pop_front();
|
||||
assert(cfd->pending_compaction());
|
||||
cfd->set_pending_compaction(false);
|
||||
assert(cfd->queued_for_compaction());
|
||||
cfd->set_queued_for_compaction(false);
|
||||
return cfd;
|
||||
}
|
||||
|
||||
void DBImpl::AddToFlushQueue(ColumnFamilyData* cfd, FlushReason flush_reason) {
|
||||
assert(!cfd->pending_flush());
|
||||
assert(!cfd->queued_for_flush());
|
||||
cfd->Ref();
|
||||
flush_queue_.push_back(cfd);
|
||||
cfd->set_pending_flush(true);
|
||||
cfd->set_queued_for_flush(true);
|
||||
cfd->SetFlushReason(flush_reason);
|
||||
}
|
||||
|
||||
@@ -1225,32 +1301,31 @@ ColumnFamilyData* DBImpl::PopFirstFromFlushQueue() {
|
||||
assert(!flush_queue_.empty());
|
||||
auto cfd = *flush_queue_.begin();
|
||||
flush_queue_.pop_front();
|
||||
assert(cfd->pending_flush());
|
||||
cfd->set_pending_flush(false);
|
||||
assert(cfd->queued_for_flush());
|
||||
cfd->set_queued_for_flush(false);
|
||||
// TODO: need to unset flush reason?
|
||||
return cfd;
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingFlush(ColumnFamilyData* cfd,
|
||||
FlushReason flush_reason) {
|
||||
if (!cfd->pending_flush() && cfd->imm()->IsFlushPending()) {
|
||||
if (!cfd->queued_for_flush() && cfd->imm()->IsFlushPending()) {
|
||||
AddToFlushQueue(cfd, flush_reason);
|
||||
++unscheduled_flushes_;
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingCompaction(ColumnFamilyData* cfd) {
|
||||
if (!cfd->pending_compaction() && cfd->NeedsCompaction()) {
|
||||
if (!cfd->queued_for_compaction() && cfd->NeedsCompaction()) {
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingPurge(std::string fname, FileType type,
|
||||
uint64_t number, uint32_t path_id,
|
||||
int job_id) {
|
||||
void DBImpl::SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
FileType type, uint64_t number, int job_id) {
|
||||
mutex_.AssertHeld();
|
||||
PurgeFileInfo file_info(fname, type, number, path_id, job_id);
|
||||
PurgeFileInfo file_info(fname, dir_to_sync, type, number, job_id);
|
||||
purge_queue_.push_back(std::move(file_info));
|
||||
}
|
||||
|
||||
@@ -1379,7 +1454,7 @@ void DBImpl::BackgroundCallFlush() {
|
||||
// chew up resources for failed flushes for the duration of
|
||||
// the problem.
|
||||
uint64_t error_cnt =
|
||||
default_cf_internal_stats_->BumpAndGetBackgroundErrorCount();
|
||||
default_cf_internal_stats_->BumpAndGetBackgroundErrorCount();
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
@@ -1479,6 +1554,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
// have created (they might not be all recorded in job_context in case of a
|
||||
// failure). Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:FoundObsoleteFiles");
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
@@ -1492,6 +1568,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
log_buffer.FlushBufferToLog();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles");
|
||||
}
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
@@ -1576,9 +1653,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
|
||||
// InternalKey manual_end_storage;
|
||||
// InternalKey* manual_end = &manual_end_storage;
|
||||
#ifndef ROCKSDB_LITE
|
||||
bool sfm_bookkeeping = false;
|
||||
#endif // ROCKSDB_LITE
|
||||
bool sfm_reserved_compact_space = false;
|
||||
if (is_manual) {
|
||||
ManualCompactionState* m = manual_compaction;
|
||||
assert(m->in_progress);
|
||||
@@ -1592,16 +1667,29 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"));
|
||||
} else {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Manual compaction from level-%d to level-%d from %s .. "
|
||||
"%s; will stop at %s\n",
|
||||
m->cfd->GetName().c_str(), m->input_level, c->output_level(),
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"),
|
||||
((m->done || m->manual_end == nullptr)
|
||||
? "(end)"
|
||||
: m->manual_end->DebugString().c_str()));
|
||||
// First check if we have enough room to do the compaction
|
||||
bool enough_room = EnoughRoomForCompaction(
|
||||
*(c->inputs()), &sfm_reserved_compact_space, log_buffer);
|
||||
|
||||
if (!enough_room) {
|
||||
// Then don't do the compaction
|
||||
c->ReleaseCompactionFiles(status);
|
||||
c.reset();
|
||||
// m's vars will get set properly at the end of this function,
|
||||
// as long as status == CompactionTooLarge
|
||||
status = Status::CompactionTooLarge();
|
||||
} else {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Manual compaction from level-%d to level-%d from %s .. "
|
||||
"%s; will stop at %s\n",
|
||||
m->cfd->GetName().c_str(), m->input_level, c->output_level(),
|
||||
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
|
||||
(m->end ? m->end->DebugString().c_str() : "(end)"),
|
||||
((m->done || m->manual_end == nullptr)
|
||||
? "(end)"
|
||||
: m->manual_end->DebugString().c_str()));
|
||||
}
|
||||
}
|
||||
} else if (!is_prepicked && !compaction_queue_.empty()) {
|
||||
if (HasExclusiveManualCompaction()) {
|
||||
@@ -1642,24 +1730,10 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c.reset(cfd->PickCompaction(*mutable_cf_options, log_buffer));
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction():AfterPickCompaction");
|
||||
|
||||
bool enough_room = true;
|
||||
if (c != nullptr) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
if (sfm) {
|
||||
enough_room = sfm->EnoughRoomForCompaction(c.get());
|
||||
if (enough_room) {
|
||||
sfm_bookkeeping = true;
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
if (!enough_room) {
|
||||
// Just in case tests want to change the value of enough_room
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction():CancelledCompaction",
|
||||
&enough_room);
|
||||
}
|
||||
bool enough_room = EnoughRoomForCompaction(
|
||||
*(c->inputs()), &sfm_reserved_compact_space, log_buffer);
|
||||
|
||||
if (!enough_room) {
|
||||
// Then don't do the compaction
|
||||
c->ReleaseCompactionFiles(status);
|
||||
@@ -1668,9 +1742,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
->storage_info()
|
||||
->ComputeCompactionScore(*(c->immutable_cf_options()),
|
||||
*(c->mutable_cf_options()));
|
||||
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"Cancelled compaction because not enough room");
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
|
||||
@@ -1678,7 +1749,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// Don't need to sleep here, because BackgroundCallCompaction
|
||||
// will sleep if !s.ok()
|
||||
status = Status::CompactionTooLarge();
|
||||
RecordTick(stats_, COMPACTION_CANCELLED, 1);
|
||||
} else {
|
||||
// update statistics
|
||||
MeasureTime(stats_, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
@@ -1727,7 +1797,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
*c->mutable_cf_options());
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Deleted %d files\n",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
c->num_input_files(0));
|
||||
@@ -1758,11 +1828,11 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
f->largest, f->smallest_seqno, f->largest_seqno,
|
||||
f->marked_for_compaction);
|
||||
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Moving #%" PRIu64
|
||||
" to level-%d %" PRIu64 " bytes\n",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
f->fd.GetNumber(), c->output_level(),
|
||||
f->fd.GetFileSize());
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Moving #%" PRIu64 " to level-%d %" PRIu64 " bytes\n",
|
||||
c->column_family_data()->GetName().c_str(), f->fd.GetNumber(),
|
||||
c->output_level(), f->fd.GetFileSize());
|
||||
++moved_files;
|
||||
moved_bytes += f->fd.GetFileSize();
|
||||
}
|
||||
@@ -1774,7 +1844,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
*c->mutable_cf_options());
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
c->column_family_data()->internal_stats()->IncBytesMoved(c->output_level(),
|
||||
@@ -1796,9 +1866,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
|
||||
// Clear Instrument
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
} else if (c->column_family_data()->ioptions()->compaction_style ==
|
||||
kCompactionStyleUniversal &&
|
||||
!is_prepicked && c->output_level() > 0 &&
|
||||
} else if (!is_prepicked && c->output_level() > 0 &&
|
||||
c->output_level() ==
|
||||
c->column_family_data()
|
||||
->current()
|
||||
@@ -1806,9 +1874,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
->MaxOutputLevel(
|
||||
immutable_db_options_.allow_ingest_behind) &&
|
||||
env_->GetBackgroundThreads(Env::Priority::BOTTOM) > 0) {
|
||||
// Forward universal compactions involving last level to the bottom pool
|
||||
// if it exists, such that long-running compactions can't block short-
|
||||
// lived ones, like L0->L0s.
|
||||
// Forward compactions involving last level to the bottom pool if it exists,
|
||||
// such that compactions unlikely to contribute to write stalls can be
|
||||
// delayed or deprioritized.
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:ForwardToBottomPriPool");
|
||||
CompactionArg* ca = new CompactionArg;
|
||||
ca->db = this;
|
||||
@@ -1819,7 +1887,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
env_->Schedule(&DBImpl::BGWorkBottomCompaction, ca, Env::Priority::BOTTOM,
|
||||
this, &DBImpl::UnscheduleCallback);
|
||||
} else {
|
||||
int output_level __attribute__((__unused__));
|
||||
int output_level __attribute__((__unused__));
|
||||
output_level = c->output_level();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:NonTrivial",
|
||||
&output_level);
|
||||
@@ -1836,8 +1904,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
env_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
preserve_deletes_seqnum_.load(), log_buffer, directories_.GetDbDir(),
|
||||
directories_.GetDataDir(c->output_path_id()), stats_, &mutex_,
|
||||
&bg_error_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()), stats_,
|
||||
&mutex_, &bg_error_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
@@ -1853,7 +1921,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
*c->mutable_cf_options());
|
||||
*c->mutable_cf_options(), FlushReason::kAutoCompaction);
|
||||
}
|
||||
*made_progress = true;
|
||||
}
|
||||
@@ -1865,14 +1933,13 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
// Need to make sure SstFileManager does its bookkeeping
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
if (sfm && sfm_bookkeeping) {
|
||||
if (sfm && sfm_reserved_compact_space) {
|
||||
sfm->OnCompactionCompletion(c.get());
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
NotifyOnCompactionCompleted(
|
||||
c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
NotifyOnCompactionCompleted(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
}
|
||||
// this will unref its input_version and column_family_data
|
||||
c.reset();
|
||||
@@ -1930,7 +1997,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
m->begin = &m->tmp_storage;
|
||||
m->incomplete = true;
|
||||
}
|
||||
m->in_progress = false; // not being processed anymore
|
||||
m->in_progress = false; // not being processed anymore
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:Finish");
|
||||
return status;
|
||||
@@ -2030,8 +2097,7 @@ bool DBImpl::MCOverlap(ManualCompactionState* m, ManualCompactionState* m1) {
|
||||
}
|
||||
|
||||
// SuperVersionContext gets created and destructed outside of the lock --
|
||||
// we
|
||||
// use this convinently to:
|
||||
// we use this conveniently to:
|
||||
// * malloc one SuperVersion() outside of the lock -- new_superversion
|
||||
// * delete SuperVersion()s outside of the lock -- superversions_to_free
|
||||
//
|
||||
@@ -2044,7 +2110,7 @@ bool DBImpl::MCOverlap(ManualCompactionState* m, ManualCompactionState* m1) {
|
||||
|
||||
void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
const MutableCFOptions& mutable_cf_options, FlushReason flush_reason) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
// Update max_total_in_memory_state_
|
||||
@@ -2058,20 +2124,49 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
if (sv_context->new_superversion == nullptr) {
|
||||
sv_context->NewSuperVersion();
|
||||
}
|
||||
cfd->InstallSuperVersion(sv_context, &mutex_,
|
||||
mutable_cf_options);
|
||||
cfd->InstallSuperVersion(sv_context, &mutex_, mutable_cf_options);
|
||||
|
||||
// Whenever we install new SuperVersion, we might need to issue new flushes or
|
||||
// compactions.
|
||||
SchedulePendingFlush(cfd, FlushReason::kSuperVersionChange);
|
||||
SchedulePendingFlush(cfd, flush_reason);
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
// Update max_total_in_memory_state_
|
||||
max_total_in_memory_state_ =
|
||||
max_total_in_memory_state_ - old_memtable_size +
|
||||
mutable_cf_options.write_buffer_size *
|
||||
mutable_cf_options.max_write_buffer_number;
|
||||
max_total_in_memory_state_ = max_total_in_memory_state_ - old_memtable_size +
|
||||
mutable_cf_options.write_buffer_size *
|
||||
mutable_cf_options.max_write_buffer_number;
|
||||
}
|
||||
|
||||
// ShouldPurge is called by FindObsoleteFiles when doing a full scan,
|
||||
// and db mutex (mutex_) should already be held. This function performs a
|
||||
// linear scan of an vector (files_grabbed_for_purge_) in search of a
|
||||
// certain element. We expect FindObsoleteFiles with full scan to occur once
|
||||
// every 10 hours by default, and the size of the vector is small.
|
||||
// Therefore, the cost is affordable even if the mutex is held.
|
||||
// Actually, the current implementation of FindObsoleteFiles with
|
||||
// full_scan=true can issue I/O requests to obtain list of files in
|
||||
// directories, e.g. env_->getChildren while holding db mutex.
|
||||
// In the future, if we want to reduce the cost of search, we may try to keep
|
||||
// the vector sorted.
|
||||
bool DBImpl::ShouldPurge(uint64_t file_number) const {
|
||||
for (auto fn : files_grabbed_for_purge_) {
|
||||
if (file_number == fn) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const auto& purge_file_info : purge_queue_) {
|
||||
if (purge_file_info.number == file_number) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// MarkAsGrabbedForPurge is called by FindObsoleteFiles, and db mutex
|
||||
// (mutex_) should already be held.
|
||||
void DBImpl::MarkAsGrabbedForPurge(uint64_t file_number) {
|
||||
files_grabbed_for_purge_.emplace_back(file_number);
|
||||
}
|
||||
|
||||
void DBImpl::SetSnapshotChecker(SnapshotChecker* snapshot_checker) {
|
||||
|
||||
+15
-5
@@ -25,6 +25,12 @@ void DBImpl::TEST_SwitchWAL() {
|
||||
SwitchWAL(&write_context);
|
||||
}
|
||||
|
||||
bool DBImpl::TEST_WALBufferIsEmpty() {
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
return cur_log_writer->TEST_BufferIsEmpty();
|
||||
}
|
||||
|
||||
int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
@@ -80,7 +86,7 @@ Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO)
|
||||
? level
|
||||
: level + 1;
|
||||
return RunManualCompaction(cfd, level, output_level, 0, begin, end, true,
|
||||
return RunManualCompaction(cfd, level, output_level, 0, 0, begin, end, true,
|
||||
disallow_trivial_move);
|
||||
}
|
||||
|
||||
@@ -184,17 +190,21 @@ Status DBImpl::TEST_GetAllImmutableCFOptions(
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_FindMinLogContainingOutstandingPrep() {
|
||||
return FindMinLogContainingOutstandingPrep();
|
||||
return logs_with_prep_tracker_.FindMinLogContainingOutstandingPrep();
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_PreparedSectionCompletedSize() {
|
||||
return prepared_section_completed_.size();
|
||||
return logs_with_prep_tracker_.TEST_PreparedSectionCompletedSize();
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_LogsWithPrepSize() { return logs_with_prep_.size(); }
|
||||
size_t DBImpl::TEST_LogsWithPrepSize() {
|
||||
return logs_with_prep_tracker_.TEST_LogsWithPrepSize();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_FindMinPrepLogReferencedByMemTable() {
|
||||
return FindMinPrepLogReferencedByMemTable();
|
||||
autovector<MemTable*> empty_list;
|
||||
return FindMinPrepLogReferencedByMemTable(versions_.get(), nullptr,
|
||||
empty_list);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_GetLatestMutableCFOptions(
|
||||
|
||||
+190
-139
@@ -12,125 +12,20 @@
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
|
||||
|
||||
namespace rocksdb {
|
||||
uint64_t DBImpl::FindMinPrepLogReferencedByMemTable() {
|
||||
if (!allow_2pc()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t min_log = 0;
|
||||
|
||||
// we must look through the memtables for two phase transactions
|
||||
// that have been committed but not yet flushed
|
||||
for (auto loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (loop_cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto log = loop_cfd->imm()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
|
||||
log = loop_cfd->mem()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
}
|
||||
|
||||
return min_log;
|
||||
}
|
||||
|
||||
void DBImpl::MarkLogAsHavingPrepSectionFlushed(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(prepared_section_completed_mutex_);
|
||||
auto it = prepared_section_completed_.find(log);
|
||||
if (UNLIKELY(it == prepared_section_completed_.end())) {
|
||||
prepared_section_completed_[log] = 1;
|
||||
} else {
|
||||
it->second += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::MarkLogAsContainingPrepSection(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(logs_with_prep_mutex_);
|
||||
|
||||
auto rit = logs_with_prep_.rbegin();
|
||||
bool updated = false;
|
||||
// Most probably the last log is the one that is being marked for
|
||||
// having a prepare section; so search from the end.
|
||||
for (; rit != logs_with_prep_.rend() && rit->log >= log; ++rit) {
|
||||
if (rit->log == log) {
|
||||
rit->cnt++;
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!updated) {
|
||||
// We are either at the start, or at a position with rit->log < log
|
||||
logs_with_prep_.insert(rit.base(), {log, 1});
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DBImpl::FindMinLogContainingOutstandingPrep() {
|
||||
std::lock_guard<std::mutex> lock(logs_with_prep_mutex_);
|
||||
auto it = logs_with_prep_.begin();
|
||||
// start with the smallest log
|
||||
for (; it != logs_with_prep_.end();) {
|
||||
auto min_log = it->log;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock2(prepared_section_completed_mutex_);
|
||||
auto completed_it = prepared_section_completed_.find(min_log);
|
||||
if (completed_it == prepared_section_completed_.end() ||
|
||||
completed_it->second < it->cnt) {
|
||||
return min_log;
|
||||
}
|
||||
assert(completed_it != prepared_section_completed_.end() &&
|
||||
completed_it->second == it->cnt);
|
||||
prepared_section_completed_.erase(completed_it);
|
||||
}
|
||||
// erase from beginning in vector is not efficient but this function is not
|
||||
// on the fast path.
|
||||
it = logs_with_prep_.erase(it);
|
||||
}
|
||||
// no such log found
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
uint64_t log_number = versions_->MinLogNumber();
|
||||
|
||||
if (allow_2pc()) {
|
||||
// if are 2pc we must consider logs containing prepared
|
||||
// sections of outstanding transactions.
|
||||
//
|
||||
// We must check min logs with outstanding prep before we check
|
||||
// logs references by memtables because a log referenced by the
|
||||
// first data structure could transition to the second under us.
|
||||
//
|
||||
// TODO(horuff): iterating over all column families under db mutex.
|
||||
// should find more optimal solution
|
||||
auto min_log_in_prep_heap = FindMinLogContainingOutstandingPrep();
|
||||
|
||||
if (min_log_in_prep_heap != 0 && min_log_in_prep_heap < log_number) {
|
||||
log_number = min_log_in_prep_heap;
|
||||
}
|
||||
|
||||
auto min_log_refed_by_mem = FindMinPrepLogReferencedByMemTable();
|
||||
|
||||
if (min_log_refed_by_mem != 0 && min_log_refed_by_mem < log_number) {
|
||||
log_number = min_log_refed_by_mem;
|
||||
}
|
||||
return versions_->min_log_number_to_keep_2pc();
|
||||
} else {
|
||||
return versions_->MinLogNumberWithUnflushedData();
|
||||
}
|
||||
return log_number;
|
||||
}
|
||||
|
||||
// * Returns the list of live files in 'sst_live'
|
||||
@@ -187,27 +82,69 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
&job_context->manifest_delete_files,
|
||||
job_context->min_pending_output);
|
||||
|
||||
// Mark the elements in job_context->sst_delete_files as grabbedForPurge
|
||||
// so that other threads calling FindObsoleteFiles with full_scan=true
|
||||
// will not add these files to candidate list for purge.
|
||||
for (const auto& sst_to_del : job_context->sst_delete_files) {
|
||||
MarkAsGrabbedForPurge(sst_to_del.metadata->fd.GetNumber());
|
||||
}
|
||||
|
||||
// store the current filenum, lognum, etc
|
||||
job_context->manifest_file_number = versions_->manifest_file_number();
|
||||
job_context->pending_manifest_file_number =
|
||||
versions_->pending_manifest_file_number();
|
||||
job_context->log_number = MinLogNumberToKeep();
|
||||
|
||||
job_context->prev_log_number = versions_->prev_log_number();
|
||||
|
||||
versions_->AddLiveFiles(&job_context->sst_live);
|
||||
if (doing_the_full_scan) {
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
std::set<std::string> paths;
|
||||
for (size_t path_id = 0; path_id < immutable_db_options_.db_paths.size();
|
||||
path_id++) {
|
||||
paths.insert(immutable_db_options_.db_paths[path_id].path);
|
||||
}
|
||||
|
||||
// Note that if cf_paths is not specified in the ColumnFamilyOptions
|
||||
// of a particular column family, we use db_paths as the cf_paths
|
||||
// setting. Hence, there can be multiple duplicates of files from db_paths
|
||||
// in the following code. The duplicate are removed while identifying
|
||||
// unique files in PurgeObsoleteFiles.
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (size_t path_id = 0; path_id < cfd->ioptions()->cf_paths.size();
|
||||
path_id++) {
|
||||
auto& path = cfd->ioptions()->cf_paths[path_id].path;
|
||||
|
||||
if (paths.find(path) == paths.end()) {
|
||||
paths.insert(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& path : paths) {
|
||||
// set of all files in the directory. We'll exclude files that are still
|
||||
// alive in the subsequent processings.
|
||||
std::vector<std::string> files;
|
||||
env_->GetChildren(immutable_db_options_.db_paths[path_id].path,
|
||||
&files); // Ignore errors
|
||||
for (std::string file : files) {
|
||||
env_->GetChildren(path, &files); // Ignore errors
|
||||
for (const std::string& file : files) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
// 1. If we cannot parse the file name, we skip;
|
||||
// 2. If the file with file_number equals number has already been
|
||||
// grabbed for purge by another compaction job, or it has already been
|
||||
// schedule for purge, we also skip it if we
|
||||
// are doing full scan in order to avoid double deletion of the same
|
||||
// file under race conditions. See
|
||||
// https://github.com/facebook/rocksdb/issues/3573
|
||||
if (!ParseFileName(file, &number, info_log_prefix.prefix, &type) ||
|
||||
!ShouldPurge(number)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO(icanadi) clean up this mess to avoid having one-off "/" prefixes
|
||||
job_context->full_scan_candidate_files.emplace_back(
|
||||
"/" + file, static_cast<uint32_t>(path_id));
|
||||
"/" + file, path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,8 +153,9 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
std::vector<std::string> log_files;
|
||||
env_->GetChildren(immutable_db_options_.wal_dir,
|
||||
&log_files); // Ignore errors
|
||||
for (std::string log_file : log_files) {
|
||||
job_context->full_scan_candidate_files.emplace_back(log_file, 0);
|
||||
for (const std::string& log_file : log_files) {
|
||||
job_context->full_scan_candidate_files.emplace_back(log_file,
|
||||
immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
// Add info log files in db_log_dir
|
||||
@@ -226,8 +164,9 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
std::vector<std::string> info_log_files;
|
||||
// Ignore errors
|
||||
env_->GetChildren(immutable_db_options_.db_log_dir, &info_log_files);
|
||||
for (std::string log_file : info_log_files) {
|
||||
job_context->full_scan_candidate_files.emplace_back(log_file, 0);
|
||||
for (std::string& log_file : info_log_files) {
|
||||
job_context->full_scan_candidate_files.emplace_back(log_file,
|
||||
immutable_db_options_.db_log_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,22 +241,24 @@ bool CompareCandidateFile(const JobContext::CandidateFileInfo& first,
|
||||
} else if (first.file_name < second.file_name) {
|
||||
return false;
|
||||
} else {
|
||||
return (first.path_id > second.path_id);
|
||||
return (first.file_path > second.file_path);
|
||||
}
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
// Delete obsolete files and log status and information of file deletion
|
||||
void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
FileType type, uint64_t number,
|
||||
uint32_t path_id) {
|
||||
const std::string& path_to_sync,
|
||||
FileType type, uint64_t number) {
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile) {
|
||||
file_deletion_status =
|
||||
DeleteSSTFile(&immutable_db_options_, fname, path_id);
|
||||
DeleteSSTFile(&immutable_db_options_, fname, path_to_sync);
|
||||
} else {
|
||||
file_deletion_status = env_->DeleteFile(fname);
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DeleteObsoleteFileImpl:AfterDeletion",
|
||||
&file_deletion_status);
|
||||
if (file_deletion_status.ok()) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[JOB %d] Delete %s type=%d #%" PRIu64 " -- %s\n", job_id,
|
||||
@@ -347,7 +288,7 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
// belong to live files are possibly removed. Also, removes all the
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
TEST_SYNC_POINT("DBImpl::PurgeObsoleteFiles:Begin");
|
||||
// we'd better have sth to delete
|
||||
assert(state.HaveSomethingToDelete());
|
||||
@@ -370,23 +311,23 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
state.log_delete_files.size() + state.manifest_delete_files.size());
|
||||
// We may ignore the dbname when generating the file names.
|
||||
const char* kDumbDbName = "";
|
||||
for (auto file : state.sst_delete_files) {
|
||||
for (auto& file : state.sst_delete_files) {
|
||||
candidate_files.emplace_back(
|
||||
MakeTableFileName(kDumbDbName, file->fd.GetNumber()),
|
||||
file->fd.GetPathId());
|
||||
if (file->table_reader_handle) {
|
||||
table_cache_->Release(file->table_reader_handle);
|
||||
MakeTableFileName(kDumbDbName, file.metadata->fd.GetNumber()), file.path);
|
||||
if (file.metadata->table_reader_handle) {
|
||||
table_cache_->Release(file.metadata->table_reader_handle);
|
||||
}
|
||||
delete file;
|
||||
file.DeleteMetadata();
|
||||
}
|
||||
|
||||
for (auto file_num : state.log_delete_files) {
|
||||
if (file_num > 0) {
|
||||
candidate_files.emplace_back(LogFileName(kDumbDbName, file_num), 0);
|
||||
candidate_files.emplace_back(LogFileName(kDumbDbName, file_num),
|
||||
immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
for (const auto& filename : state.manifest_delete_files) {
|
||||
candidate_files.emplace_back(filename, 0);
|
||||
candidate_files.emplace_back(filename, dbname_);
|
||||
}
|
||||
|
||||
// dedup state.candidate_files so we don't try to delete the same
|
||||
@@ -409,9 +350,9 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
std::vector<std::string> old_info_log_files;
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
std::unordered_set<uint64_t> files_to_del;
|
||||
for (const auto& candidate_file : candidate_files) {
|
||||
std::string to_delete = candidate_file.file_name;
|
||||
uint32_t path_id = candidate_file.path_id;
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
// Ignore file if we cannot recognize it.
|
||||
@@ -437,6 +378,9 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
// DontDeletePendingOutputs fail
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
number >= state.min_pending_output;
|
||||
if (!keep) {
|
||||
files_to_del.insert(number);
|
||||
}
|
||||
break;
|
||||
case kTempFile:
|
||||
// Any temp files that are currently being written to must
|
||||
@@ -472,13 +416,16 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
}
|
||||
|
||||
std::string fname;
|
||||
std::string dir_to_sync;
|
||||
if (type == kTableFile) {
|
||||
// evict from cache
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
fname = TableFileName(immutable_db_options_.db_paths, number, path_id);
|
||||
fname = MakeTableFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else {
|
||||
fname = ((type == kLogFile) ? immutable_db_options_.wal_dir : dbname_) +
|
||||
"/" + to_delete;
|
||||
dir_to_sync =
|
||||
(type == kLogFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
fname = dir_to_sync + "/" + to_delete;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -492,12 +439,25 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
Status file_deletion_status;
|
||||
if (schedule_only) {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
SchedulePendingPurge(fname, type, number, path_id, state.job_id);
|
||||
SchedulePendingPurge(fname, dir_to_sync, type, number, state.job_id);
|
||||
} else {
|
||||
DeleteObsoleteFileImpl(state.job_id, fname, type, number, path_id);
|
||||
DeleteObsoleteFileImpl(state.job_id, fname, dir_to_sync, type, number);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// After purging obsolete files, remove them from files_grabbed_for_purge_.
|
||||
// Use a temporary vector to perform bulk deletion via swap.
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
std::vector<uint64_t> tmp;
|
||||
for (auto fn : files_grabbed_for_purge_) {
|
||||
if (files_to_del.count(fn) == 0) {
|
||||
tmp.emplace_back(fn);
|
||||
}
|
||||
}
|
||||
files_grabbed_for_purge_.swap(tmp);
|
||||
}
|
||||
|
||||
// Delete old info log files.
|
||||
size_t old_info_log_file_count = old_info_log_files.size();
|
||||
if (old_info_log_file_count != 0 &&
|
||||
@@ -557,4 +517,95 @@ void DBImpl::DeleteObsoleteFiles() {
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const ColumnFamilyData* cfd_to_flush,
|
||||
const autovector<MemTable*>& memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
// we must look through the memtables for two phase transactions
|
||||
// that have been committed but not yet flushed
|
||||
for (auto loop_cfd : *vset->GetColumnFamilySet()) {
|
||||
if (loop_cfd->IsDropped() || loop_cfd == cfd_to_flush) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto log = loop_cfd->imm()->PrecomputeMinLogContainingPrepSection(
|
||||
memtables_to_flush);
|
||||
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
|
||||
log = loop_cfd->mem()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
}
|
||||
|
||||
return min_log;
|
||||
}
|
||||
|
||||
uint64_t PrecomputeMinLogNumberToKeep(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
autovector<VersionEdit*> edit_list,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker) {
|
||||
assert(vset != nullptr);
|
||||
assert(prep_tracker != nullptr);
|
||||
// Calculate updated min_log_number_to_keep
|
||||
// Since the function should only be called in 2pc mode, log number in
|
||||
// the version edit should be sufficient.
|
||||
|
||||
// Precompute the min log number containing unflushed data for the column
|
||||
// family being flushed (`cfd_to_flush`).
|
||||
uint64_t cf_min_log_number_to_keep = 0;
|
||||
for (auto& e : edit_list) {
|
||||
if (e->has_log_number()) {
|
||||
cf_min_log_number_to_keep =
|
||||
std::max(cf_min_log_number_to_keep, e->log_number());
|
||||
}
|
||||
}
|
||||
if (cf_min_log_number_to_keep == 0) {
|
||||
// No version edit contains information on log number. The log number
|
||||
// for this column family should stay the same as it is.
|
||||
cf_min_log_number_to_keep = cfd_to_flush.GetLogNumber();
|
||||
}
|
||||
|
||||
// Get min log number containing unflushed data for other column families.
|
||||
uint64_t min_log_number_to_keep =
|
||||
vset->PreComputeMinLogNumberWithUnflushedData(&cfd_to_flush);
|
||||
if (cf_min_log_number_to_keep != 0) {
|
||||
min_log_number_to_keep =
|
||||
std::min(cf_min_log_number_to_keep, min_log_number_to_keep);
|
||||
}
|
||||
|
||||
// if are 2pc we must consider logs containing prepared
|
||||
// sections of outstanding transactions.
|
||||
//
|
||||
// We must check min logs with outstanding prep before we check
|
||||
// logs references by memtables because a log referenced by the
|
||||
// first data structure could transition to the second under us.
|
||||
//
|
||||
// TODO: iterating over all column families under db mutex.
|
||||
// should find more optimal solution
|
||||
auto min_log_in_prep_heap =
|
||||
prep_tracker->FindMinLogContainingOutstandingPrep();
|
||||
|
||||
if (min_log_in_prep_heap != 0 &&
|
||||
min_log_in_prep_heap < min_log_number_to_keep) {
|
||||
min_log_number_to_keep = min_log_in_prep_heap;
|
||||
}
|
||||
|
||||
uint64_t min_log_refed_by_mem = FindMinPrepLogReferencedByMemTable(
|
||||
vset, &cfd_to_flush, memtables_to_flush);
|
||||
|
||||
if (min_log_refed_by_mem != 0 &&
|
||||
min_log_refed_by_mem < min_log_number_to_keep) {
|
||||
min_log_number_to_keep = min_log_refed_by_mem;
|
||||
}
|
||||
return min_log_number_to_keep;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+68
-34
@@ -106,14 +106,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_io_for_flush_and_compaction &&
|
||||
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_io_for_flush_and_compaction) {
|
||||
result.use_direct_reads) {
|
||||
result.new_table_reader_for_compaction_inputs = true;
|
||||
}
|
||||
|
||||
@@ -163,28 +163,23 @@ static Status ValidateOptions(
|
||||
if (s.ok() && db_options.allow_concurrent_memtable_write) {
|
||||
s = CheckConcurrentWritesSupported(cfd.options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = CheckCFPathsSupported(db_options, cfd.options);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (db_options.db_paths.size() > 1) {
|
||||
if ((cfd.options.compaction_style != kCompactionStyleUniversal) &&
|
||||
(cfd.options.compaction_style != kCompactionStyleLevel)) {
|
||||
return Status::NotSupported(
|
||||
"More than one DB paths are only supported in "
|
||||
"universal and level compaction styles. ");
|
||||
}
|
||||
}
|
||||
if (cfd.options.compaction_options_fifo.ttl > 0) {
|
||||
|
||||
if (cfd.options.ttl > 0 || cfd.options.compaction_options_fifo.ttl > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"FIFO Compaction with TTL is only supported when files are always "
|
||||
"TTL is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cfd.options.table_factory->Name() !=
|
||||
BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"FIFO Compaction with TTL is only supported in "
|
||||
"Block-Based Table format. ");
|
||||
"TTL is only supported in Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,9 +249,9 @@ Status DBImpl::NewDB() {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::Directories::CreateAndNewDirectory(
|
||||
Status DBImpl::CreateAndNewDirectory(
|
||||
Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory) const {
|
||||
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
|
||||
@@ -274,12 +269,12 @@ Status DBImpl::Directories::CreateAndNewDirectory(
|
||||
Status DBImpl::Directories::SetDirectories(
|
||||
Env* env, const std::string& dbname, const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths) {
|
||||
Status s = CreateAndNewDirectory(env, dbname, &db_dir_);
|
||||
Status s = DBImpl::CreateAndNewDirectory(env, dbname, &db_dir_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (!wal_dir.empty() && dbname != wal_dir) {
|
||||
s = CreateAndNewDirectory(env, wal_dir, &wal_dir_);
|
||||
s = DBImpl::CreateAndNewDirectory(env, wal_dir, &wal_dir_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -292,7 +287,7 @@ Status DBImpl::Directories::SetDirectories(
|
||||
data_dirs_.emplace_back(nullptr);
|
||||
} else {
|
||||
std::unique_ptr<Directory> path_directory;
|
||||
s = CreateAndNewDirectory(env, db_path, &path_directory);
|
||||
s = DBImpl::CreateAndNewDirectory(env, db_path, &path_directory);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -385,6 +380,14 @@ Status DBImpl::Recover(
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
if (s.ok() && !read_only) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
s = cfd->AddDirectories();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
SequenceNumber next_sequence(kMaxSequenceNumber);
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
@@ -529,6 +532,13 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_log_number = kMaxSequenceNumber;
|
||||
for (auto log_number : log_numbers) {
|
||||
if (log_number < versions_->min_log_number_to_keep_2pc()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Skipping log #%" PRIu64
|
||||
" since it is older than min log to keep #%" PRIu64,
|
||||
log_number, versions_->min_log_number_to_keep_2pc());
|
||||
continue;
|
||||
}
|
||||
// The previous incarnation may not have written any MANIFEST
|
||||
// records after allocating this log number. So we manually
|
||||
// update the file number allocation counter in VersionSet.
|
||||
@@ -962,7 +972,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
meta.marked_for_compaction);
|
||||
}
|
||||
|
||||
InternalStats::CompactionStats stats(1);
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
stats.micros = env_->NowMicros() - start_micros;
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
@@ -1024,8 +1034,17 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
DBImpl* impl = new DBImpl(db_options, dbname, seq_per_batch);
|
||||
s = impl->env_->CreateDirIfMissing(impl->immutable_db_options_.wal_dir);
|
||||
if (s.ok()) {
|
||||
for (auto db_path : impl->immutable_db_options_.db_paths) {
|
||||
s = impl->env_->CreateDirIfMissing(db_path.path);
|
||||
std::vector<std::string> paths;
|
||||
for (auto& db_path : impl->immutable_db_options_.db_paths) {
|
||||
paths.emplace_back(db_path.path);
|
||||
}
|
||||
for (auto& cf : column_families) {
|
||||
for (auto& cf_path : cf.options.cf_paths) {
|
||||
paths.emplace_back(cf_path.path);
|
||||
}
|
||||
}
|
||||
for (auto& path : paths) {
|
||||
s = impl->env_->CreateDirIfMissing(path);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -1071,7 +1090,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
new_log_number,
|
||||
new log::Writer(
|
||||
std::move(file_writer), new_log_number,
|
||||
impl->immutable_db_options_.recycle_log_file_num > 0));
|
||||
impl->immutable_db_options_.recycle_log_file_num > 0,
|
||||
impl->immutable_db_options_.manual_wal_flush));
|
||||
}
|
||||
|
||||
// set column family handles
|
||||
@@ -1168,17 +1188,28 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
impl->immutable_db_options_.sst_file_manager.get());
|
||||
if (s.ok() && sfm) {
|
||||
// Notify SstFileManager about all sst files that already exist in
|
||||
// db_paths[0] when the DB is opened.
|
||||
auto& db_path = impl->immutable_db_options_.db_paths[0];
|
||||
std::vector<std::string> existing_files;
|
||||
impl->immutable_db_options_.env->GetChildren(db_path.path, &existing_files);
|
||||
for (auto& file_name : existing_files) {
|
||||
uint64_t file_number;
|
||||
FileType file_type;
|
||||
std::string file_path = db_path.path + "/" + file_name;
|
||||
if (ParseFileName(file_name, &file_number, &file_type) &&
|
||||
file_type == kTableFile) {
|
||||
sfm->OnAddFile(file_path);
|
||||
// db_paths[0] and cf_paths[0] when the DB is opened.
|
||||
std::vector<std::string> paths;
|
||||
paths.emplace_back(impl->immutable_db_options_.db_paths[0].path);
|
||||
for (auto& cf : column_families) {
|
||||
if (!cf.options.cf_paths.empty()) {
|
||||
paths.emplace_back(cf.options.cf_paths[0].path);
|
||||
}
|
||||
}
|
||||
// Remove duplicate paths.
|
||||
std::sort(paths.begin(), paths.end());
|
||||
paths.erase(std::unique(paths.begin(), paths.end()), paths.end());
|
||||
for (auto& path : paths) {
|
||||
std::vector<std::string> existing_files;
|
||||
impl->immutable_db_options_.env->GetChildren(path, &existing_files);
|
||||
for (auto& file_name : existing_files) {
|
||||
uint64_t file_number;
|
||||
FileType file_type;
|
||||
std::string file_path = path + "/" + file_name;
|
||||
if (ParseFileName(file_name, &file_number, &file_type) &&
|
||||
file_type == kTableFile) {
|
||||
sfm->OnAddFile(file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1187,6 +1218,9 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
if (s.ok()) {
|
||||
ROCKS_LOG_INFO(impl->immutable_db_options_.info_log, "DB pointer %p", impl);
|
||||
LogFlush(impl->immutable_db_options_.info_log);
|
||||
assert(impl->TEST_WALBufferIsEmpty());
|
||||
// If the assert above fails then we need to FlushWAL before returning
|
||||
// control back to the user.
|
||||
if (!persist_options_status.ok()) {
|
||||
s = Status::IOError(
|
||||
"DB::Open() failed --- Unable to persist Options file",
|
||||
|
||||
+12
-12
@@ -24,8 +24,7 @@ DBImplReadOnly::DBImplReadOnly(const DBOptions& db_options,
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
DBImplReadOnly::~DBImplReadOnly() {
|
||||
}
|
||||
DBImplReadOnly::~DBImplReadOnly() {}
|
||||
|
||||
// Implementations of the DB interface
|
||||
Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
@@ -59,7 +58,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(),
|
||||
env_, read_options, *cfd->ioptions(), super_version->mutable_cf_options,
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
@@ -89,7 +88,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
auto* sv = cfd->GetSuperVersion()->Ref();
|
||||
auto* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(),
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
@@ -183,20 +182,21 @@ Status DB::OpenForReadOnly(
|
||||
return s;
|
||||
}
|
||||
|
||||
#else // !ROCKSDB_LITE
|
||||
#else // !ROCKSDB_LITE
|
||||
|
||||
Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
DB** dbptr, bool error_if_log_file_exist) {
|
||||
Status DB::OpenForReadOnly(const Options& /*options*/,
|
||||
const std::string& /*dbname*/, DB** /*dbptr*/,
|
||||
bool /*error_if_log_file_exist*/) {
|
||||
return Status::NotSupported("Not supported in ROCKSDB_LITE.");
|
||||
}
|
||||
|
||||
Status DB::OpenForReadOnly(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist) {
|
||||
const DBOptions& /*db_options*/, const std::string& /*dbname*/,
|
||||
const std::vector<ColumnFamilyDescriptor>& /*column_families*/,
|
||||
std::vector<ColumnFamilyHandle*>* /*handles*/, DB** /*dbptr*/,
|
||||
bool /*error_if_log_file_exist*/) {
|
||||
return Status::NotSupported("Not supported in ROCKSDB_LITE.");
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
} // namespace rocksdb
|
||||
|
||||
+100
-30
@@ -45,6 +45,11 @@ Status DBImpl::SingleDelete(const WriteOptions& write_options,
|
||||
return DB::SingleDelete(write_options, column_family, key);
|
||||
}
|
||||
|
||||
void DBImpl::SetRecoverableStatePreReleaseCallback(
|
||||
PreReleaseCallback* callback) {
|
||||
recoverable_state_pre_release_callback_.reset(callback);
|
||||
}
|
||||
|
||||
Status DBImpl::Write(const WriteOptions& write_options, WriteBatch* my_batch) {
|
||||
return WriteImpl(write_options, my_batch, nullptr, nullptr);
|
||||
}
|
||||
@@ -117,15 +122,19 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
// we are a non-leader in a parallel group
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
if (w.ShouldWriteToMemtable()) {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
true /*concurrent_memtable_writes*/, seq_per_batch_, w.batch_cnt);
|
||||
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
}
|
||||
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
@@ -133,7 +142,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
for (auto* writer : *(w.write_group)) {
|
||||
if (!writer->CallbackFailed() && writer->pre_release_callback) {
|
||||
assert(writer->sequence != kMaxSequenceNumber);
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence);
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence,
|
||||
disable_memtable);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
break;
|
||||
@@ -184,7 +194,13 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// With concurrent writes we do preprocess only in the write thread that
|
||||
// also does write to memtable to avoid sync issue on shared data structure
|
||||
// with the other thread
|
||||
|
||||
// PreprocessWrite does its own perf timing.
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
}
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
|
||||
@@ -195,6 +211,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// and protects against concurrent loggers and concurrent writes
|
||||
// into memtables
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::WriteImpl:BeforeLeaderEnters");
|
||||
last_batch_group_size_ =
|
||||
write_thread_.EnterAsBatchGroupLeader(&w, &write_group);
|
||||
|
||||
@@ -339,7 +356,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteCallbackStatusCheck(status);
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -347,7 +364,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, status);
|
||||
mutex_.Unlock();
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hance provide a simple implementation that is not necessarily efficient.
|
||||
// hence provide a simple implementation that is not necessarily efficient.
|
||||
if (two_write_queues_) {
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
@@ -368,7 +385,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed() && writer->pre_release_callback) {
|
||||
assert(writer->sequence != kMaxSequenceNumber);
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence);
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence,
|
||||
disable_memtable);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
break;
|
||||
@@ -407,7 +425,10 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
mutex_.Lock();
|
||||
bool need_log_sync = !write_options.disableWAL && write_options.sync;
|
||||
bool need_log_dir_sync = need_log_sync && !log_dir_synced_;
|
||||
// PreprocessWrite does its own perf timing.
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
w.status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -462,7 +483,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteCallbackStatusCheck(w.status);
|
||||
WriteStatusCheck(w.status);
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -623,13 +644,15 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteCallbackStatusCheck(status);
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
if (status.ok()) {
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed() && writer->pre_release_callback) {
|
||||
assert(writer->sequence != kMaxSequenceNumber);
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence);
|
||||
const bool DISABLE_MEMTABLE = true;
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence,
|
||||
DISABLE_MEMTABLE);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
break;
|
||||
@@ -647,7 +670,7 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
return status;
|
||||
}
|
||||
|
||||
void DBImpl::WriteCallbackStatusCheck(const Status& status) {
|
||||
void DBImpl::WriteStatusCheck(const Status& status) {
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
if (immutable_db_options_.paranoid_checks && !status.ok() &&
|
||||
@@ -695,6 +718,8 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
assert(write_context != nullptr && need_log_sync != nullptr);
|
||||
Status status;
|
||||
|
||||
PERF_TIMER_GUARD(write_scheduling_flushes_compactions_time);
|
||||
|
||||
assert(!single_column_family_mode_ ||
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1);
|
||||
if (UNLIKELY(status.ok() && !single_column_family_mode_ &&
|
||||
@@ -719,14 +744,19 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
status = ScheduleFlushes(write_context);
|
||||
}
|
||||
|
||||
PERF_TIMER_STOP(write_scheduling_flushes_compactions_time);
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
|
||||
if (UNLIKELY(status.ok() && (write_controller_.IsStopped() ||
|
||||
write_controller_.NeedsDelay()))) {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_delay_time);
|
||||
// We don't know size of curent batch so that we always use the size
|
||||
// for previous one. It might create a fairness issue that expiration
|
||||
// might happen for smaller writes but larger writes can go through.
|
||||
// Can optimize it if it is an issue.
|
||||
status = DelayWrite(last_batch_group_size_, write_options);
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
}
|
||||
|
||||
if (status.ok() && *need_log_sync) {
|
||||
@@ -804,7 +834,21 @@ Status DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
assert(log_size != nullptr);
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
*log_size = log_entry.size();
|
||||
// When two_write_queues_ WriteToWAL has to be protected from concurretn calls
|
||||
// from the two queues anyway and log_write_mutex_ is already held. Otherwise
|
||||
// if manual_wal_flush_ is enabled we need to protect log_writer->AddRecord
|
||||
// from possible concurrent calls via the FlushWAL by the application.
|
||||
const bool needs_locking = manual_wal_flush_ && !two_write_queues_;
|
||||
// Due to performance cocerns of missed branch prediction penalize the new
|
||||
// manual_wal_flush_ feature (by UNLIKELY) instead of the more common case
|
||||
// when we do not need any locking.
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
Status status = log_writer->AddRecord(log_entry);
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
if (log_used != nullptr) {
|
||||
*log_used = logfile_number_;
|
||||
}
|
||||
@@ -942,7 +986,12 @@ Status DBImpl::WriteRecoverableState() {
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
SequenceNumber seq = versions_->LastSequence();
|
||||
SequenceNumber seq;
|
||||
if (two_write_queues_) {
|
||||
seq = versions_->FetchAddLastAllocatedSequence(0);
|
||||
} else {
|
||||
seq = versions_->LastSequence();
|
||||
}
|
||||
WriteBatchInternal::SetSequence(&cached_recoverable_state_, seq + 1);
|
||||
auto status = WriteBatchInternal::InsertInto(
|
||||
&cached_recoverable_state_, column_family_memtables_.get(),
|
||||
@@ -952,12 +1001,20 @@ Status DBImpl::WriteRecoverableState() {
|
||||
auto last_seq = next_seq - 1;
|
||||
if (two_write_queues_) {
|
||||
versions_->FetchAddLastAllocatedSequence(last_seq - seq);
|
||||
versions_->SetLastPublishedSequence(last_seq);
|
||||
}
|
||||
versions_->SetLastSequence(last_seq);
|
||||
versions_->SetLastPublishedSequence(last_seq);
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
if (status.ok() && recoverable_state_pre_release_callback_) {
|
||||
const bool DISABLE_MEMTABLE = true;
|
||||
for (uint64_t sub_batch_seq = seq + 1;
|
||||
sub_batch_seq < next_seq && status.ok(); sub_batch_seq++) {
|
||||
status = recoverable_state_pre_release_callback_->Callback(
|
||||
sub_batch_seq, !DISABLE_MEMTABLE);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
cached_recoverable_state_.Clear();
|
||||
cached_recoverable_state_empty_ = true;
|
||||
@@ -977,28 +1034,34 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
}
|
||||
|
||||
auto oldest_alive_log = alive_log_files_.begin()->number;
|
||||
auto oldest_log_with_uncommited_prep = FindMinLogContainingOutstandingPrep();
|
||||
bool flush_wont_release_oldest_log = false;
|
||||
if (allow_2pc()) {
|
||||
auto oldest_log_with_uncommited_prep =
|
||||
logs_with_prep_tracker_.FindMinLogContainingOutstandingPrep();
|
||||
|
||||
if (allow_2pc() &&
|
||||
oldest_log_with_uncommited_prep > 0 &&
|
||||
oldest_log_with_uncommited_prep <= oldest_alive_log) {
|
||||
if (unable_to_flush_oldest_log_) {
|
||||
assert(oldest_log_with_uncommited_prep == 0 ||
|
||||
oldest_log_with_uncommited_prep >= oldest_alive_log);
|
||||
if (oldest_log_with_uncommited_prep > 0 &&
|
||||
oldest_log_with_uncommited_prep == oldest_alive_log) {
|
||||
if (unable_to_release_oldest_log_) {
|
||||
// we already attempted to flush all column families dependent on
|
||||
// the oldest alive log but the log still contained uncommited transactions.
|
||||
// the oldest alive log STILL contains uncommited transaction so there
|
||||
// is still nothing that we can do.
|
||||
// the oldest alive log but the log still contained uncommited
|
||||
// transactions so there is still nothing that we can do.
|
||||
return status;
|
||||
} else {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Unable to release oldest log due to uncommited transaction");
|
||||
unable_to_flush_oldest_log_ = true;
|
||||
} else {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Unable to release oldest log due to uncommited transaction");
|
||||
unable_to_release_oldest_log_ = true;
|
||||
flush_wont_release_oldest_log = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (!flush_wont_release_oldest_log) {
|
||||
// we only mark this log as getting flushed if we have successfully
|
||||
// flushed all data in this log. If this log contains outstanding prepared
|
||||
// transactions then we cannot flush this log until those transactions are commited.
|
||||
unable_to_flush_oldest_log_ = false;
|
||||
unable_to_release_oldest_log_ = false;
|
||||
alive_log_files_.begin()->getting_flushed = true;
|
||||
}
|
||||
|
||||
@@ -1062,7 +1125,8 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
}
|
||||
}
|
||||
if (cfd_picked != nullptr) {
|
||||
status = SwitchMemtable(cfd_picked, write_context);
|
||||
status = SwitchMemtable(cfd_picked, write_context,
|
||||
FlushReason::kWriteBufferFull);
|
||||
if (status.ok()) {
|
||||
cfd_picked->imm()->FlushRequested();
|
||||
SchedulePendingFlush(cfd_picked, FlushReason::kWriteBufferFull);
|
||||
@@ -1152,6 +1216,7 @@ Status DBImpl::ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
|
||||
// is that in case the write is heavy, low pri writes may never have
|
||||
// a chance to run. Now we guarantee we are still slowly making
|
||||
// progress.
|
||||
PERF_TIMER_GUARD(write_delay_time);
|
||||
write_controller_.low_pri_rate_limiter()->Request(
|
||||
my_batch->GetDataSize(), Env::IO_HIGH, nullptr /* stats */,
|
||||
RateLimiter::OpType::kWrite);
|
||||
@@ -1163,7 +1228,7 @@ Status DBImpl::ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
|
||||
Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
ColumnFamilyData* cfd;
|
||||
while ((cfd = flush_scheduler_.TakeNextColumnFamily()) != nullptr) {
|
||||
auto status = SwitchMemtable(cfd, context);
|
||||
auto status = SwitchMemtable(cfd, context, FlushReason::kWriteBufferFull);
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
}
|
||||
@@ -1192,7 +1257,8 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* /*cfd*/,
|
||||
|
||||
// REQUIRES: mutex_ is held
|
||||
// REQUIRES: this thread is currently at the front of the writer queue
|
||||
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
FlushReason flush_reason) {
|
||||
mutex_.AssertHeld();
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (two_write_queues_) {
|
||||
@@ -1215,7 +1281,11 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
// In case of pipelined write is enabled, wait for all pending memtable
|
||||
// writers.
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
// Memtable writers may call DB::Get in case max_successive_merges > 0,
|
||||
// which may lock mutex. Unlocking mutex here to avoid deadlock.
|
||||
mutex_.Unlock();
|
||||
write_thread_.WaitForMemTableWriters();
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
// Attempt to switch to a new memtable and trigger flush of old.
|
||||
@@ -1360,7 +1430,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
new_mem->Ref();
|
||||
cfd->SetMemtable(new_mem);
|
||||
InstallSuperVersionAndScheduleWork(cfd, &context->superversion_context,
|
||||
mutable_cf_options);
|
||||
mutable_cf_options, flush_reason);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
|
||||
+365
-275
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -30,6 +30,7 @@ class InternalIterator;
|
||||
// into appropriate user keys.
|
||||
extern Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
@@ -71,6 +72,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
|
||||
void Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, bool allow_blob, bool allow_refresh);
|
||||
@@ -102,7 +104,8 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
// be supported.
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const SequenceNumber& sequence,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false,
|
||||
|
||||
@@ -0,0 +1,654 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
#ifdef GFLAGS
|
||||
|
||||
#include "util/gflags_compat.h"
|
||||
|
||||
using GFLAGS_NAMESPACE::ParseCommandLineFlags;
|
||||
|
||||
DEFINE_bool(verbose, false,
|
||||
"Print huge, detailed trace. Intended for debugging failures.");
|
||||
|
||||
#else
|
||||
|
||||
void ParseCommandLineFlags(int*, char***, bool) {}
|
||||
bool FLAGS_verbose = false;
|
||||
|
||||
#endif
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBIteratorStressTest : public testing::Test {
|
||||
public:
|
||||
Env* env_;
|
||||
|
||||
DBIteratorStressTest() : env_(Env::Default()) {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
struct Entry {
|
||||
std::string key;
|
||||
ValueType type; // kTypeValue, kTypeDeletion, kTypeMerge
|
||||
uint64_t sequence;
|
||||
std::string ikey; // internal key, made from `key`, `sequence` and `type`
|
||||
std::string value;
|
||||
// If false, we'll pretend that this entry doesn't exist.
|
||||
bool visible = true;
|
||||
|
||||
bool operator<(const Entry& e) const {
|
||||
if (key != e.key) return key < e.key;
|
||||
return std::tie(sequence, type) > std::tie(e.sequence, e.type);
|
||||
}
|
||||
};
|
||||
|
||||
struct Data {
|
||||
std::vector<Entry> entries;
|
||||
|
||||
// Indices in `entries` with `visible` = false.
|
||||
std::vector<size_t> hidden;
|
||||
// Keys of entries whose `visible` changed since the last seek of iterators.
|
||||
std::set<std::string> recently_touched_keys;
|
||||
};
|
||||
|
||||
struct StressTestIterator : public InternalIterator {
|
||||
Data* data;
|
||||
Random64* rnd;
|
||||
InternalKeyComparator cmp;
|
||||
|
||||
// Each operation will return error with this probability...
|
||||
double error_probability = 0;
|
||||
// ... and add/remove entries with this probability.
|
||||
double mutation_probability = 0;
|
||||
// The probability of adding vs removing entries will be chosen so that the
|
||||
// amount of removed entries stays somewhat close to this number.
|
||||
double target_hidden_fraction = 0;
|
||||
// If true, print all mutations to stdout for debugging.
|
||||
bool trace = false;
|
||||
|
||||
int iter = -1;
|
||||
Status status_;
|
||||
|
||||
StressTestIterator(Data* _data, Random64* _rnd, const Comparator* _cmp)
|
||||
: data(_data), rnd(_rnd), cmp(_cmp) {}
|
||||
|
||||
bool Valid() const override {
|
||||
if (iter >= 0 && iter < (int)data->entries.size()) {
|
||||
assert(status_.ok());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Status status() const override { return status_; }
|
||||
|
||||
bool MaybeFail() {
|
||||
if (rnd->Next() >=
|
||||
std::numeric_limits<uint64_t>::max() * error_probability) {
|
||||
return false;
|
||||
}
|
||||
if (rnd->Next() % 2) {
|
||||
status_ = Status::Incomplete("test");
|
||||
} else {
|
||||
status_ = Status::IOError("test");
|
||||
}
|
||||
if (trace) {
|
||||
std::cout << "injecting " << status_.ToString() << std::endl;
|
||||
}
|
||||
iter = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MaybeMutate() {
|
||||
if (rnd->Next() >=
|
||||
std::numeric_limits<uint64_t>::max() * mutation_probability) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
// If too many entries are hidden, hide less, otherwise hide more.
|
||||
double hide_probability =
|
||||
data->hidden.size() > data->entries.size() * target_hidden_fraction
|
||||
? 1. / 3
|
||||
: 2. / 3;
|
||||
if (data->hidden.empty()) {
|
||||
hide_probability = 1;
|
||||
}
|
||||
bool do_hide =
|
||||
rnd->Next() < std::numeric_limits<uint64_t>::max() * hide_probability;
|
||||
if (do_hide) {
|
||||
// Hide a random entry.
|
||||
size_t idx = rnd->Next() % data->entries.size();
|
||||
Entry& e = data->entries[idx];
|
||||
if (e.visible) {
|
||||
if (trace) {
|
||||
std::cout << "hiding idx " << idx << std::endl;
|
||||
}
|
||||
e.visible = false;
|
||||
data->hidden.push_back(idx);
|
||||
data->recently_touched_keys.insert(e.key);
|
||||
} else {
|
||||
// Already hidden. Let's go unhide something instead, just because
|
||||
// it's easy and it doesn't really matter what we do.
|
||||
do_hide = false;
|
||||
}
|
||||
}
|
||||
if (!do_hide) {
|
||||
// Unhide a random entry.
|
||||
size_t hi = rnd->Next() % data->hidden.size();
|
||||
size_t idx = data->hidden[hi];
|
||||
if (trace) {
|
||||
std::cout << "unhiding idx " << idx << std::endl;
|
||||
}
|
||||
Entry& e = data->entries[idx];
|
||||
assert(!e.visible);
|
||||
e.visible = true;
|
||||
data->hidden[hi] = data->hidden.back();
|
||||
data->hidden.pop_back();
|
||||
data->recently_touched_keys.insert(e.key);
|
||||
}
|
||||
} while (rnd->Next() % 3 != 0); // do 3 mutations on average
|
||||
}
|
||||
|
||||
void SkipForward() {
|
||||
while (iter < (int)data->entries.size() && !data->entries[iter].visible) {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
void SkipBackward() {
|
||||
while (iter >= 0 && !data->entries[iter].visible) {
|
||||
--iter;
|
||||
}
|
||||
}
|
||||
|
||||
void SeekToFirst() override {
|
||||
if (MaybeFail()) return;
|
||||
MaybeMutate();
|
||||
|
||||
status_ = Status::OK();
|
||||
iter = 0;
|
||||
SkipForward();
|
||||
}
|
||||
void SeekToLast() override {
|
||||
if (MaybeFail()) return;
|
||||
MaybeMutate();
|
||||
|
||||
status_ = Status::OK();
|
||||
iter = (int)data->entries.size() - 1;
|
||||
SkipBackward();
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) override {
|
||||
if (MaybeFail()) return;
|
||||
MaybeMutate();
|
||||
|
||||
status_ = Status::OK();
|
||||
// Binary search.
|
||||
auto it = std::partition_point(
|
||||
data->entries.begin(), data->entries.end(),
|
||||
[&](const Entry& e) { return cmp.Compare(e.ikey, target) < 0; });
|
||||
iter = (int)(it - data->entries.begin());
|
||||
SkipForward();
|
||||
}
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
if (MaybeFail()) return;
|
||||
MaybeMutate();
|
||||
|
||||
status_ = Status::OK();
|
||||
// Binary search.
|
||||
auto it = std::partition_point(
|
||||
data->entries.begin(), data->entries.end(),
|
||||
[&](const Entry& e) { return cmp.Compare(e.ikey, target) <= 0; });
|
||||
iter = (int)(it - data->entries.begin());
|
||||
--iter;
|
||||
SkipBackward();
|
||||
}
|
||||
|
||||
void Next() override {
|
||||
assert(Valid());
|
||||
if (MaybeFail()) return;
|
||||
MaybeMutate();
|
||||
++iter;
|
||||
SkipForward();
|
||||
}
|
||||
void Prev() override {
|
||||
assert(Valid());
|
||||
if (MaybeFail()) return;
|
||||
MaybeMutate();
|
||||
--iter;
|
||||
SkipBackward();
|
||||
}
|
||||
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return data->entries[iter].ikey;
|
||||
}
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
return data->entries[iter].value;
|
||||
}
|
||||
|
||||
bool IsKeyPinned() const override { return true; }
|
||||
bool IsValuePinned() const override { return true; }
|
||||
};
|
||||
|
||||
// A small reimplementation of DBIter, supporting only some of the features,
|
||||
// and doing everything in O(log n).
|
||||
// Skips all keys that are in recently_touched_keys.
|
||||
struct ReferenceIterator {
|
||||
Data* data;
|
||||
uint64_t sequence; // ignore entries with sequence number below this
|
||||
|
||||
bool valid = false;
|
||||
std::string key;
|
||||
std::string value;
|
||||
|
||||
ReferenceIterator(Data* _data, uint64_t _sequence)
|
||||
: data(_data), sequence(_sequence) {}
|
||||
|
||||
bool Valid() const { return valid; }
|
||||
|
||||
// Finds the first entry with key
|
||||
// greater/less/greater-or-equal/less-or-equal than `key`, depending on
|
||||
// arguments: if `skip`, inequality is strict; if `forward`, it's
|
||||
// greater/greater-or-equal, otherwise less/less-or-equal.
|
||||
// Sets `key` to the result.
|
||||
// If no such key exists, returns false. Doesn't check `visible`.
|
||||
bool FindNextKey(bool skip, bool forward) {
|
||||
valid = false;
|
||||
auto it = std::partition_point(data->entries.begin(), data->entries.end(),
|
||||
[&](const Entry& e) {
|
||||
if (forward != skip) {
|
||||
return e.key < key;
|
||||
} else {
|
||||
return e.key <= key;
|
||||
}
|
||||
});
|
||||
if (forward) {
|
||||
if (it != data->entries.end()) {
|
||||
key = it->key;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (it != data->entries.begin()) {
|
||||
--it;
|
||||
key = it->key;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindValueForCurrentKey() {
|
||||
if (data->recently_touched_keys.count(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the first entry for the key. The caller promises that it exists.
|
||||
auto it = std::partition_point(data->entries.begin(), data->entries.end(),
|
||||
[&](const Entry& e) {
|
||||
if (e.key != key) {
|
||||
return e.key < key;
|
||||
}
|
||||
return e.sequence > sequence;
|
||||
});
|
||||
|
||||
// Find the first visible entry.
|
||||
for (;; ++it) {
|
||||
if (it == data->entries.end()) {
|
||||
return false;
|
||||
}
|
||||
Entry& e = *it;
|
||||
if (e.key != key) {
|
||||
return false;
|
||||
}
|
||||
assert(e.sequence <= sequence);
|
||||
if (!e.visible) continue;
|
||||
if (e.type == kTypeDeletion) {
|
||||
return false;
|
||||
}
|
||||
if (e.type == kTypeValue) {
|
||||
value = e.value;
|
||||
valid = true;
|
||||
return true;
|
||||
}
|
||||
assert(e.type == kTypeMerge);
|
||||
break;
|
||||
}
|
||||
|
||||
// Collect merge operands.
|
||||
std::vector<Slice> operands;
|
||||
for (; it != data->entries.end(); ++it) {
|
||||
Entry& e = *it;
|
||||
if (e.key != key) {
|
||||
break;
|
||||
}
|
||||
assert(e.sequence <= sequence);
|
||||
if (!e.visible) continue;
|
||||
if (e.type == kTypeDeletion) {
|
||||
break;
|
||||
}
|
||||
operands.push_back(e.value);
|
||||
if (e.type == kTypeValue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Do a merge.
|
||||
value = operands.back().ToString();
|
||||
for (int i = (int)operands.size() - 2; i >= 0; --i) {
|
||||
value.append(",");
|
||||
value.append(operands[i].data(), operands[i].size());
|
||||
}
|
||||
|
||||
valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Start at `key` and move until we encounter a valid value.
|
||||
// `forward` defines the direction of movement.
|
||||
// If `skip` is true, we're looking for key not equal to `key`.
|
||||
void DoTheThing(bool skip, bool forward) {
|
||||
while (FindNextKey(skip, forward) && !FindValueForCurrentKey()) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) {
|
||||
key = target.ToString();
|
||||
DoTheThing(false, true);
|
||||
}
|
||||
void SeekForPrev(const Slice& target) {
|
||||
key = target.ToString();
|
||||
DoTheThing(false, false);
|
||||
}
|
||||
void SeekToFirst() { Seek(""); }
|
||||
void SeekToLast() {
|
||||
key = data->entries.back().key;
|
||||
DoTheThing(false, false);
|
||||
}
|
||||
void Next() {
|
||||
assert(Valid());
|
||||
DoTheThing(true, true);
|
||||
}
|
||||
void Prev() {
|
||||
assert(Valid());
|
||||
DoTheThing(true, false);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Use an internal iterator that sometimes returns errors and sometimes
|
||||
// adds/removes entries on the fly. Do random operations on a DBIter and
|
||||
// check results.
|
||||
// TODO: can be improved for more coverage:
|
||||
// * Override IsKeyPinned() and IsValuePinned() to actually use
|
||||
// PinnedIteratorManager and check that there's no use-after free.
|
||||
// * Try different combinations of prefix_extractor, total_order_seek,
|
||||
// prefix_same_as_start, iterate_lower_bound, iterate_upper_bound.
|
||||
TEST_F(DBIteratorStressTest, StressTest) {
|
||||
// We use a deterministic RNG, and everything happens in a single thread.
|
||||
Random64 rnd(826909345792864532ll);
|
||||
|
||||
auto gen_key = [&](int max_key) {
|
||||
assert(max_key > 0);
|
||||
int len = 0;
|
||||
int a = max_key;
|
||||
while (a) {
|
||||
a /= 10;
|
||||
++len;
|
||||
}
|
||||
std::string s = ToString(rnd.Next() % static_cast<uint64_t>(max_key));
|
||||
s.insert(0, len - (int)s.size(), '0');
|
||||
return s;
|
||||
};
|
||||
|
||||
Options options;
|
||||
options.merge_operator = MergeOperators::CreateFromStringId("stringappend");
|
||||
ReadOptions ropt;
|
||||
|
||||
size_t num_matching = 0;
|
||||
size_t num_at_end = 0;
|
||||
size_t num_not_ok = 0;
|
||||
size_t num_recently_removed = 0;
|
||||
|
||||
// Number of iterations for each combination of parameters
|
||||
// (there are ~250 of those).
|
||||
// Tweak this to change the test run time.
|
||||
// As of the time of writing, the test takes ~4 seconds for value of 5000.
|
||||
const int num_iterations = 5000;
|
||||
// Enable this to print all the operations for debugging.
|
||||
bool trace = FLAGS_verbose;
|
||||
|
||||
for (int num_entries : {5, 10, 100}) {
|
||||
for (double key_space : {0.1, 1.0, 3.0}) {
|
||||
for (ValueType prevalent_entry_type :
|
||||
{kTypeValue, kTypeDeletion, kTypeMerge}) {
|
||||
for (double error_probability : {0.01, 0.1}) {
|
||||
for (double mutation_probability : {0.01, 0.5}) {
|
||||
for (double target_hidden_fraction : {0.1, 0.5}) {
|
||||
std::string trace_str =
|
||||
"entries: " + ToString(num_entries) +
|
||||
", key_space: " + ToString(key_space) +
|
||||
", error_probability: " + ToString(error_probability) +
|
||||
", mutation_probability: " + ToString(mutation_probability) +
|
||||
", target_hidden_fraction: " +
|
||||
ToString(target_hidden_fraction);
|
||||
SCOPED_TRACE(trace_str);
|
||||
if (trace) {
|
||||
std::cout << trace_str << std::endl;
|
||||
}
|
||||
|
||||
// Generate data.
|
||||
Data data;
|
||||
int max_key = (int)(num_entries * key_space) + 1;
|
||||
for (int i = 0; i < num_entries; ++i) {
|
||||
Entry e;
|
||||
e.key = gen_key(max_key);
|
||||
if (rnd.Next() % 10 != 0) {
|
||||
e.type = prevalent_entry_type;
|
||||
} else {
|
||||
const ValueType types[] = {kTypeValue, kTypeDeletion,
|
||||
kTypeMerge};
|
||||
e.type =
|
||||
types[rnd.Next() % (sizeof(types) / sizeof(types[0]))];
|
||||
}
|
||||
e.sequence = i;
|
||||
e.value = "v" + ToString(i);
|
||||
ParsedInternalKey internal_key(e.key, e.sequence, e.type);
|
||||
AppendInternalKey(&e.ikey, internal_key);
|
||||
|
||||
data.entries.push_back(e);
|
||||
}
|
||||
std::sort(data.entries.begin(), data.entries.end());
|
||||
if (trace) {
|
||||
std::cout << "entries:";
|
||||
for (size_t i = 0; i < data.entries.size(); ++i) {
|
||||
Entry& e = data.entries[i];
|
||||
std::cout
|
||||
<< "\n idx " << i << ": \"" << e.key << "\": \""
|
||||
<< e.value << "\" seq: " << e.sequence << " type: "
|
||||
<< (e.type == kTypeValue
|
||||
? "val"
|
||||
: e.type == kTypeDeletion ? "del" : "merge");
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> db_iter;
|
||||
std::unique_ptr<ReferenceIterator> ref_iter;
|
||||
for (int iteration = 0; iteration < num_iterations; ++iteration) {
|
||||
SCOPED_TRACE(iteration);
|
||||
// Create a new iterator every ~30 operations.
|
||||
if (db_iter == nullptr || rnd.Next() % 30 == 0) {
|
||||
uint64_t sequence = rnd.Next() % (data.entries.size() + 2);
|
||||
ref_iter.reset(new ReferenceIterator(&data, sequence));
|
||||
if (trace) {
|
||||
std::cout << "new iterator, seq: " << sequence << std::endl;
|
||||
}
|
||||
|
||||
auto internal_iter =
|
||||
new StressTestIterator(&data, &rnd, BytewiseComparator());
|
||||
internal_iter->error_probability = error_probability;
|
||||
internal_iter->mutation_probability = mutation_probability;
|
||||
internal_iter->target_hidden_fraction =
|
||||
target_hidden_fraction;
|
||||
internal_iter->trace = trace;
|
||||
db_iter.reset(NewDBIterator(
|
||||
env_, ropt, ImmutableCFOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, sequence,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
}
|
||||
|
||||
// Do a random operation. It's important to do it on ref_it
|
||||
// later than on db_iter to make sure ref_it sees the correct
|
||||
// recently_touched_keys.
|
||||
std::string old_key;
|
||||
bool forward = rnd.Next() % 2 > 0;
|
||||
// Do Next()/Prev() ~90% of the time.
|
||||
bool seek = !ref_iter->Valid() || rnd.Next() % 10 == 0;
|
||||
if (trace) {
|
||||
std::cout << iteration << ": ";
|
||||
}
|
||||
|
||||
if (!seek) {
|
||||
assert(db_iter->Valid());
|
||||
old_key = ref_iter->key;
|
||||
if (trace) {
|
||||
std::cout << (forward ? "Next" : "Prev") << std::endl;
|
||||
}
|
||||
|
||||
if (forward) {
|
||||
db_iter->Next();
|
||||
ref_iter->Next();
|
||||
} else {
|
||||
db_iter->Prev();
|
||||
ref_iter->Prev();
|
||||
}
|
||||
} else {
|
||||
data.recently_touched_keys.clear();
|
||||
// Do SeekToFirst less often than Seek.
|
||||
if (rnd.Next() % 4 == 0) {
|
||||
if (trace) {
|
||||
std::cout << (forward ? "SeekToFirst" : "SeekToLast")
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (forward) {
|
||||
old_key = "";
|
||||
db_iter->SeekToFirst();
|
||||
ref_iter->SeekToFirst();
|
||||
} else {
|
||||
old_key = data.entries.back().key;
|
||||
db_iter->SeekToLast();
|
||||
ref_iter->SeekToLast();
|
||||
}
|
||||
} else {
|
||||
old_key = gen_key(max_key);
|
||||
if (trace) {
|
||||
std::cout << (forward ? "Seek" : "SeekForPrev") << " \""
|
||||
<< old_key << '"' << std::endl;
|
||||
}
|
||||
if (forward) {
|
||||
db_iter->Seek(old_key);
|
||||
ref_iter->Seek(old_key);
|
||||
} else {
|
||||
db_iter->SeekForPrev(old_key);
|
||||
ref_iter->SeekForPrev(old_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the result.
|
||||
if (db_iter->Valid()) {
|
||||
ASSERT_TRUE(db_iter->status().ok());
|
||||
if (data.recently_touched_keys.count(
|
||||
db_iter->key().ToString())) {
|
||||
// Ended on a key that may have been mutated during the
|
||||
// operation. Reference iterator skips such keys, so we
|
||||
// can't check the exact result.
|
||||
|
||||
// Check that the key moved in the right direction.
|
||||
if (forward) {
|
||||
if (seek)
|
||||
ASSERT_GE(db_iter->key().ToString(), old_key);
|
||||
else
|
||||
ASSERT_GT(db_iter->key().ToString(), old_key);
|
||||
} else {
|
||||
if (seek)
|
||||
ASSERT_LE(db_iter->key().ToString(), old_key);
|
||||
else
|
||||
ASSERT_LT(db_iter->key().ToString(), old_key);
|
||||
}
|
||||
|
||||
if (ref_iter->Valid()) {
|
||||
// Check that DBIter didn't miss any non-mutated key.
|
||||
if (forward) {
|
||||
ASSERT_LT(db_iter->key().ToString(), ref_iter->key);
|
||||
} else {
|
||||
ASSERT_GT(db_iter->key().ToString(), ref_iter->key);
|
||||
}
|
||||
}
|
||||
// Tell the next iteration of the loop to reseek the
|
||||
// iterators.
|
||||
ref_iter->valid = false;
|
||||
|
||||
++num_recently_removed;
|
||||
} else {
|
||||
ASSERT_TRUE(ref_iter->Valid());
|
||||
ASSERT_EQ(ref_iter->key, db_iter->key().ToString());
|
||||
ASSERT_EQ(ref_iter->value, db_iter->value());
|
||||
++num_matching;
|
||||
}
|
||||
} else if (db_iter->status().ok()) {
|
||||
ASSERT_FALSE(ref_iter->Valid());
|
||||
++num_at_end;
|
||||
} else {
|
||||
// Non-ok status. Nothing to check here.
|
||||
// Tell the next iteration of the loop to reseek the
|
||||
// iterators.
|
||||
ref_iter->valid = false;
|
||||
++num_not_ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all cases were hit many times.
|
||||
EXPECT_GT(num_matching, 10000);
|
||||
EXPECT_GT(num_at_end, 10000);
|
||||
EXPECT_GT(num_not_ok, 10000);
|
||||
EXPECT_GT(num_recently_removed, 10000);
|
||||
|
||||
std::cout << "stats:\n exact matches: " << num_matching
|
||||
<< "\n end reached: " << num_at_end
|
||||
<< "\n non-ok status: " << num_not_ok
|
||||
<< "\n mutated on the fly: " << num_recently_removed << std::endl;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+327
-180
File diff suppressed because it is too large
Load Diff
+44
-3
@@ -862,6 +862,8 @@ TEST_P(DBIteratorTest, IteratorPinsRef) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
// SetOptions not defined in ROCKSDB LITE
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(DBIteratorTest, DBIteratorBoundTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -946,9 +948,7 @@ TEST_P(DBIteratorTest, DBIteratorBoundTest) {
|
||||
}
|
||||
|
||||
// prefix is the first letter of the key
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:1"}}));
|
||||
ASSERT_OK(Put("a", "0"));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("foo1", "bar1"));
|
||||
@@ -1035,6 +1035,7 @@ TEST_P(DBIteratorTest, DBIteratorBoundTest) {
|
||||
ASSERT_EQ(static_cast<int>(get_perf_context()->internal_delete_skipped_count), 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
int upper_bound_hits = 0;
|
||||
@@ -2232,6 +2233,46 @@ TEST_P(DBIteratorTest, SeekAfterHittingManyInternalKeys) {
|
||||
ASSERT_EQ(iter2->value().ToString(), "val_6");
|
||||
}
|
||||
|
||||
// Reproduces a former bug where iterator would skip some records when DBIter
|
||||
// re-seeks subiterator with Incomplete status.
|
||||
TEST_P(DBIteratorTest, NonBlockingIterationBugRepro) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
// Make sure the sst file has more than one block.
|
||||
table_options.flush_block_policy_factory =
|
||||
std::make_shared<FlushBlockEveryKeyPolicyFactory>();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Two records in sst file, each in its own block.
|
||||
Put("b", "");
|
||||
Put("d", "");
|
||||
Flush();
|
||||
|
||||
// Create a nonblocking iterator before writing to memtable.
|
||||
ReadOptions ropt;
|
||||
ropt.read_tier = kBlockCacheTier;
|
||||
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).
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
Put("c", "");
|
||||
}
|
||||
|
||||
// Load the second block in sst file into the block cache.
|
||||
{
|
||||
unique_ptr<Iterator> iter2(NewIterator(ReadOptions()));
|
||||
iter2->Seek("d");
|
||||
}
|
||||
|
||||
// Finally seek the nonblocking iterator.
|
||||
iter->Seek("a");
|
||||
// With the bug, the status used to be OK, and the iterator used to point to
|
||||
// "d".
|
||||
EXPECT_TRUE(iter->status().IsIncomplete());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBIteratorTestInstance, DBIteratorTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
|
||||
@@ -289,6 +289,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ TEST_F(DBOptionsTest, SetBytesPerSync) {
|
||||
const std::string kValue(kValueSize, 'v');
|
||||
ASSERT_EQ(options.bytes_per_sync, dbfull()->GetDBOptions().bytes_per_sync);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::RangeSync:0", [&](void* arg) {
|
||||
"WritableFileWriter::RangeSync:0", [&](void* /*arg*/) {
|
||||
counter++;
|
||||
});
|
||||
|
||||
@@ -183,7 +183,7 @@ TEST_F(DBOptionsTest, SetWalBytesPerSync) {
|
||||
int counter = 0;
|
||||
int low_bytes_per_sync = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::RangeSync:0", [&](void* arg) {
|
||||
"WritableFileWriter::RangeSync:0", [&](void* /*arg*/) {
|
||||
counter++;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -242,11 +242,12 @@ TEST_F(DBOptionsTest, WritableFileMaxBufferSize) {
|
||||
ASSERT_EQ(unmatch_cnt, 0);
|
||||
ASSERT_GE(match_cnt, 11);
|
||||
|
||||
buffer_size = 512 * 1024;
|
||||
match_cnt = 0;
|
||||
unmatch_cnt = 0;
|
||||
ASSERT_OK(
|
||||
dbfull()->SetDBOptions({{"writable_file_max_buffer_size", "524288"}}));
|
||||
buffer_size = 512 * 1024;
|
||||
match_cnt = 0;
|
||||
unmatch_cnt = 0; // SetDBOptions() will create a WriteableFileWriter
|
||||
|
||||
ASSERT_EQ(buffer_size,
|
||||
dbfull()->GetDBOptions().writable_file_max_buffer_size);
|
||||
i = 0;
|
||||
|
||||
+121
-18
@@ -69,27 +69,27 @@ TEST_F(DBPropertiesTest, Empty) {
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("1", num);
|
||||
ASSERT_EQ("0", num);
|
||||
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("2", num);
|
||||
ASSERT_EQ("0", num);
|
||||
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("3", num);
|
||||
ASSERT_EQ("0", num);
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions(false));
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("2", num);
|
||||
ASSERT_EQ("0", num);
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
|
||||
ASSERT_EQ("0", num);
|
||||
ASSERT_EQ("1", num);
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
@@ -177,17 +177,16 @@ void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
|
||||
std::replace(tp_string.begin(), tp_string.end(), ';', ' ');
|
||||
std::replace(tp_string.begin(), tp_string.end(), '=', ' ');
|
||||
ResetTableProperties(tp);
|
||||
|
||||
sscanf(tp_string.c_str(),
|
||||
"# data blocks %" SCNu64 " # entries %" 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 %" SCNu64
|
||||
" filter block size %" SCNu64,
|
||||
" data block size %" SCNu64 " index block size (user-key? %" SCNu64
|
||||
") %" SCNu64 " filter block size %" SCNu64,
|
||||
&tp->num_data_blocks, &tp->num_entries, &tp->raw_key_size,
|
||||
&dummy_double, &tp->raw_value_size, &dummy_double, &tp->data_size,
|
||||
&tp->index_size, &tp->filter_size);
|
||||
&tp->index_key_is_user_key, &tp->index_size, &tp->filter_size);
|
||||
}
|
||||
|
||||
void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
@@ -224,7 +223,8 @@ void GetExpectedTableProperties(TableProperties* expected_tp,
|
||||
const int kKeySize, const int kValueSize,
|
||||
const int kKeysPerTable, const int kTableCount,
|
||||
const int kBloomBitsPerKey,
|
||||
const size_t kBlockSize) {
|
||||
const size_t kBlockSize,
|
||||
const bool index_key_is_user_key) {
|
||||
const int kKeyCount = kTableCount * kKeysPerTable;
|
||||
const int kAvgSuccessorSize = kKeySize / 5;
|
||||
const int kEncodingSavePerKey = kKeySize / 4;
|
||||
@@ -238,7 +238,8 @@ void GetExpectedTableProperties(TableProperties* expected_tp,
|
||||
expected_tp->data_size =
|
||||
kTableCount * (kKeysPerTable * (kKeySize + 8 + kValueSize));
|
||||
expected_tp->index_size =
|
||||
expected_tp->num_data_blocks * (kAvgSuccessorSize + 8);
|
||||
expected_tp->num_data_blocks *
|
||||
(kAvgSuccessorSize + (index_key_is_user_key ? 0 : 8));
|
||||
expected_tp->filter_size =
|
||||
kTableCount * (kKeysPerTable * kBloomBitsPerKey / 8);
|
||||
}
|
||||
@@ -315,14 +316,14 @@ TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
}
|
||||
std::string property;
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
|
||||
TableProperties output_tp;
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
bool index_key_is_user_key = output_tp.index_key_is_user_key > 0;
|
||||
|
||||
TableProperties expected_tp;
|
||||
GetExpectedTableProperties(&expected_tp, kKeySize, kValueSize,
|
||||
kKeysPerTable, kTableCount, kBloomBitsPerKey,
|
||||
table_options.block_size);
|
||||
|
||||
TableProperties output_tp;
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
table_options.block_size, index_key_is_user_key);
|
||||
|
||||
VerifyTableProperties(expected_tp, output_tp);
|
||||
}
|
||||
@@ -489,6 +490,7 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
}
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &tp_string);
|
||||
ParseTablePropertiesString(tp_string, &tp);
|
||||
bool index_key_is_user_key = tp.index_key_is_user_key > 0;
|
||||
ASSERT_EQ(sum_tp.data_size, tp.data_size);
|
||||
ASSERT_EQ(sum_tp.index_size, tp.index_size);
|
||||
ASSERT_EQ(sum_tp.filter_size, tp.filter_size);
|
||||
@@ -497,9 +499,9 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
ASSERT_EQ(sum_tp.num_data_blocks, tp.num_data_blocks);
|
||||
ASSERT_EQ(sum_tp.num_entries, tp.num_entries);
|
||||
if (table > 3) {
|
||||
GetExpectedTableProperties(&expected_tp, kKeySize, kValueSize,
|
||||
kKeysPerTable, table, kBloomBitsPerKey,
|
||||
table_options.block_size);
|
||||
GetExpectedTableProperties(
|
||||
&expected_tp, kKeySize, kValueSize, kKeysPerTable, table,
|
||||
kBloomBitsPerKey, table_options.block_size, index_key_is_user_key);
|
||||
// Gives larger bias here as index block size, filter block size,
|
||||
// and data block size become much harder to estimate in this test.
|
||||
VerifyTableProperties(tp, expected_tp, 0.5, 0.4, 0.4, 0.25);
|
||||
@@ -1436,6 +1438,107 @@ TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
ASSERT_TRUE(listener->callback_triggered);
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, BlockCacheProperties) {
|
||||
Options options;
|
||||
uint64_t value;
|
||||
|
||||
// Block cache properties are not available for tables other than
|
||||
// block-based table.
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
Reopen(options);
|
||||
ASSERT_FALSE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
|
||||
ASSERT_FALSE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
ASSERT_FALSE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
|
||||
options.table_factory.reset(NewCuckooTableFactory());
|
||||
Reopen(options);
|
||||
ASSERT_FALSE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
|
||||
ASSERT_FALSE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
ASSERT_FALSE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
|
||||
// Block cache properties are not available if block cache is not used.
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
ASSERT_FALSE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
|
||||
ASSERT_FALSE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
ASSERT_FALSE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
|
||||
// Test with empty block cache.
|
||||
constexpr size_t kCapacity = 100;
|
||||
auto block_cache = NewLRUCache(kCapacity, 0 /*num_shard_bits*/);
|
||||
table_options.block_cache = block_cache;
|
||||
table_options.no_block_cache = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
|
||||
ASSERT_EQ(kCapacity, value);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
ASSERT_EQ(0, value);
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
ASSERT_EQ(0, value);
|
||||
|
||||
// Insert unpinned item to the cache and check size.
|
||||
constexpr size_t kSize1 = 50;
|
||||
block_cache->Insert("item1", nullptr /*value*/, kSize1, nullptr /*deleter*/);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
|
||||
ASSERT_EQ(kCapacity, value);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
ASSERT_EQ(kSize1, value);
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
ASSERT_EQ(0, value);
|
||||
|
||||
// Insert pinned item to the cache and check size.
|
||||
constexpr size_t kSize2 = 30;
|
||||
Cache::Handle* item2 = nullptr;
|
||||
block_cache->Insert("item2", nullptr /*value*/, kSize2, nullptr /*deleter*/,
|
||||
&item2);
|
||||
ASSERT_NE(nullptr, item2);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
|
||||
ASSERT_EQ(kCapacity, value);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
ASSERT_EQ(kSize1 + kSize2, value);
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
ASSERT_EQ(kSize2, value);
|
||||
|
||||
// Insert another pinned item to make the cache over-sized.
|
||||
constexpr size_t kSize3 = 80;
|
||||
Cache::Handle* item3 = nullptr;
|
||||
block_cache->Insert("item3", nullptr /*value*/, kSize3, nullptr /*deleter*/,
|
||||
&item3);
|
||||
ASSERT_NE(nullptr, item2);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
|
||||
ASSERT_EQ(kCapacity, value);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
// Item 1 is evicted.
|
||||
ASSERT_EQ(kSize2 + kSize3, value);
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
ASSERT_EQ(kSize2 + kSize3, value);
|
||||
|
||||
// Check size after release.
|
||||
block_cache->Release(item2);
|
||||
block_cache->Release(item3);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
|
||||
ASSERT_EQ(kCapacity, value);
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
// item2 will be evicted, while item3 remain in cache after release.
|
||||
ASSERT_EQ(kSize3, value);
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
ASSERT_EQ(0, value);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
@@ -433,8 +433,8 @@ TEST_F(DBRangeDelTest, ValidUniversalSubcompactionBoundaries) {
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
->cfd(),
|
||||
1 /* input_level */, 2 /* output_level */, 0 /* output_path_id */,
|
||||
nullptr /* begin */, nullptr /* end */, true /* exclusive */,
|
||||
true /* disallow_trivial_move */));
|
||||
0 /* max_subcompactions */, nullptr /* begin */, nullptr /* end */,
|
||||
true /* exclusive */, true /* disallow_trivial_move */));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
+116
-10
@@ -20,6 +20,37 @@ class DBSSTTest : public DBTestBase {
|
||||
DBSSTTest() : DBTestBase("/db_sst_test") {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// A class which remembers the name of each flushed file.
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(info.file_path);
|
||||
}
|
||||
|
||||
std::vector<std::string> GetFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> result;
|
||||
for (auto fname : flushed_files_) {
|
||||
result.push_back(fname);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void ClearFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> flushed_files_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBSSTTest, DontDeletePendingOutputs) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
@@ -72,7 +103,7 @@ TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) {
|
||||
ASSERT_GT(num_files, 0);
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
GetSstFiles(dbname_, &filenames);
|
||||
GetSstFiles(env_, dbname_, &filenames);
|
||||
int num_ldb_files = 0;
|
||||
for (size_t i = 0; i < filenames.size(); ++i) {
|
||||
if (i & 1) {
|
||||
@@ -231,11 +262,11 @@ TEST_F(DBSSTTest, DBWithSstFileManager) {
|
||||
int files_deleted = 0;
|
||||
int files_moved = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnAddFile", [&](void* arg) { files_added++; });
|
||||
"SstFileManagerImpl::OnAddFile", [&](void* /*arg*/) { files_added++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnDeleteFile", [&](void* arg) { files_deleted++; });
|
||||
"SstFileManagerImpl::OnDeleteFile", [&](void* /*arg*/) { files_deleted++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnMoveFile", [&](void* arg) { files_moved++; });
|
||||
"SstFileManagerImpl::OnMoveFile", [&](void* /*arg*/) { files_moved++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -408,12 +439,12 @@ TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
|
||||
int bg_delete_file = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DeleteScheduler::DeleteTrashFile:DeleteFile",
|
||||
[&](void* arg) { bg_delete_file++; });
|
||||
[&](void* /*arg*/) { bg_delete_file++; });
|
||||
// The deletion scheduler sometimes skips marking file as trash according to
|
||||
// a heuristic. In that case the deletion will go through the below SyncPoint.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DeleteScheduler::DeleteFile",
|
||||
[&](void* arg) { bg_delete_file++; });
|
||||
[&](void* /*arg*/) { bg_delete_file++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -483,7 +514,7 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
|
||||
int bg_delete_file = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DeleteScheduler::DeleteTrashFile:DeleteFile",
|
||||
[&](void* arg) { bg_delete_file++; });
|
||||
[&](void* /*arg*/) { bg_delete_file++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Status s;
|
||||
@@ -557,12 +588,13 @@ TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
|
||||
int completed_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction():CancelledCompaction", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction():CancelledCompaction", [&](void* /*arg*/) {
|
||||
sfm->SetMaxAllowedSpaceUsage(0);
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun",
|
||||
[&](void* arg) { completed_compactions++; });
|
||||
[&](void* /*arg*/) { completed_compactions++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
@@ -584,6 +616,8 @@ TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
// Because we set a callback in CancelledCompaction, we actually
|
||||
// let the compaction run
|
||||
ASSERT_GT(completed_compactions, 0);
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
// Make sure the stat is bumped
|
||||
@@ -591,6 +625,78 @@ TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.sst_file_manager = sst_file_manager;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
FlushedFileCollector* collector = new FlushedFileCollector();
|
||||
options.listeners.emplace_back(collector);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
// Generate a file containing 10 keys.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
uint64_t total_file_size = 0;
|
||||
auto files_in_db = GetAllSSTFiles(&total_file_size);
|
||||
// Set the maximum allowed space usage to the current total size
|
||||
sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1);
|
||||
|
||||
// Generate another file to trigger compaction.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// OK, now trigger a manual compaction
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
// Wait for manual compaction to get scheduled and finish
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
// Make sure the stat is bumped
|
||||
ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
COMPACTION_CANCELLED),
|
||||
1);
|
||||
|
||||
// Now make sure CompactFiles also gets cancelled
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
dbfull()->CompactFiles(rocksdb::CompactionOptions(), l0_files, 0);
|
||||
|
||||
// Wait for manual compaction to get scheduled and finish
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
COMPACTION_CANCELLED),
|
||||
2);
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
|
||||
// Now let the flush through and make sure GetCompactionsReservedSize
|
||||
// returns to normal
|
||||
sfm->SetMaxAllowedSpaceUsage(0);
|
||||
int completed_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactFilesImpl:End", [&](void* /*arg*/) { completed_compactions++; });
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
dbfull()->CompactFiles(rocksdb::CompactionOptions(), l0_files, 0);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
ASSERT_GT(completed_compactions, 0);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
|
||||
// This test will set a maximum allowed space for the DB, then it will
|
||||
// keep filling the DB until the limit is reached and bg_error_ is set.
|
||||
@@ -627,7 +733,7 @@ TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::FinishCompactionOutputFile:MaxAllowedSpaceReached",
|
||||
[&](void* arg) {
|
||||
[&](void* /*arg*/) {
|
||||
bg_error_set = true;
|
||||
GetAllSSTFiles(&total_sst_files_size);
|
||||
reached_max_space_on_compaction++;
|
||||
|
||||
@@ -157,10 +157,10 @@ TEST_F(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::RenewIterators:Null",
|
||||
[&](void* arg) { file_iters_renewed_null = true; });
|
||||
[&](void* /*arg*/) { file_iters_renewed_null = true; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::RenewIterators:Copy",
|
||||
[&](void* arg) { file_iters_renewed_copy = true; });
|
||||
[&](void* /*arg*/) { file_iters_renewed_copy = true; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
const int num_records = 1000;
|
||||
for (int i = 1; i < num_records; ++i) {
|
||||
@@ -415,7 +415,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorUpperBound) {
|
||||
int immutable_seeks = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::SeekInternal:Immutable",
|
||||
[&](void* arg) { ++immutable_seeks; });
|
||||
[&](void* /*arg*/) { ++immutable_seeks; });
|
||||
|
||||
// Seek to 13. This should not require any immutable seeks.
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -809,6 +809,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
+249
-25
@@ -234,11 +234,11 @@ TEST_F(DBTest, SkipDelay) {
|
||||
std::atomic<int> sleep_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:Sleep",
|
||||
[&](void* arg) { sleep_count.fetch_add(1); });
|
||||
[&](void* /*arg*/) { sleep_count.fetch_add(1); });
|
||||
std::atomic<int> wait_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:Wait",
|
||||
[&](void* arg) { wait_count.fetch_add(1); });
|
||||
[&](void* /*arg*/) { wait_count.fetch_add(1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
@@ -2153,10 +2153,14 @@ TEST_F(DBTest, GroupCommitTest) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
env_->log_write_slowdown_.store(100);
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
Reopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"WriteThread::JoinBatchGroup:BeganWaiting",
|
||||
"DBImpl::WriteImpl:BeforeLeaderEnters"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Start threads
|
||||
GCThread thread[kGCNumThreads];
|
||||
for (int id = 0; id < kGCNumThreads; id++) {
|
||||
@@ -2165,13 +2169,7 @@ TEST_F(DBTest, GroupCommitTest) {
|
||||
thread[id].done = false;
|
||||
env_->StartThread(GCThreadBody, &thread[id]);
|
||||
}
|
||||
|
||||
for (int id = 0; id < kGCNumThreads; id++) {
|
||||
while (thread[id].done == false) {
|
||||
env_->SleepForMicroseconds(100000);
|
||||
}
|
||||
}
|
||||
env_->log_write_slowdown_.store(0);
|
||||
env_->WaitForJoin();
|
||||
|
||||
ASSERT_GT(TestGetTickerCount(options, WRITE_DONE_BY_OTHER), 0);
|
||||
|
||||
@@ -2563,6 +2561,7 @@ class ModelDB : public DB {
|
||||
std::string name_ = "";
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
static std::string RandomKey(Random* rnd, int minimum = 0) {
|
||||
int len;
|
||||
do {
|
||||
@@ -2719,6 +2718,7 @@ TEST_P(DBTestRandomized, Randomized) {
|
||||
if (model_snap != nullptr) model.ReleaseSnapshot(model_snap);
|
||||
if (db_snap != nullptr) db_->ReleaseSnapshot(db_snap);
|
||||
}
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
|
||||
TEST_F(DBTest, BlockBasedTablePrefixIndexTest) {
|
||||
// create a DB with block prefix index
|
||||
@@ -3348,6 +3348,56 @@ TEST_F(DBTest, WriteSingleThreadEntry) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ConcurrentFlushWAL) {
|
||||
const size_t cnt = 100;
|
||||
Options options;
|
||||
WriteOptions wopt;
|
||||
ReadOptions ropt;
|
||||
for (bool two_write_queues : {false, true}) {
|
||||
for (bool manual_wal_flush : {false, true}) {
|
||||
options.two_write_queues = two_write_queues;
|
||||
options.manual_wal_flush = manual_wal_flush;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
std::vector<port::Thread> threads;
|
||||
threads.emplace_back([&] {
|
||||
for (size_t i = 0; i < cnt; i++) {
|
||||
auto istr = ToString(i);
|
||||
db_->Put(wopt, db_->DefaultColumnFamily(), "a" + istr, "b" + istr);
|
||||
}
|
||||
});
|
||||
if (two_write_queues) {
|
||||
threads.emplace_back([&] {
|
||||
for (size_t i = cnt; i < 2 * cnt; i++) {
|
||||
auto istr = ToString(i);
|
||||
WriteBatch batch;
|
||||
batch.Put("a" + istr, "b" + istr);
|
||||
dbfull()->WriteImpl(wopt, &batch, nullptr, nullptr, 0, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
threads.emplace_back([&] {
|
||||
for (size_t i = 0; i < cnt * 100; i++) { // FlushWAL is faster than Put
|
||||
db_->FlushWAL(false);
|
||||
}
|
||||
});
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
options.create_if_missing = false;
|
||||
// Recover from the wal and make sure that it is not corrupted
|
||||
Reopen(options);
|
||||
for (size_t i = 0; i < cnt; i++) {
|
||||
PinnableSlice pval;
|
||||
auto istr = ToString(i);
|
||||
ASSERT_OK(
|
||||
db_->Get(ropt, db_->DefaultColumnFamily(), "a" + istr, &pval));
|
||||
ASSERT_TRUE(pval == ("b" + istr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, DynamicMemtableOptions) {
|
||||
const uint64_t k64KB = 1 << 16;
|
||||
@@ -3441,7 +3491,7 @@ TEST_F(DBTest, DynamicMemtableOptions) {
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:Wait",
|
||||
[&](void* arg) { sleeping_task_low.WakeUp(); });
|
||||
[&](void* /*arg*/) { sleeping_task_low.WakeUp(); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
while (!sleeping_task_low.WokenUp() && count < 256) {
|
||||
@@ -4557,6 +4607,181 @@ TEST_F(DBTest, FileCreationRandomFailure) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
int CountIter(Iterator* iter, const Slice& key) {
|
||||
int count = 0;
|
||||
for (iter->Seek(key); iter->Valid() && iter->status() == Status::OK();
|
||||
iter->Next()) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Create multiple SST files each with a different prefix_extractor config,
|
||||
// verify iterators can read all SST files using the latest config.
|
||||
TEST_F(DBTest, DynamicBloomFilterMultipleSST) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.disable_auto_compactions = true;
|
||||
// Enable prefix bloom for SST files
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.prefix_same_as_start = true;
|
||||
|
||||
// first SST with fixed:1 BF
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("foq1", "bar1"));
|
||||
ASSERT_OK(Put("fpa", "0"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
Iterator* iter_old = db_->NewIterator(read_options);
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 4);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "capped:3"}}));
|
||||
ASSERT_EQ(0, strcmp(dbfull()->GetOptions().prefix_extractor->Name(),
|
||||
"rocksdb.CappedPrefix.3"));
|
||||
Iterator* iter = db_->NewIterator(read_options);
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 2);
|
||||
|
||||
// second SST with capped:3 BF
|
||||
ASSERT_OK(Put("foo3", "bar3"));
|
||||
ASSERT_OK(Put("foo4", "bar4"));
|
||||
ASSERT_OK(Put("foq5", "bar5"));
|
||||
ASSERT_OK(Put("fpb", "1"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
// BF is cappped:3 now
|
||||
Iterator* iter_tmp = db_->NewIterator(read_options);
|
||||
ASSERT_EQ(CountIter(iter_tmp, "foo"), 4);
|
||||
delete iter_tmp;
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:2"}}));
|
||||
ASSERT_EQ(0, strcmp(dbfull()->GetOptions().prefix_extractor->Name(),
|
||||
"rocksdb.FixedPrefix.2"));
|
||||
// third SST with fixed:2 BF
|
||||
ASSERT_OK(Put("foo6", "bar6"));
|
||||
ASSERT_OK(Put("foo7", "bar7"));
|
||||
ASSERT_OK(Put("foq8", "bar8"));
|
||||
ASSERT_OK(Put("fpc", "2"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
// BF is fixed:2 now
|
||||
iter_tmp = db_->NewIterator(read_options);
|
||||
ASSERT_EQ(CountIter(iter_tmp, "foo"), 9);
|
||||
delete iter_tmp;
|
||||
|
||||
// TODO(Zhongyi): verify existing iterator cannot see newly inserted keys
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 4);
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 2);
|
||||
delete iter;
|
||||
delete iter_old;
|
||||
|
||||
// keys in all three SSTs are visible to iterator
|
||||
Iterator* iter_all = db_->NewIterator(read_options);
|
||||
ASSERT_EQ(CountIter(iter_all, "foo"), 9);
|
||||
delete iter_all;
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "capped:3"}}));
|
||||
ASSERT_EQ(0, strcmp(dbfull()->GetOptions().prefix_extractor->Name(),
|
||||
"rocksdb.CappedPrefix.3"));
|
||||
iter_all = db_->NewIterator(read_options);
|
||||
ASSERT_EQ(CountIter(iter_all, "foo"), 6);
|
||||
delete iter_all;
|
||||
// TODO(Zhongyi): add test for cases where certain SST are skipped
|
||||
// Also verify BF related counters like BLOOM_FILTER_USEFUL
|
||||
}
|
||||
|
||||
// Create a new column family in a running DB, change prefix_extractor
|
||||
// dynamically, verify the iterator created on the new column family behaves
|
||||
// as expected
|
||||
TEST_F(DBTest, DynamicBloomFilterNewColumnFamily) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.disable_auto_compactions = true;
|
||||
// Enable prefix bloom for SST files
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.prefix_same_as_start = true;
|
||||
// create a new CF and set prefix_extractor dynamically
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(3));
|
||||
CreateColumnFamilies({"ramen_dojo"}, options);
|
||||
ASSERT_EQ(0,
|
||||
strcmp(dbfull()->GetOptions(handles_[2]).prefix_extractor->Name(),
|
||||
"rocksdb.CappedPrefix.3"));
|
||||
ASSERT_OK(Put(2, "foo3", "bar3"));
|
||||
ASSERT_OK(Put(2, "foo4", "bar4"));
|
||||
ASSERT_OK(Put(2, "foo5", "bar5"));
|
||||
ASSERT_OK(Put(2, "foq6", "bar6"));
|
||||
ASSERT_OK(Put(2, "fpq7", "bar7"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
Iterator* iter = db_->NewIterator(read_options, handles_[2]);
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 3);
|
||||
delete iter;
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions(handles_[2], {{"prefix_extractor", "fixed:2"}}));
|
||||
ASSERT_EQ(0,
|
||||
strcmp(dbfull()->GetOptions(handles_[2]).prefix_extractor->Name(),
|
||||
"rocksdb.FixedPrefix.2"));
|
||||
iter = db_->NewIterator(read_options, handles_[2]);
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 4);
|
||||
delete iter;
|
||||
}
|
||||
|
||||
// Verify it's possible to change prefix_extractor at runtime and iterators
|
||||
// behaves as expected
|
||||
TEST_F(DBTest, DynamicBloomFilterOptions) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.disable_auto_compactions = true;
|
||||
// Enable prefix bloom for SST files
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("foo1", "bar1"));
|
||||
ASSERT_OK(Put("fpa", "0"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("foo3", "bar3"));
|
||||
ASSERT_OK(Put("foo4", "bar4"));
|
||||
ASSERT_OK(Put("foo5", "bar5"));
|
||||
ASSERT_OK(Put("fpb", "1"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("foo6", "bar6"));
|
||||
ASSERT_OK(Put("foo7", "bar7"));
|
||||
ASSERT_OK(Put("foo8", "bar8"));
|
||||
ASSERT_OK(Put("fpc", "2"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.prefix_same_as_start = true;
|
||||
Iterator* iter = db_->NewIterator(read_options);
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 12);
|
||||
delete iter;
|
||||
Iterator* iter_old = db_->NewIterator(read_options);
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 12);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "capped:3"}}));
|
||||
ASSERT_EQ(0, strcmp(dbfull()->GetOptions().prefix_extractor->Name(),
|
||||
"rocksdb.CappedPrefix.3"));
|
||||
iter = db_->NewIterator(read_options);
|
||||
// "fp*" should be skipped
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 9);
|
||||
delete iter;
|
||||
|
||||
// iterator created before should not be affected and see all keys
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 12);
|
||||
delete iter_old;
|
||||
}
|
||||
|
||||
TEST_F(DBTest, DynamicMiscOptions) {
|
||||
// Test max_sequential_skip_in_iterations
|
||||
Options options;
|
||||
@@ -5144,7 +5369,7 @@ TEST_F(DBTest, AutomaticConflictsWithManualCompaction) {
|
||||
std::atomic<int> callback_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:Conflict",
|
||||
[&](void* arg) { callback_count.fetch_add(1); });
|
||||
[&](void* /*arg*/) { callback_count.fetch_add(1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
@@ -5367,7 +5592,7 @@ TEST_F(DBTest, HardLimit) {
|
||||
|
||||
std::atomic<int> callback_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack("DBImpl::DelayWrite:Wait",
|
||||
[&](void* arg) {
|
||||
[&](void* /*arg*/) {
|
||||
callback_count.fetch_add(1);
|
||||
sleeping_task_low.WakeUp();
|
||||
});
|
||||
@@ -5392,21 +5617,20 @@ TEST_F(DBTest, HardLimit) {
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
#if !defined(ROCKSDB_LITE) && !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
class WriteStallListener : public EventListener {
|
||||
public:
|
||||
WriteStallListener() : cond_(&mutex_),
|
||||
condition_(WriteStallCondition::kNormal),
|
||||
expected_(WriteStallCondition::kNormal),
|
||||
expected_set_(false)
|
||||
{}
|
||||
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;
|
||||
if (expected_set_ && condition_ == expected_) {
|
||||
cond_.Signal();
|
||||
expected_set_ = false;
|
||||
}
|
||||
}
|
||||
bool CheckCondition(WriteStallCondition expected) {
|
||||
@@ -5504,7 +5728,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
|
||||
// Only allow one compactin going through.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BackgroundCallCompaction:0", [&](void* arg) {
|
||||
"BackgroundCallCompaction:0", [&](void* /*arg*/) {
|
||||
// Schedule a sleeping task.
|
||||
sleeping_task_low.Reset();
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
@@ -5614,7 +5838,7 @@ TEST_F(DBTest, LastWriteBufferDelay) {
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
#endif // !defined(ROCKSDB_LITE) && !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
|
||||
TEST_F(DBTest, FailWhenCompressionNotSupportedTest) {
|
||||
CompressionType compressions[] = {kZlibCompression, kBZip2Compression,
|
||||
|
||||
+8
-7
@@ -1112,7 +1112,7 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
|
||||
size_t out_bytes = 0;
|
||||
std::vector<std::string> files;
|
||||
GetSstFiles(dbname_, &files);
|
||||
GetSstFiles(env_, dbname_, &files);
|
||||
for (const auto& file : files) {
|
||||
uint64_t curr_bytes;
|
||||
env_->GetFileSize(dbname_ + "/" + file, &curr_bytes);
|
||||
@@ -1691,7 +1691,7 @@ TEST_F(DBTest2, SyncPointMarker) {
|
||||
std::atomic<int> sync_point_called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTest2::MarkedPoint",
|
||||
[&](void* arg) { sync_point_called.fetch_add(1); });
|
||||
[&](void* /*arg*/) { sync_point_called.fetch_add(1); });
|
||||
|
||||
// The first dependency enforces Marker can be loaded before MarkedPoint.
|
||||
// The second checks that thread 1's MarkedPoint should be disabled here.
|
||||
@@ -1978,7 +1978,7 @@ TEST_F(DBTest2, AutomaticCompactionOverlapManualCompaction) {
|
||||
// can fit in L2, these 2 files will be moved to L2 and overlap with
|
||||
// the running compaction and break the LSM consistency.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Start", [&](void* arg) {
|
||||
"CompactionJob::Run():Start", [&](void* /*arg*/) {
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "2"},
|
||||
{"max_bytes_for_level_base", "1"}}));
|
||||
@@ -2044,7 +2044,7 @@ TEST_F(DBTest2, ManualCompactionOverlapManualCompaction) {
|
||||
// the running compaction and break the LSM consistency.
|
||||
std::atomic<bool> flag(false);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Start", [&](void* arg) {
|
||||
"CompactionJob::Run():Start", [&](void* /*arg*/) {
|
||||
if (flag.exchange(true)) {
|
||||
// We want to make sure to call this callback only once
|
||||
return;
|
||||
@@ -2296,7 +2296,8 @@ TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
kBytesPerKey) /* rate_bytes_per_sec */,
|
||||
10 * 1000 /* refill_period_us */, 10 /* fairness */,
|
||||
RateLimiter::Mode::kReadsOnly));
|
||||
options.use_direct_io_for_flush_and_compaction = use_direct_io;
|
||||
options.use_direct_reads = options.use_direct_io_for_flush_and_compaction =
|
||||
use_direct_io;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = 16384;
|
||||
bbto.no_block_cache = true;
|
||||
@@ -2318,7 +2319,7 @@ TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
// chose 1MB as the upper bound on the total bytes read.
|
||||
size_t rate_limited_bytes =
|
||||
options.rate_limiter->GetTotalBytesThrough(Env::IO_LOW);
|
||||
// Include the explict prefetch of the footer in direct I/O case.
|
||||
// Include the explicit prefetch of the footer in direct I/O case.
|
||||
size_t direct_io_extra = use_direct_io ? 512 * 1024 : 0;
|
||||
ASSERT_GE(rate_limited_bytes,
|
||||
static_cast<size_t>(kNumKeysPerFile * kBytesPerKey * kNumL0Files +
|
||||
@@ -2478,7 +2479,7 @@ TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::PurgeObsoleteFiles:Begin",
|
||||
[&](void* arg) { env_->SleepForMicroseconds(1000000); });
|
||||
[&](void* /*arg*/) { env_->SleepForMicroseconds(1000000); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put("key", "val");
|
||||
|
||||
+13
-5
@@ -575,9 +575,17 @@ void DBTestBase::DestroyAndReopen(const Options& options) {
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
|
||||
void DBTestBase::Destroy(const Options& options) {
|
||||
void DBTestBase::Destroy(const Options& options, bool delete_cf_paths) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
if (delete_cf_paths) {
|
||||
for (size_t i = 0; i < handles_.size(); ++i) {
|
||||
ColumnFamilyDescriptor cfdescriptor;
|
||||
handles_[i]->GetDescriptor(&cfdescriptor);
|
||||
column_families.push_back(cfdescriptor);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
ASSERT_OK(DestroyDB(dbname_, options, column_families));
|
||||
}
|
||||
|
||||
Status DBTestBase::ReadOnlyReopen(const Options& options) {
|
||||
@@ -1017,9 +1025,9 @@ std::string DBTestBase::DumpSSTableList() {
|
||||
return property;
|
||||
}
|
||||
|
||||
void DBTestBase::GetSstFiles(std::string path,
|
||||
void DBTestBase::GetSstFiles(Env* env, std::string path,
|
||||
std::vector<std::string>* files) {
|
||||
env_->GetChildren(path, files);
|
||||
env->GetChildren(path, files);
|
||||
|
||||
files->erase(
|
||||
std::remove_if(files->begin(), files->end(), [](std::string name) {
|
||||
@@ -1031,7 +1039,7 @@ void DBTestBase::GetSstFiles(std::string path,
|
||||
|
||||
int DBTestBase::GetSstFileCount(std::string path) {
|
||||
std::vector<std::string> files;
|
||||
GetSstFiles(path, &files);
|
||||
DBTestBase::GetSstFiles(env_, path, &files);
|
||||
return static_cast<int>(files.size());
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -451,8 +451,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status NewSequentialFile(const std::string& f, unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
virtual Status NewSequentialFile(const std::string& f,
|
||||
unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
class CountingFile : public SequentialFile {
|
||||
public:
|
||||
CountingFile(unique_ptr<SequentialFile>&& target,
|
||||
@@ -605,7 +606,7 @@ class MockTimeEnv : public EnvWrapper {
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t current_time_ = 0;
|
||||
std::atomic<uint64_t> current_time_{0};
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -803,7 +804,7 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
void DestroyAndReopen(const Options& options);
|
||||
|
||||
void Destroy(const Options& options);
|
||||
void Destroy(const Options& options, bool delete_cf_paths = false);
|
||||
|
||||
Status ReadOnlyReopen(const Options& options);
|
||||
|
||||
@@ -903,7 +904,8 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
std::string DumpSSTableList();
|
||||
|
||||
void GetSstFiles(std::string path, std::vector<std::string>* files);
|
||||
static void GetSstFiles(Env* env, std::string path,
|
||||
std::vector<std::string>* files);
|
||||
|
||||
int GetSstFileCount(std::string path);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -40,6 +41,12 @@ class DBTestUniversalCompaction : public DBTestUniversalCompactionBase {
|
||||
DBTestUniversalCompactionBase("/db_universal_compaction_test") {}
|
||||
};
|
||||
|
||||
class DBTestUniversalDeleteTrigCompaction : public DBTestBase {
|
||||
public:
|
||||
DBTestUniversalDeleteTrigCompaction()
|
||||
: DBTestBase("/db_universal_compaction_test") {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
void VerifyCompactionResult(
|
||||
const ColumnFamilyMetaData& cf_meta,
|
||||
@@ -655,7 +662,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionTargetLevel) {
|
||||
ASSERT_EQ("0,0,0,0,1", FilesPerLevel(0));
|
||||
}
|
||||
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
class DBTestUniversalCompactionMultiLevels
|
||||
: public DBTestUniversalCompactionBase {
|
||||
public:
|
||||
@@ -691,13 +698,14 @@ TEST_P(DBTestUniversalCompactionMultiLevels, UniversalCompactionMultiLevels) {
|
||||
ASSERT_EQ(Get(1, Key(i % num_keys)), Key(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Tests universal compaction with trivial move enabled
|
||||
TEST_P(DBTestUniversalCompactionMultiLevels, UniversalCompactionTrivialMove) {
|
||||
int32_t trivial_move = 0;
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial", [&](void* arg) {
|
||||
non_trivial_move++;
|
||||
@@ -769,7 +777,7 @@ TEST_P(DBTestUniversalCompactionParallel, UniversalCompactionParallel) {
|
||||
std::atomic<int> num_compactions_running(0);
|
||||
std::atomic<bool> has_parallel(false);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack("CompactionJob::Run():Start",
|
||||
[&](void* arg) {
|
||||
[&](void* /*arg*/) {
|
||||
if (num_compactions_running.fetch_add(1) > 0) {
|
||||
has_parallel.store(true);
|
||||
return;
|
||||
@@ -784,7 +792,7 @@ TEST_P(DBTestUniversalCompactionParallel, UniversalCompactionParallel) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():End",
|
||||
[&](void* arg) { num_compactions_running.fetch_add(-1); });
|
||||
[&](void* /*arg*/) { num_compactions_running.fetch_add(-1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options = CurrentOptions(options);
|
||||
@@ -933,6 +941,7 @@ INSTANTIATE_TEST_CASE_P(DBTestUniversalCompactionParallel,
|
||||
DBTestUniversalCompactionParallel,
|
||||
::testing::Combine(::testing::Values(1, 10),
|
||||
::testing::Values(false)));
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionOptions) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -1148,13 +1157,14 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionCompressRatio2) {
|
||||
ASSERT_LT(TotalSize(), 120000U * 12 * 0.8 + 120000 * 2);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
// Test that checks trivial move in universal compaction
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionTrivialMoveTest1) {
|
||||
int32_t trivial_move = 0;
|
||||
int32_t non_trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial", [&](void* arg) {
|
||||
non_trivial_move++;
|
||||
@@ -1200,7 +1210,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionTrivialMoveTest2) {
|
||||
int32_t trivial_move = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* arg) { trivial_move++; });
|
||||
[&](void* /*arg*/) { trivial_move++; });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
@@ -1240,6 +1250,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionTrivialMoveTest2) {
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionFourPaths) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -1343,6 +1354,146 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionFourPaths) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionCFPathUse) {
|
||||
Options options = CurrentOptions();
|
||||
options.db_paths.emplace_back(dbname_, 300 * 1024);
|
||||
options.db_paths.emplace_back(dbname_ + "_2", 300 * 1024);
|
||||
options.db_paths.emplace_back(dbname_ + "_3", 500 * 1024);
|
||||
options.db_paths.emplace_back(dbname_ + "_4", 1024 * 1024 * 1024);
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.compaction_options_universal.size_ratio = 5;
|
||||
options.write_buffer_size = 111 << 10; // 114KB
|
||||
options.arena_block_size = 4 << 10;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.num_levels = 1;
|
||||
|
||||
std::vector<Options> option_vector;
|
||||
option_vector.emplace_back(options);
|
||||
ColumnFamilyOptions cf_opt1(options), cf_opt2(options);
|
||||
// Configure CF1 specific paths.
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1", 300 * 1024);
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_2", 300 * 1024);
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_3", 500 * 1024);
|
||||
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_4", 1024 * 1024 * 1024);
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt1);
|
||||
CreateColumnFamilies({"one"},option_vector[1]);
|
||||
|
||||
// Configura CF2 specific paths.
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2", 300 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_2", 300 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_3", 500 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_4", 1024 * 1024 * 1024);
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt2);
|
||||
CreateColumnFamilies({"two"},option_vector[2]);
|
||||
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
|
||||
|
||||
Random rnd(301);
|
||||
int key_idx = 0;
|
||||
int key_idx1 = 0;
|
||||
int key_idx2 = 0;
|
||||
|
||||
auto generate_file = [&]() {
|
||||
GenerateNewFile(0, &rnd, &key_idx);
|
||||
GenerateNewFile(1, &rnd, &key_idx1);
|
||||
GenerateNewFile(2, &rnd, &key_idx2);
|
||||
};
|
||||
|
||||
auto check_sstfilecount = [&](int path_id, int expected) {
|
||||
ASSERT_EQ(expected, GetSstFileCount(options.db_paths[path_id].path));
|
||||
ASSERT_EQ(expected, GetSstFileCount(cf_opt1.cf_paths[path_id].path));
|
||||
ASSERT_EQ(expected, GetSstFileCount(cf_opt2.cf_paths[path_id].path));
|
||||
};
|
||||
|
||||
auto check_getvalues = [&]() {
|
||||
for (int i = 0; i < key_idx; i++) {
|
||||
auto v = Get(0, Key(i));
|
||||
ASSERT_NE(v, "NOT_FOUND");
|
||||
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
|
||||
}
|
||||
|
||||
for (int i = 0; i < key_idx1; i++) {
|
||||
auto v = Get(1, Key(i));
|
||||
ASSERT_NE(v, "NOT_FOUND");
|
||||
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
|
||||
}
|
||||
|
||||
for (int i = 0; i < key_idx2; i++) {
|
||||
auto v = Get(2, Key(i));
|
||||
ASSERT_NE(v, "NOT_FOUND");
|
||||
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
|
||||
}
|
||||
};
|
||||
|
||||
// First three 110KB files are not going to second path.
|
||||
// After that, (100K, 200K)
|
||||
for (int num = 0; num < 3; num++) {
|
||||
generate_file();
|
||||
}
|
||||
|
||||
// Another 110KB triggers a compaction to 400K file to second path
|
||||
generate_file();
|
||||
check_sstfilecount(2, 1);
|
||||
|
||||
// (1, 4)
|
||||
generate_file();
|
||||
check_sstfilecount(2, 1);
|
||||
check_sstfilecount(0, 1);
|
||||
|
||||
// (1,1,4) -> (2, 4)
|
||||
generate_file();
|
||||
check_sstfilecount(2, 1);
|
||||
check_sstfilecount(1, 1);
|
||||
check_sstfilecount(0, 0);
|
||||
|
||||
// (1, 2, 4) -> (3, 4)
|
||||
generate_file();
|
||||
check_sstfilecount(2, 1);
|
||||
check_sstfilecount(1, 1);
|
||||
check_sstfilecount(0, 0);
|
||||
|
||||
// (1, 3, 4) -> (8)
|
||||
generate_file();
|
||||
check_sstfilecount(3, 1);
|
||||
|
||||
// (1, 8)
|
||||
generate_file();
|
||||
check_sstfilecount(3, 1);
|
||||
check_sstfilecount(0, 1);
|
||||
|
||||
// (1, 1, 8) -> (2, 8)
|
||||
generate_file();
|
||||
check_sstfilecount(3, 1);
|
||||
check_sstfilecount(1, 1);
|
||||
|
||||
// (1, 2, 8) -> (3, 8)
|
||||
generate_file();
|
||||
check_sstfilecount(3, 1);
|
||||
check_sstfilecount(1, 1);
|
||||
check_sstfilecount(0, 0);
|
||||
|
||||
// (1, 3, 8) -> (4, 8)
|
||||
generate_file();
|
||||
check_sstfilecount(2, 1);
|
||||
check_sstfilecount(3, 1);
|
||||
|
||||
// (1, 4, 8) -> (5, 8)
|
||||
generate_file();
|
||||
check_sstfilecount(3, 1);
|
||||
check_sstfilecount(2, 1);
|
||||
check_sstfilecount(0, 0);
|
||||
|
||||
check_getvalues();
|
||||
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
|
||||
|
||||
check_getvalues();
|
||||
|
||||
Destroy(options, true);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, IncreaseUniversalCompactionNumLevels) {
|
||||
std::function<void(int)> verify_func = [&](int num_keys_in_db) {
|
||||
std::string keys_in_db;
|
||||
@@ -1540,50 +1691,6 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSecondPathRatio) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, FullCompactionInBottomPriThreadPool) {
|
||||
const int kNumFilesTrigger = 3;
|
||||
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
for (bool allow_ingest_behind : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
options.allow_ingest_behind = allow_ingest_behind;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = num_levels_;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int num_bottom_pri_compactions = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkBottomCompaction",
|
||||
[&](void* arg) { ++num_bottom_pri_compactions; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
ASSERT_EQ(NumSortedRuns(), num);
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
if (allow_ingest_behind || num_levels_ > 1) {
|
||||
// allow_ingest_behind increases number of levels while sanitizing.
|
||||
ASSERT_EQ(1, num_bottom_pri_compactions);
|
||||
} else {
|
||||
// for single-level universal, everything's bottom level so nothing should
|
||||
// be executed in bottom-pri thread pool.
|
||||
ASSERT_EQ(0, num_bottom_pri_compactions);
|
||||
}
|
||||
// Verify that size amplification did occur
|
||||
ASSERT_EQ(NumSortedRuns(), 1);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, ConcurrentBottomPriLowPriCompactions) {
|
||||
if (num_levels_ == 1) {
|
||||
// for single-level universal, everything's bottom level so nothing should
|
||||
@@ -1656,7 +1763,7 @@ TEST_P(DBTestUniversalCompaction, RecalculateScoreAfterPicking) {
|
||||
|
||||
std::atomic<int> num_compactions_attempted(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:Start", [&](void* arg) {
|
||||
"DBImpl::BackgroundCompaction:Start", [&](void* /*arg*/) {
|
||||
++num_compactions_attempted;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -1749,6 +1856,241 @@ INSTANTIATE_TEST_CASE_P(DBTestUniversalManualCompactionOutputPathId,
|
||||
::testing::Combine(::testing::Values(1, 8),
|
||||
::testing::Bool()));
|
||||
|
||||
TEST_F(DBTestUniversalDeleteTrigCompaction, BasicL0toL1) {
|
||||
const int kNumKeys = 3000;
|
||||
const int kWindowSize = 100;
|
||||
const int kNumDelsTrigger = 90;
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_properties_collector_factories.emplace_back(
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger));
|
||||
opts.compaction_style = kCompactionStyleUniversal;
|
||||
opts.level0_file_num_compaction_trigger = 2;
|
||||
opts.compression = kNoCompression;
|
||||
opts.compaction_options_universal.size_ratio = 10;
|
||||
opts.compaction_options_universal.min_merge_width = 2;
|
||||
opts.compaction_options_universal.max_size_amplification_percent = 200;
|
||||
Reopen(opts);
|
||||
|
||||
// add an L1 file to prevent tombstones from dropping due to obsolescence
|
||||
// during flush
|
||||
int i;
|
||||
for (i = 0; i < 2000; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
// MoveFilesToLevel(6);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
for (i = 1999; i < kNumKeys; ++i) {
|
||||
if (i >= kNumKeys - kWindowSize &&
|
||||
i < kNumKeys - kWindowSize + kNumDelsTrigger) {
|
||||
Delete(Key(i));
|
||||
} else {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
}
|
||||
Flush();
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(6), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTestUniversalDeleteTrigCompaction, SingleLevel) {
|
||||
const int kNumKeys = 3000;
|
||||
const int kWindowSize = 100;
|
||||
const int kNumDelsTrigger = 90;
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_properties_collector_factories.emplace_back(
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger));
|
||||
opts.compaction_style = kCompactionStyleUniversal;
|
||||
opts.level0_file_num_compaction_trigger = 2;
|
||||
opts.compression = kNoCompression;
|
||||
opts.num_levels = 1;
|
||||
opts.compaction_options_universal.size_ratio = 10;
|
||||
opts.compaction_options_universal.min_merge_width = 2;
|
||||
opts.compaction_options_universal.max_size_amplification_percent = 200;
|
||||
Reopen(opts);
|
||||
|
||||
// add an L1 file to prevent tombstones from dropping due to obsolescence
|
||||
// during flush
|
||||
int i;
|
||||
for (i = 0; i < 2000; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
|
||||
for (i = 1999; i < kNumKeys; ++i) {
|
||||
if (i >= kNumKeys - kWindowSize &&
|
||||
i < kNumKeys - kWindowSize + kNumDelsTrigger) {
|
||||
Delete(Key(i));
|
||||
} else {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
}
|
||||
Flush();
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(DBTestUniversalDeleteTrigCompaction, MultipleLevels) {
|
||||
const int kWindowSize = 100;
|
||||
const int kNumDelsTrigger = 90;
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_properties_collector_factories.emplace_back(
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger));
|
||||
opts.compaction_style = kCompactionStyleUniversal;
|
||||
opts.level0_file_num_compaction_trigger = 4;
|
||||
opts.compression = kNoCompression;
|
||||
opts.compaction_options_universal.size_ratio = 10;
|
||||
opts.compaction_options_universal.min_merge_width = 2;
|
||||
opts.compaction_options_universal.max_size_amplification_percent = 200;
|
||||
Reopen(opts);
|
||||
|
||||
// add an L1 file to prevent tombstones from dropping due to obsolescence
|
||||
// during flush
|
||||
int i;
|
||||
for (i = 0; i < 500; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
for (i = 500; i < 1000; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
for (i = 1000; i < 1500; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
for (i = 1500; i < 2000; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(6), 0);
|
||||
|
||||
for (i = 1999; i < 2333; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
for (i = 2333; i < 2666; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
for (i = 2666; i < 2999; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(6), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(5), 0);
|
||||
|
||||
for (i = 1900; i < 2100; ++i) {
|
||||
Delete(Key(i));
|
||||
}
|
||||
Flush();
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(2));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(3));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(4));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(5));
|
||||
ASSERT_GT(NumTableFilesAtLevel(6), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTestUniversalDeleteTrigCompaction, OverlappingL0) {
|
||||
const int kWindowSize = 100;
|
||||
const int kNumDelsTrigger = 90;
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_properties_collector_factories.emplace_back(
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger));
|
||||
opts.compaction_style = kCompactionStyleUniversal;
|
||||
opts.level0_file_num_compaction_trigger = 5;
|
||||
opts.compression = kNoCompression;
|
||||
opts.compaction_options_universal.size_ratio = 10;
|
||||
opts.compaction_options_universal.min_merge_width = 2;
|
||||
opts.compaction_options_universal.max_size_amplification_percent = 200;
|
||||
Reopen(opts);
|
||||
|
||||
// add an L1 file to prevent tombstones from dropping due to obsolescence
|
||||
// during flush
|
||||
int i;
|
||||
for (i = 0; i < 2000; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
for (i = 2000; i < 3000; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
for (i = 3500; i < 4000; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
for (i = 2900; i < 3100; ++i) {
|
||||
Delete(Key(i));
|
||||
}
|
||||
Flush();
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(6), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTestUniversalDeleteTrigCompaction, IngestBehind) {
|
||||
const int kNumKeys = 3000;
|
||||
const int kWindowSize = 100;
|
||||
const int kNumDelsTrigger = 90;
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_properties_collector_factories.emplace_back(
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger));
|
||||
opts.compaction_style = kCompactionStyleUniversal;
|
||||
opts.level0_file_num_compaction_trigger = 2;
|
||||
opts.compression = kNoCompression;
|
||||
opts.allow_ingest_behind = true;
|
||||
opts.compaction_options_universal.size_ratio = 10;
|
||||
opts.compaction_options_universal.min_merge_width = 2;
|
||||
opts.compaction_options_universal.max_size_amplification_percent = 200;
|
||||
Reopen(opts);
|
||||
|
||||
// add an L1 file to prevent tombstones from dropping due to obsolescence
|
||||
// during flush
|
||||
int i;
|
||||
for (i = 0; i < 2000; ++i) {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
Flush();
|
||||
// MoveFilesToLevel(6);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
for (i = 1999; i < kNumKeys; ++i) {
|
||||
if (i >= kNumKeys - kWindowSize &&
|
||||
i < kNumKeys - kWindowSize + kNumDelsTrigger) {
|
||||
Delete(Key(i));
|
||||
} else {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
}
|
||||
Flush();
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(6));
|
||||
ASSERT_GT(NumTableFilesAtLevel(5), 0);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
@@ -1759,6 +2101,8 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
+101
-1
@@ -20,6 +20,106 @@ class DBWALTest : public DBTestBase {
|
||||
DBWALTest() : DBTestBase("/db_wal_test") {}
|
||||
};
|
||||
|
||||
// A SpecialEnv enriched to give more insight about deleted files
|
||||
class EnrichedSpecialEnv : public SpecialEnv {
|
||||
public:
|
||||
explicit EnrichedSpecialEnv(Env* base) : SpecialEnv(base) {}
|
||||
Status NewSequentialFile(const std::string& f, unique_ptr<SequentialFile>* r,
|
||||
const EnvOptions& soptions) override {
|
||||
InstrumentedMutexLock l(&env_mutex_);
|
||||
if (f == skipped_wal) {
|
||||
deleted_wal_reopened = true;
|
||||
if (IsWAL(f) && largetest_deleted_wal.size() != 0 &&
|
||||
f.compare(largetest_deleted_wal) <= 0) {
|
||||
gap_in_wals = true;
|
||||
}
|
||||
}
|
||||
return SpecialEnv::NewSequentialFile(f, r, soptions);
|
||||
}
|
||||
Status DeleteFile(const std::string& fname) override {
|
||||
if (IsWAL(fname)) {
|
||||
deleted_wal_cnt++;
|
||||
InstrumentedMutexLock l(&env_mutex_);
|
||||
// If this is the first WAL, remember its name and skip deleting it. We
|
||||
// remember its name partly because the application might attempt to
|
||||
// delete the file again.
|
||||
if (skipped_wal.size() != 0 && skipped_wal != fname) {
|
||||
if (largetest_deleted_wal.size() == 0 ||
|
||||
largetest_deleted_wal.compare(fname) < 0) {
|
||||
largetest_deleted_wal = fname;
|
||||
}
|
||||
} else {
|
||||
skipped_wal = fname;
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
return SpecialEnv::DeleteFile(fname);
|
||||
}
|
||||
bool IsWAL(const std::string& fname) {
|
||||
// printf("iswal %s\n", fname.c_str());
|
||||
return fname.compare(fname.size() - 3, 3, "log") == 0;
|
||||
}
|
||||
|
||||
InstrumentedMutex env_mutex_;
|
||||
// the wal whose actual delete was skipped by the env
|
||||
std::string skipped_wal = "";
|
||||
// the largest WAL that was requested to be deleted
|
||||
std::string largetest_deleted_wal = "";
|
||||
// number of WALs that were successfully deleted
|
||||
std::atomic<size_t> deleted_wal_cnt = {0};
|
||||
// the WAL whose delete from fs was skipped is reopened during recovery
|
||||
std::atomic<bool> deleted_wal_reopened = {false};
|
||||
// whether a gap in the WALs was detected during recovery
|
||||
std::atomic<bool> gap_in_wals = {false};
|
||||
};
|
||||
|
||||
class DBWALTestWithEnrichedEnv : public DBTestBase {
|
||||
public:
|
||||
DBWALTestWithEnrichedEnv() : DBTestBase("/db_wal_test") {
|
||||
enriched_env_ = new EnrichedSpecialEnv(env_->target());
|
||||
auto options = CurrentOptions();
|
||||
options.env = enriched_env_;
|
||||
Reopen(options);
|
||||
delete env_;
|
||||
// to be deleted by the parent class
|
||||
env_ = enriched_env_;
|
||||
}
|
||||
|
||||
protected:
|
||||
EnrichedSpecialEnv* enriched_env_;
|
||||
};
|
||||
|
||||
// Test that the recovery would successfully avoid the gaps between the logs.
|
||||
// One known scenario that could cause this is that the application issue the
|
||||
// WAL deletion out of order. For the sake of simplicity in the test, here we
|
||||
// create the gap by manipulating the env to skip deletion of the first WAL but
|
||||
// not the ones after it.
|
||||
TEST_F(DBWALTestWithEnrichedEnv, SkipDeletedWALs) {
|
||||
auto options = last_options_;
|
||||
// To cause frequent WAL deletion
|
||||
options.write_buffer_size = 128;
|
||||
Reopen(options);
|
||||
|
||||
WriteOptions writeOpt = WriteOptions();
|
||||
for (int i = 0; i < 128 * 5; i++) {
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, "foo", "v1"));
|
||||
}
|
||||
FlushOptions fo;
|
||||
fo.wait = true;
|
||||
ASSERT_OK(db_->Flush(fo));
|
||||
|
||||
// some wals are deleted
|
||||
ASSERT_NE(0, enriched_env_->deleted_wal_cnt);
|
||||
// but not the first one
|
||||
ASSERT_NE(0, enriched_env_->skipped_wal.size());
|
||||
|
||||
// Test that the WAL that was not deleted will be skipped during recovery
|
||||
options = last_options_;
|
||||
Reopen(options);
|
||||
ASSERT_FALSE(enriched_env_->deleted_wal_reopened);
|
||||
ASSERT_FALSE(enriched_env_->gap_in_wals);
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, WAL) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
@@ -891,7 +991,7 @@ TEST_F(DBWALTest, kPointInTimeRecoveryCFConsistency) {
|
||||
|
||||
// Record the offset at this point
|
||||
Env* env = options.env;
|
||||
int wal_file_id = RecoveryTestHelper::kWALFileOffset + 1;
|
||||
uint64_t wal_file_id = dbfull()->TEST_LogfileNumber();
|
||||
std::string fname = LogFileName(dbname_, wal_file_id);
|
||||
uint64_t offset_to_corrupt;
|
||||
ASSERT_OK(env->GetFileSize(fname, &offset_to_corrupt));
|
||||
|
||||
+61
-1
@@ -50,6 +50,7 @@ TEST_P(DBWriteTest, IOErrorOnWALWritePropagateToWriteThreadFollower) {
|
||||
std::atomic<int> leader_count{0};
|
||||
std::vector<port::Thread> threads;
|
||||
mock_env->SetFilesystemActive(false);
|
||||
|
||||
// Wait until all threads linked to write threads, to make sure
|
||||
// all threads join the same batch group.
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
@@ -68,7 +69,19 @@ TEST_P(DBWriteTest, IOErrorOnWALWritePropagateToWriteThreadFollower) {
|
||||
threads.push_back(port::Thread(
|
||||
[&](int index) {
|
||||
// All threads should fail.
|
||||
ASSERT_FALSE(Put("key" + ToString(index), "value").ok());
|
||||
auto res = Put("key" + ToString(index), "value");
|
||||
if (options.manual_wal_flush) {
|
||||
ASSERT_TRUE(res.ok());
|
||||
// we should see fs error when we do the flush
|
||||
|
||||
// TSAN reports a false alarm for lock-order-inversion but Open and
|
||||
// FlushWAL are not run concurrently. Disabling this until TSAN is
|
||||
// fixed.
|
||||
// res = dbfull()->FlushWAL(false);
|
||||
// ASSERT_FALSE(res.ok());
|
||||
} else {
|
||||
ASSERT_FALSE(res.ok());
|
||||
}
|
||||
},
|
||||
i));
|
||||
}
|
||||
@@ -80,6 +93,53 @@ TEST_P(DBWriteTest, IOErrorOnWALWritePropagateToWriteThreadFollower) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, ManualWalFlushInEffect) {
|
||||
Options options = GetOptions();
|
||||
Reopen(options);
|
||||
// try the 1st WAL created during open
|
||||
ASSERT_TRUE(Put("key" + ToString(0), "value").ok());
|
||||
ASSERT_TRUE(options.manual_wal_flush != dbfull()->TEST_WALBufferIsEmpty());
|
||||
ASSERT_TRUE(dbfull()->FlushWAL(false).ok());
|
||||
ASSERT_TRUE(dbfull()->TEST_WALBufferIsEmpty());
|
||||
// try the 2nd wal created during SwitchWAL
|
||||
dbfull()->TEST_SwitchWAL();
|
||||
ASSERT_TRUE(Put("key" + ToString(0), "value").ok());
|
||||
ASSERT_TRUE(options.manual_wal_flush != dbfull()->TEST_WALBufferIsEmpty());
|
||||
ASSERT_TRUE(dbfull()->FlushWAL(false).ok());
|
||||
ASSERT_TRUE(dbfull()->TEST_WALBufferIsEmpty());
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, IOErrorOnWALWriteTriggersReadOnlyMode) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
Options options = GetOptions();
|
||||
options.env = mock_env.get();
|
||||
Reopen(options);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Forcibly fail WAL write for the first Put only. Subsequent Puts should
|
||||
// fail due to read-only mode
|
||||
mock_env->SetFilesystemActive(i != 0);
|
||||
auto res = Put("key" + ToString(i), "value");
|
||||
// TSAN reports a false alarm for lock-order-inversion but Open and
|
||||
// FlushWAL are not run concurrently. Disabling this until TSAN is
|
||||
// fixed.
|
||||
/*
|
||||
if (options.manual_wal_flush && i == 0) {
|
||||
// even with manual_wal_flush the 2nd Put should return error because of
|
||||
// the read-only mode
|
||||
ASSERT_TRUE(res.ok());
|
||||
// we should see fs error when we do the flush
|
||||
res = dbfull()->FlushWAL(false);
|
||||
}
|
||||
*/
|
||||
if (!options.manual_wal_flush) {
|
||||
ASSERT_FALSE(res.ok());
|
||||
}
|
||||
}
|
||||
// Close before mock_env destruct.
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBWriteTestInstance, DBWriteTest,
|
||||
testing::Values(DBTestBase::kDefault,
|
||||
DBTestBase::kConcurrentWALWrites,
|
||||
|
||||
+2
-39
@@ -46,6 +46,8 @@ EntryType GetEntryType(ValueType value_type) {
|
||||
return kEntrySingleDelete;
|
||||
case kTypeMerge:
|
||||
return kEntryMerge;
|
||||
case kTypeRangeDeletion:
|
||||
return kEntryRangeDeletion;
|
||||
default:
|
||||
return kEntryOther;
|
||||
}
|
||||
@@ -106,45 +108,6 @@ const char* InternalKeyComparator::Name() const {
|
||||
return name_.c_str();
|
||||
}
|
||||
|
||||
int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const {
|
||||
// Order by:
|
||||
// increasing user key (according to user-supplied comparator)
|
||||
// decreasing sequence number
|
||||
// decreasing type (though sequence# should be enough to disambiguate)
|
||||
int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey));
|
||||
PERF_COUNTER_ADD(user_key_comparison_count, 1);
|
||||
if (r == 0) {
|
||||
const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8);
|
||||
const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8);
|
||||
if (anum > bnum) {
|
||||
r = -1;
|
||||
} else if (anum < bnum) {
|
||||
r = +1;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int InternalKeyComparator::CompareKeySeq(const Slice& akey,
|
||||
const Slice& bkey) const {
|
||||
// Order by:
|
||||
// increasing user key (according to user-supplied comparator)
|
||||
// decreasing sequence number
|
||||
int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey));
|
||||
PERF_COUNTER_ADD(user_key_comparison_count, 1);
|
||||
if (r == 0) {
|
||||
// Shift the number to exclude the last byte which contains the value type
|
||||
const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8) >> 8;
|
||||
const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8) >> 8;
|
||||
if (anum > bnum) {
|
||||
r = -1;
|
||||
} else if (anum < bnum) {
|
||||
r = +1;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int InternalKeyComparator::Compare(const ParsedInternalKey& a,
|
||||
const ParsedInternalKey& b) const {
|
||||
// Order by:
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
@@ -607,4 +608,46 @@ struct RangeTombstone {
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const {
|
||||
// Order by:
|
||||
// increasing user key (according to user-supplied comparator)
|
||||
// decreasing sequence number
|
||||
// decreasing type (though sequence# should be enough to disambiguate)
|
||||
int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey));
|
||||
PERF_COUNTER_ADD(user_key_comparison_count, 1);
|
||||
if (r == 0) {
|
||||
const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8);
|
||||
const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8);
|
||||
if (anum > bnum) {
|
||||
r = -1;
|
||||
} else if (anum < bnum) {
|
||||
r = +1;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline
|
||||
int InternalKeyComparator::CompareKeySeq(const Slice& akey,
|
||||
const Slice& bkey) const {
|
||||
// Order by:
|
||||
// increasing user key (according to user-supplied comparator)
|
||||
// decreasing sequence number
|
||||
int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey));
|
||||
PERF_COUNTER_ADD(user_key_comparison_count, 1);
|
||||
if (r == 0) {
|
||||
// Shift the number to exclude the last byte which contains the value type
|
||||
const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8) >> 8;
|
||||
const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8) >> 8;
|
||||
if (anum > bnum) {
|
||||
r = -1;
|
||||
} else if (anum < bnum) {
|
||||
r = +1;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -500,7 +500,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as DBImpl::DeleteFile is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
|
||||
+23
-8
@@ -8,7 +8,7 @@
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
template<class T>
|
||||
template <class T>
|
||||
inline T SafeDivide(T a, T b) {
|
||||
return b == 0 ? 0 : a / b;
|
||||
}
|
||||
@@ -17,7 +17,8 @@ inline T SafeDivide(T a, T b) {
|
||||
void EventHelpers::AppendCurrentTime(JSONWriter* jwriter) {
|
||||
*jwriter << "time_micros"
|
||||
<< std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -52,6 +53,11 @@ void EventHelpers::NotifyOnBackgroundError(
|
||||
listener->OnBackgroundError(reason, bg_error);
|
||||
}
|
||||
db_mutex->Lock();
|
||||
#else
|
||||
(void)listeners;
|
||||
(void)reason;
|
||||
(void)bg_error;
|
||||
(void)db_mutex;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -117,20 +123,25 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
for (auto& listener : listeners) {
|
||||
listener->OnTableFileCreated(info);
|
||||
}
|
||||
#else
|
||||
(void)listeners;
|
||||
(void)db_name;
|
||||
(void)cf_name;
|
||||
(void)file_path;
|
||||
(void)reason;
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void EventHelpers::LogAndNotifyTableFileDeletion(
|
||||
EventLogger* event_logger, int job_id,
|
||||
uint64_t file_number, const std::string& file_path,
|
||||
const Status& status, const std::string& dbname,
|
||||
EventLogger* event_logger, int job_id, uint64_t file_number,
|
||||
const std::string& file_path, const Status& status,
|
||||
const std::string& dbname,
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners) {
|
||||
|
||||
JSONWriter jwriter;
|
||||
AppendCurrentTime(&jwriter);
|
||||
|
||||
jwriter << "job" << job_id
|
||||
<< "event" << "table_file_deletion"
|
||||
jwriter << "job" << job_id << "event"
|
||||
<< "table_file_deletion"
|
||||
<< "file_number" << file_number;
|
||||
if (!status.ok()) {
|
||||
jwriter << "status" << status.ToString();
|
||||
@@ -149,6 +160,10 @@ void EventHelpers::LogAndNotifyTableFileDeletion(
|
||||
for (auto& listener : listeners) {
|
||||
listener->OnTableFileDeleted(info);
|
||||
}
|
||||
#else
|
||||
(void)file_path;
|
||||
(void)dbname;
|
||||
(void)listeners;
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -30,12 +30,13 @@ Status PromoteL0(DB* db, ColumnFamilyHandle* column_family, int target_level) {
|
||||
|
||||
#else // ROCKSDB_LITE
|
||||
|
||||
Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
Status SuggestCompactRange(DB* /*db*/, ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/, const Slice* /*end*/) {
|
||||
return Status::NotSupported("Not supported in RocksDB LITE");
|
||||
}
|
||||
|
||||
Status PromoteL0(DB* db, ColumnFamilyHandle* column_family, int target_level) {
|
||||
Status PromoteL0(DB* /*db*/, ColumnFamilyHandle* /*column_family*/,
|
||||
int /*target_level*/) {
|
||||
return Status::NotSupported("Not supported in RocksDB LITE");
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@
|
||||
namespace rocksdb {
|
||||
|
||||
Status ExternalSstFileIngestionJob::Prepare(
|
||||
const std::vector<std::string>& external_files_paths) {
|
||||
const std::vector<std::string>& external_files_paths, SuperVersion* sv) {
|
||||
Status status;
|
||||
|
||||
// Read the information of files we are ingesting
|
||||
for (const std::string& file_path : external_files_paths) {
|
||||
IngestedFileInfo file_to_ingest;
|
||||
status = GetIngestedFileInfo(file_path, &file_to_ingest);
|
||||
status = GetIngestedFileInfo(file_path, &file_to_ingest, sv);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
@@ -94,7 +94,8 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
|
||||
const std::string path_outside_db = f.external_file_path;
|
||||
const std::string path_inside_db =
|
||||
TableFileName(db_options_.db_paths, f.fd.GetNumber(), f.fd.GetPathId());
|
||||
TableFileName(cfd_->ioptions()->cf_paths, f.fd.GetNumber(),
|
||||
f.fd.GetPathId());
|
||||
|
||||
if (ingestion_options_.move_files) {
|
||||
status = env_->LinkFile(path_outside_db, path_inside_db);
|
||||
@@ -102,12 +103,16 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
// Original file is on a different FS, use copy instead of hard linking
|
||||
status = CopyFile(env_, path_outside_db, path_inside_db, 0,
|
||||
db_options_.use_fsync);
|
||||
f.copy_file = true;
|
||||
} else {
|
||||
f.copy_file = false;
|
||||
}
|
||||
} else {
|
||||
status = CopyFile(env_, path_outside_db, path_inside_db, 0,
|
||||
db_options_.use_fsync);
|
||||
f.copy_file = true;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::AddFile:FileCopied");
|
||||
TEST_SYNC_POINT("ExternalSstFileIngestionJob::Prepare:FileAdded");
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -117,7 +122,7 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
if (!status.ok()) {
|
||||
// We failed, remove all files that we copied into the db
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
if (f.internal_file_path == "") {
|
||||
if (f.internal_file_path.empty()) {
|
||||
break;
|
||||
}
|
||||
Status s = env_->DeleteFile(f.internal_file_path);
|
||||
@@ -217,9 +222,17 @@ void ExternalSstFileIngestionJob::UpdateStats() {
|
||||
uint64_t total_l0_files = 0;
|
||||
uint64_t total_time = env_->NowMicros() - job_start_time_;
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
InternalStats::CompactionStats stats(1);
|
||||
InternalStats::CompactionStats stats(CompactionReason::kExternalSstIngestion, 1);
|
||||
stats.micros = total_time;
|
||||
stats.bytes_written = f.fd.GetFileSize();
|
||||
// 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?
|
||||
if (f.copy_file) {
|
||||
stats.bytes_written = f.fd.GetFileSize();
|
||||
} else {
|
||||
stats.bytes_moved = f.fd.GetFileSize();
|
||||
}
|
||||
stats.num_output_files = 1;
|
||||
cfd_->internal_stats()->AddCompactionStats(f.picked_level, stats);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::BYTES_INGESTED_ADD_FILE,
|
||||
@@ -271,7 +284,8 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
const std::string& external_file, IngestedFileInfo* file_to_ingest) {
|
||||
const std::string& external_file, IngestedFileInfo* file_to_ingest,
|
||||
SuperVersion* sv) {
|
||||
file_to_ingest->external_file_path = external_file;
|
||||
|
||||
// Get external file size
|
||||
@@ -293,8 +307,9 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
external_file));
|
||||
|
||||
status = cfd_->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(*cfd_->ioptions(), env_options_,
|
||||
cfd_->internal_comparator()),
|
||||
TableReaderOptions(*cfd_->ioptions(),
|
||||
sv->mutable_cf_options.prefix_extractor.get(),
|
||||
env_options_, cfd_->internal_comparator()),
|
||||
std::move(sst_file_reader), file_to_ingest->file_size, &table_reader);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -350,7 +365,8 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
// We need to disable fill_cache so that we read from the file without
|
||||
// updating the block cache.
|
||||
ro.fill_cache = false;
|
||||
std::unique_ptr<InternalIterator> iter(table_reader->NewIterator(ro));
|
||||
std::unique_ptr<InternalIterator> iter(table_reader->NewIterator(
|
||||
ro, sv->mutable_cf_options.prefix_extractor.get()));
|
||||
|
||||
// Get first (smallest) key from file
|
||||
iter->SeekToFirst();
|
||||
@@ -510,6 +526,9 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
std::string seqno_val;
|
||||
PutFixed64(&seqno_val, seqno);
|
||||
status = rwfile->Write(file_to_ingest->global_seqno_offset, seqno_val);
|
||||
if (status.ok()) {
|
||||
status = rwfile->Fsync();
|
||||
}
|
||||
if (status.ok()) {
|
||||
file_to_ingest->assigned_seqno = seqno;
|
||||
}
|
||||
|
||||
@@ -46,11 +46,18 @@ struct IngestedFileInfo {
|
||||
// FileDescriptor for the file inside the DB
|
||||
FileDescriptor fd;
|
||||
// file path that we picked for file inside the DB
|
||||
std::string internal_file_path = "";
|
||||
std::string internal_file_path;
|
||||
// Global sequence number that we picked for the file inside the DB
|
||||
SequenceNumber assigned_seqno = 0;
|
||||
// Level inside the DB we picked for the external file.
|
||||
int picked_level = 0;
|
||||
// Whether to copy or link the external sst file. copy_file will be set to
|
||||
// false if ingestion_options.move_files is true and underlying FS
|
||||
// supports link operation. Need to provide a default value to make the
|
||||
// undefined-behavior sanity check of llvm happy. Since
|
||||
// ingestion_options.move_files is false by default, thus copy_file is true
|
||||
// by default.
|
||||
bool copy_file = true;
|
||||
|
||||
InternalKey smallest_internal_key() const {
|
||||
return InternalKey(smallest_user_key, assigned_seqno,
|
||||
@@ -79,7 +86,8 @@ class ExternalSstFileIngestionJob {
|
||||
job_start_time_(env_->NowMicros()) {}
|
||||
|
||||
// Prepare the job by copying external files into the DB.
|
||||
Status Prepare(const std::vector<std::string>& external_files_paths);
|
||||
Status Prepare(const std::vector<std::string>& external_files_paths,
|
||||
SuperVersion* sv);
|
||||
|
||||
// Check if we need to flush the memtable before running the ingestion job
|
||||
// This will be true if the files we are ingesting are overlapping with any
|
||||
@@ -112,7 +120,8 @@ class ExternalSstFileIngestionJob {
|
||||
// Open the external file and populate `file_to_ingest` with all the
|
||||
// external information we need to ingest this file.
|
||||
Status GetIngestedFileInfo(const std::string& external_file,
|
||||
IngestedFileInfo* file_to_ingest);
|
||||
IngestedFileInfo* file_to_ingest,
|
||||
SuperVersion* sv);
|
||||
|
||||
// Assign `file_to_ingest` the appropriate sequence number and the lowest
|
||||
// possible level that it can be ingested to according to compaction_style.
|
||||
|
||||
@@ -689,7 +689,7 @@ TEST_F(ExternalSSTFileTest, PurgeObsoleteFilesBug) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AddFile:FileCopied", [&](void* arg) {
|
||||
"ExternalSstFileIngestionJob::Prepare:FileAdded", [&](void* /* arg */) {
|
||||
ASSERT_OK(Put("aaa", "bbb"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("aaa", "xxx"));
|
||||
@@ -1128,7 +1128,7 @@ TEST_F(ExternalSSTFileTest, PickedLevelBug) {
|
||||
std::atomic<bool> bg_compact_started(false);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:Start",
|
||||
[&](void* arg) { bg_compact_started.store(true); });
|
||||
[&](void* /*arg*/) { bg_compact_started.store(true); });
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -1409,12 +1409,16 @@ TEST_F(ExternalSSTFileTest, AddFileTrivialMoveBug) {
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {22, 23}, 6)); // L2
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Start", [&](void* arg) {
|
||||
"CompactionJob::Run():Start", [&](void* /*arg*/) {
|
||||
// fit in L3 but will overlap with compaction so will be added
|
||||
// to L2 but a compaction will trivially move it to L3
|
||||
// and break LSM consistency
|
||||
ASSERT_OK(dbfull()->SetOptions({{"max_bytes_for_level_base", "1"}}));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {15, 16}, 7));
|
||||
static std::atomic<bool> called = {false};
|
||||
if (!called) {
|
||||
called = true;
|
||||
ASSERT_OK(dbfull()->SetOptions({{"max_bytes_for_level_base", "1"}}));
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {15, 16}, 7));
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -1797,6 +1801,56 @@ TEST_F(ExternalSSTFileTest, FileWithCFInfo) {
|
||||
ASSERT_OK(db_->IngestExternalFile(handles_[2], {unknown_sst}, ifo));
|
||||
}
|
||||
|
||||
/*
|
||||
* Test and verify the functionality of ingestion_options.move_files.
|
||||
*/
|
||||
TEST_F(ExternalSSTFileTest, LinkExternalSst) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
const int kNumKeys = 10000;
|
||||
|
||||
std::string file_path = sst_files_dir_ + "file1.sst";
|
||||
// Create SstFileWriter for default column family
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(i), Key(i) + "_value"));
|
||||
}
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
uint64_t file_size = 0;
|
||||
ASSERT_OK(env_->GetFileSize(file_path, &file_size));
|
||||
|
||||
IngestExternalFileOptions ifo;
|
||||
ifo.move_files = true;
|
||||
ASSERT_OK(db_->IngestExternalFile({file_path}, ifo));
|
||||
|
||||
ColumnFamilyHandleImpl* cfh =
|
||||
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily());
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
const InternalStats* internal_stats_ptr = cfd->internal_stats();
|
||||
const std::vector<InternalStats::CompactionStats>& comp_stats =
|
||||
internal_stats_ptr->TEST_GetCompactionStats();
|
||||
uint64_t bytes_copied = 0;
|
||||
uint64_t bytes_moved = 0;
|
||||
for (const auto& stats : comp_stats) {
|
||||
bytes_copied += stats.bytes_written;
|
||||
bytes_moved += stats.bytes_moved;
|
||||
}
|
||||
// If bytes_moved > 0, it means external sst resides on the same FS
|
||||
// supporting hard link operation. Therefore,
|
||||
// 0 bytes should be copied, and the bytes_moved == file_size.
|
||||
// Otherwise, FS does not support hard link, or external sst file resides on
|
||||
// a different file system, then the bytes_copied should be equal to
|
||||
// file_size.
|
||||
if (bytes_moved > 0) {
|
||||
ASSERT_EQ(0, bytes_copied);
|
||||
ASSERT_EQ(file_size, bytes_moved);
|
||||
} else {
|
||||
ASSERT_EQ(file_size, bytes_copied);
|
||||
}
|
||||
}
|
||||
|
||||
class TestIngestExternalFileListener : public EventListener {
|
||||
public:
|
||||
void OnExternalFileIngested(DB* /*db*/,
|
||||
@@ -2002,7 +2056,7 @@ int main(int argc, char** argv) {
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as External SST File Writer and Ingestion are not supported "
|
||||
"in ROCKSDB_LITE\n");
|
||||
|
||||
+35
-20
@@ -34,19 +34,22 @@ static const int kValueSize = 1000;
|
||||
static const int kMaxNumValues = 2000;
|
||||
static const size_t kNumIterations = 3;
|
||||
|
||||
class FaultInjectionTest : public testing::Test,
|
||||
public testing::WithParamInterface<bool> {
|
||||
enum FaultInjectionOptionConfig {
|
||||
kDefault,
|
||||
kDifferentDataDir,
|
||||
kWalDir,
|
||||
kSyncWal,
|
||||
kWalDirSyncWal,
|
||||
kMultiLevels,
|
||||
kEnd,
|
||||
};
|
||||
class FaultInjectionTest
|
||||
: public testing::Test,
|
||||
public testing::WithParamInterface<std::tuple<
|
||||
bool, FaultInjectionOptionConfig, FaultInjectionOptionConfig>> {
|
||||
protected:
|
||||
enum OptionConfig {
|
||||
kDefault,
|
||||
kDifferentDataDir,
|
||||
kWalDir,
|
||||
kSyncWal,
|
||||
kWalDirSyncWal,
|
||||
kMultiLevels,
|
||||
kEnd,
|
||||
};
|
||||
int option_config_;
|
||||
int non_inclusive_end_range_; // kEnd or equivalent to that
|
||||
// When need to make sure data is persistent, sync WAL
|
||||
bool sync_use_wal_;
|
||||
// When need to make sure data is persistent, call DB::CompactRange()
|
||||
@@ -72,13 +75,13 @@ class FaultInjectionTest : public testing::Test,
|
||||
DB* db_;
|
||||
|
||||
FaultInjectionTest()
|
||||
: option_config_(kDefault),
|
||||
: option_config_(std::get<1>(GetParam())),
|
||||
non_inclusive_end_range_(std::get<2>(GetParam())),
|
||||
sync_use_wal_(false),
|
||||
sync_use_compact_(true),
|
||||
base_env_(nullptr),
|
||||
env_(nullptr),
|
||||
db_(nullptr) {
|
||||
}
|
||||
db_(nullptr) {}
|
||||
|
||||
~FaultInjectionTest() {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -87,7 +90,7 @@ class FaultInjectionTest : public testing::Test,
|
||||
|
||||
bool ChangeOptions() {
|
||||
option_config_++;
|
||||
if (option_config_ >= kEnd) {
|
||||
if (option_config_ >= non_inclusive_end_range_) {
|
||||
return false;
|
||||
} else {
|
||||
if (option_config_ == kMultiLevels) {
|
||||
@@ -166,7 +169,7 @@ class FaultInjectionTest : public testing::Test,
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
sequential_order_ = GetParam();
|
||||
sequential_order_ = std::get<0>(GetParam());
|
||||
ASSERT_OK(NewDB());
|
||||
}
|
||||
|
||||
@@ -343,7 +346,9 @@ class FaultInjectionTest : public testing::Test,
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(FaultInjectionTest, FaultTest) {
|
||||
class FaultInjectionTestSplitted : public FaultInjectionTest {};
|
||||
|
||||
TEST_P(FaultInjectionTestSplitted, FaultTest) {
|
||||
do {
|
||||
Random rnd(301);
|
||||
|
||||
@@ -456,10 +461,10 @@ TEST_P(FaultInjectionTest, UninstalledCompaction) {
|
||||
|
||||
std::atomic<bool> opened(false);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::Open:Opened", [&](void* arg) { opened.store(true); });
|
||||
"DBImpl::Open:Opened", [&](void* /*arg*/) { opened.store(true); });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BGWorkCompaction",
|
||||
[&](void* arg) { ASSERT_TRUE(opened.load()); });
|
||||
[&](void* /*arg*/) { ASSERT_TRUE(opened.load()); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(OpenDB());
|
||||
ASSERT_OK(Verify(0, kNumKeys, FaultInjectionTest::kValExpectFound));
|
||||
@@ -530,7 +535,17 @@ TEST_P(FaultInjectionTest, WriteBatchWalTerminationTest) {
|
||||
ASSERT_EQ(db_->Get(ro, "boys", &val), Status::NotFound());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FaultTest, FaultInjectionTest, ::testing::Bool());
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FaultTest, FaultInjectionTest,
|
||||
::testing::Values(std::make_tuple(false, kDefault, kEnd),
|
||||
std::make_tuple(true, kDefault, kEnd)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FaultTest, FaultInjectionTestSplitted,
|
||||
::testing::Values(std::make_tuple(false, kDefault, kSyncWal),
|
||||
std::make_tuple(true, kDefault, kSyncWal),
|
||||
std::make_tuple(false, kSyncWal, kEnd),
|
||||
std::make_tuple(true, kSyncWal, kEnd)));
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
+12
-7
@@ -56,8 +56,8 @@ namespace rocksdb {
|
||||
|
||||
const char* GetFlushReasonString (FlushReason flush_reason) {
|
||||
switch (flush_reason) {
|
||||
case FlushReason::kUnknown:
|
||||
return "Unknown";
|
||||
case FlushReason::kOthers:
|
||||
return "Other Reasons";
|
||||
case FlushReason::kGetLiveFiles:
|
||||
return "Get Live Files";
|
||||
case FlushReason::kShutDown:
|
||||
@@ -72,8 +72,12 @@ const char* GetFlushReasonString (FlushReason flush_reason) {
|
||||
return "Write Buffer Full";
|
||||
case FlushReason::kTest:
|
||||
return "Test";
|
||||
case FlushReason::kSuperVersionChange:
|
||||
return "SuperVersion Change";
|
||||
case FlushReason::kDeleteFiles:
|
||||
return "Delete Files";
|
||||
case FlushReason::kAutoCompaction:
|
||||
return "Auto Compaction";
|
||||
case FlushReason::kManualFlush:
|
||||
return "Manual Flush";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
@@ -181,7 +185,8 @@ void FlushJob::PickMemTable() {
|
||||
base_->Ref(); // it is likely that we do not need this reference
|
||||
}
|
||||
|
||||
Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
FileMetaData* file_meta) {
|
||||
db_mutex_->AssertHeld();
|
||||
assert(pick_memtable_called);
|
||||
AutoThreadOperationStageUpdater stage_run(
|
||||
@@ -222,7 +227,7 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
TEST_SYNC_POINT("FlushJob::InstallResults");
|
||||
// Replace immutable memtable with the generated Table
|
||||
s = cfd_->imm()->InstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems_, versions_, db_mutex_,
|
||||
cfd_, mutable_cf_options_, mems_, prep_tracker, versions_, db_mutex_,
|
||||
meta_.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_);
|
||||
}
|
||||
@@ -388,7 +393,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
}
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(1);
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
stats.micros = db_options_.env->NowMicros() - start_micros;
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
MeasureTime(stats_, FLUSH_TIME, stats.micros);
|
||||
|
||||
+4
-1
@@ -22,6 +22,7 @@
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
#include "db/version_edit.h"
|
||||
@@ -42,6 +43,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBImpl;
|
||||
class MemTable;
|
||||
class SnapshotChecker;
|
||||
class TableCache;
|
||||
@@ -71,7 +73,8 @@ class FlushJob {
|
||||
// Require db_mutex held.
|
||||
// Once PickMemTable() is called, either Run() or Cancel() has to be called.
|
||||
void PickMemTable();
|
||||
Status Run(FileMetaData* file_meta = nullptr);
|
||||
Status Run(LogsWithPrepTracker* prep_tracker = nullptr,
|
||||
FileMetaData* file_meta = nullptr);
|
||||
void Cancel();
|
||||
TableProperties GetTableProperties() const { return table_properties_; }
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
FileMetaData fd;
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run(&fd));
|
||||
ASSERT_OK(flush_job.Run(nullptr, &fd));
|
||||
mutex_.Unlock();
|
||||
db_options_.statistics->histogramData(FLUSH_TIME, &hist);
|
||||
ASSERT_GT(hist.average, 0.0);
|
||||
|
||||
+50
-21
@@ -27,20 +27,22 @@ namespace rocksdb {
|
||||
// Usage:
|
||||
// ForwardLevelIterator iter;
|
||||
// iter.SetFileIndex(file_index);
|
||||
// iter.Seek(target);
|
||||
// iter.Seek(target); // or iter.SeekToFirst();
|
||||
// iter.Next()
|
||||
class ForwardLevelIterator : public InternalIterator {
|
||||
public:
|
||||
ForwardLevelIterator(const ColumnFamilyData* const cfd,
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files)
|
||||
const std::vector<FileMetaData*>& files,
|
||||
const SliceTransform* prefix_extractor)
|
||||
: cfd_(cfd),
|
||||
read_options_(read_options),
|
||||
files_(files),
|
||||
valid_(false),
|
||||
file_index_(std::numeric_limits<uint32_t>::max()),
|
||||
file_iter_(nullptr),
|
||||
pinned_iters_mgr_(nullptr) {}
|
||||
pinned_iters_mgr_(nullptr),
|
||||
prefix_extractor_(prefix_extractor) {}
|
||||
|
||||
~ForwardLevelIterator() {
|
||||
// Reset current pointer
|
||||
@@ -53,11 +55,11 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
|
||||
void SetFileIndex(uint32_t file_index) {
|
||||
assert(file_index < files_.size());
|
||||
status_ = Status::OK();
|
||||
if (file_index != file_index_) {
|
||||
file_index_ = file_index;
|
||||
Reset();
|
||||
}
|
||||
valid_ = false;
|
||||
}
|
||||
void Reset() {
|
||||
assert(file_index_ < files_.size());
|
||||
@@ -75,12 +77,12 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
read_options_, *(cfd_->soptions()), cfd_->internal_comparator(),
|
||||
files_[file_index_]->fd,
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
|
||||
nullptr /* table_reader_ptr */, nullptr, false);
|
||||
prefix_extractor_, nullptr /* table_reader_ptr */, nullptr, false);
|
||||
file_iter_->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
valid_ = false;
|
||||
if (!range_del_agg.IsEmpty()) {
|
||||
status_ = Status::NotSupported(
|
||||
"Range tombstones unsupported with ForwardIterator");
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
void SeekToLast() override {
|
||||
@@ -95,12 +97,27 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
return valid_;
|
||||
}
|
||||
void SeekToFirst() override {
|
||||
SetFileIndex(0);
|
||||
assert(file_iter_ != nullptr);
|
||||
if (!status_.ok()) {
|
||||
assert(!valid_);
|
||||
return;
|
||||
}
|
||||
file_iter_->SeekToFirst();
|
||||
valid_ = file_iter_->Valid();
|
||||
}
|
||||
void Seek(const Slice& internal_key) override {
|
||||
assert(file_iter_ != nullptr);
|
||||
|
||||
// This deviates from the usual convention for InternalIterator::Seek() in
|
||||
// that it doesn't discard pre-existing error status. That's because this
|
||||
// Seek() is only supposed to be called immediately after SetFileIndex()
|
||||
// (which discards pre-existing error status), and SetFileIndex() may set
|
||||
// an error status, which we shouldn't discard.
|
||||
if (!status_.ok()) {
|
||||
assert(!valid_);
|
||||
return;
|
||||
}
|
||||
|
||||
file_iter_->Seek(internal_key);
|
||||
valid_ = file_iter_->Valid();
|
||||
}
|
||||
@@ -112,8 +129,12 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
assert(valid_);
|
||||
file_iter_->Next();
|
||||
for (;;) {
|
||||
if (file_iter_->status().IsIncomplete() || file_iter_->Valid()) {
|
||||
valid_ = !file_iter_->status().IsIncomplete();
|
||||
valid_ = file_iter_->Valid();
|
||||
if (!file_iter_->status().ok()) {
|
||||
assert(!valid_);
|
||||
return;
|
||||
}
|
||||
if (valid_) {
|
||||
return;
|
||||
}
|
||||
if (file_index_ + 1 >= files_.size()) {
|
||||
@@ -121,6 +142,10 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
return;
|
||||
}
|
||||
SetFileIndex(file_index_ + 1);
|
||||
if (!status_.ok()) {
|
||||
assert(!valid_);
|
||||
return;
|
||||
}
|
||||
file_iter_->SeekToFirst();
|
||||
}
|
||||
}
|
||||
@@ -135,7 +160,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
Status status() const override {
|
||||
if (!status_.ok()) {
|
||||
return status_;
|
||||
} else if (file_iter_ && !file_iter_->status().ok()) {
|
||||
} else if (file_iter_) {
|
||||
return file_iter_->status();
|
||||
}
|
||||
return Status::OK();
|
||||
@@ -165,6 +190,7 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
Status status_;
|
||||
InternalIterator* file_iter_;
|
||||
PinnedIteratorsManager* pinned_iters_mgr_;
|
||||
const SliceTransform* prefix_extractor_;
|
||||
};
|
||||
|
||||
ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
@@ -173,7 +199,7 @@ ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
: db_(db),
|
||||
read_options_(read_options),
|
||||
cfd_(cfd),
|
||||
prefix_extractor_(cfd->ioptions()->prefix_extractor),
|
||||
prefix_extractor_(current_sv->mutable_cf_options.prefix_extractor.get()),
|
||||
user_comparator_(cfd->user_comparator()),
|
||||
immutable_min_heap_(MinIterComparator(&cfd_->internal_comparator())),
|
||||
sv_(current_sv),
|
||||
@@ -299,9 +325,6 @@ bool ForwardIterator::IsOverUpperBound(const Slice& internal_key) const {
|
||||
}
|
||||
|
||||
void ForwardIterator::Seek(const Slice& internal_key) {
|
||||
if (IsOverUpperBound(internal_key)) {
|
||||
valid_ = false;
|
||||
}
|
||||
if (sv_ == nullptr) {
|
||||
RebuildIterators(true);
|
||||
} else if (sv_->version_number != cfd_->GetSuperVersionNumber()) {
|
||||
@@ -605,13 +628,16 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
if ((read_options_.iterate_upper_bound != nullptr) &&
|
||||
cfd_->internal_comparator().user_comparator()->Compare(
|
||||
l0->smallest.user_key(), *read_options_.iterate_upper_bound) > 0) {
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
// No need to set has_iter_trimmed_for_upper_bound_: this ForwardIterator
|
||||
// will never be interested in files with smallest key above
|
||||
// iterate_upper_bound, since iterate_upper_bound can't be changed.
|
||||
l0_iters_.push_back(nullptr);
|
||||
continue;
|
||||
}
|
||||
l0_iters_.push_back(cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(), l0->fd,
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg));
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
|
||||
sv_->mutable_cf_options.prefix_extractor.get()));
|
||||
}
|
||||
BuildLevelIterators(vstorage);
|
||||
current_ = nullptr;
|
||||
@@ -681,7 +707,8 @@ void ForwardIterator::RenewIterators() {
|
||||
l0_iters_new.push_back(cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
|
||||
l0_files_new[inew]->fd,
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg));
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
|
||||
svnew->mutable_cf_options.prefix_extractor.get()));
|
||||
}
|
||||
|
||||
for (auto* f : l0_iters_) {
|
||||
@@ -722,8 +749,9 @@ void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage) {
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
}
|
||||
} else {
|
||||
level_iters_.push_back(
|
||||
new ForwardLevelIterator(cfd_, read_options_, level_files));
|
||||
level_iters_.push_back(new ForwardLevelIterator(
|
||||
cfd_, read_options_, level_files,
|
||||
sv_->mutable_cf_options.prefix_extractor.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -738,7 +766,8 @@ void ForwardIterator::ResetIncompleteIterators() {
|
||||
DeleteIterator(l0_iters_[i]);
|
||||
l0_iters_[i] = cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
|
||||
l0_files[i]->fd, nullptr /* range_del_agg */);
|
||||
l0_files[i]->fd, nullptr /* range_del_agg */,
|
||||
sv_->mutable_cf_options.prefix_extractor.get());
|
||||
l0_iters_[i]->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
@@ -773,7 +802,7 @@ void ForwardIterator::UpdateCurrent() {
|
||||
current_ = mutable_iter_;
|
||||
}
|
||||
}
|
||||
valid_ = (current_ != nullptr);
|
||||
valid_ = current_ != nullptr && immutable_status_.ok();
|
||||
if (!status_.ok()) {
|
||||
status_ = Status::OK();
|
||||
}
|
||||
|
||||
+182
-115
@@ -18,9 +18,10 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -86,8 +87,7 @@ void PrepareLevelStats(std::map<LevelStatType, double>* level_stats,
|
||||
const InternalStats::CompactionStats& stats) {
|
||||
uint64_t bytes_read =
|
||||
stats.bytes_read_non_output_levels + stats.bytes_read_output_level;
|
||||
int64_t bytes_new =
|
||||
stats.bytes_written - stats.bytes_read_output_level;
|
||||
int64_t bytes_new = stats.bytes_written - stats.bytes_read_output_level;
|
||||
double elapsed = (stats.micros + 1) / kMicrosInSec;
|
||||
|
||||
(*level_stats)[LevelStatType::NUM_FILES] = num_files;
|
||||
@@ -117,50 +117,50 @@ void PrepareLevelStats(std::map<LevelStatType, double>* level_stats,
|
||||
|
||||
void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
const std::map<LevelStatType, double>& stat_value) {
|
||||
snprintf(buf, len,
|
||||
"%4s " /* Level */
|
||||
"%6d/%-3d " /* Files */
|
||||
"%8s " /* Size */
|
||||
"%5.1f " /* Score */
|
||||
"%8.1f " /* Read(GB) */
|
||||
"%7.1f " /* Rn(GB) */
|
||||
"%8.1f " /* Rnp1(GB) */
|
||||
"%9.1f " /* Write(GB) */
|
||||
"%8.1f " /* Wnew(GB) */
|
||||
"%9.1f " /* Moved(GB) */
|
||||
"%5.1f " /* W-Amp */
|
||||
"%8.1f " /* Rd(MB/s) */
|
||||
"%8.1f " /* Wr(MB/s) */
|
||||
"%9.0f " /* Comp(sec) */
|
||||
"%9d " /* Comp(cnt) */
|
||||
"%8.3f " /* Avg(sec) */
|
||||
"%7s " /* KeyIn */
|
||||
"%6s\n", /* KeyDrop */
|
||||
name.c_str(),
|
||||
static_cast<int>(stat_value.at(LevelStatType::NUM_FILES)),
|
||||
static_cast<int>(stat_value.at(LevelStatType::COMPACTED_FILES)),
|
||||
BytesToHumanString(
|
||||
static_cast<uint64_t>(stat_value.at(LevelStatType::SIZE_BYTES)))
|
||||
.c_str(),
|
||||
stat_value.at(LevelStatType::SCORE),
|
||||
stat_value.at(LevelStatType::READ_GB),
|
||||
stat_value.at(LevelStatType::RN_GB),
|
||||
stat_value.at(LevelStatType::RNP1_GB),
|
||||
stat_value.at(LevelStatType::WRITE_GB),
|
||||
stat_value.at(LevelStatType::W_NEW_GB),
|
||||
stat_value.at(LevelStatType::MOVED_GB),
|
||||
stat_value.at(LevelStatType::WRITE_AMP),
|
||||
stat_value.at(LevelStatType::READ_MBPS),
|
||||
stat_value.at(LevelStatType::WRITE_MBPS),
|
||||
stat_value.at(LevelStatType::COMP_SEC),
|
||||
static_cast<int>(stat_value.at(LevelStatType::COMP_COUNT)),
|
||||
stat_value.at(LevelStatType::AVG_SEC),
|
||||
NumberToHumanString(
|
||||
static_cast<std::int64_t>(stat_value.at(LevelStatType::KEY_IN)))
|
||||
.c_str(),
|
||||
NumberToHumanString(static_cast<std::int64_t>(
|
||||
stat_value.at(LevelStatType::KEY_DROP)))
|
||||
.c_str());
|
||||
snprintf(
|
||||
buf, len,
|
||||
"%4s " /* Level */
|
||||
"%6d/%-3d " /* Files */
|
||||
"%8s " /* Size */
|
||||
"%5.1f " /* Score */
|
||||
"%8.1f " /* Read(GB) */
|
||||
"%7.1f " /* Rn(GB) */
|
||||
"%8.1f " /* Rnp1(GB) */
|
||||
"%9.1f " /* Write(GB) */
|
||||
"%8.1f " /* Wnew(GB) */
|
||||
"%9.1f " /* Moved(GB) */
|
||||
"%5.1f " /* W-Amp */
|
||||
"%8.1f " /* Rd(MB/s) */
|
||||
"%8.1f " /* Wr(MB/s) */
|
||||
"%9.0f " /* Comp(sec) */
|
||||
"%9d " /* Comp(cnt) */
|
||||
"%8.3f " /* Avg(sec) */
|
||||
"%7s " /* KeyIn */
|
||||
"%6s\n", /* KeyDrop */
|
||||
name.c_str(), static_cast<int>(stat_value.at(LevelStatType::NUM_FILES)),
|
||||
static_cast<int>(stat_value.at(LevelStatType::COMPACTED_FILES)),
|
||||
BytesToHumanString(
|
||||
static_cast<uint64_t>(stat_value.at(LevelStatType::SIZE_BYTES)))
|
||||
.c_str(),
|
||||
stat_value.at(LevelStatType::SCORE),
|
||||
stat_value.at(LevelStatType::READ_GB),
|
||||
stat_value.at(LevelStatType::RN_GB),
|
||||
stat_value.at(LevelStatType::RNP1_GB),
|
||||
stat_value.at(LevelStatType::WRITE_GB),
|
||||
stat_value.at(LevelStatType::W_NEW_GB),
|
||||
stat_value.at(LevelStatType::MOVED_GB),
|
||||
stat_value.at(LevelStatType::WRITE_AMP),
|
||||
stat_value.at(LevelStatType::READ_MBPS),
|
||||
stat_value.at(LevelStatType::WRITE_MBPS),
|
||||
stat_value.at(LevelStatType::COMP_SEC),
|
||||
static_cast<int>(stat_value.at(LevelStatType::COMP_COUNT)),
|
||||
stat_value.at(LevelStatType::AVG_SEC),
|
||||
NumberToHumanString(
|
||||
static_cast<std::int64_t>(stat_value.at(LevelStatType::KEY_IN)))
|
||||
.c_str(),
|
||||
NumberToHumanString(
|
||||
static_cast<std::int64_t>(stat_value.at(LevelStatType::KEY_DROP)))
|
||||
.c_str());
|
||||
}
|
||||
|
||||
void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
@@ -208,30 +208,30 @@ static const std::string mem_table_flush_pending = "mem-table-flush-pending";
|
||||
static const std::string compaction_pending = "compaction-pending";
|
||||
static const std::string background_errors = "background-errors";
|
||||
static const std::string cur_size_active_mem_table =
|
||||
"cur-size-active-mem-table";
|
||||
"cur-size-active-mem-table";
|
||||
static const std::string cur_size_all_mem_tables = "cur-size-all-mem-tables";
|
||||
static const std::string size_all_mem_tables = "size-all-mem-tables";
|
||||
static const std::string num_entries_active_mem_table =
|
||||
"num-entries-active-mem-table";
|
||||
"num-entries-active-mem-table";
|
||||
static const std::string num_entries_imm_mem_tables =
|
||||
"num-entries-imm-mem-tables";
|
||||
"num-entries-imm-mem-tables";
|
||||
static const std::string num_deletes_active_mem_table =
|
||||
"num-deletes-active-mem-table";
|
||||
"num-deletes-active-mem-table";
|
||||
static const std::string num_deletes_imm_mem_tables =
|
||||
"num-deletes-imm-mem-tables";
|
||||
"num-deletes-imm-mem-tables";
|
||||
static const std::string estimate_num_keys = "estimate-num-keys";
|
||||
static const std::string estimate_table_readers_mem =
|
||||
"estimate-table-readers-mem";
|
||||
"estimate-table-readers-mem";
|
||||
static const std::string is_file_deletions_enabled =
|
||||
"is-file-deletions-enabled";
|
||||
"is-file-deletions-enabled";
|
||||
static const std::string num_snapshots = "num-snapshots";
|
||||
static const std::string oldest_snapshot_time = "oldest-snapshot-time";
|
||||
static const std::string num_live_versions = "num-live-versions";
|
||||
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 = "min-log-number-to-keep";
|
||||
static const std::string base_level = "base-level";
|
||||
static const std::string min_log_number_to_keep_str = "min-log-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";
|
||||
static const std::string estimate_pending_comp_bytes =
|
||||
@@ -246,11 +246,14 @@ static const std::string actual_delayed_write_rate =
|
||||
"actual-delayed-write-rate";
|
||||
static const std::string is_write_stopped = "is-write-stopped";
|
||||
static const std::string estimate_oldest_key_time = "estimate-oldest-key-time";
|
||||
static const std::string block_cache_capacity = "block-cache-capacity";
|
||||
static const std::string block_cache_usage = "block-cache-usage";
|
||||
static const std::string block_cache_pinned_usage = "block-cache-pinned-usage";
|
||||
|
||||
const std::string DB::Properties::kNumFilesAtLevelPrefix =
|
||||
rocksdb_prefix + num_files_at_level_prefix;
|
||||
rocksdb_prefix + num_files_at_level_prefix;
|
||||
const std::string DB::Properties::kCompressionRatioAtLevelPrefix =
|
||||
rocksdb_prefix + compression_ratio_at_level_prefix;
|
||||
rocksdb_prefix + compression_ratio_at_level_prefix;
|
||||
const std::string DB::Properties::kStats = rocksdb_prefix + allstats;
|
||||
const std::string DB::Properties::kSSTables = rocksdb_prefix + sstables;
|
||||
const std::string DB::Properties::kCFStats = rocksdb_prefix + cfstats;
|
||||
@@ -261,56 +264,56 @@ const std::string DB::Properties::kCFFileHistogram =
|
||||
const std::string DB::Properties::kDBStats = rocksdb_prefix + dbstats;
|
||||
const std::string DB::Properties::kLevelStats = rocksdb_prefix + levelstats;
|
||||
const std::string DB::Properties::kNumImmutableMemTable =
|
||||
rocksdb_prefix + num_immutable_mem_table;
|
||||
rocksdb_prefix + num_immutable_mem_table;
|
||||
const std::string DB::Properties::kNumImmutableMemTableFlushed =
|
||||
rocksdb_prefix + num_immutable_mem_table_flushed;
|
||||
const std::string DB::Properties::kMemTableFlushPending =
|
||||
rocksdb_prefix + mem_table_flush_pending;
|
||||
rocksdb_prefix + mem_table_flush_pending;
|
||||
const std::string DB::Properties::kCompactionPending =
|
||||
rocksdb_prefix + compaction_pending;
|
||||
rocksdb_prefix + compaction_pending;
|
||||
const std::string DB::Properties::kNumRunningCompactions =
|
||||
rocksdb_prefix + num_running_compactions;
|
||||
const std::string DB::Properties::kNumRunningFlushes =
|
||||
rocksdb_prefix + num_running_flushes;
|
||||
const std::string DB::Properties::kBackgroundErrors =
|
||||
rocksdb_prefix + background_errors;
|
||||
rocksdb_prefix + background_errors;
|
||||
const std::string DB::Properties::kCurSizeActiveMemTable =
|
||||
rocksdb_prefix + cur_size_active_mem_table;
|
||||
rocksdb_prefix + cur_size_active_mem_table;
|
||||
const std::string DB::Properties::kCurSizeAllMemTables =
|
||||
rocksdb_prefix + cur_size_all_mem_tables;
|
||||
const std::string DB::Properties::kSizeAllMemTables =
|
||||
rocksdb_prefix + size_all_mem_tables;
|
||||
const std::string DB::Properties::kNumEntriesActiveMemTable =
|
||||
rocksdb_prefix + num_entries_active_mem_table;
|
||||
rocksdb_prefix + num_entries_active_mem_table;
|
||||
const std::string DB::Properties::kNumEntriesImmMemTables =
|
||||
rocksdb_prefix + num_entries_imm_mem_tables;
|
||||
rocksdb_prefix + num_entries_imm_mem_tables;
|
||||
const std::string DB::Properties::kNumDeletesActiveMemTable =
|
||||
rocksdb_prefix + num_deletes_active_mem_table;
|
||||
rocksdb_prefix + num_deletes_active_mem_table;
|
||||
const std::string DB::Properties::kNumDeletesImmMemTables =
|
||||
rocksdb_prefix + num_deletes_imm_mem_tables;
|
||||
rocksdb_prefix + num_deletes_imm_mem_tables;
|
||||
const std::string DB::Properties::kEstimateNumKeys =
|
||||
rocksdb_prefix + estimate_num_keys;
|
||||
rocksdb_prefix + estimate_num_keys;
|
||||
const std::string DB::Properties::kEstimateTableReadersMem =
|
||||
rocksdb_prefix + estimate_table_readers_mem;
|
||||
rocksdb_prefix + estimate_table_readers_mem;
|
||||
const std::string DB::Properties::kIsFileDeletionsEnabled =
|
||||
rocksdb_prefix + is_file_deletions_enabled;
|
||||
rocksdb_prefix + is_file_deletions_enabled;
|
||||
const std::string DB::Properties::kNumSnapshots =
|
||||
rocksdb_prefix + num_snapshots;
|
||||
rocksdb_prefix + num_snapshots;
|
||||
const std::string DB::Properties::kOldestSnapshotTime =
|
||||
rocksdb_prefix + oldest_snapshot_time;
|
||||
rocksdb_prefix + oldest_snapshot_time;
|
||||
const std::string DB::Properties::kNumLiveVersions =
|
||||
rocksdb_prefix + num_live_versions;
|
||||
rocksdb_prefix + num_live_versions;
|
||||
const std::string DB::Properties::kCurrentSuperVersionNumber =
|
||||
rocksdb_prefix + current_version_number;
|
||||
const std::string DB::Properties::kEstimateLiveDataSize =
|
||||
rocksdb_prefix + estimate_live_data_size;
|
||||
rocksdb_prefix + estimate_live_data_size;
|
||||
const std::string DB::Properties::kMinLogNumberToKeep =
|
||||
rocksdb_prefix + min_log_number_to_keep;
|
||||
rocksdb_prefix + min_log_number_to_keep_str;
|
||||
const std::string DB::Properties::kTotalSstFilesSize =
|
||||
rocksdb_prefix + total_sst_files_size;
|
||||
rocksdb_prefix + total_sst_files_size;
|
||||
const std::string DB::Properties::kLiveSstFilesSize =
|
||||
rocksdb_prefix + live_sst_files_size;
|
||||
const std::string DB::Properties::kBaseLevel = rocksdb_prefix + base_level;
|
||||
const std::string DB::Properties::kBaseLevel = rocksdb_prefix + base_level_str;
|
||||
const std::string DB::Properties::kEstimatePendingCompactionBytes =
|
||||
rocksdb_prefix + estimate_pending_comp_bytes;
|
||||
const std::string DB::Properties::kAggregatedTableProperties =
|
||||
@@ -323,6 +326,12 @@ const std::string DB::Properties::kIsWriteStopped =
|
||||
rocksdb_prefix + is_write_stopped;
|
||||
const std::string DB::Properties::kEstimateOldestKeyTime =
|
||||
rocksdb_prefix + estimate_oldest_key_time;
|
||||
const std::string DB::Properties::kBlockCacheCapacity =
|
||||
rocksdb_prefix + block_cache_capacity;
|
||||
const std::string DB::Properties::kBlockCacheUsage =
|
||||
rocksdb_prefix + block_cache_usage;
|
||||
const std::string DB::Properties::kBlockCachePinnedUsage =
|
||||
rocksdb_prefix + block_cache_pinned_usage;
|
||||
|
||||
const std::unordered_map<std::string, DBPropertyInfo>
|
||||
InternalStats::ppt_name_to_info = {
|
||||
@@ -426,6 +435,13 @@ const std::unordered_map<std::string, DBPropertyInfo>
|
||||
{DB::Properties::kEstimateOldestKeyTime,
|
||||
{false, nullptr, &InternalStats::HandleEstimateOldestKeyTime,
|
||||
nullptr}},
|
||||
{DB::Properties::kBlockCacheCapacity,
|
||||
{false, nullptr, &InternalStats::HandleBlockCacheCapacity, nullptr}},
|
||||
{DB::Properties::kBlockCacheUsage,
|
||||
{false, nullptr, &InternalStats::HandleBlockCacheUsage, nullptr}},
|
||||
{DB::Properties::kBlockCachePinnedUsage,
|
||||
{false, nullptr, &InternalStats::HandleBlockCachePinnedUsage,
|
||||
nullptr}},
|
||||
};
|
||||
|
||||
const DBPropertyInfo* GetPropertyInfo(const Slice& property) {
|
||||
@@ -772,8 +788,8 @@ bool InternalStats::HandleEstimateTableReadersMem(uint64_t* value,
|
||||
}
|
||||
|
||||
bool InternalStats::HandleEstimateLiveDataSize(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
Version* version) {
|
||||
const auto* vstorage = version->storage_info();
|
||||
*value = vstorage->EstimateLiveDataSize();
|
||||
return true;
|
||||
}
|
||||
@@ -831,6 +847,58 @@ bool InternalStats::HandleEstimateOldestKeyTime(uint64_t* value, DBImpl* /*db*/,
|
||||
return *value > 0 && *value < std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
bool InternalStats::HandleBlockCacheStat(Cache** block_cache) {
|
||||
assert(block_cache != nullptr);
|
||||
auto* table_factory = cfd_->ioptions()->table_factory;
|
||||
assert(table_factory != nullptr);
|
||||
if (BlockBasedTableFactory::kName != table_factory->Name()) {
|
||||
return false;
|
||||
}
|
||||
auto* table_options =
|
||||
reinterpret_cast<BlockBasedTableOptions*>(table_factory->GetOptions());
|
||||
if (table_options == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*block_cache = table_options->block_cache.get();
|
||||
if (table_options->no_block_cache || *block_cache == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleBlockCacheCapacity(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
Cache* block_cache;
|
||||
bool ok = HandleBlockCacheStat(&block_cache);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
*value = static_cast<uint64_t>(block_cache->GetCapacity());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleBlockCacheUsage(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
Cache* block_cache;
|
||||
bool ok = HandleBlockCacheStat(&block_cache);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
*value = static_cast<uint64_t>(block_cache->GetUsage());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleBlockCachePinnedUsage(uint64_t* value, DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
Cache* block_cache;
|
||||
bool ok = HandleBlockCacheStat(&block_cache);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
*value = static_cast<uint64_t>(block_cache->GetPinnedUsage());
|
||||
return true;
|
||||
}
|
||||
|
||||
void InternalStats::DumpDBStats(std::string* value) {
|
||||
char buf[1000];
|
||||
// DB-level stats, only available from default column family
|
||||
@@ -882,8 +950,7 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
value->append(buf);
|
||||
// Stall
|
||||
AppendHumanMicros(write_stall_micros, human_micros, kHumanMicrosLen, true);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative stall: %s, %.1f percent\n",
|
||||
snprintf(buf, sizeof(buf), "Cumulative stall: %s, %.1f percent\n",
|
||||
human_micros,
|
||||
// 10000 = divide by 1M to get secs, then multiply by 100 for pct
|
||||
write_stall_micros / 10000.0 / std::max(seconds_up, 0.001));
|
||||
@@ -894,43 +961,40 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
uint64_t interval_write_self = write_self - db_stats_snapshot_.write_self;
|
||||
uint64_t interval_num_keys_written =
|
||||
num_keys_written - db_stats_snapshot_.num_keys_written;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval writes: %s writes, %s keys, %s commit groups, "
|
||||
"%.1f writes per commit group, ingest: %.2f MB, %.2f MB/s\n",
|
||||
NumberToHumanString(
|
||||
interval_write_other + interval_write_self).c_str(),
|
||||
NumberToHumanString(interval_num_keys_written).c_str(),
|
||||
NumberToHumanString(interval_write_self).c_str(),
|
||||
static_cast<double>(interval_write_other + interval_write_self) /
|
||||
(interval_write_self + 1),
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB,
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB /
|
||||
std::max(interval_seconds_up, 0.001)),
|
||||
value->append(buf);
|
||||
snprintf(
|
||||
buf, sizeof(buf),
|
||||
"Interval writes: %s writes, %s keys, %s commit groups, "
|
||||
"%.1f writes per commit group, ingest: %.2f MB, %.2f MB/s\n",
|
||||
NumberToHumanString(interval_write_other + interval_write_self).c_str(),
|
||||
NumberToHumanString(interval_num_keys_written).c_str(),
|
||||
NumberToHumanString(interval_write_self).c_str(),
|
||||
static_cast<double>(interval_write_other + interval_write_self) /
|
||||
(interval_write_self + 1),
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB,
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB /
|
||||
std::max(interval_seconds_up, 0.001)),
|
||||
value->append(buf);
|
||||
|
||||
uint64_t interval_write_with_wal =
|
||||
write_with_wal - db_stats_snapshot_.write_with_wal;
|
||||
uint64_t interval_wal_synced = wal_synced - db_stats_snapshot_.wal_synced;
|
||||
uint64_t interval_wal_bytes = wal_bytes - db_stats_snapshot_.wal_bytes;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval WAL: %s writes, %s syncs, "
|
||||
"%.2f writes per sync, written: %.2f MB, %.2f MB/s\n",
|
||||
NumberToHumanString(interval_write_with_wal).c_str(),
|
||||
NumberToHumanString(interval_wal_synced).c_str(),
|
||||
interval_write_with_wal /
|
||||
static_cast<double>(interval_wal_synced + 1),
|
||||
interval_wal_bytes / kGB,
|
||||
interval_wal_bytes / kMB / std::max(interval_seconds_up, 0.001));
|
||||
snprintf(
|
||||
buf, sizeof(buf),
|
||||
"Interval WAL: %s writes, %s syncs, "
|
||||
"%.2f writes per sync, written: %.2f MB, %.2f MB/s\n",
|
||||
NumberToHumanString(interval_write_with_wal).c_str(),
|
||||
NumberToHumanString(interval_wal_synced).c_str(),
|
||||
interval_write_with_wal / static_cast<double>(interval_wal_synced + 1),
|
||||
interval_wal_bytes / kGB,
|
||||
interval_wal_bytes / kMB / std::max(interval_seconds_up, 0.001));
|
||||
value->append(buf);
|
||||
|
||||
// Stall
|
||||
AppendHumanMicros(
|
||||
write_stall_micros - db_stats_snapshot_.write_stall_micros,
|
||||
human_micros, kHumanMicrosLen, true);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval stall: %s, %.1f percent\n",
|
||||
human_micros,
|
||||
AppendHumanMicros(write_stall_micros - db_stats_snapshot_.write_stall_micros,
|
||||
human_micros, kHumanMicrosLen, true);
|
||||
snprintf(buf, sizeof(buf), "Interval stall: %s, %.1f percent\n", human_micros,
|
||||
// 10000 = divide by 1M to get secs, then multiply by 100 for pct
|
||||
(write_stall_micros - db_stats_snapshot_.write_stall_micros) /
|
||||
10000.0 / std::max(interval_seconds_up, 0.001));
|
||||
@@ -956,8 +1020,8 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
* and values represent uint64 encoded as strings.
|
||||
*/
|
||||
void InternalStats::DumpCFMapStats(
|
||||
std::map<std::string, std::string>* cf_stats) {
|
||||
CompactionStats compaction_stats_sum(0);
|
||||
std::map<std::string, std::string>* cf_stats) {
|
||||
CompactionStats compaction_stats_sum;
|
||||
std::map<int, std::map<LevelStatType, double>> levels_stats;
|
||||
DumpCFMapStats(&levels_stats, &compaction_stats_sum);
|
||||
for (auto const& level_ent : levels_stats) {
|
||||
@@ -1088,7 +1152,7 @@ void InternalStats::DumpCFStatsNoFileHistogram(std::string* value) {
|
||||
|
||||
// Print stats for each level
|
||||
std::map<int, std::map<LevelStatType, double>> levels_stats;
|
||||
CompactionStats compaction_stats_sum(0);
|
||||
CompactionStats compaction_stats_sum;
|
||||
DumpCFMapStats(&levels_stats, &compaction_stats_sum);
|
||||
for (int l = 0; l < number_levels_; ++l) {
|
||||
if (levels_stats.find(l) != levels_stats.end()) {
|
||||
@@ -1143,8 +1207,9 @@ void InternalStats::DumpCFStatsNoFileHistogram(std::string* value) {
|
||||
|
||||
uint64_t interval_ingest_files_addfile =
|
||||
ingest_files_addfile - cf_stats_snapshot_.ingest_files_addfile;
|
||||
snprintf(buf, sizeof(buf), "AddFile(Total Files): cumulative %" PRIu64
|
||||
", interval %" PRIu64 "\n",
|
||||
snprintf(buf, sizeof(buf),
|
||||
"AddFile(Total Files): cumulative %" PRIu64 ", interval %" PRIu64
|
||||
"\n",
|
||||
ingest_files_addfile, interval_ingest_files_addfile);
|
||||
value->append(buf);
|
||||
|
||||
@@ -1262,7 +1327,9 @@ void InternalStats::DumpCFFileHistogram(std::string* value) {
|
||||
|
||||
#else
|
||||
|
||||
const DBPropertyInfo* GetPropertyInfo(const Slice& property) { return nullptr; }
|
||||
const DBPropertyInfo* GetPropertyInfo(const Slice& /*property*/) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
|
||||
+86
-26
@@ -19,8 +19,8 @@ class ColumnFamilyData;
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class MemTableList;
|
||||
class DBImpl;
|
||||
class MemTableList;
|
||||
|
||||
// Config for retrieving a property's value.
|
||||
struct DBPropertyInfo {
|
||||
@@ -163,7 +163,10 @@ class InternalStats {
|
||||
// Number of compactions done
|
||||
int count;
|
||||
|
||||
explicit CompactionStats(int _count = 0)
|
||||
// Number of compactions done per CompactionReason
|
||||
int counts[static_cast<int>(CompactionReason::kNumOfReasons)];
|
||||
|
||||
explicit CompactionStats()
|
||||
: micros(0),
|
||||
bytes_read_non_output_levels(0),
|
||||
bytes_read_output_level(0),
|
||||
@@ -174,7 +177,36 @@ class InternalStats {
|
||||
num_output_files(0),
|
||||
num_input_records(0),
|
||||
num_dropped_records(0),
|
||||
count(_count) {}
|
||||
count(0) {
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
for (int i = 0; i < num_of_reasons; i++) {
|
||||
counts[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
explicit CompactionStats(CompactionReason reason, int c)
|
||||
: micros(0),
|
||||
bytes_read_non_output_levels(0),
|
||||
bytes_read_output_level(0),
|
||||
bytes_written(0),
|
||||
bytes_moved(0),
|
||||
num_input_files_in_non_output_levels(0),
|
||||
num_input_files_in_output_level(0),
|
||||
num_output_files(0),
|
||||
num_input_records(0),
|
||||
num_dropped_records(0),
|
||||
count(c) {
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
for (int i = 0; i < num_of_reasons; i++) {
|
||||
counts[i] = 0;
|
||||
}
|
||||
int r = static_cast<int>(reason);
|
||||
if (r >= 0 && r < num_of_reasons) {
|
||||
counts[r] = c;
|
||||
} else {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
explicit CompactionStats(const CompactionStats& c)
|
||||
: micros(c.micros),
|
||||
@@ -189,7 +221,12 @@ class InternalStats {
|
||||
num_output_files(c.num_output_files),
|
||||
num_input_records(c.num_input_records),
|
||||
num_dropped_records(c.num_dropped_records),
|
||||
count(c.count) {}
|
||||
count(c.count) {
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
for (int i = 0; i < num_of_reasons; i++) {
|
||||
counts[i] = c.counts[i];
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
this->micros = 0;
|
||||
@@ -203,6 +240,10 @@ class InternalStats {
|
||||
this->num_input_records = 0;
|
||||
this->num_dropped_records = 0;
|
||||
this->count = 0;
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
for (int i = 0; i < num_of_reasons; i++) {
|
||||
counts[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Add(const CompactionStats& c) {
|
||||
@@ -219,6 +260,10 @@ class InternalStats {
|
||||
this->num_input_records += c.num_input_records;
|
||||
this->num_dropped_records += c.num_dropped_records;
|
||||
this->count += c.count;
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
for (int i = 0; i< num_of_reasons; i++) {
|
||||
counts[i] += c.counts[i];
|
||||
}
|
||||
}
|
||||
|
||||
void Subtract(const CompactionStats& c) {
|
||||
@@ -235,6 +280,10 @@ class InternalStats {
|
||||
this->num_input_records -= c.num_input_records;
|
||||
this->num_dropped_records -= c.num_dropped_records;
|
||||
this->count -= c.count;
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
for (int i = 0; i < num_of_reasons; i++) {
|
||||
counts[i] -= c.counts[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -307,6 +356,10 @@ class InternalStats {
|
||||
bool GetIntPropertyOutOfMutex(const DBPropertyInfo& property_info,
|
||||
Version* version, uint64_t* value);
|
||||
|
||||
const std::vector<CompactionStats>& TEST_GetCompactionStats() const {
|
||||
return comp_stats_;
|
||||
}
|
||||
|
||||
// Store a mapping from the user-facing DB::Properties string to our
|
||||
// DBPropertyInfo struct used internally for retrieving properties.
|
||||
static const std::unordered_map<std::string, DBPropertyInfo> ppt_name_to_info;
|
||||
@@ -322,6 +375,8 @@ class InternalStats {
|
||||
void DumpCFStatsNoFileHistogram(std::string* value);
|
||||
void DumpCFFileHistogram(std::string* value);
|
||||
|
||||
bool HandleBlockCacheStat(Cache** block_cache);
|
||||
|
||||
// Per-DB stats
|
||||
std::atomic<uint64_t> db_stats_[INTERNAL_DB_STATS_ENUM_MAX];
|
||||
// Per-ColumnFamily stats
|
||||
@@ -350,8 +405,7 @@ class InternalStats {
|
||||
uint64_t ingest_keys_addfile; // Total number of keys ingested
|
||||
|
||||
CFStatsSnapshot()
|
||||
: comp_stats(0),
|
||||
ingest_bytes_flush(0),
|
||||
: ingest_bytes_flush(0),
|
||||
stall_count(0),
|
||||
compact_bytes_write(0),
|
||||
compact_bytes_read(0),
|
||||
@@ -480,6 +534,10 @@ class InternalStats {
|
||||
bool HandleIsWriteStopped(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleEstimateOldestKeyTime(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
bool HandleBlockCacheCapacity(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleBlockCacheUsage(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleBlockCachePinnedUsage(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
|
||||
// Total number of background errors encountered. Every time a flush task
|
||||
// or compaction task fails, this counter is incremented. The failure can
|
||||
@@ -528,7 +586,7 @@ class InternalStats {
|
||||
INTERNAL_DB_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
InternalStats(int num_levels, Env* env, ColumnFamilyData* cfd) {}
|
||||
InternalStats(int /*num_levels*/, Env* /*env*/, ColumnFamilyData* /*cfd*/) {}
|
||||
|
||||
struct CompactionStats {
|
||||
uint64_t micros;
|
||||
@@ -543,48 +601,50 @@ class InternalStats {
|
||||
uint64_t num_dropped_records;
|
||||
int count;
|
||||
|
||||
explicit CompactionStats(int _count = 0) {}
|
||||
explicit CompactionStats() {}
|
||||
|
||||
explicit CompactionStats(const CompactionStats& c) {}
|
||||
explicit CompactionStats(CompactionReason /*reason*/, int /*c*/) {}
|
||||
|
||||
void Add(const CompactionStats& c) {}
|
||||
explicit CompactionStats(const CompactionStats& /*c*/) {}
|
||||
|
||||
void Subtract(const CompactionStats& c) {}
|
||||
void Add(const CompactionStats& /*c*/) {}
|
||||
|
||||
void Subtract(const CompactionStats& /*c*/) {}
|
||||
};
|
||||
|
||||
void AddCompactionStats(int level, const CompactionStats& stats) {}
|
||||
void AddCompactionStats(int /*level*/, const CompactionStats& /*stats*/) {}
|
||||
|
||||
void IncBytesMoved(int level, uint64_t amount) {}
|
||||
void IncBytesMoved(int /*level*/, uint64_t /*amount*/) {}
|
||||
|
||||
void AddCFStats(InternalCFStatsType type, uint64_t value) {}
|
||||
void AddCFStats(InternalCFStatsType /*type*/, uint64_t /*value*/) {}
|
||||
|
||||
void AddDBStats(InternalDBStatsType type, uint64_t value,
|
||||
bool concurrent = false) {}
|
||||
void AddDBStats(InternalDBStatsType /*type*/, uint64_t /*value*/,
|
||||
bool /*concurrent */ = false) {}
|
||||
|
||||
HistogramImpl* GetFileReadHist(int level) { return nullptr; }
|
||||
HistogramImpl* GetFileReadHist(int /*level*/) { return nullptr; }
|
||||
|
||||
uint64_t GetBackgroundErrorCount() const { return 0; }
|
||||
|
||||
uint64_t BumpAndGetBackgroundErrorCount() { return 0; }
|
||||
|
||||
bool GetStringProperty(const DBPropertyInfo& property_info,
|
||||
const Slice& property, std::string* value) {
|
||||
bool GetStringProperty(const DBPropertyInfo& /*property_info*/,
|
||||
const Slice& /*property*/, std::string* /*value*/) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetMapProperty(const DBPropertyInfo& property_info,
|
||||
const Slice& property,
|
||||
std::map<std::string, std::string>* value) {
|
||||
bool GetMapProperty(const DBPropertyInfo& /*property_info*/,
|
||||
const Slice& /*property*/,
|
||||
std::map<std::string, std::string>* /*value*/) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetIntProperty(const DBPropertyInfo& property_info, uint64_t* value,
|
||||
DBImpl* db) const {
|
||||
bool GetIntProperty(const DBPropertyInfo& /*property_info*/, uint64_t* /*value*/,
|
||||
DBImpl* /*db*/) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetIntPropertyOutOfMutex(const DBPropertyInfo& property_info,
|
||||
Version* version, uint64_t* value) const {
|
||||
bool GetIntPropertyOutOfMutex(const DBPropertyInfo& /*property_info*/,
|
||||
Version* /*version*/, uint64_t* /*value*/) const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
+27
-13
@@ -27,7 +27,9 @@ struct SuperVersionContext {
|
||||
};
|
||||
|
||||
autovector<SuperVersion*> superversions_to_free;
|
||||
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
||||
autovector<WriteStallNotification> write_stall_notifications;
|
||||
#endif
|
||||
unique_ptr<SuperVersion> new_superversion; // if nullptr no new superversion
|
||||
|
||||
explicit SuperVersionContext(bool create_superversion = false)
|
||||
@@ -38,28 +40,37 @@ struct SuperVersionContext {
|
||||
}
|
||||
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return superversions_to_free.size() > 0 ||
|
||||
write_stall_notifications.size() > 0;
|
||||
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
||||
return !superversions_to_free.empty() ||
|
||||
!write_stall_notifications.empty();
|
||||
#else
|
||||
return !superversions_to_free.empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushWriteStallNotification(
|
||||
WriteStallCondition old_cond, WriteStallCondition new_cond,
|
||||
const std::string& name, const ImmutableCFOptions* ioptions) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
#if !defined(ROCKSDB_LITE) && !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
WriteStallNotification notif;
|
||||
notif.write_stall_info.cf_name = name;
|
||||
notif.write_stall_info.condition.prev = old_cond;
|
||||
notif.write_stall_info.condition.cur = new_cond;
|
||||
notif.immutable_cf_options = ioptions;
|
||||
write_stall_notifications.push_back(notif);
|
||||
#endif // !ROCKSDB_LITE
|
||||
#else
|
||||
(void)old_cond;
|
||||
(void)new_cond;
|
||||
(void)name;
|
||||
(void)ioptions;
|
||||
#endif // !defined(ROCKSDB_LITE) && !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
}
|
||||
|
||||
void Clean() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
#if !defined(ROCKSDB_LITE) && !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
// notify listeners on changed write stall conditions
|
||||
for (auto& notif : write_stall_notifications) {
|
||||
for (auto listener : notif.immutable_cf_options->listeners) {
|
||||
for (auto& listener : notif.immutable_cf_options->listeners) {
|
||||
listener->OnStallConditionsChanged(notif.write_stall_info);
|
||||
}
|
||||
}
|
||||
@@ -73,8 +84,10 @@ struct SuperVersionContext {
|
||||
}
|
||||
|
||||
~SuperVersionContext() {
|
||||
assert(write_stall_notifications.size() == 0);
|
||||
assert(superversions_to_free.size() == 0);
|
||||
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
||||
assert(write_stall_notifications.empty());
|
||||
#endif
|
||||
assert(superversions_to_free.empty());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -92,11 +105,12 @@ struct JobContext {
|
||||
// Structure to store information for candidate files to delete.
|
||||
struct CandidateFileInfo {
|
||||
std::string file_name;
|
||||
uint32_t path_id;
|
||||
CandidateFileInfo(std::string name, uint32_t path)
|
||||
: file_name(std::move(name)), path_id(path) {}
|
||||
std::string file_path;
|
||||
CandidateFileInfo(std::string name, std::string path)
|
||||
: file_name(std::move(name)), file_path(std::move(path)) {}
|
||||
bool operator==(const CandidateFileInfo& other) const {
|
||||
return file_name == other.file_name && path_id == other.path_id;
|
||||
return file_name == other.file_name &&
|
||||
file_path == other.file_path;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,7 +127,7 @@ struct JobContext {
|
||||
std::vector<FileDescriptor> sst_live;
|
||||
|
||||
// a list of sst files that we need to delete
|
||||
std::vector<FileMetaData*> sst_delete_files;
|
||||
std::vector<ObsoleteFileInfo> sst_delete_files;
|
||||
|
||||
// a list of log files that we need to delete
|
||||
std::vector<uint64_t> log_delete_files;
|
||||
|
||||
+2
-2
@@ -260,7 +260,7 @@ TEST_F(EventListenerTest, OnSingleDBFlushTest) {
|
||||
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
|
||||
}
|
||||
|
||||
// make sure call-back functions are called in the right order
|
||||
// make sure callback functions are called in the right order
|
||||
for (size_t i = 0; i < cf_names.size(); ++i) {
|
||||
ASSERT_EQ(listener->flushed_dbs_[i], db_);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_[i], cf_names[i]);
|
||||
@@ -296,7 +296,7 @@ TEST_F(EventListenerTest, MultiCF) {
|
||||
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
|
||||
}
|
||||
|
||||
// make sure call-back functions are called in the right order
|
||||
// make sure callback functions are called in the right order
|
||||
for (size_t i = 0; i < cf_names.size(); i++) {
|
||||
ASSERT_EQ(listener->flushed_dbs_[i], db_);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_[i], cf_names[i]);
|
||||
|
||||
+7
-3
@@ -57,9 +57,11 @@ Status Writer::AddRecord(const Slice& slice) {
|
||||
// Fill the trailer (literal below relies on kHeaderSize and
|
||||
// kRecyclableHeaderSize being <= 11)
|
||||
assert(header_size <= 11);
|
||||
dest_->Append(
|
||||
Slice("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
static_cast<size_t>(leftover)));
|
||||
s = dest_->Append(Slice("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
static_cast<size_t>(leftover)));
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
block_offset_ = 0;
|
||||
}
|
||||
@@ -90,6 +92,8 @@ Status Writer::AddRecord(const Slice& slice) {
|
||||
return s;
|
||||
}
|
||||
|
||||
bool Writer::TEST_BufferIsEmpty() { return dest_->TEST_BufferIsEmpty(); }
|
||||
|
||||
Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
assert(n <= 0xffff); // Must fit in two bytes
|
||||
|
||||
|
||||
@@ -85,6 +85,8 @@ class Writer {
|
||||
|
||||
Status WriteBuffer();
|
||||
|
||||
bool TEST_BufferIsEmpty();
|
||||
|
||||
private:
|
||||
unique_ptr<WritableFileWriter> dest_;
|
||||
size_t block_offset_; // Current offset in block
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
|
||||
#include "port/likely.h"
|
||||
|
||||
namespace rocksdb {
|
||||
void LogsWithPrepTracker::MarkLogAsHavingPrepSectionFlushed(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(prepared_section_completed_mutex_);
|
||||
auto it = prepared_section_completed_.find(log);
|
||||
if (UNLIKELY(it == prepared_section_completed_.end())) {
|
||||
prepared_section_completed_[log] = 1;
|
||||
} else {
|
||||
it->second += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void LogsWithPrepTracker::MarkLogAsContainingPrepSection(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(logs_with_prep_mutex_);
|
||||
|
||||
auto rit = logs_with_prep_.rbegin();
|
||||
bool updated = false;
|
||||
// Most probably the last log is the one that is being marked for
|
||||
// having a prepare section; so search from the end.
|
||||
for (; rit != logs_with_prep_.rend() && rit->log >= log; ++rit) {
|
||||
if (rit->log == log) {
|
||||
rit->cnt++;
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!updated) {
|
||||
// We are either at the start, or at a position with rit->log < log
|
||||
logs_with_prep_.insert(rit.base(), {log, 1});
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t LogsWithPrepTracker::FindMinLogContainingOutstandingPrep() {
|
||||
std::lock_guard<std::mutex> lock(logs_with_prep_mutex_);
|
||||
auto it = logs_with_prep_.begin();
|
||||
// start with the smallest log
|
||||
for (; it != logs_with_prep_.end();) {
|
||||
auto min_log = it->log;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock2(prepared_section_completed_mutex_);
|
||||
auto completed_it = prepared_section_completed_.find(min_log);
|
||||
if (completed_it == prepared_section_completed_.end() ||
|
||||
completed_it->second < it->cnt) {
|
||||
return min_log;
|
||||
}
|
||||
assert(completed_it != prepared_section_completed_.end() &&
|
||||
completed_it->second == it->cnt);
|
||||
prepared_section_completed_.erase(completed_it);
|
||||
}
|
||||
// erase from beginning in vector is not efficient but this function is not
|
||||
// on the fast path.
|
||||
it = logs_with_prep_.erase(it);
|
||||
}
|
||||
// no such log found
|
||||
return 0;
|
||||
}
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// This class is used to track the log files with outstanding prepare entries.
|
||||
class LogsWithPrepTracker {
|
||||
public:
|
||||
// Called when a transaction prepared in `log` has been committed or aborted.
|
||||
void MarkLogAsHavingPrepSectionFlushed(uint64_t log);
|
||||
// Called when a transaction is prepared in `log`.
|
||||
void MarkLogAsContainingPrepSection(uint64_t log);
|
||||
// Return the earliest log file with outstanding prepare entries.
|
||||
uint64_t FindMinLogContainingOutstandingPrep();
|
||||
size_t TEST_PreparedSectionCompletedSize() {
|
||||
return prepared_section_completed_.size();
|
||||
}
|
||||
size_t TEST_LogsWithPrepSize() { return logs_with_prep_.size(); }
|
||||
|
||||
private:
|
||||
// REQUIRES: logs_with_prep_mutex_ held
|
||||
//
|
||||
// sorted list of log numbers still containing prepared data.
|
||||
// this is used by FindObsoleteFiles to determine which
|
||||
// flushed logs we must keep around because they still
|
||||
// contain prepared data which has not been committed or rolled back
|
||||
struct LogCnt {
|
||||
uint64_t log; // the log number
|
||||
uint64_t cnt; // number of prepared sections in the log
|
||||
};
|
||||
std::vector<LogCnt> logs_with_prep_;
|
||||
std::mutex logs_with_prep_mutex_;
|
||||
|
||||
// REQUIRES: prepared_section_completed_mutex_ held
|
||||
//
|
||||
// to be used in conjunction with logs_with_prep_.
|
||||
// once a transaction with data in log L is committed or rolled back
|
||||
// rather than updating logs_with_prep_ directly we keep track of that
|
||||
// in prepared_section_completed_ which maps LOG -> instance_count. This helps
|
||||
// avoiding contention between a commit thread and the prepare threads.
|
||||
//
|
||||
// when trying to determine the minimum log still active we first
|
||||
// consult logs_with_prep_. while that root value maps to
|
||||
// an equal value in prepared_section_completed_ we erase the log from
|
||||
// both logs_with_prep_ and prepared_section_completed_.
|
||||
std::unordered_map<uint64_t, uint64_t> prepared_section_completed_;
|
||||
std::mutex prepared_section_completed_mutex_;
|
||||
|
||||
};
|
||||
} // namespace rocksdb
|
||||
+24
-32
@@ -101,9 +101,7 @@ void ManagedIterator::SeekToLast() {
|
||||
}
|
||||
assert(mutable_iter_ != nullptr);
|
||||
mutable_iter_->SeekToLast();
|
||||
if (mutable_iter_->status().ok()) {
|
||||
UpdateCurrent();
|
||||
}
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
void ManagedIterator::SeekToFirst() {
|
||||
@@ -146,27 +144,13 @@ void ManagedIterator::Prev() {
|
||||
}
|
||||
MILock l(&in_use_, this);
|
||||
if (NeedToRebuild()) {
|
||||
std::string current_key = key().ToString();
|
||||
Slice old_key(current_key);
|
||||
RebuildIterator();
|
||||
SeekInternal(old_key, false);
|
||||
UpdateCurrent();
|
||||
RebuildIterator(true);
|
||||
if (!valid_) {
|
||||
return;
|
||||
}
|
||||
if (key().compare(old_key) != 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::Incomplete("Cannot do Prev now");
|
||||
return;
|
||||
}
|
||||
}
|
||||
mutable_iter_->Prev();
|
||||
if (mutable_iter_->status().ok()) {
|
||||
UpdateCurrent();
|
||||
status_ = Status::OK();
|
||||
} else {
|
||||
status_ = mutable_iter_->status();
|
||||
}
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
void ManagedIterator::Next() {
|
||||
@@ -176,19 +160,10 @@ void ManagedIterator::Next() {
|
||||
}
|
||||
MILock l(&in_use_, this);
|
||||
if (NeedToRebuild()) {
|
||||
std::string current_key = key().ToString();
|
||||
Slice old_key(current_key.data(), cached_key_.Size());
|
||||
RebuildIterator();
|
||||
SeekInternal(old_key, false);
|
||||
UpdateCurrent();
|
||||
RebuildIterator(true);
|
||||
if (!valid_) {
|
||||
return;
|
||||
}
|
||||
if (key().compare(old_key) != 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::Incomplete("Cannot do Next now");
|
||||
return;
|
||||
}
|
||||
}
|
||||
mutable_iter_->Next();
|
||||
UpdateCurrent();
|
||||
@@ -206,21 +181,38 @@ Slice ManagedIterator::value() const {
|
||||
|
||||
Status ManagedIterator::status() const { return status_; }
|
||||
|
||||
void ManagedIterator::RebuildIterator() {
|
||||
void ManagedIterator::RebuildIterator(bool reseek) {
|
||||
std::string current_key;
|
||||
if (reseek) {
|
||||
current_key = key().ToString();
|
||||
}
|
||||
|
||||
svnum_ = cfd_->GetSuperVersionNumber();
|
||||
mutable_iter_ = unique_ptr<Iterator>(db_->NewIterator(read_options_, &cfh_));
|
||||
|
||||
if (reseek) {
|
||||
Slice old_key(current_key.data(), current_key.size());
|
||||
SeekInternal(old_key, false);
|
||||
UpdateCurrent();
|
||||
if (!valid_ || key().compare(old_key) != 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::Incomplete(
|
||||
"Next/Prev failed because current key has "
|
||||
"been removed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ManagedIterator::UpdateCurrent() {
|
||||
assert(mutable_iter_ != nullptr);
|
||||
|
||||
valid_ = mutable_iter_->Valid();
|
||||
status_ = mutable_iter_->status();
|
||||
|
||||
if (!valid_) {
|
||||
status_ = mutable_iter_->status();
|
||||
return;
|
||||
}
|
||||
|
||||
status_ = Status::OK();
|
||||
cached_key_.SetUserKey(mutable_iter_->key());
|
||||
cached_value_.SetUserKey(mutable_iter_->value());
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class ManagedIterator : public Iterator {
|
||||
}
|
||||
|
||||
private:
|
||||
void RebuildIterator();
|
||||
void RebuildIterator(bool reseek = false);
|
||||
void UpdateCurrent();
|
||||
void SeekInternal(const Slice& user_key, bool seek_to_first);
|
||||
bool NeedToRebuild();
|
||||
|
||||
@@ -19,7 +19,12 @@ using namespace rocksdb;
|
||||
|
||||
namespace {
|
||||
|
||||
const int kNumKeys = 1100000;
|
||||
// Reasoning: previously the number was 1100000. Since the keys are written to
|
||||
// the batch in one write each write will result into one SST file. each write
|
||||
// will result into one SST file. We reduced the write_buffer_size to 1K to
|
||||
// basically have the same effect with however less number of keys, which
|
||||
// results into less test runtime.
|
||||
const int kNumKeys = 1100;
|
||||
|
||||
std::string Key1(int i) {
|
||||
char buf[100];
|
||||
@@ -99,6 +104,7 @@ TEST_F(ManualCompactionTest, Test) {
|
||||
// specific scenario.
|
||||
rocksdb::DB* db;
|
||||
rocksdb::Options db_options;
|
||||
db_options.write_buffer_size = 1024;
|
||||
db_options.create_if_missing = true;
|
||||
db_options.compression = rocksdb::kNoCompression;
|
||||
ASSERT_OK(rocksdb::DB::Open(db_options, dbname_, &db));
|
||||
|
||||
+7
-7
@@ -74,8 +74,8 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
: nullptr,
|
||||
mutable_cf_options.memtable_huge_page_size),
|
||||
table_(ioptions.memtable_factory->CreateMemTableRep(
|
||||
comparator_, &arena_, ioptions.prefix_extractor, ioptions.info_log,
|
||||
column_family_id)),
|
||||
comparator_, &arena_, mutable_cf_options.prefix_extractor.get(),
|
||||
ioptions.info_log, column_family_id)),
|
||||
range_del_table_(SkipListFactory().CreateMemTableRep(
|
||||
comparator_, &arena_, nullptr /* transform */, ioptions.info_log,
|
||||
column_family_id)),
|
||||
@@ -95,7 +95,7 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
locks_(moptions_.inplace_update_support
|
||||
? moptions_.inplace_update_num_locks
|
||||
: 0),
|
||||
prefix_extractor_(ioptions.prefix_extractor),
|
||||
prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
|
||||
flush_state_(FLUSH_NOT_REQUESTED),
|
||||
env_(ioptions.env),
|
||||
insert_with_hint_prefix_extractor_(
|
||||
@@ -229,7 +229,7 @@ int MemTable::KeyComparator::operator()(const char* prefix_len_key1,
|
||||
}
|
||||
|
||||
int MemTable::KeyComparator::operator()(const char* prefix_len_key,
|
||||
const Slice& key)
|
||||
const KeyComparator::DecodedType& key)
|
||||
const {
|
||||
// Internal keys are encoded as length-prefixed strings.
|
||||
Slice a = GetLengthPrefixedSlice(prefix_len_key);
|
||||
@@ -479,12 +479,12 @@ bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
insert_with_hint_prefix_extractor_->InDomain(key_slice)) {
|
||||
Slice prefix = insert_with_hint_prefix_extractor_->Transform(key_slice);
|
||||
bool res = table->InsertKeyWithHint(handle, &insert_hints_[prefix]);
|
||||
if (!res) {
|
||||
if (UNLIKELY(!res)) {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
bool res = table->InsertKey(handle);
|
||||
if (!res) {
|
||||
if (UNLIKELY(!res)) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -520,7 +520,7 @@ bool MemTable::Add(SequenceNumber s, ValueType type,
|
||||
UpdateFlushState();
|
||||
} else {
|
||||
bool res = table->InsertKeyConcurrently(handle);
|
||||
if (!res) {
|
||||
if (UNLIKELY(!res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ struct MemTablePostProcessInfo {
|
||||
};
|
||||
|
||||
// Note: Many of the methods in this class have comments indicating that
|
||||
// external synchromization is required as these methods are not thread-safe.
|
||||
// external synchronization is required as these methods are not thread-safe.
|
||||
// It is up to higher layers of code to decide how to prevent concurrent
|
||||
// invokation of these methods. This is usually done by acquiring either
|
||||
// the db mutex or the single writer thread.
|
||||
@@ -84,7 +84,7 @@ class MemTable {
|
||||
virtual int operator()(const char* prefix_len_key1,
|
||||
const char* prefix_len_key2) const override;
|
||||
virtual int operator()(const char* prefix_len_key,
|
||||
const Slice& key) const override;
|
||||
const DecodedType& key) const override;
|
||||
};
|
||||
|
||||
// MemTables are reference counted. The initial reference count
|
||||
|
||||
+27
-7
@@ -12,6 +12,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/version_set.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
@@ -322,9 +323,10 @@ void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
// Record a successful flush in the manifest file
|
||||
Status MemTableList::InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& mems, VersionSet* vset, InstrumentedMutex* mu,
|
||||
uint64_t file_number, autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory, LogBuffer* log_buffer) {
|
||||
const autovector<MemTable*>& mems, LogsWithPrepTracker* prep_tracker,
|
||||
VersionSet* vset, InstrumentedMutex* mu, uint64_t file_number,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
@@ -361,6 +363,7 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
uint64_t batch_file_number = 0;
|
||||
size_t batch_count = 0;
|
||||
autovector<VersionEdit*> edit_list;
|
||||
autovector<MemTable*> memtables_to_flush;
|
||||
// enumerate from the last (earliest) element to see how many batch finished
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
@@ -373,11 +376,20 @@ Status MemTableList::InstallMemtableFlushResults(
|
||||
"[%s] Level-0 commit table #%" PRIu64 " started",
|
||||
cfd->GetName().c_str(), m->file_number_);
|
||||
edit_list.push_back(&m->edit_);
|
||||
memtables_to_flush.push_back(m);
|
||||
}
|
||||
batch_count++;
|
||||
}
|
||||
|
||||
if (batch_count > 0) {
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
assert(edit_list.size() > 0);
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// manifest entry for the last file flushed.
|
||||
edit_list.back()->SetMinLogNumberToKeep(PrecomputeMinLogNumberToKeep(
|
||||
vset, *cfd, edit_list, memtables_to_flush, prep_tracker));
|
||||
}
|
||||
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(cfd, mutable_cf_options, edit_list, mu,
|
||||
db_directory);
|
||||
@@ -468,13 +480,21 @@ void MemTableList::InstallNewVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t MemTableList::GetMinLogContainingPrepSection() {
|
||||
uint64_t MemTableList::PrecomputeMinLogContainingPrepSection(
|
||||
const autovector<MemTable*>& memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
for (auto& m : current_->memlist_) {
|
||||
// this mem has been flushed it no longer
|
||||
// needs to hold on the its prep section
|
||||
if (m->flush_completed_) {
|
||||
// Assume the list is very short, we can live with O(m*n). We can optimize
|
||||
// if the performance has some problem.
|
||||
bool should_skip = false;
|
||||
for (MemTable* m_to_flush : memtables_to_flush) {
|
||||
if (m == m_to_flush) {
|
||||
should_skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (should_skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+9
-4
@@ -13,6 +13,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
@@ -210,9 +211,10 @@ class MemTableList {
|
||||
// Commit a successful flush in the manifest file
|
||||
Status InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& m, VersionSet* vset, InstrumentedMutex* mu,
|
||||
uint64_t file_number, autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory, LogBuffer* log_buffer);
|
||||
const autovector<MemTable*>& m, LogsWithPrepTracker* prep_tracker,
|
||||
VersionSet* vset, InstrumentedMutex* mu, uint64_t file_number,
|
||||
autovector<MemTable*>* to_delete, Directory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
// Takes ownership of the referenced held on *m by the caller of Add().
|
||||
@@ -243,7 +245,10 @@ class MemTableList {
|
||||
|
||||
size_t* current_memory_usage() { return ¤t_memory_usage_; }
|
||||
|
||||
uint64_t GetMinLogContainingPrepSection();
|
||||
// Returns the min log containing the prep section after memtables listsed in
|
||||
// `memtables_to_flush` are flushed and their status is persisted in manifest.
|
||||
uint64_t PrecomputeMinLogContainingPrepSection(
|
||||
const autovector<MemTable*>& memtables_to_flush);
|
||||
|
||||
uint64_t GetEarliestMemTableID() const {
|
||||
auto& memlist = current_->memlist_;
|
||||
|
||||
@@ -82,10 +82,10 @@ class MemTableListTest : public testing::Test {
|
||||
// Create dummy mutex.
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
|
||||
return list->InstallMemtableFlushResults(cfd, mutable_cf_options, m,
|
||||
&versions, &mutex, 1, to_delete,
|
||||
nullptr, &log_buffer);
|
||||
LogsWithPrepTracker dummy_prep_tracker;
|
||||
return list->InstallMemtableFlushResults(
|
||||
cfd, mutable_cf_options, m, &dummy_prep_tracker, &versions, &mutex, 1,
|
||||
to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
// 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.
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::flush;
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class ObsoleteFilesTest : public testing::Test {
|
||||
public:
|
||||
std::string dbname_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
Env* env_;
|
||||
int numlevels_;
|
||||
|
||||
ObsoleteFilesTest() {
|
||||
db_ = nullptr;
|
||||
env_ = Env::Default();
|
||||
// Trigger compaction when the number of level 0 files reaches 2.
|
||||
options_.level0_file_num_compaction_trigger = 2;
|
||||
options_.disable_auto_compactions = false;
|
||||
options_.delete_obsolete_files_period_micros = 0; // always do full purge
|
||||
options_.enable_thread_tracking = true;
|
||||
options_.write_buffer_size = 1024*1024*1000;
|
||||
options_.target_file_size_base = 1024*1024*1000;
|
||||
options_.max_bytes_for_level_base = 1024*1024*1000;
|
||||
options_.WAL_ttl_seconds = 300; // Used to test log files
|
||||
options_.WAL_size_limit_MB = 1024; // Used to test log files
|
||||
dbname_ = test::TmpDir() + "/obsolete_files_test";
|
||||
options_.wal_dir = dbname_ + "/wal_files";
|
||||
|
||||
// clean up all the files that might have been there before
|
||||
std::vector<std::string> old_files;
|
||||
env_->GetChildren(dbname_, &old_files);
|
||||
for (auto file : old_files) {
|
||||
env_->DeleteFile(dbname_ + "/" + file);
|
||||
}
|
||||
env_->GetChildren(options_.wal_dir, &old_files);
|
||||
for (auto file : old_files) {
|
||||
env_->DeleteFile(options_.wal_dir + "/" + file);
|
||||
}
|
||||
|
||||
DestroyDB(dbname_, options_);
|
||||
numlevels_ = 7;
|
||||
EXPECT_OK(ReopenDB(true));
|
||||
}
|
||||
|
||||
Status ReopenDB(bool create) {
|
||||
delete db_;
|
||||
if (create) {
|
||||
DestroyDB(dbname_, options_);
|
||||
}
|
||||
db_ = nullptr;
|
||||
options_.create_if_missing = create;
|
||||
return DB::Open(options_, dbname_, &db_);
|
||||
}
|
||||
|
||||
void CloseDB() {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
void AddKeys(int numkeys, int startkey) {
|
||||
WriteOptions options;
|
||||
options.sync = false;
|
||||
for (int i = startkey; i < (numkeys + startkey) ; i++) {
|
||||
std::string temp = ToString(i);
|
||||
Slice key(temp);
|
||||
Slice value(temp);
|
||||
ASSERT_OK(db_->Put(options, key, value));
|
||||
}
|
||||
}
|
||||
|
||||
int numKeysInLevels(
|
||||
std::vector<LiveFileMetaData> &metadata,
|
||||
std::vector<int> *keysperlevel = nullptr) {
|
||||
|
||||
if (keysperlevel != nullptr) {
|
||||
keysperlevel->resize(numlevels_);
|
||||
}
|
||||
|
||||
int numKeys = 0;
|
||||
for (size_t i = 0; i < metadata.size(); i++) {
|
||||
int startkey = atoi(metadata[i].smallestkey.c_str());
|
||||
int endkey = atoi(metadata[i].largestkey.c_str());
|
||||
int numkeysinfile = (endkey - startkey + 1);
|
||||
numKeys += numkeysinfile;
|
||||
if (keysperlevel != nullptr) {
|
||||
(*keysperlevel)[(int)metadata[i].level] += numkeysinfile;
|
||||
}
|
||||
fprintf(stderr, "level %d name %s smallest %s largest %s\n",
|
||||
metadata[i].level, metadata[i].name.c_str(),
|
||||
metadata[i].smallestkey.c_str(),
|
||||
metadata[i].largestkey.c_str());
|
||||
}
|
||||
return numKeys;
|
||||
}
|
||||
|
||||
void createLevel0Files(int numFiles, int numKeysPerFile) {
|
||||
int startKey = 0;
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
AddKeys(numKeysPerFile, startKey);
|
||||
startKey += numKeysPerFile;
|
||||
ASSERT_OK(dbi->TEST_FlushMemTable());
|
||||
ASSERT_OK(dbi->TEST_WaitForFlushMemTable());
|
||||
}
|
||||
}
|
||||
|
||||
void CheckFileTypeCounts(std::string& dir,
|
||||
int required_log,
|
||||
int required_sst,
|
||||
int required_manifest) {
|
||||
std::vector<std::string> filenames;
|
||||
env_->GetChildren(dir, &filenames);
|
||||
|
||||
int log_cnt = 0, sst_cnt = 0, manifest_cnt = 0;
|
||||
for (auto file : filenames) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
log_cnt += (type == kLogFile);
|
||||
sst_cnt += (type == kTableFile);
|
||||
manifest_cnt += (type == kDescriptorFile);
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(required_log, log_cnt);
|
||||
ASSERT_EQ(required_sst, sst_cnt);
|
||||
ASSERT_EQ(required_manifest, manifest_cnt);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ObsoleteFilesTest, RaceForObsoleteFileDeletion) {
|
||||
createLevel0Files(2, 50000);
|
||||
CheckFileTypeCounts(options_.wal_dir, 1, 0, 0);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::BackgroundCallCompaction:FoundObsoleteFiles",
|
||||
"ObsoleteFilesTest::RaceForObsoleteFileDeletion:1"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"ObsoleteFilesTest::RaceForObsoleteFileDeletion:2"},
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DeleteObsoleteFileImpl:AfterDeletion", [&](void* arg) {
|
||||
Status* p_status = reinterpret_cast<Status*>(arg);
|
||||
ASSERT_TRUE(p_status->ok());
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::CloseHelper:PendingPurgeFinished", [&](void* arg) {
|
||||
std::vector<uint64_t>* files_grabbed_for_purge_ptr =
|
||||
reinterpret_cast<std::vector<uint64_t>*>(arg);
|
||||
ASSERT_TRUE(files_grabbed_for_purge_ptr->empty());
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
port::Thread user_thread([&]() {
|
||||
JobContext jobCxt(0);
|
||||
TEST_SYNC_POINT("ObsoleteFilesTest::RaceForObsoleteFileDeletion:1");
|
||||
dbi->TEST_LockMutex();
|
||||
dbi->FindObsoleteFiles(&jobCxt,
|
||||
true /* force=true */, false /* no_full_scan=false */);
|
||||
dbi->TEST_UnlockMutex();
|
||||
TEST_SYNC_POINT("ObsoleteFilesTest::RaceForObsoleteFileDeletion:2");
|
||||
dbi->PurgeObsoleteFiles(jobCxt);
|
||||
jobCxt.Clean();
|
||||
});
|
||||
|
||||
user_thread.join();
|
||||
|
||||
CloseDB();
|
||||
}
|
||||
|
||||
} //namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
fprintf(stderr,
|
||||
"SKIPPED as DBImpl::DeleteFile is not supported in ROCKSDB_LITE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user