mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Compare commits
299 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 913c664f25 | |||
| 772c07e65a | |||
| 1f9d7c0f54 | |||
| 2c9e9f2a59 | |||
| 5ef27dea33 | |||
| 78b28d80b0 | |||
| 93edd51c4a | |||
| 5f025ea832 | |||
| a59dc843a4 | |||
| 231fffd07c | |||
| a6e615a7ba | |||
| 6febfd8451 | |||
| 4e729f9095 | |||
| b00761eea6 | |||
| bc8b05cb77 | |||
| ddb62d1f29 | |||
| 1e9c8d42a0 | |||
| 4c49e38f15 | |||
| 80ad996b35 | |||
| 5b123813f8 | |||
| 90e285efde | |||
| 167cdc9f17 | |||
| 2f4e288143 | |||
| e3a93c9ee1 | |||
| 46ca51d430 | |||
| 457bcfde02 | |||
| 9905101c8c | |||
| da3b2840cb | |||
| 19a97dd139 | |||
| 9f54446525 | |||
| cca87d7722 | |||
| 9e4913ce9d | |||
| d783af1857 | |||
| 9f31df8679 | |||
| 846e05005d | |||
| 503a756e42 | |||
| 51413e0a85 | |||
| 69c4ccb970 | |||
| 643df920d8 | |||
| 5cd8aaf75f | |||
| 679a45d0cb | |||
| 51185592fd | |||
| ae45835703 | |||
| 76e951dbb1 | |||
| 6652c94f59 | |||
| c9932d18cc | |||
| 02554b3c38 | |||
| 2367656b6c | |||
| 42f898bf83 | |||
| e8263dbdaa | |||
| a5fa8735e9 | |||
| c06b54d0c6 | |||
| 6ec6a4a9a4 | |||
| a9c5e8e944 | |||
| 2cbb61eadb | |||
| 2389aa2da9 | |||
| 6a279037cf | |||
| 3a408eeae9 | |||
| 6d072f2a03 | |||
| 43a340bebe | |||
| a68d814570 | |||
| 9a87ae46fd | |||
| 6287f0d73b | |||
| 622683000c | |||
| f5a59c42c5 | |||
| 811e403f57 | |||
| 68626249c3 | |||
| 638d239507 | |||
| 94d62d771e | |||
| d3a6726f02 | |||
| 9bd5fce6e8 | |||
| 54fc617891 | |||
| b931f84e56 | |||
| 915d72d849 | |||
| 0b59ef519f | |||
| 2ed91622fb | |||
| bf5dbc17e3 | |||
| 97631357aa | |||
| 83a6a614e9 | |||
| 6a171724b7 | |||
| 9ba88a1e5d | |||
| 27f516acc8 | |||
| aa2486b23c | |||
| 1a928c22a0 | |||
| a378a4c2ac | |||
| d35ffd569c | |||
| e8c2e68b4e | |||
| d05c0fe4d1 | |||
| 9eb3e1f77d | |||
| fcda80fc33 | |||
| dd2a35f13f | |||
| 20dd828c01 | |||
| c85c87a718 | |||
| 2becafdb43 | |||
| eb9026f09b | |||
| 4d945c57ac | |||
| 699e1b5ede | |||
| 7af6ced14b | |||
| 108c619acb | |||
| fbab9913e2 | |||
| adbc25a4c8 | |||
| cbfa729d37 | |||
| 78b8cfc7ec | |||
| 2208cc0196 | |||
| 533e47709c | |||
| cfc20019d1 | |||
| eae9f040eb | |||
| 83b991922e | |||
| 43a5cdb58c | |||
| ac97e6930f | |||
| b55b2f45d0 | |||
| 19e8c9b64f | |||
| 20dec1401f | |||
| 3f2723a81b | |||
| f9fb9f1421 | |||
| 229e6fbe0e | |||
| 38b17ecd0e | |||
| cdb6334e68 | |||
| 979fbdc696 | |||
| 44eca41add | |||
| 672befea2a | |||
| 9a449865e3 | |||
| a281822331 | |||
| 1729779b85 | |||
| c5e12ebfd2 | |||
| ab0645a596 | |||
| e10570331d | |||
| 1daff8f85a | |||
| 1b4c104a67 | |||
| 1d6a10f52d | |||
| ba0967b567 | |||
| 62829ff751 | |||
| 2f41ecfe75 | |||
| 26293c89a6 | |||
| df8c307d63 | |||
| d8a27d9331 | |||
| 202942b20c | |||
| 244e6f2002 | |||
| 9046bdc5d3 | |||
| 7bc18e2727 | |||
| 3552473668 | |||
| 4c74dba5fa | |||
| 8e12638f3d | |||
| e1c468d16f | |||
| e89b1c9c6e | |||
| f2bf0b2d1e | |||
| 35fe685402 | |||
| e0515607bc | |||
| 353a68d550 | |||
| a2e46eae46 | |||
| c2404d9928 | |||
| c762efc4a9 | |||
| 40712df9ab | |||
| 3a3dc29437 | |||
| bd2c753dd0 | |||
| 6ec2bf3fce | |||
| d61d4507c0 | |||
| d92a59b6f2 | |||
| 77273d4137 | |||
| 7785f61132 | |||
| 0a97125ec0 | |||
| 4c70cb7306 | |||
| 90cd6c2bb1 | |||
| 8a678a50ba | |||
| 04a849b7b4 | |||
| 64855979ae | |||
| 6f0f82de87 | |||
| de3fb9a6ff | |||
| 12eaacb71d | |||
| 5d9a67e718 | |||
| 3da225716c | |||
| e0b84538af | |||
| 38b03c840e | |||
| 6e78fe3c8d | |||
| d150e01474 | |||
| 4f98b43ba3 | |||
| cc9fa7fcdb | |||
| f4a616ebf9 | |||
| b1a02ffeab | |||
| 208556ee13 | |||
| e579e32eaa | |||
| 30edf1874c | |||
| d1c9ede195 | |||
| 1dfc5eaab0 | |||
| f622ca2c7c | |||
| d599135a03 | |||
| 4834dab578 | |||
| b538e756c2 | |||
| 265db3ebb5 | |||
| 55f4f5486d | |||
| 849a8c0ae0 | |||
| 399f477818 | |||
| e648c1d9eb | |||
| 80d7067cb2 | |||
| 3f89af1c39 | |||
| 70c7302fb5 | |||
| 41df734830 | |||
| 3617287e0e | |||
| 230b909da8 | |||
| 74782cec32 | |||
| 9625a2bc2b | |||
| ae152ee666 | |||
| 0d16fad51b | |||
| d9dc6b4637 | |||
| 7260347fd1 | |||
| 5daa426a18 | |||
| 66b524a911 | |||
| f5b951f7b6 | |||
| cfcf045acc | |||
| 092f417037 | |||
| 6b7fcc0d5f | |||
| 3782accf7d | |||
| 112702ac6c | |||
| eae832740b | |||
| 327c4807a7 | |||
| 66b5613d0c | |||
| 3778470061 | |||
| a78503bd6c | |||
| 0be1feec21 | |||
| 4f7ba3aaed | |||
| c129c75fb7 | |||
| 6bb3b4b567 | |||
| abd1fdddef | |||
| 3a6e83b56b | |||
| ec2b996b29 | |||
| 9f5cfb8e71 | |||
| 8a008d4170 | |||
| 22ce462450 | |||
| a3c1832e86 | |||
| 74fb7f0ba5 | |||
| 0f4d90e6e4 | |||
| 699a569c52 | |||
| 0acaa1a846 | |||
| 3bde41b5a3 | |||
| cd2520361d | |||
| 6e8a1354a7 | |||
| b0259e45e0 | |||
| f064d74e45 | |||
| 68d43b4d30 | |||
| 61876614dc | |||
| 3e9c5a3523 | |||
| 1a59b6e2a9 | |||
| 82d8ca8ade | |||
| 60d8b19836 | |||
| f786b4a5b4 | |||
| aa0367aabb | |||
| cb19e7411f | |||
| a6a9213a36 | |||
| 872a261ffc | |||
| 6ca3feed5c | |||
| 7c76a7fba2 | |||
| 8d34806972 | |||
| e0d9d57750 | |||
| 2de61d9129 | |||
| 4f66ec977d | |||
| e4dcf5fd22 | |||
| 6edc5d0719 | |||
| 84c5c9aab1 | |||
| 09ea5d8944 | |||
| 0d57d93a06 | |||
| 662ce62044 | |||
| cfdf2116d3 | |||
| 66464d1fde | |||
| 3e6c185381 | |||
| 1e87f2b68b | |||
| f872009237 | |||
| 9f0bd56889 | |||
| 3886dddc3b | |||
| c360675750 | |||
| 7259e28d91 | |||
| 68b46a2e36 | |||
| 10bae8ceb3 | |||
| 5c2f13fb14 | |||
| 15fd3be07b | |||
| c08c0ae731 | |||
| a8975b6245 | |||
| 9dbcda9e3b | |||
| b4d7209428 | |||
| 554a6456aa | |||
| acb80534ca | |||
| e731f44022 | |||
| c92c58f84d | |||
| 68980df89c | |||
| 22028aa9ab | |||
| 2730fe693e | |||
| 1bfeffab2d | |||
| 705b8eecb4 | |||
| 0b0cb6f1a2 | |||
| 24f73436fb | |||
| 68614a9608 | |||
| 5830c619d5 | |||
| 24b118ad98 | |||
| fe90ed7a70 | |||
| 5355e527d9 | |||
| 2e8ad03ab3 | |||
| f46a2a0375 | |||
| 92f631da33 | |||
| d0c6aea192 | |||
| 220870523c |
@@ -24,6 +24,7 @@ make_config.mk
|
||||
*.vcxproj.filters
|
||||
*.sln
|
||||
*.cmake
|
||||
.watchmanconfig
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
build/
|
||||
@@ -32,6 +33,8 @@ ldb
|
||||
manifest_dump
|
||||
sst_dump
|
||||
blob_dump
|
||||
block_cache_trace_analyzer
|
||||
tools/block_cache_analyzer/*.pyc
|
||||
column_aware_encoding_exp
|
||||
util/build_version.cc
|
||||
build_tools/VALGRIND_LOGS/
|
||||
@@ -47,6 +50,7 @@ rocksdb_undump
|
||||
db_test2
|
||||
trace_analyzer
|
||||
trace_analyzer_test
|
||||
block_cache_trace_analyzer
|
||||
.DS_Store
|
||||
|
||||
java/out
|
||||
|
||||
@@ -122,5 +122,3 @@ script:
|
||||
notifications:
|
||||
email:
|
||||
- leveldb@fb.com
|
||||
webhooks:
|
||||
- https://buildtimetrend.herokuapp.com/travis
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"content_hash_warming": true,
|
||||
"content_hash_max_items": 333333,
|
||||
"hint_num_files_per_dir": 8,
|
||||
"fsevents_latency": 0.05
|
||||
}
|
||||
+75
-51
@@ -32,18 +32,19 @@
|
||||
# 3. cmake ..
|
||||
# 4. make -j
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(rocksdb)
|
||||
enable_language(CXX)
|
||||
enable_language(C)
|
||||
enable_language(ASM)
|
||||
cmake_minimum_required(VERSION 3.5.1)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
include(ReadVersion)
|
||||
get_rocksdb_version(rocksdb_VERSION)
|
||||
project(rocksdb
|
||||
VERSION ${rocksdb_VERSION}
|
||||
LANGUAGES CXX C ASM)
|
||||
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
@@ -59,6 +60,13 @@ option(WITH_WINDOWS_UTF8_FILENAMES "use UTF8 as characterset for opening files,
|
||||
if (WITH_WINDOWS_UTF8_FILENAMES)
|
||||
add_definitions(-DROCKSDB_WINDOWS_UTF8_FILENAMES)
|
||||
endif()
|
||||
# third-party/folly is only validated to work on Linux and Windows for now.
|
||||
# So only turn it on there by default.
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" ON)
|
||||
else()
|
||||
option(WITH_FOLLY_DISTRIBUTED_MUTEX "build with folly::DistributedMutex" OFF)
|
||||
endif()
|
||||
if(MSVC)
|
||||
# Defaults currently different for GFLAGS.
|
||||
# We will address find_package work a little later
|
||||
@@ -74,8 +82,7 @@ else()
|
||||
if(WITH_JEMALLOC)
|
||||
find_package(JeMalloc REQUIRED)
|
||||
add_definitions(-DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE)
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${JEMALLOC_LIBRARIES})
|
||||
list(APPEND THIRDPARTY_LIBS JeMalloc::JeMalloc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -93,47 +100,43 @@ else()
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
add_definitions(-DSNAPPY)
|
||||
include_directories(${SNAPPY_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${SNAPPY_LIBRARIES})
|
||||
list(APPEND THIRDPARTY_LIBS snappy::snappy)
|
||||
endif()
|
||||
|
||||
if(WITH_ZLIB)
|
||||
find_package(ZLIB REQUIRED)
|
||||
add_definitions(-DZLIB)
|
||||
if(ZLIB_INCLUDE_DIRS)
|
||||
# CMake 3
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
else()
|
||||
# CMake 2
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
endif()
|
||||
list(APPEND THIRDPARTY_LIBS ${ZLIB_LIBRARIES})
|
||||
list(APPEND THIRDPARTY_LIBS ZLIB::ZLIB)
|
||||
endif()
|
||||
|
||||
option(WITH_BZ2 "build with bzip2" OFF)
|
||||
if(WITH_BZ2)
|
||||
find_package(bzip2 REQUIRED)
|
||||
find_package(BZip2 REQUIRED)
|
||||
add_definitions(-DBZIP2)
|
||||
include_directories(${BZIP2_INCLUDE_DIR})
|
||||
if(BZIP2_INCLUDE_DIRS)
|
||||
include_directories(${BZIP2_INCLUDE_DIRS})
|
||||
else()
|
||||
include_directories(${BZIP2_INCLUDE_DIR})
|
||||
endif()
|
||||
list(APPEND THIRDPARTY_LIBS ${BZIP2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(WITH_LZ4)
|
||||
find_package(lz4 REQUIRED)
|
||||
add_definitions(-DLZ4)
|
||||
include_directories(${LZ4_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${LZ4_LIBRARIES})
|
||||
list(APPEND THIRDPARTY_LIBS lz4::lz4)
|
||||
endif()
|
||||
|
||||
if(WITH_ZSTD)
|
||||
find_package(zstd REQUIRED)
|
||||
add_definitions(-DZSTD)
|
||||
include_directories(${ZSTD_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${ZSTD_LIBRARIES})
|
||||
list(APPEND THIRDPARTY_LIBS zstd::zstd)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(TIMESTAMP GIT_DATE_TIME "%Y/%m/%d %H:%M:%S" UTC)
|
||||
string(TIMESTAMP TS "%Y/%m/%d %H:%M:%S" UTC)
|
||||
set(GIT_DATE_TIME "${TS}" CACHE STRING "the time we first built rocksdb")
|
||||
|
||||
find_package(Git)
|
||||
|
||||
@@ -150,17 +153,6 @@ endif()
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
|
||||
# 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)
|
||||
if(WITH_MD_LIBRARY)
|
||||
@@ -209,11 +201,11 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
CHECK_C_COMPILER_FLAG("-march=armv8-a+crc" HAS_ARMV8_CRC)
|
||||
CHECK_C_COMPILER_FLAG("-march=armv8-a+crc+crypto" HAS_ARMV8_CRC)
|
||||
if(HAS_ARMV8_CRC)
|
||||
message(STATUS " HAS_ARMV8_CRC yes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+crc -Wno-unused-function")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+crc -Wno-unused-function")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+crc+crypto -Wno-unused-function")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+crc+crypto -Wno-unused-function")
|
||||
endif(HAS_ARMV8_CRC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
|
||||
@@ -316,15 +308,14 @@ if(WITH_NUMA)
|
||||
find_package(NUMA REQUIRED)
|
||||
add_definitions(-DNUMA)
|
||||
include_directories(${NUMA_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${NUMA_LIBRARIES})
|
||||
list(APPEND THIRDPARTY_LIBS NUMA::NUMA)
|
||||
endif()
|
||||
|
||||
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})
|
||||
list(APPEND THIRDPARTY_LIBS TBB::TBB)
|
||||
endif()
|
||||
|
||||
# Stall notifications eat some performance from inserts
|
||||
@@ -478,7 +469,10 @@ endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.7.0/fused-src)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.8.1/fused-src)
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# Main library source code
|
||||
@@ -487,6 +481,7 @@ set(SOURCES
|
||||
cache/clock_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
db/column_family.cc
|
||||
@@ -520,6 +515,7 @@ set(SOURCES
|
||||
db/flush_job.cc
|
||||
db/flush_scheduler.cc
|
||||
db/forward_iterator.cc
|
||||
db/import_column_family_job.cc
|
||||
db/internal_stats.cc
|
||||
db/logs_with_prep_tracker.cc
|
||||
db/log_reader.cc
|
||||
@@ -536,6 +532,7 @@ set(SOURCES
|
||||
db/table_cache.cc
|
||||
db/table_properties_collector.cc
|
||||
db/transaction_log_impl.cc
|
||||
db/trim_history_scheduler.cc
|
||||
db/version_builder.cc
|
||||
db/version_edit.cc
|
||||
db/version_set.cc
|
||||
@@ -550,9 +547,15 @@ set(SOURCES
|
||||
env/env_hdfs.cc
|
||||
env/mock_env.cc
|
||||
file/delete_scheduler.cc
|
||||
file/file_prefetch_buffer.cc
|
||||
file/file_util.cc
|
||||
file/filename.cc
|
||||
file/random_access_file_reader.cc
|
||||
file/read_write_util.cc
|
||||
file/readahead_raf.cc
|
||||
file/sequence_file_reader.cc
|
||||
file/sst_file_manager_impl.cc
|
||||
file/writable_file_writer.cc
|
||||
logging/auto_roll_logger.cc
|
||||
logging/event_logger.cc
|
||||
logging/log_buffer.cc
|
||||
@@ -595,12 +598,13 @@ set(SOURCES
|
||||
table/block_based/block_prefix_index.cc
|
||||
table/block_based/data_block_hash_index.cc
|
||||
table/block_based/data_block_footer.cc
|
||||
table/block_based/filter_block_reader_common.cc
|
||||
table/block_based/flush_block_policy.cc
|
||||
table/block_based/full_filter_block.cc
|
||||
table/block_based/index_builder.cc
|
||||
table/block_based/partitioned_filter_block.cc
|
||||
table/block_based/uncompression_dict_reader.cc
|
||||
table/block_fetcher.cc
|
||||
table/bloom_block.cc
|
||||
table/cuckoo/cuckoo_table_builder.cc
|
||||
table/cuckoo/cuckoo_table_factory.cc
|
||||
table/cuckoo/cuckoo_table_reader.cc
|
||||
@@ -610,6 +614,7 @@ set(SOURCES
|
||||
table/merging_iterator.cc
|
||||
table/meta_blocks.cc
|
||||
table/persistent_cache_helper.cc
|
||||
table/plain/plain_table_bloom.cc
|
||||
table/plain/plain_table_builder.cc
|
||||
table/plain/plain_table_factory.cc
|
||||
table/plain/plain_table_index.cc
|
||||
@@ -623,7 +628,7 @@ set(SOURCES
|
||||
test_util/sync_point_impl.cc
|
||||
test_util/testutil.cc
|
||||
test_util/transaction_test_util.cc
|
||||
tools/block_cache_trace_analyzer.cc
|
||||
tools/block_cache_analyzer/block_cache_trace_analyzer.cc
|
||||
tools/db_bench_tool.cc
|
||||
tools/dump/db_dump_tool.cc
|
||||
tools/ldb_cmd.cc
|
||||
@@ -640,7 +645,6 @@ set(SOURCES
|
||||
util/concurrent_task_limiter_impl.cc
|
||||
util/crc32c.cc
|
||||
util/dynamic_bloom.cc
|
||||
util/file_reader_writer.cc
|
||||
util/filter_policy.cc
|
||||
util/hash.cc
|
||||
util/murmurhash.cc
|
||||
@@ -675,9 +679,11 @@ set(SOURCES
|
||||
utilities/merge_operators/bytesxor.cc
|
||||
utilities/merge_operators/max.cc
|
||||
utilities/merge_operators/put.cc
|
||||
utilities/merge_operators/sortlist.cc
|
||||
utilities/merge_operators/string_append/stringappend.cc
|
||||
utilities/merge_operators/string_append/stringappend2.cc
|
||||
utilities/merge_operators/uint64add.cc
|
||||
utilities/object_registry.cc
|
||||
utilities/option_change_migration/option_change_migration.cc
|
||||
utilities/options/options_util.cc
|
||||
utilities/persistent_cache/block_cache_tier.cc
|
||||
@@ -685,6 +691,7 @@ set(SOURCES
|
||||
utilities/persistent_cache/block_cache_tier_metadata.cc
|
||||
utilities/persistent_cache/persistent_cache_tier.cc
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/simulator_cache/cache_simulator.cc
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
@@ -749,6 +756,15 @@ else()
|
||||
env/io_posix.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
list(APPEND SOURCES
|
||||
third-party/folly/folly/detail/Futex.cpp
|
||||
third-party/folly/folly/synchronization/AtomicNotification.cpp
|
||||
third-party/folly/folly/synchronization/DistributedMutex.cpp
|
||||
third-party/folly/folly/synchronization/ParkingLot.cpp
|
||||
third-party/folly/folly/synchronization/WaitOptions.cpp)
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_SHARED_LIB rocksdb-shared${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_IMPORT_LIB ${ROCKSDB_SHARED_LIB})
|
||||
@@ -772,8 +788,8 @@ else()
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
set_target_properties(${ROCKSDB_SHARED_LIB} PROPERTIES
|
||||
LINKER_LANGUAGE CXX
|
||||
VERSION ${ROCKSDB_VERSION}
|
||||
SOVERSION ${ROCKSDB_VERSION_MAJOR}
|
||||
VERSION ${rocksdb_VERSION}
|
||||
SOVERSION ${rocksdb_VERSION_MAJOR}
|
||||
CXX_STANDARD 11
|
||||
OUTPUT_NAME "rocksdb")
|
||||
endif()
|
||||
@@ -828,7 +844,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
|
||||
write_basic_package_version_file(
|
||||
RocksDBConfigVersion.cmake
|
||||
VERSION ${ROCKSDB_VERSION}
|
||||
VERSION ${rocksdb_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
@@ -870,7 +886,7 @@ endif()
|
||||
|
||||
option(WITH_TESTS "build with tests" ON)
|
||||
if(WITH_TESTS)
|
||||
add_subdirectory(third-party/gtest-1.7.0/fused-src/gtest)
|
||||
add_subdirectory(third-party/gtest-1.8.1/fused-src/gtest)
|
||||
set(TESTS
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
@@ -899,6 +915,7 @@ if(WITH_TESTS)
|
||||
db/db_log_iter_test.cc
|
||||
db/db_memtable_test.cc
|
||||
db/db_merge_operator_test.cc
|
||||
db/db_merge_operand_test.cc
|
||||
db/db_options_test.cc
|
||||
db/db_properties_test.cc
|
||||
db/db_range_del_test.cc
|
||||
@@ -948,6 +965,7 @@ if(WITH_TESTS)
|
||||
env/mock_env_test.cc
|
||||
file/delete_scheduler_test.cc
|
||||
logging/auto_roll_logger_test.cc
|
||||
logging/env_logger_test.cc
|
||||
logging/event_logger_test.cc
|
||||
memory/arena_test.cc
|
||||
memtable/inlineskiplist_test.cc
|
||||
@@ -970,7 +988,7 @@ if(WITH_TESTS)
|
||||
table/merger_test.cc
|
||||
table/sst_file_reader_test.cc
|
||||
table/table_test.cc
|
||||
tools/block_cache_trace_analyzer_test.cc
|
||||
tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
tools/sst_dump_test.cc
|
||||
@@ -1004,6 +1022,7 @@ if(WITH_TESTS)
|
||||
utilities/options/options_util_test.cc
|
||||
utilities/persistent_cache/hash_table_test.cc
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/simulator_cache/cache_simulator_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
@@ -1017,12 +1036,17 @@ if(WITH_TESTS)
|
||||
list(APPEND TESTS utilities/env_librados_test.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
list(APPEND TESTS third-party/folly/folly/synchronization/test/DistributedMutexTest.cpp)
|
||||
endif()
|
||||
|
||||
set(BENCHMARKS
|
||||
cache/cache_bench.cc
|
||||
memtable/memtablerep_bench.cc
|
||||
db/range_del_aggregator_bench.cc
|
||||
tools/db_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
util/filter_bench.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
add_library(testharness OBJECT test_util/testharness.cc)
|
||||
foreach(sourcefile ${BENCHMARKS})
|
||||
|
||||
+75
-1
@@ -1,3 +1,77 @@
|
||||
# Code of Conduct
|
||||
|
||||
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at <opensource-conduct@fb.com>. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
|
||||
|
||||
+88
-2
@@ -1,24 +1,108 @@
|
||||
test
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Bug Fixes
|
||||
* Fixed a number of data races in BlobDB.
|
||||
* Fix a bug where the compaction snapshot refresh feature is not disabled as advertised when `snap_refresh_nanos` is set to 0..
|
||||
* Fix bloom filter lookups by the MultiGet batching API when BlockBasedTableOptions::whole_key_filtering is false, by checking that a key is in the perfix_extractor domain and extracting the prefix before looking up.
|
||||
* Fix a bug in file ingestion caused by incorrect file number allocation when the number of column families involved in the ingestion exceeds 2.
|
||||
* Fix a bug when format_version=3, partitioned fitlers, and prefix search are used in conjunction. The bug could result into Seek::(prefix) returning NotFound for an existing prefix.
|
||||
* Revert the feature "Merging iterator to avoid child iterator reseek for some cases (#5286)" since it might cause strong results when reseek happens with a different iterator upper bound.
|
||||
* Fix a bug causing a crash during ingest external file when background compaction cause severe error (file not found).
|
||||
* Fix a bug when partitioned filters and prefix search are used in conjunction, ::SeekForPrev could return invalid for an existing prefix. ::SeekForPrev might be called by the user, or internally on ::Prev, or within ::Seek if the return value involves Delete or a Merge operand.
|
||||
* Fix OnFlushCompleted fired before flush result persisted in MANIFEST when there's concurrent flush job. The bug exists since OnFlushCompleted was introduced in rocksdb 3.8.
|
||||
### New Features
|
||||
* Introduced DBOptions::max_write_batch_group_size_bytes to configure maximum limit on number of bytes that are written in a single batch of WAL or memtable write. It is followed when the leader write size is larger than 1/8 of this limit.
|
||||
* VerifyChecksum() by default will issue readahead. Allow ReadOptions to be passed in to those functions to override the readhead size. For checksum verifying before external SST file ingestion, a new option IngestExternalFileOptions.verify_checksums_readahead_size, is added for this readahead setting.
|
||||
* When user uses options.force_consistency_check in RocksDb, instead of crashing the process, we now pass the error back to the users without killing the process.
|
||||
* Add an option `memtable_insert_hint_per_batch` to WriteOptions. If it is true, each WriteBatch will maintain its own insert hints for each memtable in concurrent write. See include/rocksdb/options.h for more details.
|
||||
* The `sst_dump` command line tool `recompress` command now displays how many blocks were compressed and how many were not, in particular how many were not compressed because the compression ratio was not met (12.5% threshold for GoodCompressionRatio), as seen in the `number.block.not_compressed` counter stat since version 6.0.0.
|
||||
* The block cache usage is now takes into account the overhead of metadata per each entry. This results into more accurate managment of memory. A side-effect of this feature is that less items are fit into the block cache of the same size, which would result to higher cache miss rates. This can be remedied by increasing the block cache size or passing kDontChargeCacheMetadata to its constuctor to restore the old behavior.
|
||||
### Public API Change
|
||||
* Added max_write_buffer_size_to_maintain option to better control memory usage of immutable memtables.
|
||||
* Added a lightweight API GetCurrentWalFile() to get last live WAL filename and size. Meant to be used as a helper for backup/restore tooling in a larger ecosystem such as MySQL with a MyRocks storage engine.
|
||||
* The MemTable Bloom filter, when enabled, now always uses cache locality. Options::bloom_locality now only affects the PlainTable SST format.
|
||||
* Deprecate `snap_refresh_nanos` option.
|
||||
* Added DisableManualCompaction/EnableManualCompaction to stop and resume manual compaction.
|
||||
* Add TryCatchUpWithPrimary() to StackableDB in non-LITE mode.
|
||||
* Add a new Env::LoadEnv() overloaded function to return a shared_ptr to Env.
|
||||
* Flush sets file name to "(nil)" for OnTableFileCreationCompleted() if the flush does not produce any L0. This can happen if the file is empty thus delete by RocksDB.
|
||||
### Performance Improvements
|
||||
* Improve the speed of the MemTable Bloom filter, reducing the write overhead of enabling it by 1/3 to 1/2, with similar benefit to read performance.
|
||||
|
||||
## 6.4.0 (7/30/2019)
|
||||
### Default Option Change
|
||||
* LRUCacheOptions.high_pri_pool_ratio is set to 0.5 (previously 0.0) by default, which means that by default midpoint insertion is enabled. The same change is made for the default value of high_pri_pool_ratio argument in NewLRUCache(). When block cache is not explictly created, the small block cache created by BlockBasedTable will still has this option to be 0.0.
|
||||
* Change BlockBasedTableOptions.cache_index_and_filter_blocks_with_high_priority's default value from false to true.
|
||||
|
||||
### Public API Change
|
||||
* Filter and compression dictionary blocks are now handled similarly to data blocks with regards to the block cache: instead of storing objects in the cache, only the blocks themselves are cached. In addition, filter and compression dictionary blocks (as well as filter partitions) no longer get evicted from the cache when a table is closed.
|
||||
* Due to the above refactoring, block cache eviction statistics for filter and compression dictionary blocks are temporarily broken. We plan to reintroduce them in a later phase.
|
||||
* The semantics of the per-block-type block read counts in the performance context now match those of the generic block_read_count.
|
||||
* Errors related to the retrieval of the compression dictionary are now propagated to the user.
|
||||
* db_bench adds a "benchmark" stats_history, which prints out the whole stats history.
|
||||
* Overload GetAllKeyVersions() to support non-default column family.
|
||||
* Added new APIs ExportColumnFamily() and CreateColumnFamilyWithImport() to support export and import of a Column Family. https://github.com/facebook/rocksdb/issues/3469
|
||||
* ldb sometimes uses a string-append merge operator if no merge operator is passed in. This is to allow users to print keys from a DB with a merge operator.
|
||||
* Replaces old Registra with ObjectRegistry to allow user to create custom object from string, also add LoadEnv() to Env.
|
||||
* Added new overload of GetApproximateSizes which gets SizeApproximationOptions object and returns a Status. The older overloads are redirecting their calls to this new method and no longer assert if the include_flags doesn't have either of INCLUDE_MEMTABLES or INCLUDE_FILES bits set. It's recommended to use the new method only, as it is more type safe and returns a meaningful status in case of errors.
|
||||
* LDBCommandRunner::RunCommand() to return the status code as an integer, rather than call exit() using the code.
|
||||
|
||||
### New Features
|
||||
* Add argument `--secondary_path` to ldb to open the database as the secondary instance. This would keep the original DB intact.
|
||||
* Compression dictionary blocks are now prefetched and pinned in the cache (based on the customer's settings) the same way as index and filter blocks.
|
||||
* Added DBOptions::log_readahead_size which specifies the number of bytes to prefetch when reading the log. This is mostly useful for reading a remotely located log, as it can save the number of round-trips. If 0 (default), then the prefetching is disabled.
|
||||
* Added new option in SizeApproximationOptions used with DB::GetApproximateSizes. When approximating the files total size that is used to store a keys range, allow approximation with an error margin of up to total_files_size * files_size_error_margin. This allows to take some shortcuts in files size approximation, resulting in better performance, while guaranteeing the resulting error is within a reasonable margin.
|
||||
* Support loading custom objects in unit tests. In the affected unit tests, RocksDB will create custom Env objects based on environment variable TEST_ENV_URI. Users need to make sure custom object types are properly registered. For example, a static library should expose a `RegisterCustomObjects` function. By linking the unit test binary with the static library, the unit test can execute this function.
|
||||
|
||||
### Performance Improvements
|
||||
* Reduce iterator key comparision for upper/lower bound check.
|
||||
* Improve performance of row_cache: make reads with newer snapshots than data in an SST file share the same cache key, except in some transaction cases.
|
||||
* The compression dictionary is no longer copied to a new object upon retrieval.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix ingested file and directory not being fsync.
|
||||
* Return TryAgain status in place of Corruption when new tail is not visible to TransactionLogIterator.
|
||||
* Fixed a regression where the fill_cache read option also affected index blocks.
|
||||
* Fixed an issue where using cache_index_and_filter_blocks==false affected partitions of partitioned indexes/filters as well.
|
||||
|
||||
## 6.3.2 (8/15/2019)
|
||||
### Public API Change
|
||||
* The semantics of the per-block-type block read counts in the performance context now match those of the generic block_read_count.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a regression where the fill_cache read option also affected index blocks.
|
||||
* Fixed an issue where using cache_index_and_filter_blocks==false affected partitions of partitioned indexes as well.
|
||||
|
||||
## 6.3.1 (7/24/2019)
|
||||
### Bug Fixes
|
||||
* Fix auto rolling bug introduced in 6.3.0, which causes segfault if log file creation fails.
|
||||
|
||||
## 6.3.0 (6/18/2019)
|
||||
### Public API Change
|
||||
* Now DB::Close() will return Aborted() error when there is unreleased snapshot. Users can retry after all snapshots are released.
|
||||
* Index blocks are now handled similarly to data blocks with regards to the block cache: instead of storing objects in the cache, only the blocks themselves are cached. In addition, index blocks no longer get evicted from the cache when a table is closed, can now use the compressed block cache (if any), and can be shared among multiple table readers.
|
||||
* Partitions of partitioned indexes no longer affect the read amplification statistics.
|
||||
* Due to a refactoring, block cache eviction statistics for indexes are temporarily broken. We plan to reintroduce them in a later phase.
|
||||
* Due to the above refactoring, block cache eviction statistics for indexes are temporarily broken. We plan to reintroduce them in a later phase.
|
||||
* options.keep_log_file_num will be enforced strictly all the time. File names of all log files will be tracked, which may take significantly amount of memory if options.keep_log_file_num is large and either of options.max_log_file_size or options.log_file_time_to_roll is set.
|
||||
* Add initial support for Get/Put with user timestamps. Users can specify timestamps via ReadOptions and WriteOptions when calling DB::Get and DB::Put.
|
||||
* Accessing a partition of a partitioned filter or index through a pinned reference is no longer considered a cache hit.
|
||||
* Add C bindings for secondary instance, i.e. DBImplSecondary.
|
||||
* Rate limited deletion of WALs is only enabled if DBOptions::wal_dir is not set, or explicitly set to db_name passed to DB::Open and DBOptions::db_paths is empty, or same as db_paths[0].path
|
||||
|
||||
### New Features
|
||||
* Add an option `snap_refresh_nanos` (default to 0.1s) to periodically refresh the snapshot list in compaction jobs. Assign to 0 to disable the feature.
|
||||
* Add an option `snap_refresh_nanos` (default to 0) to periodically refresh the snapshot list in compaction jobs. Assign to 0 to disable the feature.
|
||||
* Add an option `unordered_write` which trades snapshot guarantees with higher write throughput. When used with WRITE_PREPARED transactions with two_write_queues=true, it offers higher throughput with however no compromise on guarantees.
|
||||
* Allow DBImplSecondary to remove memtables with obsolete data after replaying MANIFEST and WAL.
|
||||
* Add an option `failed_move_fall_back_to_copy` (default is true) for external SST ingestion. When `move_files` is true and hard link fails, ingestion falls back to copy if `failed_move_fall_back_to_copy` is true. Otherwise, ingestion reports an error.
|
||||
* Add command `list_file_range_deletes` in ldb, which prints out tombstones in SST files.
|
||||
|
||||
### Performance Improvements
|
||||
* Reduce binary search when iterator reseek into the same data block.
|
||||
* DBIter::Next() can skip user key checking if previous entry's seqnum is 0.
|
||||
* Merging iterator to avoid child iterator reseek for some cases
|
||||
* Log Writer will flush after finishing the whole record, rather than a fragment.
|
||||
* Lower MultiGet batching API latency by reading data blocks from disk in parallel
|
||||
|
||||
### General Improvements
|
||||
* Added new status code kColumnFamilyDropped to distinguish between Column Family Dropped and DB Shutdown in progress.
|
||||
@@ -29,6 +113,7 @@
|
||||
* Fix flush's/compaction's merge processing logic which allowed `Put`s covered by range tombstones to reappear. Note `Put`s may exist even if the user only ever called `Merge()` due to an internal conversion during compaction to the bottommost level.
|
||||
* Fix/improve memtable earliest sequence assignment and WAL replay so that WAL entries of unflushed column families will not be skipped after replaying the MANIFEST and increasing db sequence due to another flushed/compacted column family.
|
||||
* Fix a bug caused by secondary not skipping the beginning of new MANIFEST.
|
||||
* On DB open, delete WAL trash files left behind in wal_dir
|
||||
|
||||
## 6.2.0 (4/30/2019)
|
||||
### New Features
|
||||
@@ -38,6 +123,7 @@
|
||||
* Block-based table index now contains exact highest key in the file, rather than an upper bound. This may improve Get() and iterator Seek() performance in some situations, especially when direct IO is enabled and block cache is disabled. A setting BlockBasedTableOptions::index_shortening is introduced to control this behavior. Set it to kShortenSeparatorsAndSuccessor to get the old behavior.
|
||||
* When reading from option file/string/map, customized envs can be filled according to object registry.
|
||||
* Improve range scan performance when using explicit user readahead by not creating new table readers for every iterator.
|
||||
* Add index type BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey. It significantly reduces read amplification in some setups, especially for iterator seeks. It's not fully implemented yet: IO errors are not handled right.
|
||||
|
||||
### Public API Change
|
||||
* Change the behavior of OptimizeForPointLookup(): move away from hash-based block-based-table index, and use whole key memtable filtering.
|
||||
|
||||
@@ -89,7 +89,7 @@ endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticreleasedocker)
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
DEBUG_LEVEL=0
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -143,9 +143,9 @@ CFLAGS += -DHAVE_POWER8
|
||||
HAVE_POWER8=1
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -march=armv8-a+crc -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -march=armv8-a+crc
|
||||
CFLAGS += -march=armv8-a+crc
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -march=armv8-a+crc+crypto -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -march=armv8-a+crc+crypto
|
||||
CFLAGS += -march=armv8-a+crc+crypto
|
||||
ARMCRC_SOURCE=1
|
||||
endif
|
||||
|
||||
@@ -256,8 +256,8 @@ endif
|
||||
ifdef COMPILE_WITH_TSAN
|
||||
DISABLE_JEMALLOC=1
|
||||
EXEC_LDFLAGS += -fsanitize=thread
|
||||
PLATFORM_CCFLAGS += -fsanitize=thread -fPIC
|
||||
PLATFORM_CXXFLAGS += -fsanitize=thread -fPIC
|
||||
PLATFORM_CCFLAGS += -fsanitize=thread -fPIC -DFOLLY_SANITIZE_THREAD
|
||||
PLATFORM_CXXFLAGS += -fsanitize=thread -fPIC -DFOLLY_SANITIZE_THREAD
|
||||
# Turn off -pg when enabling TSAN testing, because that induces
|
||||
# a link failure. TODO: find the root cause
|
||||
PROFILING_FLAGS =
|
||||
@@ -304,9 +304,13 @@ ifndef DISABLE_JEMALLOC
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE)
|
||||
endif
|
||||
|
||||
ifndef USE_FOLLY_DISTRIBUTED_MUTEX
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=0
|
||||
endif
|
||||
|
||||
export GTEST_THROW_ON_FAILURE=1
|
||||
export GTEST_HAS_EXCEPTIONS=1
|
||||
GTEST_DIR = ./third-party/gtest-1.7.0/fused-src
|
||||
GTEST_DIR = ./third-party/gtest-1.8.1/fused-src
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(GTEST_DIR)
|
||||
@@ -316,6 +320,23 @@ else
|
||||
PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
|
||||
endif
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
FOLLY_DIR = ./third-party/folly
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(FOLLY_DIR)
|
||||
PLATFORM_CXXFLAGS += -I$(FOLLY_DIR)
|
||||
else
|
||||
PLATFORM_CCFLAGS += -isystem $(FOLLY_DIR)
|
||||
PLATFORM_CXXFLAGS += -isystem $(FOLLY_DIR)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef TEST_CACHE_LINE_SIZE
|
||||
PLATFORM_CCFLAGS += -DTEST_CACHE_LINE_SIZE=$(TEST_CACHE_LINE_SIZE)
|
||||
PLATFORM_CXXFLAGS += -DTEST_CACHE_LINE_SIZE=$(TEST_CACHE_LINE_SIZE)
|
||||
endif
|
||||
|
||||
# This (the first rule) must depend on "all".
|
||||
default: all
|
||||
|
||||
@@ -402,6 +423,9 @@ endif
|
||||
|
||||
LIBOBJECTS += $(TOOL_LIB_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_LIB_SOURCES:.cc=.o)
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
FOLLYOBJECTS = $(FOLLY_SOURCES:.cpp=.o)
|
||||
endif
|
||||
|
||||
GTEST = $(GTEST_DIR)/gtest/gtest-all.o
|
||||
TESTUTIL = ./test_util/testutil.o
|
||||
@@ -432,6 +456,7 @@ TESTS = \
|
||||
inlineskiplist_test \
|
||||
env_basic_test \
|
||||
env_test \
|
||||
env_logger_test \
|
||||
hash_test \
|
||||
thread_local_test \
|
||||
rate_limiter_test \
|
||||
@@ -441,10 +466,10 @@ TESTS = \
|
||||
db_block_cache_test \
|
||||
db_test \
|
||||
db_blob_index_test \
|
||||
db_bloom_filter_test \
|
||||
db_iter_test \
|
||||
db_iter_stress_test \
|
||||
db_log_iter_test \
|
||||
db_bloom_filter_test \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_dynamic_level_test \
|
||||
@@ -453,6 +478,7 @@ TESTS = \
|
||||
db_iterator_test \
|
||||
db_memtable_test \
|
||||
db_merge_operator_test \
|
||||
db_merge_operand_test \
|
||||
db_options_test \
|
||||
db_range_del_test \
|
||||
db_secondary_test \
|
||||
@@ -499,6 +525,7 @@ TESTS = \
|
||||
plain_table_db_test \
|
||||
comparator_db_test \
|
||||
external_sst_file_test \
|
||||
import_column_family_test \
|
||||
prefix_test \
|
||||
skiplist_test \
|
||||
write_buffer_manager_test \
|
||||
@@ -509,6 +536,7 @@ TESTS = \
|
||||
cassandra_serialize_test \
|
||||
ttl_test \
|
||||
backupable_db_test \
|
||||
cache_simulator_test \
|
||||
sim_cache_test \
|
||||
version_edit_test \
|
||||
version_set_test \
|
||||
@@ -565,8 +593,13 @@ TESTS = \
|
||||
block_cache_tracer_test \
|
||||
block_cache_trace_analyzer_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
db_bloom_filter_test \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_merge_operator_test \
|
||||
@@ -575,7 +608,9 @@ PARALLEL_TEST = \
|
||||
db_universal_compaction_test \
|
||||
db_wal_test \
|
||||
external_sst_file_test \
|
||||
import_column_family_test \
|
||||
fault_injection_test \
|
||||
file_reader_writer_test \
|
||||
inlineskiplist_test \
|
||||
manual_compaction_test \
|
||||
persistent_cache_test \
|
||||
@@ -614,7 +649,7 @@ TEST_LIBS = \
|
||||
librocksdb_env_basic_test.a
|
||||
|
||||
# TODO: add back forward_iterator_bench, after making it build in all environemnts.
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench persistent_cache_bench range_del_aggregator_bench
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench filter_bench persistent_cache_bench range_del_aggregator_bench
|
||||
|
||||
# if user didn't config LIBNAME, set the default
|
||||
ifeq ($(LIBNAME),)
|
||||
@@ -920,7 +955,7 @@ blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_atomic_flush: db_stress
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
@@ -933,7 +968,7 @@ whitebox_crash_test: db_stress
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_atomic_flush: db_stress
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush whitebox --random_kill_odd \
|
||||
python -u tools/db_crashtest.py --cf_consistency whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
asan_check:
|
||||
@@ -1064,7 +1099,7 @@ rocksdb.h rocksdb.cc: build_tools/amalgamate.py Makefile $(LIB_SOURCES) unity.cc
|
||||
build_tools/amalgamate.py -I. -i./include unity.cc -x include/rocksdb/c.h -H rocksdb.h -o rocksdb.cc
|
||||
|
||||
clean:
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(LIBRARY) $(SHARED)
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(PARALLEL_TEST) $(LIBRARY) $(SHARED)
|
||||
rm -rf $(CLEAN_FILES) ios-x86 ios-arm scan_build_report
|
||||
$(FIND) . -name "*.[oda]" -exec rm -f {} \;
|
||||
$(FIND) . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm {} \;
|
||||
@@ -1110,9 +1145,14 @@ db_bench: tools/db_bench.o $(BENCHTOOLOBJECTS)
|
||||
trace_analyzer: tools/trace_analyzer.o $(ANALYZETOOLOBJECTS) $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_trace_analyzer: tools/block_cache_trace_analyzer_tool.o $(ANALYZETOOLOBJECTS) $(LIBOBJECTS)
|
||||
block_cache_trace_analyzer: tools/block_cache_analyzer/block_cache_trace_analyzer_tool.o $(ANALYZETOOLOBJECTS) $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
folly_synchronization_distributed_mutex_test: $(LIBOBJECTS) $(TESTHARNESS) $(FOLLYOBJECTS) third-party/folly/folly/synchronization/test/DistributedMutexTest.o
|
||||
$(AM_LINK)
|
||||
endif
|
||||
|
||||
cache_bench: cache/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1122,6 +1162,9 @@ persistent_cache_bench: utilities/persistent_cache/persistent_cache_bench.o $(LI
|
||||
memtablerep_bench: memtable/memtablerep_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
filter_bench: util/filter_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
db_stress: tools/db_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1191,7 +1234,7 @@ histogram_test: monitoring/histogram_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
thread_local_test: util/thread_local_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
corruption_test: db/corruption_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1248,6 +1291,9 @@ db_memtable_test: db/db_memtable_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHA
|
||||
db_merge_operator_test: db/db_merge_operator_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_merge_operand_test: db/db_merge_operand_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_options_test: db/db_options_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1272,6 +1318,9 @@ external_sst_file_basic_test: db/external_sst_file_basic_test.o db/db_test_util.
|
||||
external_sst_file_test: db/external_sst_file_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
import_column_family_test: db/import_column_family_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_tailing_iter_test: db/db_tailing_iter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1320,6 +1369,9 @@ backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TE
|
||||
checkpoint_test: utilities/checkpoint/checkpoint_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_simulator_test: utilities/simulator_cache/cache_simulator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sim_cache_test: utilities/simulator_cache/sim_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1460,10 +1512,10 @@ util_merge_operators_test: utilities/util_merge_operators_test.o $(LIBOBJECTS) $
|
||||
options_file_test: db/options_file_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
deletefile_test: db/deletefile_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
deletefile_test: db/deletefile_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
obsolete_files_test: db/obsolete_files_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
obsolete_files_test: db/obsolete_files_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
rocksdb_dump: tools/dump/rocksdb_dump.o $(LIBOBJECTS)
|
||||
@@ -1529,6 +1581,9 @@ filelock_test: util/filelock_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
auto_roll_logger_test: logging/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_logger_test: logging/env_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memtable_list_test: db/memtable_list_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1601,7 +1656,7 @@ db_secondary_test: db/db_impl/db_secondary_test.o db/db_test_util.o $(LIBOBJECTS
|
||||
block_cache_tracer_test: trace_replay/block_cache_tracer_test.o trace_replay/block_cache_tracer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_trace_analyzer_test: tools/block_cache_trace_analyzer_test.o tools/block_cache_trace_analyzer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
block_cache_trace_analyzer_test: tools/block_cache_analyzer/block_cache_trace_analyzer_test.o tools/block_cache_analyzer/block_cache_trace_analyzer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
@@ -1649,7 +1704,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
else ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ifneq (,$(filter $(MACHINE), amd64 arm64 sparc64 aarch64))
|
||||
ifneq (,$(filter $(MACHINE), amd64 arm64 aarch64 sparc64))
|
||||
ARCH := 64
|
||||
else
|
||||
ARCH := 32
|
||||
@@ -1658,14 +1713,11 @@ else
|
||||
ARCH := $(shell getconf LONG_BIT)
|
||||
endif
|
||||
|
||||
ifeq (,$(findstring ppc,$(MACHINE)))
|
||||
ifeq (,$(filter $(MACHINE), ppc arm64 aarch64 sparc64))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
endif
|
||||
ifneq (,$(findstring aarch64,$(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
endif
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux$(ARCH).jar
|
||||
ROCKSDB_JAR_ALL = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-javadoc.jar
|
||||
@@ -1862,6 +1914,14 @@ rocksdbjavastaticdockerppc64le:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_ARM64V8_CONTAINER=`docker ps -aqf name=rocksdb_linux_arm64v8-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_ARM64V8_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_arm64v8-be evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_arm64v8-be
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
rocksdbjavastaticpublishdocker: rocksdbjavastaticreleasedocker rocksdbjavastaticpublishcentral
|
||||
|
||||
@@ -9,11 +9,11 @@ It is built on earlier work on [LevelDB](https://github.com/google/leveldb) by S
|
||||
and Jeff Dean (jeff@google.com)
|
||||
|
||||
This code is a library that forms the core building block for a fast
|
||||
key value server, especially suited for storing data on flash drives.
|
||||
key-value server, especially suited for storing data on flash drives.
|
||||
It has a Log-Structured-Merge-Database (LSM) design with flexible tradeoffs
|
||||
between Write-Amplification-Factor (WAF), Read-Amplification-Factor (RAF)
|
||||
and Space-Amplification-Factor (SAF). It has multi-threaded compactions,
|
||||
making it specially suitable for storing multiple terabytes of data in a
|
||||
making it especially suitable for storing multiple terabytes of data in a
|
||||
single database.
|
||||
|
||||
Start with example usage here: https://github.com/facebook/rocksdb/tree/master/examples
|
||||
|
||||
@@ -96,3 +96,9 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
|
||||
## IOTA Foundation
|
||||
[IOTA Foundation](https://www.iota.org/) is using RocksDB in the [IOTA Reference Implementation (IRI)](https://github.com/iotaledger/iri) to store the local state of the Tangle. The Tangle is the first open-source distributed ledger powering the future of the Internet of Things.
|
||||
|
||||
## Avrio Project
|
||||
[Avrio Project](http://avrio-project.github.io/avrio.network/) is using RocksDB in [Avrio ](https://github.com/avrio-project/avrio) to store blocks, account balances and data and other blockchain-releated data. Avrio is a multiblockchain decentralized cryptocurrency empowering monetary transactions.
|
||||
|
||||
## Crux
|
||||
[Crux](https://github.com/juxt/crux) is a document database that uses RocksDB for local [EAV](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model) index storage to enable point-in-time bitemporal Datalog queries. The "unbundled" architecture uses Kafka to provide horizontal scalability.
|
||||
|
||||
+59
-7
@@ -1,15 +1,67 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
|
||||
THIRDPARTY_HOME: $(APPVEYOR_BUILD_FOLDER)\thirdparty
|
||||
SNAPPY_HOME: $(THIRDPARTY_HOME)\snappy-1.1.7
|
||||
SNAPPY_INCLUDE: $(SNAPPY_HOME);$(SNAPPY_HOME)\build
|
||||
SNAPPY_LIB_DEBUG: $(SNAPPY_HOME)\build\Debug\snappy.lib
|
||||
SNAPPY_LIB_RELEASE: $(SNAPPY_HOME)\build\Release\snappy.lib
|
||||
LZ4_HOME: $(THIRDPARTY_HOME)\lz4-1.8.3
|
||||
LZ4_INCLUDE: $(LZ4_HOME)\lib
|
||||
LZ4_LIB_DEBUG: $(LZ4_HOME)\visual\VS2010\bin\x64_Debug\liblz4_static.lib
|
||||
LZ4_LIB_RELEASE: $(LZ4_HOME)\visual\VS2010\bin\x64_Release\liblz4_static.lib
|
||||
ZSTD_HOME: $(THIRDPARTY_HOME)\zstd-1.4.0
|
||||
ZSTD_INCLUDE: $(ZSTD_HOME)\lib;$(ZSTD_HOME)\lib\dictBuilder
|
||||
ZSTD_LIB_DEBUG: $(ZSTD_HOME)\build\VS2010\bin\x64_Debug\libzstd_static.lib
|
||||
ZSTD_LIB_RELEASE: $(ZSTD_HOME)\build\VS2010\bin\x64_Release\libzstd_static.lib
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
- echo "Building Snappy dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o snappy-1.1.7.zip https://github.com/google/snappy/archive/1.1.7.zip
|
||||
- unzip snappy-1.1.7.zip
|
||||
- cd snappy-1.1.7
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
|
||||
- msbuild Snappy.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild Snappy.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building LZ4 dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o lz4-1.8.3.zip https://github.com/lz4/lz4/archive/v1.8.3.zip
|
||||
- unzip lz4-1.8.3.zip
|
||||
- cd lz4-1.8.3\visual\VS2010
|
||||
- ps: $CMD="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com"; & $CMD lz4.sln /upgrade
|
||||
- msbuild lz4.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild lz4.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building ZStd dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o zstd-1.4.0.zip https://github.com/facebook/zstd/archive/v1.4.0.zip
|
||||
- unzip zstd-1.4.0.zip
|
||||
- cd zstd-1.4.0\build\VS2010
|
||||
- ps: $CMD="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com"; & $CMD zstd.sln /upgrade
|
||||
- msbuild zstd.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild zstd.sln /p:Configuration=Release /p:Platform=x64
|
||||
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
parallel: true
|
||||
verbosity: normal
|
||||
test:
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
test:
|
||||
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test,db_merge_operand_test -Concurrency 8
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a build-failed.zip %APPVEYOR_BUILD_FOLDER%\build\ && appveyor PushArtifact build-failed.zip
|
||||
|
||||
|
||||
@@ -4,12 +4,31 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from targets_builder import TARGETSBuilder
|
||||
import json
|
||||
import os
|
||||
import fnmatch
|
||||
import sys
|
||||
|
||||
from util import ColorString
|
||||
|
||||
# This script generates TARGETS file for Buck.
|
||||
# Buck is a build tool specifying dependencies among different build targets.
|
||||
# User can pass extra dependencies as a JSON object via command line, and this
|
||||
# script can include these dependencies in the generate TARGETS file.
|
||||
# Usage:
|
||||
# $python buckifier/buckify_rocksdb.py
|
||||
# (This generates a TARGET file without user-specified dependency for unit
|
||||
# tests.)
|
||||
# $python buckifier/buckify_rocksdb.py \
|
||||
# '{"fake": { \
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"], \
|
||||
# "extra_compiler_flags": ["-DROCKSDB_LITE", "-Os"], \
|
||||
# } \
|
||||
# }'
|
||||
# (Generated TARGETS file has test_dep and mock1 as dependencies for RocksDB
|
||||
# unit tests, and will use the extra_compiler_flags to compile the unit test
|
||||
# source.)
|
||||
|
||||
# tests to export as libraries for inclusion in other projects
|
||||
_EXPORTED_TEST_LIBS = ["env_basic_test"]
|
||||
|
||||
@@ -86,8 +105,38 @@ def get_tests(repo_path):
|
||||
return tests
|
||||
|
||||
|
||||
# Parse extra dependencies passed by user from command line
|
||||
def get_dependencies():
|
||||
deps_map = {
|
||||
''.encode('ascii'): {
|
||||
'extra_deps'.encode('ascii'): [],
|
||||
'extra_compiler_flags'.encode('ascii'): []
|
||||
}
|
||||
}
|
||||
if len(sys.argv) < 2:
|
||||
return deps_map
|
||||
|
||||
def encode_dict(data):
|
||||
rv = {}
|
||||
for k, v in data.items():
|
||||
if isinstance(k, unicode):
|
||||
k = k.encode('ascii')
|
||||
if isinstance(v, unicode):
|
||||
v = v.encode('ascii')
|
||||
elif isinstance(v, list):
|
||||
v = [x.encode('ascii') for x in v]
|
||||
elif isinstance(v, dict):
|
||||
v = encode_dict(v)
|
||||
rv[k] = v
|
||||
return rv
|
||||
extra_deps = json.loads(sys.argv[1], object_hook=encode_dict)
|
||||
for target_alias, deps in extra_deps.items():
|
||||
deps_map[target_alias] = deps
|
||||
return deps_map
|
||||
|
||||
|
||||
# Prepare TARGETS file for buck
|
||||
def generate_targets(repo_path):
|
||||
def generate_targets(repo_path, deps_map):
|
||||
print(ColorString.info("Generating TARGETS"))
|
||||
# parsed src.mk file
|
||||
src_mk = parse_src_mk(repo_path)
|
||||
@@ -121,24 +170,33 @@ def generate_targets(repo_path):
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
|
||||
print("Extra dependencies:\n{0}".format(str(deps_map)))
|
||||
# test for every test we found in the Makefile
|
||||
for test in sorted(tests):
|
||||
match_src = [src for src in cc_files if ("/%s.c" % test) in src]
|
||||
if len(match_src) == 0:
|
||||
print(ColorString.warning("Cannot find .cc file for %s" % test))
|
||||
continue
|
||||
elif len(match_src) > 1:
|
||||
print(ColorString.warning("Found more than one .cc for %s" % test))
|
||||
print(match_src)
|
||||
continue
|
||||
for target_alias, deps in deps_map.items():
|
||||
for test in sorted(tests):
|
||||
match_src = [src for src in cc_files if ("/%s.c" % test) in src]
|
||||
if len(match_src) == 0:
|
||||
print(ColorString.warning("Cannot find .cc file for %s" % test))
|
||||
continue
|
||||
elif len(match_src) > 1:
|
||||
print(ColorString.warning("Found more than one .cc for %s" % test))
|
||||
print(match_src)
|
||||
continue
|
||||
|
||||
assert(len(match_src) == 1)
|
||||
is_parallel = tests[test]
|
||||
TARGETS.register_test(test, match_src[0], is_parallel)
|
||||
assert(len(match_src) == 1)
|
||||
is_parallel = tests[test]
|
||||
test_target_name = \
|
||||
test if not target_alias else test + "_" + target_alias
|
||||
TARGETS.register_test(
|
||||
test_target_name,
|
||||
match_src[0],
|
||||
is_parallel,
|
||||
deps['extra_deps'],
|
||||
deps['extra_compiler_flags'])
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test
|
||||
TARGETS.add_library(test_library, match_src, [":rocksdb_test_lib"])
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test_target_name
|
||||
TARGETS.add_library(test_library, match_src, [":rocksdb_test_lib"])
|
||||
TARGETS.flush_tests()
|
||||
|
||||
print(ColorString.info("Generated TARGETS Summary:"))
|
||||
@@ -163,8 +221,9 @@ def exit_with_error(msg):
|
||||
|
||||
|
||||
def main():
|
||||
deps_map = get_dependencies()
|
||||
# Generate TARGETS file for buck
|
||||
ok = generate_targets(get_rocksdb_path())
|
||||
ok = generate_targets(get_rocksdb_path(), deps_map)
|
||||
if not ok:
|
||||
exit_with_error("Failed to generate TARGETS files")
|
||||
|
||||
|
||||
@@ -51,14 +51,21 @@ class TARGETSBuilder:
|
||||
pretty_list(deps)))
|
||||
self.total_bin = self.total_bin + 1
|
||||
|
||||
def register_test(self, test_name, src, is_parallel):
|
||||
def register_test(self,
|
||||
test_name,
|
||||
src,
|
||||
is_parallel,
|
||||
extra_deps,
|
||||
extra_compiler_flags):
|
||||
exec_mode = "serial"
|
||||
if is_parallel:
|
||||
exec_mode = "parallel"
|
||||
self.tests_cfg += targets_cfg.test_cfg_template % (
|
||||
test_name,
|
||||
str(src),
|
||||
str(exec_mode))
|
||||
str(exec_mode),
|
||||
extra_deps,
|
||||
extra_compiler_flags)
|
||||
|
||||
self.total_test = self.total_test + 1
|
||||
|
||||
|
||||
+62
-26
@@ -11,29 +11,9 @@ REPO_PATH = package_name() + "/"
|
||||
|
||||
ROCKSDB_COMPILER_FLAGS = [
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DROCKSDB_SUPPORT_THREAD_LOCAL",
|
||||
"-DOS_LINUX",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DZSTD_STATIC_LINKING_ONLY",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-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",
|
||||
"-Wnarrowing",
|
||||
"-DROCKSDB_NO_DYNAMIC_EXTENSION",
|
||||
]
|
||||
@@ -46,11 +26,54 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("numa", None, "numa"),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
(
|
||||
"linux",
|
||||
["third-party//numa:numa"],
|
||||
),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_PREPROCESSOR_FLAGS = [
|
||||
(
|
||||
"linux",
|
||||
[
|
||||
"-DOS_LINUX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DHAVE_SSE42",
|
||||
"-DNUMA",
|
||||
],
|
||||
),
|
||||
(
|
||||
"macos",
|
||||
["-DOS_MACOSX"],
|
||||
),
|
||||
]
|
||||
|
||||
ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_SUPPORT_THREAD_LOCAL",
|
||||
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DZSTD_STATIC_LINKING_ONLY",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DTBB",
|
||||
|
||||
# Added missing flags from output of build_detect_platform
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
@@ -58,7 +81,6 @@ ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
"x86_64": [
|
||||
"-DHAVE_SSE42",
|
||||
"-DHAVE_PCLMUL",
|
||||
],
|
||||
}
|
||||
@@ -75,9 +97,15 @@ sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DROCKSDB_JEMALLOC"] if sanitizer == "" else [])
|
||||
ROCKSDB_OS_PREPROCESSOR_FLAGS += ([(
|
||||
"linux",
|
||||
["-DROCKSDB_JEMALLOC"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS += ([("jemalloc", None, "headers")] if sanitizer == "" else [])
|
||||
ROCKSDB_OS_DEPS += ([(
|
||||
"linux",
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
"""
|
||||
|
||||
|
||||
@@ -88,6 +116,8 @@ cpp_library(
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [{deps}],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
@@ -110,11 +140,13 @@ test_cfg_template = """ [
|
||||
"%s",
|
||||
"%s",
|
||||
"%s",
|
||||
%s,
|
||||
%s,
|
||||
],
|
||||
"""
|
||||
|
||||
unittests_template = """
|
||||
# [test_name, test_src, test_type]
|
||||
# [test_name, test_src, test_type, extra_deps, extra_compiler_flags]
|
||||
ROCKS_TESTS = [
|
||||
%s]
|
||||
|
||||
@@ -123,15 +155,19 @@ ROCKS_TESTS = [
|
||||
# will not be included.
|
||||
[
|
||||
test_binary(
|
||||
extra_compiler_flags = extra_compiler_flags,
|
||||
extra_deps = extra_deps,
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_os_deps = ROCKSDB_OS_DEPS,
|
||||
rocksdb_os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
)
|
||||
for test_name, test_cc, parallelism in ROCKS_TESTS
|
||||
for test_name, test_cc, parallelism, extra_deps, extra_compiler_flags in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
]
|
||||
"""
|
||||
|
||||
@@ -1,377 +0,0 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. 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).
|
||||
|
||||
// Name of the environment variables which need to be set by the entity which
|
||||
// triggers continuous runs so that code at the end of the file gets executed
|
||||
// and Sandcastle run starts.
|
||||
const ENV_POST_RECEIVE_HOOK = "POST_RECEIVE_HOOK";
|
||||
const ENV_HTTPS_APP_VALUE = "HTTPS_APP_VALUE";
|
||||
const ENV_HTTPS_TOKEN_VALUE = "HTTPS_TOKEN_VALUE";
|
||||
|
||||
const PRIMARY_TOKEN_FILE = '/home/krad/.sandcastle';
|
||||
const CONT_RUN_ALIAS = "leveldb";
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* Run tests in sandcastle */
|
||||
function postURL($diffID, $url) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($url) > 0);
|
||||
|
||||
$cmd_args = array(
|
||||
'diff_id' => (int)$diffID,
|
||||
'name' => sprintf(
|
||||
'click here for sandcastle tests for D%d',
|
||||
(int)$diffID
|
||||
),
|
||||
'link' => $url
|
||||
);
|
||||
$cmd = 'echo ' . escapeshellarg(json_encode($cmd_args))
|
||||
. ' | arc call-conduit differential.updateunitresults';
|
||||
|
||||
shell_exec($cmd);
|
||||
}
|
||||
|
||||
function buildUpdateTestStatusCmd($diffID, $test, $status) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($test) > 0);
|
||||
assert(strlen($status) > 0);
|
||||
|
||||
$cmd_args = array(
|
||||
'diff_id' => (int)$diffID,
|
||||
'name' => $test,
|
||||
'result' => $status
|
||||
);
|
||||
|
||||
$cmd = 'echo ' . escapeshellarg(json_encode($cmd_args))
|
||||
. ' | arc call-conduit differential.updateunitresults';
|
||||
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
function updateTestStatus($diffID, $test) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($test) > 0);
|
||||
|
||||
shell_exec(buildUpdateTestStatusCmd($diffID, $test, "waiting"));
|
||||
}
|
||||
|
||||
function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
assert(strlen($username) > 0);
|
||||
assert(strlen($test) > 0);
|
||||
|
||||
if ($applyDiff) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
|
||||
$arcrc_content = (PHP_OS == "Darwin" ?
|
||||
exec("cat ~/.arcrc | gzip -f | base64") :
|
||||
exec("cat ~/.arcrc | gzip -f | base64 -w0"));
|
||||
assert(strlen($arcrc_content) > 0);
|
||||
|
||||
// Sandcastle machines don't have arc setup. We copy the user certificate
|
||||
// and authenticate using that in Sandcastle.
|
||||
$setup = array(
|
||||
"name" => "Setup arcrc",
|
||||
"shell" => "echo " . escapeshellarg($arcrc_content) . " | base64 --decode"
|
||||
. " | gzip -d > ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// arc demands certain permission on its config.
|
||||
// also fix the sticky bit issue in sandcastle
|
||||
$fix_permission = array(
|
||||
"name" => "Fix environment",
|
||||
"shell" => "chmod 600 ~/.arcrc && chmod +t /dev/shm",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Construct the steps in the order of execution.
|
||||
$steps[] = $setup;
|
||||
$steps[] = $fix_permission;
|
||||
}
|
||||
|
||||
// fbcode is a sub-repo. We cannot patch until we add it to ignore otherwise
|
||||
// Git thinks it is an uncommitted change.
|
||||
$fix_git_ignore = array(
|
||||
"name" => "Fix git ignore",
|
||||
"shell" => "echo fbcode >> .git/info/exclude",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// This fixes "FATAL: ThreadSanitizer can not mmap the shadow memory"
|
||||
// Source:
|
||||
// https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual#FAQ
|
||||
$fix_kernel_issue = array(
|
||||
"name" => "Fix kernel issue with tsan",
|
||||
"shell" => "echo 2 >/proc/sys/kernel/randomize_va_space",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $fix_git_ignore;
|
||||
$steps[] = $fix_kernel_issue;
|
||||
|
||||
// This will be the command used to execute particular type of tests.
|
||||
$cmd = "";
|
||||
|
||||
if ($applyDiff) {
|
||||
// Patch the code (keep your fingures crossed).
|
||||
$patch = array(
|
||||
"name" => "Patch " . $diffID,
|
||||
"shell" => "arc --arcrc-file ~/.arcrc "
|
||||
. "patch --nocommit --diff " . escapeshellarg($diffID),
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $patch;
|
||||
|
||||
updateTestStatus($diffID, $test);
|
||||
$cmd = buildUpdateTestStatusCmd($diffID, $test, "running") . "; ";
|
||||
}
|
||||
|
||||
// Run the actual command.
|
||||
$cmd = $cmd . "J=$(nproc) ./build_tools/precommit_checker.py " .
|
||||
escapeshellarg($test) . "; exit_code=$?; ";
|
||||
|
||||
if ($applyDiff) {
|
||||
$cmd = $cmd . "([[ \$exit_code -eq 0 ]] &&"
|
||||
. buildUpdateTestStatusCmd($diffID, $test, "pass") . ")"
|
||||
. "||" . buildUpdateTestStatusCmd($diffID, $test, "fail")
|
||||
. "; ";
|
||||
}
|
||||
|
||||
// shell command to sort the tests based on exit code and print
|
||||
// the output of the log files.
|
||||
$cat_sorted_logs = "
|
||||
while read code log_file;
|
||||
do echo \"################ cat \$log_file [exit_code : \$code] ################\";
|
||||
cat \$log_file;
|
||||
done < <(tail -n +2 LOG | sort -k7,7n -k4,4gr | awk '{print \$7,\$NF}')";
|
||||
|
||||
// Shell command to cat all log files
|
||||
$cat_all_logs = "for f in `ls t/!(run-*)`; do echo \$f;cat \$f; done";
|
||||
|
||||
// If LOG file exist use it to cat log files sorted by exit code, otherwise
|
||||
// cat everything
|
||||
$logs_cmd = "if [ -f LOG ]; then {$cat_sorted_logs}; else {$cat_all_logs}; fi";
|
||||
|
||||
$cmd = $cmd . " cat /tmp/precommit-check.log"
|
||||
. "; shopt -s extglob; {$logs_cmd}"
|
||||
. "; shopt -u extglob; [[ \$exit_code -eq 0 ]]";
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$run_test = array(
|
||||
"name" => "Run " . $test,
|
||||
"shell" => $cmd,
|
||||
"user" => "root",
|
||||
"parser" => "python build_tools/error_filter.py " . escapeshellarg($test),
|
||||
);
|
||||
|
||||
$steps[] = $run_test;
|
||||
|
||||
if ($applyDiff) {
|
||||
// Clean up the user arc config we are using.
|
||||
$cleanup = array(
|
||||
"name" => "Arc cleanup",
|
||||
"shell" => "rm -f ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $cleanup;
|
||||
}
|
||||
|
||||
assert(count($steps) > 0);
|
||||
return $steps;
|
||||
}
|
||||
|
||||
function getSandcastleConfig() {
|
||||
$sandcastle_config = array();
|
||||
|
||||
$cwd = getcwd();
|
||||
$cwd_token_file = "{$cwd}/.sandcastle";
|
||||
// This is a case when we're executed from a continuous run. Fetch the values
|
||||
// from the environment.
|
||||
if (getenv(ENV_POST_RECEIVE_HOOK)) {
|
||||
$sandcastle_config[0] = getenv(ENV_HTTPS_APP_VALUE);
|
||||
$sandcastle_config[1] = getenv(ENV_HTTPS_TOKEN_VALUE);
|
||||
} else {
|
||||
// This is a typical `[p]arc diff` case. Fetch the values from the specific
|
||||
// configuration files.
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
if (file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists($cwd_token_file)) {
|
||||
break;
|
||||
}
|
||||
// If we failed to fetch the tokens, sleep for 0.2 second and try again
|
||||
usleep(200000);
|
||||
}
|
||||
assert(file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists($cwd_token_file));
|
||||
|
||||
// Try the primary location first, followed by a secondary.
|
||||
if (file_exists(PRIMARY_TOKEN_FILE)) {
|
||||
$cmd = 'cat ' . PRIMARY_TOKEN_FILE;
|
||||
} else {
|
||||
$cmd = 'cat ' . escapeshellarg($cwd_token_file);
|
||||
}
|
||||
|
||||
assert(strlen($cmd) > 0);
|
||||
$sandcastle_config = explode(':', rtrim(shell_exec($cmd)));
|
||||
}
|
||||
|
||||
// In this case be very explicit about the implications.
|
||||
if (count($sandcastle_config) != 2) {
|
||||
echo "Sandcastle configuration files don't contain valid information " .
|
||||
"or the necessary environment variables aren't defined. Unable " .
|
||||
"to validate the code changes.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
assert(strlen($sandcastle_config[0]) > 0);
|
||||
assert(strlen($sandcastle_config[1]) > 0);
|
||||
assert(count($sandcastle_config) > 0);
|
||||
|
||||
return $sandcastle_config;
|
||||
}
|
||||
|
||||
// This function can be called either from `[p]arc diff` command or during
|
||||
// the Git post-receive hook.
|
||||
function startTestsInSandcastle($applyDiff, $workflow, $diffID) {
|
||||
// Default options don't terminate on failure, but that's what we want. In
|
||||
// the current case we use assertions intentionally as "terminate on failure
|
||||
// invariants".
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
|
||||
// In case of a diff we'll send notificatios to the author. Else it'll go to
|
||||
// the entire team because failures indicate that build quality has regressed.
|
||||
$username = $applyDiff ? exec("whoami") : CONT_RUN_ALIAS;
|
||||
assert(strlen($username) > 0);
|
||||
|
||||
if ($applyDiff) {
|
||||
assert($workflow);
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
}
|
||||
|
||||
// List of tests we want to run in Sandcastle.
|
||||
$tests = array("unit", "unit_non_shm", "unit_481", "clang_unit", "tsan",
|
||||
"asan", "lite_test", "valgrind", "release", "release_481",
|
||||
"clang_release", "clang_analyze", "code_cov",
|
||||
"java_build", "no_compression", "unity", "ubsan");
|
||||
|
||||
$send_email_template = array(
|
||||
'type' => 'email',
|
||||
'triggers' => array('fail'),
|
||||
'emails' => array($username . '@fb.com'),
|
||||
);
|
||||
|
||||
// Construct a job definition for each test and add it to the master plan.
|
||||
foreach ($tests as $test) {
|
||||
$stepName = "RocksDB diff " . $diffID . " test " . $test;
|
||||
|
||||
if (!$applyDiff) {
|
||||
$stepName = "RocksDB continuous integration test " . $test;
|
||||
}
|
||||
|
||||
$arg[] = array(
|
||||
"name" => $stepName,
|
||||
"report" => array($send_email_template),
|
||||
"steps" => getSteps($applyDiff, $diffID, $username, $test)
|
||||
);
|
||||
}
|
||||
|
||||
// We cannot submit the parallel execution master plan to Sandcastle and
|
||||
// need supply the job plan as a determinator. So we construct a small job
|
||||
// that will spit out the master job plan which Sandcastle will parse and
|
||||
// execute. Why compress the job definitions? Otherwise we run over the max
|
||||
// string size.
|
||||
$cmd = "echo " . base64_encode(json_encode($arg))
|
||||
. (PHP_OS == "Darwin" ?
|
||||
" | gzip -f | base64" :
|
||||
" | gzip -f | base64 -w0");
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$arg_encoded = shell_exec($cmd);
|
||||
assert(strlen($arg_encoded) > 0);
|
||||
|
||||
$runName = "Run diff " . $diffID . "for user " . $username;
|
||||
|
||||
if (!$applyDiff) {
|
||||
$runName = "RocksDB continuous integration build and test run";
|
||||
}
|
||||
|
||||
$command = array(
|
||||
"name" => $runName,
|
||||
"steps" => array()
|
||||
);
|
||||
|
||||
$command["steps"][] = array(
|
||||
"name" => "Generate determinator",
|
||||
"shell" => "echo " . $arg_encoded . " | base64 --decode | gzip -d"
|
||||
. " | base64 --decode",
|
||||
"determinator" => true,
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Submit to Sandcastle.
|
||||
$url = 'https://interngraph.intern.facebook.com/sandcastle/create';
|
||||
|
||||
$job = array(
|
||||
'command' => 'SandcastleUniversalCommand',
|
||||
'args' => $command,
|
||||
'capabilities' => array(
|
||||
'vcs' => 'rocksdb-int-git',
|
||||
'type' => 'lego',
|
||||
),
|
||||
'hash' => 'origin/master',
|
||||
'user' => $username,
|
||||
'alias' => 'rocksdb-precommit',
|
||||
'tags' => array('rocksdb'),
|
||||
'description' => 'Rocksdb precommit job',
|
||||
);
|
||||
|
||||
// Fetch the configuration necessary to submit a successful HTTPS request.
|
||||
$sandcastle_config = getSandcastleConfig();
|
||||
|
||||
$app = $sandcastle_config[0];
|
||||
$token = $sandcastle_config[1];
|
||||
|
||||
$cmd = 'curl -s -k '
|
||||
. ' -F app=' . escapeshellarg($app)
|
||||
. ' -F token=' . escapeshellarg($token)
|
||||
. ' -F job=' . escapeshellarg(json_encode($job))
|
||||
.' ' . escapeshellarg($url);
|
||||
|
||||
$output = shell_exec($cmd);
|
||||
assert(strlen($output) > 0);
|
||||
|
||||
// Extract Sandcastle URL from the response.
|
||||
preg_match('/url": "(.+)"/', $output, $sandcastle_url);
|
||||
|
||||
assert(count($sandcastle_url) > 0, "Unable to submit Sandcastle request.");
|
||||
assert(strlen($sandcastle_url[1]) > 0, "Unable to extract Sandcastle URL.");
|
||||
|
||||
if ($applyDiff) {
|
||||
echo "\nSandcastle URL: " . $sandcastle_url[1] . "\n";
|
||||
// Ask Phabricator to display it on the diff UI.
|
||||
postURL($diffID, $sandcastle_url[1]);
|
||||
} else {
|
||||
echo "Continuous integration started Sandcastle tests. You can look at ";
|
||||
echo "the progress at:\n" . $sandcastle_url[1] . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Continuous run cript will set the environment variable and based on that
|
||||
// we'll trigger the execution of tests in Sandcastle. In that case we don't
|
||||
// need to apply any diffs and there's no associated workflow either.
|
||||
if (getenv(ENV_POST_RECEIVE_HOOK)) {
|
||||
startTestsInSandcastle(
|
||||
false /* $applyDiff */,
|
||||
NULL /* $workflow */,
|
||||
NULL /* $diffID */);
|
||||
}
|
||||
@@ -150,6 +150,9 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
if test -z "$USE_FOLLY_DISTRIBUTED_MUTEX"; then
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
fi
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
@@ -530,7 +533,7 @@ if test "$USE_HDFS"; then
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS $HDFS_LDFLAGS"
|
||||
fi
|
||||
|
||||
if test -z "$PORTABLE"; then
|
||||
if test "0$PORTABLE" -eq 0; then
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^ppc64`"; then
|
||||
# Tune for this POWER processor, treating '+' models as base models
|
||||
POWER=`LD_SHOW_AUXV=1 /bin/true | grep AT_PLATFORM | grep -E -o power[0-9]+`
|
||||
@@ -544,16 +547,34 @@ if test -z "$PORTABLE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
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
|
||||
# TODO: Not sure why we don't use -march=native on these OSes
|
||||
if test "$USE_SSE"; then
|
||||
TRY_SSE_ETC="1"
|
||||
fi
|
||||
else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
elif test "$USE_SSE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse4.2 -mpclmul"
|
||||
fi
|
||||
elif test "$USE_SSE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse4.2 -mpclmul"
|
||||
else
|
||||
# PORTABLE=1
|
||||
if test "$USE_SSE"; then
|
||||
TRY_SSE_ETC="1"
|
||||
fi
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
if test "$TRY_SSE_ETC"; then
|
||||
# The USE_SSE flag now means "attempt to compile with widely-available
|
||||
# Intel architecture extensions utilized by specific optimizations in the
|
||||
# source code." It's a qualifier on PORTABLE=1 that means "mostly portable."
|
||||
# It doesn't even really check that your current CPU is compatible.
|
||||
#
|
||||
# SSE4.2 available since nehalem, ca. 2008-2010
|
||||
TRY_SSE42="-msse4.2"
|
||||
# PCLMUL available since westmere, ca. 2010-2011
|
||||
TRY_PCLMUL="-mpclmul"
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_SSE42 -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
int main() {
|
||||
@@ -562,13 +583,12 @@ $CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SSE42"
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_SSE42 -DHAVE_SSE42"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_PCLMUL -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
@@ -580,10 +600,9 @@ $CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_PCLMUL"
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_PCLMUL -DHAVE_PCLMUL"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# iOS doesn't support thread-local storage, but this check would erroneously
|
||||
@@ -661,3 +680,6 @@ if test -n "$WITH_JEMALLOC_FLAG"; then
|
||||
echo "WITH_JEMALLOC_FLAG=$WITH_JEMALLOC_FLAG" >> "$OUTPUT"
|
||||
fi
|
||||
echo "LUA_PATH=$LUA_PATH" >> "$OUTPUT"
|
||||
if test -n "$USE_FOLLY_DISTRIBUTED_MUTEX"; then
|
||||
echo "USE_FOLLY_DISTRIBUTED_MUTEX=$USE_FOLLY_DISTRIBUTED_MUTEX" >> "$OUTPUT"
|
||||
fi
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (c) 2016, Facebook. All rights reserved.
|
||||
#
|
||||
# Overall wrapper script for RocksDB continuous builds. The implementation is a
|
||||
# trivial pulling scheme. We loop infinitely, check if any new changes have been
|
||||
# committed, if yes then trigger a Sandcastle run, and finally go to sleep again
|
||||
# for a certain interval.
|
||||
#
|
||||
|
||||
SRC_GIT_REPO=/data/git/rocksdb-public
|
||||
error=0
|
||||
|
||||
function log {
|
||||
DATE=`date +%Y-%m-%d:%H:%M:%S`
|
||||
# shellcheck disable=SC2068
|
||||
echo $DATE $@
|
||||
}
|
||||
|
||||
function log_err {
|
||||
# shellcheck disable=SC2145
|
||||
log "ERROR: $@ Error code: $error."
|
||||
}
|
||||
|
||||
function update_repo_status {
|
||||
# Update the parent first.
|
||||
pushd $SRC_GIT_REPO
|
||||
|
||||
# This is a fatal error. Something in the environment isn't right and we will
|
||||
# terminate the execution.
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "Where is $SRC_GIT_REPO?"
|
||||
exit $error
|
||||
fi
|
||||
|
||||
HTTPS_PROXY=fwdproxy:8080 git fetch -f
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git fetch -f failed."
|
||||
popd
|
||||
return $error
|
||||
fi
|
||||
|
||||
git update-ref refs/heads/master refs/remotes/origin/master
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git update-ref failed."
|
||||
popd
|
||||
return $error
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
# We're back in an instance-specific directory. Get the latest changes.
|
||||
git pull --rebase
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git pull --rebase failed."
|
||||
return $error
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Execution starts here.
|
||||
#
|
||||
|
||||
# Path to the determinator from the root of the RocksDB repo.
|
||||
CONTRUN_DETERMINATOR=./build_tools/RocksDBCommonHelper.php
|
||||
|
||||
# Value of the previous commit.
|
||||
PREV_COMMIT=
|
||||
|
||||
log "Starting to monitor for new RocksDB changes ..."
|
||||
log "Running under `pwd` as `whoami`."
|
||||
|
||||
# Paranoia. Make sure that we're using the right branch.
|
||||
git checkout master
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "This is not good. Can't checkout master. Bye-bye!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# We'll run forever and let the execution environment terminate us if we'll
|
||||
# exceed whatever timeout is set for the job.
|
||||
while true;
|
||||
do
|
||||
# Get the latest changes committed.
|
||||
update_repo_status
|
||||
|
||||
error=$?
|
||||
if [ $error -eq 0 ]; then
|
||||
LAST_COMMIT=`git log -1 | head -1 | grep commit | awk '{ print $2; }'`
|
||||
|
||||
log "Last commit is '$LAST_COMMIT', previous commit is '$PREV_COMMIT'."
|
||||
|
||||
if [ "$PREV_COMMIT" == "$LAST_COMMIT" ]; then
|
||||
log "There were no changes since the last time I checked. Going to sleep."
|
||||
else
|
||||
if [ ! -z "$LAST_COMMIT" ]; then
|
||||
log "New code has been committed or previous commit not known. " \
|
||||
"Will trigger the tests."
|
||||
|
||||
PREV_COMMIT=$LAST_COMMIT
|
||||
log "Updated previous commit to '$PREV_COMMIT'."
|
||||
|
||||
#
|
||||
# This is where we'll trigger the Sandcastle run. The values for
|
||||
# HTTPS_APP_VALUE and HTTPS_APP_VALUE will be set in the container we're
|
||||
# running in.
|
||||
#
|
||||
POST_RECEIVE_HOOK=1 php $CONTRUN_DETERMINATOR
|
||||
|
||||
error=$?
|
||||
if [ $error -eq 0 ]; then
|
||||
log "Sandcastle run successfully triggered."
|
||||
else
|
||||
log_err "Failed to trigger Sandcastle run."
|
||||
fi
|
||||
else
|
||||
log_err "Previous commit not updated. Don't know what the last one is."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_err "Getting latest changes failed. Will skip running tests for now."
|
||||
fi
|
||||
|
||||
# Always sleep, even if errors happens while trying to determine the latest
|
||||
# commit. This will prevent us terminating in case of transient errors.
|
||||
log "Will go to sleep for 5 minutes."
|
||||
sleep 5m
|
||||
done
|
||||
+14
-14
@@ -1,19 +1,19 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/112ec378fec7002ad3e09afde022e656049f7191/5.x/centos7-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/04999bdb3ce81a11073535dcb00b5e13dc1cbaf5/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/92b0c8e5c8eecc71eb042594ce1ab3413799b385/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/3d8698d5973ba94f41620a80a67e4457fdf01e90/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/5.x/centos7-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1bd23f9917738974ad0ff305aa23eb5f93f18305/9.0.0/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/907b498203d297947f3bb70b9466f47e100f1873/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/af6628a46758f1a15484a1760cd7294164bc5ba1/1.3.5/gcc-5-glibc-2.23/03859b5
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/b1a0e56c1e3e6929813a4331ade3a58ff083afbb/master/gcc-5-glibc-2.23/aa64d6b
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/gcc-5-glibc-2.23/b443de1
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/55031de95a2b46c82948743419a603b3d6aefe28/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/gcc-5-glibc-2.23/0c8f76d
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/gcc-5-glibc-2.23/b443de1
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/2e3cb7d119b3cea5f1e738cc13a1ac69f49eb875/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/6e8e715624fd15256a7970073387793dfcf79b46/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/ef37e1faa1c29782abfac1ae65a291b9b7966f6d/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c67031f0f739ac61575a061518d6ef5038f99f90/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/60d6f124a78798b73944f5ba87c2306ae3460153/2.26/platform007/f259413
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1bd23f9917738974ad0ff305aa23eb5f93f18305/9.0.0/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/1.2.8/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/907b498203d297947f3bb70b9466f47e100f1873/r131/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/3ee276cbacfad3074e3f07bf826ac47f06970f4e/1.3.5/platform007/15a3614
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/9c910d36d6235cc40e8ff559358f1833452300ca/master/platform007/5b0f53e
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/92ff90349e2f43ea0a8246d8b1cf17b6869013e3/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/2e3cb7d119b3cea5f1e738cc13a1ac69f49eb875/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
|
||||
@@ -86,9 +86,10 @@ else
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
@@ -159,4 +160,6 @@ else
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua_pic.a"
|
||||
fi
|
||||
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
|
||||
@@ -53,9 +53,10 @@ LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
@@ -86,9 +86,10 @@ else
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
@@ -155,4 +156,6 @@ VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
|
||||
@@ -54,15 +54,25 @@ fi
|
||||
set -e
|
||||
|
||||
uncommitted_code=`git diff HEAD`
|
||||
LAST_MASTER=`git merge-base master HEAD`
|
||||
|
||||
# If there's no uncommitted changes, we assume user are doing post-commit
|
||||
# format check, in which case we'll check the modified lines since last commit
|
||||
# from master. Otherwise, we'll check format of the uncommitted code only.
|
||||
# format check, in which case we'll try to check the modified lines vs. the
|
||||
# facebook/rocksdb.git master branch. Otherwise, we'll check format of the
|
||||
# uncommitted code only.
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
# Check the format of last commit
|
||||
diffs=$(git diff -U0 $LAST_MASTER^ | $CLANG_FORMAT_DIFF -p 1)
|
||||
# Attempt to get name of facebook/rocksdb.git remote.
|
||||
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)"
|
||||
# Fall back on 'origin' if that fails
|
||||
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE=origin
|
||||
# Use master branch from that remote
|
||||
[ "$FORMAT_UPSTREAM" ] || FORMAT_UPSTREAM="$FORMAT_REMOTE/master"
|
||||
# Get the common ancestor with that remote branch. Everything after that
|
||||
# common ancestor would be considered the contents of a pull request, so
|
||||
# should be relevant for formatting fixes.
|
||||
FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
|
||||
# Get the differences
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1)
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
|
||||
@@ -76,12 +86,12 @@ fi
|
||||
|
||||
# Highlight the insertion/deletion from the clang-format-diff.py's output
|
||||
COLOR_END="\033[0m"
|
||||
COLOR_RED="\033[0;31m"
|
||||
COLOR_GREEN="\033[0;32m"
|
||||
COLOR_RED="\033[0;31m"
|
||||
COLOR_GREEN="\033[0;32m"
|
||||
|
||||
echo -e "Detect lines that doesn't follow the format rules:\r"
|
||||
# Add the color to the diff. lines added will be green; lines removed will be red.
|
||||
echo "$diffs" |
|
||||
echo "$diffs" |
|
||||
sed -e "s/\(^-.*$\)/`echo -e \"$COLOR_RED\1$COLOR_END\"`/" |
|
||||
sed -e "s/\(^+.*$\)/`echo -e \"$COLOR_GREEN\1$COLOR_END\"`/"
|
||||
|
||||
@@ -104,7 +114,7 @@ fi
|
||||
# Do in-place format adjustment.
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
git diff -U0 $LAST_MASTER^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -i -p 1
|
||||
else
|
||||
git diff -U0 HEAD^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
fi
|
||||
|
||||
@@ -63,6 +63,21 @@ CLEANUP_ENV="
|
||||
'user':'root'
|
||||
}"
|
||||
|
||||
UPLOAD_DB_DIR="
|
||||
{
|
||||
'name':'Upload database directory',
|
||||
'shell':'tar -cvzf rocksdb_db.tar.gz /dev/shm/rocksdb/',
|
||||
'user':'root',
|
||||
'cleanup':true,
|
||||
'provide_artifacts': [
|
||||
{
|
||||
'name':'rocksdb_db_dir',
|
||||
'paths': ['rocksdb_db.tar.gz'],
|
||||
'bundle': false,
|
||||
},
|
||||
],
|
||||
}"
|
||||
|
||||
# We will eventually set the RATIO to 1, but we want do this
|
||||
# in steps. RATIO=$(nproc) will make it work as J=1
|
||||
if [ -z $RATIO ]; then
|
||||
@@ -428,7 +443,8 @@ STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
@@ -519,6 +535,7 @@ ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
@@ -586,6 +603,7 @@ UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
@@ -678,6 +696,7 @@ TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
BASEDIR=$(dirname $0)
|
||||
OUTPUT=""
|
||||
|
||||
function log_header()
|
||||
{
|
||||
echo "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved." >> "$OUTPUT"
|
||||
}
|
||||
|
||||
|
||||
function log_variable()
|
||||
{
|
||||
echo "$1=${!1}" >> "$OUTPUT"
|
||||
@@ -69,6 +75,7 @@ echo "Writing dependencies to $OUTPUT"
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/7.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
@@ -108,6 +115,7 @@ echo "Writing dependencies to $OUTPUT"
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/5.x/centos7-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos7-native/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
@@ -147,6 +155,7 @@ echo "Writing 4.8.1 dependencies to $OUTPUT"
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/4.8.1/centos6-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/stable/centos6-native/*/`
|
||||
|
||||
log_header
|
||||
log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -11,9 +11,9 @@ int main() {
|
||||
}
|
||||
#else
|
||||
|
||||
#include <cinttypes>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
|
||||
Vendored
+83
-13
@@ -86,14 +86,22 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit) {
|
||||
std::shared_ptr<Cache> NewCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy charge_policy = kDontChargeCacheMetadata) {
|
||||
auto type = GetParam();
|
||||
if (type == kLRU) {
|
||||
return NewLRUCache(capacity, num_shard_bits, strict_capacity_limit);
|
||||
LRUCacheOptions co;
|
||||
co.capacity = capacity;
|
||||
co.num_shard_bits = num_shard_bits;
|
||||
co.strict_capacity_limit = strict_capacity_limit;
|
||||
co.high_pri_pool_ratio = 0;
|
||||
co.metadata_charge_policy = charge_policy;
|
||||
return NewLRUCache(co);
|
||||
}
|
||||
if (type == kClock) {
|
||||
return NewClockCache(capacity, num_shard_bits, strict_capacity_limit);
|
||||
return NewClockCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
charge_policy);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -143,10 +151,15 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
};
|
||||
CacheTest* CacheTest::current_;
|
||||
|
||||
class LRUCacheTest : public CacheTest {};
|
||||
|
||||
TEST_P(CacheTest, UsageTest) {
|
||||
// cache is std::shared_ptr and will be automatically cleaned up.
|
||||
const uint64_t kCapacity = 100000;
|
||||
auto cache = NewCache(kCapacity, 8, false);
|
||||
auto cache = NewCache(kCapacity, 8, false, kDontChargeCacheMetadata);
|
||||
auto precise_cache = NewCache(kCapacity, 0, false, kFullChargeCacheMetadata);
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
ASSERT_EQ(0, precise_cache->GetUsage());
|
||||
|
||||
size_t usage = 0;
|
||||
char value[10] = "abcdef";
|
||||
@@ -155,31 +168,45 @@ TEST_P(CacheTest, UsageTest) {
|
||||
std::string key(i, 'a');
|
||||
auto kv_size = key.size() + 5;
|
||||
cache->Insert(key, reinterpret_cast<void*>(value), kv_size, dumbDeleter);
|
||||
precise_cache->Insert(key, reinterpret_cast<void*>(value), kv_size,
|
||||
dumbDeleter);
|
||||
usage += kv_size;
|
||||
ASSERT_EQ(usage, cache->GetUsage());
|
||||
ASSERT_LT(usage, precise_cache->GetUsage());
|
||||
}
|
||||
|
||||
cache->EraseUnRefEntries();
|
||||
precise_cache->EraseUnRefEntries();
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
ASSERT_EQ(0, precise_cache->GetUsage());
|
||||
|
||||
// make sure the cache will be overloaded
|
||||
for (uint64_t i = 1; i < kCapacity; ++i) {
|
||||
auto key = ToString(i);
|
||||
cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter);
|
||||
precise_cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter);
|
||||
}
|
||||
|
||||
// the usage should be close to the capacity
|
||||
ASSERT_GT(kCapacity, cache->GetUsage());
|
||||
ASSERT_GT(kCapacity, precise_cache->GetUsage());
|
||||
ASSERT_LT(kCapacity * 0.95, cache->GetUsage());
|
||||
ASSERT_LT(kCapacity * 0.95, precise_cache->GetUsage());
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, PinnedUsageTest) {
|
||||
// cache is std::shared_ptr and will be automatically cleaned up.
|
||||
const uint64_t kCapacity = 100000;
|
||||
auto cache = NewCache(kCapacity, 8, false);
|
||||
const uint64_t kCapacity = 200000;
|
||||
auto cache = NewCache(kCapacity, 8, false, kDontChargeCacheMetadata);
|
||||
auto precise_cache = NewCache(kCapacity, 8, false, kFullChargeCacheMetadata);
|
||||
|
||||
size_t pinned_usage = 0;
|
||||
char value[10] = "abcdef";
|
||||
|
||||
std::forward_list<Cache::Handle*> unreleased_handles;
|
||||
std::forward_list<Cache::Handle*> unreleased_handles_in_precise_cache;
|
||||
|
||||
// Add entries. Unpin some of them after insertion. Then, pin some of them
|
||||
// again. Check GetPinnedUsage().
|
||||
@@ -187,40 +214,72 @@ TEST_P(CacheTest, PinnedUsageTest) {
|
||||
std::string key(i, 'a');
|
||||
auto kv_size = key.size() + 5;
|
||||
Cache::Handle* handle;
|
||||
Cache::Handle* handle_in_precise_cache;
|
||||
cache->Insert(key, reinterpret_cast<void*>(value), kv_size, dumbDeleter,
|
||||
&handle);
|
||||
assert(handle);
|
||||
precise_cache->Insert(key, reinterpret_cast<void*>(value), kv_size,
|
||||
dumbDeleter, &handle_in_precise_cache);
|
||||
assert(handle_in_precise_cache);
|
||||
pinned_usage += kv_size;
|
||||
ASSERT_EQ(pinned_usage, cache->GetPinnedUsage());
|
||||
ASSERT_LT(pinned_usage, precise_cache->GetPinnedUsage());
|
||||
if (i % 2 == 0) {
|
||||
cache->Release(handle);
|
||||
precise_cache->Release(handle_in_precise_cache);
|
||||
pinned_usage -= kv_size;
|
||||
ASSERT_EQ(pinned_usage, cache->GetPinnedUsage());
|
||||
ASSERT_LT(pinned_usage, precise_cache->GetPinnedUsage());
|
||||
} else {
|
||||
unreleased_handles.push_front(handle);
|
||||
unreleased_handles_in_precise_cache.push_front(handle_in_precise_cache);
|
||||
}
|
||||
if (i % 3 == 0) {
|
||||
unreleased_handles.push_front(cache->Lookup(key));
|
||||
auto x = precise_cache->Lookup(key);
|
||||
assert(x);
|
||||
unreleased_handles_in_precise_cache.push_front(x);
|
||||
// If i % 2 == 0, then the entry was unpinned before Lookup, so pinned
|
||||
// usage increased
|
||||
if (i % 2 == 0) {
|
||||
pinned_usage += kv_size;
|
||||
}
|
||||
ASSERT_EQ(pinned_usage, cache->GetPinnedUsage());
|
||||
ASSERT_LT(pinned_usage, precise_cache->GetPinnedUsage());
|
||||
}
|
||||
}
|
||||
auto precise_cache_pinned_usage = precise_cache->GetPinnedUsage();
|
||||
ASSERT_LT(pinned_usage, precise_cache_pinned_usage);
|
||||
|
||||
// check that overloading the cache does not change the pinned usage
|
||||
for (uint64_t i = 1; i < 2 * kCapacity; ++i) {
|
||||
auto key = ToString(i);
|
||||
cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter);
|
||||
precise_cache->Insert(key, reinterpret_cast<void*>(value), key.size() + 5,
|
||||
dumbDeleter);
|
||||
}
|
||||
ASSERT_EQ(pinned_usage, cache->GetPinnedUsage());
|
||||
ASSERT_EQ(precise_cache_pinned_usage, precise_cache->GetPinnedUsage());
|
||||
|
||||
cache->EraseUnRefEntries();
|
||||
precise_cache->EraseUnRefEntries();
|
||||
ASSERT_EQ(pinned_usage, cache->GetPinnedUsage());
|
||||
ASSERT_EQ(precise_cache_pinned_usage, precise_cache->GetPinnedUsage());
|
||||
|
||||
// release handles for pinned entries to prevent memory leaks
|
||||
for (auto handle : unreleased_handles) {
|
||||
cache->Release(handle);
|
||||
}
|
||||
for (auto handle : unreleased_handles_in_precise_cache) {
|
||||
precise_cache->Release(handle);
|
||||
}
|
||||
ASSERT_EQ(0, cache->GetPinnedUsage());
|
||||
ASSERT_EQ(0, precise_cache->GetPinnedUsage());
|
||||
cache->EraseUnRefEntries();
|
||||
precise_cache->EraseUnRefEntries();
|
||||
ASSERT_EQ(0, cache->GetUsage());
|
||||
ASSERT_EQ(0, precise_cache->GetUsage());
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, HitAndMiss) {
|
||||
@@ -550,10 +609,10 @@ TEST_P(CacheTest, SetCapacity) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, SetStrictCapacityLimit) {
|
||||
TEST_P(LRUCacheTest, SetStrictCapacityLimit) {
|
||||
// test1: set the flag to false. Insert more keys than capacity. See if they
|
||||
// all go through.
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(5, 0, false);
|
||||
std::shared_ptr<Cache> cache = NewCache(5, 0, false);
|
||||
std::vector<Cache::Handle*> handles(10);
|
||||
Status s;
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
@@ -562,6 +621,7 @@ TEST_P(CacheTest, SetStrictCapacityLimit) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_NE(nullptr, handles[i]);
|
||||
}
|
||||
ASSERT_EQ(10, cache->GetUsage());
|
||||
|
||||
// test2: set the flag to true. Insert and check if it fails.
|
||||
std::string extra_key = "extra";
|
||||
@@ -571,13 +631,14 @@ TEST_P(CacheTest, SetStrictCapacityLimit) {
|
||||
s = cache->Insert(extra_key, extra_value, 1, &deleter, &handle);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
ASSERT_EQ(nullptr, handle);
|
||||
ASSERT_EQ(10, cache->GetUsage());
|
||||
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
cache->Release(handles[i]);
|
||||
}
|
||||
|
||||
// test3: init with flag being true.
|
||||
std::shared_ptr<Cache> cache2 = NewLRUCache(5, 0, true);
|
||||
std::shared_ptr<Cache> cache2 = NewCache(5, 0, true);
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
std::string key = ToString(i + 1);
|
||||
s = cache2->Insert(key, new Value(i + 1), 1, &deleter, &handles[i]);
|
||||
@@ -591,7 +652,7 @@ TEST_P(CacheTest, SetStrictCapacityLimit) {
|
||||
s = cache2->Insert(extra_key, extra_value, 1, &deleter);
|
||||
// AS if the key have been inserted into cache but get evicted immediately.
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(5, cache->GetUsage());
|
||||
ASSERT_EQ(5, cache2->GetUsage());
|
||||
ASSERT_EQ(nullptr, cache2->Lookup(extra_key));
|
||||
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
@@ -686,14 +747,23 @@ TEST_P(CacheTest, DefaultShardBits) {
|
||||
ASSERT_EQ(6, sc->GetNumShardBits());
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, GetCharge) {
|
||||
Insert(1, 2);
|
||||
Cache::Handle* h1 = cache_->Lookup(EncodeKey(1));
|
||||
ASSERT_EQ(2, DecodeValue(cache_->Value(h1)));
|
||||
ASSERT_EQ(1, cache_->GetCharge(h1));
|
||||
cache_->Release(h1);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_CLOCK_CACHE
|
||||
std::shared_ptr<Cache> (*new_clock_cache_func)(size_t, int,
|
||||
bool) = NewClockCache;
|
||||
std::shared_ptr<Cache> (*new_clock_cache_func)(
|
||||
size_t, int, bool, CacheMetadataChargePolicy) = NewClockCache;
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
testing::Values(kLRU, kClock));
|
||||
#else
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest, testing::Values(kLRU));
|
||||
#endif // SUPPORT_CLOCK_CACHE
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest, testing::Values(kLRU));
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
Vendored
+46
-13
@@ -13,8 +13,9 @@
|
||||
|
||||
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*/,
|
||||
CacheMetadataChargePolicy /*metadata_charge_policy*/) {
|
||||
// Clock cache not supported.
|
||||
return nullptr;
|
||||
}
|
||||
@@ -35,6 +36,7 @@ std::shared_ptr<Cache> NewClockCache(size_t /*capacity*/, int /*num_shard_bits*/
|
||||
#include "tbb/concurrent_hash_map.h"
|
||||
|
||||
#include "cache/sharded_cache.h"
|
||||
#include "port/malloc.h"
|
||||
#include "port/port.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/mutexlock.h"
|
||||
@@ -202,6 +204,27 @@ struct CacheHandle {
|
||||
deleter = a.deleter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline static size_t CalcTotalCharge(
|
||||
Slice key, size_t charge,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
size_t meta_charge = 0;
|
||||
if (metadata_charge_policy == kFullChargeCacheMetadata) {
|
||||
meta_charge += sizeof(CacheHandle);
|
||||
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
meta_charge +=
|
||||
malloc_usable_size(static_cast<void*>(const_cast<char*>(key.data())));
|
||||
#else
|
||||
meta_charge += key.size();
|
||||
#endif
|
||||
}
|
||||
return charge + meta_charge;
|
||||
}
|
||||
|
||||
inline size_t CalcTotalCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
return CalcTotalCharge(key, charge, metadata_charge_policy);
|
||||
}
|
||||
};
|
||||
|
||||
// Key of hash map. We store hash value with the key for convenience.
|
||||
@@ -404,11 +427,12 @@ void ClockCacheShard::RecycleHandle(CacheHandle* handle,
|
||||
assert(!InCache(handle->flags) && CountRefs(handle->flags) == 0);
|
||||
context->to_delete_key.push_back(handle->key.data());
|
||||
context->to_delete_value.emplace_back(*handle);
|
||||
size_t total_charge = handle->CalcTotalCharge(metadata_charge_policy_);
|
||||
handle->key.clear();
|
||||
handle->value = nullptr;
|
||||
handle->deleter = nullptr;
|
||||
recycle_.push_back(handle);
|
||||
usage_.fetch_sub(handle->charge, std::memory_order_relaxed);
|
||||
usage_.fetch_sub(total_charge, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ClockCacheShard::Cleanup(const CleanupContext& context) {
|
||||
@@ -434,7 +458,8 @@ bool ClockCacheShard::Ref(Cache::Handle* h) {
|
||||
std::memory_order_relaxed)) {
|
||||
if (CountRefs(flags) == 0) {
|
||||
// No reference count before the operation.
|
||||
pinned_usage_.fetch_add(handle->charge, std::memory_order_relaxed);
|
||||
size_t total_charge = handle->CalcTotalCharge(metadata_charge_policy_);
|
||||
pinned_usage_.fetch_add(total_charge, std::memory_order_relaxed);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -454,7 +479,8 @@ bool ClockCacheShard::Unref(CacheHandle* handle, bool set_usage,
|
||||
assert(CountRefs(flags) > 0);
|
||||
if (CountRefs(flags) == 1) {
|
||||
// this is the last reference.
|
||||
pinned_usage_.fetch_sub(handle->charge, std::memory_order_relaxed);
|
||||
size_t total_charge = handle->CalcTotalCharge(metadata_charge_policy_);
|
||||
pinned_usage_.fetch_sub(total_charge, std::memory_order_relaxed);
|
||||
// Cleanup if it is the last reference.
|
||||
if (!InCache(flags)) {
|
||||
MutexLock l(&mutex_);
|
||||
@@ -539,8 +565,10 @@ CacheHandle* ClockCacheShard::Insert(
|
||||
const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value), bool hold_reference,
|
||||
CleanupContext* context) {
|
||||
size_t total_charge =
|
||||
CacheHandle::CalcTotalCharge(key, charge, metadata_charge_policy_);
|
||||
MutexLock l(&mutex_);
|
||||
bool success = EvictFromCache(charge, context);
|
||||
bool success = EvictFromCache(total_charge, context);
|
||||
bool strict = strict_capacity_limit_.load(std::memory_order_relaxed);
|
||||
if (!success && (strict || !hold_reference)) {
|
||||
context->to_delete_key.push_back(key.data());
|
||||
@@ -575,9 +603,9 @@ CacheHandle* ClockCacheShard::Insert(
|
||||
}
|
||||
table_.insert(HashTable::value_type(CacheKey(key, hash), handle));
|
||||
if (hold_reference) {
|
||||
pinned_usage_.fetch_add(charge, std::memory_order_relaxed);
|
||||
pinned_usage_.fetch_add(total_charge, std::memory_order_relaxed);
|
||||
}
|
||||
usage_.fetch_add(charge, std::memory_order_relaxed);
|
||||
usage_.fetch_add(total_charge, std::memory_order_relaxed);
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -674,10 +702,14 @@ void ClockCacheShard::EraseUnRefEntries() {
|
||||
|
||||
class ClockCache final : public ShardedCache {
|
||||
public:
|
||||
ClockCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit)
|
||||
ClockCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit) {
|
||||
int num_shards = 1 << num_shard_bits;
|
||||
shards_ = new ClockCacheShard[num_shards];
|
||||
for (int i = 0; i < num_shards; i++) {
|
||||
shards_[i].set_metadata_charge_policy(metadata_charge_policy);
|
||||
}
|
||||
SetCapacity(capacity);
|
||||
SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
@@ -714,13 +746,14 @@ class ClockCache final : public ShardedCache {
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
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,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
if (num_shard_bits < 0) {
|
||||
num_shard_bits = GetDefaultCacheShardBits(capacity);
|
||||
}
|
||||
return std::make_shared<ClockCache>(capacity, num_shard_bits,
|
||||
strict_capacity_limit);
|
||||
return std::make_shared<ClockCache>(
|
||||
capacity, num_shard_bits, strict_capacity_limit, metadata_charge_policy);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
Vendored
+97
-91
@@ -24,7 +24,7 @@ LRUHandleTable::LRUHandleTable() : list_(nullptr), length_(0), elems_(0) {
|
||||
|
||||
LRUHandleTable::~LRUHandleTable() {
|
||||
ApplyToAllCacheEntries([](LRUHandle* h) {
|
||||
if (h->refs == 1) {
|
||||
if (!h->HasRefs()) {
|
||||
h->Free();
|
||||
}
|
||||
});
|
||||
@@ -97,7 +97,8 @@ void LRUHandleTable::Resize() {
|
||||
|
||||
LRUCacheShard::LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
bool use_adaptive_mutex)
|
||||
bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: capacity_(0),
|
||||
high_pri_pool_usage_(0),
|
||||
strict_capacity_limit_(strict_capacity_limit),
|
||||
@@ -106,6 +107,7 @@ LRUCacheShard::LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
usage_(0),
|
||||
lru_usage_(0),
|
||||
mutex_(use_adaptive_mutex) {
|
||||
set_metadata_charge_policy(metadata_charge_policy);
|
||||
// Make empty circular linked list
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
@@ -113,30 +115,20 @@ LRUCacheShard::LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
SetCapacity(capacity);
|
||||
}
|
||||
|
||||
LRUCacheShard::~LRUCacheShard() {}
|
||||
|
||||
bool LRUCacheShard::Unref(LRUHandle* e) {
|
||||
assert(e->refs > 0);
|
||||
e->refs--;
|
||||
return e->refs == 0;
|
||||
}
|
||||
|
||||
// Call deleter and free
|
||||
|
||||
void LRUCacheShard::EraseUnRefEntries() {
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
while (lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
assert(old->InCache());
|
||||
assert(old->refs ==
|
||||
1); // LRU list contains elements which may be evicted
|
||||
// LRU list contains only elements which can be evicted
|
||||
assert(old->InCache() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
old->SetInCache(false);
|
||||
Unref(old);
|
||||
usage_ -= old->charge;
|
||||
size_t total_charge = old->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= total_charge);
|
||||
usage_ -= total_charge;
|
||||
last_reference_list.push_back(old);
|
||||
}
|
||||
}
|
||||
@@ -148,22 +140,27 @@ void LRUCacheShard::EraseUnRefEntries() {
|
||||
|
||||
void LRUCacheShard::ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) {
|
||||
const auto applyCallback = [&]() {
|
||||
table_.ApplyToAllCacheEntries(
|
||||
[callback](LRUHandle* h) { callback(h->value, h->charge); });
|
||||
};
|
||||
|
||||
if (thread_safe) {
|
||||
mutex_.Lock();
|
||||
}
|
||||
table_.ApplyToAllCacheEntries(
|
||||
[callback](LRUHandle* h) { callback(h->value, h->charge); });
|
||||
if (thread_safe) {
|
||||
mutex_.Unlock();
|
||||
MutexLock l(&mutex_);
|
||||
applyCallback();
|
||||
} else {
|
||||
applyCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri) {
|
||||
MutexLock l(&mutex_);
|
||||
*lru = &lru_;
|
||||
*lru_low_pri = lru_low_pri_;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::TEST_GetLRUSize() {
|
||||
MutexLock l(&mutex_);
|
||||
LRUHandle* lru_handle = lru_.next;
|
||||
size_t lru_size = 0;
|
||||
while (lru_handle != &lru_) {
|
||||
@@ -187,16 +184,19 @@ void LRUCacheShard::LRU_Remove(LRUHandle* e) {
|
||||
e->next->prev = e->prev;
|
||||
e->prev->next = e->next;
|
||||
e->prev = e->next = nullptr;
|
||||
lru_usage_ -= e->charge;
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(lru_usage_ >= total_charge);
|
||||
lru_usage_ -= total_charge;
|
||||
if (e->InHighPriPool()) {
|
||||
assert(high_pri_pool_usage_ >= e->charge);
|
||||
high_pri_pool_usage_ -= e->charge;
|
||||
assert(high_pri_pool_usage_ >= total_charge);
|
||||
high_pri_pool_usage_ -= total_charge;
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
assert(e->next == nullptr);
|
||||
assert(e->prev == nullptr);
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
if (high_pri_pool_ratio_ > 0 && (e->IsHighPri() || e->HasHit())) {
|
||||
// Inset "e" to head of LRU list.
|
||||
e->next = &lru_;
|
||||
@@ -204,7 +204,7 @@ void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
e->prev->next = e;
|
||||
e->next->prev = e;
|
||||
e->SetInHighPriPool(true);
|
||||
high_pri_pool_usage_ += e->charge;
|
||||
high_pri_pool_usage_ += total_charge;
|
||||
MaintainPoolSize();
|
||||
} else {
|
||||
// Insert "e" to the head of low-pri pool. Note that when
|
||||
@@ -216,7 +216,7 @@ void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
e->SetInHighPriPool(false);
|
||||
lru_low_pri_ = e;
|
||||
}
|
||||
lru_usage_ += e->charge;
|
||||
lru_usage_ += total_charge;
|
||||
}
|
||||
|
||||
void LRUCacheShard::MaintainPoolSize() {
|
||||
@@ -225,21 +225,25 @@ void LRUCacheShard::MaintainPoolSize() {
|
||||
lru_low_pri_ = lru_low_pri_->next;
|
||||
assert(lru_low_pri_ != &lru_);
|
||||
lru_low_pri_->SetInHighPriPool(false);
|
||||
high_pri_pool_usage_ -= lru_low_pri_->charge;
|
||||
size_t total_charge =
|
||||
lru_low_pri_->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(high_pri_pool_usage_ >= total_charge);
|
||||
high_pri_pool_usage_ -= total_charge;
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::EvictFromLRU(size_t charge,
|
||||
autovector<LRUHandle*>* deleted) {
|
||||
while (usage_ + charge > capacity_ && lru_.next != &lru_) {
|
||||
while ((usage_ + charge) > capacity_ && lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
assert(old->InCache());
|
||||
assert(old->refs == 1); // LRU list contains elements which may be evicted
|
||||
// LRU list contains only elements which can be evicted
|
||||
assert(old->InCache() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
old->SetInCache(false);
|
||||
Unref(old);
|
||||
usage_ -= old->charge;
|
||||
size_t old_total_charge = old->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= old_total_charge);
|
||||
usage_ -= old_total_charge;
|
||||
deleted->push_back(old);
|
||||
}
|
||||
}
|
||||
@@ -252,8 +256,8 @@ void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
high_pri_pool_capacity_ = capacity_ * high_pri_pool_ratio_;
|
||||
EvictFromLRU(0, &last_reference_list);
|
||||
}
|
||||
// we free the entries here outside of mutex for
|
||||
// performance reasons
|
||||
|
||||
// Free the entries outside of mutex for performance reasons
|
||||
for (auto entry : last_reference_list) {
|
||||
entry->Free();
|
||||
}
|
||||
@@ -269,22 +273,22 @@ Cache::Handle* LRUCacheShard::Lookup(const Slice& key, uint32_t hash) {
|
||||
LRUHandle* e = table_.Lookup(key, hash);
|
||||
if (e != nullptr) {
|
||||
assert(e->InCache());
|
||||
if (e->refs == 1) {
|
||||
if (!e->HasRefs()) {
|
||||
// The entry is in LRU since it's in hash and has no external references
|
||||
LRU_Remove(e);
|
||||
}
|
||||
e->refs++;
|
||||
e->Ref();
|
||||
e->SetHit();
|
||||
}
|
||||
return reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
|
||||
bool LRUCacheShard::Ref(Cache::Handle* h) {
|
||||
LRUHandle* handle = reinterpret_cast<LRUHandle*>(h);
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(h);
|
||||
MutexLock l(&mutex_);
|
||||
if (handle->InCache() && handle->refs == 1) {
|
||||
LRU_Remove(handle);
|
||||
}
|
||||
handle->refs++;
|
||||
// To create another reference - entry must be already externally referenced
|
||||
assert(e->HasRefs());
|
||||
e->Ref();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -303,30 +307,29 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
bool last_reference = false;
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
last_reference = Unref(e);
|
||||
if (last_reference) {
|
||||
usage_ -= e->charge;
|
||||
}
|
||||
if (e->refs == 1 && e->InCache()) {
|
||||
last_reference = e->Unref();
|
||||
if (last_reference && e->InCache()) {
|
||||
// The item is still in cache, and nobody else holds a reference to it
|
||||
if (usage_ > capacity_ || force_erase) {
|
||||
// the cache is full
|
||||
// The LRU list must be empty since the cache is full
|
||||
assert(!(usage_ > capacity_) || lru_.next == &lru_);
|
||||
// take this opportunity and remove the item
|
||||
assert(lru_.next == &lru_ || force_erase);
|
||||
// Take this opportunity and remove the item
|
||||
table_.Remove(e->key(), e->hash);
|
||||
e->SetInCache(false);
|
||||
Unref(e);
|
||||
usage_ -= e->charge;
|
||||
last_reference = true;
|
||||
} else {
|
||||
// put the item on the list to be potentially freed
|
||||
// Put the item back on the LRU list, and don't free it
|
||||
LRU_Insert(e);
|
||||
last_reference = false;
|
||||
}
|
||||
}
|
||||
if (last_reference) {
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= total_charge);
|
||||
usage_ -= total_charge;
|
||||
}
|
||||
}
|
||||
|
||||
// free outside of mutex
|
||||
// Free the entry here outside of mutex for performance reasons
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
}
|
||||
@@ -342,7 +345,7 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
// It shouldn't happen very often though.
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(
|
||||
new char[sizeof(LRUHandle) - 1 + key.size()]);
|
||||
Status s;
|
||||
Status s = Status::OK();
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
|
||||
e->value = value;
|
||||
@@ -351,26 +354,26 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
e->key_length = key.size();
|
||||
e->flags = 0;
|
||||
e->hash = hash;
|
||||
e->refs = (handle == nullptr
|
||||
? 1
|
||||
: 2); // One from LRUCache, one for the returned handle
|
||||
e->refs = 0;
|
||||
e->next = e->prev = nullptr;
|
||||
e->SetInCache(true);
|
||||
e->SetPriority(priority);
|
||||
memcpy(e->key_data, key.data(), key.size());
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
|
||||
// Free the space following strict LRU policy until enough space
|
||||
// is freed or the lru list is empty
|
||||
EvictFromLRU(charge, &last_reference_list);
|
||||
EvictFromLRU(total_charge, &last_reference_list);
|
||||
|
||||
if (usage_ - lru_usage_ + charge > capacity_ &&
|
||||
if ((usage_ + total_charge) > capacity_ &&
|
||||
(strict_capacity_limit_ || handle == nullptr)) {
|
||||
if (handle == nullptr) {
|
||||
// Don't insert the entry but still return ok, as if the entry inserted
|
||||
// into cache and get evicted immediately.
|
||||
e->SetInCache(false);
|
||||
last_reference_list.push_back(e);
|
||||
} else {
|
||||
delete[] reinterpret_cast<char*>(e);
|
||||
@@ -378,32 +381,33 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
s = Status::Incomplete("Insert failed due to LRU cache being full.");
|
||||
}
|
||||
} else {
|
||||
// insert into the cache
|
||||
// note that the cache might get larger than its capacity if not enough
|
||||
// space was freed
|
||||
// Insert into the cache. Note that the cache might get larger than its
|
||||
// capacity if not enough space was freed up.
|
||||
LRUHandle* old = table_.Insert(e);
|
||||
usage_ += e->charge;
|
||||
usage_ += total_charge;
|
||||
if (old != nullptr) {
|
||||
assert(old->InCache());
|
||||
old->SetInCache(false);
|
||||
if (Unref(old)) {
|
||||
usage_ -= old->charge;
|
||||
// old is on LRU because it's in cache and its reference count
|
||||
// was just 1 (Unref returned 0)
|
||||
if (!old->HasRefs()) {
|
||||
// old is on LRU because it's in cache and its reference count is 0
|
||||
LRU_Remove(old);
|
||||
size_t old_total_charge =
|
||||
old->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= old_total_charge);
|
||||
usage_ -= old_total_charge;
|
||||
last_reference_list.push_back(old);
|
||||
}
|
||||
}
|
||||
if (handle == nullptr) {
|
||||
LRU_Insert(e);
|
||||
} else {
|
||||
e->Ref();
|
||||
*handle = reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
s = Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
// we free the entries here outside of mutex for
|
||||
// performance reasons
|
||||
// Free the entries here outside of mutex for performance reasons
|
||||
for (auto entry : last_reference_list) {
|
||||
entry->Free();
|
||||
}
|
||||
@@ -418,18 +422,20 @@ void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
MutexLock l(&mutex_);
|
||||
e = table_.Remove(key, hash);
|
||||
if (e != nullptr) {
|
||||
last_reference = Unref(e);
|
||||
if (last_reference) {
|
||||
usage_ -= e->charge;
|
||||
}
|
||||
if (last_reference && e->InCache()) {
|
||||
LRU_Remove(e);
|
||||
}
|
||||
assert(e->InCache());
|
||||
e->SetInCache(false);
|
||||
if (!e->HasRefs()) {
|
||||
// The entry is in LRU since it's in hash and has no external references
|
||||
LRU_Remove(e);
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= total_charge);
|
||||
usage_ -= total_charge;
|
||||
last_reference = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mutex not held here
|
||||
// Free the entry here outside of mutex for performance reasons
|
||||
// last_reference will only be true if e != nullptr
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
@@ -461,7 +467,8 @@ std::string LRUCacheShard::GetPrintableOptions() const {
|
||||
LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit, double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> allocator,
|
||||
bool use_adaptive_mutex)
|
||||
bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
std::move(allocator)) {
|
||||
num_shards_ = 1 << num_shard_bits;
|
||||
@@ -471,7 +478,7 @@ LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
for (int i = 0; i < num_shards_; i++) {
|
||||
new (&shards_[i])
|
||||
LRUCacheShard(per_shard, strict_capacity_limit, high_pri_pool_ratio,
|
||||
use_adaptive_mutex);
|
||||
use_adaptive_mutex, metadata_charge_policy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,15 +547,15 @@ std::shared_ptr<Cache> NewLRUCache(const LRUCacheOptions& cache_opts) {
|
||||
return NewLRUCache(cache_opts.capacity, cache_opts.num_shard_bits,
|
||||
cache_opts.strict_capacity_limit,
|
||||
cache_opts.high_pri_pool_ratio,
|
||||
cache_opts.memory_allocator,
|
||||
cache_opts.use_adaptive_mutex);
|
||||
cache_opts.memory_allocator, cache_opts.use_adaptive_mutex,
|
||||
cache_opts.metadata_charge_policy);
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator,
|
||||
bool use_adaptive_mutex) {
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
if (num_shard_bits >= 20) {
|
||||
return nullptr; // the cache cannot be sharded into too many fine pieces
|
||||
}
|
||||
@@ -559,10 +566,9 @@ std::shared_ptr<Cache> NewLRUCache(
|
||||
if (num_shard_bits < 0) {
|
||||
num_shard_bits = GetDefaultCacheShardBits(capacity);
|
||||
}
|
||||
return std::make_shared<LRUCache>(capacity, num_shard_bits,
|
||||
strict_capacity_limit, high_pri_pool_ratio,
|
||||
std::move(memory_allocator),
|
||||
use_adaptive_mutex);
|
||||
return std::make_shared<LRUCache>(
|
||||
capacity, num_shard_bits, strict_capacity_limit, high_pri_pool_ratio,
|
||||
std::move(memory_allocator), use_adaptive_mutex, metadata_charge_policy);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
Vendored
+62
-38
@@ -12,36 +12,40 @@
|
||||
|
||||
#include "cache/sharded_cache.h"
|
||||
|
||||
#include "port/malloc.h"
|
||||
#include "port/port.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// LRU cache implementation
|
||||
// LRU cache implementation. This class is not thread-safe.
|
||||
|
||||
// An entry is a variable length heap-allocated structure.
|
||||
// Entries are referenced by cache and/or by any external entity.
|
||||
// The cache keeps all its entries in table. Some elements
|
||||
// The cache keeps all its entries in a hash table. Some elements
|
||||
// are also stored on LRU list.
|
||||
//
|
||||
// LRUHandle can be in these states:
|
||||
// 1. Referenced externally AND in hash table.
|
||||
// In that case the entry is *not* in the LRU. (refs > 1 && in_cache == true)
|
||||
// 2. Not referenced externally and in hash table. In that case the entry is
|
||||
// in the LRU and can be freed. (refs == 1 && in_cache == true)
|
||||
// 3. Referenced externally and not in hash table. In that case the entry is
|
||||
// in not on LRU and not in table. (refs >= 1 && in_cache == false)
|
||||
// In that case the entry is *not* in the LRU list
|
||||
// (refs >= 1 && in_cache == true)
|
||||
// 2. Not referenced externally AND in hash table.
|
||||
// In that case the entry is in the LRU list and can be freed.
|
||||
// (refs == 0 && in_cache == true)
|
||||
// 3. Referenced externally AND not in hash table.
|
||||
// In that case the entry is not in the LRU list and not in hash table.
|
||||
// The entry can be freed when refs becomes 0.
|
||||
// (refs >= 1 && in_cache == false)
|
||||
//
|
||||
// All newly created LRUHandles are in state 1. If you call
|
||||
// LRUCacheShard::Release
|
||||
// on entry in state 1, it will go into state 2. To move from state 1 to
|
||||
// state 3, either call LRUCacheShard::Erase or LRUCacheShard::Insert with the
|
||||
// same key.
|
||||
// LRUCacheShard::Release on entry in state 1, it will go into state 2.
|
||||
// To move from state 1 to state 3, either call LRUCacheShard::Erase or
|
||||
// LRUCacheShard::Insert with the same key (but possibly different value).
|
||||
// To move from state 2 to state 1, use LRUCacheShard::Lookup.
|
||||
// Before destruction, make sure that no handles are in state 1. This means
|
||||
// that any successful LRUCacheShard::Lookup/LRUCacheShard::Insert have a
|
||||
// matching
|
||||
// RUCache::Release (to move into state 2) or LRUCacheShard::Erase (for state 3)
|
||||
// matching LRUCache::Release (to move into state 2) or LRUCacheShard::Erase
|
||||
// (to move into state 3).
|
||||
|
||||
struct LRUHandle {
|
||||
void* value;
|
||||
@@ -51,37 +55,42 @@ struct LRUHandle {
|
||||
LRUHandle* prev;
|
||||
size_t charge; // TODO(opt): Only allow uint32_t?
|
||||
size_t key_length;
|
||||
uint32_t refs; // a number of refs to this entry
|
||||
// cache itself is counted as 1
|
||||
// The hash of key(). Used for fast sharding and comparisons.
|
||||
uint32_t hash;
|
||||
// The number of external refs to this entry. The cache itself is not counted.
|
||||
uint32_t refs;
|
||||
|
||||
// Include the following flags:
|
||||
// IN_CACHE: whether this entry is referenced by the hash table.
|
||||
// IS_HIGH_PRI: whether this entry is high priority entry.
|
||||
// IN_HIGH_PRI_POOL: whether this entry is in high-pri pool.
|
||||
// HAS_HIT: whether this entry has had any lookups (hits).
|
||||
enum Flags : uint8_t {
|
||||
// Whether this entry is referenced by the hash table.
|
||||
IN_CACHE = (1 << 0),
|
||||
// Whether this entry is high priority entry.
|
||||
IS_HIGH_PRI = (1 << 1),
|
||||
// Whether this entry is in high-pri pool.
|
||||
IN_HIGH_PRI_POOL = (1 << 2),
|
||||
// Wwhether this entry has had any lookups (hits).
|
||||
HAS_HIT = (1 << 3),
|
||||
};
|
||||
|
||||
uint8_t flags;
|
||||
|
||||
uint32_t hash; // Hash of key(); used for fast sharding and comparisons
|
||||
// Beginning of the key (MUST BE THE LAST FIELD IN THIS STRUCT!)
|
||||
char key_data[1];
|
||||
|
||||
char key_data[1]; // Beginning of key
|
||||
Slice key() const { return Slice(key_data, key_length); }
|
||||
|
||||
Slice key() const {
|
||||
// For cheaper lookups, we allow a temporary Handle object
|
||||
// to store a pointer to a key in "value".
|
||||
if (next == this) {
|
||||
return *(reinterpret_cast<Slice*>(value));
|
||||
} else {
|
||||
return Slice(key_data, key_length);
|
||||
}
|
||||
// Increase the reference count by 1.
|
||||
void Ref() { refs++; }
|
||||
|
||||
// Just reduce the reference count by 1. Return true if it was last reference.
|
||||
bool Unref() {
|
||||
assert(refs > 0);
|
||||
refs--;
|
||||
return refs == 0;
|
||||
}
|
||||
|
||||
// Return true if there are external refs, false otherwise.
|
||||
bool HasRefs() const { return refs > 0; }
|
||||
|
||||
bool InCache() const { return flags & IN_CACHE; }
|
||||
bool IsHighPri() const { return flags & IS_HIGH_PRI; }
|
||||
bool InHighPriPool() const { return flags & IN_HIGH_PRI_POOL; }
|
||||
@@ -114,12 +123,28 @@ struct LRUHandle {
|
||||
void SetHit() { flags |= HAS_HIT; }
|
||||
|
||||
void Free() {
|
||||
assert((refs == 1 && InCache()) || (refs == 0 && !InCache()));
|
||||
assert(refs == 0);
|
||||
if (deleter) {
|
||||
(*deleter)(key(), value);
|
||||
}
|
||||
delete[] reinterpret_cast<char*>(this);
|
||||
}
|
||||
|
||||
// Caclculate the memory usage by metadata
|
||||
inline size_t CalcTotalCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
assert(key_length);
|
||||
size_t meta_charge = 0;
|
||||
if (metadata_charge_policy == kFullChargeCacheMetadata) {
|
||||
#ifdef ROCKSDB_MALLOC_USABLE_SIZE
|
||||
meta_charge += malloc_usable_size(static_cast<void*>(this));
|
||||
#else
|
||||
// This is the size that is used when a new handle is created
|
||||
meta_charge += sizeof(LRUHandle) - 1 + key_length;
|
||||
#endif
|
||||
}
|
||||
return charge + meta_charge;
|
||||
}
|
||||
};
|
||||
|
||||
// We provide our own simple hash table since it removes a whole bunch
|
||||
@@ -168,8 +193,9 @@ class LRUHandleTable {
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
public:
|
||||
LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio, bool use_adaptive_mutex);
|
||||
virtual ~LRUCacheShard();
|
||||
double high_pri_pool_ratio, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy);
|
||||
virtual ~LRUCacheShard() override = default;
|
||||
|
||||
// Separate from constructor so caller can easily make an array of LRUCache
|
||||
// if current usage is more than new capacity, the function will attempt to
|
||||
@@ -225,10 +251,6 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
// high-pri pool is no larger than the size specify by high_pri_pool_pct.
|
||||
void MaintainPoolSize();
|
||||
|
||||
// Just reduce the reference count by 1.
|
||||
// Return true if last reference
|
||||
bool Unref(LRUHandle* e);
|
||||
|
||||
// Free some space following strict LRU policy until enough space
|
||||
// to hold (usage_ + charge) is freed or the lru list is empty
|
||||
// This function is not thread safe - it needs to be executed while
|
||||
@@ -293,7 +315,9 @@ class LRUCache
|
||||
LRUCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex);
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDontChargeCacheMetadata);
|
||||
virtual ~LRUCache();
|
||||
virtual const char* Name() const override { return "LRUCache"; }
|
||||
virtual CacheShard* GetShard(int shard) override;
|
||||
|
||||
Vendored
+2
-1
@@ -31,7 +31,8 @@ class LRUCacheTest : public testing::Test {
|
||||
cache_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard)));
|
||||
new (cache_) LRUCacheShard(capacity, false /*strict_capcity_limit*/,
|
||||
high_pri_pool_ratio, use_adaptive_mutex);
|
||||
high_pri_pool_ratio, use_adaptive_mutex,
|
||||
kDontChargeCacheMetadata);
|
||||
}
|
||||
|
||||
void Insert(const std::string& key,
|
||||
|
||||
Vendored
+9
-1
@@ -40,6 +40,13 @@ class CacheShard {
|
||||
bool thread_safe) = 0;
|
||||
virtual void EraseUnRefEntries() = 0;
|
||||
virtual std::string GetPrintableOptions() const { return ""; }
|
||||
void set_metadata_charge_policy(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
metadata_charge_policy_ = metadata_charge_policy;
|
||||
}
|
||||
|
||||
protected:
|
||||
CacheMetadataChargePolicy metadata_charge_policy_ = kDontChargeCacheMetadata;
|
||||
};
|
||||
|
||||
// Generic cache interface which shards cache by hash of keys. 2^num_shard_bits
|
||||
@@ -54,7 +61,8 @@ class ShardedCache : public Cache {
|
||||
virtual CacheShard* GetShard(int shard) = 0;
|
||||
virtual const CacheShard* GetShard(int shard) const = 0;
|
||||
virtual void* Value(Handle* handle) override = 0;
|
||||
virtual size_t GetCharge(Handle* handle) const = 0;
|
||||
virtual size_t GetCharge(Handle* handle) const override = 0;
|
||||
|
||||
virtual uint32_t GetHash(Handle* handle) const = 0;
|
||||
virtual void DisownData() override = 0;
|
||||
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
# - Find JeMalloc library
|
||||
# Find the native JeMalloc includes and library
|
||||
#
|
||||
# JEMALLOC_INCLUDE_DIR - where to find jemalloc.h, etc.
|
||||
# JEMALLOC_LIBRARIES - List of libraries when using jemalloc.
|
||||
# JEMALLOC_FOUND - True if jemalloc found.
|
||||
# JeMalloc_INCLUDE_DIRS - where to find jemalloc.h, etc.
|
||||
# JeMalloc_LIBRARIES - List of libraries when using jemalloc.
|
||||
# JeMalloc_FOUND - True if jemalloc found.
|
||||
|
||||
find_path(JEMALLOC_INCLUDE_DIR
|
||||
find_path(JeMalloc_INCLUDE_DIRS
|
||||
NAMES jemalloc/jemalloc.h
|
||||
HINTS ${JEMALLOC_ROOT_DIR}/include)
|
||||
|
||||
find_library(JEMALLOC_LIBRARIES
|
||||
find_library(JeMalloc_LIBRARIES
|
||||
NAMES jemalloc
|
||||
HINTS ${JEMALLOC_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(jemalloc DEFAULT_MSG JEMALLOC_LIBRARIES JEMALLOC_INCLUDE_DIR)
|
||||
find_package_handle_standard_args(JeMalloc DEFAULT_MSG JeMalloc_LIBRARIES JeMalloc_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
JEMALLOC_LIBRARIES
|
||||
JEMALLOC_INCLUDE_DIR)
|
||||
JeMalloc_LIBRARIES
|
||||
JeMalloc_INCLUDE_DIRS)
|
||||
|
||||
if(JeMalloc_FOUND AND NOT (TARGET JeMalloc::JeMalloc))
|
||||
add_library (JeMalloc::JeMalloc UNKNOWN IMPORTED)
|
||||
set_target_properties(JeMalloc::JeMalloc
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${JeMalloc_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${JeMalloc_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# - Find NUMA
|
||||
# Find the NUMA library and includes
|
||||
#
|
||||
# NUMA_INCLUDE_DIR - where to find numa.h, etc.
|
||||
# NUMA_INCLUDE_DIRS - where to find numa.h, etc.
|
||||
# NUMA_LIBRARIES - List of libraries when using NUMA.
|
||||
# NUMA_FOUND - True if NUMA found.
|
||||
|
||||
find_path(NUMA_INCLUDE_DIR
|
||||
find_path(NUMA_INCLUDE_DIRS
|
||||
NAMES numa.h numaif.h
|
||||
HINTS ${NUMA_ROOT_DIR}/include)
|
||||
|
||||
@@ -14,8 +14,16 @@ find_library(NUMA_LIBRARIES
|
||||
HINTS ${NUMA_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(NUMA DEFAULT_MSG NUMA_LIBRARIES NUMA_INCLUDE_DIR)
|
||||
find_package_handle_standard_args(NUMA DEFAULT_MSG NUMA_LIBRARIES NUMA_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
NUMA_LIBRARIES
|
||||
NUMA_INCLUDE_DIR)
|
||||
NUMA_INCLUDE_DIRS)
|
||||
|
||||
if(NUMA_FOUND AND NOT (TARGET NUMA::NUMA))
|
||||
add_library (NUMA::NUMA UNKNOWN IMPORTED)
|
||||
set_target_properties(NUMA::NUMA
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${NUMA_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${NUMA_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# - Find TBB
|
||||
# Find the Thread Building Blocks library and includes
|
||||
#
|
||||
# TBB_INCLUDE_DIR - where to find tbb.h, etc.
|
||||
# TBB_INCLUDE_DIRS - where to find tbb.h, etc.
|
||||
# TBB_LIBRARIES - List of libraries when using TBB.
|
||||
# TBB_FOUND - True if TBB found.
|
||||
|
||||
@@ -9,17 +9,25 @@ 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_path(TBB_INCLUDE_DIRS
|
||||
NAMES tbb/tbb.h
|
||||
HINTS ${TBB_ROOT_DIR}/include)
|
||||
|
||||
find_library(TBB_LIBRARIES
|
||||
NAMES tbb
|
||||
HINTS ${TBB_ROOT_DIR}/lib ENV LIBRARY_PATH)
|
||||
NAMES tbb
|
||||
HINTS ${TBB_ROOT_DIR}/lib ENV LIBRARY_PATH)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(TBB DEFAULT_MSG TBB_LIBRARIES TBB_INCLUDE_DIR)
|
||||
find_package_handle_standard_args(TBB DEFAULT_MSG TBB_LIBRARIES TBB_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
TBB_LIBRARIES
|
||||
TBB_INCLUDE_DIR)
|
||||
TBB_LIBRARIES
|
||||
TBB_INCLUDE_DIRS)
|
||||
|
||||
if(TBB_FOUND AND NOT (TARGET TBB::TBB))
|
||||
add_library (TBB::TBB UNKNOWN IMPORTED)
|
||||
set_target_properties(TBB::TBB
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${TBB_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# - Find Bzip2
|
||||
# Find the bzip2 compression library and includes
|
||||
#
|
||||
# BZIP2_INCLUDE_DIR - where to find bzlib.h, etc.
|
||||
# BZIP2_LIBRARIES - List of libraries when using bzip2.
|
||||
# BZIP2_FOUND - True if bzip2 found.
|
||||
|
||||
find_path(BZIP2_INCLUDE_DIR
|
||||
NAMES bzlib.h
|
||||
HINTS ${BZIP2_ROOT_DIR}/include)
|
||||
|
||||
find_library(BZIP2_LIBRARIES
|
||||
NAMES bz2
|
||||
HINTS ${BZIP2_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(bzip2 DEFAULT_MSG BZIP2_LIBRARIES BZIP2_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
BZIP2_LIBRARIES
|
||||
BZIP2_INCLUDE_DIR)
|
||||
+18
-10
@@ -1,21 +1,29 @@
|
||||
# - Find Lz4
|
||||
# Find the lz4 compression library and includes
|
||||
#
|
||||
# LZ4_INCLUDE_DIR - where to find lz4.h, etc.
|
||||
# LZ4_LIBRARIES - List of libraries when using lz4.
|
||||
# LZ4_FOUND - True if lz4 found.
|
||||
# lz4_INCLUDE_DIRS - where to find lz4.h, etc.
|
||||
# lz4_LIBRARIES - List of libraries when using lz4.
|
||||
# lz4_FOUND - True if lz4 found.
|
||||
|
||||
find_path(LZ4_INCLUDE_DIR
|
||||
find_path(lz4_INCLUDE_DIRS
|
||||
NAMES lz4.h
|
||||
HINTS ${LZ4_ROOT_DIR}/include)
|
||||
HINTS ${lz4_ROOT_DIR}/include)
|
||||
|
||||
find_library(LZ4_LIBRARIES
|
||||
find_library(lz4_LIBRARIES
|
||||
NAMES lz4
|
||||
HINTS ${LZ4_ROOT_DIR}/lib)
|
||||
HINTS ${lz4_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(lz4 DEFAULT_MSG LZ4_LIBRARIES LZ4_INCLUDE_DIR)
|
||||
find_package_handle_standard_args(lz4 DEFAULT_MSG lz4_LIBRARIES lz4_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
LZ4_LIBRARIES
|
||||
LZ4_INCLUDE_DIR)
|
||||
lz4_LIBRARIES
|
||||
lz4_INCLUDE_DIRS)
|
||||
|
||||
if(lz4_FOUND AND NOT (TARGET lz4::lz4))
|
||||
add_library(lz4::lz4 UNKNOWN IMPORTED)
|
||||
set_target_properties(lz4::lz4
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${lz4_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${lz4_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# SNAPPY_INCLUDE_DIR - where to find snappy.h, etc.
|
||||
# SNAPPY_LIBRARIES - List of libraries when using snappy.
|
||||
# SNAPPY_FOUND - True if snappy found.
|
||||
# snappy_INCLUDE_DIRS - where to find snappy.h, etc.
|
||||
# snappy_LIBRARIES - List of libraries when using snappy.
|
||||
# snappy_FOUND - True if snappy found.
|
||||
|
||||
find_path(SNAPPY_INCLUDE_DIR
|
||||
find_path(snappy_INCLUDE_DIRS
|
||||
NAMES snappy.h
|
||||
HINTS ${SNAPPY_ROOT_DIR}/include)
|
||||
HINTS ${snappy_ROOT_DIR}/include)
|
||||
|
||||
find_library(SNAPPY_LIBRARIES
|
||||
find_library(snappy_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${SNAPPY_ROOT_DIR}/lib)
|
||||
HINTS ${snappy_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(snappy DEFAULT_MSG SNAPPY_LIBRARIES SNAPPY_INCLUDE_DIR)
|
||||
find_package_handle_standard_args(snappy DEFAULT_MSG snappy_LIBRARIES snappy_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
SNAPPY_LIBRARIES
|
||||
SNAPPY_INCLUDE_DIR)
|
||||
snappy_LIBRARIES
|
||||
snappy_INCLUDE_DIRS)
|
||||
|
||||
if(snappy_FOUND AND NOT (TARGET snappy::snappy))
|
||||
add_library (snappy::snappy UNKNOWN IMPORTED)
|
||||
set_target_properties(snappy::snappy
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${snappy_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${snappy_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
# - Find zstd
|
||||
# Find the zstd compression library and includes
|
||||
#
|
||||
# ZSTD_INCLUDE_DIR - where to find zstd.h, etc.
|
||||
# ZSTD_LIBRARIES - List of libraries when using zstd.
|
||||
# ZSTD_FOUND - True if zstd found.
|
||||
# zstd_INCLUDE_DIRS - where to find zstd.h, etc.
|
||||
# zstd_LIBRARIES - List of libraries when using zstd.
|
||||
# zstd_FOUND - True if zstd found.
|
||||
|
||||
find_path(ZSTD_INCLUDE_DIR
|
||||
find_path(zstd_INCLUDE_DIRS
|
||||
NAMES zstd.h
|
||||
HINTS ${ZSTD_ROOT_DIR}/include)
|
||||
HINTS ${zstd_ROOT_DIR}/include)
|
||||
|
||||
find_library(ZSTD_LIBRARIES
|
||||
find_library(zstd_LIBRARIES
|
||||
NAMES zstd
|
||||
HINTS ${ZSTD_ROOT_DIR}/lib)
|
||||
HINTS ${zstd_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG ZSTD_LIBRARIES ZSTD_INCLUDE_DIR)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES zstd_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
ZSTD_LIBRARIES
|
||||
ZSTD_INCLUDE_DIR)
|
||||
zstd_LIBRARIES
|
||||
zstd_INCLUDE_DIRS)
|
||||
|
||||
if(zstd_FOUND AND NOT (TARGET zstd::zstd))
|
||||
add_library (zstd::zstd UNKNOWN IMPORTED)
|
||||
set_target_properties(zstd::zstd
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${zstd_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${zstd_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Read rocksdb version from version.h header file.
|
||||
|
||||
function(get_rocksdb_version version_var)
|
||||
file(READ "${CMAKE_SOURCE_DIR}/include/rocksdb/version.h" version_header_file)
|
||||
foreach(component MAJOR MINOR PATCH)
|
||||
string(REGEX MATCH "#define ROCKSDB_${component} ([0-9]+)" _ ${version_header_file})
|
||||
set(ROCKSDB_VERSION_${component} ${CMAKE_MATCH_1})
|
||||
endforeach()
|
||||
set(${version_var} "${ROCKSDB_VERSION_MAJOR}.${ROCKSDB_VERSION_MINOR}.${ROCKSDB_VERSION_PATCH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
import re
|
||||
import sys
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
#include "memory/arena.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
#include "util/user_comparator_wrapper.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
|
||||
std::string* prop) {
|
||||
if (prop_name == "rocksdb.iterator.super-version-number") {
|
||||
// First try to pass the value returned from inner iterator.
|
||||
if (!db_iter_->GetProperty(prop_name, prop).ok()) {
|
||||
*prop = ToString(sv_number_);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
return db_iter_->GetProperty(prop_name, prop);
|
||||
}
|
||||
|
||||
void ArenaWrappedDBIter::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_iteration,
|
||||
uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool allow_blob,
|
||||
bool allow_refresh) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem) DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
cf_options.user_comparator, nullptr, sequence,
|
||||
true, max_sequential_skip_in_iteration,
|
||||
read_callback, db_impl, cfd, allow_blob);
|
||||
sv_number_ = version_number;
|
||||
allow_refresh_ = allow_refresh;
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh() {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr || !allow_refresh_) {
|
||||
return Status::NotSupported("Creating renew iterator is not allowed.");
|
||||
}
|
||||
assert(db_iter_ != nullptr);
|
||||
// TODO(yiwu): For last_seq_same_as_publish_seq_==false, this is not the
|
||||
// correct behavior. Will be corrected automatically when we take a snapshot
|
||||
// here for the case of WritePreparedTxnDB.
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_->mutex());
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
latest_seq, sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, allow_blob_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
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, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob, bool allow_refresh) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, cf_options, mutable_cf_options, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
db_impl, cfd, allow_blob, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(read_options, db_impl, cfd, read_callback,
|
||||
allow_blob);
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "memory/arena.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class Arena;
|
||||
|
||||
// A wrapper iterator which wraps DB Iterator and the arena, with which the DB
|
||||
// iterator is supposed to be allocated. This class is used as an entry point of
|
||||
// a iterator hierarchy whose memory can be allocated inline. In that way,
|
||||
// accessing the iterator tree can be more cache friendly. It is also faster
|
||||
// to allocate.
|
||||
// When using the class's Iterator interface, the behavior is exactly
|
||||
// the same as the inner DBIter.
|
||||
class ArenaWrappedDBIter : public Iterator {
|
||||
public:
|
||||
virtual ~ArenaWrappedDBIter() { db_iter_->~DBIter(); }
|
||||
|
||||
// Get the arena to be used to allocate memory for DBIter to be wrapped,
|
||||
// as well as child iterators in it.
|
||||
virtual Arena* GetArena() { return &arena_; }
|
||||
virtual ReadRangeDelAggregator* GetRangeDelAggregator() {
|
||||
return db_iter_->GetRangeDelAggregator();
|
||||
}
|
||||
|
||||
// Set the internal iterator wrapped inside the DB Iterator. Usually it is
|
||||
// a merging iterator.
|
||||
virtual void SetIterUnderDBIter(InternalIterator* iter) {
|
||||
static_cast<DBIter*>(db_iter_)->SetIter(iter);
|
||||
}
|
||||
|
||||
virtual bool Valid() const override { return db_iter_->Valid(); }
|
||||
virtual void SeekToFirst() override { db_iter_->SeekToFirst(); }
|
||||
virtual void SeekToLast() override { db_iter_->SeekToLast(); }
|
||||
virtual void Seek(const Slice& target) override { db_iter_->Seek(target); }
|
||||
virtual void SeekForPrev(const Slice& target) override {
|
||||
db_iter_->SeekForPrev(target);
|
||||
}
|
||||
virtual void Next() override { db_iter_->Next(); }
|
||||
virtual void Prev() override { db_iter_->Prev(); }
|
||||
virtual Slice key() const override { return db_iter_->key(); }
|
||||
virtual Slice value() const override { return db_iter_->value(); }
|
||||
virtual Status status() const override { return db_iter_->status(); }
|
||||
bool IsBlob() const { return db_iter_->IsBlob(); }
|
||||
|
||||
virtual Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
|
||||
virtual Status Refresh() override;
|
||||
|
||||
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, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob, bool allow_refresh);
|
||||
|
||||
// Store some parameters so we can refresh the iterator at a later point
|
||||
// with these same params
|
||||
void StoreRefreshInfo(const ReadOptions& read_options, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, ReadCallback* read_callback,
|
||||
bool allow_blob) {
|
||||
read_options_ = read_options;
|
||||
db_impl_ = db_impl;
|
||||
cfd_ = cfd;
|
||||
read_callback_ = read_callback;
|
||||
allow_blob_ = allow_blob;
|
||||
}
|
||||
|
||||
private:
|
||||
DBIter* db_iter_;
|
||||
Arena arena_;
|
||||
uint64_t sv_number_;
|
||||
ColumnFamilyData* cfd_ = nullptr;
|
||||
DBImpl* db_impl_ = nullptr;
|
||||
ReadOptions read_options_;
|
||||
ReadCallback* read_callback_;
|
||||
bool allow_blob_ = false;
|
||||
bool allow_refresh_ = true;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
// `db_impl` and `cfd` are used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
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, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false,
|
||||
bool allow_refresh = true);
|
||||
} // namespace rocksdb
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace blob_db {
|
||||
|
||||
// BlobIndex is a pointer to the blob and metadata of the blob. The index is
|
||||
// stored in base DB as ValueType::kTypeBlobIndex.
|
||||
@@ -156,6 +155,5 @@ class BlobIndex {
|
||||
CompressionType compression_ = kNoCompression;
|
||||
};
|
||||
|
||||
} // namespace blob_db
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
+12
-6
@@ -22,6 +22,8 @@
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/read_write_util.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -33,7 +35,6 @@
|
||||
#include "table/format.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -123,7 +124,7 @@ Status BuildTable(
|
||||
if (!s.ok()) {
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger, ioptions.listeners, dbname, column_family_name, fname,
|
||||
job_id, meta->fd, tp, reason, s);
|
||||
job_id, meta->fd, kInvalidBlobFileNumber, tp, reason, s);
|
||||
return s;
|
||||
}
|
||||
file->SetIOPriority(io_priority);
|
||||
@@ -156,8 +157,9 @@ Status BuildTable(
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
const Slice& key = c_iter.key();
|
||||
const Slice& value = c_iter.value();
|
||||
const ParsedInternalKey& ikey = c_iter.ikey();
|
||||
builder->Add(key, value);
|
||||
meta->UpdateBoundaries(key, c_iter.ikey().sequence);
|
||||
meta->UpdateBoundaries(key, value, ikey.sequence, ikey.type);
|
||||
|
||||
// TODO(noetzli): Update stats after flush, too.
|
||||
if (io_priority == Env::IO_HIGH &&
|
||||
@@ -221,8 +223,9 @@ Status BuildTable(
|
||||
mutable_cf_options.prefix_extractor.get(), nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
false /* for_compaction */, nullptr /* arena */,
|
||||
false /* skip_filter */, level));
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
/*skip_filter=*/false, level, /*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr));
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
@@ -241,10 +244,13 @@ Status BuildTable(
|
||||
env->DeleteFile(fname);
|
||||
}
|
||||
|
||||
if (meta->fd.GetFileSize() == 0) {
|
||||
fname = "(nil)";
|
||||
}
|
||||
// Output to event logger and fire events.
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger, ioptions.listeners, dbname, column_family_name, fname,
|
||||
job_id, meta->fd, tp, reason, s);
|
||||
job_id, meta->fd, meta->oldest_blob_file_number, tp, reason, s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -517,6 +517,21 @@ rocksdb_t* rocksdb_open_for_read_only(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_as_secondary(const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
const char* secondary_path,
|
||||
char** errptr) {
|
||||
DB* db;
|
||||
if (SaveError(errptr,
|
||||
DB::OpenAsSecondary(options->rep, std::string(name),
|
||||
std::string(secondary_path), &db))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_backup_engine_t* rocksdb_backup_engine_open(
|
||||
const rocksdb_options_t* options, const char* path, char** errptr) {
|
||||
BackupEngine* be;
|
||||
@@ -717,6 +732,37 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_as_secondary_column_families(
|
||||
const rocksdb_options_t* db_options, const char* name,
|
||||
const char* secondary_path, int num_column_families,
|
||||
const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i != num_column_families; ++i) {
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
DB* db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
if (SaveError(errptr, DB::OpenAsSecondary(DBOptions(db_options->rep),
|
||||
std::string(name),
|
||||
std::string(secondary_path),
|
||||
column_families, &handles, &db))) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i != handles.size(); ++i) {
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
char** rocksdb_list_column_families(
|
||||
const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
@@ -988,7 +1034,7 @@ void rocksdb_wal_iter_destroy (const rocksdb_wal_iterator_t* 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();
|
||||
result->rep = std::move(*wal_batch.writeBatchPtr);
|
||||
if (seq != nullptr) {
|
||||
*seq = wal_batch.sequence;
|
||||
}
|
||||
@@ -2226,11 +2272,6 @@ void rocksdb_options_set_max_bytes_for_level_base(
|
||||
opt->rep.max_bytes_for_level_base = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_snap_refresh_nanos(rocksdb_options_t* opt,
|
||||
uint64_t n) {
|
||||
opt->rep.snap_refresh_nanos = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.level_compaction_dynamic_level_bytes = v;
|
||||
@@ -2468,6 +2509,11 @@ void rocksdb_options_set_max_write_buffer_number_to_maintain(
|
||||
opt->rep.max_write_buffer_number_to_maintain = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_size_to_maintain(
|
||||
rocksdb_options_t* opt, int64_t n) {
|
||||
opt->rep.max_write_buffer_size_to_maintain = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_enable_pipelined_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.enable_pipelined_write = v;
|
||||
@@ -3173,6 +3219,11 @@ void rocksdb_writeoptions_set_low_pri(
|
||||
opt->rep.low_pri = v;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_set_memtable_insert_hint_per_batch(
|
||||
rocksdb_writeoptions_t* opt, unsigned char v) {
|
||||
opt->rep.memtable_insert_hint_per_batch = v;
|
||||
}
|
||||
|
||||
rocksdb_compactoptions_t* rocksdb_compactoptions_create() {
|
||||
return new rocksdb_compactoptions_t;
|
||||
}
|
||||
@@ -3423,6 +3474,10 @@ void rocksdb_ingest_external_file_cf(
|
||||
SaveError(errptr, db->rep->IngestExternalFile(handle->rep, files, opt->rep));
|
||||
}
|
||||
|
||||
void rocksdb_try_catch_up_with_primary(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->TryCatchUpWithPrimary());
|
||||
}
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create(
|
||||
void* state,
|
||||
void (*destructor)(void*),
|
||||
|
||||
+54
@@ -45,6 +45,7 @@ static char sstfilename[200];
|
||||
static char dbbackupname[200];
|
||||
static char dbcheckpointname[200];
|
||||
static char dbpathname[200];
|
||||
static char secondary_path[200];
|
||||
|
||||
static void StartPhase(const char* name) {
|
||||
fprintf(stderr, "=== Test %s\n", name);
|
||||
@@ -1722,6 +1723,59 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
// Check that secondary instance works.
|
||||
StartPhase("open_as_secondary");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
|
||||
rocksdb_options_t* db_options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(db_options, 1);
|
||||
db = rocksdb_open(db_options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_t* db1;
|
||||
rocksdb_options_t* opts = rocksdb_options_create();
|
||||
rocksdb_options_set_max_open_files(opts, -1);
|
||||
rocksdb_options_set_create_if_missing(opts, 1);
|
||||
snprintf(secondary_path, sizeof(secondary_path),
|
||||
"%s/rocksdb_c_test_secondary-%d", GetTempDir(), ((int)geteuid()));
|
||||
db1 = rocksdb_open_as_secondary(opts, dbname, secondary_path, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_writeoptions_set_sync(woptions, 0);
|
||||
rocksdb_writeoptions_disable_WAL(woptions, 1);
|
||||
rocksdb_put(db, woptions, "key0", 4, "value0", 6, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_flushoptions_t* flush_opts = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_opts, 1);
|
||||
rocksdb_flush(db, flush_opts, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_try_catch_up_with_primary(db1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_readoptions_t* ropts = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_verify_checksums(ropts, 1);
|
||||
rocksdb_readoptions_set_snapshot(ropts, NULL);
|
||||
CheckGet(db, ropts, "key0", "value0");
|
||||
CheckGet(db1, ropts, "key0", "value0");
|
||||
|
||||
rocksdb_writeoptions_disable_WAL(woptions, 0);
|
||||
rocksdb_put(db, woptions, "key1", 4, "value1", 6, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_try_catch_up_with_primary(db1, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db1, ropts, "key0", "value0");
|
||||
CheckGet(db1, ropts, "key1", "value1");
|
||||
|
||||
rocksdb_close(db1);
|
||||
rocksdb_destroy_db(opts, secondary_path, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_destroy(db_options);
|
||||
rocksdb_options_destroy(opts);
|
||||
rocksdb_readoptions_destroy(ropts);
|
||||
rocksdb_flushoptions_destroy(flush_opts);
|
||||
}
|
||||
|
||||
// Simple sanity check that options setting db_paths work.
|
||||
StartPhase("open_db_paths");
|
||||
{
|
||||
|
||||
+13
-5
@@ -9,11 +9,11 @@
|
||||
|
||||
#include "db/column_family.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
@@ -227,7 +227,14 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
if (result.max_write_buffer_number < 2) {
|
||||
result.max_write_buffer_number = 2;
|
||||
}
|
||||
if (result.max_write_buffer_number_to_maintain < 0) {
|
||||
// fall back max_write_buffer_number_to_maintain if
|
||||
// max_write_buffer_size_to_maintain is not set
|
||||
if (result.max_write_buffer_size_to_maintain < 0) {
|
||||
result.max_write_buffer_size_to_maintain =
|
||||
result.max_write_buffer_number *
|
||||
static_cast<int64_t>(result.write_buffer_size);
|
||||
} else if (result.max_write_buffer_size_to_maintain == 0 &&
|
||||
result.max_write_buffer_number_to_maintain < 0) {
|
||||
result.max_write_buffer_number_to_maintain = result.max_write_buffer_number;
|
||||
}
|
||||
// bloom filter size shouldn't exceed 1/4 of memtable size.
|
||||
@@ -423,7 +430,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
mem_(nullptr),
|
||||
imm_(ioptions_.min_write_buffer_number_to_merge,
|
||||
ioptions_.max_write_buffer_number_to_maintain),
|
||||
ioptions_.max_write_buffer_number_to_maintain,
|
||||
ioptions_.max_write_buffer_size_to_maintain),
|
||||
super_version_(nullptr),
|
||||
super_version_number_(0),
|
||||
local_sv_(new ThreadLocalPtr(&SuperVersionUnrefHandle)),
|
||||
|
||||
+2
-2
@@ -275,7 +275,7 @@ class ColumnFamilyData {
|
||||
// Ref() can only be called from a context where the caller can guarantee
|
||||
// that ColumnFamilyData is alive (while holding a non-zero ref already,
|
||||
// holding a DB mutex, or as the leader in a write batch group).
|
||||
void Ref() { refs_.fetch_add(1, std::memory_order_relaxed); }
|
||||
void Ref() { refs_.fetch_add(1); }
|
||||
|
||||
// Unref decreases the reference count, but does not handle deletion
|
||||
// when the count goes to 0. If this method returns true then the
|
||||
@@ -283,7 +283,7 @@ class ColumnFamilyData {
|
||||
// FreeDeadColumnFamilies(). Unref() can only be called while holding
|
||||
// a DB mutex, or during single-threaded recovery.
|
||||
bool Unref() {
|
||||
int old_refs = refs_.fetch_sub(1, std::memory_order_relaxed);
|
||||
int old_refs = refs_.fetch_sub(1);
|
||||
assert(old_refs > 0);
|
||||
return old_refs == 1;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
@@ -60,8 +62,20 @@ class EnvCounter : public EnvWrapper {
|
||||
|
||||
class ColumnFamilyTestBase : public testing::Test {
|
||||
public:
|
||||
ColumnFamilyTestBase(uint32_t format) : rnd_(139), format_(format) {
|
||||
env_ = new EnvCounter(Env::Default());
|
||||
explicit ColumnFamilyTestBase(uint32_t format) : rnd_(139), format_(format) {
|
||||
Env* base_env = Env::Default();
|
||||
#ifndef ROCKSDB_LITE
|
||||
const char* test_env_uri = getenv("TEST_ENV_URI");
|
||||
if (test_env_uri) {
|
||||
Status s = ObjectRegistry::NewInstance()->NewSharedObject(test_env_uri,
|
||||
&env_guard_);
|
||||
base_env = env_guard_.get();
|
||||
EXPECT_OK(s);
|
||||
EXPECT_NE(Env::Default(), base_env);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
EXPECT_NE(nullptr, base_env);
|
||||
env_ = new EnvCounter(base_env);
|
||||
dbname_ = test::PerThreadDBPath("column_family_test");
|
||||
db_options_.create_if_missing = true;
|
||||
db_options_.fail_if_options_file_error = true;
|
||||
@@ -532,6 +546,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
std::string dbname_;
|
||||
DB* db_ = nullptr;
|
||||
EnvCounter* env_;
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
Random rnd_;
|
||||
uint32_t format_;
|
||||
};
|
||||
@@ -1117,22 +1132,25 @@ TEST_P(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
default_cf.arena_block_size = 4 * 4096;
|
||||
default_cf.max_write_buffer_number = 10;
|
||||
default_cf.min_write_buffer_number_to_merge = 1;
|
||||
default_cf.max_write_buffer_number_to_maintain = 0;
|
||||
default_cf.max_write_buffer_size_to_maintain = 0;
|
||||
one.write_buffer_size = 200000;
|
||||
one.arena_block_size = 4 * 4096;
|
||||
one.max_write_buffer_number = 10;
|
||||
one.min_write_buffer_number_to_merge = 2;
|
||||
one.max_write_buffer_number_to_maintain = 1;
|
||||
one.max_write_buffer_size_to_maintain =
|
||||
static_cast<int>(one.write_buffer_size);
|
||||
two.write_buffer_size = 1000000;
|
||||
two.arena_block_size = 4 * 4096;
|
||||
two.max_write_buffer_number = 10;
|
||||
two.min_write_buffer_number_to_merge = 3;
|
||||
two.max_write_buffer_number_to_maintain = 2;
|
||||
two.max_write_buffer_size_to_maintain =
|
||||
static_cast<int>(two.write_buffer_size);
|
||||
three.write_buffer_size = 4096 * 22;
|
||||
three.arena_block_size = 4096;
|
||||
three.max_write_buffer_number = 10;
|
||||
three.min_write_buffer_number_to_merge = 4;
|
||||
three.max_write_buffer_number_to_maintain = -1;
|
||||
three.max_write_buffer_size_to_maintain =
|
||||
static_cast<int>(three.write_buffer_size);
|
||||
|
||||
Reopen({default_cf, one, two, three});
|
||||
|
||||
@@ -3312,7 +3330,17 @@ TEST_P(ColumnFamilyTest, MultipleCFPathsTest) {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
extern "C" {
|
||||
void RegisterCustomObjects(int argc, char** argv);
|
||||
}
|
||||
#else
|
||||
void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
|
||||
#endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
const Slice& key, PinnableSlice* value) {
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, key, value, nullptr, nullptr,
|
||||
nullptr, nullptr);
|
||||
true, nullptr, nullptr);
|
||||
LookupKey lkey(key, kMaxSequenceNumber);
|
||||
files_.files[FindFile(key)].fd.table_reader->Get(options, lkey.internal_key(),
|
||||
&get_context, nullptr);
|
||||
@@ -70,7 +70,7 @@ std::vector<Status> CompactedDBImpl::MultiGet(const ReadOptions& options,
|
||||
std::string& value = (*values)[idx];
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, keys[idx], &pinnable_val,
|
||||
nullptr, nullptr, nullptr, nullptr);
|
||||
nullptr, nullptr, true, nullptr, nullptr);
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber);
|
||||
r->Get(options, lkey.internal_key(), &get_context, nullptr);
|
||||
value.assign(pinnable_val.data(), pinnable_val.size());
|
||||
|
||||
+13
-4
@@ -14,6 +14,10 @@ namespace rocksdb {
|
||||
class CompactedDBImpl : public DBImpl {
|
||||
public:
|
||||
CompactedDBImpl(const DBOptions& options, const std::string& dbname);
|
||||
// No copying allowed
|
||||
CompactedDBImpl(const CompactedDBImpl&) = delete;
|
||||
void operator=(const CompactedDBImpl&) = delete;
|
||||
|
||||
virtual ~CompactedDBImpl();
|
||||
|
||||
static Status Open(const Options& options, const std::string& dbname,
|
||||
@@ -85,6 +89,15 @@ class CompactedDBImpl : public DBImpl {
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
@@ -95,10 +108,6 @@ class CompactedDBImpl : public DBImpl {
|
||||
Version* version_;
|
||||
const Comparator* user_comparator_;
|
||||
LevelFilesBrief files_;
|
||||
|
||||
// No copying allowed
|
||||
CompactedDBImpl(const CompactedDBImpl&);
|
||||
void operator=(const CompactedDBImpl&);
|
||||
};
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -38,7 +38,7 @@ CompactionIterator::CompactionIterator(
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
SnapshotListFetchCallback* snap_list_callback)
|
||||
const std::atomic<bool>* manual_compaction_paused)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, env,
|
||||
@@ -46,7 +46,7 @@ CompactionIterator::CompactionIterator(
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new CompactionProxy(compaction) : nullptr),
|
||||
compaction_filter, shutting_down, preserve_deletes_seqnum,
|
||||
snap_list_callback) {}
|
||||
manual_compaction_paused) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
@@ -59,7 +59,7 @@ CompactionIterator::CompactionIterator(
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
SnapshotListFetchCallback* snap_list_callback)
|
||||
const std::atomic<bool>* manual_compaction_paused)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
@@ -73,12 +73,12 @@ CompactionIterator::CompactionIterator(
|
||||
compaction_(std::move(compaction)),
|
||||
compaction_filter_(compaction_filter),
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
current_user_key_sequence_(0),
|
||||
current_user_key_snapshot_(0),
|
||||
merge_out_iter_(merge_helper_),
|
||||
current_key_committed_(false),
|
||||
snap_list_callback_(snap_list_callback) {
|
||||
current_key_committed_(false) {
|
||||
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
||||
assert(snapshots_ != nullptr);
|
||||
bottommost_level_ =
|
||||
@@ -86,7 +86,24 @@ CompactionIterator::CompactionIterator(
|
||||
if (compaction_ != nullptr) {
|
||||
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
||||
}
|
||||
ProcessSnapshotList();
|
||||
if (snapshots_->size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = true;
|
||||
earliest_snapshot_iter_ = snapshots_->end();
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
latest_snapshot_ = 0;
|
||||
} else {
|
||||
visible_at_tip_ = false;
|
||||
earliest_snapshot_iter_ = snapshots_->begin();
|
||||
earliest_snapshot_ = snapshots_->at(0);
|
||||
latest_snapshot_ = snapshots_->back();
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
// findEarliestVisibleSnapshot assumes this ordering.
|
||||
for (size_t i = 1; i < snapshots_->size(); ++i) {
|
||||
assert(snapshots_->at(i - 1) < snapshots_->at(i));
|
||||
}
|
||||
#endif
|
||||
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator:AfterInit", compaction_.get());
|
||||
}
|
||||
@@ -208,33 +225,12 @@ void CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionIterator::ProcessSnapshotList() {
|
||||
#ifndef NDEBUG
|
||||
// findEarliestVisibleSnapshot assumes this ordering.
|
||||
for (size_t i = 1; i < snapshots_->size(); ++i) {
|
||||
assert(snapshots_->at(i - 1) < snapshots_->at(i));
|
||||
}
|
||||
#endif
|
||||
if (snapshots_->size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = true;
|
||||
earliest_snapshot_iter_ = snapshots_->end();
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
latest_snapshot_ = 0;
|
||||
} else {
|
||||
visible_at_tip_ = false;
|
||||
earliest_snapshot_iter_ = snapshots_->begin();
|
||||
earliest_snapshot_ = snapshots_->at(0);
|
||||
latest_snapshot_ = snapshots_->back();
|
||||
}
|
||||
released_snapshots_.clear();
|
||||
}
|
||||
|
||||
void CompactionIterator::NextFromInput() {
|
||||
at_next_ = false;
|
||||
valid_ = false;
|
||||
|
||||
while (!valid_ && input_->Valid() && !IsShuttingDown()) {
|
||||
while (!valid_ && input_->Valid() && !IsPausingManualCompaction() &&
|
||||
!IsShuttingDown()) {
|
||||
key_ = input_->key();
|
||||
value_ = input_->value();
|
||||
iter_stats_.num_input_records++;
|
||||
@@ -277,13 +273,6 @@ void CompactionIterator::NextFromInput() {
|
||||
// compaction filter). ikey_.user_key is pointing to the copy.
|
||||
if (!has_current_user_key_ ||
|
||||
!cmp_->Equal(ikey_.user_key, current_user_key_)) {
|
||||
num_keys_++;
|
||||
// Use num_keys_ to reduce the overhead of reading current time
|
||||
if (snap_list_callback_ && snapshots_->size() &&
|
||||
snap_list_callback_->TimeToRefresh(num_keys_)) {
|
||||
snap_list_callback_->Refresh(snapshots_, latest_snapshot_);
|
||||
ProcessSnapshotList();
|
||||
}
|
||||
// First occurrence of this user key
|
||||
// Copy key for output
|
||||
key_ = current_key_.SetInternalKey(key_, &ikey_);
|
||||
@@ -612,6 +601,10 @@ void CompactionIterator::NextFromInput() {
|
||||
if (!valid_ && IsShuttingDown()) {
|
||||
status_ = Status::ShutdownInProgress();
|
||||
}
|
||||
|
||||
if (IsPausingManualCompaction()) {
|
||||
status_ = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionIterator::PrepareOutput() {
|
||||
|
||||
@@ -21,53 +21,6 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// This callback can be used to refresh the snapshot list from the db. It
|
||||
// includes logics to exponentially decrease the refresh rate to limit the
|
||||
// overhead of refresh.
|
||||
class SnapshotListFetchCallback {
|
||||
public:
|
||||
SnapshotListFetchCallback(Env* env, uint64_t snap_refresh_nanos,
|
||||
size_t every_nth_key = 1024)
|
||||
: timer_(env, /*auto restart*/ true),
|
||||
snap_refresh_nanos_(snap_refresh_nanos),
|
||||
every_nth_key_minus_one_(every_nth_key - 1) {
|
||||
assert(every_nth_key > 0);
|
||||
assert((ceil(log2(every_nth_key)) == floor(log2(every_nth_key))));
|
||||
}
|
||||
// Refresh the snapshot list. snapshots will bre replacted with the new list.
|
||||
// max is the upper bound. Note: this function will acquire the db_mutex_.
|
||||
virtual void Refresh(std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber max) = 0;
|
||||
inline bool TimeToRefresh(const size_t key_index) {
|
||||
// skip the key if key_index % every_nth_key (which is of power 2) is not 0.
|
||||
if ((key_index & every_nth_key_minus_one_) != 0) {
|
||||
return false;
|
||||
}
|
||||
const uint64_t elapsed = timer_.ElapsedNanos();
|
||||
auto ret = elapsed > snap_refresh_nanos_;
|
||||
// pre-compute the next time threshold
|
||||
if (ret) {
|
||||
// inc next refresh period exponentially (by x4)
|
||||
auto next_refresh_threshold = snap_refresh_nanos_ << 2;
|
||||
// make sure the shift has not overflown the highest 1 bit
|
||||
snap_refresh_nanos_ =
|
||||
std::max(snap_refresh_nanos_, next_refresh_threshold);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static constexpr SnapshotListFetchCallback* kDisabled = nullptr;
|
||||
|
||||
virtual ~SnapshotListFetchCallback() {}
|
||||
|
||||
private:
|
||||
// Time since the callback was created
|
||||
StopWatchNano timer_;
|
||||
// The delay before calling ::Refresh. To be increased exponentially.
|
||||
uint64_t snap_refresh_nanos_;
|
||||
// Skip evey nth key. Number n if of power 2. The math will require n-1.
|
||||
const uint64_t every_nth_key_minus_one_;
|
||||
};
|
||||
|
||||
class CompactionIterator {
|
||||
public:
|
||||
// A wrapper around Compaction. Has a much smaller interface, only what
|
||||
@@ -106,32 +59,32 @@ class CompactionIterator {
|
||||
const Compaction* compaction_;
|
||||
};
|
||||
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
SnapshotListFetchCallback* snap_list_callback = nullptr);
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
SnapshotListFetchCallback* snap_list_callback = nullptr);
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
@@ -159,8 +112,6 @@ class CompactionIterator {
|
||||
private:
|
||||
// Processes the input stream to find the next output
|
||||
void NextFromInput();
|
||||
// Process snapshots_ and assign related variables
|
||||
void ProcessSnapshotList();
|
||||
|
||||
// Do last preparations before presenting the output to the callee. At this
|
||||
// point this only zeroes out the sequence number if possible for better
|
||||
@@ -195,7 +146,7 @@ class CompactionIterator {
|
||||
InternalIterator* input_;
|
||||
const Comparator* cmp_;
|
||||
MergeHelper* merge_helper_;
|
||||
std::vector<SequenceNumber>* snapshots_;
|
||||
const std::vector<SequenceNumber>* snapshots_;
|
||||
// List of snapshots released during compaction.
|
||||
// findEarliestVisibleSnapshot() find them out from return of
|
||||
// snapshot_checker, and make sure they will not be returned as
|
||||
@@ -212,6 +163,7 @@ class CompactionIterator {
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<bool>* manual_compaction_paused_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
@@ -270,13 +222,16 @@ class CompactionIterator {
|
||||
// Used to avoid purging uncommitted values. The application can specify
|
||||
// uncommitted values by providing a SnapshotChecker object.
|
||||
bool current_key_committed_;
|
||||
SnapshotListFetchCallback* snap_list_callback_;
|
||||
// number of distinct keys processed
|
||||
size_t num_keys_ = 0;
|
||||
|
||||
bool IsShuttingDown() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
return shutting_down_ && shutting_down_->load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool IsPausingManualCompaction() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
return manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
@@ -34,7 +34,9 @@
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/read_write_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
@@ -52,7 +54,6 @@
|
||||
#include "table/table_builder.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
#include "util/stop_watch.h"
|
||||
@@ -310,7 +311,7 @@ CompactionJob::CompactionJob(
|
||||
const SnapshotChecker* snapshot_checker, std::shared_ptr<Cache> table_cache,
|
||||
EventLogger* event_logger, bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, SnapshotListFetchCallback* snap_list_callback)
|
||||
Env::Priority thread_pri, const std::atomic<bool>* manual_compaction_paused)
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_job_stats_(compaction_job_stats),
|
||||
@@ -323,6 +324,7 @@ CompactionJob::CompactionJob(
|
||||
env_->OptimizeForCompactionTableRead(env_options, db_options_)),
|
||||
versions_(versions),
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
log_buffer_(log_buffer),
|
||||
db_directory_(db_directory),
|
||||
@@ -331,7 +333,6 @@ CompactionJob::CompactionJob(
|
||||
db_mutex_(db_mutex),
|
||||
db_error_handler_(db_error_handler),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
snap_list_callback_(snap_list_callback),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
table_cache_(std::move(table_cache)),
|
||||
@@ -520,8 +521,9 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// to the index block and may incur I/O cost in the process. Unlock db
|
||||
// mutex to reduce contention
|
||||
db_mutex_->Unlock();
|
||||
uint64_t size = versions_->ApproximateSize(v, a, b, start_lvl, out_lvl + 1,
|
||||
/*for_compaction*/ true);
|
||||
uint64_t size = versions_->ApproximateSize(SizeApproximationOptions(), v, a,
|
||||
b, start_lvl, out_lvl + 1,
|
||||
TableReaderCaller::kCompaction);
|
||||
db_mutex_->Lock();
|
||||
ranges.emplace_back(a, b, size);
|
||||
sum += size;
|
||||
@@ -646,12 +648,14 @@ Status CompactionJob::Run() {
|
||||
// to cache it here for further user reads
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
ReadOptions(), env_options_, cfd->internal_comparator(),
|
||||
*files_meta[file_idx], nullptr /* range_del_agg */,
|
||||
prefix_extractor, nullptr,
|
||||
*files_meta[file_idx], /*range_del_agg=*/nullptr, prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr,
|
||||
cfd->internal_stats()->GetFileReadHist(
|
||||
compact_->compaction->output_level()),
|
||||
false, nullptr /* arena */, false /* skip_filters */,
|
||||
compact_->compaction->output_level());
|
||||
TableReaderCaller::kCompactionRefill, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, compact_->compaction->output_level(),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr);
|
||||
auto s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
@@ -863,9 +867,13 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
false /* internal key corruption is expected */,
|
||||
existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
|
||||
snapshot_checker_, compact_->compaction->level(),
|
||||
db_options_.statistics.get(), shutting_down_);
|
||||
db_options_.statistics.get());
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(manual_compaction_paused_)));
|
||||
|
||||
Slice* start = sub_compact->start;
|
||||
Slice* end = sub_compact->end;
|
||||
@@ -883,9 +891,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_), false,
|
||||
&range_del_agg, sub_compact->compaction, compaction_filter,
|
||||
shutting_down_, preserve_deletes_seqnum_,
|
||||
// Currently range_del_agg is incompatible with snapshot refresh feature.
|
||||
range_del_agg.IsEmpty() ? snap_list_callback_ : nullptr));
|
||||
shutting_down_, preserve_deletes_seqnum_, manual_compaction_paused_));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
@@ -927,8 +933,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact->current_output() != nullptr);
|
||||
sub_compact->builder->Add(key, value);
|
||||
sub_compact->current_output_file_size = sub_compact->builder->FileSize();
|
||||
const ParsedInternalKey& ikey = c_iter->ikey();
|
||||
sub_compact->current_output()->meta.UpdateBoundaries(
|
||||
key, c_iter->ikey().sequence);
|
||||
key, value, ikey.sequence, ikey.type);
|
||||
sub_compact->num_output_records++;
|
||||
|
||||
// Close output file if it is big enough. Two possibilities determine it's
|
||||
@@ -949,7 +956,14 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
input_status = input->status();
|
||||
output_file_ended = true;
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:2",
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<bool>*>(manual_compaction_paused_)));
|
||||
c_iter->Next();
|
||||
if (c_iter->status().IsManualCompactionPaused()) {
|
||||
break;
|
||||
}
|
||||
if (!output_file_ended && c_iter->Valid() &&
|
||||
sub_compact->compaction->output_level() != 0 &&
|
||||
sub_compact->ShouldStopBefore(c_iter->key(),
|
||||
@@ -1002,6 +1016,11 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
shutting_down_->load(std::memory_order_relaxed)) {
|
||||
status = Status::ShutdownInProgress("Database shutdown");
|
||||
}
|
||||
if ((status.ok() || status.IsColumnFamilyDropped()) &&
|
||||
(manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed))) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
if (status.ok()) {
|
||||
status = input->status();
|
||||
}
|
||||
@@ -1331,17 +1350,20 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
}
|
||||
std::string fname;
|
||||
FileDescriptor output_fd;
|
||||
uint64_t oldest_blob_file_number = kInvalidBlobFileNumber;
|
||||
if (meta != nullptr) {
|
||||
fname =
|
||||
TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
|
||||
meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
output_fd = meta->fd;
|
||||
oldest_blob_file_number = meta->oldest_blob_file_number;
|
||||
} else {
|
||||
fname = "(nil)";
|
||||
}
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(), fname,
|
||||
job_id_, output_fd, tp, TableFileCreationReason::kCompaction, s);
|
||||
job_id_, output_fd, oldest_blob_file_number, tp,
|
||||
TableFileCreationReason::kCompaction, s);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Report new file to SstFileManagerImpl
|
||||
@@ -1451,8 +1473,8 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
LogFlush(db_options_.info_log);
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(),
|
||||
fname, job_id_, FileDescriptor(), TableProperties(),
|
||||
TableFileCreationReason::kCompaction, s);
|
||||
fname, job_id_, FileDescriptor(), kInvalidBlobFileNumber,
|
||||
TableProperties(), TableFileCreationReason::kCompaction, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,20 +62,23 @@ class VersionSet;
|
||||
// if needed.
|
||||
class CompactionJob {
|
||||
public:
|
||||
CompactionJob(
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, SnapshotListFetchCallback* snap_list_callback);
|
||||
CompactionJob(int job_id, Compaction* compaction,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
Directory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname,
|
||||
CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri,
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr);
|
||||
|
||||
~CompactionJob();
|
||||
|
||||
@@ -154,6 +157,7 @@ class CompactionJob {
|
||||
EnvOptions env_options_for_read_;
|
||||
VersionSet* versions_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<bool>* manual_compaction_paused_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
@@ -166,7 +170,6 @@ class CompactionJob {
|
||||
// entirely within s1 and s2, then the earlier version of k1 can be safely
|
||||
// deleted because that version is not visible in any snapshot.
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
SnapshotListFetchCallback* snap_list_callback_;
|
||||
|
||||
// This is the earliest snapshot that could be used for write-conflict
|
||||
// checking by a transaction. For any user-key newer than this snapshot, we
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
@@ -5,17 +5,20 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "db/blob_index.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/options.h"
|
||||
@@ -23,7 +26,6 @@
|
||||
#include "table/mock_table.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
@@ -96,11 +98,34 @@ class CompactionJobTest : public testing::Test {
|
||||
return TableFileName(db_paths, meta.fd.GetNumber(), meta.fd.GetPathId());
|
||||
}
|
||||
|
||||
std::string KeyStr(const std::string& user_key, const SequenceNumber seq_num,
|
||||
const ValueType t) {
|
||||
static std::string KeyStr(const std::string& user_key,
|
||||
const SequenceNumber seq_num, const ValueType t) {
|
||||
return InternalKey(user_key, seq_num, t).Encode().ToString();
|
||||
}
|
||||
|
||||
static std::string BlobStr(uint64_t blob_file_number, uint64_t offset,
|
||||
uint64_t size) {
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeBlob(&blob_index, blob_file_number, offset, size,
|
||||
kNoCompression);
|
||||
return blob_index;
|
||||
}
|
||||
|
||||
static std::string BlobStrTTL(uint64_t blob_file_number, uint64_t offset,
|
||||
uint64_t size, uint64_t expiration) {
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeBlobTTL(&blob_index, expiration, blob_file_number, offset,
|
||||
size, kNoCompression);
|
||||
return blob_index;
|
||||
}
|
||||
|
||||
static std::string BlobStrInlinedTTL(const Slice& value,
|
||||
uint64_t expiration) {
|
||||
std::string blob_index;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, expiration, value);
|
||||
return blob_index;
|
||||
}
|
||||
|
||||
void AddMockFile(const stl_wrappers::KVMap& contents, int level = 0) {
|
||||
assert(contents.size() > 0);
|
||||
|
||||
@@ -109,12 +134,13 @@ class CompactionJobTest : public testing::Test {
|
||||
InternalKey smallest_key, largest_key;
|
||||
SequenceNumber smallest_seqno = kMaxSequenceNumber;
|
||||
SequenceNumber largest_seqno = 0;
|
||||
uint64_t oldest_blob_file_number = kInvalidBlobFileNumber;
|
||||
for (auto kv : contents) {
|
||||
ParsedInternalKey key;
|
||||
std::string skey;
|
||||
std::string value;
|
||||
std::tie(skey, value) = kv;
|
||||
ParseInternalKey(skey, &key);
|
||||
bool parsed = ParseInternalKey(skey, &key);
|
||||
|
||||
smallest_seqno = std::min(smallest_seqno, key.sequence);
|
||||
largest_seqno = std::max(largest_seqno, key.sequence);
|
||||
@@ -131,6 +157,24 @@ class CompactionJobTest : public testing::Test {
|
||||
}
|
||||
|
||||
first_key = false;
|
||||
|
||||
if (parsed && key.type == kTypeBlobIndex) {
|
||||
BlobIndex blob_index;
|
||||
const Status s = blob_index.DecodeFrom(value);
|
||||
if (!s.ok()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (blob_index.IsInlined() || blob_index.HasTTL() ||
|
||||
blob_index.file_number() == kInvalidBlobFileNumber) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldest_blob_file_number == kInvalidBlobFileNumber ||
|
||||
oldest_blob_file_number > blob_index.file_number()) {
|
||||
oldest_blob_file_number = blob_index.file_number();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t file_number = versions_->NewFileNumber();
|
||||
@@ -139,7 +183,7 @@ class CompactionJobTest : public testing::Test {
|
||||
|
||||
VersionEdit edit;
|
||||
edit.AddFile(level, file_number, 0, 10, smallest_key, largest_key,
|
||||
smallest_seqno, largest_seqno, false);
|
||||
smallest_seqno, largest_seqno, false, oldest_blob_file_number);
|
||||
|
||||
mutex_.Lock();
|
||||
versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
@@ -204,8 +248,15 @@ class CompactionJobTest : public testing::Test {
|
||||
&write_controller_,
|
||||
/*block_cache_tracer=*/nullptr));
|
||||
compaction_job_stats_.Reset();
|
||||
SetIdentityFile(env_, dbname_);
|
||||
|
||||
VersionEdit new_db;
|
||||
if (db_options_.write_dbid_to_manifest) {
|
||||
DBImpl* impl = new DBImpl(DBOptions(), dbname_);
|
||||
std::string db_id;
|
||||
impl->GetDbIdentityFromIdentityFile(&db_id);
|
||||
new_db.SetDBId(db_id);
|
||||
}
|
||||
new_db.SetLogNumber(0);
|
||||
new_db.SetNextFile(2);
|
||||
new_db.SetLastSequence(0);
|
||||
@@ -243,8 +294,7 @@ class CompactionJobTest : public testing::Test {
|
||||
const std::vector<SequenceNumber>& snapshots = {},
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
|
||||
int output_level = 1, bool verify = true,
|
||||
SnapshotListFetchCallback* snapshot_fetcher =
|
||||
SnapshotListFetchCallback::kDisabled) {
|
||||
uint64_t expected_oldest_blob_file_number = kInvalidBlobFileNumber) {
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
|
||||
size_t num_input_files = 0;
|
||||
@@ -277,7 +327,7 @@ class CompactionJobTest : public testing::Test {
|
||||
nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, table_cache_,
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_,
|
||||
Env::Priority::USER, snapshot_fetcher);
|
||||
Env::Priority::USER);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
compaction_job.Prepare();
|
||||
@@ -290,15 +340,20 @@ class CompactionJobTest : public testing::Test {
|
||||
mutex_.Unlock();
|
||||
|
||||
if (verify) {
|
||||
if (expected_results.size() == 0) {
|
||||
ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
|
||||
ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
|
||||
ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
|
||||
ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
|
||||
|
||||
if (expected_results.empty()) {
|
||||
ASSERT_EQ(compaction_job_stats_.num_output_files, 0U);
|
||||
} else {
|
||||
ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
|
||||
ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
|
||||
ASSERT_EQ(compaction_job_stats_.num_output_files, 1U);
|
||||
mock_table_factory_->AssertLatestFile(expected_results);
|
||||
|
||||
auto output_files =
|
||||
cfd->current()->storage_info()->LevelFiles(output_level);
|
||||
ASSERT_EQ(output_files.size(), 1);
|
||||
ASSERT_EQ(output_files[0]->oldest_blob_file_number,
|
||||
expected_oldest_blob_file_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -954,103 +1009,52 @@ TEST_F(CompactionJobTest, CorruptionAfterDeletion) {
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
// Test the snapshot fetcher in compaction
|
||||
TEST_F(CompactionJobTest, SnapshotRefresh) {
|
||||
uint64_t time_seed = env_->NowMicros();
|
||||
printf("time_seed is %" PRIu64 "\n", time_seed); // would help to reproduce
|
||||
Random64 rand(time_seed);
|
||||
std::vector<SequenceNumber> db_snapshots;
|
||||
class SnapshotListFetchCallbackTest : public SnapshotListFetchCallback {
|
||||
public:
|
||||
SnapshotListFetchCallbackTest(Env* env, Random64& rand,
|
||||
std::vector<SequenceNumber>* snapshots)
|
||||
: SnapshotListFetchCallback(env, 0 /*no time delay*/,
|
||||
1 /*fetch after each key*/),
|
||||
rand_(rand),
|
||||
snapshots_(snapshots) {}
|
||||
virtual void Refresh(std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber) override {
|
||||
assert(snapshots->size());
|
||||
assert(snapshots_->size());
|
||||
assert(snapshots_->size() == snapshots->size());
|
||||
if (rand_.OneIn(2)) {
|
||||
uint64_t release_index = rand_.Uniform(snapshots_->size());
|
||||
snapshots_->erase(snapshots_->begin() + release_index);
|
||||
*snapshots = *snapshots_;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Random64 rand_;
|
||||
std::vector<SequenceNumber>* snapshots_;
|
||||
} snapshot_fetcher(env_, rand, &db_snapshots);
|
||||
|
||||
std::vector<std::pair<const std::string, std::string>> file1_kvs, file2_kvs;
|
||||
std::array<ValueType, 4> types = {kTypeValue, kTypeDeletion,
|
||||
kTypeSingleDeletion};
|
||||
SequenceNumber last_seq = 0;
|
||||
for (int i = 1; i < 100; i++) {
|
||||
SequenceNumber seq = last_seq + 1;
|
||||
last_seq = seq;
|
||||
if (rand.OneIn(2)) {
|
||||
auto type = types[rand.Uniform(types.size())];
|
||||
file1_kvs.push_back(
|
||||
{test::KeyStr("k" + ToString(i), seq, type), "v" + ToString(i)});
|
||||
}
|
||||
}
|
||||
auto file1 = mock::MakeMockFile(file1_kvs);
|
||||
for (int i = 1; i < 100; i++) {
|
||||
SequenceNumber seq = last_seq + 1;
|
||||
last_seq++;
|
||||
if (rand.OneIn(2)) {
|
||||
auto type = types[rand.Uniform(types.size())];
|
||||
file2_kvs.push_back(
|
||||
{test::KeyStr("k" + ToString(i), seq, type), "v" + ToString(i)});
|
||||
}
|
||||
}
|
||||
auto file2 = mock::MakeMockFile(file2_kvs);
|
||||
for (SequenceNumber i = 1; i < last_seq + 1; i++) {
|
||||
if (rand.OneIn(5)) {
|
||||
db_snapshots.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
const bool kVerify = true;
|
||||
const int output_level_0 = 0;
|
||||
TEST_F(CompactionJobTest, OldestBlobFileNumber) {
|
||||
NewDB();
|
||||
|
||||
// Note: blob1 is inlined TTL, so it will not be considered for the purposes
|
||||
// of identifying the oldest referenced blob file. Similarly, blob6 will be
|
||||
// ignored because it has TTL and hence refers to a TTL blob file.
|
||||
const stl_wrappers::KVMap::value_type blob1(
|
||||
KeyStr("a", 1U, kTypeBlobIndex), BlobStrInlinedTTL("foo", 1234567890ULL));
|
||||
const stl_wrappers::KVMap::value_type blob2(KeyStr("b", 2U, kTypeBlobIndex),
|
||||
BlobStr(59, 123456, 999));
|
||||
const stl_wrappers::KVMap::value_type blob3(KeyStr("c", 3U, kTypeBlobIndex),
|
||||
BlobStr(138, 1000, 1 << 8));
|
||||
auto file1 = mock::MakeMockFile({blob1, blob2, blob3});
|
||||
AddMockFile(file1);
|
||||
|
||||
const stl_wrappers::KVMap::value_type blob4(KeyStr("d", 4U, kTypeBlobIndex),
|
||||
BlobStr(199, 3 << 10, 1 << 20));
|
||||
const stl_wrappers::KVMap::value_type blob5(KeyStr("e", 5U, kTypeBlobIndex),
|
||||
BlobStr(19, 6789, 333));
|
||||
const stl_wrappers::KVMap::value_type blob6(
|
||||
KeyStr("f", 6U, kTypeBlobIndex),
|
||||
BlobStrTTL(5, 2048, 1 << 7, 1234567890ULL));
|
||||
auto file2 = mock::MakeMockFile({blob4, blob5, blob6});
|
||||
AddMockFile(file2);
|
||||
SetLastSequence(last_seq);
|
||||
|
||||
const stl_wrappers::KVMap::value_type expected_blob1(
|
||||
KeyStr("a", 0U, kTypeBlobIndex), blob1.second);
|
||||
const stl_wrappers::KVMap::value_type expected_blob2(
|
||||
KeyStr("b", 0U, kTypeBlobIndex), blob2.second);
|
||||
const stl_wrappers::KVMap::value_type expected_blob3(
|
||||
KeyStr("c", 0U, kTypeBlobIndex), blob3.second);
|
||||
const stl_wrappers::KVMap::value_type expected_blob4(
|
||||
KeyStr("d", 0U, kTypeBlobIndex), blob4.second);
|
||||
const stl_wrappers::KVMap::value_type expected_blob5(
|
||||
KeyStr("e", 0U, kTypeBlobIndex), blob5.second);
|
||||
const stl_wrappers::KVMap::value_type expected_blob6(
|
||||
KeyStr("f", 0U, kTypeBlobIndex), blob6.second);
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({expected_blob1, expected_blob2, expected_blob3,
|
||||
expected_blob4, expected_blob5, expected_blob6});
|
||||
|
||||
SetLastSequence(6U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
// put the output on L0 since it is easier to feed them again to the 2nd
|
||||
// compaction
|
||||
RunCompaction({files}, file1, db_snapshots, kMaxSequenceNumber,
|
||||
output_level_0, !kVerify, &snapshot_fetcher);
|
||||
|
||||
// Now db_snapshots are changed. Run the compaction again without snapshot
|
||||
// fetcher but with the updated snapshot list.
|
||||
compaction_job_stats_.Reset();
|
||||
files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, file1, db_snapshots, kMaxSequenceNumber,
|
||||
output_level_0 + 1, !kVerify);
|
||||
// The result should be what we get if we run compaction without snapshot
|
||||
// fetcher on the updated list of snapshots
|
||||
auto expected = mock_table_factory_->output();
|
||||
|
||||
NewDB();
|
||||
AddMockFile(file1);
|
||||
AddMockFile(file2);
|
||||
SetLastSequence(last_seq);
|
||||
files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, expected, db_snapshots, kMaxSequenceNumber,
|
||||
output_level_0, !kVerify);
|
||||
// The 2nd compaction above would get rid of useless delete markers. To get
|
||||
// the output here exactly as what we got above after two compactions, we also
|
||||
// run the compaction for 2nd time.
|
||||
compaction_job_stats_.Reset();
|
||||
files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, expected, db_snapshots, kMaxSequenceNumber,
|
||||
output_level_0 + 1, !kVerify);
|
||||
RunCompaction({files}, expected_results, std::vector<SequenceNumber>(),
|
||||
kMaxSequenceNumber, /* output_level */ 1, /* verify */ true,
|
||||
/* expected_oldest_blob_file_number */ 19);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -54,6 +54,15 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
}
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
if (!level0_compactions_in_progress_.empty()) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: Already executing compaction. No need "
|
||||
"to run parallel compactions since compactions are very fast",
|
||||
cf_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
inputs.emplace_back();
|
||||
inputs[0].level = 0;
|
||||
|
||||
@@ -88,15 +88,13 @@ class CompactionPickerTest : public testing::Test {
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0) {
|
||||
assert(level < vstorage_->num_levels());
|
||||
FileMetaData* f = new FileMetaData;
|
||||
f->fd = FileDescriptor(file_number, path_id, file_size);
|
||||
f->smallest = InternalKey(smallest, smallest_seq, kTypeValue);
|
||||
f->largest = InternalKey(largest, largest_seq, kTypeValue);
|
||||
f->fd.smallest_seqno = smallest_seq;
|
||||
f->fd.largest_seqno = largest_seq;
|
||||
FileMetaData* f = new FileMetaData(
|
||||
file_number, path_id, file_size,
|
||||
InternalKey(smallest, smallest_seq, kTypeValue),
|
||||
InternalKey(largest, largest_seq, kTypeValue), smallest_seq,
|
||||
largest_seq, /* marked_for_compact */ false, kInvalidBlobFileNumber);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->refs = 0;
|
||||
vstorage_->AddFile(level, f);
|
||||
files_.emplace_back(f);
|
||||
file_map_.insert({file_number, {f, level}});
|
||||
@@ -1504,7 +1502,7 @@ TEST_F(CompactionPickerTest, IntraL0MaxCompactionBytesNotHit) {
|
||||
ASSERT_EQ(5U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0U, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IntraL0MaxCompactionBytesHit) {
|
||||
@@ -1534,7 +1532,7 @@ TEST_F(CompactionPickerTest, IntraL0MaxCompactionBytesHit) {
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0U, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -25,6 +25,98 @@
|
||||
|
||||
namespace rocksdb {
|
||||
namespace {
|
||||
// A helper class that form universal compactions. The class is used by
|
||||
// UniversalCompactionPicker::PickCompaction().
|
||||
// The usage is to create the class, and get the compaction object by calling
|
||||
// PickCompaction().
|
||||
class UniversalCompactionBuilder {
|
||||
public:
|
||||
UniversalCompactionBuilder(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp,
|
||||
const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
UniversalCompactionPicker* picker,
|
||||
LogBuffer* log_buffer)
|
||||
: ioptions_(ioptions),
|
||||
icmp_(icmp),
|
||||
cf_name_(cf_name),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
vstorage_(vstorage),
|
||||
picker_(picker),
|
||||
log_buffer_(log_buffer) {}
|
||||
|
||||
// Form and return the compaction object. The caller owns return object.
|
||||
Compaction* PickCompaction();
|
||||
|
||||
private:
|
||||
struct SortedRun {
|
||||
SortedRun(int _level, FileMetaData* _file, uint64_t _size,
|
||||
uint64_t _compensated_file_size, bool _being_compacted)
|
||||
: level(_level),
|
||||
file(_file),
|
||||
size(_size),
|
||||
compensated_file_size(_compensated_file_size),
|
||||
being_compacted(_being_compacted) {
|
||||
assert(compensated_file_size > 0);
|
||||
assert(level != 0 || file != nullptr);
|
||||
}
|
||||
|
||||
void Dump(char* out_buf, size_t out_buf_size,
|
||||
bool print_path = false) const;
|
||||
|
||||
// sorted_run_count is added into the string to print
|
||||
void DumpSizeInfo(char* out_buf, size_t out_buf_size,
|
||||
size_t sorted_run_count) const;
|
||||
|
||||
int level;
|
||||
// `file` Will be null for level > 0. For level = 0, the sorted run is
|
||||
// for this file.
|
||||
FileMetaData* file;
|
||||
// For level > 0, `size` and `compensated_file_size` are sum of sizes all
|
||||
// files in the level. `being_compacted` should be the same for all files
|
||||
// in a non-zero level. Use the value here.
|
||||
uint64_t size;
|
||||
uint64_t compensated_file_size;
|
||||
bool being_compacted;
|
||||
};
|
||||
|
||||
// Pick Universal compaction to limit read amplification
|
||||
Compaction* PickCompactionToReduceSortedRuns(
|
||||
unsigned int ratio, unsigned int max_number_of_files_to_compact);
|
||||
|
||||
// Pick Universal compaction to limit space amplification.
|
||||
Compaction* PickCompactionToReduceSizeAmp();
|
||||
|
||||
Compaction* PickDeleteTriggeredCompaction();
|
||||
|
||||
// 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
|
||||
// overlapping.
|
||||
bool IsInputFilesNonOverlapping(Compaction* c);
|
||||
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
const InternalKeyComparator* icmp_;
|
||||
double score_;
|
||||
std::vector<SortedRun> sorted_runs_;
|
||||
const std::string& cf_name_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
UniversalCompactionPicker* picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
static uint32_t GetPathId(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
uint64_t file_size);
|
||||
};
|
||||
|
||||
// Used in universal compaction when trivial move is enabled.
|
||||
// This structure is used for the construction of min heap
|
||||
// that contains the file meta data, the level of the file
|
||||
@@ -113,7 +205,7 @@ void GetSmallestLargestSeqno(const std::vector<FileMetaData*>& files,
|
||||
|
||||
// Algorithm that checks to see if there are any overlapping
|
||||
// files in the input
|
||||
bool UniversalCompactionPicker::IsInputFilesNonOverlapping(Compaction* c) {
|
||||
bool UniversalCompactionBuilder::IsInputFilesNonOverlapping(Compaction* c) {
|
||||
auto comparator = icmp_->user_comparator();
|
||||
int first_iter = 1;
|
||||
|
||||
@@ -167,9 +259,18 @@ bool UniversalCompactionPicker::NeedsCompaction(
|
||||
return false;
|
||||
}
|
||||
|
||||
void UniversalCompactionPicker::SortedRun::Dump(char* out_buf,
|
||||
size_t out_buf_size,
|
||||
bool print_path) const {
|
||||
Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
UniversalCompactionBuilder builder(ioptions_, icmp_, cf_name,
|
||||
mutable_cf_options, vstorage, this,
|
||||
log_buffer);
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
|
||||
void UniversalCompactionBuilder::SortedRun::Dump(char* out_buf,
|
||||
size_t out_buf_size,
|
||||
bool print_path) const {
|
||||
if (level == 0) {
|
||||
assert(file != nullptr);
|
||||
if (file->fd.GetPathId() == 0 || !print_path) {
|
||||
@@ -185,7 +286,7 @@ void UniversalCompactionPicker::SortedRun::Dump(char* out_buf,
|
||||
}
|
||||
}
|
||||
|
||||
void UniversalCompactionPicker::SortedRun::DumpSizeInfo(
|
||||
void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
char* out_buf, size_t out_buf_size, size_t sorted_run_count) const {
|
||||
if (level == 0) {
|
||||
assert(file != nullptr);
|
||||
@@ -204,11 +305,11 @@ void UniversalCompactionPicker::SortedRun::DumpSizeInfo(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<UniversalCompactionPicker::SortedRun>
|
||||
UniversalCompactionPicker::CalculateSortedRuns(
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& /*ioptions*/,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
std::vector<UniversalCompactionPicker::SortedRun> ret;
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
@@ -231,8 +332,8 @@ UniversalCompactionPicker::CalculateSortedRuns(
|
||||
// non-zero level, all the files should share the same being_compacted
|
||||
// value.
|
||||
// This assumption is only valid when
|
||||
// mutable_cf_options.compaction_options_universal.allow_trivial_move is
|
||||
// false
|
||||
// mutable_cf_options.compaction_options_universal.allow_trivial_move
|
||||
// is false
|
||||
assert(is_first || f->being_compacted == being_compacted);
|
||||
}
|
||||
if (is_first) {
|
||||
@@ -250,65 +351,59 @@ UniversalCompactionPicker::CalculateSortedRuns(
|
||||
|
||||
// Universal style of compaction. Pick files that are contiguous in
|
||||
// time-range to compact.
|
||||
Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
const int kLevel0 = 0;
|
||||
double score = vstorage->CompactionScore(kLevel0);
|
||||
std::vector<SortedRun> sorted_runs =
|
||||
CalculateSortedRuns(*vstorage, ioptions_, mutable_cf_options);
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, ioptions_, mutable_cf_options_);
|
||||
|
||||
if (sorted_runs.size() == 0 ||
|
||||
(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",
|
||||
nullptr);
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(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(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER_MAX_SZ(
|
||||
log_buffer, 3072,
|
||||
log_buffer_, 3072,
|
||||
"[%s] Universal: sorted runs files(%" ROCKSDB_PRIszt "): %s\n",
|
||||
cf_name.c_str(), sorted_runs.size(), vstorage->LevelSummary(&tmp));
|
||||
cf_name_.c_str(), sorted_runs_.size(), vstorage_->LevelSummary(&tmp));
|
||||
|
||||
// Check for size amplification first.
|
||||
Compaction* c = nullptr;
|
||||
if (sorted_runs.size() >=
|
||||
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());
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger)) {
|
||||
if ((c = PickCompactionToReduceSizeAmp()) != 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;
|
||||
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,
|
||||
if ((c = PickCompactionToReduceSortedRuns(ratio, UINT_MAX)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: compacting for size ratio\n",
|
||||
cf_name.c_str());
|
||||
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() >=
|
||||
assert(sorted_runs_.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger));
|
||||
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) {
|
||||
for (size_t i = 0; i < sorted_runs_.size(); i++) {
|
||||
if (sorted_runs_[i].being_compacted == false) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
@@ -316,16 +411,15 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
// 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) {
|
||||
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,
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionToReduceSortedRuns(UINT_MAX, num_files)) !=
|
||||
nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
cf_name_.c_str(), num_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,22 +427,20 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
}
|
||||
|
||||
if (c == nullptr) {
|
||||
if ((c = PickDeleteTriggeredCompaction(cf_name, mutable_cf_options,
|
||||
vstorage, score, sorted_runs,
|
||||
log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
if ((c = PickDeleteTriggeredCompaction()) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: delete triggered compaction\n",
|
||||
cf_name.c_str());
|
||||
cf_name_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr) {
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
nullptr);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mutable_cf_options.compaction_options_universal.allow_trivial_move ==
|
||||
if (mutable_cf_options_.compaction_options_universal.allow_trivial_move ==
|
||||
true) {
|
||||
c->set_is_trivial_move(IsInputFilesNonOverlapping(c));
|
||||
}
|
||||
@@ -394,15 +486,15 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
RecordInHistogram(ioptions_.statistics, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
c->inputs(0)->size());
|
||||
|
||||
RegisterCompaction(c);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
picker_->RegisterCompaction(c);
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionBuilder::PickCompaction:Return",
|
||||
c);
|
||||
return c;
|
||||
}
|
||||
|
||||
uint32_t UniversalCompactionPicker::GetPathId(
|
||||
uint32_t UniversalCompactionBuilder::GetPathId(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, uint64_t file_size) {
|
||||
// Two conditions need to be satisfied:
|
||||
@@ -440,15 +532,12 @@ uint32_t UniversalCompactionPicker::GetPathId(
|
||||
// Consider compaction files based on their size differences with
|
||||
// the next file in time order.
|
||||
//
|
||||
Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score, unsigned int ratio,
|
||||
unsigned int max_number_of_files_to_compact,
|
||||
const std::vector<SortedRun>& sorted_runs, LogBuffer* log_buffer) {
|
||||
Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
unsigned int ratio, unsigned int max_number_of_files_to_compact) {
|
||||
unsigned int min_merge_width =
|
||||
mutable_cf_options.compaction_options_universal.min_merge_width;
|
||||
mutable_cf_options_.compaction_options_universal.min_merge_width;
|
||||
unsigned int max_merge_width =
|
||||
mutable_cf_options.compaction_options_universal.max_merge_width;
|
||||
mutable_cf_options_.compaction_options_universal.max_merge_width;
|
||||
|
||||
const SortedRun* sr = nullptr;
|
||||
bool done = false;
|
||||
@@ -462,16 +551,16 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
// Caller checks the size before executing this function. This invariant is
|
||||
// important because otherwise we may have a possible integer underflow when
|
||||
// dealing with unsigned types.
|
||||
assert(sorted_runs.size() > 0);
|
||||
assert(sorted_runs_.size() > 0);
|
||||
|
||||
// Considers a candidate file only if it is smaller than the
|
||||
// total size accumulated so far.
|
||||
for (size_t loop = 0; loop < sorted_runs.size(); loop++) {
|
||||
for (size_t loop = 0; loop < sorted_runs_.size(); loop++) {
|
||||
candidate_count = 0;
|
||||
|
||||
// Skip files that are already being compacted
|
||||
for (sr = nullptr; loop < sorted_runs.size(); loop++) {
|
||||
sr = &sorted_runs[loop];
|
||||
for (sr = nullptr; loop < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
|
||||
if (!sr->being_compacted) {
|
||||
candidate_count = 1;
|
||||
@@ -479,10 +568,10 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
}
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf));
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] being compacted, skipping",
|
||||
cf_name.c_str(), file_num_buf, loop);
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
|
||||
sr = nullptr;
|
||||
}
|
||||
@@ -493,15 +582,16 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
if (sr != nullptr) {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: Possible candidate %s[%d].",
|
||||
cf_name.c_str(), file_num_buf, loop);
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: Possible candidate %s[%d].",
|
||||
cf_name_.c_str(), file_num_buf, loop);
|
||||
}
|
||||
|
||||
// Check if the succeeding files need compaction.
|
||||
for (size_t i = loop + 1;
|
||||
candidate_count < max_files_to_compact && i < sorted_runs.size();
|
||||
candidate_count < max_files_to_compact && i < sorted_runs_.size();
|
||||
i++) {
|
||||
const SortedRun* succeeding_sr = &sorted_runs[i];
|
||||
const SortedRun* succeeding_sr = &sorted_runs_[i];
|
||||
if (succeeding_sr->being_compacted) {
|
||||
break;
|
||||
}
|
||||
@@ -515,7 +605,7 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
if (sz < static_cast<double>(succeeding_sr->size)) {
|
||||
break;
|
||||
}
|
||||
if (mutable_cf_options.compaction_options_universal.stop_style ==
|
||||
if (mutable_cf_options_.compaction_options_universal.stop_style ==
|
||||
kCompactionStopStyleSimilarSize) {
|
||||
// Similar-size stopping rule: also check the last picked file isn't
|
||||
// far larger than the next candidate file.
|
||||
@@ -541,12 +631,12 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
break;
|
||||
} else {
|
||||
for (size_t i = loop;
|
||||
i < loop + candidate_count && i < sorted_runs.size(); i++) {
|
||||
const SortedRun* skipping_sr = &sorted_runs[i];
|
||||
i < loop + candidate_count && i < sorted_runs_.size(); i++) {
|
||||
const SortedRun* skipping_sr = &sorted_runs_[i];
|
||||
char file_num_buf[256];
|
||||
skipping_sr->DumpSizeInfo(file_num_buf, sizeof(file_num_buf), loop);
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: Skipping %s",
|
||||
cf_name.c_str(), file_num_buf);
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: Skipping %s",
|
||||
cf_name_.c_str(), file_num_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -558,16 +648,16 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
// size ratio of compression.
|
||||
bool enable_compression = true;
|
||||
int ratio_to_compress =
|
||||
mutable_cf_options.compaction_options_universal.compression_size_percent;
|
||||
mutable_cf_options_.compaction_options_universal.compression_size_percent;
|
||||
if (ratio_to_compress >= 0) {
|
||||
uint64_t total_size = 0;
|
||||
for (auto& sorted_run : sorted_runs) {
|
||||
for (auto& sorted_run : sorted_runs_) {
|
||||
total_size += sorted_run.compensated_file_size;
|
||||
}
|
||||
|
||||
uint64_t older_file_size = 0;
|
||||
for (size_t i = sorted_runs.size() - 1; i >= first_index_after; i--) {
|
||||
older_file_size += sorted_runs[i].size;
|
||||
for (size_t i = sorted_runs_.size() - 1; i >= first_index_after; i--) {
|
||||
older_file_size += sorted_runs_[i].size;
|
||||
if (older_file_size * 100L >= total_size * (long)ratio_to_compress) {
|
||||
enable_compression = false;
|
||||
break;
|
||||
@@ -577,46 +667,46 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
|
||||
uint64_t estimated_total_size = 0;
|
||||
for (unsigned int i = 0; i < first_index_after; i++) {
|
||||
estimated_total_size += sorted_runs[i].size;
|
||||
estimated_total_size += sorted_runs_[i].size;
|
||||
}
|
||||
uint32_t path_id =
|
||||
GetPathId(ioptions_, mutable_cf_options, estimated_total_size);
|
||||
int start_level = sorted_runs[start_index].level;
|
||||
GetPathId(ioptions_, mutable_cf_options_, estimated_total_size);
|
||||
int start_level = sorted_runs_[start_index].level;
|
||||
int output_level;
|
||||
if (first_index_after == sorted_runs.size()) {
|
||||
output_level = vstorage->num_levels() - 1;
|
||||
} else if (sorted_runs[first_index_after].level == 0) {
|
||||
if (first_index_after == sorted_runs_.size()) {
|
||||
output_level = vstorage_->num_levels() - 1;
|
||||
} else if (sorted_runs_[first_index_after].level == 0) {
|
||||
output_level = 0;
|
||||
} else {
|
||||
output_level = sorted_runs[first_index_after].level - 1;
|
||||
output_level = sorted_runs_[first_index_after].level - 1;
|
||||
}
|
||||
|
||||
// last level is reserved for the files ingested behind
|
||||
if (ioptions_.allow_ingest_behind &&
|
||||
(output_level == vstorage->num_levels() - 1)) {
|
||||
(output_level == vstorage_->num_levels() - 1)) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage->num_levels());
|
||||
std::vector<CompactionInputFiles> inputs(vstorage_->num_levels());
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
inputs[i].level = start_level + static_cast<int>(i);
|
||||
}
|
||||
for (size_t i = start_index; i < first_index_after; i++) {
|
||||
auto& picking_sr = sorted_runs[i];
|
||||
auto& picking_sr = sorted_runs_[i];
|
||||
if (picking_sr.level == 0) {
|
||||
FileMetaData* picking_file = picking_sr.file;
|
||||
inputs[0].files.push_back(picking_file);
|
||||
} else {
|
||||
auto& files = inputs[picking_sr.level - start_level].files;
|
||||
for (auto* f : vstorage->LevelFiles(picking_sr.level)) {
|
||||
for (auto* f : vstorage_->LevelFiles(picking_sr.level)) {
|
||||
files.push_back(f);
|
||||
}
|
||||
}
|
||||
char file_num_buf[256];
|
||||
picking_sr.DumpSizeInfo(file_num_buf, sizeof(file_num_buf), i);
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: Picking %s", cf_name.c_str(),
|
||||
file_num_buf);
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: Picking %s",
|
||||
cf_name_.c_str(), file_num_buf);
|
||||
}
|
||||
|
||||
CompactionReason compaction_reason;
|
||||
@@ -626,16 +716,17 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
compaction_reason = CompactionReason::kUniversalSortedRunNum;
|
||||
}
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
vstorage_, ioptions_, mutable_cf_options_, std::move(inputs),
|
||||
output_level,
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level,
|
||||
kCompactionStyleUniversal),
|
||||
LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, start_level,
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_, start_level,
|
||||
1, enable_compression),
|
||||
GetCompressionOptions(ioptions_, vstorage, start_level,
|
||||
GetCompressionOptions(ioptions_, vstorage_, start_level,
|
||||
enable_compression),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score, false /* deletion_compaction */, compaction_reason);
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
|
||||
// Look at overall size amplification. If size amplification
|
||||
@@ -644,12 +735,9 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
// base file (overrides configured values of file-size ratios,
|
||||
// min_merge_width and max_merge_width).
|
||||
//
|
||||
Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score,
|
||||
const std::vector<SortedRun>& sorted_runs, LogBuffer* log_buffer) {
|
||||
Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
// percentage flexibility while reducing size amplification
|
||||
uint64_t ratio = mutable_cf_options.compaction_options_universal
|
||||
uint64_t ratio = mutable_cf_options_.compaction_options_universal
|
||||
.max_size_amplification_percent;
|
||||
|
||||
unsigned int candidate_count = 0;
|
||||
@@ -657,21 +745,23 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
size_t start_index = 0;
|
||||
const SortedRun* sr = nullptr;
|
||||
|
||||
if (sorted_runs.back().being_compacted) {
|
||||
assert(!sorted_runs_.empty());
|
||||
if (sorted_runs_.back().being_compacted) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skip files that are already being compacted
|
||||
for (size_t loop = 0; loop < sorted_runs.size() - 1; loop++) {
|
||||
sr = &sorted_runs[loop];
|
||||
for (size_t loop = 0; loop < sorted_runs_.size() - 1; loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
if (!sr->being_compacted) {
|
||||
start_index = loop; // Consider this as the first candidate.
|
||||
break;
|
||||
}
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: skipping %s[%d] compacted %s",
|
||||
cf_name.c_str(), file_num_buf, loop,
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: skipping %s[%d] compacted %s",
|
||||
cf_name_.c_str(), file_num_buf, loop,
|
||||
" cannot be a candidate to reduce size amp.\n");
|
||||
sr = nullptr;
|
||||
}
|
||||
@@ -683,20 +773,20 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
log_buffer_,
|
||||
"[%s] Universal: First candidate %s[%" ROCKSDB_PRIszt "] %s",
|
||||
cf_name.c_str(), file_num_buf, start_index, " to reduce size amp.\n");
|
||||
cf_name_.c_str(), file_num_buf, start_index, " to reduce size amp.\n");
|
||||
}
|
||||
|
||||
// keep adding up all the remaining files
|
||||
for (size_t loop = start_index; loop < sorted_runs.size() - 1; loop++) {
|
||||
sr = &sorted_runs[loop];
|
||||
for (size_t loop = start_index; loop < sorted_runs_.size() - 1; loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
if (sr->being_compacted) {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer, "[%s] Universal: Possible candidate %s[%d] %s",
|
||||
cf_name.c_str(), file_num_buf, start_index,
|
||||
log_buffer_, "[%s] Universal: Possible candidate %s[%d] %s",
|
||||
cf_name_.c_str(), file_num_buf, start_index,
|
||||
" is already being compacted. No size amp reduction possible.\n");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -708,58 +798,58 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
}
|
||||
|
||||
// size of earliest file
|
||||
uint64_t earliest_file_size = sorted_runs.back().size;
|
||||
uint64_t earliest_file_size = sorted_runs_.back().size;
|
||||
|
||||
// size amplification = percentage of additional size
|
||||
if (candidate_size * 100 < ratio * earliest_file_size) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
log_buffer_,
|
||||
"[%s] Universal: size amp not needed. newer-files-total-size %" PRIu64
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name.c_str(), candidate_size, earliest_file_size);
|
||||
cf_name_.c_str(), candidate_size, earliest_file_size);
|
||||
return nullptr;
|
||||
} else {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
log_buffer_,
|
||||
"[%s] Universal: size amp needed. newer-files-total-size %" PRIu64
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name.c_str(), candidate_size, earliest_file_size);
|
||||
cf_name_.c_str(), candidate_size, earliest_file_size);
|
||||
}
|
||||
assert(start_index < sorted_runs.size() - 1);
|
||||
assert(start_index < sorted_runs_.size() - 1);
|
||||
|
||||
// Estimate total file size
|
||||
uint64_t estimated_total_size = 0;
|
||||
for (size_t loop = start_index; loop < sorted_runs.size(); loop++) {
|
||||
estimated_total_size += sorted_runs[loop].size;
|
||||
for (size_t loop = start_index; loop < sorted_runs_.size(); loop++) {
|
||||
estimated_total_size += sorted_runs_[loop].size;
|
||||
}
|
||||
uint32_t path_id =
|
||||
GetPathId(ioptions_, mutable_cf_options, estimated_total_size);
|
||||
int start_level = sorted_runs[start_index].level;
|
||||
GetPathId(ioptions_, mutable_cf_options_, estimated_total_size);
|
||||
int start_level = sorted_runs_[start_index].level;
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage->num_levels());
|
||||
std::vector<CompactionInputFiles> inputs(vstorage_->num_levels());
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
inputs[i].level = start_level + static_cast<int>(i);
|
||||
}
|
||||
// We always compact all the files, so always compress.
|
||||
for (size_t loop = start_index; loop < sorted_runs.size(); loop++) {
|
||||
auto& picking_sr = sorted_runs[loop];
|
||||
for (size_t loop = start_index; loop < sorted_runs_.size(); loop++) {
|
||||
auto& picking_sr = sorted_runs_[loop];
|
||||
if (picking_sr.level == 0) {
|
||||
FileMetaData* f = picking_sr.file;
|
||||
inputs[0].files.push_back(f);
|
||||
} else {
|
||||
auto& files = inputs[picking_sr.level - start_level].files;
|
||||
for (auto* f : vstorage->LevelFiles(picking_sr.level)) {
|
||||
for (auto* f : vstorage_->LevelFiles(picking_sr.level)) {
|
||||
files.push_back(f);
|
||||
}
|
||||
}
|
||||
char file_num_buf[256];
|
||||
picking_sr.DumpSizeInfo(file_num_buf, sizeof(file_num_buf), loop);
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: size amp picking %s",
|
||||
cf_name.c_str(), file_num_buf);
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: size amp picking %s",
|
||||
cf_name_.c_str(), file_num_buf);
|
||||
}
|
||||
|
||||
// output files at the bottom most level, unless it's reserved
|
||||
int output_level = vstorage->num_levels() - 1;
|
||||
int output_level = vstorage_->num_levels() - 1;
|
||||
// last level is reserved for the files ingested behind
|
||||
if (ioptions_.allow_ingest_behind) {
|
||||
assert(output_level > 1);
|
||||
@@ -767,29 +857,27 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
}
|
||||
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
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),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score, false /* deletion_compaction */,
|
||||
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*/) {
|
||||
Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
CompactionInputFiles start_level_inputs;
|
||||
int output_level;
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
|
||||
if (vstorage->num_levels() == 1) {
|
||||
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
|
||||
@@ -799,7 +887,7 @@ Compaction* UniversalCompactionPicker::PickDeleteTriggeredCompaction(
|
||||
start_level_inputs.level = 0;
|
||||
start_level_inputs.files.clear();
|
||||
output_level = 0;
|
||||
for (FileMetaData* f : vstorage->LevelFiles(0)) {
|
||||
for (FileMetaData* f : vstorage_->LevelFiles(0)) {
|
||||
if (f->marked_for_compaction) {
|
||||
compact = true;
|
||||
}
|
||||
@@ -818,24 +906,24 @@ Compaction* UniversalCompactionPicker::PickDeleteTriggeredCompaction(
|
||||
// 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);
|
||||
picker_->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();
|
||||
for (output_level = start_level + 1; output_level < vstorage_->num_levels();
|
||||
output_level++) {
|
||||
if (vstorage->NumLevelFiles(output_level) != 0) {
|
||||
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 (output_level == vstorage_->num_levels()) {
|
||||
if (start_level == 0) {
|
||||
output_level = vstorage->num_levels() - 1;
|
||||
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
|
||||
@@ -845,15 +933,15 @@ Compaction* UniversalCompactionPicker::PickDeleteTriggeredCompaction(
|
||||
}
|
||||
}
|
||||
if (ioptions_.allow_ingest_behind &&
|
||||
output_level == vstorage->num_levels() - 1) {
|
||||
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)) {
|
||||
if (!picker_->GetOverlappingL0Files(vstorage_, &start_level_inputs,
|
||||
output_level, nullptr)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -862,16 +950,16 @@ Compaction* UniversalCompactionPicker::PickDeleteTriggeredCompaction(
|
||||
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)) {
|
||||
if (!picker_->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)) {
|
||||
if (picker_->FilesRangeOverlapWithCompaction(inputs, output_level)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
@@ -881,21 +969,22 @@ Compaction* UniversalCompactionPicker::PickDeleteTriggeredCompaction(
|
||||
|
||||
uint64_t estimated_total_size = 0;
|
||||
// Use size of the output level as estimated file size
|
||||
for (FileMetaData* f : vstorage->LevelFiles(output_level)) {
|
||||
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);
|
||||
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,
|
||||
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),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level),
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ true,
|
||||
score, false /* deletion_compaction */,
|
||||
score_, false /* deletion_compaction */,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -27,72 +27,6 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
private:
|
||||
struct SortedRun {
|
||||
SortedRun(int _level, FileMetaData* _file, uint64_t _size,
|
||||
uint64_t _compensated_file_size, bool _being_compacted)
|
||||
: level(_level),
|
||||
file(_file),
|
||||
size(_size),
|
||||
compensated_file_size(_compensated_file_size),
|
||||
being_compacted(_being_compacted) {
|
||||
assert(compensated_file_size > 0);
|
||||
assert(level != 0 || file != nullptr);
|
||||
}
|
||||
|
||||
void Dump(char* out_buf, size_t out_buf_size,
|
||||
bool print_path = false) const;
|
||||
|
||||
// sorted_run_count is added into the string to print
|
||||
void DumpSizeInfo(char* out_buf, size_t out_buf_size,
|
||||
size_t sorted_run_count) const;
|
||||
|
||||
int level;
|
||||
// `file` Will be null for level > 0. For level = 0, the sorted run is
|
||||
// for this file.
|
||||
FileMetaData* file;
|
||||
// For level > 0, `size` and `compensated_file_size` are sum of sizes all
|
||||
// files in the level. `being_compacted` should be the same for all files
|
||||
// in a non-zero level. Use the value here.
|
||||
uint64_t size;
|
||||
uint64_t compensated_file_size;
|
||||
bool being_compacted;
|
||||
};
|
||||
|
||||
// Pick Universal compaction to limit read amplification
|
||||
Compaction* PickCompactionToReduceSortedRuns(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score, unsigned int ratio,
|
||||
unsigned int num_files, const std::vector<SortedRun>& sorted_runs,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Pick Universal compaction to limit space amplification.
|
||||
Compaction* PickCompactionToReduceSizeAmp(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
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
|
||||
// overlapping.
|
||||
bool IsInputFilesNonOverlapping(Compaction* c);
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
static uint32_t GetPathId(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
uint64_t file_size);
|
||||
};
|
||||
} // namespace rocksdb
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
+8
-1
@@ -35,6 +35,12 @@ Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
const EnvOptions& env_options,
|
||||
const std::string& file_path) {
|
||||
return VerifySstFileChecksum(options, env_options, ReadOptions(), file_path);
|
||||
}
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
const EnvOptions& env_options,
|
||||
const ReadOptions& read_options,
|
||||
const std::string& file_path) {
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
uint64_t file_size;
|
||||
InternalKeyComparator internal_comparator(options.comparator);
|
||||
@@ -59,7 +65,8 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
s = table_reader->VerifyChecksum();
|
||||
s = table_reader->VerifyChecksum(read_options,
|
||||
TableReaderCaller::kUserVerifyChecksum);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+59
-2
@@ -13,10 +13,11 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <cinttypes>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <cinttypes>
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/filename.h"
|
||||
@@ -76,7 +77,11 @@ class CorruptionTest : public testing::Test {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
Options opt = (options ? *options : options_);
|
||||
opt.env = &env_;
|
||||
if (opt.env == Options().env) {
|
||||
// If env is not overridden, replace it with ErrorEnv.
|
||||
// Otherwise, the test already uses a non-default Env.
|
||||
opt.env = &env_;
|
||||
}
|
||||
opt.arena_block_size = 4096;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = tiny_cache_;
|
||||
@@ -321,6 +326,58 @@ TEST_F(CorruptionTest, TableFile) {
|
||||
ASSERT_NOK(dbi->VerifyChecksum());
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, VerifyChecksumReadahead) {
|
||||
Options options;
|
||||
SpecialEnv senv(Env::Default());
|
||||
options.env = &senv;
|
||||
// Disable block cache as we are going to check checksum for
|
||||
// the same file twice and measure number of reads.
|
||||
BlockBasedTableOptions table_options_no_bc;
|
||||
table_options_no_bc.no_block_cache = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options_no_bc));
|
||||
|
||||
Reopen(&options);
|
||||
|
||||
Build(10000);
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_FlushMemTable();
|
||||
dbi->TEST_CompactRange(0, nullptr, nullptr);
|
||||
dbi->TEST_CompactRange(1, nullptr, nullptr);
|
||||
|
||||
senv.count_random_reads_ = true;
|
||||
senv.random_read_counter_.Reset();
|
||||
ASSERT_OK(dbi->VerifyChecksum());
|
||||
|
||||
// Make sure the counter is enabled.
|
||||
ASSERT_GT(senv.random_read_counter_.Read(), 0);
|
||||
|
||||
// The SST file is about 10MB. Default readahead size is 256KB.
|
||||
// Give a conservative 20 reads for metadata blocks, The number
|
||||
// of random reads should be within 10 MB / 256KB + 20 = 60.
|
||||
ASSERT_LT(senv.random_read_counter_.Read(), 60);
|
||||
|
||||
senv.random_read_bytes_counter_ = 0;
|
||||
ReadOptions ro;
|
||||
ro.readahead_size = size_t{32 * 1024};
|
||||
ASSERT_OK(dbi->VerifyChecksum(ro));
|
||||
// The SST file is about 10MB. We set readahead size to 32KB.
|
||||
// Give 0 to 20 reads for metadata blocks, and allow real read
|
||||
// to range from 24KB to 48KB. The lower bound would be:
|
||||
// 10MB / 48KB + 0 = 213
|
||||
// The higher bound is
|
||||
// 10MB / 24KB + 20 = 447.
|
||||
ASSERT_GE(senv.random_read_counter_.Read(), 213);
|
||||
ASSERT_LE(senv.random_read_counter_.Read(), 447);
|
||||
|
||||
// Test readahead shouldn't break mmap mode (where it should be
|
||||
// disabled).
|
||||
options.allow_mmap_reads = true;
|
||||
Reopen(&options);
|
||||
ASSERT_OK(dbi->VerifyChecksum(ro));
|
||||
|
||||
CloseDb();
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, TableFileIndexData) {
|
||||
Options options;
|
||||
// very big, we'll trigger flushes manually
|
||||
|
||||
+576
-29
@@ -10,6 +10,8 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "table/block_based/block_builder.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "test_util/sync_point.h"
|
||||
@@ -68,6 +70,44 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, ReadOnlyDBWithWriteDBIdToManifestSet) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("bar", "v2"));
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
Close();
|
||||
|
||||
auto options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = true;
|
||||
assert(options.env == env_);
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
std::string db_id1;
|
||||
db_->GetDbIdentity(db_id1);
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
int count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
++count;
|
||||
}
|
||||
ASSERT_EQ(count, 2);
|
||||
delete iter;
|
||||
Close();
|
||||
|
||||
// Reopen and flush memtable.
|
||||
Reopen(options);
|
||||
Flush();
|
||||
Close();
|
||||
// Now check keys in read only mode.
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
ASSERT_TRUE(db_->SyncWAL().IsNotSupported());
|
||||
std::string db_id2;
|
||||
db_->GetDbIdentity(db_id2);
|
||||
ASSERT_EQ(db_id1, db_id2);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, CompactedDB) {
|
||||
const uint64_t kFileSize = 1 << 20;
|
||||
Options options = CurrentOptions();
|
||||
@@ -295,7 +335,7 @@ TEST_F(DBBasicTest, FlushMultipleMemtable) {
|
||||
writeOpt.disableWAL = true;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.max_write_buffer_number_to_maintain = -1;
|
||||
options.max_write_buffer_size_to_maintain = -1;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v1"));
|
||||
ASSERT_OK(Flush(1));
|
||||
@@ -325,7 +365,8 @@ TEST_F(DBBasicTest, FlushEmptyColumnFamily) {
|
||||
writeOpt.disableWAL = true;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.min_write_buffer_number_to_merge = 1;
|
||||
options.max_write_buffer_number_to_maintain = 1;
|
||||
options.max_write_buffer_size_to_maintain =
|
||||
static_cast<int64_t>(options.write_buffer_size);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Compaction can still go through even if no thread can flush the
|
||||
@@ -421,7 +462,7 @@ TEST_F(DBBasicTest, ManifestRollOver) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, IdentityAcrossRestarts) {
|
||||
TEST_F(DBBasicTest, IdentityAcrossRestarts1) {
|
||||
do {
|
||||
std::string id1;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
@@ -433,13 +474,40 @@ TEST_F(DBBasicTest, IdentityAcrossRestarts) {
|
||||
// id1 should match id2 because identity was not regenerated
|
||||
ASSERT_EQ(id1.compare(id2), 0);
|
||||
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
if (options.write_dbid_to_manifest) {
|
||||
ASSERT_EQ(id1.compare(id3), 0);
|
||||
} else {
|
||||
// id1 should NOT match id3 because identity was regenerated
|
||||
ASSERT_NE(id1.compare(id3), 0);
|
||||
}
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, IdentityAcrossRestarts2) {
|
||||
do {
|
||||
std::string id1;
|
||||
ASSERT_OK(db_->GetDbIdentity(id1));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_dbid_to_manifest = true;
|
||||
Reopen(options);
|
||||
std::string id2;
|
||||
ASSERT_OK(db_->GetDbIdentity(id2));
|
||||
// id1 should match id2 because identity was not regenerated
|
||||
ASSERT_EQ(id1.compare(id2), 0);
|
||||
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
ASSERT_OK(env_->DeleteFile(idfilename));
|
||||
Reopen(options);
|
||||
std::string id3;
|
||||
ASSERT_OK(db_->GetDbIdentity(id3));
|
||||
// id1 should NOT match id3 because identity was regenerated
|
||||
ASSERT_NE(id1.compare(id3), 0);
|
||||
ASSERT_EQ(id1, id3);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
@@ -845,30 +913,30 @@ TEST_F(DBBasicTest, MmapAndBufferOptions) {
|
||||
|
||||
class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv() : EnvWrapper(Env::Default()),
|
||||
close_count(0) { }
|
||||
explicit TestEnv(Env* base_env) : EnvWrapper(base_env), close_count(0) {}
|
||||
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
TestLogger(TestEnv *env_ptr) : Logger() { env = env_ptr; }
|
||||
~TestLogger() override {
|
||||
if (!closed_) {
|
||||
CloseHelper();
|
||||
}
|
||||
}
|
||||
void Logv(const char* /*format*/, va_list /*ap*/) override{};
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
explicit TestLogger(TestEnv* env_ptr) : Logger() { env = env_ptr; }
|
||||
~TestLogger() override {
|
||||
if (!closed_) {
|
||||
CloseHelper();
|
||||
}
|
||||
}
|
||||
void Logv(const char* /*format*/, va_list /*ap*/) override {}
|
||||
|
||||
protected:
|
||||
Status CloseImpl() override { return CloseHelper(); }
|
||||
protected:
|
||||
Status CloseImpl() override { return CloseHelper(); }
|
||||
|
||||
private:
|
||||
Status CloseHelper() {
|
||||
env->CloseCountInc();;
|
||||
return Status::IOError();
|
||||
}
|
||||
TestEnv *env;
|
||||
};
|
||||
private:
|
||||
Status CloseHelper() {
|
||||
env->CloseCountInc();
|
||||
;
|
||||
return Status::IOError();
|
||||
}
|
||||
TestEnv* env;
|
||||
};
|
||||
|
||||
void CloseCountInc() { close_count++; }
|
||||
|
||||
@@ -890,7 +958,8 @@ TEST_F(DBBasicTest, DBClose) {
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
|
||||
DB* db = nullptr;
|
||||
TestEnv* env = new TestEnv();
|
||||
TestEnv* env = new TestEnv(env_);
|
||||
std::unique_ptr<TestEnv> local_env_guard(env);
|
||||
options.create_if_missing = true;
|
||||
options.env = env;
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
@@ -924,13 +993,11 @@ TEST_F(DBBasicTest, DBClose) {
|
||||
ASSERT_EQ(env->GetCloseCount(), 2);
|
||||
options.info_log.reset();
|
||||
ASSERT_EQ(env->GetCloseCount(), 3);
|
||||
|
||||
delete options.env;
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DBCloseFlushError) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.manual_wal_flush = true;
|
||||
@@ -1285,6 +1352,477 @@ TEST_F(DBBasicTest, MultiGetBatchedMultiLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test class for batched MultiGet with prefix extractor
|
||||
// Param bool - If true, use partitioned filters
|
||||
// If false, use full filter block
|
||||
class MultiGetPrefixExtractorTest : public DBBasicTest,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
};
|
||||
|
||||
TEST_P(MultiGetPrefixExtractorTest, Batched) {
|
||||
Options options = CurrentOptions();
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
options.memtable_prefix_bloom_size_ratio = 10;
|
||||
BlockBasedTableOptions bbto;
|
||||
if (GetParam()) {
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
bbto.partition_filters = true;
|
||||
}
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.whole_key_filtering = false;
|
||||
bbto.cache_index_and_filter_blocks = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
get_perf_context()->Reset();
|
||||
|
||||
// First key is not in the prefix_extractor domain
|
||||
ASSERT_OK(Put("k", "v0"));
|
||||
ASSERT_OK(Put("kk1", "v1"));
|
||||
ASSERT_OK(Put("kk2", "v2"));
|
||||
ASSERT_OK(Put("kk3", "v3"));
|
||||
ASSERT_OK(Put("kk4", "v4"));
|
||||
std::vector<std::string> mem_keys(
|
||||
{"k", "kk1", "kk2", "kk3", "kk4", "rofl", "lmho"});
|
||||
std::vector<std::string> inmem_values;
|
||||
inmem_values = MultiGet(mem_keys, nullptr);
|
||||
ASSERT_EQ(inmem_values[0], "v0");
|
||||
ASSERT_EQ(inmem_values[1], "v1");
|
||||
ASSERT_EQ(inmem_values[2], "v2");
|
||||
ASSERT_EQ(inmem_values[3], "v3");
|
||||
ASSERT_EQ(inmem_values[4], "v4");
|
||||
ASSERT_EQ(get_perf_context()->bloom_memtable_miss_count, 2);
|
||||
ASSERT_EQ(get_perf_context()->bloom_memtable_hit_count, 5);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::vector<std::string> keys({"k", "kk1", "kk2", "kk3", "kk4"});
|
||||
std::vector<std::string> values;
|
||||
get_perf_context()->Reset();
|
||||
values = MultiGet(keys, nullptr);
|
||||
ASSERT_EQ(values[0], "v0");
|
||||
ASSERT_EQ(values[1], "v1");
|
||||
ASSERT_EQ(values[2], "v2");
|
||||
ASSERT_EQ(values[3], "v3");
|
||||
ASSERT_EQ(values[4], "v4");
|
||||
// Filter hits for 4 in-domain keys
|
||||
ASSERT_EQ(get_perf_context()->bloom_sst_hit_count, 4);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MultiGetPrefix, MultiGetPrefixExtractorTest,
|
||||
::testing::Bool());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class DBMultiGetRowCacheTest : public DBBasicTest,
|
||||
public ::testing::WithParamInterface<bool> {};
|
||||
|
||||
TEST_P(DBMultiGetRowCacheTest, MultiGetBatched) {
|
||||
do {
|
||||
option_config_ = kRowCache;
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_OK(Put(1, "k1", "v1"));
|
||||
ASSERT_OK(Put(1, "k2", "v2"));
|
||||
ASSERT_OK(Put(1, "k3", "v3"));
|
||||
ASSERT_OK(Put(1, "k4", "v4"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "k5", "v5"));
|
||||
const Snapshot* snap1 = dbfull()->GetSnapshot();
|
||||
ASSERT_OK(Delete(1, "k4"));
|
||||
Flush(1);
|
||||
const Snapshot* snap2 = dbfull()->GetSnapshot();
|
||||
|
||||
get_perf_context()->Reset();
|
||||
|
||||
std::vector<Slice> keys({"no_key", "k5", "k4", "k3", "k1"});
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
bool use_snapshots = GetParam();
|
||||
if (use_snapshots) {
|
||||
ro.snapshot = snap2;
|
||||
}
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
ASSERT_EQ(std::string(values[4].data(), values[4].size()), "v1");
|
||||
ASSERT_EQ(std::string(values[3].data(), values[3].size()), "v3");
|
||||
ASSERT_EQ(std::string(values[1].data(), values[1].size()), "v5");
|
||||
// four kv pairs * two bytes per value
|
||||
ASSERT_EQ(6, (int)get_perf_context()->multiget_read_bytes);
|
||||
|
||||
ASSERT_TRUE(s[0].IsNotFound());
|
||||
ASSERT_OK(s[1]);
|
||||
ASSERT_TRUE(s[2].IsNotFound());
|
||||
ASSERT_OK(s[3]);
|
||||
ASSERT_OK(s[4]);
|
||||
|
||||
// Call MultiGet() again with some intersection with the previous set of
|
||||
// keys. Those should already be in the row cache.
|
||||
keys.assign({"no_key", "k5", "k3", "k2"});
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
values[i].Reset();
|
||||
s[i] = Status::OK();
|
||||
}
|
||||
get_perf_context()->Reset();
|
||||
|
||||
if (use_snapshots) {
|
||||
ro.snapshot = snap1;
|
||||
}
|
||||
db_->MultiGet(ReadOptions(), handles_[1], keys.size(), keys.data(),
|
||||
values.data(), s.data(), false);
|
||||
|
||||
ASSERT_EQ(std::string(values[3].data(), values[3].size()), "v2");
|
||||
ASSERT_EQ(std::string(values[2].data(), values[2].size()), "v3");
|
||||
ASSERT_EQ(std::string(values[1].data(), values[1].size()), "v5");
|
||||
// four kv pairs * two bytes per value
|
||||
ASSERT_EQ(6, (int)get_perf_context()->multiget_read_bytes);
|
||||
|
||||
ASSERT_TRUE(s[0].IsNotFound());
|
||||
ASSERT_OK(s[1]);
|
||||
ASSERT_OK(s[2]);
|
||||
ASSERT_OK(s[3]);
|
||||
if (use_snapshots) {
|
||||
// Only reads from the first SST file would have been cached, since
|
||||
// snapshot seq no is > fd.largest_seqno
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, ROW_CACHE_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, ROW_CACHE_HIT));
|
||||
}
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
dbfull()->ReleaseSnapshot(snap1);
|
||||
dbfull()->ReleaseSnapshot(snap2);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBMultiGetRowCacheTest, DBMultiGetRowCacheTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
const size_t kNumInserts = 4;
|
||||
const size_t kNumDeletes = 4;
|
||||
const size_t kNumUpdates = 4;
|
||||
|
||||
// Check default column family
|
||||
for (size_t i = 0; i != kNumInserts; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(i), "value"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumUpdates; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(i), "value1"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumDeletes; ++i) {
|
||||
ASSERT_OK(Delete(std::to_string(i)));
|
||||
}
|
||||
std::vector<KeyVersion> key_versions;
|
||||
ASSERT_OK(rocksdb::GetAllKeyVersions(db_, Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
ASSERT_OK(rocksdb::GetAllKeyVersions(db_, handles_[0], Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
|
||||
// Check non-default column family
|
||||
for (size_t i = 0; i != kNumInserts - 1; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "value"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumUpdates - 1; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "value1"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumDeletes - 1; ++i) {
|
||||
ASSERT_OK(Delete(1, std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(rocksdb::GetAllKeyVersions(db_, handles_[1], Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates - 3, key_versions.size());
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
class DBBasicTestWithParallelIO
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
|
||||
public:
|
||||
DBBasicTestWithParallelIO() : DBTestBase("/db_basic_test_with_parallel_io") {
|
||||
bool compressed_cache = std::get<0>(GetParam());
|
||||
bool uncompressed_cache = std::get<1>(GetParam());
|
||||
compression_enabled_ = std::get<2>(GetParam());
|
||||
fill_cache_ = std::get<3>(GetParam());
|
||||
|
||||
if (compressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
compressed_cache_ = std::make_shared<MyBlockCache>(cache);
|
||||
}
|
||||
if (uncompressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
uncompressed_cache_ = std::make_shared<MyBlockCache>(cache);
|
||||
}
|
||||
|
||||
env_->count_random_reads_ = true;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
Random rnd(301);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.block_cache = uncompressed_cache_;
|
||||
table_options.block_cache_compressed = compressed_cache_;
|
||||
table_options.flush_block_policy_factory.reset(
|
||||
new MyFlushBlockPolicyFactory());
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
if (!compression_enabled_) {
|
||||
options.compression = kNoCompression;
|
||||
}
|
||||
Reopen(options);
|
||||
|
||||
std::string zero_str(128, '\0');
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// Make the value compressible. A purely random string doesn't compress
|
||||
// and the resultant data block will not be compressed
|
||||
values_.emplace_back(RandomString(&rnd, 128) + zero_str);
|
||||
assert(Put(Key(i), values_[i]) == Status::OK());
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
bool CheckValue(int i, const std::string& value) {
|
||||
if (values_[i].compare(value) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int num_lookups() { return uncompressed_cache_->num_lookups(); }
|
||||
int num_found() { return uncompressed_cache_->num_found(); }
|
||||
int num_inserts() { return uncompressed_cache_->num_inserts(); }
|
||||
|
||||
int num_lookups_compressed() { return compressed_cache_->num_lookups(); }
|
||||
int num_found_compressed() { return compressed_cache_->num_found(); }
|
||||
int num_inserts_compressed() { return compressed_cache_->num_inserts(); }
|
||||
|
||||
bool fill_cache() { return fill_cache_; }
|
||||
|
||||
static void SetUpTestCase() {}
|
||||
static void TearDownTestCase() {}
|
||||
|
||||
private:
|
||||
class MyFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
|
||||
public:
|
||||
MyFlushBlockPolicyFactory() {}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "MyFlushBlockPolicyFactory";
|
||||
}
|
||||
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new MyFlushBlockPolicy(data_block_builder);
|
||||
}
|
||||
};
|
||||
|
||||
class MyFlushBlockPolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
explicit MyFlushBlockPolicy(const BlockBuilder& data_block_builder)
|
||||
: num_keys_(0), data_block_builder_(data_block_builder) {}
|
||||
|
||||
bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (data_block_builder_.empty()) {
|
||||
// First key in this block
|
||||
num_keys_ = 1;
|
||||
return false;
|
||||
}
|
||||
// Flush every 10 keys
|
||||
if (num_keys_ == 10) {
|
||||
num_keys_ = 1;
|
||||
return true;
|
||||
}
|
||||
num_keys_++;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int num_keys_;
|
||||
const BlockBuilder& data_block_builder_;
|
||||
};
|
||||
|
||||
class MyBlockCache : public Cache {
|
||||
public:
|
||||
explicit MyBlockCache(std::shared_ptr<Cache>& target)
|
||||
: target_(target), num_lookups_(0), num_found_(0), num_inserts_(0) {}
|
||||
|
||||
virtual const char* Name() const override { return "MyBlockCache"; }
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
num_inserts_++;
|
||||
return target_->Insert(key, value, charge, deleter, handle, priority);
|
||||
}
|
||||
|
||||
virtual Handle* Lookup(const Slice& key,
|
||||
Statistics* stats = nullptr) override {
|
||||
num_lookups_++;
|
||||
Handle* handle = target_->Lookup(key, stats);
|
||||
if (handle != nullptr) {
|
||||
num_found_++;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
virtual bool Ref(Handle* handle) override { return target_->Ref(handle); }
|
||||
|
||||
virtual bool Release(Handle* handle, bool force_erase = false) override {
|
||||
return target_->Release(handle, force_erase);
|
||||
}
|
||||
|
||||
virtual void* Value(Handle* handle) override {
|
||||
return target_->Value(handle);
|
||||
}
|
||||
|
||||
virtual void Erase(const Slice& key) override { target_->Erase(key); }
|
||||
virtual uint64_t NewId() override { return target_->NewId(); }
|
||||
|
||||
virtual void SetCapacity(size_t capacity) override {
|
||||
target_->SetCapacity(capacity);
|
||||
}
|
||||
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override {
|
||||
target_->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
virtual bool HasStrictCapacityLimit() const override {
|
||||
return target_->HasStrictCapacityLimit();
|
||||
}
|
||||
|
||||
virtual size_t GetCapacity() const override {
|
||||
return target_->GetCapacity();
|
||||
}
|
||||
|
||||
virtual size_t GetUsage() const override { return target_->GetUsage(); }
|
||||
|
||||
virtual size_t GetUsage(Handle* handle) const override {
|
||||
return target_->GetUsage(handle);
|
||||
}
|
||||
|
||||
virtual size_t GetPinnedUsage() const override {
|
||||
return target_->GetPinnedUsage();
|
||||
}
|
||||
|
||||
virtual size_t GetCharge(Handle* /*handle*/) const override { return 0; }
|
||||
|
||||
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override {
|
||||
return target_->ApplyToAllCacheEntries(callback, thread_safe);
|
||||
}
|
||||
|
||||
virtual void EraseUnRefEntries() override {
|
||||
return target_->EraseUnRefEntries();
|
||||
}
|
||||
|
||||
int num_lookups() { return num_lookups_; }
|
||||
|
||||
int num_found() { return num_found_; }
|
||||
|
||||
int num_inserts() { return num_inserts_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> target_;
|
||||
int num_lookups_;
|
||||
int num_found_;
|
||||
int num_inserts_;
|
||||
};
|
||||
|
||||
std::shared_ptr<MyBlockCache> compressed_cache_;
|
||||
std::shared_ptr<MyBlockCache> uncompressed_cache_;
|
||||
bool compression_enabled_;
|
||||
std::vector<std::string> values_;
|
||||
bool fill_cache_;
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithParallelIO, MultiGet) {
|
||||
std::vector<std::string> key_data(10);
|
||||
std::vector<Slice> keys;
|
||||
// We cannot resize a PinnableSlice vector, so just set initial size to
|
||||
// largest we think we will need
|
||||
std::vector<PinnableSlice> values(10);
|
||||
std::vector<Status> statuses;
|
||||
ReadOptions ro;
|
||||
ro.fill_cache = fill_cache();
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
ASSERT_TRUE(CheckValue(0, values[0].ToString()));
|
||||
ASSERT_TRUE(CheckValue(50, values[1].ToString()));
|
||||
|
||||
int random_reads = env_->random_read_counter_.Read();
|
||||
key_data[0] = Key(1);
|
||||
key_data[1] = Key(51);
|
||||
keys[0] = Slice(key_data[0]);
|
||||
keys[1] = Slice(key_data[1]);
|
||||
values[0].Reset();
|
||||
values[1].Reset();
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
ASSERT_TRUE(CheckValue(1, values[0].ToString()));
|
||||
ASSERT_TRUE(CheckValue(51, values[1].ToString()));
|
||||
|
||||
int expected_reads = random_reads + (fill_cache() ? 0 : 2);
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), expected_reads);
|
||||
|
||||
keys.resize(10);
|
||||
statuses.resize(10);
|
||||
std::vector<int> key_ints{1, 2, 15, 16, 55, 81, 82, 83, 84, 85};
|
||||
for (size_t i = 0; i < key_ints.size(); ++i) {
|
||||
key_data[i] = Key(key_ints[i]);
|
||||
keys[i] = Slice(key_data[i]);
|
||||
statuses[i] = Status::OK();
|
||||
values[i].Reset();
|
||||
}
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
for (size_t i = 0; i < key_ints.size(); ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
ASSERT_TRUE(CheckValue(key_ints[i], values[i].ToString()));
|
||||
}
|
||||
expected_reads += (fill_cache() ? 2 : 4);
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), expected_reads);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ParallelIO, DBBasicTestWithParallelIO,
|
||||
// Params are as follows -
|
||||
// Param 0 - Compressed cache enabled
|
||||
// Param 1 - Uncompressed cache enabled
|
||||
// Param 2 - Data compression enabled
|
||||
// Param 3 - ReadOptions::fill_cache
|
||||
::testing::Values(std::make_tuple(false, true, true, true),
|
||||
std::make_tuple(true, true, true, true),
|
||||
std::make_tuple(false, true, false, true),
|
||||
std::make_tuple(false, true, true, false),
|
||||
std::make_tuple(true, true, true, false),
|
||||
std::make_tuple(false, true, false, false)));
|
||||
|
||||
class DBBasicTestWithTimestampWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
@@ -1437,8 +1975,17 @@ INSTANTIATE_TEST_CASE_P(Timestamp, DBBasicTestWithTimestampWithParam,
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
extern "C" {
|
||||
void RegisterCustomObjects(int argc, char** argv);
|
||||
}
|
||||
#else
|
||||
void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
|
||||
#endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/db_test_util.h"
|
||||
@@ -63,9 +64,11 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
ReadOptions read_options;
|
||||
read_options.snapshot = snapshot;
|
||||
PinnableSlice value;
|
||||
auto s = dbfull()->GetImpl(read_options, cfh(), key, &value,
|
||||
nullptr /*value_found*/, nullptr /*callback*/,
|
||||
is_blob_index);
|
||||
DBImpl::GetImplOptions get_impl_options;
|
||||
get_impl_options.column_family = cfh();
|
||||
get_impl_options.value = &value;
|
||||
get_impl_options.is_blob_index = is_blob_index;
|
||||
auto s = dbfull()->GetImpl(read_options, key, get_impl_options);
|
||||
if (s.IsNotFound()) {
|
||||
return "NOT_FOUND";
|
||||
}
|
||||
|
||||
+72
-32
@@ -19,6 +19,9 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
size_t hit_count_ = 0;
|
||||
size_t insert_count_ = 0;
|
||||
size_t failure_count_ = 0;
|
||||
size_t compression_dict_miss_count_ = 0;
|
||||
size_t compression_dict_hit_count_ = 0;
|
||||
size_t compression_dict_insert_count_ = 0;
|
||||
size_t compressed_miss_count_ = 0;
|
||||
size_t compressed_hit_count_ = 0;
|
||||
size_t compressed_insert_count_ = 0;
|
||||
@@ -69,6 +72,15 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSED_ADD_FAILURES);
|
||||
}
|
||||
|
||||
void RecordCacheCountersForCompressionDict(const Options& options) {
|
||||
compression_dict_miss_count_ =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS);
|
||||
compression_dict_hit_count_ =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_HIT);
|
||||
compression_dict_insert_count_ =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_ADD);
|
||||
}
|
||||
|
||||
void CheckCacheCounters(const Options& options, size_t expected_misses,
|
||||
size_t expected_hits, size_t expected_inserts,
|
||||
size_t expected_failures) {
|
||||
@@ -87,6 +99,28 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
failure_count_ = new_failure_count;
|
||||
}
|
||||
|
||||
void CheckCacheCountersForCompressionDict(
|
||||
const Options& options, size_t expected_compression_dict_misses,
|
||||
size_t expected_compression_dict_hits,
|
||||
size_t expected_compression_dict_inserts) {
|
||||
size_t new_compression_dict_miss_count =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS);
|
||||
size_t new_compression_dict_hit_count =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_HIT);
|
||||
size_t new_compression_dict_insert_count =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_ADD);
|
||||
ASSERT_EQ(compression_dict_miss_count_ + expected_compression_dict_misses,
|
||||
new_compression_dict_miss_count);
|
||||
ASSERT_EQ(compression_dict_hit_count_ + expected_compression_dict_hits,
|
||||
new_compression_dict_hit_count);
|
||||
ASSERT_EQ(
|
||||
compression_dict_insert_count_ + expected_compression_dict_inserts,
|
||||
new_compression_dict_insert_count);
|
||||
compression_dict_miss_count_ = new_compression_dict_miss_count;
|
||||
compression_dict_hit_count_ = new_compression_dict_hit_count;
|
||||
compression_dict_insert_count_ = new_compression_dict_insert_count;
|
||||
}
|
||||
|
||||
void CheckCompressedCacheCounters(const Options& options,
|
||||
size_t expected_misses,
|
||||
size_t expected_hits,
|
||||
@@ -346,8 +380,13 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
LRUCacheOptions co;
|
||||
// 500 bytes are enough to hold the first two blocks
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(500, 0, false);
|
||||
co.capacity = 500;
|
||||
co.num_shard_bits = 0;
|
||||
co.strict_capacity_limit = false;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(co);
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20, true));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
@@ -365,11 +404,11 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
ASSERT_EQ(cache->GetUsage(), index_bytes_insert + filter_bytes_insert);
|
||||
// set the cache capacity to the current usage
|
||||
cache->SetCapacity(index_bytes_insert + filter_bytes_insert);
|
||||
// The index eviction statistics were broken by the refactoring that moved
|
||||
// the index readers out of the block cache. Disabling these until we can
|
||||
// The index and filter eviction statistics were broken by the refactoring
|
||||
// that moved the readers out of the block cache. Disabling these until we can
|
||||
// bring the stats back.
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT), 0);
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT), 0);
|
||||
// Note that the second key needs to be no longer than the first one.
|
||||
// Otherwise the second index block may not fit in cache.
|
||||
ASSERT_OK(Put(1, "key", "val"));
|
||||
@@ -380,13 +419,13 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
index_bytes_insert);
|
||||
ASSERT_GT(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_INSERT),
|
||||
filter_bytes_insert);
|
||||
// The index eviction statistics were broken by the refactoring that moved
|
||||
// the index readers out of the block cache. Disabling these until we can
|
||||
// The index and filter eviction statistics were broken by the refactoring
|
||||
// that moved the readers out of the block cache. Disabling these until we can
|
||||
// bring the stats back.
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT),
|
||||
// index_bytes_insert);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT),
|
||||
filter_bytes_insert);
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT),
|
||||
// filter_bytes_insert);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -450,11 +489,11 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksCachePriority) {
|
||||
TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_MISS));
|
||||
if (priority == Cache::Priority::LOW) {
|
||||
ASSERT_EQ(0, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(2, MockCache::low_pri_insert_count);
|
||||
ASSERT_EQ(0u, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(2u, MockCache::low_pri_insert_count);
|
||||
} else {
|
||||
ASSERT_EQ(2, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(0, MockCache::low_pri_insert_count);
|
||||
ASSERT_EQ(2u, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(0u, MockCache::low_pri_insert_count);
|
||||
}
|
||||
|
||||
// Access data block.
|
||||
@@ -468,11 +507,11 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksCachePriority) {
|
||||
|
||||
// Data block should be inserted with low priority.
|
||||
if (priority == Cache::Priority::LOW) {
|
||||
ASSERT_EQ(0, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(3, MockCache::low_pri_insert_count);
|
||||
ASSERT_EQ(0u, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(3u, MockCache::low_pri_insert_count);
|
||||
} else {
|
||||
ASSERT_EQ(2, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(1, MockCache::low_pri_insert_count);
|
||||
ASSERT_EQ(2u, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(1u, MockCache::low_pri_insert_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,6 +710,8 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
RecordCacheCountersForCompressionDict(options);
|
||||
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
ASSERT_EQ(i, NumTableFilesAtLevel(0, 0));
|
||||
for (int j = 0; j < kNumEntriesPerFile; ++j) {
|
||||
@@ -683,27 +724,26 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
// Compression dictionary blocks are preloaded.
|
||||
CheckCacheCountersForCompressionDict(
|
||||
options, kNumFiles /* expected_compression_dict_misses */,
|
||||
0 /* expected_compression_dict_hits */,
|
||||
kNumFiles /* expected_compression_dict_inserts */);
|
||||
|
||||
// Seek to a key in a file. It should cause the SST's dictionary meta-block
|
||||
// to be read.
|
||||
RecordCacheCounters(options);
|
||||
ASSERT_EQ(0,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_ADD));
|
||||
ASSERT_EQ(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
RecordCacheCountersForCompressionDict(options);
|
||||
ReadOptions read_options;
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(kNumFiles * kNumEntriesPerFile - 1)));
|
||||
// Two blocks missed/added: dictionary and data block
|
||||
// One block hit: index since it's prefetched
|
||||
CheckCacheCounters(options, 2 /* expected_misses */, 1 /* expected_hits */,
|
||||
2 /* expected_inserts */, 0 /* expected_failures */);
|
||||
ASSERT_EQ(1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_ADD));
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
// Two block hits: index and dictionary since they are prefetched
|
||||
// One block missed/added: data block
|
||||
CheckCacheCounters(options, 1 /* expected_misses */, 2 /* expected_hits */,
|
||||
1 /* expected_inserts */, 0 /* expected_failures */);
|
||||
CheckCacheCountersForCompressionDict(
|
||||
options, 0 /* expected_compression_dict_misses */,
|
||||
1 /* expected_compression_dict_hits */,
|
||||
0 /* expected_compression_dict_inserts */);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -720,7 +720,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
cfilter_count = 0;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
// The filter should delete 40 records.
|
||||
ASSERT_EQ(40U, cfilter_count);
|
||||
ASSERT_EQ(40, cfilter_count);
|
||||
|
||||
{
|
||||
// Scan the entire database as of the snapshot to ensure
|
||||
|
||||
+43
-24
@@ -141,7 +141,7 @@ Options DeletionTriggerOptions(Options options) {
|
||||
options.compression = kNoCompression;
|
||||
options.write_buffer_size = kCDTKeysPerBuffer * (kCDTValueSize + 24);
|
||||
options.min_write_buffer_number_to_merge = 1;
|
||||
options.max_write_buffer_number_to_maintain = 0;
|
||||
options.max_write_buffer_size_to_maintain = 0;
|
||||
options.num_levels = kCDTNumLevels;
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.target_file_size_base = options.write_buffer_size * 2;
|
||||
@@ -497,14 +497,14 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
|
||||
// Create new iterator for:
|
||||
// (1) 1 for verifying flush results
|
||||
// (2) 3 for compaction input files
|
||||
// (3) 1 for verifying compaction results.
|
||||
ASSERT_EQ(num_new_table_reader, 5);
|
||||
// (2) 1 for verifying compaction results.
|
||||
// (3) New TableReaders will not be created for compaction inputs
|
||||
ASSERT_EQ(num_new_table_reader, 2);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(1), Get(Key(1)));
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 3);
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 5);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
@@ -519,13 +519,14 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
// May preload table cache too.
|
||||
ASSERT_GE(num_table_cache_lookup, 1);
|
||||
old_num_table_cache_lookup2 = num_table_cache_lookup;
|
||||
// One for compaction input, one for verifying compaction results.
|
||||
ASSERT_EQ(num_new_table_reader, 2);
|
||||
// One for verifying compaction results.
|
||||
// No new iterator created for compaction.
|
||||
ASSERT_EQ(num_new_table_reader, 1);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(1), Get(Key(1)));
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 2);
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 3);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -731,7 +732,7 @@ TEST_F(DBCompactionTest, BGCompactionsAllowed) {
|
||||
|
||||
// Now all column families qualify compaction but only one should be
|
||||
// scheduled, because no column family hits speed up condition.
|
||||
ASSERT_EQ(1, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
ASSERT_EQ(1u, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
|
||||
// Create two more files for one column family, which triggers speed up
|
||||
// condition, three compactions will be scheduled.
|
||||
@@ -745,7 +746,7 @@ TEST_F(DBCompactionTest, BGCompactionsAllowed) {
|
||||
ASSERT_EQ(options.level0_file_num_compaction_trigger + num + 1,
|
||||
NumTableFilesAtLevel(0, 2));
|
||||
}
|
||||
ASSERT_EQ(3, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
ASSERT_EQ(3U, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
|
||||
// Unblock all threads to unblock all compactions.
|
||||
for (size_t i = 0; i < kTotalTasks; i++) {
|
||||
@@ -776,7 +777,7 @@ TEST_F(DBCompactionTest, BGCompactionsAllowed) {
|
||||
|
||||
// Now all column families qualify compaction but only one should be
|
||||
// scheduled, because no column family hits speed up condition.
|
||||
ASSERT_EQ(1, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
ASSERT_EQ(1U, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
|
||||
for (size_t i = 0; i < kTotalTasks; i++) {
|
||||
sleeping_tasks[i].WakeUp();
|
||||
@@ -4164,7 +4165,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
|
||||
const char* cf_names[] = {"default", "0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
|
||||
const int cf_count = sizeof cf_names / sizeof cf_names[0];
|
||||
const unsigned int cf_count = sizeof cf_names / sizeof cf_names[0];
|
||||
|
||||
std::unordered_map<std::string, CompactionLimiter*> cf_to_limiter;
|
||||
|
||||
@@ -4183,7 +4184,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
std::vector<Options> option_vector;
|
||||
option_vector.reserve(cf_count);
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
for (unsigned int cf = 0; cf < cf_count; cf++) {
|
||||
ColumnFamilyOptions cf_opt(options);
|
||||
if (cf == 0) {
|
||||
// "Default" CF does't use compaction limiter
|
||||
@@ -4201,7 +4202,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt);
|
||||
}
|
||||
|
||||
for (int cf = 1; cf < cf_count; cf++) {
|
||||
for (unsigned int cf = 1; cf < cf_count; cf++) {
|
||||
CreateColumnFamilies({cf_names[cf]}, option_vector[cf]);
|
||||
}
|
||||
|
||||
@@ -4253,7 +4254,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
int keyIndex = 0;
|
||||
|
||||
for (int n = 0; n < options.level0_file_num_compaction_trigger; n++) {
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
for (unsigned int cf = 0; cf < cf_count; cf++) {
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(cf, Key(keyIndex++), ""));
|
||||
}
|
||||
@@ -4261,13 +4262,13 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
ASSERT_OK(Put(cf, "", ""));
|
||||
}
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
for (unsigned int cf = 0; cf < cf_count; cf++) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf]);
|
||||
}
|
||||
}
|
||||
|
||||
// Enough L0 files to trigger compaction
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
for (unsigned int cf = 0; cf < cf_count; cf++) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, cf),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
}
|
||||
@@ -4294,7 +4295,7 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
sleeping_compact_tasks[i].WaitUntilDone();
|
||||
}
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
for (unsigned int cf = 0; cf < cf_count; cf++) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf]);
|
||||
}
|
||||
|
||||
@@ -4339,12 +4340,6 @@ TEST_P(DBCompactionDirectIOTest, DirectIO) {
|
||||
options.env = new MockEnv(Env::Default());
|
||||
Reopen(options);
|
||||
bool readahead = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::NewIterator:for_compaction", [&](void* arg) {
|
||||
bool* use_direct_reads = static_cast<bool*>(arg);
|
||||
ASSERT_EQ(*use_direct_reads,
|
||||
options.use_direct_reads);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::OpenCompactionOutputFile", [&](void* arg) {
|
||||
bool* use_direct_writes = static_cast<bool*>(arg);
|
||||
@@ -4663,7 +4658,31 @@ TEST_P(DBCompactionTestWithParam, FixFileIngestionCompactionDeadlock) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
Close();
|
||||
}
|
||||
TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
*(p->first) = *(p->second);
|
||||
*(p->second) = temp;
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
Flush();
|
||||
}
|
||||
|
||||
ASSERT_NOK(Put("foo", "bar"));
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
@@ -83,6 +83,34 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBEncryptionTest, ReadEmptyFile) {
|
||||
auto defaultEnv = Env::Default();
|
||||
|
||||
// create empty file for reading it back in later
|
||||
auto envOptions = EnvOptions(CurrentOptions());
|
||||
auto filePath = dbname_ + "/empty.empty";
|
||||
|
||||
Status status;
|
||||
{
|
||||
std::unique_ptr<WritableFile> writableFile;
|
||||
status = defaultEnv->NewWritableFile(filePath, &writableFile, envOptions);
|
||||
ASSERT_OK(status);
|
||||
}
|
||||
|
||||
std::unique_ptr<SequentialFile> seqFile;
|
||||
status = defaultEnv->NewSequentialFile(filePath, &seqFile, envOptions);
|
||||
ASSERT_OK(status);
|
||||
|
||||
std::string scratch;
|
||||
Slice data;
|
||||
// reading back 16 bytes from the empty file shouldn't trigger an assertion.
|
||||
// it should just work and return an empty string
|
||||
status = seqFile->Read(16, &data, (char*)scratch.data());
|
||||
ASSERT_OK(status);
|
||||
|
||||
ASSERT_TRUE(data.empty());
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+10
-1
@@ -6,9 +6,9 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <cinttypes>
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/job_context.h"
|
||||
@@ -163,6 +163,15 @@ Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
return wal_manager_.GetSortedWalFiles(files);
|
||||
}
|
||||
|
||||
Status DBImpl::GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) {
|
||||
uint64_t current_logfile_number;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
current_logfile_number = logfile_number_;
|
||||
}
|
||||
|
||||
return wal_manager_.GetLiveWalFile(current_logfile_number, current_log_file);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -7,10 +7,16 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -290,6 +296,129 @@ TEST_F(DBFlushTest, ManualFlushFailsInReadOnlyMode) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, CFDropRaceWithWaitForFlushMemTables) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:AfterScheduleFlush",
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop"},
|
||||
{"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BackgroundCallFlush:start",
|
||||
"DBImpl::FlushMemTable:BeforeWaitForBgFlush"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(Put(1, "key", "value"));
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
|
||||
port::Thread drop_cf_thr([&]() {
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop");
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_OK(dbfull()->DestroyColumnFamilyHandle(handles_[1]));
|
||||
handles_.resize(1);
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree");
|
||||
});
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
ASSERT_NOK(dbfull()->TEST_FlushMemTable(cfd, flush_opts));
|
||||
drop_cf_thr.join();
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBFlushTest, FireOnFlushCompletedAfterCommittedResult) {
|
||||
class TestListener : public EventListener {
|
||||
public:
|
||||
void OnFlushCompleted(DB* db, const FlushJobInfo& info) override {
|
||||
// There's only one key in each flush.
|
||||
ASSERT_EQ(info.smallest_seqno, info.largest_seqno);
|
||||
ASSERT_NE(0, info.smallest_seqno);
|
||||
if (info.smallest_seqno == seq1) {
|
||||
// First flush completed
|
||||
ASSERT_FALSE(completed1);
|
||||
completed1 = true;
|
||||
CheckFlushResultCommitted(db, seq1);
|
||||
} else {
|
||||
// Second flush completed
|
||||
ASSERT_FALSE(completed2);
|
||||
completed2 = true;
|
||||
ASSERT_EQ(info.smallest_seqno, seq2);
|
||||
CheckFlushResultCommitted(db, seq2);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckFlushResultCommitted(DB* db, SequenceNumber seq) {
|
||||
DBImpl* db_impl = static_cast_with_check<DBImpl>(db);
|
||||
InstrumentedMutex* mutex = db_impl->mutex();
|
||||
mutex->Lock();
|
||||
auto* cfd =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db->DefaultColumnFamily())
|
||||
->cfd();
|
||||
ASSERT_LT(seq, cfd->imm()->current()->GetEarliestSequenceNumber());
|
||||
mutex->Unlock();
|
||||
}
|
||||
|
||||
std::atomic<SequenceNumber> seq1{0};
|
||||
std::atomic<SequenceNumber> seq2{0};
|
||||
std::atomic<bool> completed1{false};
|
||||
std::atomic<bool> completed2{false};
|
||||
};
|
||||
std::shared_ptr<TestListener> listener = std::make_shared<TestListener>();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:AfterScheduleFlush",
|
||||
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:WaitFirst"},
|
||||
{"DBImpl::FlushMemTableToOutputFile:Finish",
|
||||
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:WaitSecond"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table", [&listener](void* arg) {
|
||||
// Wait for the second flush finished, out of mutex.
|
||||
auto* mems = reinterpret_cast<autovector<MemTable*>*>(arg);
|
||||
if (mems->front()->GetEarliestSequenceNumber() == listener->seq1 - 1) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:"
|
||||
"WaitSecond");
|
||||
}
|
||||
});
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.push_back(listener);
|
||||
// Setting max_flush_jobs = max_background_jobs / 4 = 2.
|
||||
options.max_background_jobs = 8;
|
||||
// Allow 2 immutable memtables.
|
||||
options.max_write_buffer_number = 3;
|
||||
Reopen(options);
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(Put("foo", "v"));
|
||||
listener->seq1 = db_->GetLatestSequenceNumber();
|
||||
// t1 will wait for the second flush complete before committing flush result.
|
||||
auto t1 = port::Thread([&]() {
|
||||
// flush_opts.wait = true
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
});
|
||||
// Wait for first flush scheduled.
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:WaitFirst");
|
||||
// The second flush will exit early without commit its result. The work
|
||||
// is delegated to the first flush.
|
||||
ASSERT_OK(Put("bar", "v"));
|
||||
listener->seq2 = db_->GetLatestSequenceNumber();
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
ASSERT_OK(db_->Flush(flush_opts));
|
||||
t1.join();
|
||||
ASSERT_TRUE(listener->completed1);
|
||||
ASSERT_TRUE(listener->completed2);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -545,6 +674,49 @@ TEST_P(DBAtomicFlushTest, PickMemtablesRaceWithBackgroundFlush) {
|
||||
handles_.clear();
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, CFDropRaceWithWaitForFlushMemTables) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::AtomicFlushMemTables:AfterScheduleFlush",
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop"},
|
||||
{"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BackgroundCallFlush:start",
|
||||
"DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(Put(0, "key", "value"));
|
||||
ASSERT_OK(Put(1, "key", "value"));
|
||||
auto* cfd_default =
|
||||
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily())
|
||||
->cfd();
|
||||
auto* cfd_pikachu = static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
|
||||
port::Thread drop_cf_thr([&]() {
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop");
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
delete handles_[1];
|
||||
handles_.resize(1);
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree");
|
||||
});
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
ASSERT_OK(dbfull()->TEST_AtomicFlushMemTables({cfd_default, cfd_pikachu},
|
||||
flush_opts));
|
||||
drop_cf_thr.join();
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBFlushDirectIOTest, DBFlushDirectIOTest,
|
||||
testing::Bool());
|
||||
|
||||
|
||||
+385
-154
@@ -14,6 +14,7 @@
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
#include "db/builder.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/db_info_dumper.h"
|
||||
@@ -34,6 +36,7 @@
|
||||
#include "db/external_sst_file_ingestion_job.h"
|
||||
#include "db/flush_job.h"
|
||||
#include "db/forward_iterator.h"
|
||||
#include "db/import_column_family_job.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
@@ -51,6 +54,7 @@
|
||||
#include "db/write_callback.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "logging/auto_roll_logger.h"
|
||||
#include "logging/log_buffer.h"
|
||||
@@ -89,10 +93,10 @@
|
||||
#include "tools/sst_dump_tool_imp.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/build_version.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -101,7 +105,6 @@ namespace rocksdb {
|
||||
const std::string kDefaultColumnFamilyName("default");
|
||||
const std::string kPersistentStatsColumnFamilyName(
|
||||
"___rocksdb_stats_history___");
|
||||
const int kMicrosInSecond = 1000 * 1000;
|
||||
void DumpRocksDBBuildVersion(Logger* log);
|
||||
|
||||
CompressionType GetCompressionFlush(
|
||||
@@ -162,6 +165,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
batch_per_txn_(batch_per_txn),
|
||||
db_lock_(nullptr),
|
||||
shutting_down_(false),
|
||||
manual_compaction_paused_(false),
|
||||
bg_cv_(&mutex_),
|
||||
logfile_number_(0),
|
||||
log_dir_synced_(false),
|
||||
@@ -237,8 +241,11 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
const int table_cache_size = (mutable_db_options_.max_open_files == -1)
|
||||
? TableCache::kInfiniteCapacity
|
||||
: mutable_db_options_.max_open_files - 10;
|
||||
table_cache_ = NewLRUCache(table_cache_size,
|
||||
immutable_db_options_.table_cache_numshardbits);
|
||||
LRUCacheOptions co;
|
||||
co.capacity = table_cache_size;
|
||||
co.num_shard_bits = immutable_db_options_.table_cache_numshardbits;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
table_cache_ = NewLRUCache(co);
|
||||
|
||||
versions_.reset(new VersionSet(dbname_, &immutable_db_options_, env_options_,
|
||||
table_cache_.get(), write_buffer_manager_,
|
||||
@@ -470,6 +477,7 @@ Status DBImpl::CloseHelper() {
|
||||
&files_grabbed_for_purge_);
|
||||
EraseThreadStatusDbInfo();
|
||||
flush_scheduler_.Clear();
|
||||
trim_history_scheduler_.Clear();
|
||||
|
||||
while (!flush_queue_.empty()) {
|
||||
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
||||
@@ -698,10 +706,15 @@ void DBImpl::PersistStats() {
|
||||
if (!statistics->getTickerMap(&stats_map)) {
|
||||
return;
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"------- PERSISTING STATS -------");
|
||||
|
||||
if (immutable_db_options_.persist_stats_to_disk) {
|
||||
WriteBatch batch;
|
||||
if (stats_slice_initialized_) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Reading %" ROCKSDB_PRIszt " stats from statistics\n",
|
||||
stats_slice_.size());
|
||||
for (const auto& stat : stats_map) {
|
||||
char key[100];
|
||||
int length =
|
||||
@@ -723,8 +736,13 @@ void DBImpl::PersistStats() {
|
||||
Status s = Write(wo, &batch);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Writing to persistent stats CF failed -- %s\n",
|
||||
"Writing to persistent stats CF failed -- %s",
|
||||
s.ToString().c_str());
|
||||
} else {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Writing %" ROCKSDB_PRIszt " stats with timestamp %" PRIu64
|
||||
" to persistent stats CF succeeded",
|
||||
stats_slice_.size(), now_seconds);
|
||||
}
|
||||
// TODO(Zhongyi): add purging for persisted data
|
||||
} else {
|
||||
@@ -737,6 +755,10 @@ void DBImpl::PersistStats() {
|
||||
stats_delta[stat.first] = stat.second - stats_slice_[stat.first];
|
||||
}
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Storing %" ROCKSDB_PRIszt " stats with timestamp %" PRIu64
|
||||
" to in-memory stats history",
|
||||
stats_slice_.size(), now_seconds);
|
||||
stats_history_[now_seconds] = stats_delta;
|
||||
}
|
||||
stats_slice_initialized_ = true;
|
||||
@@ -744,15 +766,22 @@ void DBImpl::PersistStats() {
|
||||
TEST_SYNC_POINT("DBImpl::PersistStats:StatsCopied");
|
||||
|
||||
// delete older stats snapshots to control memory consumption
|
||||
bool purge_needed =
|
||||
EstimateInMemoryStatsHistorySize() > stats_history_size_limit;
|
||||
size_t stats_history_size = EstimateInMemoryStatsHistorySize();
|
||||
bool purge_needed = stats_history_size > stats_history_size_limit;
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[Pre-GC] In-memory stats history size: %" ROCKSDB_PRIszt
|
||||
" bytes, slice count: %" ROCKSDB_PRIszt,
|
||||
stats_history_size, stats_history_.size());
|
||||
while (purge_needed && !stats_history_.empty()) {
|
||||
stats_history_.erase(stats_history_.begin());
|
||||
purge_needed =
|
||||
EstimateInMemoryStatsHistorySize() > stats_history_size_limit;
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[Post-GC] In-memory stats history size: %" ROCKSDB_PRIszt
|
||||
" bytes, slice count: %" ROCKSDB_PRIszt,
|
||||
stats_history_size, stats_history_.size());
|
||||
}
|
||||
// TODO: persist stats to disk
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -842,6 +871,24 @@ void DBImpl::DumpStats() {
|
||||
PrintStatistics();
|
||||
}
|
||||
|
||||
Status DBImpl::TablesRangeTombstoneSummary(ColumnFamilyHandle* column_family,
|
||||
int max_entries_to_print,
|
||||
std::string* out_str) {
|
||||
auto* cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl, ColumnFamilyHandle>(
|
||||
column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
Version* version = super_version->current;
|
||||
|
||||
Status s =
|
||||
version->TablesRangeTombstoneSummary(max_entries_to_print, out_str);
|
||||
|
||||
CleanupSuperVersion(super_version);
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBImpl::ScheduleBgLogWriterClose(JobContext* job_context) {
|
||||
if (!job_context->logs_to_free.empty()) {
|
||||
for (auto l : job_context->logs_to_free) {
|
||||
@@ -861,16 +908,6 @@ Directory* DBImpl::GetDataDir(ColumnFamilyData* cfd, size_t path_id) const {
|
||||
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) {
|
||||
// Should use db_dir_
|
||||
return db_dir_.get();
|
||||
}
|
||||
return ret_dir;
|
||||
}
|
||||
|
||||
Status DBImpl::SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
@@ -1277,32 +1314,28 @@ void DBImpl::SchedulePurge() {
|
||||
void DBImpl::BackgroundCallPurge() {
|
||||
mutex_.Lock();
|
||||
|
||||
// We use one single loop to clear both queues so that after existing the loop
|
||||
// both queues are empty. This is stricter than what is needed, but can make
|
||||
// it easier for us to reason the correctness.
|
||||
while (!purge_queue_.empty() || !logs_to_free_queue_.empty()) {
|
||||
// Check logs_to_free_queue_ first and close log writers.
|
||||
if (!logs_to_free_queue_.empty()) {
|
||||
assert(!logs_to_free_queue_.empty());
|
||||
log::Writer* log_writer = *(logs_to_free_queue_.begin());
|
||||
logs_to_free_queue_.pop_front();
|
||||
mutex_.Unlock();
|
||||
delete log_writer;
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
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 job_id = purge_file->job_id;
|
||||
purge_queue_.pop_front();
|
||||
|
||||
mutex_.Unlock();
|
||||
DeleteObsoleteFileImpl(job_id, fname, dir_to_sync, type, number);
|
||||
mutex_.Lock();
|
||||
}
|
||||
while (!logs_to_free_queue_.empty()) {
|
||||
assert(!logs_to_free_queue_.empty());
|
||||
log::Writer* log_writer = *(logs_to_free_queue_.begin());
|
||||
logs_to_free_queue_.pop_front();
|
||||
mutex_.Unlock();
|
||||
delete log_writer;
|
||||
mutex_.Lock();
|
||||
}
|
||||
for (const auto& file : purge_files_) {
|
||||
const PurgeFileInfo& purge_file = file.second;
|
||||
const std::string& fname = purge_file.fname;
|
||||
const std::string& dir_to_sync = purge_file.dir_to_sync;
|
||||
FileType type = purge_file.type;
|
||||
uint64_t number = purge_file.number;
|
||||
int job_id = purge_file.job_id;
|
||||
|
||||
mutex_.Unlock();
|
||||
DeleteObsoleteFileImpl(job_id, fname, dir_to_sync, type, number);
|
||||
mutex_.Lock();
|
||||
}
|
||||
purge_files_.clear();
|
||||
|
||||
bg_purge_scheduled_--;
|
||||
|
||||
bg_cv_.SignalAll();
|
||||
@@ -1429,28 +1462,22 @@ ColumnFamilyHandle* DBImpl::PersistentStatsColumnFamily() const {
|
||||
Status DBImpl::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) {
|
||||
if (nullptr == read_options.timestamp) {
|
||||
return GetImpl(read_options, column_family, key, value);
|
||||
}
|
||||
Slice akey;
|
||||
std::string buf;
|
||||
Status s = AppendTimestamp(key, *(read_options.timestamp), &akey, &buf);
|
||||
if (s.ok()) {
|
||||
s = GetImpl(read_options, column_family, akey, value);
|
||||
}
|
||||
return s;
|
||||
GetImplOptions get_impl_options;
|
||||
get_impl_options.column_family = column_family;
|
||||
get_impl_options.value = value;
|
||||
return GetImpl(read_options, key, get_impl_options);
|
||||
}
|
||||
|
||||
Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* pinnable_val, bool* value_found,
|
||||
ReadCallback* callback, bool* is_blob_index) {
|
||||
assert(pinnable_val != nullptr);
|
||||
Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
GetImplOptions get_impl_options) {
|
||||
assert(get_impl_options.value != nullptr ||
|
||||
get_impl_options.merge_operands != nullptr);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfh =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(get_impl_options.column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
if (tracer_) {
|
||||
@@ -1458,7 +1485,7 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
// tracing is enabled.
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
tracer_->Get(column_family, key);
|
||||
tracer_->Get(get_impl_options.column_family, key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1470,9 +1497,9 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
|
||||
SequenceNumber snapshot;
|
||||
if (read_options.snapshot != nullptr) {
|
||||
if (callback) {
|
||||
if (get_impl_options.callback) {
|
||||
// Already calculated based on read_options.snapshot
|
||||
snapshot = callback->max_visible_seq();
|
||||
snapshot = get_impl_options.callback->max_visible_seq();
|
||||
} else {
|
||||
snapshot =
|
||||
reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)->number_;
|
||||
@@ -1486,8 +1513,23 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
snapshot = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
if (callback) {
|
||||
callback->Refresh(snapshot);
|
||||
if (get_impl_options.callback) {
|
||||
// The unprep_seqs are not published for write unprepared, so it could be
|
||||
// that max_visible_seq is larger. Seek to the std::max of the two.
|
||||
// However, we still want our callback to contain the actual snapshot so
|
||||
// that it can do the correct visibility filtering.
|
||||
get_impl_options.callback->Refresh(snapshot);
|
||||
|
||||
// Internally, WriteUnpreparedTxnReadCallback::Refresh would set
|
||||
// max_visible_seq = max(max_visible_seq, snapshot)
|
||||
//
|
||||
// Currently, the commented out assert is broken by
|
||||
// InvalidSnapshotReadCallback, but if write unprepared recovery followed
|
||||
// the regular transaction flow, then this special read callback would not
|
||||
// be needed.
|
||||
//
|
||||
// assert(callback->max_visible_seq() >= snapshot);
|
||||
snapshot = get_impl_options.callback->max_visible_seq();
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:3");
|
||||
@@ -1501,26 +1543,46 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
// 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.
|
||||
LookupKey lkey(key, snapshot);
|
||||
LookupKey lkey(key, snapshot, read_options.timestamp);
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
|
||||
bool skip_memtable = (read_options.read_tier == kPersistedTier &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed));
|
||||
bool done = false;
|
||||
if (!skip_memtable) {
|
||||
if (sv->mem->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, callback,
|
||||
is_blob_index)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
sv->imm->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, callback,
|
||||
is_blob_index)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
// Get value associated with key
|
||||
if (get_impl_options.get_value) {
|
||||
if (sv->mem->Get(lkey, get_impl_options.value->GetSelf(), &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
read_options, get_impl_options.callback,
|
||||
get_impl_options.is_blob_index)) {
|
||||
done = true;
|
||||
get_impl_options.value->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
sv->imm->Get(lkey, get_impl_options.value->GetSelf(), &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
read_options, get_impl_options.callback,
|
||||
get_impl_options.is_blob_index)) {
|
||||
done = true;
|
||||
get_impl_options.value->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
} else {
|
||||
// Get Merge Operands associated with key, Merge Operands should not be
|
||||
// merged and raw values should be returned to the user.
|
||||
if (sv->mem->Get(lkey, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options, nullptr,
|
||||
nullptr, false)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
sv->imm->GetMergeOperands(lkey, &s, &merge_context,
|
||||
&max_covering_tombstone_seq,
|
||||
read_options)) {
|
||||
done = true;
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
}
|
||||
if (!done && !s.ok() && !s.IsMergeInProgress()) {
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
@@ -1529,9 +1591,14 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
}
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
sv->current->Get(read_options, lkey, pinnable_val, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, value_found, nullptr, nullptr,
|
||||
callback, is_blob_index);
|
||||
sv->current->Get(
|
||||
read_options, lkey, get_impl_options.value, &s, &merge_context,
|
||||
&max_covering_tombstone_seq,
|
||||
get_impl_options.get_value ? get_impl_options.value_found : nullptr,
|
||||
nullptr, nullptr,
|
||||
get_impl_options.get_value ? get_impl_options.callback : nullptr,
|
||||
get_impl_options.get_value ? get_impl_options.is_blob_index : nullptr,
|
||||
get_impl_options.get_value);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
|
||||
@@ -1543,7 +1610,25 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
size_t size = 0;
|
||||
if (s.ok()) {
|
||||
size = pinnable_val->size();
|
||||
if (get_impl_options.get_value) {
|
||||
size = get_impl_options.value->size();
|
||||
} else {
|
||||
// Return all merge operands for get_impl_options.key
|
||||
*get_impl_options.number_of_operands =
|
||||
static_cast<int>(merge_context.GetNumOperands());
|
||||
if (*get_impl_options.number_of_operands >
|
||||
get_impl_options.get_merge_operands_options
|
||||
->expected_max_number_of_operands) {
|
||||
s = Status::Incomplete(
|
||||
Status::SubCode::KMergeOperandsInsufficientCapacity);
|
||||
} else {
|
||||
for (const Slice& sl : merge_context.GetOperands()) {
|
||||
size += sl.size();
|
||||
get_impl_options.merge_operands->PinSelf(sl);
|
||||
get_impl_options.merge_operands++;
|
||||
}
|
||||
}
|
||||
}
|
||||
RecordTick(stats_, BYTES_READ, size);
|
||||
PERF_COUNTER_ADD(get_read_bytes, size);
|
||||
}
|
||||
@@ -1839,6 +1924,24 @@ void DBImpl::MultiGetImpl(
|
||||
snapshot = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
if (callback) {
|
||||
// The unprep_seqs are not published for write unprepared, so it could be
|
||||
// that max_visible_seq is larger. Seek to the std::max of the two.
|
||||
// However, we still want our callback to contain the actual snapshot so
|
||||
// that it can do the correct visibility filtering.
|
||||
callback->Refresh(snapshot);
|
||||
|
||||
// Internally, WriteUnpreparedTxnReadCallback::Refresh would set
|
||||
// max_visible_seq = max(max_visible_seq, snapshot)
|
||||
//
|
||||
// Currently, the commented out assert is broken by
|
||||
// InvalidSnapshotReadCallback, but if write unprepared recovery followed
|
||||
// the regular transaction flow, then this special read callback would not
|
||||
// be needed.
|
||||
//
|
||||
// assert(callback->max_visible_seq() >= snapshot);
|
||||
snapshot = callback->max_visible_seq();
|
||||
}
|
||||
}
|
||||
|
||||
// For each of the given keys, apply the entire "get" process as follows:
|
||||
@@ -1858,41 +1961,26 @@ void DBImpl::MultiGetImpl(
|
||||
keys_left -= batch_size;
|
||||
for (auto mget_iter = range.begin(); mget_iter != range.end();
|
||||
++mget_iter) {
|
||||
MergeContext& merge_context = mget_iter->merge_context;
|
||||
merge_context.Clear();
|
||||
Status& s = *mget_iter->s;
|
||||
PinnableSlice* value = mget_iter->value;
|
||||
s = Status::OK();
|
||||
|
||||
bool skip_memtable =
|
||||
(read_options.read_tier == kPersistedTier &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed));
|
||||
bool done = false;
|
||||
if (!skip_memtable) {
|
||||
if (super_version->mem->Get(*(mget_iter->lkey), value->GetSelf(), &s,
|
||||
&merge_context,
|
||||
&mget_iter->max_covering_tombstone_seq,
|
||||
read_options, callback, is_blob_index)) {
|
||||
done = true;
|
||||
value->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if (super_version->imm->Get(
|
||||
*(mget_iter->lkey), value->GetSelf(), &s, &merge_context,
|
||||
&mget_iter->max_covering_tombstone_seq, read_options,
|
||||
callback, is_blob_index)) {
|
||||
done = true;
|
||||
value->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
range.MarkKeyDone(mget_iter);
|
||||
} else {
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
lookup_current = true;
|
||||
}
|
||||
mget_iter->merge_context.Clear();
|
||||
*mget_iter->s = Status::OK();
|
||||
}
|
||||
|
||||
bool skip_memtable =
|
||||
(read_options.read_tier == kPersistedTier &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed));
|
||||
if (!skip_memtable) {
|
||||
super_version->mem->MultiGet(read_options, &range, callback,
|
||||
is_blob_index);
|
||||
if (!range.empty()) {
|
||||
super_version->imm->MultiGet(read_options, &range, callback,
|
||||
is_blob_index);
|
||||
}
|
||||
if (!range.empty()) {
|
||||
lookup_current = true;
|
||||
uint64_t left = range.KeysLeft();
|
||||
RecordTick(stats_, MEMTABLE_MISS, left);
|
||||
}
|
||||
}
|
||||
if (lookup_current) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->MultiGet(read_options, &range, callback,
|
||||
@@ -2186,7 +2274,11 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
|
||||
ReadOptions roptions = read_options;
|
||||
roptions.read_tier = kBlockCacheTier; // read from block cache only
|
||||
PinnableSlice pinnable_val;
|
||||
auto s = GetImpl(roptions, column_family, key, &pinnable_val, value_found);
|
||||
GetImplOptions get_impl_options;
|
||||
get_impl_options.column_family = column_family;
|
||||
get_impl_options.value = &pinnable_val;
|
||||
get_impl_options.value_found = value_found;
|
||||
auto s = GetImpl(roptions, key, get_impl_options);
|
||||
value->assign(pinnable_val.data(), pinnable_val.size());
|
||||
|
||||
// If block_cache is enabled and the index block of the table didn't
|
||||
@@ -2752,11 +2844,13 @@ void DBImpl::GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
|
||||
void DBImpl::GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
uint8_t include_flags) {
|
||||
assert(include_flags & DB::SizeApproximationFlags::INCLUDE_FILES ||
|
||||
include_flags & DB::SizeApproximationFlags::INCLUDE_MEMTABLES);
|
||||
Status DBImpl::GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes) {
|
||||
if (!options.include_memtabtles && !options.include_files) {
|
||||
return Status::InvalidArgument("Invalid options");
|
||||
}
|
||||
|
||||
Version* v;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
@@ -2768,18 +2862,19 @@ void DBImpl::GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
sizes[i] = 0;
|
||||
if (include_flags & DB::SizeApproximationFlags::INCLUDE_FILES) {
|
||||
if (options.include_files) {
|
||||
sizes[i] += versions_->ApproximateSize(
|
||||
v, k1.Encode(), k2.Encode(), /*start_level=*/0, /*end_level=*/-1,
|
||||
/*for_compaction=*/false);
|
||||
options, v, k1.Encode(), k2.Encode(), /*start_level=*/0,
|
||||
/*end_level=*/-1, TableReaderCaller::kUserApproximateSize);
|
||||
}
|
||||
if (include_flags & DB::SizeApproximationFlags::INCLUDE_MEMTABLES) {
|
||||
if (options.include_memtabtles) {
|
||||
sizes[i] += sv->mem->ApproximateStats(k1.Encode(), k2.Encode()).size;
|
||||
sizes[i] += sv->imm->ApproximateStats(k1.Encode(), k2.Encode()).size;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::list<uint64_t>::iterator
|
||||
@@ -2794,8 +2889,11 @@ DBImpl::CaptureCurrentFileNumberInPendingOutputs() {
|
||||
}
|
||||
|
||||
void DBImpl::ReleaseFileNumberFromPendingOutputs(
|
||||
std::list<uint64_t>::iterator v) {
|
||||
pending_outputs_.erase(v);
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v) {
|
||||
if (v.get() != nullptr) {
|
||||
pending_outputs_.erase(*v.get());
|
||||
v.reset();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -3049,6 +3147,11 @@ Status DBImpl::CheckConsistency() {
|
||||
}
|
||||
|
||||
Status DBImpl::GetDbIdentity(std::string& identity) const {
|
||||
identity.assign(db_id_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::GetDbIdentityFromIdentityFile(std::string* identity) const {
|
||||
std::string idfilename = IdentityFileName(dbname_);
|
||||
const EnvOptions soptions;
|
||||
std::unique_ptr<SequentialFileReader> id_file_reader;
|
||||
@@ -3075,10 +3178,10 @@ Status DBImpl::GetDbIdentity(std::string& identity) const {
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
identity.assign(id.ToString());
|
||||
identity->assign(id.ToString());
|
||||
// If last character is '\n' remove it from identity
|
||||
if (identity.size() > 0 && identity.back() == '\n') {
|
||||
identity.pop_back();
|
||||
if (identity->size() > 0 && identity->back() == '\n') {
|
||||
identity->pop_back();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -3148,6 +3251,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
ImmutableDBOptions soptions(SanitizeOptions(dbname, options));
|
||||
Env* env = soptions.env;
|
||||
std::vector<std::string> filenames;
|
||||
bool wal_in_db_path = IsWalDirSameAsDBPath(&soptions);
|
||||
|
||||
// Reset the logger because it holds a handle to the
|
||||
// log file and prevents cleanup and directory removal
|
||||
@@ -3170,7 +3274,8 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
if (type == kMetaDatabase) {
|
||||
del = DestroyDB(path_to_delete, options);
|
||||
} else if (type == kTableFile || type == kLogFile) {
|
||||
del = DeleteDBFile(&soptions, path_to_delete, dbname);
|
||||
del = DeleteDBFile(&soptions, path_to_delete, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
|
||||
} else {
|
||||
del = env->DeleteFile(path_to_delete);
|
||||
}
|
||||
@@ -3204,7 +3309,8 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
if (ParseFileName(fname, &number, &type) &&
|
||||
type == kTableFile) { // Lock file will be deleted at end
|
||||
std::string table_path = path + "/" + fname;
|
||||
Status del = DeleteDBFile(&soptions, table_path, dbname);
|
||||
Status del = DeleteDBFile(&soptions, table_path, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/false);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
@@ -3231,7 +3337,8 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
for (const auto& file : archiveFiles) {
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
Status del =
|
||||
DeleteDBFile(&soptions, archivedir + "/" + file, archivedir);
|
||||
DeleteDBFile(&soptions, archivedir + "/" + file, archivedir,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
@@ -3246,7 +3353,8 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
Status del =
|
||||
DeleteDBFile(&soptions, LogFileName(soptions.wal_dir, number),
|
||||
soptions.wal_dir);
|
||||
soptions.wal_dir, /*force_bg=*/false,
|
||||
/*force_fg=*/!wal_in_db_path);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
@@ -3624,7 +3732,7 @@ Status DBImpl::IngestExternalFiles(
|
||||
|
||||
// TODO (yanqin) maybe handle the case in which column_families have
|
||||
// duplicates
|
||||
std::list<uint64_t>::iterator pending_output_elem;
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> pending_output_elem;
|
||||
size_t total = 0;
|
||||
for (const auto& arg : args) {
|
||||
total += arg.external_files.size();
|
||||
@@ -3632,7 +3740,7 @@ Status DBImpl::IngestExternalFiles(
|
||||
uint64_t next_file_number = 0;
|
||||
Status status = ReserveFileNumbersBeforeIngestion(
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[0].column_family)->cfd(), total,
|
||||
&pending_output_elem, &next_file_number);
|
||||
pending_output_elem, &next_file_number);
|
||||
if (!status.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
ReleaseFileNumberFromPendingOutputs(pending_output_elem);
|
||||
@@ -3642,18 +3750,18 @@ Status DBImpl::IngestExternalFiles(
|
||||
std::vector<ExternalSstFileIngestionJob> ingestion_jobs;
|
||||
for (const auto& arg : args) {
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(arg.column_family)->cfd();
|
||||
ingestion_jobs.emplace_back(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, env_options_,
|
||||
&snapshots_, arg.options);
|
||||
ingestion_jobs.emplace_back(
|
||||
env_, versions_.get(), cfd, immutable_db_options_, env_options_,
|
||||
&snapshots_, arg.options, &directories_, &event_logger_);
|
||||
}
|
||||
std::vector<std::pair<bool, Status>> exec_results;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
exec_results.emplace_back(false, Status::OK());
|
||||
}
|
||||
// TODO(yanqin) maybe make jobs run in parallel
|
||||
uint64_t start_file_number = next_file_number;
|
||||
for (size_t i = 1; i != num_cfs; ++i) {
|
||||
uint64_t start_file_number =
|
||||
next_file_number + args[i - 1].external_files.size();
|
||||
start_file_number += args[i - 1].external_files.size();
|
||||
auto* cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
@@ -3774,19 +3882,21 @@ Status DBImpl::IngestExternalFiles(
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
bool should_increment_last_seqno =
|
||||
ingestion_jobs[0].ShouldIncrementLastSequence();
|
||||
int consumed_seqno_count =
|
||||
ingestion_jobs[0].ConsumedSequenceNumbersCount();
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i != num_cfs; ++i) {
|
||||
assert(should_increment_last_seqno ==
|
||||
ingestion_jobs[i].ShouldIncrementLastSequence());
|
||||
assert(!!consumed_seqno_count ==
|
||||
!!ingestion_jobs[i].ConsumedSequenceNumbersCount());
|
||||
consumed_seqno_count +=
|
||||
ingestion_jobs[i].ConsumedSequenceNumbersCount();
|
||||
}
|
||||
#endif
|
||||
if (should_increment_last_seqno) {
|
||||
if (consumed_seqno_count > 0) {
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
versions_->SetLastAllocatedSequence(last_seqno + 1);
|
||||
versions_->SetLastPublishedSequence(last_seqno + 1);
|
||||
versions_->SetLastSequence(last_seqno + 1);
|
||||
versions_->SetLastAllocatedSequence(last_seqno + consumed_seqno_count);
|
||||
versions_->SetLastPublishedSequence(last_seqno + consumed_seqno_count);
|
||||
versions_->SetLastSequence(last_seqno + consumed_seqno_count);
|
||||
}
|
||||
autovector<ColumnFamilyData*> cfds_to_commit;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
@@ -3877,7 +3987,127 @@ Status DBImpl::IngestExternalFiles(
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifyChecksum() {
|
||||
Status DBImpl::CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const ExportImportFilesMetaData& metadata, ColumnFamilyHandle** handle) {
|
||||
assert(handle != nullptr);
|
||||
assert(*handle == nullptr);
|
||||
std::string cf_comparator_name = options.comparator->Name();
|
||||
if (cf_comparator_name != metadata.db_comparator_name) {
|
||||
return Status::InvalidArgument("Comparator name mismatch");
|
||||
}
|
||||
|
||||
// Create column family.
|
||||
auto status = CreateColumnFamily(options, column_family_name, handle);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Import sst files from metadata.
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(*handle);
|
||||
auto cfd = cfh->cfd();
|
||||
ImportColumnFamilyJob import_job(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, env_options_,
|
||||
import_options, metadata.files);
|
||||
|
||||
SuperVersionContext dummy_sv_ctx(/* create_superversion */ true);
|
||||
VersionEdit dummy_edit;
|
||||
uint64_t next_file_number = 0;
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> pending_output_elem;
|
||||
{
|
||||
// Lock db mutex
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
// Don't import files when there is a bg_error
|
||||
status = error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
// Make sure that bg cleanup wont delete the files that we are importing
|
||||
pending_output_elem.reset(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
|
||||
if (status.ok()) {
|
||||
// If crash happen after a hard link established, Recover function may
|
||||
// reuse the file number that has already assigned to the internal file,
|
||||
// and this will overwrite the external file. To protect the external
|
||||
// file, we have to make sure the file number will never being reused.
|
||||
next_file_number = versions_->FetchAddFileNumber(metadata.files.size());
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status = versions_->LogAndApply(cfd, *cf_options, &dummy_edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx, *cf_options);
|
||||
}
|
||||
}
|
||||
}
|
||||
dummy_sv_ctx.Clean();
|
||||
|
||||
if (status.ok()) {
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
status = import_job.Prepare(next_file_number, sv);
|
||||
CleanupSuperVersion(sv);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
SuperVersionContext sv_context(true /*create_superversion*/);
|
||||
{
|
||||
// Lock db mutex
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
// Stop writes to the DB by entering both write threads
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
|
||||
num_running_ingest_file_++;
|
||||
assert(!cfd->IsDropped());
|
||||
status = import_job.Run();
|
||||
|
||||
// Install job edit [Mutex will be unlocked here]
|
||||
if (status.ok()) {
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status = versions_->LogAndApply(cfd, *cf_options, import_job.edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, *cf_options);
|
||||
}
|
||||
}
|
||||
|
||||
// Resume writes to the DB
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
|
||||
num_running_ingest_file_--;
|
||||
if (num_running_ingest_file_ == 0) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
}
|
||||
// mutex_ is unlocked here
|
||||
|
||||
sv_context.Clean();
|
||||
}
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
ReleaseFileNumberFromPendingOutputs(pending_output_elem);
|
||||
}
|
||||
|
||||
import_job.Cleanup(status);
|
||||
if (!status.ok()) {
|
||||
DropColumnFamily(*handle);
|
||||
DestroyColumnFamilyHandle(*handle);
|
||||
*handle = nullptr;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifyChecksum(const ReadOptions& read_options) {
|
||||
Status s;
|
||||
std::vector<ColumnFamilyData*> cfd_list;
|
||||
{
|
||||
@@ -3908,7 +4138,8 @@ Status DBImpl::VerifyChecksum() {
|
||||
const auto& fd = vstorage->LevelFilesBrief(i).files[j].fd;
|
||||
std::string fname = TableFileName(cfd->ioptions()->cf_paths,
|
||||
fd.GetNumber(), fd.GetPathId());
|
||||
s = rocksdb::VerifySstFileChecksum(opts, env_options_, fname);
|
||||
s = rocksdb::VerifySstFileChecksum(opts, env_options_, read_options,
|
||||
fname);
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
@@ -4012,18 +4243,18 @@ Status DBImpl::TraceIteratorSeekForPrev(const uint32_t& cf_id,
|
||||
|
||||
Status DBImpl::ReserveFileNumbersBeforeIngestion(
|
||||
ColumnFamilyData* cfd, uint64_t num,
|
||||
std::list<uint64_t>::iterator* pending_output_elem,
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& pending_output_elem,
|
||||
uint64_t* next_file_number) {
|
||||
Status s;
|
||||
SuperVersionContext dummy_sv_ctx(true /* create_superversion */);
|
||||
assert(nullptr != pending_output_elem);
|
||||
assert(nullptr != next_file_number);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
// Do not ingest files when there is a bg_error
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
*pending_output_elem = CaptureCurrentFileNumberInPendingOutputs();
|
||||
pending_output_elem.reset(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
*next_file_number = versions_->FetchAddFileNumber(static_cast<uint64_t>(num));
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
VersionEdit dummy_edit;
|
||||
|
||||
+136
-47
@@ -27,6 +27,7 @@
|
||||
#include "db/external_sst_file_ingestion_job.h"
|
||||
#include "db/flush_job.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/import_column_family_job.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/logs_with_prep_tracker.h"
|
||||
@@ -36,6 +37,7 @@
|
||||
#include "db/read_callback.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
#include "db/trim_history_scheduler.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/wal_manager.h"
|
||||
#include "db/write_controller.h"
|
||||
@@ -77,6 +79,38 @@ struct JobContext;
|
||||
struct ExternalSstFileInfo;
|
||||
struct MemTableInfo;
|
||||
|
||||
// Class to maintain directories for all database paths other than main one.
|
||||
class Directories {
|
||||
public:
|
||||
Status SetDirectories(Env* env, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths);
|
||||
|
||||
Directory* GetDataDir(size_t path_id) const {
|
||||
assert(path_id < data_dirs_.size());
|
||||
Directory* ret_dir = data_dirs_[path_id].get();
|
||||
if (ret_dir == nullptr) {
|
||||
// Should use db_dir_
|
||||
return db_dir_.get();
|
||||
}
|
||||
return ret_dir;
|
||||
}
|
||||
|
||||
Directory* GetWalDir() {
|
||||
if (wal_dir_) {
|
||||
return wal_dir_.get();
|
||||
}
|
||||
return db_dir_.get();
|
||||
}
|
||||
|
||||
Directory* GetDbDir() { return db_dir_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Directory> db_dir_;
|
||||
std::vector<std::unique_ptr<Directory>> data_dirs_;
|
||||
std::unique_ptr<Directory> wal_dir_;
|
||||
};
|
||||
|
||||
// While DB is the public interface of RocksDB, and DBImpl is the actual
|
||||
// class implementing it. It's the entrance of the core RocksdB engine.
|
||||
// All other DB implementations, e.g. TransactionDB, BlobDB, etc, wrap a
|
||||
@@ -94,6 +128,10 @@ class DBImpl : public DB {
|
||||
public:
|
||||
DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
const bool seq_per_batch = false, const bool batch_per_txn = true);
|
||||
// No copying allowed
|
||||
DBImpl(const DBImpl&) = delete;
|
||||
void operator=(const DBImpl&) = delete;
|
||||
|
||||
virtual ~DBImpl();
|
||||
|
||||
// ---- Implementations of the DB interface ----
|
||||
@@ -126,6 +164,21 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) override;
|
||||
|
||||
using DB::GetMergeOperands;
|
||||
Status GetMergeOperands(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* merge_operands,
|
||||
GetMergeOperandsOptions* get_merge_operands_options,
|
||||
int* number_of_operands) override {
|
||||
GetImplOptions get_impl_options;
|
||||
get_impl_options.column_family = column_family;
|
||||
get_impl_options.merge_operands = merge_operands;
|
||||
get_impl_options.get_merge_operands_options = get_merge_operands_options;
|
||||
get_impl_options.number_of_operands = number_of_operands;
|
||||
get_impl_options.get_value = false;
|
||||
return GetImpl(options, key, get_impl_options);
|
||||
}
|
||||
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options,
|
||||
@@ -200,9 +253,10 @@ class DBImpl : public DB {
|
||||
virtual bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* aggregated_value) override;
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(
|
||||
ColumnFamilyHandle* column_family, const Range* range, int n,
|
||||
uint64_t* sizes, uint8_t include_flags = INCLUDE_FILES) override;
|
||||
virtual Status GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes) override;
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
@@ -228,6 +282,9 @@ class DBImpl : public DB {
|
||||
virtual Status EnableAutoCompaction(
|
||||
const std::vector<ColumnFamilyHandle*>& column_family_handles) override;
|
||||
|
||||
virtual void EnableManualCompaction() override;
|
||||
virtual void DisableManualCompaction() override;
|
||||
|
||||
using DB::SetOptions;
|
||||
Status SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -267,6 +324,8 @@ class DBImpl : public DB {
|
||||
|
||||
virtual Status GetDbIdentity(std::string& identity) const override;
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override;
|
||||
|
||||
ColumnFamilyHandle* PersistentStatsColumnFamily() const;
|
||||
@@ -288,6 +347,8 @@ class DBImpl : public DB {
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true) override;
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& files) override;
|
||||
virtual Status GetCurrentWalFile(
|
||||
std::unique_ptr<LogFile>* current_log_file) override;
|
||||
|
||||
virtual Status GetUpdatesSince(
|
||||
SequenceNumber seq_number, std::unique_ptr<TransactionLogIterator>* iter,
|
||||
@@ -324,7 +385,15 @@ class DBImpl : public DB {
|
||||
virtual Status IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& args) override;
|
||||
|
||||
virtual Status VerifyChecksum() override;
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const ExportImportFilesMetaData& metadata,
|
||||
ColumnFamilyHandle** handle) override;
|
||||
|
||||
using DB::VerifyChecksum;
|
||||
virtual Status VerifyChecksum(const ReadOptions& /*read_options*/) override;
|
||||
|
||||
using DB::StartTrace;
|
||||
virtual Status StartTrace(
|
||||
@@ -354,12 +423,32 @@ class DBImpl : public DB {
|
||||
|
||||
// ---- End of implementations of the DB interface ----
|
||||
|
||||
struct GetImplOptions {
|
||||
ColumnFamilyHandle* column_family = nullptr;
|
||||
PinnableSlice* value = nullptr;
|
||||
bool* value_found = nullptr;
|
||||
ReadCallback* callback = nullptr;
|
||||
bool* is_blob_index = nullptr;
|
||||
// If true return value associated with key via value pointer else return
|
||||
// all merge operands for key via merge_operands pointer
|
||||
bool get_value = true;
|
||||
// Pointer to an array of size
|
||||
// get_merge_operands_options.expected_max_number_of_operands allocated by
|
||||
// user
|
||||
PinnableSlice* merge_operands = nullptr;
|
||||
GetMergeOperandsOptions* get_merge_operands_options = nullptr;
|
||||
int* number_of_operands = nullptr;
|
||||
};
|
||||
|
||||
// Function that Get and KeyMayExist call with no_io true or false
|
||||
// Note: 'value_found' from KeyMayExist propagates here
|
||||
Status GetImpl(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
bool* value_found = nullptr, ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr);
|
||||
// This function is also called by GetMergeOperands
|
||||
// If get_impl_options.get_value = true get value associated with
|
||||
// get_impl_options.key via get_impl_options.value
|
||||
// If get_impl_options.get_value = false get merge operands associated with
|
||||
// get_impl_options.key via get_impl_options.merge_operands
|
||||
Status GetImpl(const ReadOptions& options, const Slice& key,
|
||||
GetImplOptions get_impl_options);
|
||||
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& options,
|
||||
ColumnFamilyData* cfd,
|
||||
@@ -736,6 +825,13 @@ class DBImpl : public DB {
|
||||
uint64_t* new_time,
|
||||
std::map<std::string, uint64_t>* stats_map);
|
||||
|
||||
// Print information of all tombstones of all iterators to the std::string
|
||||
// This is only used by ldb. The output might be capped. Tombstones
|
||||
// printed out are not guaranteed to be in any order.
|
||||
Status TablesRangeTombstoneSummary(ColumnFamilyHandle* column_family,
|
||||
int max_entries_to_print,
|
||||
std::string* out_str);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Compact any files in the named level that overlap [*begin, *end]
|
||||
Status TEST_CompactRange(int level, const Slice* begin, const Slice* end,
|
||||
@@ -756,6 +852,16 @@ class DBImpl : public DB {
|
||||
Status TEST_FlushMemTable(bool wait = true, bool allow_write_stall = false,
|
||||
ColumnFamilyHandle* cfh = nullptr);
|
||||
|
||||
Status TEST_FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_opts);
|
||||
|
||||
// Flush (multiple) ColumnFamilyData without using ColumnFamilyHandle. This
|
||||
// is because in certain cases, we can flush column families, wait for the
|
||||
// flush to complete, but delete the column family handle before the wait
|
||||
// finishes. For example in CompactRange.
|
||||
Status TEST_AtomicFlushMemTables(const autovector<ColumnFamilyData*>& cfds,
|
||||
const FlushOptions& flush_opts);
|
||||
|
||||
// Wait for memtable compaction
|
||||
Status TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
@@ -826,12 +932,12 @@ class DBImpl : public DB {
|
||||
void TEST_WaitForPersistStatsRun(std::function<void()> callback) const;
|
||||
bool TEST_IsPersistentStatsEnabled() const;
|
||||
size_t TEST_EstimateInMemoryStatsHistorySize() const;
|
||||
|
||||
#endif // NDEBUG
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
std::string db_id_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
@@ -899,11 +1005,11 @@ class DBImpl : public DB {
|
||||
|
||||
void NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, TableProperties prop);
|
||||
int job_id);
|
||||
|
||||
void NotifyOnFlushCompleted(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, TableProperties prop);
|
||||
void NotifyOnFlushCompleted(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* flush_jobs_info);
|
||||
|
||||
void NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
const Status& st,
|
||||
@@ -1047,30 +1153,6 @@ class DBImpl : public DB {
|
||||
}
|
||||
};
|
||||
|
||||
// Class to maintain directories for all database paths other than main one.
|
||||
class Directories {
|
||||
public:
|
||||
Status SetDirectories(Env* env, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths);
|
||||
|
||||
Directory* GetDataDir(size_t path_id) const;
|
||||
|
||||
Directory* GetWalDir() {
|
||||
if (wal_dir_) {
|
||||
return wal_dir_.get();
|
||||
}
|
||||
return db_dir_.get();
|
||||
}
|
||||
|
||||
Directory* GetDbDir() { return db_dir_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Directory> db_dir_;
|
||||
std::vector<std::unique_ptr<Directory>> data_dirs_;
|
||||
std::unique_ptr<Directory> wal_dir_;
|
||||
};
|
||||
|
||||
struct LogFileNumberSize {
|
||||
explicit LogFileNumberSize(uint64_t _number) : number(_number) {}
|
||||
void AddSize(uint64_t new_size) { size += new_size; }
|
||||
@@ -1231,7 +1313,8 @@ class DBImpl : public DB {
|
||||
// created between the calls CaptureCurrentFileNumberInPendingOutputs() and
|
||||
// ReleaseFileNumberFromPendingOutputs() can now be deleted (if it's not live
|
||||
// and blocked by any other pending_outputs_ calls)
|
||||
void ReleaseFileNumberFromPendingOutputs(std::list<uint64_t>::iterator v);
|
||||
void ReleaseFileNumberFromPendingOutputs(
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v);
|
||||
|
||||
Status SyncClosedLogs(JobContext* job_context);
|
||||
|
||||
@@ -1284,6 +1367,10 @@ class DBImpl : public DB {
|
||||
|
||||
Status ScheduleFlushes(WriteContext* context);
|
||||
|
||||
void MaybeFlushStatsCF(autovector<ColumnFamilyData*>* cfds);
|
||||
|
||||
Status TrimMemtableHistory(WriteContext* context);
|
||||
|
||||
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context);
|
||||
|
||||
void SelectColumnFamiliesForAtomicFlush(autovector<ColumnFamilyData*>* cfds);
|
||||
@@ -1485,10 +1572,6 @@ class DBImpl : public DB {
|
||||
|
||||
void WaitForBackgroundWork();
|
||||
|
||||
// No copying allowed
|
||||
DBImpl(const DBImpl&);
|
||||
void operator=(const DBImpl&);
|
||||
|
||||
// Background threads call this function, which is just a wrapper around
|
||||
// the InstallSuperVersion() function. Background threads carry
|
||||
// sv_context which can have new_superversion already
|
||||
@@ -1523,7 +1606,7 @@ class DBImpl : public DB {
|
||||
// Write a version edit to the MANIFEST.
|
||||
Status ReserveFileNumbersBeforeIngestion(
|
||||
ColumnFamilyData* cfd, uint64_t num,
|
||||
std::list<uint64_t>::iterator* pending_output_elem,
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& pending_output_elem,
|
||||
uint64_t* next_file_number);
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
@@ -1560,6 +1643,7 @@ class DBImpl : public DB {
|
||||
InstrumentedMutex log_write_mutex_;
|
||||
|
||||
std::atomic<bool> shutting_down_;
|
||||
std::atomic<bool> manual_compaction_paused_;
|
||||
// This condition variable is signaled on these conditions:
|
||||
// * whenever bg_compaction_scheduled_ goes down to 0
|
||||
// * if AnyManualCompaction, whenever a compaction finishes, even if it hasn't
|
||||
@@ -1662,6 +1746,8 @@ class DBImpl : public DB {
|
||||
|
||||
FlushScheduler flush_scheduler_;
|
||||
|
||||
TrimHistoryScheduler trim_history_scheduler_;
|
||||
|
||||
SnapshotList snapshots_;
|
||||
|
||||
// For each background job, pending_outputs_ keeps the current file number at
|
||||
@@ -1700,12 +1786,12 @@ class DBImpl : public DB {
|
||||
// ColumnFamilyData::pending_compaction_ == true)
|
||||
std::deque<ColumnFamilyData*> compaction_queue_;
|
||||
|
||||
// A queue to store filenames of the files to be purged
|
||||
std::deque<PurgeFileInfo> purge_queue_;
|
||||
// A map to store file numbers and filenames of the files to be purged
|
||||
std::unordered_map<uint64_t, PurgeFileInfo> purge_files_;
|
||||
|
||||
// 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_;
|
||||
std::unordered_set<uint64_t> files_grabbed_for_purge_;
|
||||
|
||||
// A queue to store log writers to close
|
||||
std::deque<log::Writer*> logs_to_free_queue_;
|
||||
@@ -1783,7 +1869,8 @@ class DBImpl : public DB {
|
||||
|
||||
std::string db_absolute_path_;
|
||||
|
||||
// Number of running IngestExternalFile() calls.
|
||||
// Number of running IngestExternalFile() or CreateColumnFamilyWithImport()
|
||||
// calls.
|
||||
// REQUIRES: mutex held
|
||||
int num_running_ingest_file_;
|
||||
|
||||
@@ -1873,6 +1960,8 @@ class DBImpl : public DB {
|
||||
// results sequentially. Flush results of memtables with lower IDs get
|
||||
// installed to MANIFEST first.
|
||||
InstrumentedCondVar atomic_flush_install_cv_;
|
||||
|
||||
bool wal_in_db_path_;
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db, const Options& src);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/concurrent_task_limiter_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -163,8 +164,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id,
|
||||
flush_job.GetTableProperties());
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status s;
|
||||
@@ -212,8 +212,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
if (s.ok()) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushCompleted(cfd, &file_meta, mutable_cf_options,
|
||||
job_context->job_id, flush_job.GetTableProperties());
|
||||
NotifyOnFlushCompleted(cfd, mutable_cf_options,
|
||||
flush_job.GetCommittedFlushJobsInfo());
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
if (sfm) {
|
||||
@@ -232,6 +232,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:Finish");
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -302,7 +303,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
autovector<Directory*> distinct_output_dirs;
|
||||
autovector<std::string> distinct_output_dir_paths;
|
||||
std::vector<FlushJob> jobs;
|
||||
std::vector<std::unique_ptr<FlushJob>> jobs;
|
||||
std::vector<MutableCFOptions> all_mutable_cf_options;
|
||||
int num_cfs = static_cast<int>(cfds.size());
|
||||
all_mutable_cf_options.reserve(num_cfs);
|
||||
@@ -329,7 +330,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
|
||||
const uint64_t* max_memtable_id = &(bg_flush_args[i].max_memtable_id_);
|
||||
jobs.emplace_back(
|
||||
jobs.emplace_back(new FlushJob(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
max_memtable_id, env_options_for_compaction_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
@@ -337,8 +338,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
data_dir, GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
stats_, &event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri);
|
||||
jobs.back().PickMemTable();
|
||||
thread_pri));
|
||||
jobs.back()->PickMemTable();
|
||||
}
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
@@ -350,7 +351,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.at(i);
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfds[i], &file_meta[i], mutable_cf_options,
|
||||
job_context->job_id, jobs[i].GetTableProperties());
|
||||
job_context->job_id);
|
||||
}
|
||||
#endif /* !ROCKSDB_LITE */
|
||||
|
||||
@@ -372,7 +373,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// TODO (yanqin): parallelize jobs with threads.
|
||||
for (int i = 1; i != num_cfs; ++i) {
|
||||
exec_status[i].second =
|
||||
jobs[i].Run(&logs_with_prep_tracker_, &file_meta[i]);
|
||||
jobs[i]->Run(&logs_with_prep_tracker_, &file_meta[i]);
|
||||
exec_status[i].first = true;
|
||||
}
|
||||
if (num_cfs > 1) {
|
||||
@@ -381,8 +382,10 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::AtomicFlushMemTablesToOutputFiles:SomeFlushJobsComplete:2");
|
||||
}
|
||||
assert(exec_status.size() > 0);
|
||||
assert(!file_meta.empty());
|
||||
exec_status[0].second =
|
||||
jobs[0].Run(&logs_with_prep_tracker_, &file_meta[0]);
|
||||
jobs[0]->Run(&logs_with_prep_tracker_, &file_meta[0]);
|
||||
exec_status[0].first = true;
|
||||
|
||||
Status error_status;
|
||||
@@ -423,7 +426,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
auto wait_to_install_func = [&]() {
|
||||
bool ready = true;
|
||||
for (size_t i = 0; i != cfds.size(); ++i) {
|
||||
const auto& mems = jobs[i].GetMemTables();
|
||||
const auto& mems = jobs[i]->GetMemTables();
|
||||
if (cfds[i]->IsDropped()) {
|
||||
// If the column family is dropped, then do not wait.
|
||||
continue;
|
||||
@@ -464,7 +467,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<FileMetaData*> tmp_file_meta;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const auto& mems = jobs[i].GetMemTables();
|
||||
const auto& mems = jobs[i]->GetMemTables();
|
||||
if (!cfds[i]->IsDropped() && !mems.empty()) {
|
||||
tmp_cfds.emplace_back(cfds[i]);
|
||||
mems_list.emplace_back(&mems);
|
||||
@@ -500,12 +503,13 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
assert(all_mutable_cf_options.size() == static_cast<size_t>(num_cfs));
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
NotifyOnFlushCompleted(cfds[i], &file_meta[i], all_mutable_cf_options[i],
|
||||
job_context->job_id, jobs[i].GetTableProperties());
|
||||
NotifyOnFlushCompleted(cfds[i], all_mutable_cf_options[i],
|
||||
jobs[i]->GetCommittedFlushJobsInfo());
|
||||
if (sfm) {
|
||||
std::string file_path = MakeTableFileName(
|
||||
cfds[i]->ioptions()->cf_paths[0].path, file_meta[i].fd.GetNumber());
|
||||
@@ -529,12 +533,12 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// unref the versions.
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (!exec_status[i].first) {
|
||||
jobs[i].Cancel();
|
||||
jobs[i]->Cancel();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (exec_status[i].first && exec_status[i].second.ok()) {
|
||||
auto& mems = jobs[i].GetMemTables();
|
||||
auto& mems = jobs[i]->GetMemTables();
|
||||
cfds[i]->imm()->RollbackMemtableFlush(mems,
|
||||
file_meta[i].fd.GetNumber());
|
||||
}
|
||||
@@ -548,7 +552,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, TableProperties prop) {
|
||||
int job_id) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (immutable_db_options_.listeners.size() == 0U) {
|
||||
return;
|
||||
@@ -579,7 +583,6 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
info.triggered_writes_stop = triggered_writes_stop;
|
||||
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
||||
info.largest_seqno = file_meta->fd.largest_seqno;
|
||||
info.table_properties = prop;
|
||||
info.flush_reason = cfd->GetFlushReason();
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushBegin(this, info);
|
||||
@@ -593,15 +596,14 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
(void)file_meta;
|
||||
(void)mutable_cf_options;
|
||||
(void)job_id;
|
||||
(void)prop;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnFlushCompleted(ColumnFamilyData* cfd,
|
||||
FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, TableProperties prop) {
|
||||
void DBImpl::NotifyOnFlushCompleted(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* flush_jobs_info) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
assert(flush_jobs_info != nullptr);
|
||||
if (immutable_db_options_.listeners.size() == 0U) {
|
||||
return;
|
||||
}
|
||||
@@ -618,34 +620,22 @@ void DBImpl::NotifyOnFlushCompleted(ColumnFamilyData* cfd,
|
||||
// release lock while notifying events
|
||||
mutex_.Unlock();
|
||||
{
|
||||
FlushJobInfo info;
|
||||
info.cf_id = cfd->GetID();
|
||||
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(cfd->ioptions()->cf_paths[0].path,
|
||||
file_meta->fd.GetNumber());
|
||||
info.thread_id = env_->GetThreadID();
|
||||
info.job_id = job_id;
|
||||
info.triggered_writes_slowdown = triggered_writes_slowdown;
|
||||
info.triggered_writes_stop = triggered_writes_stop;
|
||||
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
||||
info.largest_seqno = file_meta->fd.largest_seqno;
|
||||
info.table_properties = prop;
|
||||
info.flush_reason = cfd->GetFlushReason();
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushCompleted(this, info);
|
||||
for (auto& info : *flush_jobs_info) {
|
||||
info->triggered_writes_slowdown = triggered_writes_slowdown;
|
||||
info->triggered_writes_stop = triggered_writes_stop;
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushCompleted(this, *info);
|
||||
}
|
||||
}
|
||||
flush_jobs_info->clear();
|
||||
}
|
||||
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;
|
||||
(void)flush_jobs_info;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -799,31 +789,6 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
return s;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class SnapshotListFetchCallbackImpl : public SnapshotListFetchCallback {
|
||||
public:
|
||||
SnapshotListFetchCallbackImpl(DBImpl* db_impl, Env* env,
|
||||
uint64_t snap_refresh_nanos, Logger* info_log)
|
||||
: SnapshotListFetchCallback(env, snap_refresh_nanos),
|
||||
db_impl_(db_impl),
|
||||
info_log_(info_log) {}
|
||||
virtual void Refresh(std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber max) override {
|
||||
size_t prev = snapshots->size();
|
||||
snapshots->clear();
|
||||
db_impl_->LoadSnapshots(snapshots, nullptr, max);
|
||||
size_t now = snapshots->size();
|
||||
ROCKS_LOG_DEBUG(info_log_,
|
||||
"Compaction snapshot count refreshed from %zu to %zu", prev,
|
||||
now);
|
||||
}
|
||||
|
||||
private:
|
||||
DBImpl* db_impl_;
|
||||
Logger* info_log_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& input_file_names,
|
||||
@@ -916,6 +881,9 @@ Status DBImpl::CompactFilesImpl(
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
if (manual_compaction_paused_.load(std::memory_order_acquire)) {
|
||||
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
|
||||
std::unordered_set<uint64_t> input_set;
|
||||
for (const auto& file_name : input_file_names) {
|
||||
@@ -990,14 +958,12 @@ Status DBImpl::CompactFilesImpl(
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
|
||||
auto pending_outputs_inserted_elem =
|
||||
CaptureCurrentFileNumberInPendingOutputs();
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> pending_outputs_inserted_elem(
|
||||
new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
CompactionJobStats compaction_job_stats;
|
||||
SnapshotListFetchCallbackImpl fetch_callback(
|
||||
this, env_, c->mutable_cf_options()->snap_refresh_nanos,
|
||||
immutable_db_options_.info_log.get());
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
env_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
@@ -1007,9 +973,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER,
|
||||
immutable_db_options_.max_subcompactions <= 1 ? &fetch_callback
|
||||
: nullptr);
|
||||
&compaction_job_stats, Env::Priority::USER, &manual_compaction_paused_);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
@@ -1055,6 +1019,12 @@ Status DBImpl::CompactFilesImpl(
|
||||
// Done
|
||||
} else if (status.IsColumnFamilyDropped() || status.IsShutdownInProgress()) {
|
||||
// Ignore compaction errors found during shutting down
|
||||
} else if (status.IsManualCompactionPaused()) {
|
||||
// Don't report stopping manual compaction as error
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] [JOB %d] Stopping manual compaction",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
job_context->job_id);
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] [JOB %d] Compaction error: %s",
|
||||
@@ -1125,6 +1095,10 @@ void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
if (c->is_manual_compaction() &&
|
||||
manual_compaction_paused_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
Version* current = cfd->current();
|
||||
current->Ref();
|
||||
// release lock while notifying events
|
||||
@@ -1187,6 +1161,10 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
if (c->is_manual_compaction() &&
|
||||
manual_compaction_paused_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
Version* current = cfd->current();
|
||||
current->Ref();
|
||||
// release lock while notifying events
|
||||
@@ -1268,7 +1246,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
edit.AddFile(to_level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction);
|
||||
f->marked_for_compaction, f->oldest_blob_file_number);
|
||||
}
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] Apply version edit:\n%s", cfd->GetName().c_str(),
|
||||
@@ -1551,13 +1529,40 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load()) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
if (immutable_db_options_.persist_stats_to_disk) {
|
||||
ColumnFamilyData* cfd_stats =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(
|
||||
kPersistentStatsColumnFamilyName);
|
||||
if (cfd_stats != nullptr && cfd_stats != cfd &&
|
||||
!cfd_stats->mem()->IsEmpty()) {
|
||||
// only force flush stats CF when it will be the only CF lagging
|
||||
// behind after the current flush
|
||||
bool stats_cf_flush_needed = true;
|
||||
for (auto* loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (loop_cfd == cfd_stats || loop_cfd == cfd) {
|
||||
continue;
|
||||
}
|
||||
if (loop_cfd->GetLogNumber() <= cfd_stats->GetLogNumber()) {
|
||||
stats_cf_flush_needed = false;
|
||||
}
|
||||
}
|
||||
if (stats_cf_flush_needed) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Force flushing stats CF with manual flush of %s "
|
||||
"to avoid holding old logs",
|
||||
cfd->GetName().c_str());
|
||||
s = SwitchMemtable(cfd_stats, &context);
|
||||
flush_memtable_id = cfd_stats->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd_stats, flush_memtable_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && !flush_req.empty()) {
|
||||
@@ -1565,6 +1570,16 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
ColumnFamilyData* loop_cfd = elem.first;
|
||||
loop_cfd->imm()->FlushRequested();
|
||||
}
|
||||
// If the caller wants to wait for this flush to complete, it indicates
|
||||
// that the caller expects the ColumnFamilyData not to be free'ed by
|
||||
// other threads which may drop the column family concurrently.
|
||||
// Therefore, we increase the cfd's ref count.
|
||||
if (flush_options.wait) {
|
||||
for (auto& elem : flush_req) {
|
||||
ColumnFamilyData* loop_cfd = elem.first;
|
||||
loop_cfd->Ref();
|
||||
}
|
||||
}
|
||||
SchedulePendingFlush(flush_req, flush_reason);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -1573,7 +1588,8 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:AfterScheduleFlush");
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
@@ -1583,6 +1599,13 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery));
|
||||
for (auto* tmp_cfd : cfds) {
|
||||
if (tmp_cfd->Unref()) {
|
||||
// Only one thread can reach here.
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
delete tmp_cfd;
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("FlushMemTableFinished");
|
||||
return s;
|
||||
@@ -1646,6 +1669,15 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
for (auto cfd : cfds) {
|
||||
cfd->imm()->FlushRequested();
|
||||
}
|
||||
// If the caller wants to wait for this flush to complete, it indicates
|
||||
// that the caller expects the ColumnFamilyData not to be free'ed by
|
||||
// other threads which may drop the column family concurrently.
|
||||
// Therefore, we increase the cfd's ref count.
|
||||
if (flush_options.wait) {
|
||||
for (auto cfd : cfds) {
|
||||
cfd->Ref();
|
||||
}
|
||||
}
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, flush_reason);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -1656,7 +1688,7 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
for (auto& iter : flush_req) {
|
||||
@@ -1664,6 +1696,13 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery));
|
||||
for (auto* cfd : cfds) {
|
||||
if (cfd->Unref()) {
|
||||
// Only one thread can reach here.
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
delete cfd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -1816,6 +1855,14 @@ Status DBImpl::EnableAutoCompaction(
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBImpl::DisableManualCompaction() {
|
||||
manual_compaction_paused_.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
void DBImpl::EnableManualCompaction() {
|
||||
manual_compaction_paused_.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
mutex_.AssertHeld();
|
||||
if (!opened_successfully_) {
|
||||
@@ -2001,7 +2048,7 @@ 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, dir_to_sync, type, number, job_id);
|
||||
purge_queue_.push_back(std::move(file_info));
|
||||
purge_files_.insert({{number, std::move(file_info)}});
|
||||
}
|
||||
|
||||
void DBImpl::BGWorkFlush(void* arg) {
|
||||
@@ -2010,7 +2057,8 @@ void DBImpl::BGWorkFlush(void* arg) {
|
||||
|
||||
IOSTATS_SET_THREAD_POOL_ID(fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush");
|
||||
reinterpret_cast<DBImpl*>(fta.db_)->BackgroundCallFlush(fta.thread_pri_);
|
||||
static_cast_with_check<DBImpl, DB>(fta.db_)->BackgroundCallFlush(
|
||||
fta.thread_pri_);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkFlush:done");
|
||||
}
|
||||
|
||||
@@ -2021,7 +2069,7 @@ void DBImpl::BGWorkCompaction(void* arg) {
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkCompaction");
|
||||
auto prepicked_compaction =
|
||||
static_cast<PrepickedCompaction*>(ca.prepicked_compaction);
|
||||
reinterpret_cast<DBImpl*>(ca.db)->BackgroundCallCompaction(
|
||||
static_cast_with_check<DBImpl, DB>(ca.db)->BackgroundCallCompaction(
|
||||
prepicked_compaction, Env::Priority::LOW);
|
||||
delete prepicked_compaction;
|
||||
}
|
||||
@@ -2125,6 +2173,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
}
|
||||
status = FlushMemTablesToOutputFiles(bg_flush_args, made_progress,
|
||||
job_context, log_buffer, thread_pri);
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundFlush:BeforeFlush");
|
||||
// All the CFDs in the FlushReq must have the same flush reason, so just
|
||||
// grab the first one
|
||||
*reason = bg_flush_args[0].cfd_->GetFlushReason();
|
||||
@@ -2157,8 +2206,9 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
assert(bg_flush_scheduled_);
|
||||
num_running_flushes_++;
|
||||
|
||||
auto pending_outputs_inserted_elem =
|
||||
CaptureCurrentFileNumberInPendingOutputs();
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>
|
||||
pending_outputs_inserted_elem(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
FlushReason reason;
|
||||
|
||||
Status s = BackgroundFlush(&made_progress, &job_context, &log_buffer,
|
||||
@@ -2239,8 +2289,9 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
|
||||
num_running_compactions_++;
|
||||
|
||||
auto pending_outputs_inserted_elem =
|
||||
CaptureCurrentFileNumberInPendingOutputs();
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>
|
||||
pending_outputs_inserted_elem(new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
|
||||
assert((bg_thread_pri == Env::Priority::BOTTOM &&
|
||||
bg_bottom_compaction_scheduled_) ||
|
||||
@@ -2254,7 +2305,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
env_->SleepForMicroseconds(10000); // prevent hot loop
|
||||
mutex_.Lock();
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsColumnFamilyDropped()) {
|
||||
!s.IsManualCompactionPaused() && !s.IsColumnFamilyDropped()) {
|
||||
// Wait a little bit before retrying background compaction in
|
||||
// case this is an environmental problem and we do not want to
|
||||
// chew up resources for failed compactions for the duration of
|
||||
@@ -2271,6 +2322,11 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
env_->SleepForMicroseconds(1000000);
|
||||
mutex_.Lock();
|
||||
} else if (s.IsManualCompactionPaused()) {
|
||||
ManualCompactionState* m = prepicked_compaction->manual_compaction_state;
|
||||
assert(m);
|
||||
ROCKS_LOG_BUFFER(&log_buffer, "[%s] [JOB %d] Manual compaction paused",
|
||||
m->cfd->GetName().c_str(), job_context.job_id);
|
||||
}
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
@@ -2279,6 +2335,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() &&
|
||||
!s.IsManualCompactionPaused() &&
|
||||
!s.IsColumnFamilyDropped());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:FoundObsoleteFiles");
|
||||
|
||||
@@ -2362,6 +2419,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
if (!error_handler_.IsBGWorkStopped()) {
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
status = Status::ShutdownInProgress();
|
||||
} else if (is_manual &&
|
||||
manual_compaction_paused_.load(std::memory_order_acquire)) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
} else {
|
||||
status = error_handler_.GetBGError();
|
||||
@@ -2586,7 +2646,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->edit()->AddFile(c->output_level(), f->fd.GetNumber(),
|
||||
f->fd.GetPathId(), f->fd.GetFileSize(), f->smallest,
|
||||
f->largest, f->fd.smallest_seqno,
|
||||
f->fd.largest_seqno, f->marked_for_compaction);
|
||||
f->fd.largest_seqno, f->marked_for_compaction,
|
||||
f->oldest_blob_file_number);
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
@@ -2663,9 +2724,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
SnapshotListFetchCallbackImpl fetch_callback(
|
||||
this, env_, c->mutable_cf_options()->snap_refresh_nanos,
|
||||
immutable_db_options_.info_log.get());
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
env_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
@@ -2676,8 +2734,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&event_logger_, c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri,
|
||||
immutable_db_options_.max_subcompactions <= 1 ? &fetch_callback
|
||||
: nullptr);
|
||||
is_manual ? &manual_compaction_paused_ : nullptr);
|
||||
compaction_job.Prepare();
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
@@ -2717,7 +2774,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
}
|
||||
|
||||
if (status.ok() || status.IsCompactionTooLarge()) {
|
||||
if (status.ok() || status.IsCompactionTooLarge() ||
|
||||
status.IsManualCompactionPaused()) {
|
||||
// Done
|
||||
} else if (status.IsColumnFamilyDropped() || status.IsShutdownInProgress()) {
|
||||
// Ignore compaction errors found during shutting down
|
||||
@@ -2972,34 +3030,20 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
}
|
||||
|
||||
// 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.
|
||||
// and db mutex (mutex_) should already be 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;
|
||||
return files_grabbed_for_purge_.find(file_number) ==
|
||||
files_grabbed_for_purge_.end() &&
|
||||
purge_files_.find(file_number) == purge_files_.end();
|
||||
}
|
||||
|
||||
// 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);
|
||||
files_grabbed_for_purge_.insert(file_number);
|
||||
}
|
||||
|
||||
void DBImpl::SetSnapshotChecker(SnapshotChecker* snapshot_checker) {
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
uint64_t DBImpl::TEST_GetLevel0TotalSize() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return default_cf_handle_->cfd()->current()->storage_info()->NumLevelBytes(0);
|
||||
@@ -122,6 +123,16 @@ Status DBImpl::TEST_FlushMemTable(bool wait, bool allow_write_stall,
|
||||
return FlushMemTable(cfd, fo, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_opts) {
|
||||
return FlushMemTable(cfd, flush_opts, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_AtomicFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& cfds, const FlushOptions& flush_opts) {
|
||||
return AtomicFlushMemTables(cfds, flush_opts, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
|
||||
@@ -128,7 +128,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
edit.AddFile(target_level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction);
|
||||
f->marked_for_compaction, f->oldest_blob_file_number);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
|
||||
+17
-14
@@ -15,6 +15,7 @@
|
||||
#include "db/memtable_list.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -258,7 +259,8 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile || type == kLogFile) {
|
||||
file_deletion_status =
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync);
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path_);
|
||||
} else {
|
||||
file_deletion_status = env_->DeleteFile(fname);
|
||||
}
|
||||
@@ -315,11 +317,9 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
candidate_files.size() + state.sst_delete_files.size() +
|
||||
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) {
|
||||
candidate_files.emplace_back(
|
||||
MakeTableFileName(kDumbDbName, file.metadata->fd.GetNumber()),
|
||||
file.path);
|
||||
MakeTableFileName(file.metadata->fd.GetNumber()), file.path);
|
||||
if (file.metadata->table_reader_handle) {
|
||||
table_cache_->Release(file.metadata->table_reader_handle);
|
||||
}
|
||||
@@ -328,7 +328,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
|
||||
for (auto file_num : state.log_delete_files) {
|
||||
if (file_num > 0) {
|
||||
candidate_files.emplace_back(LogFileName(kDumbDbName, file_num),
|
||||
candidate_files.emplace_back(LogFileName(file_num),
|
||||
immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
@@ -379,6 +379,12 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
}
|
||||
}
|
||||
|
||||
// Close WALs before trying to delete them.
|
||||
for (const auto w : state.logs_to_free) {
|
||||
// TODO: maybe check the return value of Close.
|
||||
w->Close();
|
||||
}
|
||||
|
||||
std::unordered_set<uint64_t> files_to_del;
|
||||
for (const auto& candidate_file : candidate_files) {
|
||||
const std::string& to_delete = candidate_file.file_name;
|
||||
@@ -478,11 +484,6 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
for (const auto w : state.logs_to_free) {
|
||||
// TODO: maybe check the return value of Close.
|
||||
w->Close();
|
||||
}
|
||||
|
||||
Status file_deletion_status;
|
||||
if (schedule_only) {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
@@ -496,13 +497,15 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
// 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;
|
||||
autovector<uint64_t> to_be_removed;
|
||||
for (auto fn : files_grabbed_for_purge_) {
|
||||
if (files_to_del.count(fn) == 0) {
|
||||
tmp.emplace_back(fn);
|
||||
if (files_to_del.count(fn) != 0) {
|
||||
to_be_removed.emplace_back(fn);
|
||||
}
|
||||
}
|
||||
files_grabbed_for_purge_.swap(tmp);
|
||||
for (auto fn : to_be_removed) {
|
||||
files_grabbed_for_purge_.erase(fn);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old info log files.
|
||||
|
||||
+95
-27
@@ -12,7 +12,9 @@
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "file/read_write_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "monitoring/persistent_stats_history.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
@@ -122,6 +124,25 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ImmutableDBOptions immutable_db_options(result);
|
||||
if (!IsWalDirSameAsDBPath(&immutable_db_options)) {
|
||||
// Either the WAL dir and db_paths[0]/db_name are not the same, or we
|
||||
// cannot tell for sure. In either case, assume they're different and
|
||||
// explicitly cleanup the trash log files (bypass DeleteScheduler)
|
||||
// Do this first so even if we end up calling
|
||||
// DeleteScheduler::CleanupDirectory on the same dir later, it will be
|
||||
// safe
|
||||
std::vector<std::string> filenames;
|
||||
result.env->GetChildren(result.wal_dir, &filenames);
|
||||
for (std::string& filename : filenames) {
|
||||
if (filename.find(".log.trash", filename.length() -
|
||||
std::string(".log.trash").length()) !=
|
||||
std::string::npos) {
|
||||
std::string trash_file = result.wal_dir + "/" + filename;
|
||||
result.env->DeleteFile(trash_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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
|
||||
@@ -207,17 +228,29 @@ Status DBImpl::ValidateOptions(const DBOptions& db_options) {
|
||||
"unordered_write is incompatible with enable_pipelined_write");
|
||||
}
|
||||
|
||||
if (db_options.atomic_flush && db_options.enable_pipelined_write) {
|
||||
return Status::InvalidArgument(
|
||||
"atomic_flush is incompatible with enable_pipelined_write");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::NewDB() {
|
||||
VersionEdit new_db;
|
||||
Status s = SetIdentityFile(env_, dbname_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (immutable_db_options_.write_dbid_to_manifest) {
|
||||
std::string temp_db_id;
|
||||
GetDbIdentityFromIdentityFile(&temp_db_id);
|
||||
new_db.SetDBId(temp_db_id);
|
||||
}
|
||||
new_db.SetLogNumber(0);
|
||||
new_db.SetNextFile(2);
|
||||
new_db.SetLastSequence(0);
|
||||
|
||||
Status s;
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Creating manifest 1 \n");
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
{
|
||||
@@ -265,9 +298,9 @@ Status DBImpl::CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
return env->NewDirectory(dirname, directory);
|
||||
}
|
||||
|
||||
Status DBImpl::Directories::SetDirectories(
|
||||
Env* env, const std::string& dbname, const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths) {
|
||||
Status Directories::SetDirectories(Env* env, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths) {
|
||||
Status s = DBImpl::CreateAndNewDirectory(env, dbname, &db_dir_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -320,6 +353,9 @@ Status DBImpl::Recover(
|
||||
s = env_->FileExists(CurrentFileName(dbname_));
|
||||
if (s.IsNotFound()) {
|
||||
if (immutable_db_options_.create_if_missing) {
|
||||
// Has to be called only after Identity File creation is successful
|
||||
// because DB ID is stored in Manifest if
|
||||
// immutable_db_options_.write_dbid_to_manifest = true
|
||||
s = NewDB();
|
||||
is_new_db = true;
|
||||
if (!s.ok()) {
|
||||
@@ -339,30 +375,19 @@ Status DBImpl::Recover(
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
// Check for the IDENTITY file and create it if not there
|
||||
s = env_->FileExists(IdentityFileName(dbname_));
|
||||
if (s.IsNotFound()) {
|
||||
s = SetIdentityFile(env_, dbname_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else if (!s.ok()) {
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
// Verify compatibility of env_options_ and filesystem
|
||||
{
|
||||
std::unique_ptr<RandomAccessFile> idfile;
|
||||
EnvOptions customized_env(env_options_);
|
||||
customized_env.use_direct_reads |=
|
||||
immutable_db_options_.use_direct_io_for_flush_and_compaction;
|
||||
s = env_->NewRandomAccessFile(IdentityFileName(dbname_), &idfile,
|
||||
s = env_->NewRandomAccessFile(CurrentFileName(dbname_), &idfile,
|
||||
customized_env);
|
||||
if (!s.ok()) {
|
||||
std::string error_str = s.ToString();
|
||||
// Check if unsupported Direct I/O is the root cause
|
||||
customized_env.use_direct_reads = false;
|
||||
s = env_->NewRandomAccessFile(IdentityFileName(dbname_), &idfile,
|
||||
s = env_->NewRandomAccessFile(CurrentFileName(dbname_), &idfile,
|
||||
customized_env);
|
||||
if (s.ok()) {
|
||||
return Status::InvalidArgument(
|
||||
@@ -374,8 +399,42 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status s = versions_->Recover(column_families, read_only);
|
||||
assert(db_id_.empty());
|
||||
Status s = versions_->Recover(column_families, read_only, &db_id_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// Happens when immutable_db_options_.write_dbid_to_manifest is set to true
|
||||
// the very first time.
|
||||
if (db_id_.empty()) {
|
||||
// Check for the IDENTITY file and create it if not there.
|
||||
s = env_->FileExists(IdentityFileName(dbname_));
|
||||
// Typically Identity file is created in NewDB() and for some reason if
|
||||
// it is no longer available then at this point DB ID is not in Identity
|
||||
// file or Manifest.
|
||||
if (s.IsNotFound()) {
|
||||
s = SetIdentityFile(env_, dbname_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else if (!s.ok()) {
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
GetDbIdentityFromIdentityFile(&db_id_);
|
||||
if (immutable_db_options_.write_dbid_to_manifest) {
|
||||
VersionEdit edit;
|
||||
edit.SetDBId(db_id_);
|
||||
Options options;
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
versions_->db_id_ = db_id_;
|
||||
versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options, &edit, &mutex_, nullptr,
|
||||
false);
|
||||
}
|
||||
} else {
|
||||
SetIdentityFile(env_, dbname_, db_id_);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
@@ -702,7 +761,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
continue;
|
||||
}
|
||||
}
|
||||
file_reader.reset(new SequentialFileReader(std::move(file), fname));
|
||||
file_reader.reset(new SequentialFileReader(
|
||||
std::move(file), fname, immutable_db_options_.log_readahead_size));
|
||||
}
|
||||
|
||||
// Create the log reader.
|
||||
@@ -842,9 +902,10 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// That's why we set ignore missing column families to true
|
||||
bool has_valid_writes = false;
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
&batch, column_family_memtables_.get(), &flush_scheduler_, true,
|
||||
log_number, this, false /* concurrent_memtable_writes */,
|
||||
next_sequence, &has_valid_writes, seq_per_batch_, batch_per_txn_);
|
||||
&batch, column_family_memtables_.get(), &flush_scheduler_,
|
||||
&trim_history_scheduler_, true, log_number, this,
|
||||
false /* concurrent_memtable_writes */, next_sequence,
|
||||
&has_valid_writes, seq_per_batch_, batch_per_txn_);
|
||||
MaybeIgnoreError(&status);
|
||||
if (!status.ok()) {
|
||||
// We are treating this as a failure while reading since we read valid
|
||||
@@ -911,6 +972,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
}
|
||||
|
||||
flush_scheduler_.Clear();
|
||||
trim_history_scheduler_.Clear();
|
||||
auto last_sequence = *next_sequence - 1;
|
||||
if ((*next_sequence != kMaxSequenceNumber) &&
|
||||
(versions_->LastSequence() <= last_sequence)) {
|
||||
@@ -996,6 +1058,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
versions_->MarkFileNumberUsed(max_log_number + 1);
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
edit, &mutex_);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::RecoverLogFiles:AfterLogAndApply",
|
||||
nullptr);
|
||||
if (!status.ok()) {
|
||||
// Recovery failed
|
||||
break;
|
||||
@@ -1072,8 +1136,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
mutex_.AssertHeld();
|
||||
const uint64_t start_micros = env_->NowMicros();
|
||||
FileMetaData meta;
|
||||
auto pending_outputs_inserted_elem =
|
||||
CaptureCurrentFileNumberInPendingOutputs();
|
||||
std::unique_ptr<std::list<uint64_t>::iterator> pending_outputs_inserted_elem(
|
||||
new std::list<uint64_t>::iterator(
|
||||
CaptureCurrentFileNumberInPendingOutputs()));
|
||||
meta.fd = FileDescriptor(versions_->NewFileNumber(), 0, 0);
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
@@ -1145,7 +1210,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
edit->AddFile(level, meta.fd.GetNumber(), meta.fd.GetPathId(),
|
||||
meta.fd.GetFileSize(), meta.smallest, meta.largest,
|
||||
meta.fd.smallest_seqno, meta.fd.largest_seqno,
|
||||
meta.marked_for_compaction);
|
||||
meta.marked_for_compaction, meta.oldest_blob_file_number);
|
||||
}
|
||||
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
@@ -1294,6 +1359,9 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
delete impl;
|
||||
return s;
|
||||
}
|
||||
|
||||
impl->wal_in_db_path_ = IsWalDirSameAsDBPath(&impl->immutable_db_options_);
|
||||
|
||||
impl->mutex_.Lock();
|
||||
// Handles create_if_missing, error_if_exists
|
||||
s = impl->Recover(column_families);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_impl/db_impl_readonly.h"
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
|
||||
#include "db/compacted_db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
@@ -16,6 +16,10 @@ namespace rocksdb {
|
||||
class DBImplReadOnly : public DBImpl {
|
||||
public:
|
||||
DBImplReadOnly(const DBOptions& options, const std::string& dbname);
|
||||
// No copying allowed
|
||||
DBImplReadOnly(const DBImplReadOnly&) = delete;
|
||||
void operator=(const DBImplReadOnly&) = delete;
|
||||
|
||||
virtual ~DBImplReadOnly();
|
||||
|
||||
// Implementations of the DB interface
|
||||
@@ -115,12 +119,18 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
// No copying allowed
|
||||
DBImplReadOnly(const DBImplReadOnly&);
|
||||
void operator=(const DBImplReadOnly&);
|
||||
};
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/db_iter.h"
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "logging/auto_roll_logger.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
@@ -150,7 +150,8 @@ Status DBImplSecondary::MaybeInitLogReader(
|
||||
*log_reader = nullptr;
|
||||
return status;
|
||||
}
|
||||
file_reader.reset(new SequentialFileReader(std::move(file), fname));
|
||||
file_reader.reset(new SequentialFileReader(
|
||||
std::move(file), fname, immutable_db_options_.log_readahead_size));
|
||||
}
|
||||
|
||||
// Create the log reader.
|
||||
@@ -252,9 +253,9 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
bool has_valid_writes = false;
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
&batch, column_family_memtables_.get(),
|
||||
nullptr /* flush_scheduler */, true, log_number, this,
|
||||
false /* concurrent_memtable_writes */, next_sequence,
|
||||
&has_valid_writes, seq_per_batch_, batch_per_txn_);
|
||||
nullptr /* flush_scheduler */, nullptr /* trim_history_scheduler*/,
|
||||
true, log_number, this, false /* concurrent_memtable_writes */,
|
||||
next_sequence, &has_valid_writes, seq_per_batch_, batch_per_txn_);
|
||||
}
|
||||
// If column family was not found, it might mean that the WAL write
|
||||
// batch references to the column family that was dropped after the
|
||||
@@ -587,6 +588,8 @@ Status DB::OpenAsSecondary(
|
||||
&impl->write_controller_));
|
||||
impl->column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
|
||||
impl->wal_in_db_path_ = IsWalDirSameAsDBPath(&impl->immutable_db_options_);
|
||||
|
||||
impl->mutex_.Lock();
|
||||
s = impl->Recover(column_families, true, false, false);
|
||||
if (s.ok()) {
|
||||
|
||||
+119
-39
@@ -171,8 +171,10 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
versions_->GetColumnFamilySet());
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
&trim_history_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
true /*concurrent_memtable_writes*/, seq_per_batch_, w.batch_cnt);
|
||||
true /*concurrent_memtable_writes*/, seq_per_batch_, w.batch_cnt,
|
||||
batch_per_txn_, write_options.memtable_insert_hint_per_batch);
|
||||
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
}
|
||||
@@ -292,18 +294,19 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
// commit. That lets us release our leader status early.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count,
|
||||
stats->AddDBStats(InternalStats::kIntStatsNumKeysWritten, total_count,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size,
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1, concurrent_update);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
auto write_done_by_other = write_group.size - 1;
|
||||
if (write_done_by_other > 0) {
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER, write_done_by_other,
|
||||
concurrent_update);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
write_done_by_other, concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
RecordInHistogram(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
@@ -374,7 +377,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// w.sequence will be set inside InsertInto
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
write_group, current_sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
&flush_scheduler_, &trim_history_scheduler_,
|
||||
write_options.ignore_missing_column_families,
|
||||
0 /*recovery_log_number*/, this, parallel, seq_per_batch_,
|
||||
batch_per_txn_);
|
||||
} else {
|
||||
@@ -390,9 +394,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
assert(w.sequence == current_sequence);
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
&trim_history_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/,
|
||||
this, true /*concurrent_memtable_writes*/, seq_per_batch_,
|
||||
w.batch_cnt, batch_per_txn_);
|
||||
w.batch_cnt, batch_per_txn_,
|
||||
write_options.memtable_insert_hint_per_batch);
|
||||
}
|
||||
}
|
||||
if (seq_used != nullptr) {
|
||||
@@ -499,9 +505,9 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::kIntStatsNumKeysWritten, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
RecordInHistogram(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
|
||||
@@ -509,10 +515,10 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (w.status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF, 1);
|
||||
if (wal_write_group.size > 1) {
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER,
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
wal_write_group.size - 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
|
||||
}
|
||||
@@ -544,9 +550,9 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
} else {
|
||||
memtable_write_group.status = WriteBatchInternal::InsertInto(
|
||||
memtable_write_group, w.sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*log_number*/, this, false /*concurrent_memtable_writes*/,
|
||||
seq_per_batch_, batch_per_txn_);
|
||||
&flush_scheduler_, &trim_history_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
false /*concurrent_memtable_writes*/, seq_per_batch_, batch_per_txn_);
|
||||
versions_->SetLastSequence(memtable_write_group.last_sequence);
|
||||
write_thread_.ExitAsMemTableWriter(&w, memtable_write_group);
|
||||
}
|
||||
@@ -558,8 +564,10 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
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*/);
|
||||
&trim_history_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*log_number*/, this, true /*concurrent_memtable_writes*/,
|
||||
false /*seq_per_batch*/, 0 /*batch_cnt*/, true /*batch_per_txn*/,
|
||||
write_options.memtable_insert_hint_per_batch);
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
versions_->SetLastSequence(w.write_group->last_sequence);
|
||||
@@ -589,15 +597,17 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
w.sequence = seq;
|
||||
size_t total_count = WriteBatchInternal::Count(my_batch);
|
||||
InternalStats* stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::kIntStatsNumKeysWritten, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
|
||||
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_, sub_batch_cnt);
|
||||
&trim_history_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*log_number*/, this, true /*concurrent_memtable_writes*/,
|
||||
seq_per_batch_, sub_batch_cnt, true /*batch_per_txn*/,
|
||||
write_options.memtable_insert_hint_per_batch);
|
||||
|
||||
WriteStatusCheck(w.status);
|
||||
if (write_options.disableWAL) {
|
||||
@@ -698,15 +708,16 @@ Status DBImpl::WriteImplWALOnly(
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
// commit. That lets us release our leader status early.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size,
|
||||
stats->AddDBStats(InternalStats::kIntStatsBytesWritten, total_byte_size,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1, concurrent_update);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneBySelf, 1,
|
||||
concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_SELF);
|
||||
auto write_done_by_other = write_group.size - 1;
|
||||
if (write_done_by_other > 0) {
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_OTHER, write_done_by_other,
|
||||
concurrent_update);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteDoneByOther,
|
||||
write_done_by_other, concurrent_update);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
RecordInHistogram(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
@@ -855,6 +866,10 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
status = HandleWriteBufferFull(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !trim_history_scheduler_.Empty())) {
|
||||
status = TrimMemtableHistory(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !flush_scheduler_.Empty())) {
|
||||
WaitForPendingWrites();
|
||||
status = ScheduleFlushes(write_context);
|
||||
@@ -1034,12 +1049,12 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
if (status.ok()) {
|
||||
auto stats = default_cf_internal_stats_;
|
||||
if (need_log_sync) {
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_SYNCED, 1);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileSynced, 1);
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
}
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileBytes, log_size);
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_WITH_WAL, write_with_wal);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return status;
|
||||
@@ -1085,9 +1100,10 @@ Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
if (status.ok()) {
|
||||
const bool concurrent = true;
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_BYTES, log_size, concurrent);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileBytes, log_size,
|
||||
concurrent);
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_WITH_WAL, write_with_wal,
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal,
|
||||
concurrent);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
@@ -1111,9 +1127,9 @@ Status DBImpl::WriteRecoverableState() {
|
||||
WriteBatchInternal::SetSequence(&cached_recoverable_state_, seq + 1);
|
||||
auto status = WriteBatchInternal::InsertInto(
|
||||
&cached_recoverable_state_, column_family_memtables_.get(),
|
||||
&flush_scheduler_, true, 0 /*recovery_log_number*/, this,
|
||||
false /* concurrent_memtable_writes */, &next_seq, &dont_care_bool,
|
||||
seq_per_batch_);
|
||||
&flush_scheduler_, &trim_history_scheduler_, true,
|
||||
0 /*recovery_log_number*/, this, false /* concurrent_memtable_writes */,
|
||||
&next_seq, &dont_care_bool, seq_per_batch_);
|
||||
auto last_seq = next_seq - 1;
|
||||
if (two_write_queues_) {
|
||||
versions_->FetchAddLastAllocatedSequence(last_seq - seq);
|
||||
@@ -1228,6 +1244,7 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
cfds.push_back(cfd);
|
||||
}
|
||||
}
|
||||
MaybeFlushStatsCF(&cfds);
|
||||
}
|
||||
for (const auto cfd : cfds) {
|
||||
cfd->Ref();
|
||||
@@ -1294,6 +1311,7 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
if (cfd_picked != nullptr) {
|
||||
cfds.push_back(cfd_picked);
|
||||
}
|
||||
MaybeFlushStatsCF(&cfds);
|
||||
}
|
||||
|
||||
for (const auto cfd : cfds) {
|
||||
@@ -1387,8 +1405,8 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
||||
}
|
||||
assert(!delayed || !write_options.no_slowdown);
|
||||
if (delayed) {
|
||||
default_cf_internal_stats_->AddDBStats(InternalStats::WRITE_STALL_MICROS,
|
||||
time_delayed);
|
||||
default_cf_internal_stats_->AddDBStats(
|
||||
InternalStats::kIntStatsWriteStallMicros, time_delayed);
|
||||
RecordTick(stats_, STALL_MICROS, time_delayed);
|
||||
}
|
||||
|
||||
@@ -1437,6 +1455,65 @@ Status DBImpl::ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::MaybeFlushStatsCF(autovector<ColumnFamilyData*>* cfds) {
|
||||
assert(cfds != nullptr);
|
||||
if (!cfds->empty() && immutable_db_options_.persist_stats_to_disk) {
|
||||
ColumnFamilyData* cfd_stats =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(
|
||||
kPersistentStatsColumnFamilyName);
|
||||
if (cfd_stats != nullptr && !cfd_stats->mem()->IsEmpty()) {
|
||||
for (ColumnFamilyData* cfd : *cfds) {
|
||||
if (cfd == cfd_stats) {
|
||||
// stats CF already included in cfds
|
||||
return;
|
||||
}
|
||||
}
|
||||
// force flush stats CF when its log number is less than all other CF's
|
||||
// log numbers
|
||||
bool force_flush_stats_cf = true;
|
||||
for (auto* loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (loop_cfd == cfd_stats) {
|
||||
continue;
|
||||
}
|
||||
if (loop_cfd->GetLogNumber() <= cfd_stats->GetLogNumber()) {
|
||||
force_flush_stats_cf = false;
|
||||
}
|
||||
}
|
||||
if (force_flush_stats_cf) {
|
||||
cfds->push_back(cfd_stats);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Force flushing stats CF with automated flush "
|
||||
"to avoid holding old logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::TrimMemtableHistory(WriteContext* context) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
ColumnFamilyData* tmp_cfd;
|
||||
while ((tmp_cfd = trim_history_scheduler_.TakeNextColumnFamily()) !=
|
||||
nullptr) {
|
||||
cfds.push_back(tmp_cfd);
|
||||
}
|
||||
for (auto& cfd : cfds) {
|
||||
autovector<MemTable*> to_delete;
|
||||
cfd->imm()->TrimHistory(&to_delete, cfd->mem()->ApproximateMemoryUsage());
|
||||
for (auto m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
context->superversion_context.NewSuperVersion();
|
||||
assert(context->superversion_context.new_superversion.get() != nullptr);
|
||||
cfd->InstallSuperVersion(&context->superversion_context, &mutex_);
|
||||
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
cfd = nullptr;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
@@ -1450,6 +1527,7 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
while ((tmp_cfd = flush_scheduler_.TakeNextColumnFamily()) != nullptr) {
|
||||
cfds.push_back(tmp_cfd);
|
||||
}
|
||||
MaybeFlushStatsCF(&cfds);
|
||||
}
|
||||
Status status;
|
||||
for (auto& cfd : cfds) {
|
||||
@@ -1696,14 +1774,16 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
Slice akey;
|
||||
std::string buf;
|
||||
Status s = AppendTimestamp(key, *(opt.timestamp), &akey, &buf);
|
||||
const Slice* ts = opt.timestamp;
|
||||
assert(nullptr != ts);
|
||||
size_t ts_sz = ts->size();
|
||||
WriteBatch batch(key.size() + ts_sz + value.size() + 24, /*max_bytes=*/0,
|
||||
ts_sz);
|
||||
Status s = batch.Put(column_family, key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
WriteBatch batch(akey.size() + value.size() + 24);
|
||||
s = batch.Put(column_family, akey, value);
|
||||
s = batch.AssignTimestamp(*ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -605,9 +605,9 @@ TEST_F(DBSecondaryTest, SwitchWAL) {
|
||||
TEST_F(DBSecondaryTest, SwitchWALMultiColumnFamilies) {
|
||||
const int kNumKeysPerMemtable = 1;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::BackgroundCallFlush:ContextCleanedUp",
|
||||
"DBSecondaryTest::SwitchWALMultipleColumnFamilies:BeforeCatchUp"}});
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCallFlush:ContextCleanedUp",
|
||||
"DBSecondaryTest::SwitchWALMultipleColumnFamilies:BeforeCatchUp"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
const std::string kCFName1 = "pikachu";
|
||||
Options options;
|
||||
@@ -662,7 +662,8 @@ TEST_F(DBSecondaryTest, SwitchWALMultiColumnFamilies) {
|
||||
Put(0 /*cf*/, "key" + std::to_string(k), "value" + std::to_string(k)));
|
||||
ASSERT_OK(
|
||||
Put(1 /*cf*/, "key" + std::to_string(k), "value" + std::to_string(k)));
|
||||
TEST_SYNC_POINT("DBSecondaryTest::SwitchWALMultipleColumnFamilies:BeforeCatchUp");
|
||||
TEST_SYNC_POINT(
|
||||
"DBSecondaryTest::SwitchWALMultipleColumnFamilies:BeforeCatchUp");
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
verify_db(dbfull(), handles_, db_secondary_, handles_secondary_);
|
||||
SyncPoint::GetInstance()->ClearTrace();
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
#include "db/db_info_dumper.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "file/filename.h"
|
||||
|
||||
+249
-543
File diff suppressed because it is too large
Load Diff
+276
-77
@@ -17,6 +17,7 @@
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -46,9 +47,282 @@ namespace rocksdb {
|
||||
// key: BBB value: v1
|
||||
// key: BBC value: v1
|
||||
//
|
||||
class Arena;
|
||||
class DBIter;
|
||||
|
||||
// Memtables and sstables that make the DB representation contain
|
||||
// (userkey,seq,type) => uservalue entries. DBIter
|
||||
// combines multiple entries for the same userkey found in the DB
|
||||
// representation into a single entry while accounting for sequence
|
||||
// numbers, deletion markers, overwrites, etc.
|
||||
class DBIter final : public Iterator {
|
||||
public:
|
||||
// The following is grossly complicated. TODO: clean it up
|
||||
// Which direction is the iterator currently moving?
|
||||
// (1) When moving forward:
|
||||
// (1a) if current_entry_is_merged_ = false, the internal iterator is
|
||||
// positioned at the exact entry that yields this->key(), this->value()
|
||||
// (1b) if current_entry_is_merged_ = true, the internal iterator is
|
||||
// positioned immediately after the last entry that contributed to the
|
||||
// current this->value(). That entry may or may not have key equal to
|
||||
// this->key().
|
||||
// (2) When moving backwards, the internal iterator is positioned
|
||||
// just before all entries whose user key == this->key().
|
||||
enum Direction { kForward, kReverse };
|
||||
|
||||
// LocalStatistics contain Statistics counters that will be aggregated per
|
||||
// each iterator instance and then will be sent to the global statistics when
|
||||
// the iterator is destroyed.
|
||||
//
|
||||
// The purpose of this approach is to avoid perf regression happening
|
||||
// when multiple threads bump the atomic counters from a DBIter::Next().
|
||||
struct LocalStatistics {
|
||||
explicit LocalStatistics() { ResetCounters(); }
|
||||
|
||||
void ResetCounters() {
|
||||
next_count_ = 0;
|
||||
next_found_count_ = 0;
|
||||
prev_count_ = 0;
|
||||
prev_found_count_ = 0;
|
||||
bytes_read_ = 0;
|
||||
skip_count_ = 0;
|
||||
}
|
||||
|
||||
void BumpGlobalStatistics(Statistics* global_statistics) {
|
||||
RecordTick(global_statistics, NUMBER_DB_NEXT, next_count_);
|
||||
RecordTick(global_statistics, NUMBER_DB_NEXT_FOUND, next_found_count_);
|
||||
RecordTick(global_statistics, NUMBER_DB_PREV, prev_count_);
|
||||
RecordTick(global_statistics, NUMBER_DB_PREV_FOUND, prev_found_count_);
|
||||
RecordTick(global_statistics, ITER_BYTES_READ, bytes_read_);
|
||||
RecordTick(global_statistics, NUMBER_ITER_SKIP, skip_count_);
|
||||
PERF_COUNTER_ADD(iter_read_bytes, bytes_read_);
|
||||
ResetCounters();
|
||||
}
|
||||
|
||||
// Map to Tickers::NUMBER_DB_NEXT
|
||||
uint64_t next_count_;
|
||||
// Map to Tickers::NUMBER_DB_NEXT_FOUND
|
||||
uint64_t next_found_count_;
|
||||
// Map to Tickers::NUMBER_DB_PREV
|
||||
uint64_t prev_count_;
|
||||
// Map to Tickers::NUMBER_DB_PREV_FOUND
|
||||
uint64_t prev_found_count_;
|
||||
// Map to Tickers::ITER_BYTES_READ
|
||||
uint64_t bytes_read_;
|
||||
// Map to Tickers::NUMBER_ITER_SKIP
|
||||
uint64_t skip_count_;
|
||||
};
|
||||
|
||||
DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob);
|
||||
|
||||
// No copying allowed
|
||||
DBIter(const DBIter&) = delete;
|
||||
void operator=(const DBIter&) = delete;
|
||||
|
||||
~DBIter() override {
|
||||
// Release pinned data if any
|
||||
if (pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
RecordTick(statistics_, NO_ITERATOR_DELETED);
|
||||
ResetInternalKeysSkippedCounter();
|
||||
local_stats_.BumpGlobalStatistics(statistics_);
|
||||
iter_.DeleteIter(arena_mode_);
|
||||
}
|
||||
virtual void SetIter(InternalIterator* iter) {
|
||||
assert(iter_.iter() == nullptr);
|
||||
iter_.Set(iter);
|
||||
iter_.iter()->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
virtual ReadRangeDelAggregator* GetRangeDelAggregator() {
|
||||
return &range_del_agg_;
|
||||
}
|
||||
|
||||
bool Valid() const override { return valid_; }
|
||||
Slice key() const override {
|
||||
assert(valid_);
|
||||
if (start_seqnum_ > 0) {
|
||||
return saved_key_.GetInternalKey();
|
||||
} else {
|
||||
return saved_key_.GetUserKey();
|
||||
}
|
||||
}
|
||||
Slice value() const override {
|
||||
assert(valid_);
|
||||
if (current_entry_is_merged_) {
|
||||
// If pinned_value_ is set then the result of merge operator is one of
|
||||
// the merge operands and we should return it.
|
||||
return pinned_value_.data() ? pinned_value_ : saved_value_;
|
||||
} else if (direction_ == kReverse) {
|
||||
return pinned_value_;
|
||||
} else {
|
||||
return iter_.value();
|
||||
}
|
||||
}
|
||||
Status status() const override {
|
||||
if (status_.ok()) {
|
||||
return iter_.status();
|
||||
} else {
|
||||
assert(!valid_);
|
||||
return status_;
|
||||
}
|
||||
}
|
||||
bool IsBlob() const {
|
||||
assert(valid_ && (allow_blob_ || !is_blob_));
|
||||
return is_blob_;
|
||||
}
|
||||
|
||||
Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
|
||||
void Next() final override;
|
||||
void Prev() final override;
|
||||
void Seek(const Slice& target) final override;
|
||||
void SeekForPrev(const Slice& target) final override;
|
||||
void SeekToFirst() final override;
|
||||
void SeekToLast() final override;
|
||||
Env* env() { return env_; }
|
||||
void set_sequence(uint64_t s) {
|
||||
sequence_ = s;
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(s);
|
||||
}
|
||||
}
|
||||
void set_valid(bool v) { valid_ = v; }
|
||||
|
||||
private:
|
||||
// For all methods in this block:
|
||||
// PRE: iter_->Valid() && status_.ok()
|
||||
// Return false if there was an error, and status() is non-ok, valid_ = false;
|
||||
// in this case callers would usually stop what they were doing and return.
|
||||
bool ReverseToForward();
|
||||
bool ReverseToBackward();
|
||||
// Set saved_key_ to the seek key to target, with proper sequence number set.
|
||||
// It might get adjusted if the seek key is smaller than iterator lower bound.
|
||||
void SetSavedKeyToSeekTarget(const Slice& /*target*/);
|
||||
// Set saved_key_ to the seek key to target, with proper sequence number set.
|
||||
// It might get adjusted if the seek key is larger than iterator upper bound.
|
||||
void SetSavedKeyToSeekForPrevTarget(const Slice& /*target*/);
|
||||
bool FindValueForCurrentKey();
|
||||
bool FindValueForCurrentKeyUsingSeek();
|
||||
bool FindUserKeyBeforeSavedKey();
|
||||
// If `skipping_saved_key` is true, the function will keep iterating until it
|
||||
// finds a user key that is larger than `saved_key_`.
|
||||
// If `prefix` is not null, the iterator needs to stop when all keys for the
|
||||
// prefix are exhausted and the interator is set to invalid.
|
||||
bool FindNextUserEntry(bool skipping_saved_key, const Slice* prefix);
|
||||
// Internal implementation of FindNextUserEntry().
|
||||
bool FindNextUserEntryInternal(bool skipping_saved_key, const Slice* prefix);
|
||||
bool ParseKey(ParsedInternalKey* key);
|
||||
bool MergeValuesNewToOld();
|
||||
|
||||
// If prefix is not null, we need to set the iterator to invalid if no more
|
||||
// entry can be found within the prefix.
|
||||
void PrevInternal(const Slice* /*prefix*/);
|
||||
bool TooManyInternalKeysSkipped(bool increment = true);
|
||||
bool IsVisible(SequenceNumber sequence);
|
||||
|
||||
// Temporarily pin the blocks that we encounter until ReleaseTempPinnedData()
|
||||
// is called
|
||||
void TempPinData() {
|
||||
if (!pin_thru_lifetime_) {
|
||||
pinned_iters_mgr_.StartPinning();
|
||||
}
|
||||
}
|
||||
|
||||
// Release blocks pinned by TempPinData()
|
||||
void ReleaseTempPinnedData() {
|
||||
if (!pin_thru_lifetime_ && pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
}
|
||||
|
||||
inline void ClearSavedValue() {
|
||||
if (saved_value_.capacity() > 1048576) {
|
||||
std::string empty;
|
||||
swap(empty, saved_value_);
|
||||
} else {
|
||||
saved_value_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
inline void ResetInternalKeysSkippedCounter() {
|
||||
local_stats_.skip_count_ += num_internal_keys_skipped_;
|
||||
if (valid_) {
|
||||
local_stats_.skip_count_--;
|
||||
}
|
||||
num_internal_keys_skipped_ = 0;
|
||||
}
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
Env* const env_;
|
||||
Logger* logger_;
|
||||
UserComparatorWrapper user_comparator_;
|
||||
const MergeOperator* const merge_operator_;
|
||||
IteratorWrapper iter_;
|
||||
ReadCallback* read_callback_;
|
||||
// Max visible sequence number. It is normally the snapshot seq unless we have
|
||||
// uncommitted data in db as in WriteUnCommitted.
|
||||
SequenceNumber sequence_;
|
||||
|
||||
IterKey saved_key_;
|
||||
// Reusable internal key data structure. This is only used inside one function
|
||||
// and should not be used across functions. Reusing this object can reduce
|
||||
// overhead of calling construction of the function if creating it each time.
|
||||
ParsedInternalKey ikey_;
|
||||
std::string saved_value_;
|
||||
Slice pinned_value_;
|
||||
// for prefix seek mode to support prev()
|
||||
Statistics* statistics_;
|
||||
uint64_t max_skip_;
|
||||
uint64_t max_skippable_internal_keys_;
|
||||
uint64_t num_internal_keys_skipped_;
|
||||
const Slice* iterate_lower_bound_;
|
||||
const Slice* iterate_upper_bound_;
|
||||
|
||||
// The prefix of the seek key. It is only used when prefix_same_as_start_
|
||||
// is true and prefix extractor is not null. In Next() or Prev(), current keys
|
||||
// will be checked against this prefix, so that the iterator can be
|
||||
// invalidated if the keys in this prefix has been exhausted. Set it using
|
||||
// SetUserKey() and use it using GetUserKey().
|
||||
IterKey prefix_;
|
||||
|
||||
Status status_;
|
||||
Direction direction_;
|
||||
bool valid_;
|
||||
bool current_entry_is_merged_;
|
||||
// True if we know that the current entry's seqnum is 0.
|
||||
// This information is used as that the next entry will be for another
|
||||
// user key.
|
||||
bool is_key_seqnum_zero_;
|
||||
const bool prefix_same_as_start_;
|
||||
// Means that we will pin all data blocks we read as long the Iterator
|
||||
// is not deleted, will be true if ReadOptions::pin_data is true
|
||||
const bool pin_thru_lifetime_;
|
||||
const bool total_order_seek_;
|
||||
bool allow_blob_;
|
||||
bool is_blob_;
|
||||
bool arena_mode_;
|
||||
// List of operands for merge operator.
|
||||
MergeContext merge_context_;
|
||||
ReadRangeDelAggregator range_del_agg_;
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
#ifdef ROCKSDB_LITE
|
||||
ROCKSDB_FIELD_UNUSED
|
||||
#endif
|
||||
DBImpl* db_impl_;
|
||||
#ifdef ROCKSDB_LITE
|
||||
ROCKSDB_FIELD_UNUSED
|
||||
#endif
|
||||
ColumnFamilyData* cfd_;
|
||||
// for diff snapshots we want the lower bound on the seqnum;
|
||||
// if this value > 0 iterator will return internal keys
|
||||
SequenceNumber start_seqnum_;
|
||||
};
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified `sequence` number
|
||||
// into appropriate user keys.
|
||||
@@ -61,79 +335,4 @@ extern Iterator* NewDBIterator(
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false);
|
||||
|
||||
// A wrapper iterator which wraps DB Iterator and the arena, with which the DB
|
||||
// iterator is supposed be allocated. This class is used as an entry point of
|
||||
// a iterator hierarchy whose memory can be allocated inline. In that way,
|
||||
// accessing the iterator tree can be more cache friendly. It is also faster
|
||||
// to allocate.
|
||||
// When using the class's Iterator interface, the behavior is exactly
|
||||
// the same as the inner DBIter.
|
||||
class ArenaWrappedDBIter : public Iterator {
|
||||
public:
|
||||
virtual ~ArenaWrappedDBIter();
|
||||
|
||||
// Get the arena to be used to allocate memory for DBIter to be wrapped,
|
||||
// as well as child iterators in it.
|
||||
virtual Arena* GetArena() { return &arena_; }
|
||||
virtual ReadRangeDelAggregator* GetRangeDelAggregator();
|
||||
|
||||
// Set the internal iterator wrapped inside the DB Iterator. Usually it is
|
||||
// a merging iterator.
|
||||
virtual void SetIterUnderDBIter(InternalIterator* iter);
|
||||
virtual bool Valid() const override;
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
virtual Slice key() const override;
|
||||
virtual Slice value() const override;
|
||||
virtual Status status() const override;
|
||||
virtual Status Refresh() override;
|
||||
bool IsBlob() const;
|
||||
|
||||
virtual Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
|
||||
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, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob, bool allow_refresh);
|
||||
|
||||
void StoreRefreshInfo(const ReadOptions& read_options, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, ReadCallback* read_callback,
|
||||
bool allow_blob) {
|
||||
read_options_ = read_options;
|
||||
db_impl_ = db_impl;
|
||||
cfd_ = cfd;
|
||||
read_callback_ = read_callback;
|
||||
allow_blob_ = allow_blob;
|
||||
}
|
||||
|
||||
private:
|
||||
DBIter* db_iter_;
|
||||
Arena arena_;
|
||||
uint64_t sv_number_;
|
||||
ColumnFamilyData* cfd_ = nullptr;
|
||||
DBImpl* db_impl_ = nullptr;
|
||||
ReadOptions read_options_;
|
||||
ReadCallback* read_callback_;
|
||||
bool allow_blob_ = false;
|
||||
bool allow_refresh_ = true;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
// `db_impl` and `cfd` are used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
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, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false,
|
||||
bool allow_refresh = true);
|
||||
} // namespace rocksdb
|
||||
|
||||
+247
-57
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
@@ -182,6 +183,33 @@ TEST_P(DBIteratorTest, IterSeekBeforePrev) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, IterReseekNewUpperBound) {
|
||||
Random rnd(301);
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
table_options.block_size_deviation = 50;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.compression = kNoCompression;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("a", RandomString(&rnd, 400)));
|
||||
ASSERT_OK(Put("aabb", RandomString(&rnd, 400)));
|
||||
ASSERT_OK(Put("aaef", RandomString(&rnd, 400)));
|
||||
ASSERT_OK(Put("b", RandomString(&rnd, 400)));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ReadOptions opts;
|
||||
Slice ub = Slice("aa");
|
||||
opts.iterate_upper_bound = &ub;
|
||||
auto iter = NewIterator(opts);
|
||||
iter->Seek(Slice("a"));
|
||||
ub = Slice("b");
|
||||
iter->Seek(Slice("aabc"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "aaef");
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, IterSeekForPrevBeforeNext) {
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
@@ -1049,6 +1077,149 @@ TEST_P(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
ASSERT_EQ(upper_bound_hits, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable kBinarySearchWithFirstKey, do some iterator operations and check that
|
||||
// they don't do unnecessary block reads.
|
||||
TEST_P(DBIteratorTest, IndexWithFirstKey) {
|
||||
for (int tailing = 0; tailing < 2; ++tailing) {
|
||||
SCOPED_TRACE("tailing = " + std::to_string(tailing));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor = nullptr;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
Statistics* stats = options.statistics.get();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey;
|
||||
table_options.index_shortening =
|
||||
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
|
||||
table_options.flush_block_policy_factory =
|
||||
std::make_shared<FlushBlockEveryKeyPolicyFactory>();
|
||||
table_options.block_cache =
|
||||
NewLRUCache(8000); // fits all blocks and their cache metadata overhead
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Merge("a1", "x1"));
|
||||
ASSERT_OK(Merge("b1", "y1"));
|
||||
ASSERT_OK(Merge("c0", "z1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("a2", "x2"));
|
||||
ASSERT_OK(Merge("b2", "y2"));
|
||||
ASSERT_OK(Merge("c0", "z2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("a3", "x3"));
|
||||
ASSERT_OK(Merge("b3", "y3"));
|
||||
ASSERT_OK(Merge("c3", "z3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Block cache is not important for this test.
|
||||
// We use BLOCK_CACHE_DATA_* counters just because they're the most readily
|
||||
// available way of counting block accesses.
|
||||
|
||||
ReadOptions ropt;
|
||||
ropt.tailing = tailing;
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ropt));
|
||||
|
||||
iter->Seek("b10");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b2", iter->key().ToString());
|
||||
EXPECT_EQ("y2", iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b3", iter->key().ToString());
|
||||
EXPECT_EQ("y3", iter->value().ToString());
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
iter->Seek("c0");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("c0", iter->key().ToString());
|
||||
EXPECT_EQ("z1,z2", iter->value().ToString());
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("c3", iter->key().ToString());
|
||||
EXPECT_EQ("z3", iter->value().ToString());
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter.reset();
|
||||
|
||||
// Enable iterate_upper_bound and check that iterator is not trying to read
|
||||
// blocks that are fully above upper bound.
|
||||
std::string ub = "b3";
|
||||
Slice ub_slice(ub);
|
||||
ropt.iterate_upper_bound = &ub_slice;
|
||||
iter.reset(NewIterator(ropt));
|
||||
|
||||
iter->Seek("b2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b2", iter->key().ToString());
|
||||
EXPECT_EQ("y2", iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, IndexWithFirstKeyGet) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor = nullptr;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
Statistics* stats = options.statistics.get();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey;
|
||||
table_options.index_shortening =
|
||||
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
|
||||
table_options.flush_block_policy_factory =
|
||||
std::make_shared<FlushBlockEveryKeyPolicyFactory>();
|
||||
table_options.block_cache = NewLRUCache(1000); // fits all blocks
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Merge("a", "x1"));
|
||||
ASSERT_OK(Merge("c", "y1"));
|
||||
ASSERT_OK(Merge("e", "z1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("c", "y2"));
|
||||
ASSERT_OK(Merge("e", "z2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Get() between blocks shouldn't read any blocks.
|
||||
ASSERT_EQ("NOT_FOUND", Get("b"));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
// Get() of an existing key shouldn't read any unnecessary blocks when there's
|
||||
// only one key per block.
|
||||
|
||||
ASSERT_EQ("y1,y2", Get("c"));
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
ASSERT_EQ("x1", Get("a"));
|
||||
EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
EXPECT_EQ(std::vector<std::string>({"NOT_FOUND", "z1,z2"}),
|
||||
MultiGet({"b", "e"}));
|
||||
}
|
||||
|
||||
// TODO(3.13): fix the issue of Seek() + Prev() which might not necessary
|
||||
// return the biggest key which is smaller than the seek key.
|
||||
TEST_P(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
@@ -2548,73 +2719,92 @@ TEST_P(DBIteratorTest, AvoidReseekLevelIterator) {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, AvoidReseekChildIterator) {
|
||||
// MyRocks may change iterate bounds before seek. Simply test to make sure such
|
||||
// usage doesn't break iterator.
|
||||
TEST_P(DBIteratorTest, IterateBoundChangedBeforeSeek) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 800;
|
||||
table_options.block_size = 100;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
std::string value(50, 'v');
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::string random_str = RandomString(&rnd, 180);
|
||||
|
||||
ASSERT_OK(Put("1", random_str));
|
||||
ASSERT_OK(Put("2", random_str));
|
||||
ASSERT_OK(Put("3", random_str));
|
||||
ASSERT_OK(Put("4", random_str));
|
||||
ASSERT_OK(Put("8", random_str));
|
||||
ASSERT_OK(Put("9", random_str));
|
||||
ASSERT_OK(Put("aaa", value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("5", random_str));
|
||||
ASSERT_OK(Put("6", random_str));
|
||||
ASSERT_OK(Put("7", random_str));
|
||||
ASSERT_OK(Put("bbb", "v"));
|
||||
ASSERT_OK(Put("ccc", "v"));
|
||||
ASSERT_OK(Put("ddd", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("eee", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// These two keys will be kept in memtable.
|
||||
ASSERT_OK(Put("0", random_str));
|
||||
ASSERT_OK(Put("8", random_str));
|
||||
std::string ub1 = "e";
|
||||
std::string ub2 = "c";
|
||||
Slice ub(ub1);
|
||||
ReadOptions read_opts1;
|
||||
read_opts1.iterate_upper_bound = &ub;
|
||||
Iterator* iter = NewIterator(read_opts1);
|
||||
// Seek and iterate accross block boundary.
|
||||
iter->Seek("b");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bbb", iter->key());
|
||||
ub = Slice(ub2);
|
||||
iter->Seek("b");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bbb", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
|
||||
int num_iter_wrapper_seek = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"IteratorWrapper::Seek:0",
|
||||
[&](void* /*arg*/) { num_iter_wrapper_seek++; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ReadOptions()));
|
||||
iter->Seek("1");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
// DBIter always wraps internal iterator with IteratorWrapper,
|
||||
// and in merging iterator each child iterator will be wrapped
|
||||
// with IteratorWrapper.
|
||||
ASSERT_EQ(4, num_iter_wrapper_seek);
|
||||
std::string lb1 = "a";
|
||||
std::string lb2 = "c";
|
||||
Slice lb(lb1);
|
||||
ReadOptions read_opts2;
|
||||
read_opts2.iterate_lower_bound = &lb;
|
||||
iter = NewIterator(read_opts2);
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("ccc", iter->key());
|
||||
lb = Slice(lb2);
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("ccc", iter->key());
|
||||
iter->Prev();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
}
|
||||
|
||||
// child position: 1 and 5
|
||||
num_iter_wrapper_seek = 0;
|
||||
iter->Seek("2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(3, num_iter_wrapper_seek);
|
||||
|
||||
// child position: 2 and 5
|
||||
num_iter_wrapper_seek = 0;
|
||||
iter->Seek("6");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(4, num_iter_wrapper_seek);
|
||||
|
||||
// child position: 8 and 6
|
||||
num_iter_wrapper_seek = 0;
|
||||
iter->Seek("7");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(3, num_iter_wrapper_seek);
|
||||
|
||||
// child position: 8 and 7
|
||||
num_iter_wrapper_seek = 0;
|
||||
iter->Seek("5");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(4, num_iter_wrapper_seek);
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
TEST_P(DBIteratorTest, IterateWithLowerBoundAcrossFileBoundary) {
|
||||
ASSERT_OK(Put("aaa", "v"));
|
||||
ASSERT_OK(Put("bbb", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("ccc", "v"));
|
||||
ASSERT_OK(Put("ddd", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
// Move both files to bottom level.
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
Slice lower_bound("b");
|
||||
ReadOptions read_opts;
|
||||
read_opts.iterate_lower_bound = &lower_bound;
|
||||
std::unique_ptr<Iterator> iter(NewIterator(read_opts));
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("ccc", iter->key());
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bbb", iter->key());
|
||||
iter->Prev();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBIteratorTestInstance, DBIteratorTest,
|
||||
|
||||
@@ -322,7 +322,7 @@ TEST_F(DBMemTableTest, ColumnFamilyId) {
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
for (int cf = 0; cf < 2; ++cf) {
|
||||
for (uint32_t cf = 0; cf < 2; ++cf) {
|
||||
ASSERT_OK(Put(cf, "key", "val"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_EQ(
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
// Copyright (c) 2018-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_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "table/block_based/block_builder.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "test_util/sync_point.h"
|
||||
#endif
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "utilities/merge_operators/sortlist.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend2.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBMergeOperandTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperandTest() : DBTestBase("/db_merge_operand_test") {}
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
class LimitedStringAppendMergeOp : public StringAppendTESTOperator {
|
||||
public:
|
||||
LimitedStringAppendMergeOp(int limit, char delim)
|
||||
: StringAppendTESTOperator(delim), limit_(limit) {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "DBMergeOperatorTest::LimitedStringAppendMergeOp";
|
||||
}
|
||||
|
||||
bool ShouldMerge(const std::vector<Slice>& operands) const override {
|
||||
if (operands.size() > 0 && limit_ > 0 && operands.size() >= limit_) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t limit_ = 0;
|
||||
};
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
std::vector<PinnableSlice> values(num_records);
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = num_records;
|
||||
|
||||
// k0 value in memtable
|
||||
Put("k0", "PutARock");
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k0",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "PutARock");
|
||||
|
||||
// k0.1 value in SST
|
||||
Put("k0.1", "RockInSST");
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k0.1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "RockInSST");
|
||||
|
||||
// All k1 values are in memtable.
|
||||
ASSERT_OK(Merge("k1", "a"));
|
||||
Put("k1", "x");
|
||||
ASSERT_OK(Merge("k1", "b"));
|
||||
ASSERT_OK(Merge("k1", "c"));
|
||||
ASSERT_OK(Merge("k1", "d"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "x");
|
||||
ASSERT_EQ(values[1], "b");
|
||||
ASSERT_EQ(values[2], "c");
|
||||
ASSERT_EQ(values[3], "d");
|
||||
|
||||
// expected_max_number_of_operands is less than number of merge operands so
|
||||
// status should be Incomplete.
|
||||
merge_operands_info.expected_max_number_of_operands = num_records - 1;
|
||||
Status status = db_->GetMergeOperands(
|
||||
ReadOptions(), db_->DefaultColumnFamily(), "k1", values.data(),
|
||||
&merge_operands_info, &number_of_operands);
|
||||
ASSERT_EQ(status.IsIncomplete(), true);
|
||||
merge_operands_info.expected_max_number_of_operands = num_records;
|
||||
|
||||
// All k1.1 values are in memtable.
|
||||
ASSERT_OK(Merge("k1.1", "r"));
|
||||
Delete("k1.1");
|
||||
ASSERT_OK(Merge("k1.1", "c"));
|
||||
ASSERT_OK(Merge("k1.1", "k"));
|
||||
ASSERT_OK(Merge("k1.1", "s"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k1.1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "c");
|
||||
ASSERT_EQ(values[1], "k");
|
||||
ASSERT_EQ(values[2], "s");
|
||||
|
||||
// All k2 values are flushed to L0 into a single file.
|
||||
ASSERT_OK(Merge("k2", "q"));
|
||||
ASSERT_OK(Merge("k2", "w"));
|
||||
ASSERT_OK(Merge("k2", "e"));
|
||||
ASSERT_OK(Merge("k2", "r"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k2",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "q");
|
||||
ASSERT_EQ(values[1], "w");
|
||||
ASSERT_EQ(values[2], "e");
|
||||
ASSERT_EQ(values[3], "r");
|
||||
|
||||
// All k2.1 values are flushed to L0 into a single file.
|
||||
ASSERT_OK(Merge("k2.1", "m"));
|
||||
Put("k2.1", "l");
|
||||
ASSERT_OK(Merge("k2.1", "n"));
|
||||
ASSERT_OK(Merge("k2.1", "o"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k2.1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "l,n,o");
|
||||
|
||||
// All k2.2 values are flushed to L0 into a single file.
|
||||
ASSERT_OK(Merge("k2.2", "g"));
|
||||
Delete("k2.2");
|
||||
ASSERT_OK(Merge("k2.2", "o"));
|
||||
ASSERT_OK(Merge("k2.2", "t"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k2.2",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "o,t");
|
||||
|
||||
// Do some compaction that will make the following tests more predictable
|
||||
// Slice start("PutARock");
|
||||
// Slice end("t");
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
// All k3 values are flushed and are in different files.
|
||||
ASSERT_OK(Merge("k3", "ab"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "bc"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "de"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k3",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "ab");
|
||||
ASSERT_EQ(values[1], "bc");
|
||||
ASSERT_EQ(values[2], "cd");
|
||||
ASSERT_EQ(values[3], "de");
|
||||
|
||||
// All k3.1 values are flushed and are in different files.
|
||||
ASSERT_OK(Merge("k3.1", "ab"));
|
||||
ASSERT_OK(Flush());
|
||||
Put("k3.1", "bc");
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3.1", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3.1", "de"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k3.1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "bc");
|
||||
ASSERT_EQ(values[1], "cd");
|
||||
ASSERT_EQ(values[2], "de");
|
||||
|
||||
// All k3.2 values are flushed and are in different files.
|
||||
ASSERT_OK(Merge("k3.2", "ab"));
|
||||
ASSERT_OK(Flush());
|
||||
Delete("k3.2");
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3.2", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3.2", "de"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k3.2",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "cd");
|
||||
ASSERT_EQ(values[1], "de");
|
||||
|
||||
// All K4 values are in different levels
|
||||
ASSERT_OK(Merge("k4", "ba"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
ASSERT_OK(Merge("k4", "cb"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(3);
|
||||
ASSERT_OK(Merge("k4", "dc"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_OK(Merge("k4", "ed"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k4",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "ba");
|
||||
ASSERT_EQ(values[1], "cb");
|
||||
ASSERT_EQ(values[2], "dc");
|
||||
ASSERT_EQ(values[3], "ed");
|
||||
|
||||
// First 3 k5 values are in SST and next 4 k5 values are in Immutable Memtable
|
||||
ASSERT_OK(Merge("k5", "who"));
|
||||
ASSERT_OK(Merge("k5", "am"));
|
||||
ASSERT_OK(Merge("k5", "i"));
|
||||
ASSERT_OK(Flush());
|
||||
Put("k5", "remember");
|
||||
ASSERT_OK(Merge("k5", "i"));
|
||||
ASSERT_OK(Merge("k5", "am"));
|
||||
ASSERT_OK(Merge("k5", "rocks"));
|
||||
dbfull()->TEST_SwitchMemtable();
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k5",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_EQ(values[0], "remember");
|
||||
ASSERT_EQ(values[1], "i");
|
||||
ASSERT_EQ(values[2], "am");
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -46,9 +46,11 @@ class DBMergeOperatorTest : public DBTestBase {
|
||||
ReadOptions read_opt;
|
||||
read_opt.snapshot = snapshot;
|
||||
PinnableSlice value;
|
||||
Status s =
|
||||
dbfull()->GetImpl(read_opt, db_->DefaultColumnFamily(), key, &value,
|
||||
nullptr /*value_found*/, &read_callback);
|
||||
DBImpl::GetImplOptions get_impl_options;
|
||||
get_impl_options.column_family = db_->DefaultColumnFamily();
|
||||
get_impl_options.value = &value;
|
||||
get_impl_options.callback = &read_callback;
|
||||
Status s = dbfull()->GetImpl(read_opt, key, get_impl_options);
|
||||
if (!s.ok()) {
|
||||
return s.ToString();
|
||||
}
|
||||
@@ -275,68 +277,6 @@ TEST_P(MergeOperatorPinningTest, OperandsMultiBlocks) {
|
||||
VerifyDBFromMap(true_data);
|
||||
}
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, Randomized) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateMaxOperator();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::map<std::string, std::string> true_data;
|
||||
|
||||
const int kTotalMerges = 5000;
|
||||
// Every key gets ~10 operands
|
||||
const int kKeyRange = kTotalMerges / 10;
|
||||
const int kOperandSize = 20;
|
||||
const int kNumPutBefore = kKeyRange / 10; // 10% value
|
||||
const int kNumPutAfter = kKeyRange / 10; // 10% overwrite
|
||||
const int kNumDelete = kKeyRange / 10; // 10% delete
|
||||
|
||||
// kNumPutBefore keys will have base values
|
||||
for (int i = 0; i < kNumPutBefore; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
true_data[key] = value;
|
||||
}
|
||||
|
||||
// Do kTotalMerges merges
|
||||
for (int i = 0; i < kTotalMerges; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, value));
|
||||
|
||||
if (true_data[key] < value) {
|
||||
true_data[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite random kNumPutAfter keys
|
||||
for (int i = 0; i < kNumPutAfter; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
true_data[key] = value;
|
||||
}
|
||||
|
||||
// Delete random kNumDelete keys
|
||||
for (int i = 0; i < kNumDelete; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), key));
|
||||
|
||||
true_data.erase(key);
|
||||
}
|
||||
|
||||
VerifyDBFromMap(true_data);
|
||||
|
||||
} while (ChangeOptions(kSkipMergePut));
|
||||
}
|
||||
|
||||
class MergeOperatorHook : public MergeOperator {
|
||||
public:
|
||||
explicit MergeOperatorHook(std::shared_ptr<MergeOperator> _merge_op)
|
||||
@@ -637,6 +577,86 @@ TEST_F(DBMergeOperatorTest, SnapshotCheckerAndReadCallback) {
|
||||
db_->ReleaseSnapshot(snapshot2);
|
||||
}
|
||||
|
||||
class PerConfigMergeOperatorPinningTest
|
||||
: public DBMergeOperatorTest,
|
||||
public testing::WithParamInterface<std::tuple<bool, int>> {
|
||||
public:
|
||||
PerConfigMergeOperatorPinningTest() {
|
||||
std::tie(disable_block_cache_, option_config_) = GetParam();
|
||||
}
|
||||
|
||||
bool disable_block_cache_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
MergeOperatorPinningTest, PerConfigMergeOperatorPinningTest,
|
||||
::testing::Combine(::testing::Bool(),
|
||||
::testing::Range(static_cast<int>(DBTestBase::kDefault),
|
||||
static_cast<int>(DBTestBase::kEnd))));
|
||||
|
||||
TEST_P(PerConfigMergeOperatorPinningTest, Randomized) {
|
||||
if (ShouldSkipOptions(option_config_, kSkipMergePut)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateMaxOperator();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::map<std::string, std::string> true_data;
|
||||
|
||||
const int kTotalMerges = 5000;
|
||||
// Every key gets ~10 operands
|
||||
const int kKeyRange = kTotalMerges / 10;
|
||||
const int kOperandSize = 20;
|
||||
const int kNumPutBefore = kKeyRange / 10; // 10% value
|
||||
const int kNumPutAfter = kKeyRange / 10; // 10% overwrite
|
||||
const int kNumDelete = kKeyRange / 10; // 10% delete
|
||||
|
||||
// kNumPutBefore keys will have base values
|
||||
for (int i = 0; i < kNumPutBefore; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
true_data[key] = value;
|
||||
}
|
||||
|
||||
// Do kTotalMerges merges
|
||||
for (int i = 0; i < kTotalMerges; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, value));
|
||||
|
||||
if (true_data[key] < value) {
|
||||
true_data[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite random kNumPutAfter keys
|
||||
for (int i = 0; i < kNumPutAfter; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
true_data[key] = value;
|
||||
}
|
||||
|
||||
// Delete random kNumDelete keys
|
||||
for (int i = 0; i < kNumDelete; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), key));
|
||||
|
||||
true_data.erase(key);
|
||||
}
|
||||
|
||||
VerifyDBFromMap(true_data);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -505,10 +505,10 @@ TEST_F(DBOptionsTest, SetStatsDumpPeriodSec) {
|
||||
options.stats_dump_period_sec = 5;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int num = rand() % 5000 + 1;
|
||||
unsigned int num = rand() % 5000 + 1;
|
||||
ASSERT_OK(
|
||||
dbfull()->SetDBOptions({{"stats_dump_period_sec", ToString(num)}}));
|
||||
ASSERT_EQ(num, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
@@ -522,12 +522,12 @@ TEST_F(DBOptionsTest, SetOptionsStatsPersistPeriodSec) {
|
||||
options.stats_persist_period_sec = 5;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
ASSERT_EQ(5u, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "12345"}}));
|
||||
ASSERT_EQ(12345, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
ASSERT_EQ(12345u, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
ASSERT_NOK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "abcde"}}));
|
||||
ASSERT_EQ(12345, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
ASSERT_EQ(12345u, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
}
|
||||
|
||||
static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
|
||||
+16
-11
@@ -210,12 +210,11 @@ void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyTableProperties(const TableProperties& base_tp,
|
||||
const TableProperties& new_tp,
|
||||
double filter_size_bias = 0.1,
|
||||
double index_size_bias = 0.1,
|
||||
double data_size_bias = 0.1,
|
||||
double num_data_blocks_bias = 0.05) {
|
||||
void VerifyTableProperties(
|
||||
const TableProperties& base_tp, const TableProperties& new_tp,
|
||||
double filter_size_bias = CACHE_LINE_SIZE >= 256 ? 0.15 : 0.1,
|
||||
double index_size_bias = 0.1, double data_size_bias = 0.1,
|
||||
double num_data_blocks_bias = 0.05) {
|
||||
VerifySimilar(base_tp.data_size, new_tp.data_size, data_size_bias);
|
||||
VerifySimilar(base_tp.index_size, new_tp.index_size, index_size_bias);
|
||||
VerifySimilar(base_tp.filter_size, new_tp.filter_size, filter_size_bias);
|
||||
@@ -266,7 +265,8 @@ void GetExpectedTableProperties(
|
||||
// discount 1 byte as value size is not encoded in value delta encoding
|
||||
(value_delta_encoding ? 1 : 0));
|
||||
expected_tp->filter_size =
|
||||
kTableCount * (kKeysPerTable * kBloomBitsPerKey / 8);
|
||||
kTableCount * ((kKeysPerTable * kBloomBitsPerKey + 7) / 8 +
|
||||
/*average-ish overhead*/ CACHE_LINE_SIZE / 2);
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -615,8 +615,9 @@ TEST_F(DBPropertiesTest, NumImmutableMemTable) {
|
||||
writeOpt.disableWAL = true;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.max_write_buffer_number_to_maintain = 4;
|
||||
options.write_buffer_size = 1000000;
|
||||
options.max_write_buffer_size_to_maintain =
|
||||
5 * static_cast<int64_t>(options.write_buffer_size);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
std::string big_value(1000000 * 2, 'x');
|
||||
@@ -747,7 +748,7 @@ TEST_F(DBPropertiesTest, DISABLED_GetProperty) {
|
||||
options.max_background_flushes = 1;
|
||||
options.max_write_buffer_number = 10;
|
||||
options.min_write_buffer_number_to_merge = 1;
|
||||
options.max_write_buffer_number_to_maintain = 0;
|
||||
options.max_write_buffer_size_to_maintain = 0;
|
||||
options.write_buffer_size = 1000000;
|
||||
Reopen(options);
|
||||
|
||||
@@ -997,7 +998,7 @@ TEST_F(DBPropertiesTest, EstimatePendingCompBytes) {
|
||||
options.max_background_flushes = 1;
|
||||
options.max_write_buffer_number = 10;
|
||||
options.min_write_buffer_number_to_merge = 1;
|
||||
options.max_write_buffer_number_to_maintain = 0;
|
||||
options.max_write_buffer_size_to_maintain = 0;
|
||||
options.write_buffer_size = 1000000;
|
||||
Reopen(options);
|
||||
|
||||
@@ -1630,7 +1631,11 @@ TEST_F(DBPropertiesTest, BlockCacheProperties) {
|
||||
|
||||
// Test with empty block cache.
|
||||
constexpr size_t kCapacity = 100;
|
||||
auto block_cache = NewLRUCache(kCapacity, 0 /*num_shard_bits*/);
|
||||
LRUCacheOptions co;
|
||||
co.capacity = kCapacity;
|
||||
co.num_shard_bits = 0;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
auto block_cache = NewLRUCache(co);
|
||||
table_options.block_cache = block_cache;
|
||||
table_options.no_block_cache = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
@@ -470,6 +470,111 @@ TEST_F(DBSSTTest, RateLimitedWALDelete) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
class DBWALTestWithParam
|
||||
: public DBSSTTest,
|
||||
public testing::WithParamInterface<std::tuple<std::string, bool>> {
|
||||
public:
|
||||
DBWALTestWithParam() {
|
||||
wal_dir_ = std::get<0>(GetParam());
|
||||
wal_dir_same_as_dbname_ = std::get<1>(GetParam());
|
||||
}
|
||||
|
||||
std::string wal_dir_;
|
||||
bool wal_dir_same_as_dbname_;
|
||||
};
|
||||
|
||||
TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
|
||||
class MyEnv : public EnvWrapper {
|
||||
public:
|
||||
MyEnv(Env* t) : EnvWrapper(t), fake_log_delete(false) {}
|
||||
|
||||
Status DeleteFile(const std::string& fname) {
|
||||
if (fname.find(".log.trash") != std::string::npos && fake_log_delete) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
void set_fake_log_delete(bool fake) { fake_log_delete = fake; }
|
||||
|
||||
private:
|
||||
bool fake_log_delete;
|
||||
};
|
||||
|
||||
std::unique_ptr<MyEnv> env(new MyEnv(Env::Default()));
|
||||
Destroy(last_options_);
|
||||
|
||||
env->set_fake_log_delete(true);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
options.env = env.get();
|
||||
options.wal_dir = dbname_ + wal_dir_;
|
||||
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
Status s;
|
||||
options.sst_file_manager.reset(
|
||||
NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
|
||||
ASSERT_OK(s);
|
||||
options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
// Create 4 files in L0
|
||||
for (char v = 'a'; v <= 'd'; v++) {
|
||||
ASSERT_OK(Put("Key2", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key3", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key4", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key1", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key4", DummyString(1024, v)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
// We created 4 sst files in L0
|
||||
ASSERT_EQ("4", FilesPerLevel(0));
|
||||
|
||||
Close();
|
||||
|
||||
options.sst_file_manager.reset();
|
||||
std::vector<std::string> filenames;
|
||||
int trash_log_count = 0;
|
||||
if (!wal_dir_same_as_dbname_) {
|
||||
// Forcibly create some trash log files
|
||||
std::unique_ptr<WritableFile> result;
|
||||
env->NewWritableFile(options.wal_dir + "/1000.log.trash", &result,
|
||||
EnvOptions());
|
||||
result.reset();
|
||||
}
|
||||
env->GetChildren(options.wal_dir, &filenames);
|
||||
for (const std::string& fname : filenames) {
|
||||
if (fname.find(".log.trash") != std::string::npos) {
|
||||
trash_log_count++;
|
||||
}
|
||||
}
|
||||
ASSERT_GE(trash_log_count, 1);
|
||||
|
||||
env->set_fake_log_delete(false);
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
filenames.clear();
|
||||
trash_log_count = 0;
|
||||
env->GetChildren(options.wal_dir, &filenames);
|
||||
for (const std::string& fname : filenames) {
|
||||
if (fname.find(".log.trash") != std::string::npos) {
|
||||
trash_log_count++;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(trash_log_count, 0);
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBWALTestWithParam, DBWALTestWithParam,
|
||||
::testing::Values(std::make_tuple("", true),
|
||||
std::make_tuple("_wal_dir", false)));
|
||||
|
||||
TEST_F(DBSSTTest, OpenDBWithExistingTrash) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ TEST_F(DBTablePropertiesTest, GetColumnFamilyNameProperty) {
|
||||
|
||||
// Create one table per CF, then verify it was created with the column family
|
||||
// name property.
|
||||
for (int cf = 0; cf < 2; ++cf) {
|
||||
for (uint32_t cf = 0; cf < 2; ++cf) {
|
||||
Put(cf, "key", "val");
|
||||
Flush(cf);
|
||||
|
||||
|
||||
+152
-28
@@ -62,7 +62,6 @@
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -883,7 +882,7 @@ TEST_F(DBTest, FlushMultipleMemtable) {
|
||||
writeOpt.disableWAL = true;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.max_write_buffer_number_to_maintain = -1;
|
||||
options.max_write_buffer_size_to_maintain = -1;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v1"));
|
||||
ASSERT_OK(Flush(1));
|
||||
@@ -901,7 +900,8 @@ TEST_F(DBTest, FlushSchedule) {
|
||||
options.level0_stop_writes_trigger = 1 << 10;
|
||||
options.level0_slowdown_writes_trigger = 1 << 10;
|
||||
options.min_write_buffer_number_to_merge = 1;
|
||||
options.max_write_buffer_number_to_maintain = 1;
|
||||
options.max_write_buffer_size_to_maintain =
|
||||
static_cast<int64_t>(options.write_buffer_size);
|
||||
options.max_write_buffer_number = 2;
|
||||
options.write_buffer_size = 120 * 1024;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -1257,6 +1257,7 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
auto default_cf = db_->DefaultColumnFamily();
|
||||
|
||||
const int N = 128;
|
||||
Random rnd(301);
|
||||
@@ -1268,9 +1269,10 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
std::string start = Key(50);
|
||||
std::string end = Key(60);
|
||||
Range r(start, end);
|
||||
uint8_t include_both = DB::SizeApproximationFlags::INCLUDE_FILES |
|
||||
DB::SizeApproximationFlags::INCLUDE_MEMTABLES;
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = true;
|
||||
size_approx_options.include_files = true;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_GT(size, 6000);
|
||||
ASSERT_LT(size, 204800);
|
||||
// Zero if not including mem table
|
||||
@@ -1280,7 +1282,7 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -1290,19 +1292,20 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(100);
|
||||
end = Key(1020);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_GT(size, 6000);
|
||||
|
||||
options.max_write_buffer_number = 8;
|
||||
options.min_write_buffer_number_to_merge = 5;
|
||||
options.write_buffer_size = 1024 * N; // Not very large
|
||||
DestroyAndReopen(options);
|
||||
default_cf = db_->DefaultColumnFamily();
|
||||
|
||||
int keys[N * 3];
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -1319,26 +1322,27 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(100);
|
||||
end = Key(300);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_GT(size, 6000);
|
||||
|
||||
start = Key(2100);
|
||||
end = Key(2300);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
uint64_t size_with_mt, size_without_mt;
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, include_both);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1,
|
||||
&size_with_mt);
|
||||
ASSERT_GT(size_with_mt, 6000);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt);
|
||||
ASSERT_EQ(size_without_mt, 0);
|
||||
@@ -1352,10 +1356,80 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, include_both);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1,
|
||||
&size_with_mt);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt);
|
||||
ASSERT_GT(size_with_mt, size_without_mt);
|
||||
ASSERT_GT(size_without_mt, 6000);
|
||||
|
||||
// Check that include_memtabtles flag works as expected
|
||||
size_approx_options.include_memtabtles = false;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_EQ(size, size_without_mt);
|
||||
|
||||
// Check that files_size_error_margin works as expected, when the heuristic
|
||||
// conditions are not met
|
||||
start = Key(1);
|
||||
end = Key(1000 + N - 2);
|
||||
r = Range(start, end);
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
uint64_t size2;
|
||||
size_approx_options.files_size_error_margin = 0.5; // enabled, but not used
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_EQ(size, size2);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 1024 * 1024;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.target_file_size_base = 1024 * 1024;
|
||||
DestroyAndReopen(options);
|
||||
const auto default_cf = db_->DefaultColumnFamily();
|
||||
|
||||
const int N = 64000;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
// Flush everything to files
|
||||
Flush();
|
||||
// Compact the entire key space into the next level
|
||||
db_->CompactRange(CompactRangeOptions(), default_cf, nullptr, nullptr);
|
||||
|
||||
// Write more keys
|
||||
for (int i = N; i < (N + N / 4); i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
// Flush everything to files again
|
||||
Flush();
|
||||
|
||||
// Wait for compaction to finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
const std::string start = Key(0);
|
||||
const std::string end = Key(2 * N);
|
||||
const Range r(start, end);
|
||||
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = false;
|
||||
size_approx_options.include_files = true;
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
|
||||
// Get the precise size without any approximation heuristic
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_NE(size, 0);
|
||||
|
||||
// Get the size with an approximation heuristic
|
||||
uint64_t size2;
|
||||
const double error_margin = 0.2;
|
||||
size_approx_options.files_size_error_margin = error_margin;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_LT(size2, size * (1 + error_margin));
|
||||
ASSERT_GT(size2, size * (1 - error_margin));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetApproximateMemTableStats) {
|
||||
@@ -2466,6 +2540,15 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported(key);
|
||||
}
|
||||
|
||||
using DB::GetMergeOperands;
|
||||
virtual Status GetMergeOperands(
|
||||
const ReadOptions& /*options*/, ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& key, PinnableSlice* /*slice*/,
|
||||
GetMergeOperandsOptions* /*merge_operands_options*/,
|
||||
int* /*number_of_operands*/) override {
|
||||
return Status::NotSupported(key);
|
||||
}
|
||||
|
||||
using DB::MultiGet;
|
||||
std::vector<Status> MultiGet(
|
||||
const ReadOptions& /*options*/,
|
||||
@@ -2492,7 +2575,18 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported("Not implemented");
|
||||
}
|
||||
|
||||
Status VerifyChecksum() override {
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
using DB::VerifyChecksum;
|
||||
Status VerifyChecksum(const ReadOptions&) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
@@ -2588,13 +2682,14 @@ class ModelDB : public DB {
|
||||
return false;
|
||||
}
|
||||
using DB::GetApproximateSizes;
|
||||
void GetApproximateSizes(ColumnFamilyHandle* /*column_family*/,
|
||||
const Range* /*range*/, int n, uint64_t* sizes,
|
||||
uint8_t /*include_flags*/
|
||||
= INCLUDE_FILES) override {
|
||||
Status GetApproximateSizes(const SizeApproximationOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Range* /*range*/, int n,
|
||||
uint64_t* sizes) override {
|
||||
for (int i = 0; i < n; i++) {
|
||||
sizes[i] = 0;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
using DB::GetApproximateMemTableStats;
|
||||
void GetApproximateMemTableStats(ColumnFamilyHandle* /*column_family*/,
|
||||
@@ -2642,6 +2737,10 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
void EnableManualCompaction() override { return; }
|
||||
|
||||
void DisableManualCompaction() override { return; }
|
||||
|
||||
using DB::NumberLevels;
|
||||
int NumberLevels(ColumnFamilyHandle* /*column_family*/) override { return 1; }
|
||||
|
||||
@@ -2694,6 +2793,11 @@ class ModelDB : public DB {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetCurrentWalFile(
|
||||
std::unique_ptr<LogFile>* /*current_log_file*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DeleteFile(std::string /*name*/) override { return Status::OK(); }
|
||||
|
||||
Status GetUpdatesSince(
|
||||
@@ -4690,15 +4794,15 @@ TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Initial defaults
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 1);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 1U);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.min_merge_width,
|
||||
2);
|
||||
2u);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_merge_width,
|
||||
UINT_MAX);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
200);
|
||||
200u);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.compression_size_percent,
|
||||
@@ -4711,15 +4815,15 @@ TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_universal", "{size_ratio=7;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 7);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 7u);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.min_merge_width,
|
||||
2);
|
||||
2u);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_merge_width,
|
||||
UINT_MAX);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
200);
|
||||
200u);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.compression_size_percent,
|
||||
@@ -4732,15 +4836,15 @@ TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_universal", "{min_merge_width=11;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 7);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 7u);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.min_merge_width,
|
||||
11);
|
||||
11u);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_merge_width,
|
||||
UINT_MAX);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
200);
|
||||
200u);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.compression_size_percent,
|
||||
@@ -6156,10 +6260,30 @@ TEST_F(DBTest, ThreadLocalPtrDeadlock) {
|
||||
fprintf(stderr, "Done. Flushed %d times, destroyed %d threads\n",
|
||||
flushes_done.load(), threads_destroyed.load());
|
||||
}
|
||||
|
||||
TEST_F(DBTest, LargeBlockSizeTest) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(Put(0, "foo", "bar"));
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 8LL * 1024 * 1024 * 1024LL;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
extern "C" {
|
||||
void RegisterCustomObjects(int argc, char** argv);
|
||||
}
|
||||
#else
|
||||
void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
|
||||
#endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
+375
-4
@@ -16,6 +16,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/persistent_cache.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -1941,7 +1942,10 @@ TEST_F(DBTest2, TestPerfContextIterCpuTime) {
|
||||
}
|
||||
#endif // OS_LINUX
|
||||
|
||||
#ifndef OS_SOLARIS // GetUniqueIdFromFile is not implemented
|
||||
// GetUniqueIdFromFile is not implemented on these platforms. Persistent cache
|
||||
// breaks when that function is not implemented and no regular block cache is
|
||||
// provided.
|
||||
#if !defined(OS_SOLARIS) && !defined(OS_WIN)
|
||||
TEST_F(DBTest2, PersistentCache) {
|
||||
int num_iter = 80;
|
||||
|
||||
@@ -2005,7 +2009,7 @@ TEST_F(DBTest2, PersistentCache) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !OS_SOLARIS
|
||||
#endif // !defined(OS_SOLARIS) && !defined(OS_WIN)
|
||||
|
||||
namespace {
|
||||
void CountSyncPoint() {
|
||||
@@ -2402,6 +2406,215 @@ TEST_F(DBTest2, ManualCompactionOverlapManualCompaction) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PausingManualCompaction1) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
|
||||
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());
|
||||
|
||||
// Generate another file containing same keys
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
int manual_compactions_paused = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:1", [&](void* arg) {
|
||||
auto paused = reinterpret_cast<std::atomic<bool>*>(arg);
|
||||
ASSERT_FALSE(paused->load(std::memory_order_acquire));
|
||||
paused->store(true, std::memory_order_release);
|
||||
manual_compactions_paused += 1;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::string> files_before_compact, files_after_compact;
|
||||
// Remember file name before compaction is triggered
|
||||
std::vector<LiveFileMetaData> files_meta;
|
||||
dbfull()->GetLiveFilesMetaData(&files_meta);
|
||||
for (auto file : files_meta) {
|
||||
files_before_compact.push_back(file.name);
|
||||
}
|
||||
|
||||
// OK, now trigger a manual compaction
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
// Wait for compactions to get scheduled and stopped
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
// Get file names after compaction is stopped
|
||||
files_meta.clear();
|
||||
dbfull()->GetLiveFilesMetaData(&files_meta);
|
||||
for (auto file : files_meta) {
|
||||
files_after_compact.push_back(file.name);
|
||||
}
|
||||
|
||||
// Like nothing happened
|
||||
ASSERT_EQ(files_before_compact, files_after_compact);
|
||||
ASSERT_EQ(manual_compactions_paused, 1);
|
||||
|
||||
manual_compactions_paused = 0;
|
||||
// Now make sure CompactFiles also not run
|
||||
dbfull()->CompactFiles(rocksdb::CompactionOptions(), files_before_compact, 0);
|
||||
// Wait for manual compaction to get scheduled and finish
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
files_meta.clear();
|
||||
files_after_compact.clear();
|
||||
dbfull()->GetLiveFilesMetaData(&files_meta);
|
||||
for (auto file : files_meta) {
|
||||
files_after_compact.push_back(file.name);
|
||||
}
|
||||
|
||||
ASSERT_EQ(files_before_compact, files_after_compact);
|
||||
// CompactFiles returns at entry point
|
||||
ASSERT_EQ(manual_compactions_paused, 0);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// PausingManualCompaction does not affect auto compaction
|
||||
TEST_F(DBTest2, PausingManualCompaction2) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.disable_auto_compactions = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
dbfull()->DisableManualCompaction();
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Generate a file containing 10 keys.
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
|
||||
std::vector<LiveFileMetaData> files_meta;
|
||||
dbfull()->GetLiveFilesMetaData(&files_meta);
|
||||
ASSERT_EQ(files_meta.size(), 1);
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PausingManualCompaction3) {
|
||||
CompactRangeOptions compact_options;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
|
||||
Random rnd(301);
|
||||
auto generate_files = [&]() {
|
||||
for (int i = 0; i < options.num_levels; i++) {
|
||||
for (int j = 0; j < options.num_levels - i + 1; j++) {
|
||||
for (int k = 0; k < 1000; k++) {
|
||||
ASSERT_OK(Put(Key(k + j * 1000), RandomString(&rnd, 50)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
for (int l = 1; l < options.num_levels - i; l++) {
|
||||
MoveFilesToLevel(l);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DestroyAndReopen(options);
|
||||
generate_files();
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
int run_manual_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
[&](void* /*arg*/) { run_manual_compactions++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
dbfull()->DisableManualCompaction();
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
// As manual compaction disabled, not even reach sync point
|
||||
ASSERT_EQ(run_manual_compactions, 0);
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:1");
|
||||
dbfull()->EnableManualCompaction();
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PausingManualCompaction4) {
|
||||
CompactRangeOptions compact_options;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
|
||||
Random rnd(301);
|
||||
auto generate_files = [&]() {
|
||||
for (int i = 0; i < options.num_levels; i++) {
|
||||
for (int j = 0; j < options.num_levels - i + 1; j++) {
|
||||
for (int k = 0; k < 1000; k++) {
|
||||
ASSERT_OK(Put(Key(k + j * 1000), RandomString(&rnd, 50)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
for (int l = 1; l < options.num_levels - i; l++) {
|
||||
MoveFilesToLevel(l);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DestroyAndReopen(options);
|
||||
generate_files();
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
int run_manual_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:2", [&](void* arg) {
|
||||
auto paused = reinterpret_cast<std::atomic<bool>*>(arg);
|
||||
ASSERT_FALSE(paused->load(std::memory_order_acquire));
|
||||
paused->store(true, std::memory_order_release);
|
||||
run_manual_compactions++;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
dbfull()->EnableManualCompaction();
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
ASSERT_EQ(run_manual_compactions, 1);
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:2");
|
||||
dbfull()->EnableManualCompaction();
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, OptimizeForPointLookup) {
|
||||
Options options = CurrentOptions();
|
||||
Close();
|
||||
@@ -2797,8 +3010,12 @@ TEST_F(DBTest2, ReadCallbackTest) {
|
||||
ReadOptions roptions;
|
||||
TestReadCallback callback(seq);
|
||||
bool dont_care = true;
|
||||
Status s = dbfull()->GetImpl(roptions, dbfull()->DefaultColumnFamily(), key,
|
||||
&pinnable_val, &dont_care, &callback);
|
||||
DBImpl::GetImplOptions get_impl_options;
|
||||
get_impl_options.column_family = dbfull()->DefaultColumnFamily();
|
||||
get_impl_options.value = &pinnable_val;
|
||||
get_impl_options.value_found = &dont_care;
|
||||
get_impl_options.callback = &callback;
|
||||
Status s = dbfull()->GetImpl(roptions, key, get_impl_options);
|
||||
ASSERT_TRUE(s.ok());
|
||||
// Assuming that after each Put the DB increased seq by one, the value and
|
||||
// seq number must be equal since we also inc value by 1 after each Put.
|
||||
@@ -3771,10 +3988,164 @@ TEST_F(DBTest2, CloseWithUnreleasedSnapshot) {
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PrefixBloomReseek) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(3));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Construct two L1 files with keys:
|
||||
// f1:[aaa1 ccc1] f2:[ddd0]
|
||||
ASSERT_OK(Put("aaa1", ""));
|
||||
ASSERT_OK(Put("ccc1", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("ddd0", ""));
|
||||
ASSERT_OK(Flush());
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kSkip;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(Put("bbb1", ""));
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
|
||||
// Seeking into f1, the iterator will check bloom filter which returns the
|
||||
// file iterator ot be invalidate, and the cursor will put into f2, with
|
||||
// the next key to be "ddd0".
|
||||
iter->Seek("bbb1");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("bbb1", iter->key().ToString());
|
||||
|
||||
// Reseek ccc1, the L1 iterator needs to go back to f1 and reseek.
|
||||
iter->Seek("ccc1");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ("ccc1", iter->key().ToString());
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, RowCacheSnapshot) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.row_cache = NewLRUCache(8 * 8192);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "bar1"));
|
||||
|
||||
const Snapshot* s1 = db_->GetSnapshot();
|
||||
|
||||
ASSERT_OK(Put("foo", "bar2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("foo2", "bar"));
|
||||
const Snapshot* s2 = db_->GetSnapshot();
|
||||
ASSERT_OK(Put("foo3", "bar"));
|
||||
const Snapshot* s3 = db_->GetSnapshot();
|
||||
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 0);
|
||||
ASSERT_EQ(Get("foo"), "bar2");
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 1);
|
||||
ASSERT_EQ(Get("foo"), "bar2");
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 1);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 1);
|
||||
ASSERT_EQ(Get("foo", s1), "bar1");
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 1);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
|
||||
ASSERT_EQ(Get("foo", s2), "bar2");
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
|
||||
ASSERT_EQ(Get("foo", s1), "bar1");
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
|
||||
ASSERT_EQ(Get("foo", s3), "bar2");
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_HIT), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ROW_CACHE_MISS), 2);
|
||||
|
||||
db_->ReleaseSnapshot(s1);
|
||||
db_->ReleaseSnapshot(s2);
|
||||
db_->ReleaseSnapshot(s3);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Disabled but the test is failing.
|
||||
// When DB is reopened with multiple column families, the manifest file
|
||||
// is written after the first CF is flushed, and it is written again
|
||||
// after each flush. If DB crashes between the flushes, the flushed CF
|
||||
// flushed will pass the latest log file, and now we require it not
|
||||
// to be corrupted, and triggering a corruption report.
|
||||
// We need to fix the bug and enable the test.
|
||||
TEST_F(DBTest2, DISABLED_CrashInRecoveryMultipleCF) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
// The value is large enough to be divided to two blocks.
|
||||
std::string large_value(400, ' ');
|
||||
ASSERT_OK(Put("foo1", large_value));
|
||||
ASSERT_OK(Put("foo2", large_value));
|
||||
Close();
|
||||
|
||||
// Corrupt the log file in the middle, so that it is not corrupted
|
||||
// in the tail.
|
||||
std::vector<std::string> filenames;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &filenames));
|
||||
for (const auto& f : filenames) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(f, &number, &type) && type == FileType::kLogFile) {
|
||||
std::string fname = dbname_ + "/" + f;
|
||||
std::string file_content;
|
||||
ASSERT_OK(ReadFileToString(env_, fname, &file_content));
|
||||
file_content[400] = 'h';
|
||||
file_content[401] = 'a';
|
||||
ASSERT_OK(WriteStringToFile(env_, file_content, fname));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reopen and freeze the file system after the first manifest write.
|
||||
FaultInjectionTestEnv fit_env(options.env);
|
||||
options.env = &fit_env;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RecoverLogFiles:AfterLogAndApply",
|
||||
[&](void* /*arg*/) { fit_env.SetFilesystemActive(false); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
fit_env.SetFilesystemActive(true);
|
||||
// If we continue using failure ingestion Env, it will conplain something
|
||||
// when renaming current file, which is not expected. Need to investigate why.
|
||||
options.env = env_;
|
||||
ASSERT_OK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
extern "C" {
|
||||
void RegisterCustomObjects(int argc, char** argv);
|
||||
}
|
||||
#else
|
||||
void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
|
||||
#endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
+26
-14
@@ -10,6 +10,7 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/forward_iterator.h"
|
||||
#include "rocksdb/env_encryption.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -47,20 +48,30 @@ ROT13BlockCipher rot13Cipher_(16);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
DBTestBase::DBTestBase(const std::string path)
|
||||
: mem_env_(!getenv("MEM_ENV") ? nullptr : new MockEnv(Env::Default())),
|
||||
: mem_env_(nullptr), encrypted_env_(nullptr), option_config_(kDefault) {
|
||||
Env* base_env = Env::Default();
|
||||
#ifndef ROCKSDB_LITE
|
||||
encrypted_env_(
|
||||
!getenv("ENCRYPTED_ENV")
|
||||
? nullptr
|
||||
: NewEncryptedEnv(mem_env_ ? mem_env_ : Env::Default(),
|
||||
new CTREncryptionProvider(rot13Cipher_))),
|
||||
#else
|
||||
encrypted_env_(nullptr),
|
||||
#endif // ROCKSDB_LITE
|
||||
env_(new SpecialEnv(encrypted_env_
|
||||
? encrypted_env_
|
||||
: (mem_env_ ? mem_env_ : Env::Default()))),
|
||||
option_config_(kDefault) {
|
||||
const char* test_env_uri = getenv("TEST_ENV_URI");
|
||||
if (test_env_uri) {
|
||||
Status s = ObjectRegistry::NewInstance()->NewSharedObject(test_env_uri,
|
||||
&env_guard_);
|
||||
base_env = env_guard_.get();
|
||||
EXPECT_OK(s);
|
||||
EXPECT_NE(Env::Default(), base_env);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
EXPECT_NE(nullptr, base_env);
|
||||
if (getenv("MEM_ENV")) {
|
||||
mem_env_ = new MockEnv(base_env);
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (getenv("ENCRYPTED_ENV")) {
|
||||
encrypted_env_ = NewEncryptedEnv(mem_env_ ? mem_env_ : base_env,
|
||||
new CTREncryptionProvider(rot13Cipher_));
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
env_ = new SpecialEnv(encrypted_env_ ? encrypted_env_
|
||||
: (mem_env_ ? mem_env_ : base_env));
|
||||
env_->SetBackgroundThreads(1, Env::LOW);
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
dbname_ = test::PerThreadDBPath(env_, path);
|
||||
@@ -573,7 +584,8 @@ void DBTestBase::CreateColumnFamilies(const std::vector<std::string>& cfs,
|
||||
size_t cfi = handles_.size();
|
||||
handles_.resize(cfi + cfs.size());
|
||||
for (auto cf : cfs) {
|
||||
ASSERT_OK(db_->CreateColumnFamily(cf_opts, cf, &handles_[cfi++]));
|
||||
Status s = db_->CreateColumnFamily(cf_opts, cf, &handles_[cfi++]);
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -702,6 +702,7 @@ class DBTestBase : public testing::Test {
|
||||
MockEnv* mem_env_;
|
||||
Env* encrypted_env_;
|
||||
SpecialEnv* env_;
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
DB* db_;
|
||||
std::vector<ColumnFamilyHandle*> handles_;
|
||||
|
||||
|
||||
@@ -397,7 +397,7 @@ TEST_P(DBTestUniversalCompaction, DynamicUniversalCompactionSizeAmplification) {
|
||||
int total_picked_compactions = 0;
|
||||
int total_size_amp_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
if (arg) {
|
||||
total_picked_compactions++;
|
||||
Compaction* c = static_cast<Compaction*>(arg);
|
||||
@@ -441,18 +441,18 @@ TEST_P(DBTestUniversalCompaction, DynamicUniversalCompactionSizeAmplification) {
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions(handles_[1])
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
200);
|
||||
200U);
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[1],
|
||||
{{"compaction_options_universal",
|
||||
"{max_size_amplification_percent=110;}"}}));
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions(handles_[1])
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
110);
|
||||
110u);
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[1],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(110, mutable_cf_options.compaction_options_universal
|
||||
.max_size_amplification_percent);
|
||||
ASSERT_EQ(110u, mutable_cf_options.compaction_options_universal
|
||||
.max_size_amplification_percent);
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Verify that size amplification did happen
|
||||
@@ -478,7 +478,7 @@ TEST_P(DBTestUniversalCompaction, DynamicUniversalCompactionReadAmplification) {
|
||||
int total_picked_compactions = 0;
|
||||
int total_size_ratio_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
if (arg) {
|
||||
total_picked_compactions++;
|
||||
Compaction* c = static_cast<Compaction*>(arg);
|
||||
@@ -522,20 +522,22 @@ TEST_P(DBTestUniversalCompaction, DynamicUniversalCompactionReadAmplification) {
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions(handles_[1])
|
||||
.compaction_options_universal.min_merge_width,
|
||||
2);
|
||||
2u);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions(handles_[1])
|
||||
.compaction_options_universal.max_merge_width,
|
||||
2);
|
||||
2u);
|
||||
ASSERT_EQ(
|
||||
dbfull()->GetOptions(handles_[1]).compaction_options_universal.size_ratio,
|
||||
100);
|
||||
100u);
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[1],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.size_ratio, 100);
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.min_merge_width, 2);
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.max_merge_width, 2);
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.size_ratio, 100u);
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.min_merge_width,
|
||||
2u);
|
||||
ASSERT_EQ(mutable_cf_options.compaction_options_universal.max_merge_width,
|
||||
2u);
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
@@ -837,14 +839,14 @@ TEST_P(DBTestUniversalCompactionParallel, PickByFileNumberBug) {
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBTestUniversalCompactionParallel::PickByFileNumberBug:0",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"UniversalCompactionPicker::PickCompaction:Return",
|
||||
{"UniversalCompactionBuilder::PickCompaction:Return",
|
||||
"DBTestUniversalCompactionParallel::PickByFileNumberBug:1"},
|
||||
{"DBTestUniversalCompactionParallel::PickByFileNumberBug:2",
|
||||
"CompactionJob::Run():Start"}});
|
||||
|
||||
int total_picked_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
|
||||
if (arg) {
|
||||
total_picked_compactions++;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user