mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9adcef7ef8 | ||
|
|
664ad1c001 | ||
|
|
0400fb91e6 | ||
|
|
dd81eae663 | ||
|
|
61720850f6 | ||
|
|
e86f6eea0a | ||
|
|
b1845f43e7 | ||
|
|
958b4c9117 | ||
|
|
385412b6c0 | ||
|
|
e38e83169a | ||
|
|
f2a0fa2640 | ||
|
|
5de4fff167 | ||
|
|
9f33284d23 | ||
|
|
fb6f3b6771 | ||
|
|
b4242884d8 | ||
|
|
96687f030c | ||
|
|
3327adcc72 | ||
|
|
f63f26f842 | ||
|
|
46e923f671 | ||
|
|
b623dca531 | ||
|
|
6189230202 | ||
|
|
10a12b2a6f | ||
|
|
915c90ec0e | ||
|
|
b27c79a010 | ||
|
|
70f7fc3250 | ||
|
|
2955407c3a | ||
|
|
6e0597951e | ||
|
|
8c1f5c254f | ||
|
|
0ccaba2a05 | ||
|
|
4646cd45c4 | ||
|
|
2d6abcade4 | ||
|
|
0b129d1f7d | ||
|
|
58ef1ca899 | ||
|
|
13712712de | ||
|
|
ab8129ab8a | ||
|
|
87f35fbd3e | ||
|
|
9e58371114 | ||
|
|
1d4dc5eb22 | ||
|
|
b54951489c | ||
|
|
e8727ff6e0 | ||
|
|
a6e1cf9d20 | ||
|
|
ded1d5a1af | ||
|
|
dc0bbf78f7 | ||
|
|
b634fd7162 | ||
|
|
22f277e034 | ||
|
|
7e62c5d67a | ||
|
|
2014cdf2d0 | ||
|
|
459e00b365 | ||
|
|
49412d93e2 | ||
|
|
5a8e732594 | ||
|
|
f2e68d2bec | ||
|
|
b07369836e | ||
|
|
f3dc93bcf1 | ||
|
|
30a6d4e3ed | ||
|
|
6d29d8b3fa | ||
|
|
1530f38baa | ||
|
|
2b621c1f83 | ||
|
|
71acb4c122 | ||
|
|
ebbce5b10d | ||
|
|
fa586740e3 | ||
|
|
4623a5521f | ||
|
|
6df24fcffc | ||
|
|
8a1c34903c | ||
|
|
48fc484950 | ||
|
|
1265ed7abb | ||
|
|
95c5e2dc6e | ||
|
|
8d7edd5908 | ||
|
|
b6f6b73a9c | ||
|
|
415be221cb | ||
|
|
3eab41d7c4 | ||
|
|
a22ed4eab1 | ||
|
|
9f2cc59ec5 | ||
|
|
10d7546961 | ||
|
|
ba7da434ae | ||
|
|
6257837d83 | ||
|
|
6a6723ee1e | ||
|
|
6a8d5c015b | ||
|
|
8f47a97512 | ||
|
|
9300ef5455 | ||
|
|
1f8b119ed6 | ||
|
|
97ec8a1349 | ||
|
|
20778f2f92 | ||
|
|
7124268a09 | ||
|
|
04abb2b2dd | ||
|
|
61730186df | ||
|
|
360e9960f8 | ||
|
|
69a5e6461f | ||
|
|
9690653db5 | ||
|
|
dd8f9e38e9 | ||
|
|
c2954f9b6f | ||
|
|
72fc1e9d07 | ||
|
|
ff97287016 | ||
|
|
9e72939029 | ||
|
|
343b59d6ee | ||
|
|
1d068f6067 | ||
|
|
df6f5a3772 | ||
|
|
107c5f6a60 | ||
|
|
d2dce5611a | ||
|
|
c50e3750dc | ||
|
|
ce64b8b719 | ||
|
|
02799ad77a | ||
|
|
e2a7b202c3 | ||
|
|
af256eb2b7 | ||
|
|
e5a1372b24 | ||
|
|
d659faad54 | ||
|
|
a12306fab7 | ||
|
|
43010a929f | ||
|
|
a30b75cdcf | ||
|
|
3450ac8c1b | ||
|
|
90cfd46458 | ||
|
|
e2c6c06366 | ||
|
|
9e44531803 | ||
|
|
6ef8c620d3 | ||
|
|
a1c469d719 | ||
|
|
0537f515c2 | ||
|
|
72e6000947 | ||
|
|
88cc81df5c | ||
|
|
d25e28d584 | ||
|
|
a1d7e487b3 | ||
|
|
b6d6090630 | ||
|
|
bd7d13835e | ||
|
|
c81a805fe6 | ||
|
|
5fc1e6765a | ||
|
|
4ab4049f27 | ||
|
|
4e0065015d | ||
|
|
6401a8b76b | ||
|
|
80fe5b3855 | ||
|
|
a9c86f51b7 | ||
|
|
da175f7ecc | ||
|
|
0ee7f04039 | ||
|
|
c6d04f2ecf | ||
|
|
58179ec4a6 | ||
|
|
f3607640a6 | ||
|
|
8a8c967460 | ||
|
|
f9813b853d | ||
|
|
8d3cb4f207 | ||
|
|
67d7623794 | ||
|
|
0fd574926c | ||
|
|
41fe9ad75b | ||
|
|
e7731d119a | ||
|
|
69c8d524a3 | ||
|
|
34a70859bc | ||
|
|
8dee8cad9e | ||
|
|
a5c8b54340 | ||
|
|
91b5feb37b | ||
|
|
41ccae6d26 | ||
|
|
88bb6f6bfa | ||
|
|
8888de2b13 | ||
|
|
203136e793 | ||
|
|
f4fce4751e | ||
|
|
a084b26a5a | ||
|
|
15950fe3a0 | ||
|
|
3e56c7e0c4 | ||
|
|
909028e214 | ||
|
|
be723a8d8c | ||
|
|
e474df9470 | ||
|
|
8b0097b49b | ||
|
|
9ef3627fd3 | ||
|
|
17866ecc3a | ||
|
|
a2a883318b | ||
|
|
78cb195595 | ||
|
|
4b04addfce | ||
|
|
8f5bf04468 | ||
|
|
6908e24b56 | ||
|
|
93c68b642e | ||
|
|
21d8c31796 | ||
|
|
36ad75778b | ||
|
|
9272e12f19 | ||
|
|
d52f334cbd | ||
|
|
e66221add4 | ||
|
|
dbae438a0e | ||
|
|
995618a821 | ||
|
|
e19163688b | ||
|
|
d525718a93 | ||
|
|
5fa927aa97 | ||
|
|
11526252cc | ||
|
|
e5bd8def1e | ||
|
|
1ffbdfd9a7 | ||
|
|
ebd5639b6d | ||
|
|
e6725e8c8d | ||
|
|
900c62be61 |
No files matched your search
File renamed without changes.
+1
-1
@@ -1,4 +1,3 @@
|
||||
TARGETS
|
||||
make_config.mk
|
||||
|
||||
*.a
|
||||
@@ -71,3 +70,4 @@ db_logs/
|
||||
tp2/
|
||||
fbcode/
|
||||
fbcode
|
||||
buckifier/*.pyc
|
||||
@@ -27,6 +27,8 @@ env:
|
||||
- JOB_NAME=java_test
|
||||
# Build ROCKSDB_LITE
|
||||
- JOB_NAME=lite_build
|
||||
# Build examples
|
||||
- JOB_NAME=examples
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
@@ -53,6 +55,7 @@ script:
|
||||
- if [[ "${TEST_GROUP}" == '2' ]]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=comparator_db_test make -j4 check_some; fi
|
||||
- if [[ "${JOB_NAME}" == 'java_test' ]]; then OPT=-DTRAVIS V=1 make clean jclean rocksdbjava jtest; fi
|
||||
- if [[ "${JOB_NAME}" == 'lite_build' ]]; then OPT="-DTRAVIS -DROCKSDB_LITE" V=1 make -j4 static_lib; fi
|
||||
- if [[ "${JOB_NAME}" == 'examples' ]]; then OPT=-DTRAVIS V=1 make -j4 static_lib; cd examples; make -j4; fi
|
||||
notifications:
|
||||
email:
|
||||
- leveldb@fb.com
|
||||
|
||||
+132
-104
@@ -41,20 +41,21 @@ endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
||||
|
||||
if(WIN32)
|
||||
if(MSVC)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
option(WITH_JEMALLOC "build with JeMalloc" OFF)
|
||||
if(WITH_JEMALLOC)
|
||||
find_package(JeMalloc REQUIRED)
|
||||
add_definitions(-DROCKSDB_JEMALLOC)
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
# FreeBSD has jemaloc as default malloc
|
||||
add_definitions(-DROCKSDB_JEMALLOC)
|
||||
# but it does not have all the jemalloc files in include/...
|
||||
set(WITH_JEMALLOC ON)
|
||||
endif()
|
||||
else()
|
||||
if(WITH_JEMALLOC)
|
||||
find_package(JeMalloc REQUIRED)
|
||||
add_definitions(-DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE)
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
@@ -78,7 +79,7 @@ endif()
|
||||
|
||||
find_package(Git)
|
||||
|
||||
if (GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
if(WIN32)
|
||||
execute_process(COMMAND $ENV{COMSPEC} /C ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse HEAD OUTPUT_VARIABLE GIT_SHA)
|
||||
else()
|
||||
@@ -90,7 +91,7 @@ endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
if (NOT WIN32)
|
||||
if(NOT WIN32)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
@@ -105,18 +106,31 @@ if (NOT WIN32)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
option(WITH_AVX2 "build with AVX2" ON)
|
||||
if(WITH_AVX2)
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
add_library(build_version OBJECT ${BUILD_VERSION_CC})
|
||||
target_include_directories(build_version PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util)
|
||||
if(WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W3 /wd4127 /wd4800 /wd4996 /wd4351")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers")
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
@@ -178,7 +192,7 @@ else()
|
||||
set(OPTIMIZE_DEBUG ${OPTIMIZE_DEBUG_DEFAULT})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(MSVC)
|
||||
if((${OPTIMIZE_DEBUG} EQUAL 1))
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt /MDd")
|
||||
@@ -226,6 +240,9 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
add_definitions(-DOS_ANDROID)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
add_definitions(-DWIN32 -DOS_WIN -D_MBCS -DWIN64 -DNOMINMAX)
|
||||
if(MINGW)
|
||||
add_definitions(-D_WIN32_WINNT=_WIN32_WINNT_VISTA)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
@@ -265,7 +282,9 @@ add_subdirectory(third-party/gtest-1.7.0/fused-src/gtest)
|
||||
# Main library source code
|
||||
|
||||
set(SOURCES
|
||||
db/auto_roll_logger.cc
|
||||
cache/clock_cache.cc
|
||||
cache/lru_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
db/column_family.cc
|
||||
@@ -274,19 +293,23 @@ set(SOURCES
|
||||
db/compaction_iterator.cc
|
||||
db/compaction_job.cc
|
||||
db/compaction_picker.cc
|
||||
db/compaction_picker_universal.cc
|
||||
db/convenience.cc
|
||||
db/dbformat.cc
|
||||
db/db_filesnapshot.cc
|
||||
db/db_impl.cc
|
||||
db/db_impl_write.cc
|
||||
db/db_impl_compaction_flush.cc
|
||||
db/db_impl_files.cc
|
||||
db/db_impl_open.cc
|
||||
db/db_impl_debug.cc
|
||||
db/db_impl_experimental.cc
|
||||
db/db_impl_readonly.cc
|
||||
db/db_info_dumper.cc
|
||||
db/db_iter.cc
|
||||
db/dbformat.cc
|
||||
db/event_helpers.cc
|
||||
db/external_sst_file_ingestion_job.cc
|
||||
db/experimental.cc
|
||||
db/filename.cc
|
||||
db/external_sst_file_ingestion_job.cc
|
||||
db/file_indexer.cc
|
||||
db/flush_job.cc
|
||||
db/flush_scheduler.cc
|
||||
@@ -296,7 +319,6 @@ set(SOURCES
|
||||
db/log_writer.cc
|
||||
db/managed_iterator.cc
|
||||
db/memtable.cc
|
||||
db/memtable_allocator.cc
|
||||
db/memtable_list.cc
|
||||
db/merge_helper.cc
|
||||
db/merge_operator.cc
|
||||
@@ -314,11 +336,33 @@ set(SOURCES
|
||||
db/write_batch_base.cc
|
||||
db/write_controller.cc
|
||||
db/write_thread.cc
|
||||
env/env.cc
|
||||
env/env_chroot.cc
|
||||
env/env_hdfs.cc
|
||||
env/memenv.cc
|
||||
memtable/hash_cuckoo_rep.cc
|
||||
memtable/hash_linklist_rep.cc
|
||||
memtable/hash_skiplist_rep.cc
|
||||
memtable/memtable_allocator.cc
|
||||
memtable/skiplistrep.cc
|
||||
memtable/vectorrep.cc
|
||||
monitoring/histogram.cc
|
||||
monitoring/histogram_windowing.cc
|
||||
monitoring/instrumented_mutex.cc
|
||||
monitoring/iostats_context.cc
|
||||
monitoring/perf_context.cc
|
||||
monitoring/perf_level.cc
|
||||
monitoring/statistics.cc
|
||||
monitoring/thread_status_impl.cc
|
||||
monitoring/thread_status_updater.cc
|
||||
monitoring/thread_status_util.cc
|
||||
monitoring/thread_status_util_debug.cc
|
||||
options/cf_options.cc
|
||||
options/db_options.cc
|
||||
options/options.cc
|
||||
options/options_helper.cc
|
||||
options/options_parser.cc
|
||||
options/options_sanity_check.cc
|
||||
port/stack_trace.cc
|
||||
table/adaptive_table_factory.cc
|
||||
table/block.cc
|
||||
@@ -339,64 +383,44 @@ set(SOURCES
|
||||
table/index_builder.cc
|
||||
table/iterator.cc
|
||||
table/merging_iterator.cc
|
||||
table/sst_file_writer.cc
|
||||
table/meta_blocks.cc
|
||||
table/partitioned_filter_block.cc
|
||||
table/persistent_cache_helper.cc
|
||||
table/plain_table_builder.cc
|
||||
table/plain_table_factory.cc
|
||||
table/plain_table_index.cc
|
||||
table/plain_table_key_coding.cc
|
||||
table/plain_table_reader.cc
|
||||
table/persistent_cache_helper.cc
|
||||
table/sst_file_writer.cc
|
||||
table/table_properties.cc
|
||||
table/two_level_iterator.cc
|
||||
tools/sst_dump_tool.cc
|
||||
tools/db_bench_tool.cc
|
||||
tools/dump/db_dump_tool.cc
|
||||
tools/ldb_cmd.cc
|
||||
tools/ldb_tool.cc
|
||||
tools/sst_dump_tool.cc
|
||||
util/arena.cc
|
||||
util/auto_roll_logger.cc
|
||||
util/bloom.cc
|
||||
util/cf_options.cc
|
||||
util/clock_cache.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
util/concurrent_arena.cc
|
||||
util/crc32c.cc
|
||||
util/db_options.cc
|
||||
util/delete_scheduler.cc
|
||||
util/dynamic_bloom.cc
|
||||
util/env.cc
|
||||
util/env_chroot.cc
|
||||
util/env_hdfs.cc
|
||||
util/event_logger.cc
|
||||
util/file_util.cc
|
||||
util/file_reader_writer.cc
|
||||
util/sst_file_manager_impl.cc
|
||||
util/file_util.cc
|
||||
util/filename.cc
|
||||
util/filter_policy.cc
|
||||
util/hash.cc
|
||||
util/histogram.cc
|
||||
util/histogram_windowing.cc
|
||||
util/instrumented_mutex.cc
|
||||
util/iostats_context.cc
|
||||
|
||||
util/lru_cache.cc
|
||||
tools/ldb_cmd.cc
|
||||
tools/ldb_tool.cc
|
||||
util/logging.cc
|
||||
util/log_buffer.cc
|
||||
util/memenv.cc
|
||||
util/murmurhash.cc
|
||||
util/options.cc
|
||||
util/options_helper.cc
|
||||
util/options_parser.cc
|
||||
util/options_sanity_check.cc
|
||||
util/perf_context.cc
|
||||
util/perf_level.cc
|
||||
util/random.cc
|
||||
util/rate_limiter.cc
|
||||
util/sharded_cache.cc
|
||||
util/slice.cc
|
||||
util/statistics.cc
|
||||
util/sst_file_manager_impl.cc
|
||||
util/status.cc
|
||||
util/status_message.cc
|
||||
util/string_util.cc
|
||||
@@ -404,29 +428,35 @@ set(SOURCES
|
||||
util/testutil.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
util/thread_status_impl.cc
|
||||
util/thread_status_updater.cc
|
||||
util/thread_status_util.cc
|
||||
util/thread_status_util_debug.cc
|
||||
util/transaction_test_util.cc
|
||||
util/xxhash.cc
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/blob_db/blob_db.cc
|
||||
utilities/blob_db/blob_db_impl.cc
|
||||
utilities/blob_db/blob_db_options_impl.cc
|
||||
utilities/blob_db/blob_file.cc
|
||||
utilities/blob_db/blob_log_reader.cc
|
||||
utilities/blob_db/blob_log_writer.cc
|
||||
utilities/blob_db/blob_log_format.cc
|
||||
utilities/checkpoint/checkpoint.cc
|
||||
utilities/col_buf_decoder.cc
|
||||
utilities/col_buf_encoder.cc
|
||||
utilities/column_aware_encoding_util.cc
|
||||
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc
|
||||
utilities/date_tiered/date_tiered_db_impl.cc
|
||||
utilities/document/document_db.cc
|
||||
utilities/document/json_document.cc
|
||||
utilities/document/json_document_builder.cc
|
||||
utilities/env_mirror.cc
|
||||
utilities/env_timed.cc
|
||||
utilities/geodb/geodb_impl.cc
|
||||
utilities/leveldb_options/leveldb_options.cc
|
||||
utilities/lua/rocks_lua_compaction_filter.cc
|
||||
utilities/memory/memory_util.cc
|
||||
utilities/merge_operators/max.cc
|
||||
utilities/merge_operators/put.cc
|
||||
utilities/merge_operators/string_append/stringappend.cc
|
||||
utilities/merge_operators/string_append/stringappend2.cc
|
||||
utilities/merge_operators/put.cc
|
||||
utilities/merge_operators/max.cc
|
||||
utilities/merge_operators/uint64add.cc
|
||||
utilities/option_change_migration/option_change_migration.cc
|
||||
utilities/options/options_util.cc
|
||||
@@ -439,20 +469,17 @@ set(SOURCES
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/spatialdb/spatial_db.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/transactions/optimistic_transaction_impl.cc
|
||||
utilities/transactions/optimistic_transaction_db_impl.cc
|
||||
utilities/transactions/optimistic_transaction_impl.cc
|
||||
utilities/transactions/transaction_base.cc
|
||||
utilities/transactions/transaction_impl.cc
|
||||
utilities/transactions/transaction_db_impl.cc
|
||||
utilities/transactions/transaction_db_mutex_impl.cc
|
||||
utilities/transactions/transaction_impl.cc
|
||||
utilities/transactions/transaction_lock_mgr.cc
|
||||
utilities/transactions/transaction_util.cc
|
||||
utilities/ttl/db_ttl_impl.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc
|
||||
utilities/col_buf_encoder.cc
|
||||
utilities/col_buf_decoder.cc
|
||||
utilities/column_aware_encoding_util.cc
|
||||
$<TARGET_OBJECTS:build_version>)
|
||||
|
||||
if(WIN32)
|
||||
@@ -467,8 +494,8 @@ if(WIN32)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
port/port_posix.cc
|
||||
util/env_posix.cc
|
||||
util/io_posix.cc)
|
||||
env/env_posix.cc
|
||||
env/io_posix.cc)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@@ -505,18 +532,18 @@ add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(${ROCKSDB_STATIC_LIB} PROPERTIES
|
||||
COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/${ROCKSDB_STATIC_LIB}.pdb")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_library(${ROCKSDB_IMPORT_LIB} SHARED ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_IMPORT_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
set_target_properties(${ROCKSDB_IMPORT_LIB} PROPERTIES
|
||||
COMPILE_FLAGS "-DROCKSDB_DLL -DROCKSDB_LIBRARY_EXPORTS /Fd${CMAKE_CFG_INTDIR}/${ROCKSDB_IMPORT_LIB}.pdb")
|
||||
else()
|
||||
COMPILE_DEFINITIONS "ROCKSDB_DLL;ROCKSDB_LIBRARY_EXPORTS")
|
||||
if(MSVC)
|
||||
set_target_properties(${ROCKSDB_STATIC_LIB} PROPERTIES
|
||||
COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/${ROCKSDB_STATIC_LIB}.pdb")
|
||||
set_target_properties(${ROCKSDB_IMPORT_LIB} PROPERTIES
|
||||
COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/${ROCKSDB_IMPORT_LIB}.pdb")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_JNI "build with JNI" OFF)
|
||||
@@ -528,60 +555,61 @@ else()
|
||||
endif()
|
||||
|
||||
set(TESTS
|
||||
db/auto_roll_logger_test.cc
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
db/column_family_test.cc
|
||||
db/compact_files_test.cc
|
||||
db/compaction_iterator_test.cc
|
||||
db/compaction_job_test.cc
|
||||
db/compaction_job_stats_test.cc
|
||||
db/compaction_job_test.cc
|
||||
db/compaction_picker_test.cc
|
||||
db/comparator_db_test.cc
|
||||
db/corruption_test.cc
|
||||
db/cuckoo_table_db_test.cc
|
||||
db/db_basic_test.cc
|
||||
db/db_block_cache_test.cc
|
||||
db/db_bloom_filter_test.cc
|
||||
db/db_compaction_filter_test.cc
|
||||
db/db_compaction_test.cc
|
||||
db/db_dynamic_level_test.cc
|
||||
db/db_flush_test.cc
|
||||
db/db_inplace_update_test.cc
|
||||
db/db_io_failure_test.cc
|
||||
db/db_iter_test.cc
|
||||
db/db_iterator_test.cc
|
||||
db/db_log_iter_test.cc
|
||||
db/db_memtable_test.cc
|
||||
db/db_merge_operator_test.cc
|
||||
db/db_options_test.cc
|
||||
db/db_properties_test.cc
|
||||
db/db_range_del_test.cc
|
||||
db/db_sst_test.cc
|
||||
db/db_statistics_test.cc
|
||||
db/db_table_properties_test.cc
|
||||
db/db_tailing_iter_test.cc
|
||||
db/db_test.cc
|
||||
db/db_test2.cc
|
||||
db/db_block_cache_test.cc
|
||||
db/db_bloom_filter_test.cc
|
||||
db/db_flush_test.cc
|
||||
db/db_iterator_test.cc
|
||||
db/db_range_del_test.cc
|
||||
db/db_sst_test.cc
|
||||
db/external_sst_file_basic_test.cc
|
||||
db/external_sst_file_test.cc
|
||||
db/db_universal_compaction_test.cc
|
||||
db/db_wal_test.cc
|
||||
db/dbformat_test.cc
|
||||
db/deletefile_test.cc
|
||||
db/external_sst_file_basic_test.cc
|
||||
db/external_sst_file_test.cc
|
||||
db/fault_injection_test.cc
|
||||
db/file_indexer_test.cc
|
||||
db/filename_test.cc
|
||||
db/flush_job_test.cc
|
||||
db/inlineskiplist_test.cc
|
||||
db/listener_test.cc
|
||||
db/log_test.cc
|
||||
db/manual_compaction_test.cc
|
||||
db/memtable_list_test.cc
|
||||
db/merge_test.cc
|
||||
db/merge_helper_test.cc
|
||||
db/merge_test.cc
|
||||
db/options_file_test.cc
|
||||
db/perf_context_test.cc
|
||||
db/plain_table_db_test.cc
|
||||
db/prefix_test.cc
|
||||
db/repair_test.cc
|
||||
db/skiplist_test.cc
|
||||
db/table_properties_collector_test.cc
|
||||
db/version_builder_test.cc
|
||||
db/version_edit_test.cc
|
||||
@@ -590,7 +618,16 @@ set(TESTS
|
||||
db/write_batch_test.cc
|
||||
db/write_callback_test.cc
|
||||
db/write_controller_test.cc
|
||||
db/db_io_failure_test.cc
|
||||
env/env_basic_test.cc
|
||||
env/env_test.cc
|
||||
env/mock_env_test.cc
|
||||
memtable/inlineskiplist_test.cc
|
||||
memtable/skiplist_test.cc
|
||||
monitoring/histogram_test.cc
|
||||
monitoring/iostats_context_test.cc
|
||||
monitoring/statistics_test.cc
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based_filter_block_test.cc
|
||||
table/block_test.cc
|
||||
table/cuckoo_table_builder_test.cc
|
||||
@@ -602,40 +639,33 @@ set(TESTS
|
||||
tools/reduce_levels_test.cc
|
||||
tools/sst_dump_test.cc
|
||||
util/arena_test.cc
|
||||
util/auto_roll_logger_test.cc
|
||||
util/autovector_test.cc
|
||||
util/bloom_test.cc
|
||||
util/cache_test.cc
|
||||
util/coding_test.cc
|
||||
util/crc32c_test.cc
|
||||
util/delete_scheduler_test.cc
|
||||
util/dynamic_bloom_test.cc
|
||||
util/env_basic_test.cc
|
||||
util/env_test.cc
|
||||
util/event_logger_test.cc
|
||||
util/filelock_test.cc
|
||||
util/file_reader_writer_test.cc
|
||||
util/filelock_test.cc
|
||||
util/heap_test.cc
|
||||
util/histogram_test.cc
|
||||
util/iostats_context_test.cc
|
||||
util/lru_cache_test.cc
|
||||
util/mock_env_test.cc
|
||||
util/options_settable_test.cc
|
||||
util/options_test.cc
|
||||
util/rate_limiter_test.cc
|
||||
util/slice_transform_test.cc
|
||||
util/statistics_test.cc
|
||||
util/timer_queue_test.cc
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
utilities/blob_db/blob_db_test.cc
|
||||
utilities/checkpoint/checkpoint_test.cc
|
||||
utilities/column_aware_encoding_test.cc
|
||||
utilities/date_tiered/date_tiered_test.cc
|
||||
utilities/document/document_db_test.cc
|
||||
utilities/document/json_document_test.cc
|
||||
utilities/object_registry_test.cc
|
||||
utilities/geodb/geodb_test.cc
|
||||
utilities/lua/rocks_lua_test.cc
|
||||
utilities/memory/memory_test.cc
|
||||
utilities/merge_operators/string_append/stringappend_test.cc
|
||||
utilities/object_registry_test.cc
|
||||
utilities/option_change_migration/option_change_migration_test.cc
|
||||
utilities/options/options_util_test.cc
|
||||
utilities/persistent_cache/hash_table_test.cc
|
||||
@@ -647,18 +677,16 @@ set(TESTS
|
||||
utilities/transactions/transaction_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
utilities/column_aware_encoding_test.cc
|
||||
utilities/lua/rocks_lua_test.cc
|
||||
)
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND TESTS utilities/env_librados_test.cc)
|
||||
endif()
|
||||
|
||||
set(BENCHMARKS
|
||||
cache/cache_bench.cc
|
||||
memtable/memtablerep_bench.cc
|
||||
tools/db_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
util/cache_bench.cc
|
||||
db/memtablerep_bench.cc
|
||||
utilities/column_aware_encoding_exp.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
add_library(testharness OBJECT util/testharness.cc)
|
||||
@@ -673,17 +701,17 @@ endforeach(sourcefile ${BENCHMARKS})
|
||||
# and linked to tests. Add test only code that is not #ifdefed for Release here.
|
||||
set(TESTUTIL_SOURCE
|
||||
db/db_test_util.cc
|
||||
env/mock_env.cc
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
util/mock_env.cc
|
||||
util/fault_injection_test_env.cc
|
||||
util/thread_status_updater_debug.cc
|
||||
)
|
||||
# test utilities are only build in debug
|
||||
enable_testing()
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
|
||||
set(TESTUTILLIB testutillib${ARTIFACT_SUFFIX})
|
||||
add_library(${TESTUTILLIB} STATIC ${TESTUTIL_SOURCE})
|
||||
if(WIN32)
|
||||
if(MSVC)
|
||||
set_target_properties(${TESTUTILLIB} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/testutillib${ARTIFACT_SUFFIX}.pdb")
|
||||
endif()
|
||||
set_target_properties(${TESTUTILLIB}
|
||||
@@ -731,7 +759,7 @@ endforeach(sourcefile ${C_TEST_EXES})
|
||||
add_subdirectory(tools)
|
||||
|
||||
# Installation and packaging for Linux
|
||||
if (NOT WIN32)
|
||||
if(NOT WIN32)
|
||||
install(TARGETS ${ROCKSDB_STATIC_LIB} COMPONENT devel ARCHIVE DESTINATION lib64)
|
||||
install(TARGETS ${ROCKSDB_SHARED_LIB} COMPONENT runtime DESTINATION lib64)
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/rocksdb/"
|
||||
|
||||
+72
-4
@@ -1,7 +1,78 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.3.7
|
||||
## 5.4.12 (08/21/2017)
|
||||
* Statistics::Reset() to reset user stats.
|
||||
|
||||
## 5.4.11 (08/17/2017)
|
||||
### Bug Fixes
|
||||
* Fix a bug in PosixWritableFile::Close()
|
||||
|
||||
## 5.4.10 (08/12/2017)
|
||||
### Bug Fixes
|
||||
* Fix incorrect dropping of deletions during intra-L0 compaction.
|
||||
|
||||
## 5.4.9 (07/20/2017)
|
||||
* Change license to Apache + GPLv2
|
||||
|
||||
## 5.4.8 (07/13/2017)
|
||||
No change from 5.4.7.
|
||||
|
||||
## 5.4.7 (06/28/2017)
|
||||
### Bug Fixes
|
||||
* Shouldn't ignore return value of fsync() in flush.
|
||||
|
||||
## 5.4.6 (05/30/2017)
|
||||
### Bug Fixes
|
||||
* Fix db_bench with lite build.
|
||||
|
||||
## 5.4.5 (05/19/2017)
|
||||
### Bug Fixes
|
||||
* Facility for cross-building RocksJava using Docker.
|
||||
* Build and link with ZStd when creating the static RocksJava build.
|
||||
* Fix snprintf's usage to be cross-platform.
|
||||
* Fix build error with blob DB.
|
||||
* Fix readamp test type inconsistency.
|
||||
|
||||
## 5.4.4 (05/11/2017)
|
||||
### New Features
|
||||
* Added DB::CreateColumnFamilie() and DB::DropColumnFamilies() to bulk create/drop column families.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a manual / auto compaction data race.
|
||||
* Fix CentOS 5 cross-building of RocksJava.
|
||||
|
||||
## 5.4.3 (05/10/2017)
|
||||
### Bug Fixes
|
||||
* Fix memory alignment with logical sector size.
|
||||
* Fix alignment in ReadaheadRandomAccessFile.
|
||||
* Fix bias with read amplification stats (READ_AMP_ESTIMATE_USEFUL_BYTES and READ_AMP_TOTAL_READ_BYTES).
|
||||
|
||||
## 5.4.2 (05/08/2017)
|
||||
### Bug Fixes
|
||||
* Fix huge fallocate calls fail and make XFS unhappy.
|
||||
|
||||
## 5.4.1 (05/02/2017)
|
||||
### Bug Fxies
|
||||
* Fix WriteBatchWithIndex address use after scope error.
|
||||
* Fix WritableFile buffer size in direct IO.
|
||||
* Add prefetch to PosixRandomAccessFile in buffered io.
|
||||
* Fix PinnableSlice access invalid address when row cache is enabled.
|
||||
|
||||
## 5.4.0 (04/11/2017)
|
||||
### Public API Change
|
||||
* Support dynamically change `stats_dump_period_sec` option via SetDBOptions().
|
||||
* Added ReadOptions::max_skippable_internal_keys to set a threshold to fail a request as incomplete when too many keys are being skipped when using iterators.
|
||||
* DB::Get in place of std::string accepts PinnableSlice, which avoids the extra memcpy of value to std::string in most of cases.
|
||||
* PinnableSlice releases the pinned resources that contain the value when it is destructed or when ::Reset() is called on it.
|
||||
* The old API that accepts std::string, although discouraged, is still supported.
|
||||
* Replace Options::use_direct_writes with Options::use_direct_io_for_flush_and_compaction. Read Direct IO wiki for details.
|
||||
* Added CompactionEventListener and EventListener::OnFlushBegin interfaces.
|
||||
|
||||
### New Features
|
||||
* Memtable flush can be avoided during checkpoint creation if total log file size is smaller than a threshold specified by the user.
|
||||
* Introduce level-based L0->L0 compactions to reduce file count, so write delays are incurred less often.
|
||||
* (Experimental) Partitioning filters which creates an index on the partitions. The feature can be enabled by setting partition_filters when using kFullFilter. Currently the feature also requires two-level indexing to be enabled. Number of partitions is the same as the number of partitions for indexes, which is controlled by metadata_block_size.
|
||||
* DB::ResetStats() to reset internal stats.
|
||||
|
||||
## 5.3.0 (03/08/2017)
|
||||
### Public API Change
|
||||
* Remove disableDataSync option.
|
||||
@@ -9,9 +80,6 @@
|
||||
* Remove option min_partial_merge_operands. Partial merge operands will always be merged in flush or compaction if there are more than one.
|
||||
* Remove option verify_checksums_in_compaction. Compaction will always verify checksum.
|
||||
|
||||
### New Features
|
||||
* Memtable flush can be avoided during checkpoint creation if total log file size is smaller than a threshold specified by the user.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix the bug that iterator may skip keys
|
||||
|
||||
|
||||
+9
-4
@@ -1,6 +1,6 @@
|
||||
## Compilation
|
||||
|
||||
**Important**: If you plan to run RocksDB in production, don't compile using default
|
||||
**Important**: If you plan to run RocksDB in production, don't compile using default
|
||||
`make` or `make all`. That will compile RocksDB in debug mode, which is much slower
|
||||
than release mode.
|
||||
|
||||
@@ -22,7 +22,8 @@ use binaries compiled by `make all` in production.
|
||||
|
||||
* By default the binary we produce is optimized for the platform you're compiling on
|
||||
(-march=native or the equivalent). If you want to build a portable binary, add 'PORTABLE=1' before
|
||||
your make commands, like this: `PORTABLE=1 make static_lib`
|
||||
your make commands, like this: `PORTABLE=1 make static_lib`. If you want to build a binary that
|
||||
makes use of SSE4, add 'USE_SSE=1' before your make commands, like this: `USE_SSE=1 make static_lib`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -58,10 +59,14 @@ your make commands, like this: `PORTABLE=1 make static_lib`
|
||||
* Install gflags:
|
||||
|
||||
git clone https://github.com/gflags/gflags.git
|
||||
git checkout v2.0
|
||||
cd gflags
|
||||
git checkout v2.0
|
||||
./configure && make && sudo make install
|
||||
|
||||
**Notice**: Once installed, please add the include path for gflags to your CPATH env var and the
|
||||
lib path to LIBRARY_PATH. If installed with default settings, the lib will be /usr/local/lib
|
||||
and the include path will be /usr/local/include.
|
||||
|
||||
* Install snappy:
|
||||
|
||||
wget https://github.com/google/snappy/releases/download/1.1.4/snappy-1.1.4.tar.gz
|
||||
@@ -82,7 +87,7 @@ your make commands, like this: `PORTABLE=1 make static_lib`
|
||||
* Install zstandard:
|
||||
|
||||
wget https://github.com/facebook/zstd/archive/v1.1.3.tar.gz
|
||||
mv v1.1.3.tar.gz zstandard-1.1.3.tar.gz
|
||||
mv v1.1.3.tar.gz zstd-1.1.3.tar.gz
|
||||
tar zxvf zstd-1.1.3.tar.gz
|
||||
cd zstd-1.1.3
|
||||
make && sudo make install
|
||||
|
||||
@@ -204,15 +204,16 @@ endif
|
||||
|
||||
ifndef DISABLE_JEMALLOC
|
||||
ifdef JEMALLOC
|
||||
PLATFORM_CXXFLAGS += "-DROCKSDB_JEMALLOC"
|
||||
PLATFORM_CCFLAGS += "-DROCKSDB_JEMALLOC"
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
PLATFORM_CCFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
endif
|
||||
EXEC_LDFLAGS := $(JEMALLOC_LIB) $(EXEC_LDFLAGS)
|
||||
PLATFORM_CXXFLAGS += $(JEMALLOC_INCLUDE)
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE)
|
||||
endif
|
||||
|
||||
export GTEST_THROW_ON_FAILURE=1 GTEST_HAS_EXCEPTIONS=1
|
||||
export GTEST_THROW_ON_FAILURE=1
|
||||
export GTEST_HAS_EXCEPTIONS=1
|
||||
GTEST_DIR = ./third-party/gtest-1.7.0/fused-src
|
||||
PLATFORM_CCFLAGS += -isystem $(GTEST_DIR)
|
||||
PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
|
||||
@@ -333,6 +334,7 @@ TESTS = \
|
||||
db_io_failure_test \
|
||||
db_properties_test \
|
||||
db_table_properties_test \
|
||||
db_statistics_test \
|
||||
autovector_test \
|
||||
cleanable_test \
|
||||
column_family_test \
|
||||
@@ -349,6 +351,7 @@ TESTS = \
|
||||
file_reader_writer_test \
|
||||
block_based_filter_block_test \
|
||||
full_filter_block_test \
|
||||
partitioned_filter_block_test \
|
||||
hash_table_test \
|
||||
histogram_test \
|
||||
log_test \
|
||||
@@ -372,7 +375,6 @@ TESTS = \
|
||||
ttl_test \
|
||||
date_tiered_test \
|
||||
backupable_db_test \
|
||||
blob_db_test \
|
||||
document_db_test \
|
||||
json_document_test \
|
||||
sim_cache_test \
|
||||
@@ -393,6 +395,7 @@ TESTS = \
|
||||
options_settable_test \
|
||||
options_util_test \
|
||||
event_logger_test \
|
||||
timer_queue_test \
|
||||
cuckoo_table_builder_test \
|
||||
cuckoo_table_reader_test \
|
||||
cuckoo_table_db_test \
|
||||
@@ -420,6 +423,7 @@ TESTS = \
|
||||
lru_cache_test \
|
||||
object_registry_test \
|
||||
repair_test \
|
||||
env_timed_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -886,20 +890,20 @@ $(TOOLS_LIBRARY): $(BENCH_LIB_SOURCES:.cc=.o) $(TOOL_LIB_SOURCES:.cc=.o) $(LIB_S
|
||||
$(AM_V_AR)rm -f $@
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
librocksdb_env_basic_test.a: util/env_basic_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
librocksdb_env_basic_test.a: env/env_basic_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_V_AR)rm -f $@
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
db_bench: tools/db_bench.o $(BENCHTOOLOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_bench: util/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
cache_bench: cache/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
persistent_cache_bench: utilities/persistent_cache/persistent_cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
memtablerep_bench: db/memtablerep_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
memtablerep_bench: memtable/memtablerep_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
db_stress: tools/db_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
@@ -935,7 +939,7 @@ dynamic_bloom_test: util/dynamic_bloom_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cache_test: cache/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -953,7 +957,7 @@ redis_test: utilities/redis/redis_lists_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
hash_table_test: utilities/persistent_cache/hash_table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
histogram_test: util/histogram_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
histogram_test: monitoring/histogram_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
thread_local_test: util/thread_local_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1019,6 +1023,9 @@ db_range_del_test: db/db_range_del_test.o db/db_test_util.o $(LIBOBJECTS) $(TEST
|
||||
db_sst_test: db/db_sst_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_statistics_test: db/db_statistics_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
external_sst_file_basic_test: db/external_sst_file_basic_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1067,9 +1074,6 @@ prefix_test: db/prefix_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_db_test: utilities/blob_db/blob_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
checkpoint_test: utilities/checkpoint/checkpoint_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1088,6 +1092,9 @@ spatial_db_test: utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNE
|
||||
env_mirror_test: utilities/env_mirror_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_timed_test: utilities/env_timed_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
ifdef ROCKSDB_USE_LIBRADOS
|
||||
env_librados_test: utilities/env_librados_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
@@ -1126,10 +1133,10 @@ wal_manager_test: db/wal_manager_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_basic_test: util/env_basic_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
env_basic_test: env/env_basic_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
env_test: env/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
fault_injection_test: db/fault_injection_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1153,6 +1160,9 @@ block_based_filter_block_test: table/block_based_filter_block_test.o $(LIBOBJECT
|
||||
full_filter_block_test: table/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
partitioned_filter_block_test: table/partitioned_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1165,10 +1175,10 @@ table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
block_test: table/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
inlineskiplist_test: db/inlineskiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
inlineskiplist_test: memtable/inlineskiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
skiplist_test: memtable/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1243,10 +1253,10 @@ thread_list_test: util/thread_list_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compact_files_test: db/compact_files_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
options_test: util/options_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
options_test: options/options_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
options_settable_test: util/options_settable_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
options_settable_test: options/options_settable_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
options_util_test: utilities/options/options_util_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1258,6 +1268,9 @@ db_bench_tool_test: tools/db_bench_tool_test.o $(BENCHTOOLOBJECTS) $(TESTHARNESS
|
||||
event_logger_test: util/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
timer_queue_test: util/timer_queue_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sst_dump_test: tools/sst_dump_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1267,7 +1280,7 @@ column_aware_encoding_test: utilities/column_aware_encoding_test.o $(TESTHARNESS
|
||||
optimistic_transaction_test: utilities/transactions/optimistic_transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
mock_env_test : util/mock_env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
mock_env_test : env/mock_env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
manual_compaction_test: db/manual_compaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1276,7 +1289,7 @@ manual_compaction_test: db/manual_compaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
filelock_test: util/filelock_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
auto_roll_logger_test: db/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
auto_roll_logger_test: util/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memtable_list_test: db/memtable_list_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1306,16 +1319,16 @@ ldb_cmd_test: tools/ldb_cmd_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
ldb: tools/ldb.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
iostats_context_test: util/iostats_context_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
iostats_context_test: monitoring/iostats_context_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
|
||||
persistent_cache_test: utilities/persistent_cache/persistent_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
statistics_test: util/statistics_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
statistics_test: monitoring/statistics_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_cache_test: util/lru_cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
lru_cache_test: cache/lru_cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lua_test: utilities/lua/rocks_lua_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1371,7 +1384,12 @@ ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
else
|
||||
ARCH := $(shell getconf LONG_BIT)
|
||||
endif
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
|
||||
ifeq (,$(findstring ppc,$(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
else
|
||||
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
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
REPO_PATH = "internal_repo_rocksdb/repo/"
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
rocksdb_compiler_flags = [
|
||||
"-msse",
|
||||
"-msse4.2",
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DOS_LINUX",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-DTBB",
|
||||
# Needed to compile in fbcode
|
||||
"-Wno-expansion-to-defined",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
('bzip2', None, 'bz2'),
|
||||
('snappy', None, "snappy"),
|
||||
('zlib', None, 'z'),
|
||||
('gflags', None, 'gflags'),
|
||||
('lz4', None, 'lz4'),
|
||||
('zstd', None),
|
||||
('tbb', None),
|
||||
("numa", "2.0.8", "numa"),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
rocksdb_preprocessor_flags = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
srcs = [
|
||||
"cache/clock_cache.cc",
|
||||
"cache/lru_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
"db/column_family.cc",
|
||||
"db/compacted_db_impl.cc",
|
||||
"db/compaction.cc",
|
||||
"db/compaction_iterator.cc",
|
||||
"db/compaction_job.cc",
|
||||
"db/compaction_picker.cc",
|
||||
"db/compaction_picker_universal.cc",
|
||||
"db/convenience.cc",
|
||||
"db/db_filesnapshot.cc",
|
||||
"db/db_impl.cc",
|
||||
"db/db_impl_write.cc",
|
||||
"db/db_impl_compaction_flush.cc",
|
||||
"db/db_impl_files.cc",
|
||||
"db/db_impl_open.cc",
|
||||
"db/db_impl_debug.cc",
|
||||
"db/db_impl_experimental.cc",
|
||||
"db/db_impl_readonly.cc",
|
||||
"db/db_info_dumper.cc",
|
||||
"db/db_iter.cc",
|
||||
"db/dbformat.cc",
|
||||
"db/event_helpers.cc",
|
||||
"db/experimental.cc",
|
||||
"db/external_sst_file_ingestion_job.cc",
|
||||
"db/file_indexer.cc",
|
||||
"db/flush_job.cc",
|
||||
"db/flush_scheduler.cc",
|
||||
"db/forward_iterator.cc",
|
||||
"db/internal_stats.cc",
|
||||
"db/log_reader.cc",
|
||||
"db/log_writer.cc",
|
||||
"db/managed_iterator.cc",
|
||||
"db/memtable.cc",
|
||||
"db/memtable_list.cc",
|
||||
"db/merge_helper.cc",
|
||||
"db/merge_operator.cc",
|
||||
"db/range_del_aggregator.cc",
|
||||
"db/repair.cc",
|
||||
"db/snapshot_impl.cc",
|
||||
"db/table_cache.cc",
|
||||
"db/table_properties_collector.cc",
|
||||
"db/transaction_log_impl.cc",
|
||||
"db/version_builder.cc",
|
||||
"db/version_edit.cc",
|
||||
"db/version_set.cc",
|
||||
"db/wal_manager.cc",
|
||||
"db/write_batch.cc",
|
||||
"db/write_batch_base.cc",
|
||||
"db/write_controller.cc",
|
||||
"db/write_thread.cc",
|
||||
"env/env.cc",
|
||||
"env/env_chroot.cc",
|
||||
"env/env_hdfs.cc",
|
||||
"env/env_posix.cc",
|
||||
"env/io_posix.cc",
|
||||
"env/memenv.cc",
|
||||
"memtable/hash_cuckoo_rep.cc",
|
||||
"memtable/hash_linklist_rep.cc",
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
"memtable/memtable_allocator.cc",
|
||||
"memtable/skiplistrep.cc",
|
||||
"memtable/vectorrep.cc",
|
||||
"monitoring/histogram.cc",
|
||||
"monitoring/histogram_windowing.cc",
|
||||
"monitoring/instrumented_mutex.cc",
|
||||
"monitoring/iostats_context.cc",
|
||||
"monitoring/perf_context.cc",
|
||||
"monitoring/perf_level.cc",
|
||||
"monitoring/statistics.cc",
|
||||
"monitoring/thread_status_impl.cc",
|
||||
"monitoring/thread_status_updater.cc",
|
||||
"monitoring/thread_status_updater_debug.cc",
|
||||
"monitoring/thread_status_util.cc",
|
||||
"monitoring/thread_status_util_debug.cc",
|
||||
"options/cf_options.cc",
|
||||
"options/db_options.cc",
|
||||
"options/options.cc",
|
||||
"options/options_helper.cc",
|
||||
"options/options_parser.cc",
|
||||
"options/options_sanity_check.cc",
|
||||
"port/port_posix.cc",
|
||||
"port/stack_trace.cc",
|
||||
"table/adaptive_table_factory.cc",
|
||||
"table/block.cc",
|
||||
"table/block_based_filter_block.cc",
|
||||
"table/block_based_table_builder.cc",
|
||||
"table/block_based_table_factory.cc",
|
||||
"table/block_based_table_reader.cc",
|
||||
"table/block_builder.cc",
|
||||
"table/block_prefix_index.cc",
|
||||
"table/bloom_block.cc",
|
||||
"table/cuckoo_table_builder.cc",
|
||||
"table/cuckoo_table_factory.cc",
|
||||
"table/cuckoo_table_reader.cc",
|
||||
"table/flush_block_policy.cc",
|
||||
"table/format.cc",
|
||||
"table/full_filter_block.cc",
|
||||
"table/get_context.cc",
|
||||
"table/index_builder.cc",
|
||||
"table/iterator.cc",
|
||||
"table/merging_iterator.cc",
|
||||
"table/meta_blocks.cc",
|
||||
"table/partitioned_filter_block.cc",
|
||||
"table/persistent_cache_helper.cc",
|
||||
"table/plain_table_builder.cc",
|
||||
"table/plain_table_factory.cc",
|
||||
"table/plain_table_index.cc",
|
||||
"table/plain_table_key_coding.cc",
|
||||
"table/plain_table_reader.cc",
|
||||
"table/sst_file_writer.cc",
|
||||
"table/table_properties.cc",
|
||||
"table/two_level_iterator.cc",
|
||||
"tools/dump/db_dump_tool.cc",
|
||||
"util/arena.cc",
|
||||
"util/auto_roll_logger.cc",
|
||||
"util/bloom.cc",
|
||||
"util/build_version.cc",
|
||||
"util/coding.cc",
|
||||
"util/compaction_job_stats_impl.cc",
|
||||
"util/comparator.cc",
|
||||
"util/concurrent_arena.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/delete_scheduler.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
"util/event_logger.cc",
|
||||
"util/file_reader_writer.cc",
|
||||
"util/file_util.cc",
|
||||
"util/filename.cc",
|
||||
"util/filter_policy.cc",
|
||||
"util/hash.cc",
|
||||
"util/log_buffer.cc",
|
||||
"util/murmurhash.cc",
|
||||
"util/random.cc",
|
||||
"util/rate_limiter.cc",
|
||||
"util/slice.cc",
|
||||
"util/sst_file_manager_impl.cc",
|
||||
"util/status.cc",
|
||||
"util/status_message.cc",
|
||||
"util/string_util.cc",
|
||||
"util/sync_point.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/transaction_test_util.cc",
|
||||
"util/xxhash.cc",
|
||||
"utilities/backupable/backupable_db.cc",
|
||||
"utilities/blob_db/blob_db.cc",
|
||||
"utilities/blob_db/blob_db_impl.cc",
|
||||
"utilities/blob_db/blob_db_options_impl.cc",
|
||||
"utilities/blob_db/blob_file.cc",
|
||||
"utilities/blob_db/blob_log_reader.cc",
|
||||
"utilities/blob_db/blob_log_writer.cc",
|
||||
"utilities/blob_db/blob_log_format.cc",
|
||||
"utilities/checkpoint/checkpoint.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
"utilities/date_tiered/date_tiered_db_impl.cc",
|
||||
"utilities/document/document_db.cc",
|
||||
"utilities/document/json_document.cc",
|
||||
"utilities/document/json_document_builder.cc",
|
||||
"utilities/env_mirror.cc",
|
||||
"utilities/env_timed.cc",
|
||||
"utilities/geodb/geodb_impl.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/lua/rocks_lua_compaction_filter.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators/max.cc",
|
||||
"utilities/merge_operators/put.cc",
|
||||
"utilities/merge_operators/string_append/stringappend.cc",
|
||||
"utilities/merge_operators/string_append/stringappend2.cc",
|
||||
"utilities/merge_operators/uint64add.cc",
|
||||
"utilities/option_change_migration/option_change_migration.cc",
|
||||
"utilities/options/options_util.cc",
|
||||
"utilities/persistent_cache/block_cache_tier.cc",
|
||||
"utilities/persistent_cache/block_cache_tier_file.cc",
|
||||
"utilities/persistent_cache/block_cache_tier_metadata.cc",
|
||||
"utilities/persistent_cache/persistent_cache_tier.cc",
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/redis/redis_lists.cc",
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/spatialdb/spatial_db.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/transactions/optimistic_transaction_db_impl.cc",
|
||||
"utilities/transactions/optimistic_transaction_impl.cc",
|
||||
"utilities/transactions/transaction_base.cc",
|
||||
"utilities/transactions/transaction_db_impl.cc",
|
||||
"utilities/transactions/transaction_db_mutex_impl.cc",
|
||||
"utilities/transactions/transaction_impl.cc",
|
||||
"utilities/transactions/transaction_lock_mgr.cc",
|
||||
"utilities/transactions/transaction_util.cc",
|
||||
"utilities/ttl/db_ttl_impl.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
"tools/ldb_cmd.cc",
|
||||
"tools/ldb_tool.cc",
|
||||
"tools/sst_dump_tool.cc",
|
||||
],
|
||||
deps = [],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_test_lib",
|
||||
headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
srcs = [
|
||||
"env/mock_env.cc",
|
||||
"table/mock_table.cc",
|
||||
"util/fault_injection_test_env.cc",
|
||||
"util/testharness.cc",
|
||||
"util/testutil.cc",
|
||||
"db/db_test_util.cc",
|
||||
"utilities/col_buf_encoder.cc",
|
||||
"utilities/col_buf_decoder.cc",
|
||||
"utilities/column_aware_encoding_util.cc",
|
||||
],
|
||||
deps = [":rocksdb_lib"],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_tools_lib",
|
||||
headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
srcs = [
|
||||
"tools/db_bench_tool.cc",
|
||||
"util/testutil.cc",
|
||||
],
|
||||
deps = [":rocksdb_lib"],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "env_basic_test_lib",
|
||||
headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
srcs = ["env/env_basic_test.cc"],
|
||||
deps = [":rocksdb_test_lib"],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
# [test_name, test_src, test_type]
|
||||
ROCKS_TESTS = [['merger_test', 'table/merger_test.cc', 'serial'],
|
||||
['cache_test', 'cache/cache_test.cc', 'serial'],
|
||||
['options_file_test', 'db/options_file_test.cc', 'serial'],
|
||||
['compaction_picker_test', 'db/compaction_picker_test.cc', 'serial'],
|
||||
['corruption_test', 'db/corruption_test.cc', 'serial'],
|
||||
['thread_list_test', 'util/thread_list_test.cc', 'serial'],
|
||||
['table_properties_collector_test',
|
||||
'db/table_properties_collector_test.cc',
|
||||
'serial'],
|
||||
['document_db_test', 'utilities/document/document_db_test.cc', 'serial'],
|
||||
['event_logger_test', 'util/event_logger_test.cc', 'serial'],
|
||||
['coding_test', 'util/coding_test.cc', 'serial'],
|
||||
['statistics_test', 'monitoring/statistics_test.cc', 'serial'],
|
||||
['options_settable_test', 'options/options_settable_test.cc', 'serial'],
|
||||
['sst_dump_test', 'tools/sst_dump_test.cc', 'serial'],
|
||||
['column_aware_encoding_test',
|
||||
'utilities/column_aware_encoding_test.cc',
|
||||
'serial'],
|
||||
['db_iterator_test', 'db/db_iterator_test.cc', 'serial'],
|
||||
['db_sst_test', 'db/db_sst_test.cc', 'parallel'],
|
||||
['geodb_test', 'utilities/geodb/geodb_test.cc', 'serial'],
|
||||
['listener_test', 'db/listener_test.cc', 'serial'],
|
||||
['write_callback_test', 'db/write_callback_test.cc', 'serial'],
|
||||
['version_set_test', 'db/version_set_test.cc', 'serial'],
|
||||
['full_filter_block_test', 'table/full_filter_block_test.cc', 'serial'],
|
||||
['cleanable_test', 'table/cleanable_test.cc', 'serial'],
|
||||
['checkpoint_test', 'utilities/checkpoint/checkpoint_test.cc', 'serial'],
|
||||
['compact_files_test', 'db/compact_files_test.cc', 'serial'],
|
||||
['db_options_test', 'db/db_options_test.cc', 'serial'],
|
||||
['object_registry_test', 'utilities/object_registry_test.cc', 'serial'],
|
||||
['auto_roll_logger_test', 'util/auto_roll_logger_test.cc', 'serial'],
|
||||
['dbformat_test', 'db/dbformat_test.cc', 'serial'],
|
||||
['write_batch_with_index_test',
|
||||
'utilities/write_batch_with_index/write_batch_with_index_test.cc',
|
||||
'serial'],
|
||||
['json_document_test', 'utilities/document/json_document_test.cc', 'serial'],
|
||||
['file_reader_writer_test', 'util/file_reader_writer_test.cc', 'serial'],
|
||||
['repair_test', 'db/repair_test.cc', 'serial'],
|
||||
['persistent_cache_test',
|
||||
'utilities/persistent_cache/persistent_cache_test.cc',
|
||||
'parallel'],
|
||||
['db_bloom_filter_test', 'db/db_bloom_filter_test.cc', 'serial'],
|
||||
['external_sst_file_basic_test',
|
||||
'db/external_sst_file_basic_test.cc',
|
||||
'serial'],
|
||||
['options_test', 'options/options_test.cc', 'serial'],
|
||||
['perf_context_test', 'db/perf_context_test.cc', 'serial'],
|
||||
['db_block_cache_test', 'db/db_block_cache_test.cc', 'serial'],
|
||||
['heap_test', 'util/heap_test.cc', 'serial'],
|
||||
['db_test2', 'db/db_test2.cc', 'serial'],
|
||||
['filelock_test', 'util/filelock_test.cc', 'serial'],
|
||||
['write_controller_test', 'db/write_controller_test.cc', 'serial'],
|
||||
['compaction_iterator_test', 'db/compaction_iterator_test.cc', 'serial'],
|
||||
['spatial_db_test', 'utilities/spatialdb/spatial_db_test.cc', 'serial'],
|
||||
['c_test', 'db/c_test.c', 'serial'],
|
||||
['range_del_aggregator_test', 'db/range_del_aggregator_test.cc', 'serial'],
|
||||
['date_tiered_test', 'utilities/date_tiered/date_tiered_test.cc', 'serial'],
|
||||
['ldb_cmd_test', 'tools/ldb_cmd_test.cc', 'serial'],
|
||||
['db_test', 'db/db_test.cc', 'parallel'],
|
||||
['block_based_filter_block_test',
|
||||
'table/block_based_filter_block_test.cc',
|
||||
'serial'],
|
||||
['merge_test', 'db/merge_test.cc', 'serial'],
|
||||
['bloom_test', 'util/bloom_test.cc', 'serial'],
|
||||
['block_test', 'table/block_test.cc', 'serial'],
|
||||
['cuckoo_table_builder_test', 'table/cuckoo_table_builder_test.cc', 'serial'],
|
||||
['backupable_db_test',
|
||||
'utilities/backupable/backupable_db_test.cc',
|
||||
'parallel'],
|
||||
['db_flush_test', 'db/db_flush_test.cc', 'serial'],
|
||||
['filename_test', 'db/filename_test.cc', 'serial'],
|
||||
['cuckoo_table_reader_test', 'table/cuckoo_table_reader_test.cc', 'serial'],
|
||||
['slice_transform_test', 'util/slice_transform_test.cc', 'serial'],
|
||||
['cuckoo_table_db_test', 'db/cuckoo_table_db_test.cc', 'serial'],
|
||||
['inlineskiplist_test', 'memtable/inlineskiplist_test.cc', 'parallel'],
|
||||
['optimistic_transaction_test',
|
||||
'utilities/transactions/optimistic_transaction_test.cc',
|
||||
'serial'],
|
||||
['hash_table_test',
|
||||
'utilities/persistent_cache/hash_table_test.cc',
|
||||
'serial'],
|
||||
['db_dynamic_level_test', 'db/db_dynamic_level_test.cc', 'serial'],
|
||||
['option_change_migration_test',
|
||||
'utilities/option_change_migration/option_change_migration_test.cc',
|
||||
'serial'],
|
||||
['db_inplace_update_test', 'db/db_inplace_update_test.cc', 'serial'],
|
||||
['autovector_test', 'util/autovector_test.cc', 'serial'],
|
||||
['db_iter_test', 'db/db_iter_test.cc', 'serial'],
|
||||
['flush_job_test', 'db/flush_job_test.cc', 'serial'],
|
||||
['wal_manager_test', 'db/wal_manager_test.cc', 'serial'],
|
||||
['write_batch_test', 'db/write_batch_test.cc', 'serial'],
|
||||
['crc32c_test', 'util/crc32c_test.cc', 'serial'],
|
||||
['rate_limiter_test', 'util/rate_limiter_test.cc', 'serial'],
|
||||
['external_sst_file_test', 'db/external_sst_file_test.cc', 'parallel'],
|
||||
['compaction_job_test', 'db/compaction_job_test.cc', 'serial'],
|
||||
['mock_env_test', 'env/mock_env_test.cc', 'serial'],
|
||||
['db_table_properties_test', 'db/db_table_properties_test.cc', 'serial'],
|
||||
['db_compaction_test', 'db/db_compaction_test.cc', 'parallel'],
|
||||
['arena_test', 'util/arena_test.cc', 'serial'],
|
||||
['stringappend_test',
|
||||
'utilities/merge_operators/string_append/stringappend_test.cc',
|
||||
'serial'],
|
||||
['reduce_levels_test', 'tools/reduce_levels_test.cc', 'serial'],
|
||||
['prefix_test', 'db/prefix_test.cc', 'serial'],
|
||||
['ttl_test', 'utilities/ttl/ttl_test.cc', 'serial'],
|
||||
['merge_helper_test', 'db/merge_helper_test.cc', 'serial'],
|
||||
['file_indexer_test', 'db/file_indexer_test.cc', 'serial'],
|
||||
['memory_test', 'utilities/memory/memory_test.cc', 'serial'],
|
||||
['log_test', 'db/log_test.cc', 'serial'],
|
||||
['env_timed_test', 'utilities/env_timed_test.cc', 'serial'],
|
||||
['deletefile_test', 'db/deletefile_test.cc', 'serial'],
|
||||
['partitioned_filter_block_test',
|
||||
'table/partitioned_filter_block_test.cc',
|
||||
'serial'],
|
||||
['comparator_db_test', 'db/comparator_db_test.cc', 'serial'],
|
||||
['compaction_job_stats_test', 'db/compaction_job_stats_test.cc', 'serial'],
|
||||
['thread_local_test', 'util/thread_local_test.cc', 'serial'],
|
||||
['version_builder_test', 'db/version_builder_test.cc', 'serial'],
|
||||
['db_range_del_test', 'db/db_range_del_test.cc', 'serial'],
|
||||
['table_test', 'table/table_test.cc', 'parallel'],
|
||||
['db_tailing_iter_test', 'db/db_tailing_iter_test.cc', 'serial'],
|
||||
['db_compaction_filter_test', 'db/db_compaction_filter_test.cc', 'parallel'],
|
||||
['options_util_test', 'utilities/options/options_util_test.cc', 'serial'],
|
||||
['dynamic_bloom_test', 'util/dynamic_bloom_test.cc', 'serial'],
|
||||
['db_basic_test', 'db/db_basic_test.cc', 'serial'],
|
||||
['db_merge_operator_test', 'db/db_merge_operator_test.cc', 'serial'],
|
||||
['manual_compaction_test', 'db/manual_compaction_test.cc', 'parallel'],
|
||||
['delete_scheduler_test', 'util/delete_scheduler_test.cc', 'serial'],
|
||||
['transaction_test', 'utilities/transactions/transaction_test.cc', 'serial'],
|
||||
['db_io_failure_test', 'db/db_io_failure_test.cc', 'serial'],
|
||||
['db_log_iter_test', 'db/db_log_iter_test.cc', 'serial'],
|
||||
['compact_on_deletion_collector_test',
|
||||
'utilities/table_properties_collectors/compact_on_deletion_collector_test.cc',
|
||||
'serial'],
|
||||
['env_test', 'env/env_test.cc', 'serial'],
|
||||
['db_wal_test', 'db/db_wal_test.cc', 'parallel'],
|
||||
['sim_cache_test', 'utilities/simulator_cache/sim_cache_test.cc', 'serial'],
|
||||
['db_memtable_test', 'db/db_memtable_test.cc', 'serial'],
|
||||
['db_universal_compaction_test',
|
||||
'db/db_universal_compaction_test.cc',
|
||||
'parallel'],
|
||||
['histogram_test', 'monitoring/histogram_test.cc', 'serial'],
|
||||
['util_merge_operators_test',
|
||||
'utilities/util_merge_operators_test.cc',
|
||||
'serial'],
|
||||
['fault_injection_test', 'db/fault_injection_test.cc', 'parallel'],
|
||||
['env_basic_test', 'env/env_basic_test.cc', 'serial'],
|
||||
['iostats_context_test', 'monitoring/iostats_context_test.cc', 'serial'],
|
||||
['memtable_list_test', 'db/memtable_list_test.cc', 'serial'],
|
||||
['column_family_test', 'db/column_family_test.cc', 'serial'],
|
||||
['db_properties_test', 'db/db_properties_test.cc', 'serial'],
|
||||
['version_edit_test', 'db/version_edit_test.cc', 'serial'],
|
||||
['skiplist_test', 'memtable/skiplist_test.cc', 'serial'],
|
||||
['lru_cache_test', 'cache/lru_cache_test.cc', 'serial'],
|
||||
['plain_table_db_test', 'db/plain_table_db_test.cc', 'serial'],
|
||||
['db_statistics_test', 'db/db_statistics_test.cc', 'serial']]
|
||||
|
||||
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
for test_cfg in ROCKS_TESTS:
|
||||
test_name = test_cfg[0]
|
||||
test_cc = test_cfg[1]
|
||||
ttype = "gtest" if test_cfg[2] == "parallel" else "simple"
|
||||
test_bin = test_name + "_bin"
|
||||
|
||||
cpp_binary (
|
||||
name = test_bin,
|
||||
srcs = [test_cc],
|
||||
deps = [":rocksdb_test_lib"],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = test_name,
|
||||
type = ttype,
|
||||
deps = [":" + test_bin],
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin]
|
||||
)
|
||||
+1
-4
@@ -11,8 +11,5 @@ build:
|
||||
verbosity: minimal
|
||||
test:
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run db_basic_test -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run db_test2 -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run db_test -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run env_test,env_basic_test -Concurrency 2
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from targets_builder import TARGETSBuilder
|
||||
from optparse import OptionParser
|
||||
import os
|
||||
import fnmatch
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from util import ColorString
|
||||
import util
|
||||
|
||||
# tests to export as libraries for inclusion in other projects
|
||||
_EXPORTED_TEST_LIBS = ["env_basic_test"]
|
||||
|
||||
# Parse src.mk files as a Dictionary of
|
||||
# VAR_NAME => list of files
|
||||
def parse_src_mk(repo_path):
|
||||
src_mk = repo_path + "/src.mk"
|
||||
src_files = {}
|
||||
for line in open(src_mk):
|
||||
line = line.strip()
|
||||
if len(line) == 0 or line[0] == '#':
|
||||
continue
|
||||
if '=' in line:
|
||||
current_src = line.split('=')[0].strip()
|
||||
src_files[current_src] = []
|
||||
elif '.cc' in line:
|
||||
src_path = line.split('.cc')[0].strip() + '.cc'
|
||||
src_files[current_src].append(src_path)
|
||||
return src_files
|
||||
|
||||
|
||||
# get all .cc / .c files
|
||||
def get_cc_files(repo_path):
|
||||
cc_files = []
|
||||
for root, dirnames, filenames in os.walk(repo_path):
|
||||
root = root[(len(repo_path) + 1):]
|
||||
if "java" in root:
|
||||
# Skip java
|
||||
continue
|
||||
for filename in fnmatch.filter(filenames, '*.cc'):
|
||||
cc_files.append(os.path.join(root, filename))
|
||||
for filename in fnmatch.filter(filenames, '*.c'):
|
||||
cc_files.append(os.path.join(root, filename))
|
||||
return cc_files
|
||||
|
||||
|
||||
# Get tests from Makefile
|
||||
def get_tests(repo_path):
|
||||
Makefile = repo_path + "/Makefile"
|
||||
|
||||
# Dictionary TEST_NAME => IS_PARALLEL
|
||||
tests = {}
|
||||
|
||||
found_tests = False
|
||||
for line in open(Makefile):
|
||||
line = line.strip()
|
||||
if line.startswith("TESTS ="):
|
||||
found_tests = True
|
||||
elif found_tests:
|
||||
if line.endswith("\\"):
|
||||
# remove the trailing \
|
||||
line = line[:-1]
|
||||
line = line.strip()
|
||||
tests[line] = False
|
||||
else:
|
||||
# we consumed all the tests
|
||||
break
|
||||
|
||||
found_parallel_tests = False
|
||||
for line in open(Makefile):
|
||||
line = line.strip()
|
||||
if line.startswith("PARALLEL_TEST ="):
|
||||
found_parallel_tests = True
|
||||
elif found_parallel_tests:
|
||||
if line.endswith("\\"):
|
||||
# remove the trailing \
|
||||
line = line[:-1]
|
||||
line = line.strip()
|
||||
tests[line] = True
|
||||
else:
|
||||
# we consumed all the parallel tests
|
||||
break
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
# Prepare TARGETS file for buck
|
||||
def generate_targets(repo_path):
|
||||
print(ColorString.info("Generating TARGETS"))
|
||||
# parsed src.mk file
|
||||
src_mk = parse_src_mk(repo_path)
|
||||
# get all .cc files
|
||||
cc_files = get_cc_files(repo_path)
|
||||
# get tests from Makefile
|
||||
tests = get_tests(repo_path)
|
||||
|
||||
if src_mk is None or cc_files is None or tests is None:
|
||||
return False
|
||||
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path)
|
||||
# rocksdb_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"])
|
||||
# rocksdb_test_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_test_lib",
|
||||
src_mk.get("MOCK_LIB_SOURCES", []) +
|
||||
src_mk.get("TEST_LIB_SOURCES", []) +
|
||||
src_mk.get("EXP_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"])
|
||||
# rocksdb_tools_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_tools_lib",
|
||||
src_mk.get("BENCH_LIB_SOURCES", []) +
|
||||
["util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
|
||||
# test for every test we found in the Makefile
|
||||
for test in 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)
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test
|
||||
TARGETS.add_library(test_library, match_src, [":rocksdb_test_lib"])
|
||||
TARGETS.flush_tests()
|
||||
|
||||
print(ColorString.info("Generated TARGETS Summary:"))
|
||||
print(ColorString.info("- %d libs" % TARGETS.total_lib))
|
||||
print(ColorString.info("- %d binarys" % TARGETS.total_bin))
|
||||
print(ColorString.info("- %d tests" % TARGETS.total_test))
|
||||
return True
|
||||
|
||||
|
||||
def get_rocksdb_path():
|
||||
# rocksdb = {script_dir}/..
|
||||
script_dir = os.path.dirname(sys.argv[0])
|
||||
script_dir = os.path.abspath(script_dir)
|
||||
rocksdb_path = os.path.abspath(
|
||||
os.path.join(script_dir, "../"))
|
||||
|
||||
return rocksdb_path
|
||||
|
||||
def exit_with_error(msg):
|
||||
print(ColorString.error(msg))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
# Generate TARGETS file for buck
|
||||
ok = generate_targets(get_rocksdb_path())
|
||||
if not ok:
|
||||
exit_with_error("Failed to generate TARGETS files")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
# Create a tmp directory for the test to use
|
||||
TEST_DIR=$(mktemp -d /dev/shm/fbcode_rocksdb_XXXXXXX)
|
||||
TEST_TMPDIR="$TEST_DIR" $@ && rm -rf "$TEST_DIR"
|
||||
@@ -0,0 +1,65 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import targets_cfg
|
||||
import pprint
|
||||
|
||||
# TODO(tec): replace this with PrettyPrinter
|
||||
def pretty_list(lst, indent=6):
|
||||
if lst is None or len(lst) == 0:
|
||||
return ""
|
||||
|
||||
if len(lst) == 1:
|
||||
return "\"%s\"" % lst[0]
|
||||
|
||||
separator = "\",\n%s\"" % (" " * indent)
|
||||
res = separator.join(lst)
|
||||
res = "\n" + (" " * indent) + "\"" + res + "\",\n" + (" " * (indent - 2))
|
||||
return res
|
||||
|
||||
|
||||
class TARGETSBuilder:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.targets_file = open(path, 'w')
|
||||
self.targets_file.write(targets_cfg.rocksdb_target_header)
|
||||
self.total_lib = 0
|
||||
self.total_bin = 0
|
||||
self.total_test = 0
|
||||
self.tests_cfg = []
|
||||
|
||||
def __del__(self):
|
||||
self.targets_file.close()
|
||||
|
||||
def add_library(self, name, srcs, deps=None, headers=None):
|
||||
if headers is None:
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
self.targets_file.write(targets_cfg.library_template % (
|
||||
name,
|
||||
headers,
|
||||
pretty_list(srcs),
|
||||
pretty_list(deps)))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_binary(self, name, srcs, deps=None):
|
||||
self.targets_file.write(targets_cfg.binary_template % (
|
||||
name,
|
||||
pretty_list(srcs),
|
||||
pretty_list(deps)))
|
||||
self.total_bin = self.total_bin + 1
|
||||
|
||||
def register_test(self, test_name, src, is_parallel):
|
||||
exec_mode = "serial"
|
||||
if is_parallel:
|
||||
exec_mode = "parallel"
|
||||
self.tests_cfg.append([test_name, str(src), str(exec_mode)])
|
||||
|
||||
self.total_test = self.total_test + 1
|
||||
|
||||
def flush_tests(self):
|
||||
self.targets_file.write(targets_cfg.unittests_template % (
|
||||
pprint.PrettyPrinter().pformat(self.tests_cfg)
|
||||
))
|
||||
|
||||
self.tests_cfg = []
|
||||
@@ -0,0 +1,112 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
rocksdb_target_header = """REPO_PATH = "internal_repo_rocksdb/repo/"
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
rocksdb_compiler_flags = [
|
||||
"-msse",
|
||||
"-msse4.2",
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DOS_LINUX",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-DTBB",
|
||||
# Needed to compile in fbcode
|
||||
"-Wno-expansion-to-defined",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
('bzip2', None, 'bz2'),
|
||||
('snappy', None, "snappy"),
|
||||
('zlib', None, 'z'),
|
||||
('gflags', None, 'gflags'),
|
||||
('lz4', None, 'lz4'),
|
||||
('zstd', None),
|
||||
('tbb', None),
|
||||
("numa", "2.0.8", "numa"),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
rocksdb_preprocessor_flags = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
"""
|
||||
|
||||
|
||||
library_template = """
|
||||
cpp_library(
|
||||
name = "%s",
|
||||
headers = %s,
|
||||
srcs = [%s],
|
||||
deps = [%s],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
"""
|
||||
|
||||
binary_template = """
|
||||
cpp_binary(
|
||||
name = "%s",
|
||||
srcs = [%s],
|
||||
deps = [%s],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
"""
|
||||
|
||||
unittests_template = """
|
||||
# [test_name, test_src, test_type]
|
||||
ROCKS_TESTS = %s
|
||||
|
||||
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
for test_cfg in ROCKS_TESTS:
|
||||
test_name = test_cfg[0]
|
||||
test_cc = test_cfg[1]
|
||||
ttype = "gtest" if test_cfg[2] == "parallel" else "simple"
|
||||
test_bin = test_name + "_bin"
|
||||
|
||||
cpp_binary (
|
||||
name = test_bin,
|
||||
srcs = [test_cc],
|
||||
deps = [":rocksdb_test_lib"],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = test_name,
|
||||
type = ttype,
|
||||
deps = [":" + test_bin],
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin]
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = "make_rocksdbjavastatic",
|
||||
type = "simple",
|
||||
command = ["internal_repo_rocksdb/make_rocksdbjavastatic.sh"],
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = "make_rocksdb_lite_release",
|
||||
type = "simple",
|
||||
command = ["internal_repo_rocksdb/make_rocksdb_lite_release.sh"],
|
||||
)
|
||||
"""
|
||||
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
This module keeps commonly used components.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import subprocess
|
||||
import os
|
||||
import time
|
||||
|
||||
class ColorString:
|
||||
""" Generate colorful strings on terminal """
|
||||
HEADER = '\033[95m'
|
||||
BLUE = '\033[94m'
|
||||
GREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
|
||||
@staticmethod
|
||||
def _make_color_str(text, color):
|
||||
return "".join([color, text.encode('utf-8'), ColorString.ENDC])
|
||||
|
||||
@staticmethod
|
||||
def ok(text):
|
||||
if ColorString.is_disabled:
|
||||
return text
|
||||
return ColorString._make_color_str(text, ColorString.GREEN)
|
||||
|
||||
@staticmethod
|
||||
def info(text):
|
||||
if ColorString.is_disabled:
|
||||
return text
|
||||
return ColorString._make_color_str(text, ColorString.BLUE)
|
||||
|
||||
@staticmethod
|
||||
def header(text):
|
||||
if ColorString.is_disabled:
|
||||
return text
|
||||
return ColorString._make_color_str(text, ColorString.HEADER)
|
||||
|
||||
@staticmethod
|
||||
def error(text):
|
||||
if ColorString.is_disabled:
|
||||
return text
|
||||
return ColorString._make_color_str(text, ColorString.FAIL)
|
||||
|
||||
@staticmethod
|
||||
def warning(text):
|
||||
if ColorString.is_disabled:
|
||||
return text
|
||||
return ColorString._make_color_str(text, ColorString.WARNING)
|
||||
|
||||
is_disabled = False
|
||||
|
||||
|
||||
def run_shell_command(shell_cmd, cmd_dir=None):
|
||||
""" Run a single shell command.
|
||||
@returns a tuple of shell command return code, stdout, stderr """
|
||||
|
||||
if cmd_dir is not None and not os.path.exists(cmd_dir):
|
||||
run_shell_command("mkdir -p %s" % cmd_dir)
|
||||
|
||||
start = time.time()
|
||||
print("\t>>> Running: " + shell_cmd)
|
||||
p = subprocess.Popen(shell_cmd,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cmd_dir)
|
||||
stdout, stderr = p.communicate()
|
||||
end = time.time()
|
||||
|
||||
# Report time if we spent more than 5 minutes executing a command
|
||||
execution_time = end - start
|
||||
if execution_time > (60 * 5):
|
||||
mins = (execution_time / 60)
|
||||
secs = (execution_time % 60)
|
||||
print("\t>time spent: %d minutes %d seconds" % (mins, secs))
|
||||
|
||||
|
||||
return p.returncode, stdout, stderr
|
||||
|
||||
|
||||
def run_shell_commands(shell_cmds, cmd_dir=None, verbose=False):
|
||||
""" Execute a sequence of shell commands, which is equivalent to
|
||||
running `cmd1 && cmd2 && cmd3`
|
||||
@returns boolean indication if all commands succeeds.
|
||||
"""
|
||||
|
||||
if cmd_dir:
|
||||
print("\t=== Set current working directory => %s" % cmd_dir)
|
||||
|
||||
for shell_cmd in shell_cmds:
|
||||
ret_code, stdout, stderr = run_shell_command(shell_cmd, cmd_dir)
|
||||
if stdout:
|
||||
if verbose or ret_code != 0:
|
||||
print(ColorString.info("stdout: \n"), stdout)
|
||||
if stderr:
|
||||
# contents in stderr is not necessarily to be error messages.
|
||||
if verbose or ret_code != 0:
|
||||
print(ColorString.error("stderr: \n"), stderr)
|
||||
if ret_code != 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -9,10 +9,12 @@ fi
|
||||
# Check clang-format-diff.py
|
||||
if ! which $CLANG_FORMAT_DIFF &> /dev/null
|
||||
then
|
||||
echo "You didn't have clang-format-diff.py available in your computer!"
|
||||
echo "You can download it by running: "
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "and move ${CLANG_FORMAT_DIFF} to some directory within PATH=${PATH}"
|
||||
echo "You can download clang-format by running: "
|
||||
echo " brew install clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
exit 128
|
||||
fi
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ PARALLELISM="$PARALLEL_J $PARALLEL_j"
|
||||
|
||||
DEBUG="OPT=-g"
|
||||
SHM="TEST_TMPDIR=/dev/shm/rocksdb"
|
||||
NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
@@ -191,7 +192,7 @@ UNIT_TEST_NON_SHM_COMMANDS="[
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'timeout': 86400,
|
||||
'shell':'$DEBUG make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
|
||||
'shell':'$NON_SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
|
||||
+240
-117
@@ -1,24 +1,50 @@
|
||||
# This script enables you running RocksDB tests by running
|
||||
# All the tests in parallel and utilizing all the cores
|
||||
# For db_test the script first lists and parses the tests
|
||||
# and then fires them up in parallel using async PS Job functionality
|
||||
# Run the script from the enlistment
|
||||
# All the tests concurrently and utilizing all the cores
|
||||
Param(
|
||||
[switch]$EnableJE = $false, # Use je executable
|
||||
[switch]$EnableRerun = $false, # Rerun failed tests sequentially at the end
|
||||
[string]$WorkFolder = "", # Direct tests to use that folder
|
||||
[int]$Limit = -1, # -1 means run all otherwise limit for testing purposes
|
||||
[string]$Exclude = "", # Expect a comma separated list, no spaces
|
||||
[string]$Run = "db_test", # Run db_test|db_test2|tests|testname1,testname2...
|
||||
[switch]$EnableJE = $false, # Look for and use _je executable, append _je to listed exclusions
|
||||
[switch]$RunAll = $false, # Will attempt discover all *_test[_je].exe binaries and run all
|
||||
# of them as Google suites. I.e. It will run test cases concurrently
|
||||
# except those mentioned as $Run, those will run as individual test cases
|
||||
# And any execlued with $ExcludeExes or $ExcludeCases
|
||||
# It will also not run any individual test cases
|
||||
# excluded but $ExcludeCasese
|
||||
[string]$SuiteRun = "", # Split test suites in test cases and run in parallel, not compatible with $RunAll
|
||||
[string]$Run = "", # Run specified executables in parallel but do not split to test cases
|
||||
[string]$ExcludeCases = "", # Exclude test cases, expects a comma separated list, no spaces
|
||||
# Takes effect when $RunAll or $SuiteRun is specified. Must have full
|
||||
# Test cases name including a group and a parameter if any
|
||||
[string]$ExcludeExes = "", # Exclude exes from consideration, expects a comma separated list,
|
||||
# no spaces. Takes effect only when $RunAll is specified
|
||||
[string]$WorkFolder = "", # Direct tests to use that folder. SSD or Ram drive are better options.
|
||||
# Number of async tasks that would run concurrently. Recommend a number below 64.
|
||||
# However, CPU utlization really depends on the storage media. Recommend ram based disk.
|
||||
# a value of 1 will run everything serially
|
||||
[int]$Concurrency = 16
|
||||
[int]$Concurrency = 8,
|
||||
[int]$Limit = -1 # -1 means do not limit for test purposes
|
||||
)
|
||||
|
||||
# Folders and commands must be fullpath to run assuming
|
||||
# the current folder is at the root of the git enlistment
|
||||
Get-Date
|
||||
$StartDate = (Get-Date)
|
||||
$StartDate
|
||||
|
||||
|
||||
$DebugPreference = "Continue"
|
||||
|
||||
# These tests are not google test suites and we should guard
|
||||
# Against running them as suites
|
||||
$RunOnly = New-Object System.Collections.Generic.HashSet[string]
|
||||
$RunOnly.Add("c_test") | Out-Null
|
||||
$RunOnly.Add("compact_on_deletion_collector_test") | Out-Null
|
||||
$RunOnly.Add("merge_test") | Out-Null
|
||||
$RunOnly.Add("stringappend_test") | Out-Null # Apparently incorrectly written
|
||||
$RunOnly.Add("backupable_db_test") | Out-Null # Disabled
|
||||
|
||||
|
||||
if($RunAll -and $SuiteRun -ne "") {
|
||||
Write-Error "$RunAll and $SuiteRun are not compatible"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If running under Appveyor assume that root
|
||||
[string]$Appveyor = $Env:APPVEYOR_BUILD_FOLDER
|
||||
@@ -47,40 +73,32 @@ if($WorkFolder -eq "") {
|
||||
}
|
||||
|
||||
Write-Output "Root: $RootFolder, WorkFolder: $WorkFolder"
|
||||
|
||||
# Use JEMALLOC executables
|
||||
if($Run -ceq "db_test" -or
|
||||
$Run -ceq "db_test2" -or
|
||||
$Run -ceq "db_basic_test") {
|
||||
|
||||
$file_name = $Run
|
||||
|
||||
if($EnableJE) {
|
||||
$file_name += "_je"
|
||||
}
|
||||
|
||||
$file_name += ".exe"
|
||||
|
||||
$db_test = -Join ($BinariesFolder, $file_name)
|
||||
|
||||
Write-Output "Binaries: $BinariesFolder db_test: $db_test"
|
||||
}
|
||||
|
||||
|
||||
#Exclusions that we do not want to run
|
||||
$ExcludeTests = New-Object System.Collections.Generic.HashSet[string]
|
||||
|
||||
|
||||
if($Exclude -ne "") {
|
||||
Write-Host "Exclude: $Exclude"
|
||||
$l = $Exclude -split ' '
|
||||
ForEach($t in $l) { $ExcludeTests.Add($t) | Out-Null }
|
||||
}
|
||||
Write-Output "BinariesFolder: $BinariesFolder, LogFolder: $LogFolder"
|
||||
|
||||
# Create test directories in the current folder
|
||||
md -Path $WorkFolder -ErrorAction Ignore | Out-Null
|
||||
md -Path $LogFolder -ErrorAction Ignore | Out-Null
|
||||
|
||||
|
||||
$ExcludeCasesSet = New-Object System.Collections.Generic.HashSet[string]
|
||||
if($ExcludeCases -ne "") {
|
||||
Write-Host "ExcludeCases: $ExcludeCases"
|
||||
$l = $ExcludeCases -split ' '
|
||||
ForEach($t in $l) {
|
||||
$ExcludeCasesSet.Add($t) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
$ExcludeExesSet = New-Object System.Collections.Generic.HashSet[string]
|
||||
if($ExcludeExes -ne "") {
|
||||
Write-Host "ExcludeExe: $ExcludeExes"
|
||||
$l = $ExcludeExes -split ' '
|
||||
ForEach($t in $l) {
|
||||
$ExcludeExesSet.Add($t) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Extract the names of its tests by running db_test with --gtest_list_tests.
|
||||
# This filter removes the "#"-introduced comments, and expands to
|
||||
# fully-qualified names by changing input like this:
|
||||
@@ -98,28 +116,37 @@ md -Path $LogFolder -ErrorAction Ignore | Out-Null
|
||||
# DBTest.WriteEmptyBatch
|
||||
# MultiThreaded/MultiThreadedDBTest.MultiThreaded/0
|
||||
# MultiThreaded/MultiThreadedDBTest.MultiThreaded/1
|
||||
#
|
||||
# Output into the parameter in a form TestName -> Log File Name
|
||||
function Normalize-DbTests($HashTable) {
|
||||
function ExtractTestCases([string]$GTestExe, $HashTable) {
|
||||
|
||||
$Tests = @()
|
||||
# Run db_test to get a list of tests and store it into $a array
|
||||
&$db_test --gtest_list_tests | tee -Variable Tests | Out-Null
|
||||
&$GTestExe --gtest_list_tests | tee -Variable Tests | Out-Null
|
||||
|
||||
# Current group
|
||||
$Group=""
|
||||
|
||||
ForEach( $l in $Tests) {
|
||||
# Trailing dot is a test group
|
||||
if( $l -match "\.$") {
|
||||
|
||||
# Leading whitespace is fine
|
||||
$l = $l -replace '^\s+',''
|
||||
# but no whitespace any other place
|
||||
if($l -match "\s+") {
|
||||
continue
|
||||
}
|
||||
# Trailing dot is a test group but no whitespace
|
||||
elseif ( $l -match "\.$" ) {
|
||||
$Group = $l
|
||||
} else {
|
||||
# Otherwise it is a test name, remove leading space
|
||||
$test = $l -replace '^\s+',''
|
||||
$test = $l
|
||||
# remove trailing comment if any and create a log name
|
||||
$test = $test -replace '\s+\#.*',''
|
||||
$test = "$Group$test"
|
||||
|
||||
if($ExcludeTests.Contains($test)) {
|
||||
if($ExcludeCasesSet.Contains($test)) {
|
||||
Write-Warning "$test case is excluded"
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -135,59 +162,130 @@ function Normalize-DbTests($HashTable) {
|
||||
|
||||
# The function removes trailing .exe siffix if any,
|
||||
# creates a name for the log file
|
||||
# Then adds the test name if it was not excluded into
|
||||
# a HashTable in a form of test_name -> log_path
|
||||
function MakeAndAdd([string]$token, $HashTable) {
|
||||
|
||||
$test_name = $token -replace '.exe$', ''
|
||||
$log_name = -join ($test_name, ".log")
|
||||
$log_path = -join ($LogFolder, $log_name)
|
||||
if(!$ExcludeTests.Contains($test_name)) {
|
||||
$HashTable.Add($test_name, $log_path)
|
||||
} else {
|
||||
Write-Warning "Test $test_name is excluded"
|
||||
}
|
||||
$HashTable.Add($test_name, $log_path)
|
||||
}
|
||||
|
||||
# The function scans build\Debug folder to discover
|
||||
# Test executables. It then populates a table with
|
||||
# Test executable name -> Log file
|
||||
function Discover-TestBinaries([string]$Pattern, $HashTable) {
|
||||
# This function takes a list of Suites to run
|
||||
# Lists all the test cases in each of the suite
|
||||
# and populates HashOfHashes
|
||||
# Ordered by suite(exe) @{ Exe = @{ TestCase = LogName }}
|
||||
function ProcessSuites($ListOfSuites, $HashOfHashes) {
|
||||
|
||||
$Exclusions = @("db_test*", "db_sanity_test*", "db_basic_test*")
|
||||
$suite_list = $ListOfSuites
|
||||
# Problem: if you run --gtest_list_tests on
|
||||
# a non Google Test executable then it will start executing
|
||||
# and we will get nowhere
|
||||
ForEach($suite in $suite_list) {
|
||||
|
||||
$p = -join ($BinariesFolder, $pattern)
|
||||
|
||||
Write-Host "Path: $p"
|
||||
|
||||
dir -Path $p -Exclude $Exclusions | ForEach-Object {
|
||||
MakeAndAdd -token ($_.Name) -HashTable $HashTable
|
||||
if($RunOnly.Contains($suite)) {
|
||||
Write-Warning "$suite is excluded from running as Google test suite"
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
$TestsToRun = [ordered]@{}
|
||||
|
||||
if($Run -ceq "db_test" -or
|
||||
$Run -ceq "db_test2" -or
|
||||
$Run -ceq "db_basic_test") {
|
||||
Normalize-DbTests -HashTable $TestsToRun
|
||||
} elseif($Run -ceq "tests") {
|
||||
if($EnableJE) {
|
||||
$pattern = "*_test_je.exe"
|
||||
$suite += "_je"
|
||||
}
|
||||
|
||||
$Cases = [ordered]@{}
|
||||
$Cases.Clear()
|
||||
$suite_exe = -Join ($BinariesFolder, $suite)
|
||||
ExtractTestCases -GTestExe $suite_exe -HashTable $Cases
|
||||
if($Cases.Count -gt 0) {
|
||||
$HashOfHashes.Add($suite, $Cases);
|
||||
}
|
||||
}
|
||||
|
||||
# Make logs and run
|
||||
if($CasesToRun.Count -lt 1) {
|
||||
Write-Error "Failed to extract tests from $SuiteRun"
|
||||
exit 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# This will contain all test executables to run
|
||||
|
||||
# Hash table that contains all non suite
|
||||
# Test executable to run
|
||||
$TestExes = [ordered]@{}
|
||||
|
||||
# Check for test exe that are not
|
||||
# Google Test Suites
|
||||
# Since this is explicitely mentioned it is not subject
|
||||
# for exclusions
|
||||
if($Run -ne "") {
|
||||
|
||||
$test_list = $Run -split ' '
|
||||
|
||||
ForEach($t in $test_list) {
|
||||
|
||||
if($EnableJE) {
|
||||
$t += "_je"
|
||||
}
|
||||
|
||||
MakeAndAdd -token $t -HashTable $TestExes
|
||||
}
|
||||
|
||||
if($TestExes.Count -lt 1) {
|
||||
Write-Error "Failed to extract tests from $Run"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Ordered by exe @{ Exe = @{ TestCase = LogName }}
|
||||
$CasesToRun = [ordered]@{}
|
||||
|
||||
if($SuiteRun -ne "") {
|
||||
$suite_list = $SuiteRun -split ' '
|
||||
ProcessSuites -ListOfSuites $suite_list -HashOfHashes $CasesToRun
|
||||
}
|
||||
|
||||
if($RunAll) {
|
||||
# Discover all the test binaries
|
||||
if($EnableJE) {
|
||||
$pattern = "*_test_je.exe"
|
||||
} else {
|
||||
$pattern = "*_test.exe"
|
||||
}
|
||||
|
||||
|
||||
$search_path = -join ($BinariesFolder, $pattern)
|
||||
Write-Host "Binaries Search Path: $search_path"
|
||||
|
||||
$ListOfExe = @()
|
||||
dir -Path $search_path | ForEach-Object {
|
||||
$ListOfExe += ($_.Name)
|
||||
}
|
||||
|
||||
# Exclude those in RunOnly from running as suites
|
||||
$ListOfSuites = @()
|
||||
ForEach($e in $ListOfExe) {
|
||||
|
||||
$e = $e -replace '.exe$', ''
|
||||
$bare_name = $e -replace '_je$', ''
|
||||
|
||||
if($ExcludeExesSet.Contains($bare_name)) {
|
||||
Write-Warning "Test $e is excluded"
|
||||
continue
|
||||
}
|
||||
|
||||
if($RunOnly.Contains($e)) {
|
||||
MakeAndAdd -token $e -HashTable $TestExes
|
||||
} else {
|
||||
$pattern = "*_test.exe"
|
||||
$ListOfSuites += $bare_name
|
||||
}
|
||||
Discover-TestBinaries -Pattern $pattern -HashTable $TestsToRun
|
||||
} else {
|
||||
}
|
||||
|
||||
$test_list = $Run -split ' '
|
||||
|
||||
ForEach($t in $test_list) {
|
||||
MakeAndAdd -token $t -HashTable $TestsToRun
|
||||
}
|
||||
ProcessSuites -ListOfSuites $ListOfSuites -HashOfHashes $CasesToRun
|
||||
}
|
||||
|
||||
$NumTestsToStart = $TestsToRun.Count
|
||||
if($Limit -ge 0 -and $NumTestsToStart -gt $Limit) {
|
||||
$NumTestsToStart = $Limit
|
||||
}
|
||||
|
||||
Write-Host "Attempting to start: $NumTestsToStart tests"
|
||||
|
||||
@@ -205,13 +303,13 @@ $InvokeTestAsync = {
|
||||
|
||||
# Hash that contains tests to rerun if any failed
|
||||
# Those tests will be rerun sequentially
|
||||
$Rerun = [ordered]@{}
|
||||
# $Rerun = [ordered]@{}
|
||||
# Test limiting factor here
|
||||
$count = 0
|
||||
[int]$count = 0
|
||||
# Overall status
|
||||
[bool]$success = $true;
|
||||
|
||||
function RunJobs($TestToLog, [int]$ConcurrencyVal, [bool]$AddForRerun)
|
||||
function RunJobs($Suites, $TestCmds, [int]$ConcurrencyVal)
|
||||
{
|
||||
# Array to wait for any of the running jobs
|
||||
$jobs = @()
|
||||
@@ -220,38 +318,66 @@ function RunJobs($TestToLog, [int]$ConcurrencyVal, [bool]$AddForRerun)
|
||||
|
||||
# Wait for all to finish and get the results
|
||||
while(($JobToLog.Count -gt 0) -or
|
||||
($TestToLog.Count -gt 0)) {
|
||||
($TestCmds.Count -gt 0) -or
|
||||
($Suites.Count -gt 0)) {
|
||||
|
||||
# Make sure we have maximum concurrent jobs running if anything
|
||||
# and the $Limit either not set or allows to proceed
|
||||
while(($JobToLog.Count -lt $ConcurrencyVal) -and
|
||||
(($TestToLog.Count -gt 0) -and
|
||||
((($TestCmds.Count -gt 0) -or ($Suites.Count -gt 0)) -and
|
||||
(($Limit -lt 0) -or ($count -lt $Limit)))) {
|
||||
|
||||
# We always favore suites to run if available
|
||||
[string]$exe_name = ""
|
||||
[string]$log_path = ""
|
||||
$Cases = @{}
|
||||
|
||||
# We only need the first key
|
||||
foreach($key in $TestToLog.keys) {
|
||||
$k = $key
|
||||
if($Suites.Count -gt 0) {
|
||||
# Will teh first one
|
||||
ForEach($e in $Suites.Keys) {
|
||||
$exe_name = $e
|
||||
$Cases = $Suites[$e]
|
||||
break
|
||||
}
|
||||
}
|
||||
[string]$test_case = ""
|
||||
[string]$log_path = ""
|
||||
ForEach($c in $Cases.Keys) {
|
||||
$test_case = $c
|
||||
$log_path = $Cases[$c]
|
||||
break
|
||||
}
|
||||
|
||||
Write-Host "Starting $k"
|
||||
$log_path = ($TestToLog.$k)
|
||||
Write-Host "Starting $exe_name::$test_case"
|
||||
[string]$Exe = -Join ($BinariesFolder, $exe_name)
|
||||
$job = Start-Job -Name "$exe_name::$test_case" -ArgumentList @($Exe,$test_case,$log_path) -ScriptBlock $InvokeTestCase
|
||||
$JobToLog.Add($job, $log_path)
|
||||
|
||||
$Cases.Remove($test_case)
|
||||
if($Cases.Count -lt 1) {
|
||||
$Suites.Remove($exe_name)
|
||||
}
|
||||
|
||||
} elseif ($TestCmds.Count -gt 0) {
|
||||
|
||||
ForEach($e in $TestCmds.Keys) {
|
||||
$exe_name = $e
|
||||
$log_path = $TestCmds[$e]
|
||||
break
|
||||
}
|
||||
|
||||
[string]$Exe = -Join ($BinariesFolder, $exe_name)
|
||||
$job = Start-Job -Name $exe_name -ScriptBlock $InvokeTestAsync -ArgumentList @($Exe,$log_path)
|
||||
$JobToLog.Add($job, $log_path)
|
||||
|
||||
$TestCmds.Remove($exe_name)
|
||||
|
||||
if($Run -ceq "db_test" -or
|
||||
$Run -ceq "db_test2" -or
|
||||
$Run -ceq "db_basic_test") {
|
||||
$job = Start-Job -Name $k -ScriptBlock $InvokeTestCase -ArgumentList @($db_test,$k,$log_path)
|
||||
} else {
|
||||
[string]$Exe = -Join ($BinariesFolder, $k)
|
||||
$job = Start-Job -Name $k -ScriptBlock $InvokeTestAsync -ArgumentList @($exe,$log_path)
|
||||
Write-Error "In the job loop but nothing to run"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$JobToLog.Add($job, $log_path)
|
||||
$TestToLog.Remove($k)
|
||||
|
||||
++$count
|
||||
}
|
||||
} # End of Job starting loop
|
||||
|
||||
if($JobToLog.Count -lt 1) {
|
||||
break
|
||||
@@ -296,9 +422,6 @@ function RunJobs($TestToLog, [int]$ConcurrencyVal, [bool]$AddForRerun)
|
||||
$success = $false;
|
||||
Write-Warning $message
|
||||
$log_content | Write-Warning
|
||||
if($AddForRerun) {
|
||||
$Rerun.Add($completed.Name, $log)
|
||||
}
|
||||
} else {
|
||||
Write-Host $message
|
||||
}
|
||||
@@ -310,16 +433,14 @@ function RunJobs($TestToLog, [int]$ConcurrencyVal, [bool]$AddForRerun)
|
||||
}
|
||||
}
|
||||
|
||||
RunJobs -TestToLog $TestsToRun -ConcurrencyVal $Concurrency -AddForRerun $EnableRerun
|
||||
RunJobs -Suites $CasesToRun -TestCmds $TestExes -ConcurrencyVal $Concurrency
|
||||
|
||||
if($Rerun.Count -gt 0) {
|
||||
Write-Host "Rerunning " ($Rerun.Count) " tests sequentially"
|
||||
$success = $true
|
||||
$count = 0
|
||||
RunJobs -TestToLog $Rerun -ConcurrencyVal 1 -AddForRerun $false
|
||||
}
|
||||
$EndDate = (Get-Date)
|
||||
|
||||
Get-Date
|
||||
New-TimeSpan -Start $StartDate -End $EndDate |
|
||||
ForEach-Object {
|
||||
"Elapsed time: {0:g}" -f $_
|
||||
}
|
||||
|
||||
|
||||
if(!$success) {
|
||||
@@ -327,7 +448,9 @@ if(!$success) {
|
||||
# So we simply exit
|
||||
# Remove-Job -Job $jobs -Force
|
||||
# indicate failure using this exit code
|
||||
exit 12345
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
File renamed without changes.
+2
-2
@@ -14,9 +14,9 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "util/clock_cache.h"
|
||||
#include "cache/clock_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/lru_cache.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
// 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 "util/clock_cache.h"
|
||||
#include "cache/clock_cache.h"
|
||||
|
||||
#ifndef SUPPORT_CLOCK_CACHE
|
||||
|
||||
@@ -30,10 +30,10 @@ 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/port.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/sharded_cache.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
File renamed without changes.
+1
-1
@@ -11,7 +11,7 @@
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include "util/lru_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
Vendored
+1
-1
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/sharded_cache.h"
|
||||
#include "cache/sharded_cache.h"
|
||||
|
||||
#include "port/port.h"
|
||||
#include "util/autovector.h"
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "util/lru_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include "util/sharded_cache.h"
|
||||
#include "cache/sharded_cache.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
File renamed without changes.
@@ -1,22 +1,21 @@
|
||||
# - Find JeMalloc library
|
||||
# Find the native JeMalloc includes and library
|
||||
# This module defines
|
||||
# JEMALLOC_INCLUDE_DIRS, where to find jemalloc.h, Set when
|
||||
# JEMALLOC_INCLUDE_DIR is found.
|
||||
# JEMALLOC_LIBRARIES, libraries to link against to use JeMalloc.
|
||||
# JEMALLOC_FOUND, If false, do not try to use JeMalloc.
|
||||
#
|
||||
# JEMALLOC_INCLUDE_DIR - 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
|
||||
jemalloc/jemalloc.h)
|
||||
NAMES jemalloc/jemalloc.h
|
||||
HINTS ${JEMALLOC_ROOT_DIR}/include)
|
||||
|
||||
find_library(JEMALLOC_LIBRARIES
|
||||
jemalloc)
|
||||
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_DIR)
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
JEMALLOC_INCLUDE_DIR
|
||||
mark_as_advanced(
|
||||
JEMALLOC_LIBRARIES
|
||||
)
|
||||
JEMALLOC_INCLUDE_DIR)
|
||||
@@ -13,8 +13,6 @@ find_library(SNAPPY_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${SNAPPY_ROOT_DIR}/lib)
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set UUID_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(snappy DEFAULT_MSG SNAPPY_LIBRARIES SNAPPY_INCLUDE_DIR)
|
||||
|
||||
|
||||
+14
-4
@@ -16,11 +16,12 @@
|
||||
#include "db/compaction_iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -29,9 +30,9 @@
|
||||
#include "table/block_based_table_builder.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/iostats_context_imp.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_status_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -103,6 +104,10 @@ Status BuildTable(
|
||||
unique_ptr<WritableFileWriter> file_writer;
|
||||
{
|
||||
unique_ptr<WritableFile> file;
|
||||
#ifndef NDEBUG
|
||||
bool use_direct_writes = env_options.use_direct_writes;
|
||||
TEST_SYNC_POINT_CALLBACK("BuildTable:create_file", &use_direct_writes);
|
||||
#endif // !NDEBUG
|
||||
s = NewWritableFile(env, fname, &file, env_options);
|
||||
if (!s.ok()) {
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
@@ -172,7 +177,7 @@ Status BuildTable(
|
||||
// Finish and check for file errors
|
||||
if (s.ok() && !empty) {
|
||||
StopWatch sw(env, ioptions.statistics, TABLE_SYNC_MICROS);
|
||||
file_writer->Sync(ioptions.use_fsync);
|
||||
s = file_writer->Sync(ioptions.use_fsync);
|
||||
}
|
||||
if (s.ok() && !empty) {
|
||||
s = file_writer->Close();
|
||||
@@ -180,6 +185,11 @@ Status BuildTable(
|
||||
|
||||
if (s.ok() && !empty) {
|
||||
// Verify that the table is usable
|
||||
// We set for_compaction to false and don't OptimizeForCompactionTableRead
|
||||
// here because this is a special case after we finish the table building
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// we will regrad this verification as user reads since the goal is
|
||||
// to cache it here for further user reads
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
ReadOptions(), env_options, internal_comparator, meta->fd,
|
||||
nullptr /* range_del_agg */, nullptr,
|
||||
|
||||
+1
-1
@@ -10,6 +10,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/listener.h"
|
||||
@@ -18,7 +19,6 @@
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/cf_options.h"
|
||||
#include "util/event_logger.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -32,8 +32,10 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/utilities/backupable_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using rocksdb::BytewiseComparator;
|
||||
using rocksdb::Cache;
|
||||
using rocksdb::ColumnFamilyDescriptor;
|
||||
using rocksdb::ColumnFamilyHandle;
|
||||
@@ -75,6 +77,7 @@ using rocksdb::SstFileWriter;
|
||||
using rocksdb::Status;
|
||||
using rocksdb::WritableFile;
|
||||
using rocksdb::WriteBatch;
|
||||
using rocksdb::WriteBatchWithIndex;
|
||||
using rocksdb::WriteOptions;
|
||||
using rocksdb::LiveFileMetaData;
|
||||
using rocksdb::BackupEngine;
|
||||
@@ -95,6 +98,7 @@ struct rocksdb_backup_engine_info_t { std::vector<BackupInfo> rep; };
|
||||
struct rocksdb_restore_options_t { RestoreOptions rep; };
|
||||
struct rocksdb_iterator_t { Iterator* rep; };
|
||||
struct rocksdb_writebatch_t { WriteBatch rep; };
|
||||
struct rocksdb_writebatch_wi_t { WriteBatchWithIndex* rep; };
|
||||
struct rocksdb_snapshot_t { const Snapshot* rep; };
|
||||
struct rocksdb_flushoptions_t { FlushOptions rep; };
|
||||
struct rocksdb_fifo_compaction_options_t { CompactionOptionsFIFO rep; };
|
||||
@@ -1356,6 +1360,377 @@ void rocksdb_writebatch_rollback_to_save_point(rocksdb_writebatch_t* b,
|
||||
SaveError(errptr, b->rep.RollbackToSavePoint());
|
||||
}
|
||||
|
||||
rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create(size_t reserved_bytes, unsigned char overwrite_key) {
|
||||
rocksdb_writebatch_wi_t* b = new rocksdb_writebatch_wi_t;
|
||||
b->rep = new WriteBatchWithIndex(BytewiseComparator(), reserved_bytes, overwrite_key);
|
||||
return b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_destroy(rocksdb_writebatch_wi_t* b) {
|
||||
if (b->rep) {
|
||||
delete b->rep;
|
||||
}
|
||||
delete b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_clear(rocksdb_writebatch_wi_t* b) {
|
||||
b->rep->Clear();
|
||||
}
|
||||
|
||||
int rocksdb_writebatch_wi_count(rocksdb_writebatch_wi_t* b) {
|
||||
return b->rep->GetWriteBatch()->Count();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_put(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen) {
|
||||
b->rep->Put(Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_put_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen) {
|
||||
b->rep->Put(column_family->rep, Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_putv(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes,
|
||||
int num_values, const char* const* values_list,
|
||||
const size_t* values_list_sizes) {
|
||||
std::vector<Slice> key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
key_slices[i] = Slice(keys_list[i], keys_list_sizes[i]);
|
||||
}
|
||||
std::vector<Slice> value_slices(num_values);
|
||||
for (int i = 0; i < num_values; i++) {
|
||||
value_slices[i] = Slice(values_list[i], values_list_sizes[i]);
|
||||
}
|
||||
b->rep->Put(SliceParts(key_slices.data(), num_keys),
|
||||
SliceParts(value_slices.data(), num_values));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_putv_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
int num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes,
|
||||
int num_values, const char* const* values_list,
|
||||
const size_t* values_list_sizes) {
|
||||
std::vector<Slice> key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
key_slices[i] = Slice(keys_list[i], keys_list_sizes[i]);
|
||||
}
|
||||
std::vector<Slice> value_slices(num_values);
|
||||
for (int i = 0; i < num_values; i++) {
|
||||
value_slices[i] = Slice(values_list[i], values_list_sizes[i]);
|
||||
}
|
||||
b->rep->Put(column_family->rep, SliceParts(key_slices.data(), num_keys),
|
||||
SliceParts(value_slices.data(), num_values));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_merge(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen) {
|
||||
b->rep->Merge(Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_merge_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen) {
|
||||
b->rep->Merge(column_family->rep, Slice(key, klen), Slice(val, vlen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_mergev(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes,
|
||||
int num_values, const char* const* values_list,
|
||||
const size_t* values_list_sizes) {
|
||||
std::vector<Slice> key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
key_slices[i] = Slice(keys_list[i], keys_list_sizes[i]);
|
||||
}
|
||||
std::vector<Slice> value_slices(num_values);
|
||||
for (int i = 0; i < num_values; i++) {
|
||||
value_slices[i] = Slice(values_list[i], values_list_sizes[i]);
|
||||
}
|
||||
b->rep->Merge(SliceParts(key_slices.data(), num_keys),
|
||||
SliceParts(value_slices.data(), num_values));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_mergev_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
int num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes,
|
||||
int num_values, const char* const* values_list,
|
||||
const size_t* values_list_sizes) {
|
||||
std::vector<Slice> key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
key_slices[i] = Slice(keys_list[i], keys_list_sizes[i]);
|
||||
}
|
||||
std::vector<Slice> value_slices(num_values);
|
||||
for (int i = 0; i < num_values; i++) {
|
||||
value_slices[i] = Slice(values_list[i], values_list_sizes[i]);
|
||||
}
|
||||
b->rep->Merge(column_family->rep, SliceParts(key_slices.data(), num_keys),
|
||||
SliceParts(value_slices.data(), num_values));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
const char* key, size_t klen) {
|
||||
b->rep->Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen) {
|
||||
b->rep->Delete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_deletev(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes) {
|
||||
std::vector<Slice> key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
key_slices[i] = Slice(keys_list[i], keys_list_sizes[i]);
|
||||
}
|
||||
b->rep->Delete(SliceParts(key_slices.data(), num_keys));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_deletev_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
int num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes) {
|
||||
std::vector<Slice> key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
key_slices[i] = Slice(keys_list[i], keys_list_sizes[i]);
|
||||
}
|
||||
b->rep->Delete(column_family->rep, SliceParts(key_slices.data(), num_keys));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete_range(rocksdb_writebatch_wi_t* b,
|
||||
const char* start_key,
|
||||
size_t start_key_len, const char* end_key,
|
||||
size_t end_key_len) {
|
||||
b->rep->DeleteRange(Slice(start_key, start_key_len),
|
||||
Slice(end_key, end_key_len));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete_range_cf(
|
||||
rocksdb_writebatch_wi_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* start_key, size_t start_key_len, const char* end_key,
|
||||
size_t end_key_len) {
|
||||
b->rep->DeleteRange(column_family->rep, Slice(start_key, start_key_len),
|
||||
Slice(end_key, end_key_len));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete_rangev(rocksdb_writebatch_wi_t* b, int num_keys,
|
||||
const char* const* start_keys_list,
|
||||
const size_t* start_keys_list_sizes,
|
||||
const char* const* end_keys_list,
|
||||
const size_t* end_keys_list_sizes) {
|
||||
std::vector<Slice> start_key_slices(num_keys);
|
||||
std::vector<Slice> end_key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
start_key_slices[i] = Slice(start_keys_list[i], start_keys_list_sizes[i]);
|
||||
end_key_slices[i] = Slice(end_keys_list[i], end_keys_list_sizes[i]);
|
||||
}
|
||||
b->rep->DeleteRange(SliceParts(start_key_slices.data(), num_keys),
|
||||
SliceParts(end_key_slices.data(), num_keys));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete_rangev_cf(
|
||||
rocksdb_writebatch_wi_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
int num_keys, const char* const* start_keys_list,
|
||||
const size_t* start_keys_list_sizes, const char* const* end_keys_list,
|
||||
const size_t* end_keys_list_sizes) {
|
||||
std::vector<Slice> start_key_slices(num_keys);
|
||||
std::vector<Slice> end_key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
start_key_slices[i] = Slice(start_keys_list[i], start_keys_list_sizes[i]);
|
||||
end_key_slices[i] = Slice(end_keys_list[i], end_keys_list_sizes[i]);
|
||||
}
|
||||
b->rep->DeleteRange(column_family->rep,
|
||||
SliceParts(start_key_slices.data(), num_keys),
|
||||
SliceParts(end_key_slices.data(), num_keys));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_put_log_data(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
const char* blob, size_t len) {
|
||||
b->rep->PutLogData(Slice(blob, len));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_iterate(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
void* state,
|
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
|
||||
void (*deleted)(void*, const char* k, size_t klen)) {
|
||||
class H : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
H handler;
|
||||
handler.state_ = state;
|
||||
handler.put_ = put;
|
||||
handler.deleted_ = deleted;
|
||||
b->rep->GetWriteBatch()->Iterate(&handler);
|
||||
}
|
||||
|
||||
const char* rocksdb_writebatch_wi_data(rocksdb_writebatch_wi_t* b, size_t* size) {
|
||||
WriteBatch* wb = b->rep->GetWriteBatch();
|
||||
*size = wb->GetDataSize();
|
||||
return wb->Data().c_str();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_set_save_point(rocksdb_writebatch_wi_t* b) {
|
||||
b->rep->SetSavePoint();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_rollback_to_save_point(rocksdb_writebatch_wi_t* b,
|
||||
char** errptr) {
|
||||
SaveError(errptr, b->rep->RollbackToSavePoint());
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base(
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
rocksdb_iterator_t* base_iterator) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = wbwi->rep->NewIteratorWithBase(base_iterator->rep);
|
||||
delete base_iterator;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_column_family_handle_t* column_family) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = wbwi->rep->NewIteratorWithBase(column_family->rep, base_iterator->rep);
|
||||
delete base_iterator;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_writebatch_wi_get_from_batch(
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
const rocksdb_options_t* options,
|
||||
const char* key, size_t keylen,
|
||||
size_t* vallen,
|
||||
char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = wbwi->rep->GetFromBatch(options->rep, Slice(key, keylen), &tmp);
|
||||
if (s.ok()) {
|
||||
*vallen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vallen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_writebatch_wi_get_from_batch_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
const rocksdb_options_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
size_t* vallen,
|
||||
char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = wbwi->rep->GetFromBatch(column_family->rep, options->rep,
|
||||
Slice(key, keylen), &tmp);
|
||||
if (s.ok()) {
|
||||
*vallen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vallen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_writebatch_wi_get_from_batch_and_db(
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key, size_t keylen,
|
||||
size_t* vallen,
|
||||
char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = wbwi->rep->GetFromBatchAndDB(db->rep, options->rep, Slice(key, keylen), &tmp);
|
||||
if (s.ok()) {
|
||||
*vallen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vallen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_writebatch_wi_get_from_batch_and_db_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t keylen,
|
||||
size_t* vallen,
|
||||
char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = wbwi->rep->GetFromBatchAndDB(db->rep, options->rep, column_family->rep,
|
||||
Slice(key, keylen), &tmp);
|
||||
if (s.ok()) {
|
||||
*vallen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vallen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_write_writebatch_wi(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
char** errptr) {
|
||||
WriteBatch* wb = wbwi->rep->GetWriteBatch();
|
||||
SaveError(errptr, db->rep->Write(options->rep, wb));
|
||||
}
|
||||
|
||||
rocksdb_block_based_table_options_t*
|
||||
rocksdb_block_based_options_create() {
|
||||
return new rocksdb_block_based_table_options_t;
|
||||
@@ -1746,9 +2121,9 @@ void rocksdb_options_set_use_direct_reads(rocksdb_options_t* opt,
|
||||
opt->rep.use_direct_reads = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_use_direct_writes(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.use_direct_writes = v;
|
||||
void rocksdb_options_set_use_direct_io_for_flush_and_compaction(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.use_direct_io_for_flush_and_compaction = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_allow_mmap_reads(
|
||||
@@ -2397,8 +2772,7 @@ void rocksdb_envoptions_destroy(rocksdb_envoptions_t* opt) { delete opt; }
|
||||
rocksdb_sstfilewriter_t* rocksdb_sstfilewriter_create(
|
||||
const rocksdb_envoptions_t* env, const rocksdb_options_t* io_options) {
|
||||
rocksdb_sstfilewriter_t* writer = new rocksdb_sstfilewriter_t;
|
||||
writer->rep =
|
||||
new SstFileWriter(env->rep, io_options->rep, io_options->rep.comparator);
|
||||
writer->rep = new SstFileWriter(env->rep, io_options->rep);
|
||||
return writer;
|
||||
}
|
||||
|
||||
@@ -2406,8 +2780,7 @@ rocksdb_sstfilewriter_t* rocksdb_sstfilewriter_create_with_comparator(
|
||||
const rocksdb_envoptions_t* env, const rocksdb_options_t* io_options,
|
||||
const rocksdb_comparator_t* comparator) {
|
||||
rocksdb_sstfilewriter_t* writer = new rocksdb_sstfilewriter_t;
|
||||
writer->rep =
|
||||
new SstFileWriter(env->rep, io_options->rep, io_options->rep.comparator);
|
||||
writer->rep = new SstFileWriter(env->rep, io_options->rep);
|
||||
return writer;
|
||||
}
|
||||
|
||||
|
||||
+125
@@ -87,6 +87,16 @@ static void Free(char** ptr) {
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckValue(
|
||||
char* err,
|
||||
const char* expected,
|
||||
char** actual,
|
||||
size_t actual_length) {
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, *actual, actual_length);
|
||||
Free(actual);
|
||||
}
|
||||
|
||||
static void CheckGet(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
@@ -608,6 +618,90 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_destroy(wb2);
|
||||
}
|
||||
|
||||
StartPhase("writebatch_wi");
|
||||
{
|
||||
rocksdb_writebatch_wi_t* wbi = rocksdb_writebatch_wi_create(0, 1);
|
||||
rocksdb_writebatch_wi_put(wbi, "foo", 3, "a", 1);
|
||||
rocksdb_writebatch_wi_clear(wbi);
|
||||
rocksdb_writebatch_wi_put(wbi, "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_wi_put(wbi, "box", 3, "c", 1);
|
||||
rocksdb_writebatch_wi_delete(wbi, "bar", 3);
|
||||
int count = rocksdb_writebatch_wi_count(wbi);
|
||||
CheckCondition(count == 3);
|
||||
size_t size;
|
||||
char* value;
|
||||
value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "box", 3, &size, &err);
|
||||
CheckValue(err, "c", &value, size);
|
||||
value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "bar", 3, &size, &err);
|
||||
CheckValue(err, NULL, &value, size);
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions, "foo", 3, &size, &err);
|
||||
CheckValue(err, "hello", &value, size);
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions, "box", 3, &size, &err);
|
||||
CheckValue(err, "c", &value, size);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wbi, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
CheckGet(db, roptions, "bar", NULL);
|
||||
CheckGet(db, roptions, "box", "c");
|
||||
int pos = 0;
|
||||
rocksdb_writebatch_wi_iterate(wbi, &pos, CheckPut, CheckDel);
|
||||
CheckCondition(pos == 3);
|
||||
rocksdb_writebatch_wi_clear(wbi);
|
||||
rocksdb_writebatch_wi_put(wbi, "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_wi_put(wbi, "bay", 3, "d", 1);
|
||||
rocksdb_writebatch_wi_delete_range(wbi, "bar", 3, "bay", 3);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wbi, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "bar", NULL);
|
||||
CheckGet(db, roptions, "bay", "d");
|
||||
rocksdb_writebatch_wi_clear(wbi);
|
||||
const char* start_list[1] = {"bay"};
|
||||
const size_t start_sizes[1] = {3};
|
||||
const char* end_list[1] = {"baz"};
|
||||
const size_t end_sizes[1] = {3};
|
||||
rocksdb_writebatch_wi_delete_rangev(wbi, 1, start_list, start_sizes, end_list,
|
||||
end_sizes);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wbi, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "bay", NULL);
|
||||
rocksdb_writebatch_wi_destroy(wbi);
|
||||
}
|
||||
|
||||
StartPhase("writebatch_wi_vectors");
|
||||
{
|
||||
rocksdb_writebatch_wi_t* wb = rocksdb_writebatch_wi_create(0, 1);
|
||||
const char* k_list[2] = { "z", "ap" };
|
||||
const size_t k_sizes[2] = { 1, 2 };
|
||||
const char* v_list[3] = { "x", "y", "z" };
|
||||
const size_t v_sizes[3] = { 1, 1, 1 };
|
||||
rocksdb_writebatch_wi_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "zap", "xyz");
|
||||
rocksdb_writebatch_wi_delete(wb, "zap", 3);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "zap", NULL);
|
||||
rocksdb_writebatch_wi_destroy(wb);
|
||||
}
|
||||
|
||||
StartPhase("writebatch_wi_savepoint");
|
||||
{
|
||||
rocksdb_writebatch_wi_t* wb = rocksdb_writebatch_wi_create(0, 1);
|
||||
rocksdb_writebatch_wi_set_save_point(wb);
|
||||
const char* k_list[2] = {"z", "ap"};
|
||||
const size_t k_sizes[2] = {1, 2};
|
||||
const char* v_list[3] = {"x", "y", "z"};
|
||||
const size_t v_sizes[3] = {1, 1, 1};
|
||||
rocksdb_writebatch_wi_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
|
||||
rocksdb_writebatch_wi_rollback_to_save_point(wb, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "zap", NULL);
|
||||
rocksdb_writebatch_wi_destroy(wb);
|
||||
}
|
||||
|
||||
StartPhase("iter");
|
||||
{
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
|
||||
@@ -634,6 +728,37 @@ int main(int argc, char** argv) {
|
||||
rocksdb_iter_destroy(iter);
|
||||
}
|
||||
|
||||
StartPhase("wbwi_iter");
|
||||
{
|
||||
rocksdb_iterator_t* base_iter = rocksdb_create_iterator(db, roptions);
|
||||
rocksdb_writebatch_wi_t* wbi = rocksdb_writebatch_wi_create(0, 1);
|
||||
rocksdb_writebatch_wi_put(wbi, "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_wi_delete(wbi, "foo", 3);
|
||||
rocksdb_iterator_t* iter = rocksdb_writebatch_wi_create_iterator_with_base(wbi, base_iter);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "bar", "b");
|
||||
rocksdb_iter_next(iter);
|
||||
CheckIter(iter, "box", "c");
|
||||
rocksdb_iter_prev(iter);
|
||||
CheckIter(iter, "bar", "b");
|
||||
rocksdb_iter_prev(iter);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_last(iter);
|
||||
CheckIter(iter, "box", "c");
|
||||
rocksdb_iter_seek(iter, "b", 1);
|
||||
CheckIter(iter, "bar", "b");
|
||||
rocksdb_iter_seek_for_prev(iter, "c", 1);
|
||||
CheckIter(iter, "box", "c");
|
||||
rocksdb_iter_seek_for_prev(iter, "box", 3);
|
||||
CheckIter(iter, "box", "c");
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_writebatch_wi_destroy(wbi);
|
||||
}
|
||||
|
||||
StartPhase("multiget");
|
||||
{
|
||||
const char* keys[3] = { "box", "foo", "notfound" };
|
||||
|
||||
+53
-46
@@ -20,6 +20,7 @@
|
||||
#include <limits>
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction_picker_universal.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/job_context.h"
|
||||
@@ -27,11 +28,11 @@
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/options_helper.h"
|
||||
#include "util/thread_status_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -205,8 +206,8 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
}
|
||||
|
||||
if (result.level0_file_num_compaction_trigger == 0) {
|
||||
Warn(db_options.info_log.get(),
|
||||
"level0_file_num_compaction_trigger cannot be 0");
|
||||
ROCKS_LOG_WARN(db_options.info_log.get(),
|
||||
"level0_file_num_compaction_trigger cannot be 0");
|
||||
result.level0_file_num_compaction_trigger = 1;
|
||||
}
|
||||
|
||||
@@ -214,14 +215,14 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
result.level0_slowdown_writes_trigger ||
|
||||
result.level0_slowdown_writes_trigger <
|
||||
result.level0_file_num_compaction_trigger) {
|
||||
Warn(db_options.info_log.get(),
|
||||
"This condition must be satisfied: "
|
||||
"level0_stop_writes_trigger(%d) >= "
|
||||
"level0_slowdown_writes_trigger(%d) >= "
|
||||
"level0_file_num_compaction_trigger(%d)",
|
||||
result.level0_stop_writes_trigger,
|
||||
result.level0_slowdown_writes_trigger,
|
||||
result.level0_file_num_compaction_trigger);
|
||||
ROCKS_LOG_WARN(db_options.info_log.get(),
|
||||
"This condition must be satisfied: "
|
||||
"level0_stop_writes_trigger(%d) >= "
|
||||
"level0_slowdown_writes_trigger(%d) >= "
|
||||
"level0_file_num_compaction_trigger(%d)",
|
||||
result.level0_stop_writes_trigger,
|
||||
result.level0_slowdown_writes_trigger,
|
||||
result.level0_file_num_compaction_trigger);
|
||||
if (result.level0_slowdown_writes_trigger <
|
||||
result.level0_file_num_compaction_trigger) {
|
||||
result.level0_slowdown_writes_trigger =
|
||||
@@ -231,14 +232,14 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
result.level0_slowdown_writes_trigger) {
|
||||
result.level0_stop_writes_trigger = result.level0_slowdown_writes_trigger;
|
||||
}
|
||||
Warn(db_options.info_log.get(),
|
||||
"Adjust the value to "
|
||||
"level0_stop_writes_trigger(%d)"
|
||||
"level0_slowdown_writes_trigger(%d)"
|
||||
"level0_file_num_compaction_trigger(%d)",
|
||||
result.level0_stop_writes_trigger,
|
||||
result.level0_slowdown_writes_trigger,
|
||||
result.level0_file_num_compaction_trigger);
|
||||
ROCKS_LOG_WARN(db_options.info_log.get(),
|
||||
"Adjust the value to "
|
||||
"level0_stop_writes_trigger(%d)"
|
||||
"level0_slowdown_writes_trigger(%d)"
|
||||
"level0_file_num_compaction_trigger(%d)",
|
||||
result.level0_stop_writes_trigger,
|
||||
result.level0_slowdown_writes_trigger,
|
||||
result.level0_file_num_compaction_trigger);
|
||||
}
|
||||
|
||||
if (result.soft_pending_compaction_bytes_limit == 0) {
|
||||
@@ -386,27 +387,27 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
} else if (ioptions_.compaction_style == kCompactionStyleNone) {
|
||||
compaction_picker_.reset(new NullCompactionPicker(
|
||||
ioptions_, &internal_comparator_));
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"Column family %s does not use any background compaction. "
|
||||
"Compactions can only be done via CompactFiles\n",
|
||||
GetName().c_str());
|
||||
ROCKS_LOG_WARN(ioptions_.info_log,
|
||||
"Column family %s does not use any background compaction. "
|
||||
"Compactions can only be done via CompactFiles\n",
|
||||
GetName().c_str());
|
||||
#endif // !ROCKSDB_LITE
|
||||
} else {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, ioptions_.info_log,
|
||||
"Unable to recognize the specified compaction style %d. "
|
||||
"Column family %s will use kCompactionStyleLevel.\n",
|
||||
ioptions_.compaction_style, GetName().c_str());
|
||||
ROCKS_LOG_ERROR(ioptions_.info_log,
|
||||
"Unable to recognize the specified compaction style %d. "
|
||||
"Column family %s will use kCompactionStyleLevel.\n",
|
||||
ioptions_.compaction_style, GetName().c_str());
|
||||
compaction_picker_.reset(
|
||||
new LevelCompactionPicker(ioptions_, &internal_comparator_));
|
||||
}
|
||||
|
||||
if (column_family_set_->NumberOfColumnFamilies() < 10) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"--------------- Options for column family [%s]:\n", name.c_str());
|
||||
ROCKS_LOG_INFO(ioptions_.info_log,
|
||||
"--------------- Options for column family [%s]:\n",
|
||||
name.c_str());
|
||||
initial_cf_options_.Dump(ioptions_.info_log);
|
||||
} else {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"\t(skipping printing options)\n");
|
||||
ROCKS_LOG_INFO(ioptions_.info_log, "\t(skipping printing options)\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,7 +621,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
if (imm()->NumNotFlushed() >= mutable_cf_options.max_write_buffer_number) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_COMPACTION, 1);
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d immutable memtables "
|
||||
"(waiting for flush), max_write_buffer_number is set to %d",
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
@@ -634,9 +636,9 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::LEVEL0_NUM_FILES_WITH_COMPACTION, 1);
|
||||
}
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
ROCKS_LOG_WARN(ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
compaction_needed_bytes >=
|
||||
@@ -644,7 +646,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::HARD_PENDING_COMPACTION_BYTES_LIMIT, 1);
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stopping writes because of estimated pending compaction "
|
||||
"bytes %" PRIu64,
|
||||
name_.c_str(), compaction_needed_bytes);
|
||||
@@ -656,7 +659,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
prev_compaction_needed_bytes_, was_stopped,
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_SLOWDOWN, 1);
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stalling writes because we have %d immutable memtables "
|
||||
"(waiting for flush), max_write_buffer_number is set to %d "
|
||||
"rate %" PRIu64,
|
||||
@@ -679,11 +683,11 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::LEVEL0_SLOWDOWN_WITH_COMPACTION, 1);
|
||||
}
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stalling writes because we have %d level-0 files "
|
||||
"rate %" PRIu64,
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count(),
|
||||
write_controller->delayed_write_rate());
|
||||
ROCKS_LOG_WARN(ioptions_.info_log,
|
||||
"[%s] Stalling writes because we have %d level-0 files "
|
||||
"rate %" PRIu64,
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count(),
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
vstorage->estimated_compaction_needed_bytes() >=
|
||||
@@ -705,7 +709,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::SOFT_PENDING_COMPACTION_BYTES_LIMIT, 1);
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stalling writes because of estimated pending compaction "
|
||||
"bytes %" PRIu64 " rate %" PRIu64,
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes(),
|
||||
@@ -717,7 +722,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
mutable_cf_options.level0_slowdown_writes_trigger)) {
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Increasing compaction threads because we have %d level-0 "
|
||||
"files ",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
@@ -730,7 +736,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Increasing compaction threads because of estimated pending "
|
||||
"compaction "
|
||||
"bytes %" PRIu64,
|
||||
|
||||
+1
-1
@@ -19,11 +19,11 @@
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/compaction_job_stats.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/cf_options.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/options_parser.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
@@ -675,6 +675,44 @@ TEST_F(ColumnFamilyTest, AddDrop) {
|
||||
std::vector<std::string>({"default", "four", "three"}));
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, BulkAddDrop) {
|
||||
constexpr int kNumCF = 1000;
|
||||
ColumnFamilyOptions cf_options;
|
||||
WriteOptions write_options;
|
||||
Open();
|
||||
std::vector<std::string> cf_names;
|
||||
std::vector<ColumnFamilyHandle*> cf_handles;
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
cf_names.push_back("cf1-" + ToString(i));
|
||||
}
|
||||
ASSERT_OK(db_->CreateColumnFamilies(cf_options, cf_names, &cf_handles));
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
ASSERT_OK(db_->Put(write_options, cf_handles[i - 1], "foo", "bar"));
|
||||
}
|
||||
ASSERT_OK(db_->DropColumnFamilies(cf_handles));
|
||||
std::vector<ColumnFamilyDescriptor> cf_descriptors;
|
||||
for (auto* handle : cf_handles) {
|
||||
delete handle;
|
||||
}
|
||||
cf_handles.clear();
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
cf_descriptors.emplace_back("cf2-" + ToString(i), ColumnFamilyOptions());
|
||||
}
|
||||
ASSERT_OK(db_->CreateColumnFamilies(cf_descriptors, &cf_handles));
|
||||
for (int i = 1; i <= kNumCF; i++) {
|
||||
ASSERT_OK(db_->Put(write_options, cf_handles[i - 1], "foo", "bar"));
|
||||
}
|
||||
ASSERT_OK(db_->DropColumnFamilies(cf_handles));
|
||||
for (auto* handle : cf_handles) {
|
||||
delete handle;
|
||||
}
|
||||
Close();
|
||||
std::vector<std::string> families;
|
||||
ASSERT_OK(DB::ListColumnFamilies(db_options_, dbname_, &families));
|
||||
std::sort(families.begin(), families.end());
|
||||
ASSERT_TRUE(families == std::vector<std::string>({"default"}));
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, DropTest) {
|
||||
// first iteration - dont reopen DB before dropping
|
||||
// second iteration - reopen DB before dropping
|
||||
@@ -1730,7 +1768,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
|
||||
one.num_levels = 1;
|
||||
// trigger compaction if there are >= 4 files
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.level0_file_num_compaction_trigger = 3;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
Reopen({default_cf, one});
|
||||
|
||||
@@ -42,8 +42,8 @@ size_t CompactedDBImpl::FindFile(const Slice& key) {
|
||||
return right;
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle*, const Slice& key, std::string* value) {
|
||||
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);
|
||||
@@ -75,11 +75,14 @@ std::vector<Status> CompactedDBImpl::MultiGet(const ReadOptions& options,
|
||||
int idx = 0;
|
||||
for (auto* r : reader_list) {
|
||||
if (r != nullptr) {
|
||||
PinnableSlice pinnable_val;
|
||||
std::string& value = (*values)[idx];
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, keys[idx], &(*values)[idx],
|
||||
GetContext::kNotFound, keys[idx], &pinnable_val,
|
||||
nullptr, nullptr, nullptr, nullptr);
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber);
|
||||
r->Get(options, lkey.internal_key(), &get_context);
|
||||
value.assign(pinnable_val.data(), pinnable_val.size());
|
||||
if (get_context.State() == GetContext::kFound) {
|
||||
statuses[idx] = Status::OK();
|
||||
}
|
||||
@@ -151,8 +154,8 @@ Status CompactedDBImpl::Open(const Options& options,
|
||||
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
||||
Status s = db->Init(options);
|
||||
if (s.ok()) {
|
||||
Log(INFO_LEVEL, db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
*dbptr = db.release();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class CompactedDBImpl : public DBImpl {
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value) override;
|
||||
PinnableSlice* value) override;
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options,
|
||||
|
||||
+13
-8
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -287,6 +287,10 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleUniversal) {
|
||||
return bottommost_level_;
|
||||
}
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel &&
|
||||
output_level_ == 0) {
|
||||
return false;
|
||||
}
|
||||
// Maybe use binary search to find right entry instead of linear search?
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
@@ -413,14 +417,15 @@ void Compaction::Summary(char* output, int len) {
|
||||
uint64_t Compaction::OutputFilePreallocationSize() const {
|
||||
uint64_t preallocation_size = 0;
|
||||
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0) {
|
||||
if (max_output_file_size_ != port::kMaxUint64 &&
|
||||
(cfd_->ioptions()->compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0)) {
|
||||
preallocation_size = max_output_file_size_;
|
||||
} else {
|
||||
// output_level() == 0
|
||||
assert(num_input_levels() > 0);
|
||||
for (const auto& f : inputs_[0].files) {
|
||||
preallocation_size += f->fd.GetFileSize();
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
preallocation_size += file->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Over-estimate slightly so we don't end up just barely crossing
|
||||
@@ -450,7 +455,7 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
return false;
|
||||
}
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
return start_level_ == 0 && !IsOutputLevelEmpty();
|
||||
return start_level_ == 0 && output_level_ > 0 && !IsOutputLevelEmpty();
|
||||
} else if (cfd_->ioptions()->compaction_style == kCompactionStyleUniversal) {
|
||||
return number_levels_ > 1 && output_level_ > 0;
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -9,9 +9,9 @@
|
||||
|
||||
#pragma once
|
||||
#include "db/version_set.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
@@ -8,12 +8,36 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
CompactionEventListener::CompactionListenerValueType fromInternalValueType(
|
||||
ValueType vt) {
|
||||
switch (vt) {
|
||||
case kTypeDeletion:
|
||||
return CompactionEventListener::CompactionListenerValueType::kDelete;
|
||||
case kTypeValue:
|
||||
return CompactionEventListener::CompactionListenerValueType::kValue;
|
||||
case kTypeMerge:
|
||||
return CompactionEventListener::CompactionListenerValueType::
|
||||
kMergeOperand;
|
||||
case kTypeSingleDeletion:
|
||||
return CompactionEventListener::CompactionListenerValueType::
|
||||
kSingleDelete;
|
||||
case kTypeRangeDeletion:
|
||||
return CompactionEventListener::CompactionListenerValueType::kRangeDelete;
|
||||
default:
|
||||
assert(false);
|
||||
return CompactionEventListener::CompactionListenerValueType::kInvalid;
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
||||
CompactionEventListener* compaction_listener,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
@@ -21,7 +45,7 @@ CompactionIterator::CompactionIterator(
|
||||
range_del_agg,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new CompactionProxy(compaction) : nullptr),
|
||||
compaction_filter, shutting_down) {}
|
||||
compaction_filter, compaction_listener, shutting_down) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
@@ -30,6 +54,7 @@ CompactionIterator::CompactionIterator(
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
CompactionEventListener* compaction_listener,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
@@ -41,7 +66,9 @@ CompactionIterator::CompactionIterator(
|
||||
range_del_agg_(range_del_agg),
|
||||
compaction_(std::move(compaction)),
|
||||
compaction_filter_(compaction_filter),
|
||||
compaction_listener_(compaction_listener),
|
||||
shutting_down_(shutting_down),
|
||||
ignore_snapshots_(false),
|
||||
merge_out_iter_(merge_helper_) {
|
||||
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
||||
bottommost_level_ =
|
||||
@@ -60,8 +87,10 @@ CompactionIterator::CompactionIterator(
|
||||
earliest_snapshot_ = snapshots_->at(0);
|
||||
latest_snapshot_ = snapshots_->back();
|
||||
}
|
||||
if (compaction_filter_ != nullptr && compaction_filter_->IgnoreSnapshots()) {
|
||||
ignore_snapshots_ = true;
|
||||
if (compaction_filter_ != nullptr) {
|
||||
if (compaction_filter_->IgnoreSnapshots()) {
|
||||
ignore_snapshots_ = true;
|
||||
}
|
||||
} else {
|
||||
ignore_snapshots_ = false;
|
||||
}
|
||||
@@ -103,7 +132,7 @@ void CompactionIterator::Next() {
|
||||
assert(valid_key);
|
||||
// Keep current_key_ in sync.
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
key_ = current_key_.GetKey();
|
||||
key_ = current_key_.GetInternalKey();
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
valid_ = true;
|
||||
} else {
|
||||
@@ -149,7 +178,7 @@ void CompactionIterator::NextFromInput() {
|
||||
status_ = Status::Corruption("Corrupted internal key not expected.");
|
||||
break;
|
||||
}
|
||||
key_ = current_key_.SetKey(key_);
|
||||
key_ = current_key_.SetInternalKey(key_);
|
||||
has_current_user_key_ = false;
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
@@ -179,13 +208,21 @@ void CompactionIterator::NextFromInput() {
|
||||
!cmp_->Equal(ikey_.user_key, current_user_key_)) {
|
||||
// First occurrence of this user key
|
||||
// Copy key for output
|
||||
key_ = current_key_.SetKey(key_, &ikey_);
|
||||
key_ = current_key_.SetInternalKey(key_, &ikey_);
|
||||
current_user_key_ = ikey_.user_key;
|
||||
has_current_user_key_ = true;
|
||||
has_outputted_key_ = false;
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (compaction_listener_) {
|
||||
compaction_listener_->OnCompaction(compaction_->level(), ikey_.user_key,
|
||||
fromInternalValueType(ikey_.type),
|
||||
value_, ikey_.sequence, true);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// apply the compaction filter to the first occurrence of the user key
|
||||
if (compaction_filter_ != nullptr && ikey_.type == kTypeValue &&
|
||||
(visible_at_tip_ || ikey_.sequence > latest_snapshot_ ||
|
||||
@@ -233,13 +270,21 @@ void CompactionIterator::NextFromInput() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (compaction_listener_) {
|
||||
compaction_listener_->OnCompaction(compaction_->level(), ikey_.user_key,
|
||||
fromInternalValueType(ikey_.type),
|
||||
value_, ikey_.sequence, false);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Update the current key to reflect the new sequence number/type without
|
||||
// copying the user key.
|
||||
// TODO(rven): Compaction filter does not process keys in this path
|
||||
// Need to have the compaction filter process multiple versions
|
||||
// if we have versions on both sides of a snapshot
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
key_ = current_key_.GetKey();
|
||||
key_ = current_key_.GetInternalKey();
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
}
|
||||
|
||||
@@ -392,7 +437,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// is the same as the visibility of a previous instance of the
|
||||
// same key, then this kv is not visible in any snapshot.
|
||||
// Hidden by an newer entry for same user key
|
||||
// TODO: why not > ?
|
||||
// TODO(noetzli): why not > ?
|
||||
//
|
||||
// Note: Dropping this key will not affect TransactionDB write-conflict
|
||||
// checking since there has already been a record returned for this key
|
||||
@@ -451,7 +496,7 @@ void CompactionIterator::NextFromInput() {
|
||||
assert(valid_key);
|
||||
// Keep current_key_ in sync.
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
key_ = current_key_.GetKey();
|
||||
key_ = current_key_.GetInternalKey();
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
valid_ = true;
|
||||
} else {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class CompactionEventListener;
|
||||
|
||||
class CompactionIterator {
|
||||
public:
|
||||
// A wrapper around Compaction. Has a much smaller interface, only what
|
||||
@@ -58,6 +60,7 @@ class CompactionIterator {
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
CompactionEventListener* compaction_listener = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
@@ -69,6 +72,7 @@ class CompactionIterator {
|
||||
RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
CompactionEventListener* compaction_listener = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
@@ -122,6 +126,7 @@ class CompactionIterator {
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
CompactionEventListener* compaction_listener_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
|
||||
@@ -189,7 +189,7 @@ class CompactionIteratorTest : public testing::Test {
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
kMaxSequenceNumber, Env::Default(), false, range_del_agg_.get(),
|
||||
std::move(compaction), filter, &shutting_down_));
|
||||
std::move(compaction), filter, nullptr, &shutting_down_));
|
||||
}
|
||||
|
||||
void AddSnapshot(SequenceNumber snapshot) { snapshots_.push_back(snapshot); }
|
||||
|
||||
+49
-28
@@ -28,7 +28,6 @@
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable.h"
|
||||
@@ -36,6 +35,9 @@
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/version_set.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/likely.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -49,16 +51,15 @@
|
||||
#include "table/table_builder.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/iostats_context_imp.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/thread_status_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -600,7 +601,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
write_amp = stats.bytes_written /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels);
|
||||
}
|
||||
LogToBuffer(
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
"files in(%d, %d) out(%d) "
|
||||
@@ -733,17 +734,28 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
if (start != nullptr) {
|
||||
IterKey start_iter;
|
||||
start_iter.SetInternalKey(*start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
input->Seek(start_iter.GetKey());
|
||||
input->Seek(start_iter.GetInternalKey());
|
||||
} else {
|
||||
input->SeekToFirst();
|
||||
}
|
||||
|
||||
// we allow only 1 compaction event listener. Used by blob storage
|
||||
CompactionEventListener* comp_event_listener = nullptr;
|
||||
#ifndef ROCKSDB_LITE
|
||||
for (auto& celitr : cfd->ioptions()->listeners) {
|
||||
comp_event_listener = celitr->GetCompactionEventListener();
|
||||
if (comp_event_listener != nullptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status status;
|
||||
sub_compact->c_iter.reset(new CompactionIterator(
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_, env_, false,
|
||||
range_del_agg.get(), sub_compact->compaction, compaction_filter,
|
||||
shutting_down_));
|
||||
comp_event_listener, shutting_down_));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() &&
|
||||
@@ -1071,6 +1083,11 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
TableProperties tp;
|
||||
if (s.ok() && current_entries > 0) {
|
||||
// Verify that the table is usable
|
||||
// We set for_compaction to false and don't OptimizeForCompactionTableRead
|
||||
// here because this is a special case after we finish the table building
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// we will regrad this verification as user reads since the goal is
|
||||
// to cache it here for further user reads
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
ReadOptions(), env_options_, cfd->internal_comparator(), meta->fd,
|
||||
nullptr /* range_del_agg */, nullptr,
|
||||
@@ -1091,12 +1108,12 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
tp = sub_compact->builder->GetTableProperties();
|
||||
sub_compact->current_output()->table_properties =
|
||||
std::make_shared<TableProperties>(tp);
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Generated table #%" PRIu64 ": %" PRIu64
|
||||
" keys, %" PRIu64 " bytes%s",
|
||||
cfd->GetName().c_str(), job_id_, output_number, current_entries,
|
||||
current_bytes,
|
||||
meta->marked_for_compaction ? " (need compaction)" : "");
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Generated table #%" PRIu64 ": %" PRIu64
|
||||
" keys, %" PRIu64 " bytes%s",
|
||||
cfd->GetName().c_str(), job_id_, output_number,
|
||||
current_entries, current_bytes,
|
||||
meta->marked_for_compaction ? " (need compaction)" : "");
|
||||
}
|
||||
}
|
||||
std::string fname = TableFileName(db_options_.db_paths, meta->fd.GetNumber(),
|
||||
@@ -1142,17 +1159,16 @@ Status CompactionJob::InstallCompactionResults(
|
||||
if (!versions_->VerifyCompactionFileConsistency(compaction)) {
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Compaction %s aborted",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary));
|
||||
ROCKS_LOG_ERROR(db_options_.info_log, "[%s] [JOB %d] Compaction %s aborted",
|
||||
compaction->column_family_data()->GetName().c_str(),
|
||||
job_id_, compaction->InputLevelSummary(&inputs_summary));
|
||||
return Status::Corruption("Compaction input files inconsistent");
|
||||
}
|
||||
|
||||
{
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Compacted %s => %" PRIu64 " bytes",
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log, "[%s] [JOB %d] Compacted %s => %" PRIu64 " bytes",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary), compact_->total_bytes);
|
||||
}
|
||||
@@ -1198,9 +1214,14 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
#endif // !ROCKSDB_LITE
|
||||
// Make the output file
|
||||
unique_ptr<WritableFile> writable_file;
|
||||
Status s = NewWritableFile(env_, fname, &writable_file, env_options_);
|
||||
EnvOptions opt_env_opts =
|
||||
env_->OptimizeForCompactionTableWrite(env_options_, db_options_);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::OpenCompactionOutputFile",
|
||||
&opt_env_opts.use_direct_writes);
|
||||
Status s = NewWritableFile(env_, fname, &writable_file, opt_env_opts);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
ROCKS_LOG_ERROR(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] OpenCompactionOutputFiles for table #%" PRIu64
|
||||
" fails at NewWritableFile with status %s",
|
||||
sub_compact->compaction->column_family_data()->GetName().c_str(),
|
||||
@@ -1376,14 +1397,14 @@ void CompactionJob::LogCompaction() {
|
||||
// we're not logging
|
||||
if (db_options_.info_log_level <= InfoLogLevel::INFO_LEVEL) {
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Compacting %s, score %.2f", cfd->GetName().c_str(),
|
||||
job_id_, compaction->InputLevelSummary(&inputs_summary),
|
||||
compaction->score());
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log, "[%s] [JOB %d] Compacting %s, score %.2f",
|
||||
cfd->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary), compaction->score());
|
||||
char scratch[2345];
|
||||
compaction->Summary(scratch, sizeof(scratch));
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Compaction start summary: %s\n", cfd->GetName().c_str(), scratch);
|
||||
ROCKS_LOG_INFO(db_options_.info_log, "[%s] Compaction start summary: %s\n",
|
||||
cfd->GetName().c_str(), scratch);
|
||||
// build event logger report
|
||||
auto stream = event_logger_->Log();
|
||||
stream << "job" << job_id_ << "event"
|
||||
|
||||
+1
-1
@@ -29,6 +29,7 @@
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "options/db_options.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/compaction_job_stats.h"
|
||||
@@ -38,7 +39,6 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -23,11 +23,13 @@
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -50,17 +52,15 @@
|
||||
#include "table/plain_table_factory.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mock_env.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/statistics.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "util/thread_status_util.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
#if !defined(IOS_CROSS_COMPILE)
|
||||
|
||||
+339
-869
File diff suppressed because it is too large.
Load diff
+25
-116
@@ -17,10 +17,10 @@
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/version_set.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -84,15 +84,15 @@ class CompactionPicker {
|
||||
void ReleaseCompactionFiles(Compaction* c, Status status);
|
||||
|
||||
// Returns true if any one of the specified files are being compacted
|
||||
bool FilesInCompaction(const std::vector<FileMetaData*>& files);
|
||||
bool AreFilesInCompaction(const std::vector<FileMetaData*>& files);
|
||||
|
||||
// Takes a list of CompactionInputFiles and returns a (manual) Compaction
|
||||
// object.
|
||||
Compaction* FormCompaction(
|
||||
const CompactionOptions& compact_options,
|
||||
const std::vector<CompactionInputFiles>& input_files, int output_level,
|
||||
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
|
||||
uint32_t output_path_id);
|
||||
Compaction* CompactFiles(const CompactionOptions& compact_options,
|
||||
const std::vector<CompactionInputFiles>& input_files,
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
uint32_t output_path_id);
|
||||
|
||||
// Converts a set of compaction input file numbers into
|
||||
// a list of CompactionInputFiles.
|
||||
@@ -102,12 +102,6 @@ class CompactionPicker {
|
||||
const VersionStorageInfo* vstorage,
|
||||
const CompactionOptions& compact_options) const;
|
||||
|
||||
// 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 IsInputNonOverlapping(Compaction* c);
|
||||
|
||||
// Is there currently a compaction involving level 0 taking place
|
||||
bool IsLevel0CompactionInProgress() const {
|
||||
return !level0_compactions_in_progress_.empty();
|
||||
@@ -138,7 +132,6 @@ class CompactionPicker {
|
||||
void GetRange(const std::vector<CompactionInputFiles>& inputs,
|
||||
InternalKey* smallest, InternalKey* largest) const;
|
||||
|
||||
protected:
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
|
||||
// Add more files to the inputs on "level" to make sure that
|
||||
@@ -151,14 +144,14 @@ class CompactionPicker {
|
||||
// populated.
|
||||
//
|
||||
// Will return false if it is impossible to apply this compaction.
|
||||
bool ExpandWhileOverlapping(const std::string& cf_name,
|
||||
bool ExpandInputsToCleanCut(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs);
|
||||
|
||||
// Returns true if any one of the parent files are being compacted
|
||||
bool RangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level, int* index);
|
||||
bool IsRangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level, int* index);
|
||||
|
||||
// Returns true if the key range that `inputs` files cover overlap with the
|
||||
// key range of a currently running compaction.
|
||||
@@ -177,6 +170,20 @@ class CompactionPicker {
|
||||
const CompactionInputFiles& output_level_inputs,
|
||||
std::vector<FileMetaData*>* grandparents);
|
||||
|
||||
// Register this compaction in the set of running compactions
|
||||
void RegisterCompaction(Compaction* c);
|
||||
|
||||
// Remove this compaction from the set of running compactions
|
||||
void UnregisterCompaction(Compaction* c);
|
||||
|
||||
std::set<Compaction*>* level0_compactions_in_progress() {
|
||||
return &level0_compactions_in_progress_;
|
||||
}
|
||||
std::unordered_set<Compaction*>* compactions_in_progress() {
|
||||
return &compactions_in_progress_;
|
||||
}
|
||||
|
||||
protected:
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
|
||||
// A helper function to SanitizeCompactionInputFiles() that
|
||||
@@ -187,12 +194,6 @@ class CompactionPicker {
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level) const;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Register this compaction in the set of running compactions
|
||||
void RegisterCompaction(Compaction* c);
|
||||
|
||||
// Remove this compaction from the set of running compactions
|
||||
void UnregisterCompaction(Compaction* c);
|
||||
|
||||
// Keeps track of all compactions that are running on Level0.
|
||||
// Protected by DB mutex
|
||||
std::set<Compaction*> level0_compactions_in_progress_;
|
||||
@@ -216,101 +217,9 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its level
|
||||
static uint32_t GetPathId(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int level);
|
||||
|
||||
private:
|
||||
// For the specfied level, pick a file that we want to compact.
|
||||
// Returns false if there is no file to compact.
|
||||
// If it returns true, inputs->files.size() will be exactly one.
|
||||
// If level is 0 and there is already a compaction on that level, this
|
||||
// function will return false.
|
||||
bool PickCompactionBySize(VersionStorageInfo* vstorage, int level,
|
||||
int output_level, CompactionInputFiles* inputs,
|
||||
int* parent_index, int* base_index);
|
||||
|
||||
// If there is any file marked for compaction, put put it into inputs.
|
||||
// This is still experimental. It will return meaningful results only if
|
||||
// clients call experimental feature SuggestCompactRange()
|
||||
void PickFilesMarkedForCompactionExperimental(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs,
|
||||
int* level, int* output_level);
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class UniversalCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
UniversalCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
|
||||
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* PickCompactionUniversalReadAmp(
|
||||
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* PickCompactionUniversalSizeAmp(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, double score,
|
||||
const std::vector<SortedRun>& sorted_runs, LogBuffer* log_buffer);
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
static uint32_t GetPathId(const ImmutableCFOptions& ioptions,
|
||||
uint64_t file_size);
|
||||
};
|
||||
|
||||
class FIFOCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
FIFOCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction_picker.h"
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction_picker_universal.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -197,7 +198,7 @@ TEST_F(CompactionPickerTest, LevelMaxScore) {
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
Add(0, 1U, "150", "200", 1000000000U);
|
||||
Add(0, 1U, "150", "200", 1000000U);
|
||||
// Level 1 score 1.2
|
||||
Add(1, 66U, "150", "200", 6000000U);
|
||||
Add(1, 88U, "201", "300", 6000000U);
|
||||
|
||||
@@ -0,0 +1,735 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction_picker_universal.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "db/column_family.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace {
|
||||
// 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
|
||||
// and the index of the file in that level
|
||||
|
||||
struct InputFileInfo {
|
||||
InputFileInfo() : f(nullptr) {}
|
||||
|
||||
FileMetaData* f;
|
||||
size_t level;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
// Used in universal compaction when trivial move is enabled.
|
||||
// This comparator is used for the construction of min heap
|
||||
// based on the smallest key of the file.
|
||||
struct SmallestKeyHeapComparator {
|
||||
explicit SmallestKeyHeapComparator(const Comparator* ucmp) { ucmp_ = ucmp; }
|
||||
|
||||
bool operator()(InputFileInfo i1, InputFileInfo i2) const {
|
||||
return (ucmp_->Compare(i1.f->smallest.user_key(),
|
||||
i2.f->smallest.user_key()) > 0);
|
||||
}
|
||||
|
||||
private:
|
||||
const Comparator* ucmp_;
|
||||
};
|
||||
|
||||
typedef std::priority_queue<InputFileInfo, std::vector<InputFileInfo>,
|
||||
SmallestKeyHeapComparator>
|
||||
SmallestKeyHeap;
|
||||
|
||||
// This function creates the heap that is used to find if the files are
|
||||
// overlapping during universal compaction when the allow_trivial_move
|
||||
// is set.
|
||||
SmallestKeyHeap create_level_heap(Compaction* c, const Comparator* ucmp) {
|
||||
SmallestKeyHeap smallest_key_priority_q =
|
||||
SmallestKeyHeap(SmallestKeyHeapComparator(ucmp));
|
||||
|
||||
InputFileInfo input_file;
|
||||
|
||||
for (size_t l = 0; l < c->num_input_levels(); l++) {
|
||||
if (c->num_input_files(l) != 0) {
|
||||
if (l == 0 && c->start_level() == 0) {
|
||||
for (size_t i = 0; i < c->num_input_files(0); i++) {
|
||||
input_file.f = c->input(0, i);
|
||||
input_file.level = 0;
|
||||
input_file.index = i;
|
||||
smallest_key_priority_q.push(std::move(input_file));
|
||||
}
|
||||
} else {
|
||||
input_file.f = c->input(l, 0);
|
||||
input_file.level = l;
|
||||
input_file.index = 0;
|
||||
smallest_key_priority_q.push(std::move(input_file));
|
||||
}
|
||||
}
|
||||
}
|
||||
return smallest_key_priority_q;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// smallest_seqno and largest_seqno are set iff. `files` is not empty.
|
||||
void GetSmallestLargestSeqno(const std::vector<FileMetaData*>& files,
|
||||
SequenceNumber* smallest_seqno,
|
||||
SequenceNumber* largest_seqno) {
|
||||
bool is_first = true;
|
||||
for (FileMetaData* f : files) {
|
||||
assert(f->smallest_seqno <= f->largest_seqno);
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
*smallest_seqno = f->smallest_seqno;
|
||||
*largest_seqno = f->largest_seqno;
|
||||
} else {
|
||||
if (f->smallest_seqno < *smallest_seqno) {
|
||||
*smallest_seqno = f->smallest_seqno;
|
||||
}
|
||||
if (f->largest_seqno > *largest_seqno) {
|
||||
*largest_seqno = f->largest_seqno;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
// Algorithm that checks to see if there are any overlapping
|
||||
// files in the input
|
||||
bool UniversalCompactionPicker::IsInputFilesNonOverlapping(Compaction* c) {
|
||||
auto comparator = icmp_->user_comparator();
|
||||
int first_iter = 1;
|
||||
|
||||
InputFileInfo prev, curr, next;
|
||||
|
||||
SmallestKeyHeap smallest_key_priority_q =
|
||||
create_level_heap(c, icmp_->user_comparator());
|
||||
|
||||
while (!smallest_key_priority_q.empty()) {
|
||||
curr = smallest_key_priority_q.top();
|
||||
smallest_key_priority_q.pop();
|
||||
|
||||
if (first_iter) {
|
||||
prev = curr;
|
||||
first_iter = 0;
|
||||
} else {
|
||||
if (comparator->Compare(prev.f->largest.user_key(),
|
||||
curr.f->smallest.user_key()) >= 0) {
|
||||
// found overlapping files, return false
|
||||
return false;
|
||||
}
|
||||
assert(comparator->Compare(curr.f->largest.user_key(),
|
||||
prev.f->largest.user_key()) > 0);
|
||||
prev = curr;
|
||||
}
|
||||
|
||||
next.f = nullptr;
|
||||
|
||||
if (curr.level != 0 && curr.index < c->num_input_files(curr.level) - 1) {
|
||||
next.f = c->input(curr.level, curr.index + 1);
|
||||
next.level = curr.level;
|
||||
next.index = curr.index + 1;
|
||||
}
|
||||
|
||||
if (next.f) {
|
||||
smallest_key_priority_q.push(std::move(next));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniversalCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
const int kLevel0 = 0;
|
||||
return vstorage->CompactionScore(kLevel0) >= 1;
|
||||
}
|
||||
|
||||
void UniversalCompactionPicker::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) {
|
||||
snprintf(out_buf, out_buf_size, "file %" PRIu64, file->fd.GetNumber());
|
||||
} else {
|
||||
snprintf(out_buf, out_buf_size, "file %" PRIu64
|
||||
"(path "
|
||||
"%" PRIu32 ")",
|
||||
file->fd.GetNumber(), file->fd.GetPathId());
|
||||
}
|
||||
} else {
|
||||
snprintf(out_buf, out_buf_size, "level %d", level);
|
||||
}
|
||||
}
|
||||
|
||||
void UniversalCompactionPicker::SortedRun::DumpSizeInfo(
|
||||
char* out_buf, size_t out_buf_size, size_t sorted_run_count) const {
|
||||
if (level == 0) {
|
||||
assert(file != nullptr);
|
||||
snprintf(out_buf, out_buf_size,
|
||||
"file %" PRIu64 "[%" ROCKSDB_PRIszt
|
||||
"] "
|
||||
"with size %" PRIu64 " (compensated size %" PRIu64 ")",
|
||||
file->fd.GetNumber(), sorted_run_count, file->fd.GetFileSize(),
|
||||
file->compensated_file_size);
|
||||
} else {
|
||||
snprintf(out_buf, out_buf_size,
|
||||
"level %d[%" ROCKSDB_PRIszt
|
||||
"] "
|
||||
"with size %" PRIu64 " (compensated size %" PRIu64 ")",
|
||||
level, sorted_run_count, size, compensated_file_size);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<UniversalCompactionPicker::SortedRun>
|
||||
UniversalCompactionPicker::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions) {
|
||||
std::vector<UniversalCompactionPicker::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
}
|
||||
for (int level = 1; level < vstorage.num_levels(); level++) {
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
bool is_first = true;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(level)) {
|
||||
total_compensated_size += f->compensated_file_size;
|
||||
total_size += f->fd.GetFileSize();
|
||||
if (ioptions.compaction_options_universal.allow_trivial_move == true) {
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
}
|
||||
} else {
|
||||
// Compaction always includes all files for a non-zero level, so for a
|
||||
// non-zero level, all the files should share the same being_compacted
|
||||
// value.
|
||||
// This assumption is only valid when
|
||||
// ioptions.compaction_options_universal.allow_trivial_move is false
|
||||
assert(is_first || f->being_compacted == being_compacted);
|
||||
}
|
||||
if (is_first) {
|
||||
being_compacted = f->being_compacted;
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
if (total_compensated_size > 0) {
|
||||
ret.emplace_back(level, nullptr, total_size, total_compensated_size,
|
||||
being_compacted);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const int kLevel0 = 0;
|
||||
double score = vstorage->CompactionScore(kLevel0);
|
||||
std::vector<SortedRun> sorted_runs =
|
||||
CalculateSortedRuns(*vstorage, ioptions_);
|
||||
|
||||
if (sorted_runs.size() == 0 ||
|
||||
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);
|
||||
return nullptr;
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_BUFFER_MAX_SZ(
|
||||
log_buffer, 3072,
|
||||
"[%s] Universal: sorted runs files(%" ROCKSDB_PRIszt "): %s\n",
|
||||
cf_name.c_str(), sorted_runs.size(), vstorage->LevelSummary(&tmp));
|
||||
|
||||
// Check for size amplification first.
|
||||
Compaction* c;
|
||||
if ((c = PickCompactionToReduceSizeAmp(cf_name, mutable_cf_options, vstorage,
|
||||
score, sorted_runs, log_buffer)) !=
|
||||
nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Universal: compacting for size amp\n",
|
||||
cf_name.c_str());
|
||||
} else {
|
||||
// Size amplification is within limits. Try reducing read
|
||||
// amplification while maintaining file size ratios.
|
||||
unsigned int ratio = ioptions_.compaction_options_universal.size_ratio;
|
||||
|
||||
if ((c = PickCompactionToReduceSortedRuns(
|
||||
cf_name, mutable_cf_options, vstorage, score, ratio, UINT_MAX,
|
||||
sorted_runs, log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Universal: compacting for size ratio\n",
|
||||
cf_name.c_str());
|
||||
} else {
|
||||
// Size amplification and file size ratios are within configured limits.
|
||||
// If max read amplification is exceeding configured limits, then force
|
||||
// compaction without looking at filesize ratios and try to reduce
|
||||
// the number of files to fewer than level0_file_num_compaction_trigger.
|
||||
// This is guaranteed by NeedsCompaction()
|
||||
assert(sorted_runs.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger));
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs.size(); i++) {
|
||||
if (sorted_runs[i].being_compacted == false) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater than
|
||||
// the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted >
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
unsigned int num_files =
|
||||
num_sr_not_compacted -
|
||||
mutable_cf_options.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionToReduceSortedRuns(
|
||||
cf_name, mutable_cf_options, vstorage, score, UINT_MAX,
|
||||
num_files, sorted_runs, log_buffer)) != nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c == nullptr) {
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ioptions_.compaction_options_universal.allow_trivial_move == true) {
|
||||
c->set_is_trivial_move(IsInputFilesNonOverlapping(c));
|
||||
}
|
||||
|
||||
// validate that all the chosen files of L0 are non overlapping in time
|
||||
#ifndef NDEBUG
|
||||
SequenceNumber prev_smallest_seqno = 0U;
|
||||
bool is_first = true;
|
||||
|
||||
size_t level_index = 0U;
|
||||
if (c->start_level() == 0) {
|
||||
for (auto f : *c->inputs(0)) {
|
||||
assert(f->smallest_seqno <= f->largest_seqno);
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
} else {
|
||||
assert(prev_smallest_seqno > f->largest_seqno);
|
||||
}
|
||||
prev_smallest_seqno = f->smallest_seqno;
|
||||
}
|
||||
level_index = 1U;
|
||||
}
|
||||
for (; level_index < c->num_input_levels(); level_index++) {
|
||||
if (c->num_input_files(level_index) != 0) {
|
||||
SequenceNumber smallest_seqno = 0U;
|
||||
SequenceNumber largest_seqno = 0U;
|
||||
GetSmallestLargestSeqno(*(c->inputs(level_index)), &smallest_seqno,
|
||||
&largest_seqno);
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
} else if (prev_smallest_seqno > 0) {
|
||||
// A level is considered as the bottommost level if there are
|
||||
// no files in higher levels or if files in higher levels do
|
||||
// not overlap with the files being compacted. Sequence numbers
|
||||
// of files in bottommost level can be set to 0 to help
|
||||
// compression. As a result, the following assert may not hold
|
||||
// if the prev_smallest_seqno is 0.
|
||||
assert(prev_smallest_seqno > largest_seqno);
|
||||
}
|
||||
prev_smallest_seqno = smallest_seqno;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// update statistics
|
||||
MeasureTime(ioptions_.statistics, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
c->inputs(0)->size());
|
||||
|
||||
RegisterCompaction(c);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
c);
|
||||
return c;
|
||||
}
|
||||
|
||||
uint32_t UniversalCompactionPicker::GetPathId(
|
||||
const ImmutableCFOptions& ioptions, uint64_t file_size) {
|
||||
// Two conditions need to be satisfied:
|
||||
// (1) the target path needs to be able to hold the file's size
|
||||
// (2) Total size left in this and previous paths need to be not
|
||||
// smaller than expected future file size before this new file is
|
||||
// compacted, which is estimated based on size_ratio.
|
||||
// For example, if now we are compacting files of size (1, 1, 2, 4, 8),
|
||||
// we will make sure the target file, probably with size of 16, will be
|
||||
// placed in a path so that eventually when new files are generated and
|
||||
// compacted to (1, 1, 2, 4, 8, 16), all those files can be stored in or
|
||||
// before the path we chose.
|
||||
//
|
||||
// TODO(sdong): now the case of multiple column families is not
|
||||
// considered in this algorithm. So the target size can be violated in
|
||||
// that case. We need to improve it.
|
||||
uint64_t accumulated_size = 0;
|
||||
uint64_t future_size =
|
||||
file_size * (100 - ioptions.compaction_options_universal.size_ratio) /
|
||||
100;
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.db_paths.empty());
|
||||
for (; p < ioptions.db_paths.size() - 1; p++) {
|
||||
uint64_t target_size = ioptions.db_paths[p].target_size;
|
||||
if (target_size > file_size &&
|
||||
accumulated_size + (target_size - file_size) > future_size) {
|
||||
return p;
|
||||
}
|
||||
accumulated_size += target_size;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
//
|
||||
// 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) {
|
||||
unsigned int min_merge_width =
|
||||
ioptions_.compaction_options_universal.min_merge_width;
|
||||
unsigned int max_merge_width =
|
||||
ioptions_.compaction_options_universal.max_merge_width;
|
||||
|
||||
const SortedRun* sr = nullptr;
|
||||
bool done = false;
|
||||
size_t start_index = 0;
|
||||
unsigned int candidate_count = 0;
|
||||
|
||||
unsigned int max_files_to_compact =
|
||||
std::min(max_merge_width, max_number_of_files_to_compact);
|
||||
min_merge_width = std::max(min_merge_width, 2U);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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++) {
|
||||
candidate_count = 0;
|
||||
|
||||
// Skip files that are already being compacted
|
||||
for (sr = nullptr; loop < sorted_runs.size(); loop++) {
|
||||
sr = &sorted_runs[loop];
|
||||
|
||||
if (!sr->being_compacted) {
|
||||
candidate_count = 1;
|
||||
break;
|
||||
}
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf));
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Universal: %s"
|
||||
"[%d] being compacted, skipping",
|
||||
cf_name.c_str(), file_num_buf, loop);
|
||||
|
||||
sr = nullptr;
|
||||
}
|
||||
|
||||
// This file is not being compacted. Consider it as the
|
||||
// first candidate to be compacted.
|
||||
uint64_t candidate_size = sr != nullptr ? sr->compensated_file_size : 0;
|
||||
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);
|
||||
}
|
||||
|
||||
// Check if the succeeding files need compaction.
|
||||
for (size_t i = loop + 1;
|
||||
candidate_count < max_files_to_compact && i < sorted_runs.size();
|
||||
i++) {
|
||||
const SortedRun* succeeding_sr = &sorted_runs[i];
|
||||
if (succeeding_sr->being_compacted) {
|
||||
break;
|
||||
}
|
||||
// Pick files if the total/last candidate file size (increased by the
|
||||
// specified ratio) is still larger than the next candidate file.
|
||||
// candidate_size is the total size of files picked so far with the
|
||||
// default kCompactionStopStyleTotalSize; with
|
||||
// kCompactionStopStyleSimilarSize, it's simply the size of the last
|
||||
// picked file.
|
||||
double sz = candidate_size * (100.0 + ratio) / 100.0;
|
||||
if (sz < static_cast<double>(succeeding_sr->size)) {
|
||||
break;
|
||||
}
|
||||
if (ioptions_.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.
|
||||
sz = (succeeding_sr->size * (100.0 + ratio)) / 100.0;
|
||||
if (sz < static_cast<double>(candidate_size)) {
|
||||
// If the small file we've encountered begins a run of similar-size
|
||||
// files, we'll pick them up on a future iteration of the outer
|
||||
// loop. If it's some lonely straggler, it'll eventually get picked
|
||||
// by the last-resort read amp strategy which disregards size ratios.
|
||||
break;
|
||||
}
|
||||
candidate_size = succeeding_sr->compensated_file_size;
|
||||
} else { // default kCompactionStopStyleTotalSize
|
||||
candidate_size += succeeding_sr->compensated_file_size;
|
||||
}
|
||||
candidate_count++;
|
||||
}
|
||||
|
||||
// Found a series of consecutive files that need compaction.
|
||||
if (candidate_count >= (unsigned int)min_merge_width) {
|
||||
start_index = loop;
|
||||
done = true;
|
||||
break;
|
||||
} else {
|
||||
for (size_t i = loop;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!done || candidate_count <= 1) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t first_index_after = start_index + candidate_count;
|
||||
// Compression is enabled if files compacted earlier already reached
|
||||
// size ratio of compression.
|
||||
bool enable_compression = true;
|
||||
int ratio_to_compress =
|
||||
ioptions_.compaction_options_universal.compression_size_percent;
|
||||
if (ratio_to_compress >= 0) {
|
||||
uint64_t total_size = 0;
|
||||
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;
|
||||
if (older_file_size * 100L >= total_size * (long)ratio_to_compress) {
|
||||
enable_compression = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t estimated_total_size = 0;
|
||||
for (unsigned int i = 0; i < first_index_after; i++) {
|
||||
estimated_total_size += sorted_runs[i].size;
|
||||
}
|
||||
uint32_t path_id = GetPathId(ioptions_, 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) {
|
||||
output_level = 0;
|
||||
} else {
|
||||
output_level = sorted_runs[first_index_after].level - 1;
|
||||
}
|
||||
|
||||
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];
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
CompactionReason compaction_reason;
|
||||
if (max_number_of_files_to_compact == UINT_MAX) {
|
||||
compaction_reason = CompactionReason::kUniversalSortedRunNum;
|
||||
} else {
|
||||
compaction_reason = CompactionReason::kUniversalSizeRatio;
|
||||
}
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level), LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, start_level,
|
||||
1, enable_compression),
|
||||
/* grandparents */ {}, /* is manual */ false, score,
|
||||
false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
|
||||
// Look at overall size amplification. If size amplification
|
||||
// exceeeds the configured value, then do a compaction
|
||||
// of the candidate files all the way upto the earliest
|
||||
// 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) {
|
||||
// percentage flexibilty while reducing size amplification
|
||||
uint64_t ratio =
|
||||
ioptions_.compaction_options_universal.max_size_amplification_percent;
|
||||
|
||||
unsigned int candidate_count = 0;
|
||||
uint64_t candidate_size = 0;
|
||||
size_t start_index = 0;
|
||||
const SortedRun* sr = nullptr;
|
||||
|
||||
// Skip files that are already being compacted
|
||||
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,
|
||||
" cannot be a candidate to reduce size amp.\n");
|
||||
sr = nullptr;
|
||||
}
|
||||
|
||||
if (sr == nullptr) {
|
||||
return nullptr; // no candidate files
|
||||
}
|
||||
{
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
sr->Dump(file_num_buf, sizeof(file_num_buf), true);
|
||||
ROCKS_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");
|
||||
}
|
||||
|
||||
// keep adding up all the remaining files
|
||||
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,
|
||||
" is already being compacted. No size amp reduction possible.\n");
|
||||
return nullptr;
|
||||
}
|
||||
candidate_size += sr->compensated_file_size;
|
||||
candidate_count++;
|
||||
}
|
||||
if (candidate_count == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// size of earliest file
|
||||
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,
|
||||
"[%s] Universal: size amp not needed. newer-files-total-size %" PRIu64
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name.c_str(), candidate_size, earliest_file_size);
|
||||
return nullptr;
|
||||
} else {
|
||||
ROCKS_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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
uint32_t path_id = GetPathId(ioptions_, estimated_total_size);
|
||||
int start_level = sorted_runs[start_index].level;
|
||||
|
||||
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];
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
return new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs),
|
||||
vstorage->num_levels() - 1,
|
||||
mutable_cf_options.MaxFileSizeForLevel(vstorage->num_levels() - 1),
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
vstorage->num_levels() - 1, 1),
|
||||
/* grandparents */ {}, /* is manual */ false, score,
|
||||
false /* deletion_compaction */,
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
|
||||
namespace rocksdb {
|
||||
class UniversalCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
UniversalCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual int MaxOutputLevel() const override { return NumberLevels() - 1; }
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
static uint32_t GetPathId(const ImmutableCFOptions& ioptions,
|
||||
uint64_t file_size);
|
||||
};
|
||||
} // namespace rocksdb
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -11,20 +11,20 @@
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
#include "db/db_impl.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "table/cuckoo_table_factory.h"
|
||||
#include "table/cuckoo_table_reader.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
// 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 <cstdlib>
|
||||
#include "cache/lru_cache.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/lru_cache.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+294
-195
@@ -20,17 +20,23 @@ class DBBloomFilterTest : public DBTestBase {
|
||||
DBBloomFilterTest() : DBTestBase("/db_bloom_filter_test") {}
|
||||
};
|
||||
|
||||
class DBBloomFilterTestWithParam : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
class DBBloomFilterTestWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
// public testing::WithParamInterface<bool> {
|
||||
protected:
|
||||
bool use_block_based_filter_;
|
||||
bool partition_filters_;
|
||||
|
||||
public:
|
||||
DBBloomFilterTestWithParam() : DBTestBase("/db_bloom_filter_tests") {}
|
||||
|
||||
~DBBloomFilterTestWithParam() {}
|
||||
|
||||
void SetUp() override { use_block_based_filter_ = GetParam(); }
|
||||
void SetUp() override {
|
||||
use_block_based_filter_ = std::get<0>(GetParam());
|
||||
partition_filters_ = std::get<1>(GetParam());
|
||||
}
|
||||
};
|
||||
|
||||
// KeyMayExist can lead to a few false positives, but not false negatives.
|
||||
@@ -43,7 +49,17 @@ TEST_P(DBBloomFilterTestWithParam, KeyMayExist) {
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.filter_policy.reset(
|
||||
NewBloomFilterPolicy(20, use_block_based_filter_));
|
||||
options_override.partition_filters = partition_filters_;
|
||||
options_override.metadata_block_size = 32;
|
||||
Options options = CurrentOptions(options_override);
|
||||
if (partition_filters_ &&
|
||||
static_cast<BlockBasedTableOptions*>(
|
||||
options.table_factory->GetOptions())
|
||||
->index_type != BlockBasedTableOptions::kTwoLevelIndexSearch) {
|
||||
// In the current implementation partitioned filters depend on partitioned
|
||||
// indexes
|
||||
continue;
|
||||
}
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
@@ -102,192 +118,204 @@ TEST_P(DBBloomFilterTestWithParam, KeyMayExist) {
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
|
||||
Options options = last_options_;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
for (bool partition_filters : {true, false}) {
|
||||
Options options = last_options_;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
if (partition_filters) {
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
WriteOptions wo;
|
||||
ReadOptions ro;
|
||||
FlushOptions fo;
|
||||
fo.wait = true;
|
||||
std::string value;
|
||||
WriteOptions wo;
|
||||
ReadOptions ro;
|
||||
FlushOptions fo;
|
||||
fo.wait = true;
|
||||
std::string value;
|
||||
|
||||
ASSERT_OK(dbfull()->Put(wo, "barbarbar", "foo"));
|
||||
ASSERT_OK(dbfull()->Put(wo, "barbarbar2", "foo2"));
|
||||
ASSERT_OK(dbfull()->Put(wo, "foofoofoo", "bar"));
|
||||
ASSERT_OK(dbfull()->Put(wo, "barbarbar", "foo"));
|
||||
ASSERT_OK(dbfull()->Put(wo, "barbarbar2", "foo2"));
|
||||
ASSERT_OK(dbfull()->Put(wo, "foofoofoo", "bar"));
|
||||
|
||||
dbfull()->Flush(fo);
|
||||
dbfull()->Flush(fo);
|
||||
|
||||
ASSERT_EQ("foo", Get("barbarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("foo2", Get("barbarbar2"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("barbarbar3"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("foo", Get("barbarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("foo2", Get("barbarbar2"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("barbarbar3"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("barfoofoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("barfoofoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
|
||||
ro.total_order_seek = true;
|
||||
ASSERT_TRUE(db_->Get(ro, "foobarbar", &value).IsNotFound());
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
ro.total_order_seek = true;
|
||||
ASSERT_TRUE(db_->Get(ro, "foobarbar", &value).IsNotFound());
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
Options options = last_options_;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
for (bool partition_filters : {true, false}) {
|
||||
Options options = last_options_;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.whole_key_filtering = false;
|
||||
if (partition_filters) {
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
WriteOptions wo;
|
||||
ReadOptions ro;
|
||||
FlushOptions fo;
|
||||
fo.wait = true;
|
||||
std::string value;
|
||||
WriteOptions wo;
|
||||
ReadOptions ro;
|
||||
FlushOptions fo;
|
||||
fo.wait = true;
|
||||
std::string value;
|
||||
|
||||
ASSERT_OK(dbfull()->Put(wo, "foobar", "foo"));
|
||||
// Needs insert some keys to make sure files are not filtered out by key
|
||||
// ranges.
|
||||
ASSERT_OK(dbfull()->Put(wo, "aaa", ""));
|
||||
ASSERT_OK(dbfull()->Put(wo, "zzz", ""));
|
||||
dbfull()->Flush(fo);
|
||||
ASSERT_OK(dbfull()->Put(wo, "foobar", "foo"));
|
||||
// Needs insert some keys to make sure files are not filtered out by key
|
||||
// ranges.
|
||||
ASSERT_OK(dbfull()->Put(wo, "aaa", ""));
|
||||
ASSERT_OK(dbfull()->Put(wo, "zzz", ""));
|
||||
dbfull()->Flush(fo);
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
Reopen(options);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
|
||||
// Reopen with whole key filtering enabled and prefix extractor
|
||||
// NULL. Bloom filter should be off for both of whole key and
|
||||
// prefix bloom.
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.prefix_extractor.reset();
|
||||
Reopen(options);
|
||||
// Reopen with whole key filtering enabled and prefix extractor
|
||||
// NULL. Bloom filter should be off for both of whole key and
|
||||
// prefix bloom.
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.prefix_extractor.reset();
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
// Write DB with only full key filtering.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foobar", "foo"));
|
||||
// Needs insert some keys to make sure files are not filtered out by key
|
||||
// ranges.
|
||||
ASSERT_OK(dbfull()->Put(wo, "aaa", ""));
|
||||
ASSERT_OK(dbfull()->Put(wo, "zzz", ""));
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
// Write DB with only full key filtering.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foobar", "foo"));
|
||||
// Needs insert some keys to make sure files are not filtered out by key
|
||||
// ranges.
|
||||
ASSERT_OK(dbfull()->Put(wo, "aaa", ""));
|
||||
ASSERT_OK(dbfull()->Put(wo, "zzz", ""));
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
// Reopen with both of whole key off and prefix extractor enabled.
|
||||
// Still no bloom filter should be used.
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
// Reopen with both of whole key off and prefix extractor enabled.
|
||||
// Still no bloom filter should be used.
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
|
||||
// Try to create a DB with mixed files:
|
||||
ASSERT_OK(dbfull()->Put(wo, "foobar", "foo"));
|
||||
// Needs insert some keys to make sure files are not filtered out by key
|
||||
// ranges.
|
||||
ASSERT_OK(dbfull()->Put(wo, "aaa", ""));
|
||||
ASSERT_OK(dbfull()->Put(wo, "zzz", ""));
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
// Try to create a DB with mixed files:
|
||||
ASSERT_OK(dbfull()->Put(wo, "foobar", "foo"));
|
||||
// Needs insert some keys to make sure files are not filtered out by key
|
||||
// ranges.
|
||||
ASSERT_OK(dbfull()->Put(wo, "aaa", ""));
|
||||
ASSERT_OK(dbfull()->Put(wo, "zzz", ""));
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
options.prefix_extractor.reset();
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
options.prefix_extractor.reset();
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
// Try to create a DB with mixed files.
|
||||
ASSERT_OK(dbfull()->Put(wo, "barfoo", "bar"));
|
||||
// In this case needs insert some keys to make sure files are
|
||||
// not filtered out by key ranges.
|
||||
ASSERT_OK(dbfull()->Put(wo, "aaa", ""));
|
||||
ASSERT_OK(dbfull()->Put(wo, "zzz", ""));
|
||||
Flush();
|
||||
// Try to create a DB with mixed files.
|
||||
ASSERT_OK(dbfull()->Put(wo, "barfoo", "bar"));
|
||||
// In this case needs insert some keys to make sure files are
|
||||
// not filtered out by key ranges.
|
||||
ASSERT_OK(dbfull()->Put(wo, "aaa", ""));
|
||||
ASSERT_OK(dbfull()->Put(wo, "zzz", ""));
|
||||
Flush();
|
||||
|
||||
// Now we have two files:
|
||||
// File 1: An older file with prefix bloom.
|
||||
// File 2: A newer file with whole bloom filter.
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
// Now we have two files:
|
||||
// File 1: An older file with prefix bloom.
|
||||
// File 2: A newer file with whole bloom filter.
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
|
||||
// Reopen with the same setting: only whole key is used
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 5);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 6);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
// Reopen with the same setting: only whole key is used
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 5);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 6);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
|
||||
// Restart with both filters are allowed
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
// File 1 will has it filtered out.
|
||||
// File 2 will not, as prefix `foo` exists in the file.
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 8);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 10);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
// Restart with both filters are allowed
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
// File 1 will has it filtered out.
|
||||
// File 2 will not, as prefix `foo` exists in the file.
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 8);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 10);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
|
||||
// Restart with only prefix bloom is allowed.
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
// Restart with only prefix bloom is allowed.
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
@@ -301,6 +329,12 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
table_options.no_block_cache = true;
|
||||
table_options.filter_policy.reset(
|
||||
NewBloomFilterPolicy(10, use_block_based_filter_));
|
||||
table_options.partition_filters = partition_filters_;
|
||||
if (partition_filters_) {
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
table_options.metadata_block_size = 32;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -327,7 +361,13 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
int reads = env_->random_read_counter_.Read();
|
||||
fprintf(stderr, "%d present => %d reads\n", N, reads);
|
||||
ASSERT_GE(reads, N);
|
||||
ASSERT_LE(reads, N + 2 * N / 100);
|
||||
if (partition_filters_) {
|
||||
// Without block cache, we read an extra partition filter per each
|
||||
// level*read and a partition index per each read
|
||||
ASSERT_LE(reads, 4 * N + 2 * N / 100);
|
||||
} else {
|
||||
ASSERT_LE(reads, N + 2 * N / 100);
|
||||
}
|
||||
|
||||
// Lookup present keys. Should rarely read from either sstable.
|
||||
env_->random_read_counter_.Reset();
|
||||
@@ -336,7 +376,13 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
}
|
||||
reads = env_->random_read_counter_.Read();
|
||||
fprintf(stderr, "%d missing => %d reads\n", N, reads);
|
||||
ASSERT_LE(reads, 3 * N / 100);
|
||||
if (partition_filters_) {
|
||||
// With partitioned filter we read one extra filter per level per each
|
||||
// missed read.
|
||||
ASSERT_LE(reads, 2 * N + 3 * N / 100);
|
||||
} else {
|
||||
ASSERT_LE(reads, 3 * N / 100);
|
||||
}
|
||||
|
||||
env_->delay_sstable_sync_.store(false, std::memory_order_release);
|
||||
Close();
|
||||
@@ -344,7 +390,9 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBloomFilterTestWithParam, DBBloomFilterTestWithParam,
|
||||
::testing::Bool());
|
||||
::testing::Values(std::make_tuple(true, false),
|
||||
std::make_tuple(false, true),
|
||||
std::make_tuple(false, false)));
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
while (ChangeFilterOptions()) {
|
||||
@@ -401,28 +449,13 @@ TEST_F(DBBloomFilterTest, BloomFilterCompatibility) {
|
||||
ASSERT_EQ(Key(i), Get(1, Key(i)));
|
||||
}
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterReverseCompatibility) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
// Check db with partitioned full filter
|
||||
table_options.partition_filters = true;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
// Create with full filter
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
const int maxKey = 10000;
|
||||
for (int i = 0; i < maxKey; i++) {
|
||||
ASSERT_OK(Put(1, Key(i), Key(i)));
|
||||
}
|
||||
ASSERT_OK(Put(1, Key(maxKey + 55555), Key(maxKey + 55555)));
|
||||
Flush(1);
|
||||
|
||||
// Check db with block_based filter
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
// Check if they can be found
|
||||
@@ -432,6 +465,43 @@ TEST_F(DBBloomFilterTest, BloomFilterReverseCompatibility) {
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterReverseCompatibility) {
|
||||
for (bool partition_filters : {true, false}) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
if (partition_filters) {
|
||||
table_options.partition_filters = true;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create with full filter
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
const int maxKey = 10000;
|
||||
for (int i = 0; i < maxKey; i++) {
|
||||
ASSERT_OK(Put(1, Key(i), Key(i)));
|
||||
}
|
||||
ASSERT_OK(Put(1, Key(maxKey + 55555), Key(maxKey + 55555)));
|
||||
Flush(1);
|
||||
|
||||
// Check db with block_based filter
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
// Check if they can be found
|
||||
for (int i = 0; i < maxKey; i++) {
|
||||
ASSERT_EQ(Key(i), Get(1, Key(i)));
|
||||
}
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A wrapped bloom over default FilterPolicy
|
||||
class WrappedBloom : public FilterPolicy {
|
||||
@@ -586,11 +656,12 @@ TEST_F(DBBloomFilterTest, PrefixExtractorBlockFilter) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
class BloomStatsTestWithParam
|
||||
: public DBBloomFilterTest,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public testing::WithParamInterface<std::tuple<bool, bool, bool>> {
|
||||
public:
|
||||
BloomStatsTestWithParam() {
|
||||
use_block_table_ = std::get<0>(GetParam());
|
||||
use_block_based_builder_ = std::get<1>(GetParam());
|
||||
partition_filters_ = std::get<2>(GetParam());
|
||||
|
||||
options_.create_if_missing = true;
|
||||
options_.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(4));
|
||||
@@ -599,13 +670,21 @@ class BloomStatsTestWithParam
|
||||
if (use_block_table_) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.hash_index_allow_collision = false;
|
||||
if (partition_filters_) {
|
||||
assert(!use_block_based_builder_);
|
||||
table_options.partition_filters = partition_filters_;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
table_options.filter_policy.reset(
|
||||
NewBloomFilterPolicy(10, use_block_based_builder_));
|
||||
options_.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
} else {
|
||||
assert(!partition_filters_); // not supported in plain table
|
||||
PlainTableOptions table_options;
|
||||
options_.table_factory.reset(NewPlainTableFactory(table_options));
|
||||
}
|
||||
options_.env = env_;
|
||||
|
||||
perf_context.Reset();
|
||||
DestroyAndReopen(options_);
|
||||
@@ -622,6 +701,7 @@ class BloomStatsTestWithParam
|
||||
|
||||
bool use_block_table_;
|
||||
bool use_block_based_builder_;
|
||||
bool partition_filters_;
|
||||
Options options_;
|
||||
};
|
||||
|
||||
@@ -716,25 +796,44 @@ TEST_P(BloomStatsTestWithParam, BloomStatsTestWithIter) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(value1, iter->value().ToString());
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_hit_count);
|
||||
if (partition_filters_) {
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_hit_count); // no_io
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_miss_count); // no_io
|
||||
} else {
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_hit_count);
|
||||
}
|
||||
|
||||
iter->Seek(key3);
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(value3, iter->value().ToString());
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
if (partition_filters_) {
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_hit_count); // no_io
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_miss_count); // no_io
|
||||
} else {
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
}
|
||||
|
||||
iter->Seek(key2);
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_miss_count);
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
if (partition_filters_) {
|
||||
// iter is still valid since filter did not reject the key2
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_hit_count); // no_io
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_miss_count); // no_io
|
||||
} else {
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_miss_count);
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(BloomStatsTestWithParam, BloomStatsTestWithParam,
|
||||
::testing::Values(std::make_tuple(true, true),
|
||||
std::make_tuple(true, false),
|
||||
std::make_tuple(false, false)));
|
||||
::testing::Values(std::make_tuple(true, true, false),
|
||||
std::make_tuple(true, false, false),
|
||||
std::make_tuple(true, false, true),
|
||||
std::make_tuple(false, false,
|
||||
false)));
|
||||
|
||||
namespace {
|
||||
void PrefixScanInit(DBBloomFilterTest* dbtest) {
|
||||
|
||||
@@ -40,6 +40,12 @@ class DBCompactionTestWithParam
|
||||
bool exclusive_manual_compaction_;
|
||||
};
|
||||
|
||||
class DBCompactionDirectIOTest : public DBCompactionTest,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBCompactionDirectIOTest() : DBCompactionTest() {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
class FlushedFileCollector : public EventListener {
|
||||
@@ -772,6 +778,41 @@ TEST_F(DBCompactionTest, ZeroSeqIdCompaction) {
|
||||
ASSERT_OK(Put("", ""));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ManualCompactionUnknownOutputSize) {
|
||||
// github issue #2249
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// create two files in l1 that we can compact
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int j = 0; j < options.level0_file_num_compaction_trigger; j++) {
|
||||
// make l0 files' ranges overlap to avoid trivial move
|
||||
Put(std::to_string(2 * i), std::string(1, 'A'));
|
||||
Put(std::to_string(2 * i + 1), std::string(1, 'A'));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 0), i + 1);
|
||||
}
|
||||
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
dbfull()->GetColumnFamilyMetaData(dbfull()->DefaultColumnFamily(), &cf_meta);
|
||||
ASSERT_EQ(2, cf_meta.levels[1].files.size());
|
||||
std::vector<std::string> input_filenames;
|
||||
for (const auto& sst_file : cf_meta.levels[1].files) {
|
||||
input_filenames.push_back(sst_file.name);
|
||||
}
|
||||
|
||||
// note CompactionOptions::output_file_size_limit is unset.
|
||||
CompactionOptions compact_opt;
|
||||
compact_opt.compression = kNoCompression;
|
||||
dbfull()->CompactFiles(compact_opt, input_filenames, 1);
|
||||
}
|
||||
|
||||
// Check that writes done during a memtable compaction are recovered
|
||||
// if the database is shutdown during the memtable compaction.
|
||||
TEST_F(DBCompactionTest, RecoverDuringMemtableCompaction) {
|
||||
@@ -1843,6 +1884,47 @@ TEST_F(DBCompactionTest, L0_CompactionBug_Issue44_b) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ManualAutoRace) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkCompaction", "DBCompactionTest::ManualAutoRace:1"},
|
||||
{"DBImpl::RunManualCompaction:WaitScheduled",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put(1, "foo", "");
|
||||
Put(1, "bar", "");
|
||||
Flush(1);
|
||||
Put(1, "foo", "");
|
||||
Put(1, "bar", "");
|
||||
// Generate four files in CF 0, which should trigger an auto compaction
|
||||
Put("foo", "");
|
||||
Put("bar", "");
|
||||
Flush();
|
||||
Put("foo", "");
|
||||
Put("bar", "");
|
||||
Flush();
|
||||
Put("foo", "");
|
||||
Put("bar", "");
|
||||
Flush();
|
||||
Put("foo", "");
|
||||
Put("bar", "");
|
||||
Flush();
|
||||
|
||||
// The auto compaction is scheduled but waited until here
|
||||
TEST_SYNC_POINT("DBCompactionTest::ManualAutoRace:1");
|
||||
// The auto compaction will wait until the the manual compaction is registerd
|
||||
// before processing so that it will be cancelled.
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr);
|
||||
ASSERT_EQ("0,1", FilesPerLevel(1));
|
||||
|
||||
// Eventually the cancelled compaction will be rescheduled and executed.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, ManualCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
@@ -2492,12 +2574,164 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.level0_file_num_compaction_trigger = 5;
|
||||
options.max_background_compactions = 2;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kValueSize = 1 << 20;
|
||||
Random rnd(301);
|
||||
std::string value(RandomString(&rnd, kValueSize));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"LevelCompactionPicker::PickCompactionBySize:0",
|
||||
"CompactionJob::Run():Start"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// index: 0 1 2 3 4 5 6 7 8 9
|
||||
// size: 1MB 1MB 1MB 1MB 1MB 2MB 1MB 1MB 1MB 1MB
|
||||
// score: 1.5 1.3 1.5 2.0 inf
|
||||
//
|
||||
// Files 0-4 will be included in an L0->L1 compaction.
|
||||
//
|
||||
// L0->L0 will be triggered since the sync points guarantee compaction to base
|
||||
// level is still blocked when files 5-9 trigger another compaction.
|
||||
//
|
||||
// Files 6-9 are the longest span of available files for which
|
||||
// work-per-deleted-file decreases (see "score" row above).
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ASSERT_OK(Put(Key(0), "")); // prevents trivial move
|
||||
if (i == 5) {
|
||||
ASSERT_OK(Put(Key(i + 1), value + value));
|
||||
} else {
|
||||
ASSERT_OK(Put(Key(i + 1), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
|
||||
&level_to_files);
|
||||
ASSERT_GE(level_to_files.size(), 2); // at least L0 and L1
|
||||
// L0 has the 2MB file (not compacted) and 4MB file (output of L0->L0)
|
||||
ASSERT_EQ(2, level_to_files[0].size());
|
||||
ASSERT_GT(level_to_files[1].size(), 0);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_GE(level_to_files[0][i].fd.file_size, 1 << 21);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, IntraL0CompactionDoesNotObsoleteDeletions) {
|
||||
// regression test for issue #2722: L0->L0 compaction can resurrect deleted
|
||||
// keys from older L0 files if L1+ files' key-ranges do not include the key.
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.level0_file_num_compaction_trigger = 5;
|
||||
options.max_background_compactions = 2;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kValueSize = 1 << 20;
|
||||
Random rnd(301);
|
||||
std::string value(RandomString(&rnd, kValueSize));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"LevelCompactionPicker::PickCompactionBySize:0",
|
||||
"CompactionJob::Run():Start"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// index: 0 1 2 3 4 5 6 7 8 9
|
||||
// size: 1MB 1MB 1MB 1MB 1MB 1MB 1MB 1MB 1MB 1MB
|
||||
// score: 1.25 1.33 1.5 2.0 inf
|
||||
//
|
||||
// Files 0-4 will be included in an L0->L1 compaction.
|
||||
//
|
||||
// L0->L0 will be triggered since the sync points guarantee compaction to base
|
||||
// level is still blocked when files 5-9 trigger another compaction. All files
|
||||
// 5-9 are included in the L0->L0 due to work-per-deleted file decreasing.
|
||||
//
|
||||
// Put a key-value in files 0-4. Delete that key in files 5-9. Verify the
|
||||
// L0->L0 preserves the deletion such that the key remains deleted.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// key 0 serves both to prevent trivial move and as the key we want to
|
||||
// verify is not resurrected by L0->L0 compaction.
|
||||
if (i < 5) {
|
||||
ASSERT_OK(Put(Key(0), ""));
|
||||
} else {
|
||||
ASSERT_OK(Delete(Key(0)));
|
||||
}
|
||||
ASSERT_OK(Put(Key(i + 1), value));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
|
||||
&level_to_files);
|
||||
ASSERT_GE(level_to_files.size(), 2); // at least L0 and L1
|
||||
// L0 has a single output file from L0->L0
|
||||
ASSERT_EQ(1, level_to_files[0].size());
|
||||
ASSERT_GT(level_to_files[1].size(), 0);
|
||||
ASSERT_GE(level_to_files[0][0].fd.file_size, 1 << 22);
|
||||
|
||||
ReadOptions roptions;
|
||||
std::string result;
|
||||
ASSERT_TRUE(db_->Get(roptions, Key(0), &result).IsNotFound());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithParam, DBCompactionTestWithParam,
|
||||
::testing::Values(std::make_tuple(1, true),
|
||||
std::make_tuple(1, false),
|
||||
std::make_tuple(4, true),
|
||||
std::make_tuple(4, false)));
|
||||
|
||||
TEST_P(DBCompactionDirectIOTest, DirectIO) {
|
||||
Options options = CurrentOptions();
|
||||
Destroy(options);
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.use_direct_io_for_flush_and_compaction = GetParam();
|
||||
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_io_for_flush_and_compaction);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::OpenCompactionOutputFile", [&](void* arg) {
|
||||
bool* use_direct_writes = static_cast<bool*>(arg);
|
||||
ASSERT_EQ(*use_direct_writes,
|
||||
options.use_direct_io_for_flush_and_compaction);
|
||||
});
|
||||
if (options.use_direct_io_for_flush_and_compaction) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"SanitizeOptions:direct_io", [&](void* arg) {
|
||||
readahead = true;
|
||||
});
|
||||
}
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
MakeTables(3, "p", "q", 1);
|
||||
ASSERT_EQ("1,1,1", FilesPerLevel(1));
|
||||
Compact(1, "p1", "p9");
|
||||
ASSERT_FALSE(readahead ^ options.use_direct_io_for_flush_and_compaction);
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel(1));
|
||||
Destroy(options);
|
||||
delete options.env;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionDirectIOTest, DBCompactionDirectIOTest,
|
||||
testing::Bool());
|
||||
|
||||
class CompactionPriTest : public DBTestBase,
|
||||
public testing::WithParamInterface<uint32_t> {
|
||||
public:
|
||||
|
||||
@@ -75,6 +75,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
|
||||
options.compression_per_level[0] = kNoCompression;
|
||||
options.compression_per_level[1] = kLZ4Compression;
|
||||
options.compression_per_level[2] = kSnappyCompression;
|
||||
options.env = env_;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
+10
-11
@@ -15,13 +15,13 @@
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
@@ -31,12 +31,11 @@ Status DBImpl::DisableFileDeletions() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
++disable_delete_obsolete_files_;
|
||||
if (disable_delete_obsolete_files_ == 1) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Disabled");
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Disabled");
|
||||
} else {
|
||||
Log(InfoLogLevel::WARN_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -55,12 +54,12 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
if (disable_delete_obsolete_files_ == 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Enabled");
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Enabled");
|
||||
should_purge_files = true;
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
} else {
|
||||
Log(InfoLogLevel::WARN_LEVEL, immutable_db_options_.info_log,
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
@@ -106,8 +105,8 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
if (!status.ok()) {
|
||||
mutex_.Unlock();
|
||||
Log(InfoLogLevel::ERROR_LEVEL, immutable_db_options_.info_log,
|
||||
"Cannot Flush data %s\n", status.ToString().c_str());
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "Cannot Flush data %s\n",
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
+35
-1
@@ -19,12 +19,19 @@ class DBFlushTest : public DBTestBase {
|
||||
DBFlushTest() : DBTestBase("/db_flush_test") {}
|
||||
};
|
||||
|
||||
class DBFlushDirectIOTest : public DBFlushTest,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBFlushDirectIOTest() : DBFlushTest() {}
|
||||
};
|
||||
|
||||
// We had issue when two background threads trying to flush at the same time,
|
||||
// only one of them get committed. The test verifies the issue is fixed.
|
||||
TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_background_flushes = 2;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
FlushOptions no_wait;
|
||||
no_wait.wait = false;
|
||||
@@ -50,7 +57,7 @@ TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
|
||||
TEST_F(DBFlushTest, SyncFail) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = fault_injection_env.get();
|
||||
@@ -82,6 +89,33 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBFlushDirectIOTest, DirectIO) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_background_flushes = 2;
|
||||
options.use_direct_io_for_flush_and_compaction = GetParam();
|
||||
options.env = new MockEnv(Env::Default());
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:create_file", [&](void* arg) {
|
||||
bool* use_direct_writes = static_cast<bool*>(arg);
|
||||
ASSERT_EQ(*use_direct_writes,
|
||||
options.use_direct_io_for_flush_and_compaction);
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("foo", "v"));
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = true;
|
||||
ASSERT_OK(dbfull()->Flush(flush_options));
|
||||
Destroy(options);
|
||||
delete options.env;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBFlushDirectIOTest, DBFlushDirectIOTest,
|
||||
testing::Bool());
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+341
-4249
File diff suppressed because it is too large.
Load diff
+69
-24
@@ -34,18 +34,19 @@
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "memtable_list.h"
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
#include "options/db_options.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -91,7 +92,7 @@ class DBImpl : public DB {
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value) override;
|
||||
PinnableSlice* value) override;
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options,
|
||||
@@ -99,10 +100,19 @@ class DBImpl : public DB {
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override;
|
||||
|
||||
virtual Status CreateColumnFamily(const ColumnFamilyOptions& options,
|
||||
virtual Status CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle) override;
|
||||
virtual Status CreateColumnFamilies(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const std::vector<std::string>& column_family_names,
|
||||
std::vector<ColumnFamilyHandle*>* handles) override;
|
||||
virtual Status CreateColumnFamilies(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles) override;
|
||||
virtual Status DropColumnFamily(ColumnFamilyHandle* column_family) override;
|
||||
virtual Status DropColumnFamilies(
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
|
||||
// Returns false if key doesn't exist in the database and true if it may.
|
||||
// If value_found is not passed in as null, then return the value if found in
|
||||
@@ -191,7 +201,11 @@ class DBImpl : public DB {
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
|
||||
bool HasActiveSnapshotLaterThanSN(SequenceNumber sn);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::ResetStats;
|
||||
virtual Status ResetStats() override;
|
||||
virtual Status DisableFileDeletions() override;
|
||||
virtual Status EnableFileDeletions(bool force) override;
|
||||
virtual int IsFileDeletionsEnabled() const;
|
||||
@@ -314,7 +328,7 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool disallow_trivial_move = false);
|
||||
|
||||
void TEST_MaybeFlushColumnFamilies();
|
||||
void TEST_HandleWALFull();
|
||||
|
||||
bool TEST_UnableToFlushOldestLog() {
|
||||
return unable_to_flush_oldest_log_;
|
||||
@@ -451,6 +465,9 @@ class DBImpl : public DB {
|
||||
// mutex is released.
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle(uint32_t column_family_id);
|
||||
|
||||
// Same as above, should called without mutex held and not on write thread.
|
||||
ColumnFamilyHandle* GetColumnFamilyHandleUnlocked(uint32_t column_family_id);
|
||||
|
||||
// Returns the number of currently running flushes.
|
||||
// REQUIREMENT: mutex_ must be held when calling this function.
|
||||
int num_running_flushes() {
|
||||
@@ -546,9 +563,10 @@ class DBImpl : public DB {
|
||||
RangeDelAggregator* range_del_agg);
|
||||
|
||||
// Except in DB::Open(), WriteOptionsFile can only be called when:
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
// 2. db_mutex is held
|
||||
Status WriteOptionsFile();
|
||||
// Persist options to options file.
|
||||
// If need_mutex_lock = false, the method will lock DB mutex.
|
||||
// If need_enter_write_thread = false, the method will enter write thread.
|
||||
Status WriteOptionsFile(bool need_mutex_lock, bool need_enter_write_thread);
|
||||
|
||||
// The following two functions can only be called when:
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
@@ -556,6 +574,10 @@ class DBImpl : public DB {
|
||||
Status RenameTempFileToOptionsFile(const std::string& file_name);
|
||||
Status DeleteObsoleteOptionsFiles();
|
||||
|
||||
void NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, TableProperties prop);
|
||||
|
||||
void NotifyOnFlushCompleted(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, TableProperties prop);
|
||||
@@ -600,7 +622,19 @@ class DBImpl : public DB {
|
||||
#endif
|
||||
struct CompactionState;
|
||||
|
||||
struct WriteContext;
|
||||
struct WriteContext {
|
||||
autovector<SuperVersion*> superversions_to_free_;
|
||||
autovector<MemTable*> memtables_to_free_;
|
||||
|
||||
~WriteContext() {
|
||||
for (auto& sv : superversions_to_free_) {
|
||||
delete sv;
|
||||
}
|
||||
for (auto& m : memtables_to_free_) {
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct PurgeFileInfo;
|
||||
|
||||
@@ -615,6 +649,12 @@ class DBImpl : public DB {
|
||||
|
||||
const Status CreateArchivalDirectory();
|
||||
|
||||
Status CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& cf_name,
|
||||
ColumnFamilyHandle** handle);
|
||||
|
||||
Status DropColumnFamilyImpl(ColumnFamilyHandle* column_family);
|
||||
|
||||
// Delete any unneeded files and stale in-memory entries.
|
||||
void DeleteObsoleteFiles();
|
||||
// Delete obsolete files and log status and information of file deletion
|
||||
@@ -677,6 +717,20 @@ class DBImpl : public DB {
|
||||
// Wait for memtable flushed
|
||||
Status WaitForFlushMemTable(ColumnFamilyData* cfd);
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status HandleWALFull(WriteContext* write_context);
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status HandleWriteBufferFull(WriteContext* write_context);
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status PreprocessWrite(const WriteOptions& write_options, bool need_log_sync,
|
||||
bool* logs_getting_syned, WriteContext* write_context);
|
||||
|
||||
Status WriteToWAL(const autovector<WriteThread::Writer*>& write_group,
|
||||
log::Writer* log_writer, bool need_log_sync,
|
||||
bool need_log_dir_sync, SequenceNumber sequence);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status CompactFilesImpl(const CompactionOptions& compact_options,
|
||||
@@ -740,12 +794,6 @@ class DBImpl : public DB {
|
||||
|
||||
const Snapshot* GetSnapshotImpl(bool is_write_conflict_boundary);
|
||||
|
||||
// Persist RocksDB options under the single write thread
|
||||
// REQUIRES: mutex locked
|
||||
Status PersistOptions();
|
||||
|
||||
void MaybeFlushColumnFamilies();
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
@@ -821,7 +869,7 @@ class DBImpl : public DB {
|
||||
std::deque<LogWriterNumber> logs_;
|
||||
// Signaled when getting_synced becomes false for some of the logs_.
|
||||
InstrumentedCondVar log_sync_cv_;
|
||||
uint64_t total_log_size_;
|
||||
std::atomic<uint64_t> total_log_size_;
|
||||
// only used for dynamically adjusting max_total_wal_size. it is a sum of
|
||||
// [write_buffer_size * max_write_buffer_number] over all column families
|
||||
uint64_t max_total_in_memory_state_;
|
||||
@@ -1070,13 +1118,6 @@ class DBImpl : public DB {
|
||||
DBImpl(const DBImpl&);
|
||||
void operator=(const DBImpl&);
|
||||
|
||||
// Return the earliest snapshot where seqno is visible.
|
||||
// Store the snapshot right before that, if any, in prev_snapshot
|
||||
inline SequenceNumber findEarliestVisibleSnapshot(
|
||||
SequenceNumber in,
|
||||
std::vector<SequenceNumber>& snapshots,
|
||||
SequenceNumber* prev_snapshot);
|
||||
|
||||
// Background threads call this function, which is just a wrapper around
|
||||
// the InstallSuperVersion() function. Background threads carry
|
||||
// job_context which can have new_superversion already
|
||||
@@ -1106,7 +1147,7 @@ class DBImpl : public DB {
|
||||
// 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, std::string* value,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
bool* value_found = nullptr);
|
||||
|
||||
bool GetIntPropertyInternal(ColumnFamilyData* cfd,
|
||||
@@ -1129,6 +1170,10 @@ extern Options SanitizeOptions(const std::string& db,
|
||||
|
||||
extern DBOptions SanitizeOptions(const std::string& db, const DBOptions& src);
|
||||
|
||||
extern CompressionType GetCompressionFlush(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Fix user-supplied options to be reasonable
|
||||
template <class T, class V>
|
||||
static void ClipToRange(T* ptr, V minvalue, V maxvalue) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+4
-3
@@ -10,7 +10,7 @@
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "util/thread_status_updater.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -19,9 +19,10 @@ uint64_t DBImpl::TEST_GetLevel0TotalSize() {
|
||||
return default_cf_handle_->cfd()->current()->storage_info()->NumLevelBytes(0);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_MaybeFlushColumnFamilies() {
|
||||
void DBImpl::TEST_HandleWALFull() {
|
||||
WriteContext write_context;
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
MaybeFlushColumnFamilies();
|
||||
HandleWALFull(&write_context);
|
||||
}
|
||||
|
||||
int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
|
||||
+14
-13
@@ -61,8 +61,8 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
assert(column_family);
|
||||
|
||||
if (target_level < 1) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Invalid target level %d\n", target_level);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Invalid target level %d\n", target_level);
|
||||
return Status::InvalidArgument("Invalid target level");
|
||||
}
|
||||
|
||||
@@ -75,8 +75,9 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
|
||||
if (target_level >= vstorage->num_levels()) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Target level %d does not exist\n", target_level);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Target level %d does not exist\n",
|
||||
target_level);
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument("Target level does not exist");
|
||||
}
|
||||
@@ -94,9 +95,9 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
for (size_t i = 0; i < l0_files.size(); ++i) {
|
||||
auto f = l0_files[i];
|
||||
if (f->being_compacted) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. File %" PRIu64 " being compacted\n",
|
||||
f->fd.GetNumber());
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. File %" PRIu64 " being compacted\n",
|
||||
f->fd.GetNumber());
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument("PromoteL0 called during L0 compaction");
|
||||
}
|
||||
@@ -104,10 +105,10 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
if (i == 0) continue;
|
||||
auto prev_f = l0_files[i - 1];
|
||||
if (icmp->Compare(prev_f->largest, f->smallest) >= 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Files %" PRIu64 " and %" PRIu64
|
||||
" have overlapping ranges\n",
|
||||
prev_f->fd.GetNumber(), f->fd.GetNumber());
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Files %" PRIu64 " and %" PRIu64
|
||||
" have overlapping ranges\n",
|
||||
prev_f->fd.GetNumber(), f->fd.GetNumber());
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument("L0 has overlapping files");
|
||||
}
|
||||
@@ -116,8 +117,8 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
// Check that all levels up to target_level are empty.
|
||||
for (int level = 1; level <= target_level; ++level) {
|
||||
if (vstorage->NumLevelFiles(level) > 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Level %d not empty\n", level);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Level %d not empty\n", level);
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument(
|
||||
"All levels up to target_level "
|
||||
|
||||
@@ -0,0 +1,536 @@
|
||||
// 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/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include "db/event_helpers.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
|
||||
|
||||
namespace rocksdb {
|
||||
uint64_t DBImpl::FindMinPrepLogReferencedByMemTable() {
|
||||
if (!allow_2pc()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t min_log = 0;
|
||||
|
||||
// we must look through the memtables for two phase transactions
|
||||
// that have been committed but not yet flushed
|
||||
for (auto loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (loop_cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto log = loop_cfd->imm()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
|
||||
log = loop_cfd->mem()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
}
|
||||
|
||||
return min_log;
|
||||
}
|
||||
|
||||
void DBImpl::MarkLogAsHavingPrepSectionFlushed(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
auto it = prepared_section_completed_.find(log);
|
||||
assert(it != prepared_section_completed_.end());
|
||||
it->second += 1;
|
||||
}
|
||||
|
||||
void DBImpl::MarkLogAsContainingPrepSection(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
min_log_with_prep_.push(log);
|
||||
auto it = prepared_section_completed_.find(log);
|
||||
if (it == prepared_section_completed_.end()) {
|
||||
prepared_section_completed_[log] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DBImpl::FindMinLogContainingOutstandingPrep() {
|
||||
|
||||
if (!allow_2pc()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
uint64_t min_log = 0;
|
||||
|
||||
// first we look in the prepared heap where we keep
|
||||
// track of transactions that have been prepared (written to WAL)
|
||||
// but not yet committed.
|
||||
while (!min_log_with_prep_.empty()) {
|
||||
min_log = min_log_with_prep_.top();
|
||||
|
||||
auto it = prepared_section_completed_.find(min_log);
|
||||
|
||||
// value was marked as 'deleted' from heap
|
||||
if (it != prepared_section_completed_.end() && it->second > 0) {
|
||||
it->second -= 1;
|
||||
min_log_with_prep_.pop();
|
||||
|
||||
// back to squere one...
|
||||
min_log = 0;
|
||||
continue;
|
||||
} else {
|
||||
// found a valid value
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return min_log;
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
uint64_t log_number = versions_->MinLogNumber();
|
||||
|
||||
if (allow_2pc()) {
|
||||
// if are 2pc we must consider logs containing prepared
|
||||
// sections of outstanding transactions.
|
||||
//
|
||||
// We must check min logs with outstanding prep before we check
|
||||
// logs referneces by memtables because a log referenced by the
|
||||
// first data structure could transition to the second under us.
|
||||
//
|
||||
// TODO(horuff): iterating over all column families under db mutex.
|
||||
// should find more optimial solution
|
||||
auto min_log_in_prep_heap = FindMinLogContainingOutstandingPrep();
|
||||
|
||||
if (min_log_in_prep_heap != 0 && min_log_in_prep_heap < log_number) {
|
||||
log_number = min_log_in_prep_heap;
|
||||
}
|
||||
|
||||
auto min_log_refed_by_mem = FindMinPrepLogReferencedByMemTable();
|
||||
|
||||
if (min_log_refed_by_mem != 0 && min_log_refed_by_mem < log_number) {
|
||||
log_number = min_log_refed_by_mem;
|
||||
}
|
||||
}
|
||||
return log_number;
|
||||
}
|
||||
|
||||
// * Returns the list of live files in 'sst_live'
|
||||
// If it's doing full scan:
|
||||
// * Returns the list of all files in the filesystem in
|
||||
// 'full_scan_candidate_files'.
|
||||
// Otherwise, gets obsolete files from VersionSet.
|
||||
// no_full_scan = true -- never do the full scan using GetChildren()
|
||||
// force = false -- don't force the full scan, except every
|
||||
// mutable_db_options_.delete_obsolete_files_period_micros
|
||||
// force = true -- force the full scan
|
||||
void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
bool no_full_scan) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
// if deletion is disabled, do nothing
|
||||
if (disable_delete_obsolete_files_ > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool doing_the_full_scan = false;
|
||||
|
||||
// logic for figurint out if we're doing the full scan
|
||||
if (no_full_scan) {
|
||||
doing_the_full_scan = false;
|
||||
} else if (force ||
|
||||
mutable_db_options_.delete_obsolete_files_period_micros == 0) {
|
||||
doing_the_full_scan = true;
|
||||
} else {
|
||||
const uint64_t now_micros = env_->NowMicros();
|
||||
if ((delete_obsolete_files_last_run_ +
|
||||
mutable_db_options_.delete_obsolete_files_period_micros) <
|
||||
now_micros) {
|
||||
doing_the_full_scan = true;
|
||||
delete_obsolete_files_last_run_ = now_micros;
|
||||
}
|
||||
}
|
||||
|
||||
// don't delete files that might be currently written to from compaction
|
||||
// threads
|
||||
// Since job_context->min_pending_output is set, until file scan finishes,
|
||||
// mutex_ cannot be released. Otherwise, we might see no min_pending_output
|
||||
// here but later find newer generated unfinalized files while scannint.
|
||||
if (!pending_outputs_.empty()) {
|
||||
job_context->min_pending_output = *pending_outputs_.begin();
|
||||
} else {
|
||||
// delete all of them
|
||||
job_context->min_pending_output = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
// Get obsolete files. This function will also update the list of
|
||||
// pending files in VersionSet().
|
||||
versions_->GetObsoleteFiles(&job_context->sst_delete_files,
|
||||
&job_context->manifest_delete_files,
|
||||
job_context->min_pending_output);
|
||||
|
||||
// store the current filenum, lognum, etc
|
||||
job_context->manifest_file_number = versions_->manifest_file_number();
|
||||
job_context->pending_manifest_file_number =
|
||||
versions_->pending_manifest_file_number();
|
||||
job_context->log_number = MinLogNumberToKeep();
|
||||
|
||||
job_context->prev_log_number = versions_->prev_log_number();
|
||||
|
||||
versions_->AddLiveFiles(&job_context->sst_live);
|
||||
if (doing_the_full_scan) {
|
||||
for (size_t path_id = 0; path_id < immutable_db_options_.db_paths.size();
|
||||
path_id++) {
|
||||
// set of all files in the directory. We'll exclude files that are still
|
||||
// alive in the subsequent processings.
|
||||
std::vector<std::string> files;
|
||||
env_->GetChildren(immutable_db_options_.db_paths[path_id].path,
|
||||
&files); // Ignore errors
|
||||
for (std::string file : files) {
|
||||
// TODO(icanadi) clean up this mess to avoid having one-off "/" prefixes
|
||||
job_context->full_scan_candidate_files.emplace_back(
|
||||
"/" + file, static_cast<uint32_t>(path_id));
|
||||
}
|
||||
}
|
||||
|
||||
// Add log files in wal_dir
|
||||
if (immutable_db_options_.wal_dir != dbname_) {
|
||||
std::vector<std::string> log_files;
|
||||
env_->GetChildren(immutable_db_options_.wal_dir,
|
||||
&log_files); // Ignore errors
|
||||
for (std::string log_file : log_files) {
|
||||
job_context->full_scan_candidate_files.emplace_back(log_file, 0);
|
||||
}
|
||||
}
|
||||
// Add info log files in db_log_dir
|
||||
if (!immutable_db_options_.db_log_dir.empty() &&
|
||||
immutable_db_options_.db_log_dir != dbname_) {
|
||||
std::vector<std::string> info_log_files;
|
||||
// Ignore errors
|
||||
env_->GetChildren(immutable_db_options_.db_log_dir, &info_log_files);
|
||||
for (std::string log_file : info_log_files) {
|
||||
job_context->full_scan_candidate_files.emplace_back(log_file, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logs_ is empty when called during recovery, in which case there can't yet
|
||||
// be any tracked obsolete logs
|
||||
if (!alive_log_files_.empty() && !logs_.empty()) {
|
||||
uint64_t min_log_number = job_context->log_number;
|
||||
size_t num_alive_log_files = alive_log_files_.size();
|
||||
// find newly obsoleted log files
|
||||
while (alive_log_files_.begin()->number < min_log_number) {
|
||||
auto& earliest = *alive_log_files_.begin();
|
||||
if (immutable_db_options_.recycle_log_file_num >
|
||||
log_recycle_files.size()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"adding log %" PRIu64 " to recycle list\n",
|
||||
earliest.number);
|
||||
log_recycle_files.push_back(earliest.number);
|
||||
} else {
|
||||
job_context->log_delete_files.push_back(earliest.number);
|
||||
}
|
||||
if (job_context->size_log_to_delete == 0) {
|
||||
job_context->prev_total_log_size = total_log_size_;
|
||||
job_context->num_alive_log_files = num_alive_log_files;
|
||||
}
|
||||
job_context->size_log_to_delete += earliest.size;
|
||||
total_log_size_ -= earliest.size;
|
||||
alive_log_files_.pop_front();
|
||||
// Current log should always stay alive since it can't have
|
||||
// number < MinLogNumber().
|
||||
assert(alive_log_files_.size());
|
||||
}
|
||||
while (!logs_.empty() && logs_.front().number < min_log_number) {
|
||||
auto& log = logs_.front();
|
||||
if (log.getting_synced) {
|
||||
log_sync_cv_.Wait();
|
||||
// logs_ could have changed while we were waiting.
|
||||
continue;
|
||||
}
|
||||
logs_to_free_.push_back(log.ReleaseWriter());
|
||||
logs_.pop_front();
|
||||
}
|
||||
// Current log cannot be obsolete.
|
||||
assert(!logs_.empty());
|
||||
}
|
||||
|
||||
// We're just cleaning up for DB::Write().
|
||||
assert(job_context->logs_to_free.empty());
|
||||
job_context->logs_to_free = logs_to_free_;
|
||||
job_context->log_recycle_files.assign(log_recycle_files.begin(),
|
||||
log_recycle_files.end());
|
||||
logs_to_free_.clear();
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool CompareCandidateFile(const JobContext::CandidateFileInfo& first,
|
||||
const JobContext::CandidateFileInfo& second) {
|
||||
if (first.file_name > second.file_name) {
|
||||
return true;
|
||||
} else if (first.file_name < second.file_name) {
|
||||
return false;
|
||||
} else {
|
||||
return (first.path_id > second.path_id);
|
||||
}
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
// Delete obsolete files and log status and information of file deletion
|
||||
void DBImpl::DeleteObsoleteFileImpl(Status file_deletion_status, int job_id,
|
||||
const std::string& fname, FileType type,
|
||||
uint64_t number, uint32_t path_id) {
|
||||
if (type == kTableFile) {
|
||||
file_deletion_status =
|
||||
DeleteSSTFile(&immutable_db_options_, fname, path_id);
|
||||
} else {
|
||||
file_deletion_status = env_->DeleteFile(fname);
|
||||
}
|
||||
if (file_deletion_status.ok()) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[JOB %d] Delete %s type=%d #%" PRIu64 " -- %s\n", job_id,
|
||||
fname.c_str(), type, number,
|
||||
file_deletion_status.ToString().c_str());
|
||||
} else if (env_->FileExists(fname).IsNotFound()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"[JOB %d] Tried to delete a non-existing file %s type=%d #%" PRIu64
|
||||
" -- %s\n",
|
||||
job_id, fname.c_str(), type, number,
|
||||
file_deletion_status.ToString().c_str());
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"[JOB %d] Failed to delete %s type=%d #%" PRIu64 " -- %s\n",
|
||||
job_id, fname.c_str(), type, number,
|
||||
file_deletion_status.ToString().c_str());
|
||||
}
|
||||
if (type == kTableFile) {
|
||||
EventHelpers::LogAndNotifyTableFileDeletion(
|
||||
&event_logger_, job_id, number, fname, file_deletion_status, GetName(),
|
||||
immutable_db_options_.listeners);
|
||||
}
|
||||
}
|
||||
|
||||
// Diffs the files listed in filenames and those that do not
|
||||
// belong to live files are posibly removed. Also, removes all the
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
// we'd better have sth to delete
|
||||
assert(state.HaveSomethingToDelete());
|
||||
|
||||
// this checks if FindObsoleteFiles() was run before. If not, don't do
|
||||
// PurgeObsoleteFiles(). If FindObsoleteFiles() was run, we need to also
|
||||
// run PurgeObsoleteFiles(), even if disable_delete_obsolete_files_ is true
|
||||
if (state.manifest_file_number == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now, convert live list to an unordered map, WITHOUT mutex held;
|
||||
// set is slow.
|
||||
std::unordered_map<uint64_t, const FileDescriptor*> sst_live_map;
|
||||
for (const FileDescriptor& fd : state.sst_live) {
|
||||
sst_live_map[fd.GetNumber()] = &fd;
|
||||
}
|
||||
std::unordered_set<uint64_t> log_recycle_files_set(
|
||||
state.log_recycle_files.begin(), state.log_recycle_files.end());
|
||||
|
||||
auto candidate_files = state.full_scan_candidate_files;
|
||||
candidate_files.reserve(
|
||||
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->fd.GetNumber()),
|
||||
file->fd.GetPathId());
|
||||
delete file;
|
||||
}
|
||||
|
||||
for (auto file_num : state.log_delete_files) {
|
||||
if (file_num > 0) {
|
||||
candidate_files.emplace_back(LogFileName(kDumbDbName, file_num), 0);
|
||||
}
|
||||
}
|
||||
for (const auto& filename : state.manifest_delete_files) {
|
||||
candidate_files.emplace_back(filename, 0);
|
||||
}
|
||||
|
||||
// dedup state.candidate_files so we don't try to delete the same
|
||||
// file twice
|
||||
std::sort(candidate_files.begin(), candidate_files.end(),
|
||||
CompareCandidateFile);
|
||||
candidate_files.erase(
|
||||
std::unique(candidate_files.begin(), candidate_files.end()),
|
||||
candidate_files.end());
|
||||
|
||||
if (state.prev_total_log_size > 0) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Try to delete WAL files size %" PRIu64
|
||||
", prev total WAL file size %" PRIu64
|
||||
", number of live WAL files %" ROCKSDB_PRIszt ".\n",
|
||||
state.job_id, state.size_log_to_delete,
|
||||
state.prev_total_log_size, state.num_alive_log_files);
|
||||
}
|
||||
|
||||
std::vector<std::string> old_info_log_files;
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
for (const auto& candidate_file : candidate_files) {
|
||||
std::string to_delete = candidate_file.file_name;
|
||||
uint32_t path_id = candidate_file.path_id;
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
// Ignore file if we cannot recognize it.
|
||||
if (!ParseFileName(to_delete, &number, info_log_prefix.prefix, &type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool keep = true;
|
||||
switch (type) {
|
||||
case kLogFile:
|
||||
keep = ((number >= state.log_number) ||
|
||||
(number == state.prev_log_number) ||
|
||||
(log_recycle_files_set.find(number) !=
|
||||
log_recycle_files_set.end()));
|
||||
break;
|
||||
case kDescriptorFile:
|
||||
// Keep my manifest file, and any newer incarnations'
|
||||
// (can happen during manifest roll)
|
||||
keep = (number >= state.manifest_file_number);
|
||||
break;
|
||||
case kTableFile:
|
||||
// If the second condition is not there, this makes
|
||||
// DontDeletePendingOutputs fail
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
number >= state.min_pending_output;
|
||||
break;
|
||||
case kTempFile:
|
||||
// Any temp files that are currently being written to must
|
||||
// be recorded in pending_outputs_, which is inserted into "live".
|
||||
// Also, SetCurrentFile creates a temp file when writing out new
|
||||
// manifest, which is equal to state.pending_manifest_file_number. We
|
||||
// should not delete that file
|
||||
//
|
||||
// TODO(yhchiang): carefully modify the third condition to safely
|
||||
// remove the temp options files.
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
(number == state.pending_manifest_file_number) ||
|
||||
(to_delete.find(kOptionsFileNamePrefix) != std::string::npos);
|
||||
break;
|
||||
case kInfoLogFile:
|
||||
keep = true;
|
||||
if (number != 0) {
|
||||
old_info_log_files.push_back(to_delete);
|
||||
}
|
||||
break;
|
||||
case kCurrentFile:
|
||||
case kDBLockFile:
|
||||
case kIdentityFile:
|
||||
case kMetaDatabase:
|
||||
case kOptionsFile:
|
||||
case kBlobFile:
|
||||
keep = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (keep) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string fname;
|
||||
if (type == kTableFile) {
|
||||
// evict from cache
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
fname = TableFileName(immutable_db_options_.db_paths, number, path_id);
|
||||
} else {
|
||||
fname = ((type == kLogFile) ? immutable_db_options_.wal_dir : dbname_) +
|
||||
"/" + to_delete;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (type == kLogFile && (immutable_db_options_.wal_ttl_seconds > 0 ||
|
||||
immutable_db_options_.wal_size_limit_mb > 0)) {
|
||||
wal_manager_.ArchiveWALFile(fname, number);
|
||||
continue;
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
Status file_deletion_status;
|
||||
if (schedule_only) {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
SchedulePendingPurge(fname, type, number, path_id, state.job_id);
|
||||
} else {
|
||||
DeleteObsoleteFileImpl(file_deletion_status, state.job_id, fname, type,
|
||||
number, path_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old info log files.
|
||||
size_t old_info_log_file_count = old_info_log_files.size();
|
||||
if (old_info_log_file_count != 0 &&
|
||||
old_info_log_file_count >= immutable_db_options_.keep_log_file_num) {
|
||||
std::sort(old_info_log_files.begin(), old_info_log_files.end());
|
||||
size_t end =
|
||||
old_info_log_file_count - immutable_db_options_.keep_log_file_num;
|
||||
for (unsigned int i = 0; i <= end; i++) {
|
||||
std::string& to_delete = old_info_log_files.at(i);
|
||||
std::string full_path_to_delete =
|
||||
(immutable_db_options_.db_log_dir.empty()
|
||||
? dbname_
|
||||
: immutable_db_options_.db_log_dir) +
|
||||
"/" + to_delete;
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Delete info log file %s\n", state.job_id,
|
||||
full_path_to_delete.c_str());
|
||||
Status s = env_->DeleteFile(full_path_to_delete);
|
||||
if (!s.ok()) {
|
||||
if (env_->FileExists(full_path_to_delete).IsNotFound()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"[JOB %d] Tried to delete non-existing info log file %s FAILED "
|
||||
"-- %s\n",
|
||||
state.job_id, to_delete.c_str(), s.ToString().c_str());
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"[JOB %d] Delete info log file %s FAILED -- %s\n",
|
||||
state.job_id, to_delete.c_str(),
|
||||
s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
wal_manager_.PurgeObsoleteWALFiles();
|
||||
#endif // ROCKSDB_LITE
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
void DBImpl::DeleteObsoleteFiles() {
|
||||
mutex_.AssertHeld();
|
||||
JobContext job_context(next_job_id_.fetch_add(1));
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
|
||||
mutex_.Unlock();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
}
|
||||
} // namespace rocksdb
|
||||
+1084
File diff suppressed because it is too large.
Load diff
+12
-10
@@ -11,7 +11,7 @@
|
||||
#include "db/db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace rocksdb {
|
||||
DBImplReadOnly::DBImplReadOnly(const DBOptions& db_options,
|
||||
const std::string& dbname)
|
||||
: DBImpl(db_options, dbname) {
|
||||
Log(INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"Opening the db in read only mode");
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Opening the db in read only mode");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ DBImplReadOnly::~DBImplReadOnly() {
|
||||
// Implementations of the DB interface
|
||||
Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value) {
|
||||
PinnableSlice* pinnable_val) {
|
||||
assert(pinnable_val != nullptr);
|
||||
Status s;
|
||||
SequenceNumber snapshot = versions_->LastSequence();
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
@@ -40,12 +41,13 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
MergeContext merge_context;
|
||||
RangeDelAggregator range_del_agg(cfd->internal_comparator(), snapshot);
|
||||
LookupKey lkey(key, snapshot);
|
||||
if (super_version->mem->Get(lkey, value, &s, &merge_context, &range_del_agg,
|
||||
read_options)) {
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&range_del_agg, read_options)) {
|
||||
pinnable_val->PinSelf();
|
||||
} else {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(read_options, lkey, value, &s, &merge_context,
|
||||
&range_del_agg);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val, &s,
|
||||
&merge_context, &range_del_agg);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -57,7 +59,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
SuperVersion* super_version = cfd->GetSuperVersion()->Ref();
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, *cfd->ioptions(), cfd->user_comparator(),
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(),
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
@@ -86,7 +88,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
auto* sv = cfd->GetSuperVersion()->Ref();
|
||||
auto* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, *cfd->ioptions(), cfd->user_comparator(),
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(),
|
||||
(read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
|
||||
@@ -22,7 +22,7 @@ class DBImplReadOnly : public DBImpl {
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value) override;
|
||||
PinnableSlice* value) override;
|
||||
|
||||
// TODO: Implement ReadOnly MultiGet?
|
||||
|
||||
|
||||
@@ -0,0 +1,834 @@
|
||||
// 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/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
// Convenience methods
|
||||
Status DBImpl::Put(const WriteOptions& o, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& val) {
|
||||
return DB::Put(o, column_family, key, val);
|
||||
}
|
||||
|
||||
Status DBImpl::Merge(const WriteOptions& o, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& val) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
if (!cfh->cfd()->ioptions()->merge_operator) {
|
||||
return Status::NotSupported("Provide a merge_operator when opening DB");
|
||||
} else {
|
||||
return DB::Merge(o, column_family, key, val);
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::Delete(const WriteOptions& write_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key) {
|
||||
return DB::Delete(write_options, column_family, key);
|
||||
}
|
||||
|
||||
Status DBImpl::SingleDelete(const WriteOptions& write_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key) {
|
||||
return DB::SingleDelete(write_options, column_family, key);
|
||||
}
|
||||
|
||||
Status DBImpl::Write(const WriteOptions& write_options, WriteBatch* my_batch) {
|
||||
return WriteImpl(write_options, my_batch, nullptr, nullptr);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch,
|
||||
WriteCallback* callback) {
|
||||
return WriteImpl(write_options, my_batch, callback, nullptr);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable) {
|
||||
if (my_batch == nullptr) {
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
}
|
||||
|
||||
Status status;
|
||||
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable);
|
||||
|
||||
if (!write_options.disableWAL) {
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
}
|
||||
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
if (w.state == WriteThread::STATE_PARALLEL_FOLLOWER) {
|
||||
// we are a non-leader in a parallel group
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
if (w.ShouldWriteToMemtable()) {
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
WriteBatchInternal::SetSequence(w.batch, w.sequence);
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
true /*concurrent_memtable_writes*/);
|
||||
}
|
||||
|
||||
if (write_thread_.CompleteParallelWorker(&w)) {
|
||||
// we're responsible for early exit
|
||||
auto last_sequence = w.parallel_group->last_sequence;
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
write_thread_.EarlyExitParallelGroup(&w);
|
||||
}
|
||||
assert(w.state == WriteThread::STATE_COMPLETED);
|
||||
// STATE_COMPLETED conditional below handles exit
|
||||
|
||||
status = w.FinalStatus();
|
||||
}
|
||||
if (w.state == WriteThread::STATE_COMPLETED) {
|
||||
if (log_used != nullptr) {
|
||||
*log_used = w.log_used;
|
||||
}
|
||||
// write is complete and leader has updated sequence
|
||||
return w.FinalStatus();
|
||||
}
|
||||
// else we are the leader of the write batch group
|
||||
assert(w.state == WriteThread::STATE_GROUP_LEADER);
|
||||
|
||||
// Once reaches this point, the current writer "w" will try to do its write
|
||||
// job. It may also pick up some of the remaining writers in the "writers_"
|
||||
// when it finds suitable, and finish them in the same write batch.
|
||||
// This is how a write job could be done by the other writer.
|
||||
WriteContext write_context;
|
||||
WriteThread::Writer* last_writer = &w; // Dummy intial value
|
||||
autovector<WriteThread::Writer*> write_group;
|
||||
bool logs_getting_synced = false;
|
||||
|
||||
mutex_.Lock();
|
||||
|
||||
bool need_log_sync = !write_options.disableWAL && write_options.sync;
|
||||
bool need_log_dir_sync = need_log_sync && !log_dir_synced_;
|
||||
status = PreprocessWrite(write_options, need_log_sync, &logs_getting_synced,
|
||||
&write_context);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
// Add to log and apply to memtable. We can release the lock
|
||||
// during this phase since &w is currently responsible for logging
|
||||
// and protects against concurrent loggers and concurrent writes
|
||||
// into memtables
|
||||
|
||||
bool exit_completed_early = false;
|
||||
last_batch_group_size_ =
|
||||
write_thread_.EnterAsBatchGroupLeader(&w, &last_writer, &write_group);
|
||||
|
||||
if (status.ok()) {
|
||||
// Rules for when we can update the memtable concurrently
|
||||
// 1. supported by memtable
|
||||
// 2. Puts are not okay if inplace_update_support
|
||||
// 3. Merges are not okay
|
||||
//
|
||||
// Rules 1..2 are enforced by checking the options
|
||||
// during startup (CheckConcurrentWritesSupported), so if
|
||||
// options.allow_concurrent_memtable_write is true then they can be
|
||||
// assumed to be true. Rule 3 is checked for each batch. We could
|
||||
// relax rules 2 if we could prevent write batches from referring
|
||||
// more than once to a particular key.
|
||||
bool parallel = immutable_db_options_.allow_concurrent_memtable_write &&
|
||||
write_group.size() > 1;
|
||||
int total_count = 0;
|
||||
uint64_t total_byte_size = 0;
|
||||
for (auto writer : write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
if (writer->ShouldWriteToMemtable()) {
|
||||
total_count += WriteBatchInternal::Count(writer->batch);
|
||||
parallel = parallel && !writer->batch->HasMerge();
|
||||
}
|
||||
|
||||
if (writer->ShouldWriteToWAL()) {
|
||||
total_byte_size = WriteBatchInternal::AppendedByteSize(
|
||||
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t last_sequence = versions_->LastSequence();
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += total_count;
|
||||
|
||||
// Update stats while we are an exclusive group leader, so we know
|
||||
// that nobody else can be writing to these particular stats.
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
// commit. That lets us release our leader status early in
|
||||
// some cases.
|
||||
auto stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_DONE_BY_SELF, 1);
|
||||
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);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, write_done_by_other);
|
||||
}
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
|
||||
if (write_options.disableWAL) {
|
||||
has_unpersisted_data_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
status = WriteToWAL(write_group, cur_log_writer, need_log_sync,
|
||||
need_log_dir_sync, current_sequence);
|
||||
if (log_used != nullptr) {
|
||||
*log_used = logfile_number_;
|
||||
}
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
if (!parallel) {
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
write_group, current_sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*recovery_log_number*/, this);
|
||||
|
||||
if (status.ok()) {
|
||||
// There were no write failures. Set leader's status
|
||||
// in case the write callback returned a non-ok status.
|
||||
status = w.FinalStatus();
|
||||
}
|
||||
|
||||
} else {
|
||||
WriteThread::ParallelGroup pg;
|
||||
pg.leader = &w;
|
||||
pg.last_writer = last_writer;
|
||||
pg.last_sequence = last_sequence;
|
||||
pg.early_exit_allowed = !need_log_sync;
|
||||
pg.running.store(static_cast<uint32_t>(write_group.size()),
|
||||
std::memory_order_relaxed);
|
||||
write_thread_.LaunchParallelFollowers(&pg, current_sequence);
|
||||
|
||||
// Each parallel follower is doing each own writes. The leader should
|
||||
// also do its own.
|
||||
if (w.ShouldWriteToMemtable()) {
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
assert(w.sequence == current_sequence);
|
||||
WriteBatchInternal::SetSequence(w.batch, w.sequence);
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/,
|
||||
this, true /*concurrent_memtable_writes*/);
|
||||
}
|
||||
|
||||
// CompleteParallelWorker returns true if this thread should
|
||||
// handle exit, false means somebody else did
|
||||
exit_completed_early = !write_thread_.CompleteParallelWorker(&w);
|
||||
status = w.FinalStatus();
|
||||
}
|
||||
|
||||
if (!exit_completed_early && w.status.ok()) {
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
if (!need_log_sync) {
|
||||
write_thread_.ExitAsBatchGroupLeader(&w, last_writer, w.status);
|
||||
exit_completed_early = true;
|
||||
}
|
||||
}
|
||||
|
||||
// A non-OK status here indicates that the state implied by the
|
||||
// WAL has diverged from the in-memory state. This could be
|
||||
// because of a corrupt write_batch (very bad), or because the
|
||||
// client specified an invalid column family and didn't specify
|
||||
// ignore_missing_column_families.
|
||||
//
|
||||
// Is setting bg_error_ enough here? This will at least stop
|
||||
// compaction and fail any further writes.
|
||||
if (!status.ok() && !w.CallbackFailed()) {
|
||||
mutex_.Lock();
|
||||
if (bg_error_.ok()) {
|
||||
bg_error_ = status; // stop compaction & fail any further writes
|
||||
}
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (immutable_db_options_.paranoid_checks && !status.ok() &&
|
||||
!w.CallbackFailed() && !status.IsBusy() && !status.IsIncomplete()) {
|
||||
mutex_.Lock();
|
||||
if (bg_error_.ok()) {
|
||||
bg_error_ = status; // stop compaction & fail any further writes
|
||||
}
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
if (logs_getting_synced) {
|
||||
mutex_.Lock();
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, status);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
if (!exit_completed_early) {
|
||||
write_thread_.ExitAsBatchGroupLeader(&w, last_writer, w.status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
bool need_log_sync, bool* logs_getting_synced,
|
||||
WriteContext* write_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(write_context != nullptr && logs_getting_synced != nullptr);
|
||||
Status status;
|
||||
|
||||
assert(!single_column_family_mode_ ||
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1);
|
||||
if (UNLIKELY(status.ok() && !single_column_family_mode_ &&
|
||||
total_log_size_ > GetMaxTotalWalSize())) {
|
||||
status = HandleWALFull(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && write_buffer_manager_->ShouldFlush())) {
|
||||
// Before a new memtable is added in SwitchMemtable(),
|
||||
// write_buffer_manager_->ShouldFlush() will keep returning true. If another
|
||||
// thread is writing to another DB with the same write buffer, they may also
|
||||
// be flushed. We may end up with flushing much more DBs than needed. It's
|
||||
// suboptimal but still correct.
|
||||
status = HandleWriteBufferFull(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !bg_error_.ok())) {
|
||||
status = bg_error_;
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !flush_scheduler_.Empty())) {
|
||||
status = ScheduleFlushes(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && (write_controller_.IsStopped() ||
|
||||
write_controller_.NeedsDelay()))) {
|
||||
PERF_TIMER_GUARD(write_delay_time);
|
||||
// We don't know size of curent batch so that we always use the size
|
||||
// for previous one. It might create a fairness issue that expiration
|
||||
// might happen for smaller writes but larger writes can go through.
|
||||
// Can optimize it if it is an issue.
|
||||
status = DelayWrite(last_batch_group_size_, write_options);
|
||||
}
|
||||
|
||||
if (status.ok() && need_log_sync) {
|
||||
// Wait until the parallel syncs are finished. Any sync process has to sync
|
||||
// the front log too so it is enough to check the status of front()
|
||||
// We do a while loop since log_sync_cv_ is signalled when any sync is
|
||||
// finished
|
||||
// Note: there does not seem to be a reason to wait for parallel sync at
|
||||
// this early step but it is not important since parallel sync (SyncWAL) and
|
||||
// need_log_sync are usually not used together.
|
||||
while (logs_.front().getting_synced) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
for (auto& log : logs_) {
|
||||
assert(!log.getting_synced);
|
||||
// This is just to prevent the logs to be synced by a parallel SyncWAL
|
||||
// call. We will do the actual syncing later after we will write to the
|
||||
// WAL.
|
||||
// Note: there does not seem to be a reason to set this early before we
|
||||
// actually write to the WAL
|
||||
log.getting_synced = true;
|
||||
}
|
||||
*logs_getting_synced = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteToWAL(const autovector<WriteThread::Writer*>& write_group,
|
||||
log::Writer* log_writer, bool need_log_sync,
|
||||
bool need_log_dir_sync, SequenceNumber sequence) {
|
||||
Status status;
|
||||
|
||||
WriteBatch* merged_batch = nullptr;
|
||||
size_t write_with_wal = 0;
|
||||
if (write_group.size() == 1 && write_group[0]->ShouldWriteToWAL() &&
|
||||
write_group[0]->batch->GetWalTerminationPoint().is_cleared()) {
|
||||
// we simply write the first WriteBatch to WAL if the group only
|
||||
// contains one batch, that batch should be written to the WAL,
|
||||
// and the batch is not wanting to be truncated
|
||||
merged_batch = write_group[0]->batch;
|
||||
write_group[0]->log_used = logfile_number_;
|
||||
write_with_wal = 1;
|
||||
} else {
|
||||
// WAL needs all of the batches flattened into a single batch.
|
||||
// We could avoid copying here with an iov-like AddRecord
|
||||
// interface
|
||||
merged_batch = &tmp_batch_;
|
||||
for (auto writer : write_group) {
|
||||
if (writer->ShouldWriteToWAL()) {
|
||||
WriteBatchInternal::Append(merged_batch, writer->batch,
|
||||
/*WAL_only*/ true);
|
||||
write_with_wal++;
|
||||
}
|
||||
writer->log_used = logfile_number_;
|
||||
}
|
||||
}
|
||||
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
Slice log_entry = WriteBatchInternal::Contents(merged_batch);
|
||||
status = log_writer->AddRecord(log_entry);
|
||||
total_log_size_ += log_entry.size();
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
log_empty_ = false;
|
||||
uint64_t log_size = log_entry.size();
|
||||
|
||||
if (status.ok() && need_log_sync) {
|
||||
StopWatch sw(env_, stats_, WAL_FILE_SYNC_MICROS);
|
||||
// It's safe to access logs_ with unlocked mutex_ here because:
|
||||
// - we've set getting_synced=true for all logs,
|
||||
// so other threads won't pop from logs_ while we're here,
|
||||
// - only writer thread can push to logs_, and we're in
|
||||
// writer thread, so no one will push to logs_,
|
||||
// - as long as other threads don't modify it, it's safe to read
|
||||
// from std::deque from multiple threads concurrently.
|
||||
for (auto& log : logs_) {
|
||||
status = log.writer->file()->Sync(immutable_db_options_.use_fsync);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
// We only sync WAL directory the first time WAL syncing is
|
||||
// requested, so that in case users never turn on WAL sync,
|
||||
// we can avoid the disk I/O in the write code path.
|
||||
status = directories_.GetWalDir()->Fsync();
|
||||
}
|
||||
}
|
||||
|
||||
if (merged_batch == &tmp_batch_) {
|
||||
tmp_batch_.Clear();
|
||||
}
|
||||
if (status.ok()) {
|
||||
auto stats = default_cf_internal_stats_;
|
||||
if (need_log_sync) {
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_SYNCED, 1);
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
}
|
||||
stats->AddDBStats(InternalStats::WAL_FILE_BYTES, log_size);
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::WRITE_WITH_WAL, write_with_wal);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::HandleWALFull(WriteContext* write_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(write_context != nullptr);
|
||||
Status status;
|
||||
|
||||
if (alive_log_files_.begin()->getting_flushed) {
|
||||
return status;
|
||||
}
|
||||
|
||||
auto oldest_alive_log = alive_log_files_.begin()->number;
|
||||
auto oldest_log_with_uncommited_prep = FindMinLogContainingOutstandingPrep();
|
||||
|
||||
if (allow_2pc() &&
|
||||
oldest_log_with_uncommited_prep > 0 &&
|
||||
oldest_log_with_uncommited_prep <= oldest_alive_log) {
|
||||
if (unable_to_flush_oldest_log_) {
|
||||
// we already attempted to flush all column families dependent on
|
||||
// the oldest alive log but the log still contained uncommited transactions.
|
||||
// the oldest alive log STILL contains uncommited transaction so there
|
||||
// is still nothing that we can do.
|
||||
return status;
|
||||
} else {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Unable to release oldest log due to uncommited transaction");
|
||||
unable_to_flush_oldest_log_ = true;
|
||||
}
|
||||
} else {
|
||||
// we only mark this log as getting flushed if we have successfully
|
||||
// flushed all data in this log. If this log contains outstanding prepared
|
||||
// transactions then we cannot flush this log until those transactions are commited.
|
||||
unable_to_flush_oldest_log_ = false;
|
||||
alive_log_files_.begin()->getting_flushed = true;
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Flushing all column families with data in WAL number %" PRIu64
|
||||
". Total log size is %" PRIu64
|
||||
" while max_total_wal_size is %" PRIu64,
|
||||
oldest_alive_log, total_log_size_.load(), GetMaxTotalWalSize());
|
||||
// no need to refcount because drop is happening in write thread, so can't
|
||||
// happen while we're in the write thread
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (cfd->OldestLogToKeep() <= oldest_alive_log) {
|
||||
status = SwitchMemtable(cfd, write_context);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
cfd->imm()->FlushRequested();
|
||||
SchedulePendingFlush(cfd);
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(write_context != nullptr);
|
||||
Status status;
|
||||
|
||||
// Before a new memtable is added in SwitchMemtable(),
|
||||
// write_buffer_manager_->ShouldFlush() will keep returning true. If another
|
||||
// thread is writing to another DB with the same write buffer, they may also
|
||||
// be flushed. We may end up with flushing much more DBs than needed. It's
|
||||
// suboptimal but still correct.
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Flushing column family with largest mem table size. Write buffer is "
|
||||
"using %" PRIu64 " bytes out of a total of %" PRIu64 ".",
|
||||
write_buffer_manager_->memory_usage(),
|
||||
write_buffer_manager_->buffer_size());
|
||||
// no need to refcount because drop is happening in write thread, so can't
|
||||
// happen while we're in the write thread
|
||||
ColumnFamilyData* cfd_picked = nullptr;
|
||||
SequenceNumber seq_num_for_cf_picked = kMaxSequenceNumber;
|
||||
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (!cfd->mem()->IsEmpty()) {
|
||||
// We only consider active mem table, hoping immutable memtable is
|
||||
// already in the process of flushing.
|
||||
uint64_t seq = cfd->mem()->GetCreationSeq();
|
||||
if (cfd_picked == nullptr || seq < seq_num_for_cf_picked) {
|
||||
cfd_picked = cfd;
|
||||
seq_num_for_cf_picked = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cfd_picked != nullptr) {
|
||||
status = SwitchMemtable(cfd_picked, write_context);
|
||||
if (status.ok()) {
|
||||
cfd_picked->imm()->FlushRequested();
|
||||
SchedulePendingFlush(cfd_picked);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
uint64_t DBImpl::GetMaxTotalWalSize() const {
|
||||
mutex_.AssertHeld();
|
||||
return mutable_db_options_.max_total_wal_size == 0
|
||||
? 4 * max_total_in_memory_state_
|
||||
: mutable_db_options_.max_total_wal_size;
|
||||
}
|
||||
|
||||
// REQUIRES: mutex_ is held
|
||||
// REQUIRES: this thread is currently at the front of the writer queue
|
||||
Status DBImpl::DelayWrite(uint64_t num_bytes,
|
||||
const WriteOptions& write_options) {
|
||||
uint64_t time_delayed = 0;
|
||||
bool delayed = false;
|
||||
{
|
||||
StopWatch sw(env_, stats_, WRITE_STALL, &time_delayed);
|
||||
uint64_t delay = write_controller_.GetDelay(env_, num_bytes);
|
||||
if (delay > 0) {
|
||||
if (write_options.no_slowdown) {
|
||||
return Status::Incomplete();
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Sleep");
|
||||
|
||||
mutex_.Unlock();
|
||||
// We will delay the write until we have slept for delay ms or
|
||||
// we don't need a delay anymore
|
||||
const uint64_t kDelayInterval = 1000;
|
||||
uint64_t stall_end = sw.start_time() + delay;
|
||||
while (write_controller_.NeedsDelay()) {
|
||||
if (env_->NowMicros() >= stall_end) {
|
||||
// We already delayed this write `delay` microseconds
|
||||
break;
|
||||
}
|
||||
|
||||
delayed = true;
|
||||
// Sleep for 0.001 seconds
|
||||
env_->SleepForMicroseconds(kDelayInterval);
|
||||
}
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
while (bg_error_.ok() && write_controller_.IsStopped()) {
|
||||
if (write_options.no_slowdown) {
|
||||
return Status::Incomplete();
|
||||
}
|
||||
delayed = true;
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Wait");
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
assert(!delayed || !write_options.no_slowdown);
|
||||
if (delayed) {
|
||||
default_cf_internal_stats_->AddDBStats(InternalStats::WRITE_STALL_MICROS,
|
||||
time_delayed);
|
||||
RecordTick(stats_, STALL_MICROS, time_delayed);
|
||||
}
|
||||
|
||||
return bg_error_;
|
||||
}
|
||||
|
||||
Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
ColumnFamilyData* cfd;
|
||||
while ((cfd = flush_scheduler_.TakeNextColumnFamily()) != nullptr) {
|
||||
auto status = SwitchMemtable(cfd, context);
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* cfd,
|
||||
const MemTableInfo& mem_table_info) {
|
||||
if (immutable_db_options_.listeners.size() == 0U) {
|
||||
return;
|
||||
}
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
listener->OnMemTableSealed(mem_table_info);
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// REQUIRES: mutex_ is held
|
||||
// REQUIRES: this thread is currently at the front of the writer queue
|
||||
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
mutex_.AssertHeld();
|
||||
unique_ptr<WritableFile> lfile;
|
||||
log::Writer* new_log = nullptr;
|
||||
MemTable* new_mem = nullptr;
|
||||
|
||||
// Attempt to switch to a new memtable and trigger flush of old.
|
||||
// Do this without holding the dbmutex lock.
|
||||
assert(versions_->prev_log_number() == 0);
|
||||
bool creating_new_log = !log_empty_;
|
||||
uint64_t recycle_log_number = 0;
|
||||
if (creating_new_log && immutable_db_options_.recycle_log_file_num &&
|
||||
!log_recycle_files.empty()) {
|
||||
recycle_log_number = log_recycle_files.front();
|
||||
log_recycle_files.pop_front();
|
||||
}
|
||||
uint64_t new_log_number =
|
||||
creating_new_log ? versions_->NewFileNumber() : logfile_number_;
|
||||
SuperVersion* new_superversion = nullptr;
|
||||
const MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
|
||||
// Set current_memtble_info for memtable sealed callback
|
||||
#ifndef ROCKSDB_LITE
|
||||
MemTableInfo memtable_info;
|
||||
memtable_info.cf_name = cfd->GetName();
|
||||
memtable_info.first_seqno = cfd->mem()->GetFirstSequenceNumber();
|
||||
memtable_info.earliest_seqno = cfd->mem()->GetEarliestSequenceNumber();
|
||||
memtable_info.num_entries = cfd->mem()->num_entries();
|
||||
memtable_info.num_deletes = cfd->mem()->num_deletes();
|
||||
#endif // ROCKSDB_LITE
|
||||
// Log this later after lock release. It may be outdated, e.g., if background
|
||||
// flush happens before logging, but that should be ok.
|
||||
int num_imm_unflushed = cfd->imm()->NumNotFlushed();
|
||||
DBOptions db_options =
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_);
|
||||
const auto preallocate_block_size =
|
||||
GetWalPreallocateBlockSize(mutable_cf_options.write_buffer_size);
|
||||
mutex_.Unlock();
|
||||
Status s;
|
||||
{
|
||||
if (creating_new_log) {
|
||||
EnvOptions opt_env_opt =
|
||||
env_->OptimizeForLogWrite(env_options_, db_options);
|
||||
if (recycle_log_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"reusing log %" PRIu64 " from recycle list\n",
|
||||
recycle_log_number);
|
||||
s = env_->ReuseWritableFile(
|
||||
LogFileName(immutable_db_options_.wal_dir, new_log_number),
|
||||
LogFileName(immutable_db_options_.wal_dir, recycle_log_number),
|
||||
&lfile, opt_env_opt);
|
||||
} else {
|
||||
s = NewWritableFile(
|
||||
env_, LogFileName(immutable_db_options_.wal_dir, new_log_number),
|
||||
&lfile, opt_env_opt);
|
||||
}
|
||||
if (s.ok()) {
|
||||
// Our final size should be less than write_buffer_size
|
||||
// (compression, etc) but err on the side of caution.
|
||||
|
||||
// use preallocate_block_size instead
|
||||
// of calling GetWalPreallocateBlockSize()
|
||||
lfile->SetPreallocationBlockSize(preallocate_block_size);
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), opt_env_opt));
|
||||
new_log =
|
||||
new log::Writer(std::move(file_writer), new_log_number,
|
||||
immutable_db_options_.recycle_log_file_num > 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
SequenceNumber seq = versions_->LastSequence();
|
||||
new_mem = cfd->ConstructNewMemtable(mutable_cf_options, seq);
|
||||
new_superversion = new SuperVersion();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// PLEASE NOTE: We assume that there are no failable operations
|
||||
// after lock is acquired below since we are already notifying
|
||||
// client about mem table becoming immutable.
|
||||
NotifyOnMemTableSealed(cfd, memtable_info);
|
||||
#endif //ROCKSDB_LITE
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] New memtable created with log file: #%" PRIu64
|
||||
". Immutable memtables: %d.\n",
|
||||
cfd->GetName().c_str(), new_log_number, num_imm_unflushed);
|
||||
mutex_.Lock();
|
||||
if (!s.ok()) {
|
||||
// how do we fail if we're not creating new log?
|
||||
assert(creating_new_log);
|
||||
assert(!new_mem);
|
||||
assert(!new_log);
|
||||
return s;
|
||||
}
|
||||
if (creating_new_log) {
|
||||
logfile_number_ = new_log_number;
|
||||
assert(new_log != nullptr);
|
||||
log_empty_ = true;
|
||||
log_dir_synced_ = false;
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
}
|
||||
for (auto loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
// all this is just optimization to delete logs that
|
||||
// are no longer needed -- if CF is empty, that means it
|
||||
// doesn't need that particular log to stay alive, so we just
|
||||
// advance the log number. no need to persist this in the manifest
|
||||
if (loop_cfd->mem()->GetFirstSequenceNumber() == 0 &&
|
||||
loop_cfd->imm()->NumNotFlushed() == 0) {
|
||||
if (creating_new_log) {
|
||||
loop_cfd->SetLogNumber(logfile_number_);
|
||||
}
|
||||
loop_cfd->mem()->SetCreationSeq(versions_->LastSequence());
|
||||
}
|
||||
}
|
||||
|
||||
cfd->mem()->SetNextLogNumber(logfile_number_);
|
||||
cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_);
|
||||
new_mem->Ref();
|
||||
cfd->SetMemtable(new_mem);
|
||||
context->superversions_to_free_.push_back(InstallSuperVersionAndScheduleWork(
|
||||
cfd, new_superversion, mutable_cf_options));
|
||||
return s;
|
||||
}
|
||||
|
||||
size_t DBImpl::GetWalPreallocateBlockSize(uint64_t write_buffer_size) const {
|
||||
mutex_.AssertHeld();
|
||||
size_t bsize = write_buffer_size / 10 + write_buffer_size;
|
||||
// Some users might set very high write_buffer_size and rely on
|
||||
// max_total_wal_size or other parameters to control the WAL size.
|
||||
if (mutable_db_options_.max_total_wal_size > 0) {
|
||||
bsize = std::min<size_t>(bsize, mutable_db_options_.max_total_wal_size);
|
||||
}
|
||||
if (immutable_db_options_.db_write_buffer_size > 0) {
|
||||
bsize = std::min<size_t>(bsize, immutable_db_options_.db_write_buffer_size);
|
||||
}
|
||||
if (immutable_db_options_.write_buffer_manager &&
|
||||
immutable_db_options_.write_buffer_manager->enabled()) {
|
||||
bsize = std::min<size_t>(
|
||||
bsize, immutable_db_options_.write_buffer_manager->buffer_size());
|
||||
}
|
||||
|
||||
return bsize;
|
||||
}
|
||||
|
||||
// Default implementations of convenience methods that subclasses of DB
|
||||
// can call if they wish
|
||||
Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& value) {
|
||||
// Pre-allocate size of write batch conservatively.
|
||||
// 8 bytes are taken by header, 4 bytes for count, 1 byte for type,
|
||||
// and we allocate 11 extra bytes for key length, as well as value length.
|
||||
WriteBatch batch(key.size() + value.size() + 24);
|
||||
batch.Put(column_family, key, value);
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
Status DB::Delete(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key) {
|
||||
WriteBatch batch;
|
||||
batch.Delete(column_family, key);
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
Status DB::SingleDelete(const WriteOptions& opt,
|
||||
ColumnFamilyHandle* column_family, const Slice& key) {
|
||||
WriteBatch batch;
|
||||
batch.SingleDelete(column_family, key);
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
Status DB::DeleteRange(const WriteOptions& opt,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key, const Slice& end_key) {
|
||||
WriteBatch batch;
|
||||
batch.DeleteRange(column_family, begin_key, end_key);
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
Status DB::Merge(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, const Slice& value) {
|
||||
WriteBatch batch;
|
||||
batch.Merge(column_family, key, value);
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
@@ -15,8 +15,8 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "db/filename.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/filename.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/db_options.h"
|
||||
#include "options/db_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
|
||||
+150
-104
@@ -14,10 +14,10 @@
|
||||
#include <limits>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -25,9 +25,9 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -101,33 +101,32 @@ class DBIter: public Iterator {
|
||||
uint64_t bytes_read_;
|
||||
};
|
||||
|
||||
DBIter(Env* env, const ImmutableCFOptions& ioptions, const Comparator* cmp,
|
||||
DBIter(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false)
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number)
|
||||
: arena_mode_(arena_mode),
|
||||
env_(env),
|
||||
logger_(ioptions.info_log),
|
||||
logger_(cf_options.info_log),
|
||||
user_comparator_(cmp),
|
||||
merge_operator_(ioptions.merge_operator),
|
||||
merge_operator_(cf_options.merge_operator),
|
||||
iter_(iter),
|
||||
sequence_(s),
|
||||
direction_(kForward),
|
||||
valid_(false),
|
||||
current_entry_is_merged_(false),
|
||||
statistics_(ioptions.statistics),
|
||||
statistics_(cf_options.statistics),
|
||||
version_number_(version_number),
|
||||
iterate_upper_bound_(iterate_upper_bound),
|
||||
prefix_same_as_start_(prefix_same_as_start),
|
||||
pin_thru_lifetime_(pin_data),
|
||||
total_order_seek_(total_order_seek),
|
||||
range_del_agg_(ioptions.internal_comparator, s,
|
||||
iterate_upper_bound_(read_options.iterate_upper_bound),
|
||||
prefix_same_as_start_(read_options.prefix_same_as_start),
|
||||
pin_thru_lifetime_(read_options.pin_data),
|
||||
total_order_seek_(read_options.total_order_seek),
|
||||
range_del_agg_(cf_options.internal_comparator, s,
|
||||
true /* collapse_deletions */) {
|
||||
RecordTick(statistics_, NO_ITERATORS);
|
||||
prefix_extractor_ = ioptions.prefix_extractor;
|
||||
prefix_extractor_ = cf_options.prefix_extractor;
|
||||
max_skip_ = max_sequential_skip_in_iterations;
|
||||
max_skippable_internal_keys_ = read_options.max_skippable_internal_keys;
|
||||
if (pin_thru_lifetime_) {
|
||||
pinned_iters_mgr_.StartPinning();
|
||||
}
|
||||
@@ -160,7 +159,7 @@ class DBIter: public Iterator {
|
||||
virtual bool Valid() const override { return valid_; }
|
||||
virtual Slice key() const override {
|
||||
assert(valid_);
|
||||
return saved_key_.GetKey();
|
||||
return saved_key_.GetUserKey();
|
||||
}
|
||||
virtual Slice value() const override {
|
||||
assert(valid_);
|
||||
@@ -224,6 +223,7 @@ class DBIter: public Iterator {
|
||||
void FindNextUserEntryInternal(bool skipping, bool prefix_check);
|
||||
bool ParseKey(ParsedInternalKey* key);
|
||||
void MergeValuesNewToOld();
|
||||
bool TooManyInternalKeysSkipped(bool increment = true);
|
||||
|
||||
// Temporarily pin the blocks that we encounter until ReleaseTempPinnedData()
|
||||
// is called
|
||||
@@ -249,6 +249,10 @@ class DBIter: public Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
inline void ResetInternalKeysSkippedCounter() {
|
||||
num_internal_keys_skipped_ = 0;
|
||||
}
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
bool arena_mode_;
|
||||
Env* const env_;
|
||||
@@ -268,6 +272,8 @@ class DBIter: public Iterator {
|
||||
// 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_;
|
||||
uint64_t version_number_;
|
||||
const Slice* iterate_upper_bound_;
|
||||
IterKey prefix_start_buf_;
|
||||
@@ -291,9 +297,8 @@ class DBIter: public Iterator {
|
||||
inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
if (!ParseInternalKey(iter_->key(), ikey)) {
|
||||
status_ = Status::Corruption("corrupted internal key in DBIter");
|
||||
Log(InfoLogLevel::ERROR_LEVEL,
|
||||
logger_, "corrupted internal key in DBIter: %s",
|
||||
iter_->key().ToString(true).c_str());
|
||||
ROCKS_LOG_ERROR(logger_, "corrupted internal key in DBIter: %s",
|
||||
iter_->key().ToString(true).c_str());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@@ -305,6 +310,7 @@ void DBIter::Next() {
|
||||
|
||||
// Release temporarily pinned blocks from last operation
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
if (direction_ == kReverse) {
|
||||
ReverseToForward();
|
||||
} else if (iter_->Valid() && !current_entry_is_merged_) {
|
||||
@@ -391,9 +397,14 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (TooManyInternalKeysSkipped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ikey.sequence <= sequence_) {
|
||||
if (skipping &&
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) <= 0) {
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetUserKey()) <=
|
||||
0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
} else {
|
||||
@@ -403,14 +414,14 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
case kTypeSingleDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
saved_key_.SetKey(
|
||||
saved_key_.SetUserKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
skipping = true;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
break;
|
||||
case kTypeValue:
|
||||
saved_key_.SetKey(
|
||||
saved_key_.SetUserKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
@@ -427,7 +438,7 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
}
|
||||
break;
|
||||
case kTypeMerge:
|
||||
saved_key_.SetKey(
|
||||
saved_key_.SetUserKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
@@ -458,12 +469,13 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
|
||||
// Here saved_key_ may contain some old key, or the default empty key, or
|
||||
// key assigned by some random other method. We don't care.
|
||||
if (user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) <= 0) {
|
||||
if (user_comparator_->Compare(ikey.user_key, saved_key_.GetUserKey()) <=
|
||||
0) {
|
||||
num_skipped++;
|
||||
} else {
|
||||
saved_key_.SetKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
saved_key_.SetUserKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
skipping = false;
|
||||
num_skipped = 0;
|
||||
}
|
||||
@@ -478,8 +490,8 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
// We're looking for the next user-key but all we see are the same
|
||||
// user-key with decreasing sequence numbers. Fast forward to
|
||||
// sequence number 0 and type deletion (the smallest type).
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetKey(), 0,
|
||||
kTypeDeletion));
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetUserKey(),
|
||||
0, kTypeDeletion));
|
||||
// Don't set skipping = false because we may still see more user-keys
|
||||
// equal to saved_key_.
|
||||
} else {
|
||||
@@ -489,7 +501,7 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
// many times since our snapshot was taken, not the case when a lot of
|
||||
// different keys were inserted after our snapshot was taken.
|
||||
AppendInternalKey(&last_key,
|
||||
ParsedInternalKey(saved_key_.GetKey(), sequence_,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
}
|
||||
iter_->Seek(last_key);
|
||||
@@ -509,8 +521,7 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
// iter_ points to the next entry (or invalid)
|
||||
void DBIter::MergeValuesNewToOld() {
|
||||
if (!merge_operator_) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL,
|
||||
logger_, "Options::merge_operator is null.");
|
||||
ROCKS_LOG_ERROR(logger_, "Options::merge_operator is null.");
|
||||
status_ = Status::InvalidArgument("merge_operator_ must be set.");
|
||||
valid_ = false;
|
||||
return;
|
||||
@@ -531,7 +542,7 @@ void DBIter::MergeValuesNewToOld() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
if (!user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
// hit the next user key, stop right here
|
||||
break;
|
||||
} else if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type ||
|
||||
@@ -571,9 +582,10 @@ void DBIter::MergeValuesNewToOld() {
|
||||
// a deletion marker.
|
||||
// feed null as the existing value to the merge operator, such that
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetUserKey(),
|
||||
nullptr, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
}
|
||||
@@ -582,6 +594,7 @@ void DBIter::MergeValuesNewToOld() {
|
||||
void DBIter::Prev() {
|
||||
assert(valid_);
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
if (direction_ == kForward) {
|
||||
ReverseToBackward();
|
||||
}
|
||||
@@ -599,8 +612,8 @@ void DBIter::ReverseToForward() {
|
||||
if (prefix_extractor_ != nullptr && !total_order_seek_) {
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(ParsedInternalKey(
|
||||
saved_key_.GetKey(), kMaxSequenceNumber, kValueTypeForSeek));
|
||||
iter_->Seek(last_key.GetKey());
|
||||
saved_key_.GetUserKey(), kMaxSequenceNumber, kValueTypeForSeek));
|
||||
iter_->Seek(last_key.GetInternalKey());
|
||||
}
|
||||
FindNextUserKey();
|
||||
direction_ = kForward;
|
||||
@@ -613,9 +626,9 @@ void DBIter::ReverseToForward() {
|
||||
void DBIter::ReverseToBackward() {
|
||||
if (prefix_extractor_ != nullptr && !total_order_seek_) {
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(
|
||||
ParsedInternalKey(saved_key_.GetKey(), 0, kValueTypeForSeekForPrev));
|
||||
iter_->SeekForPrev(last_key.GetKey());
|
||||
last_key.SetInternalKey(ParsedInternalKey(saved_key_.GetUserKey(), 0,
|
||||
kValueTypeForSeekForPrev));
|
||||
iter_->SeekForPrev(last_key.GetInternalKey());
|
||||
}
|
||||
if (current_entry_is_merged_) {
|
||||
// Not placed in the same key. Need to call Prev() until finding the
|
||||
@@ -627,7 +640,8 @@ void DBIter::ReverseToBackward() {
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
while (iter_->Valid() &&
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) > 0) {
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetUserKey()) >
|
||||
0) {
|
||||
if (ikey.sequence > sequence_) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
} else {
|
||||
@@ -641,7 +655,8 @@ void DBIter::ReverseToBackward() {
|
||||
if (iter_->Valid()) {
|
||||
ParsedInternalKey ikey;
|
||||
assert(ParseKey(&ikey));
|
||||
assert(user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) <= 0);
|
||||
assert(user_comparator_->Compare(ikey.user_key, saved_key_.GetUserKey()) <=
|
||||
0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -658,29 +673,36 @@ void DBIter::PrevInternal() {
|
||||
ParsedInternalKey ikey;
|
||||
|
||||
while (iter_->Valid()) {
|
||||
saved_key_.SetKey(ExtractUserKey(iter_->key()),
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
saved_key_.SetUserKey(
|
||||
ExtractUserKey(iter_->key()),
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
|
||||
if (FindValueForCurrentKey()) {
|
||||
valid_ = true;
|
||||
if (!iter_->Valid()) {
|
||||
return;
|
||||
}
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
if (user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
if (user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
FindPrevUserKey();
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_ &&
|
||||
prefix_extractor_->Transform(saved_key_.GetKey())
|
||||
prefix_extractor_->Transform(saved_key_.GetUserKey())
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
valid_ = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (TooManyInternalKeysSkipped(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iter_->Valid()) {
|
||||
break;
|
||||
}
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
if (user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
if (user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
FindPrevUserKey();
|
||||
}
|
||||
}
|
||||
@@ -710,7 +732,11 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
TempPinData();
|
||||
size_t num_skipped = 0;
|
||||
while (iter_->Valid() && ikey.sequence <= sequence_ &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
if (TooManyInternalKeysSkipped()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We iterate too much: let's use Seek() to avoid too much key comparisons
|
||||
if (num_skipped >= max_skip_) {
|
||||
return FindValueForCurrentKeyUsingSeek();
|
||||
@@ -757,7 +783,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
}
|
||||
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
assert(user_comparator_->Equal(ikey.user_key, saved_key_.GetKey()));
|
||||
assert(user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()));
|
||||
iter_->Prev();
|
||||
++num_skipped;
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
@@ -775,14 +801,14 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
if (last_not_merge_type == kTypeDeletion ||
|
||||
last_not_merge_type == kTypeSingleDeletion ||
|
||||
last_not_merge_type == kTypeRangeDeletion) {
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(),
|
||||
nullptr, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_);
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_);
|
||||
} else {
|
||||
assert(last_not_merge_type == kTypeValue);
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetKey(), &pinned_value_,
|
||||
merge_operator_, saved_key_.GetUserKey(), &pinned_value_,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_);
|
||||
}
|
||||
@@ -808,8 +834,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
// FindValueForCurrentKeyUsingSeek()
|
||||
assert(pinned_iters_mgr_.PinningEnabled());
|
||||
std::string last_key;
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetUserKey(),
|
||||
sequence_, kValueTypeForSeek));
|
||||
iter_->Seek(last_key);
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
|
||||
@@ -836,7 +862,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
merge_context_.Clear();
|
||||
while (
|
||||
iter_->Valid() &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey()) &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()) &&
|
||||
ikey.type == kTypeMerge &&
|
||||
!range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::kBackwardTraversal)) {
|
||||
@@ -849,17 +875,17 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
|
||||
Status s;
|
||||
if (!iter_->Valid() ||
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey()) ||
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()) ||
|
||||
ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::kBackwardTraversal)) {
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(),
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetUserKey(),
|
||||
nullptr, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_);
|
||||
// Make iter_ valid and point to saved_key_
|
||||
if (!iter_->Valid() ||
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
iter_->Seek(last_key);
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
}
|
||||
@@ -871,9 +897,10 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
}
|
||||
|
||||
const Slice& val = iter_->value();
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), &val,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetUserKey(),
|
||||
&val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_);
|
||||
valid_ = true;
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
@@ -892,7 +919,7 @@ void DBIter::FindNextUserKey() {
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kForward);
|
||||
while (iter_->Valid() &&
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
iter_->Next();
|
||||
FindParseableKey(&ikey, kForward);
|
||||
}
|
||||
@@ -907,16 +934,21 @@ void DBIter::FindPrevUserKey() {
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
int cmp;
|
||||
while (iter_->Valid() && ((cmp = user_comparator_->Compare(
|
||||
ikey.user_key, saved_key_.GetKey())) == 0 ||
|
||||
(cmp > 0 && ikey.sequence > sequence_))) {
|
||||
while (iter_->Valid() &&
|
||||
((cmp = user_comparator_->Compare(ikey.user_key,
|
||||
saved_key_.GetUserKey())) == 0 ||
|
||||
(cmp > 0 && ikey.sequence > sequence_))) {
|
||||
if (TooManyInternalKeysSkipped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmp == 0) {
|
||||
if (num_skipped >= max_skip_) {
|
||||
num_skipped = 0;
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(ParsedInternalKey(
|
||||
saved_key_.GetKey(), kMaxSequenceNumber, kValueTypeForSeek));
|
||||
iter_->Seek(last_key.GetKey());
|
||||
saved_key_.GetUserKey(), kMaxSequenceNumber, kValueTypeForSeek));
|
||||
iter_->Seek(last_key.GetInternalKey());
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
} else {
|
||||
++num_skipped;
|
||||
@@ -932,6 +964,18 @@ void DBIter::FindPrevUserKey() {
|
||||
}
|
||||
}
|
||||
|
||||
bool DBIter::TooManyInternalKeysSkipped(bool increment) {
|
||||
if ((max_skippable_internal_keys_ > 0) &&
|
||||
(num_internal_keys_skipped_ > max_skippable_internal_keys_)) {
|
||||
valid_ = false;
|
||||
status_ = Status::Incomplete("Too many internal keys skipped.");
|
||||
return true;
|
||||
} else if (increment) {
|
||||
num_internal_keys_skipped_++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip all unparseable keys
|
||||
void DBIter::FindParseableKey(ParsedInternalKey* ikey, Direction direction) {
|
||||
while (iter_->Valid() && !ParseKey(ikey)) {
|
||||
@@ -946,12 +990,13 @@ void DBIter::FindParseableKey(ParsedInternalKey* ikey, Direction direction) {
|
||||
void DBIter::Seek(const Slice& target) {
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
saved_key_.Clear();
|
||||
saved_key_.SetInternalKey(target, sequence_);
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->Seek(saved_key_.GetKey());
|
||||
iter_->Seek(saved_key_.GetInternalKey());
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
}
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
@@ -961,9 +1006,6 @@ void DBIter::Seek(const Slice& target) {
|
||||
}
|
||||
direction_ = kForward;
|
||||
ClearSavedValue();
|
||||
// convert the InternalKey to UserKey in saved_key_ before
|
||||
// passed to FindNextUserEntry
|
||||
saved_key_.Reserve(saved_key_.Size() - 8);
|
||||
FindNextUserEntry(false /* not skipping */, prefix_same_as_start_);
|
||||
if (!valid_) {
|
||||
prefix_start_key_.clear();
|
||||
@@ -979,14 +1021,15 @@ void DBIter::Seek(const Slice& target) {
|
||||
}
|
||||
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_buf_.SetKey(prefix_start_key_);
|
||||
prefix_start_key_ = prefix_start_buf_.GetKey();
|
||||
prefix_start_buf_.SetUserKey(prefix_start_key_);
|
||||
prefix_start_key_ = prefix_start_buf_.GetUserKey();
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::SeekForPrev(const Slice& target) {
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
saved_key_.Clear();
|
||||
// now saved_key is used to store internal key.
|
||||
saved_key_.SetInternalKey(target, 0 /* sequence_number */,
|
||||
@@ -994,7 +1037,7 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->SeekForPrev(saved_key_.GetKey());
|
||||
iter_->SeekForPrev(saved_key_.GetInternalKey());
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
}
|
||||
|
||||
@@ -1019,8 +1062,8 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
valid_ = false;
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_buf_.SetKey(prefix_start_key_);
|
||||
prefix_start_key_ = prefix_start_buf_.GetKey();
|
||||
prefix_start_buf_.SetUserKey(prefix_start_key_);
|
||||
prefix_start_key_ = prefix_start_buf_.GetUserKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1032,6 +1075,7 @@ void DBIter::SeekToFirst() {
|
||||
}
|
||||
direction_ = kForward;
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
ClearSavedValue();
|
||||
|
||||
{
|
||||
@@ -1042,8 +1086,9 @@ void DBIter::SeekToFirst() {
|
||||
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (iter_->Valid()) {
|
||||
saved_key_.SetKey(ExtractUserKey(iter_->key()),
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
saved_key_.SetUserKey(
|
||||
ExtractUserKey(iter_->key()),
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
FindNextUserEntry(false /* not skipping */, false /* no prefix check */);
|
||||
if (statistics_ != nullptr) {
|
||||
if (valid_) {
|
||||
@@ -1055,8 +1100,9 @@ void DBIter::SeekToFirst() {
|
||||
valid_ = false;
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_buf_.SetKey(prefix_extractor_->Transform(saved_key_.GetKey()));
|
||||
prefix_start_key_ = prefix_start_buf_.GetKey();
|
||||
prefix_start_buf_.SetUserKey(
|
||||
prefix_extractor_->Transform(saved_key_.GetUserKey()));
|
||||
prefix_start_key_ = prefix_start_buf_.GetUserKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1068,6 +1114,7 @@ void DBIter::SeekToLast() {
|
||||
}
|
||||
direction_ = kReverse;
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
ClearSavedValue();
|
||||
|
||||
{
|
||||
@@ -1097,21 +1144,22 @@ void DBIter::SeekToLast() {
|
||||
}
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_buf_.SetKey(prefix_extractor_->Transform(saved_key_.GetKey()));
|
||||
prefix_start_key_ = prefix_start_buf_.GetKey();
|
||||
prefix_start_buf_.SetUserKey(
|
||||
prefix_extractor_->Transform(saved_key_.GetUserKey()));
|
||||
prefix_start_key_ = prefix_start_buf_.GetUserKey();
|
||||
}
|
||||
}
|
||||
|
||||
Iterator* NewDBIterator(
|
||||
Env* env, const ImmutableCFOptions& ioptions,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, const Slice* iterate_upper_bound,
|
||||
bool prefix_same_as_start, bool pin_data, bool total_order_seek) {
|
||||
Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, ioptions, user_key_comparator, internal_iter, sequence, false,
|
||||
max_sequential_skip_in_iterations, version_number, iterate_upper_bound,
|
||||
prefix_same_as_start, pin_data, total_order_seek);
|
||||
env, read_options, cf_options, user_key_comparator, internal_iter,
|
||||
sequence, false, max_sequential_skip_in_iterations, version_number);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
@@ -1151,18 +1199,16 @@ void ArenaWrappedDBIter::RegisterCleanup(CleanupFunction function, void* arg1,
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ImmutableCFOptions& ioptions,
|
||||
const Comparator* user_key_comparator, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound, bool prefix_same_as_start, bool pin_data,
|
||||
bool total_order_seek) {
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const Comparator* user_key_comparator,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
Arena* arena = iter->GetArena();
|
||||
auto mem = arena->AllocateAligned(sizeof(DBIter));
|
||||
DBIter* db_iter = new (mem) DBIter(
|
||||
env, ioptions, user_key_comparator, nullptr, sequence, true,
|
||||
max_sequential_skip_in_iterations, version_number, iterate_upper_bound,
|
||||
prefix_same_as_start, pin_data, total_order_seek);
|
||||
DBIter* db_iter = new (mem)
|
||||
DBIter(env, read_options, cf_options, user_key_comparator, nullptr,
|
||||
sequence, true, max_sequential_skip_in_iterations, version_number);
|
||||
|
||||
iter->SetDBIter(db_iter);
|
||||
|
||||
|
||||
+12
-14
@@ -12,11 +12,11 @@
|
||||
#include <string>
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -27,13 +27,13 @@ class InternalIterator;
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified "sequence" number
|
||||
// into appropriate user keys.
|
||||
extern Iterator* NewDBIterator(
|
||||
Env* env, const ImmutableCFOptions& options,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false);
|
||||
extern Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number);
|
||||
|
||||
// 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
|
||||
@@ -77,11 +77,9 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ImmutableCFOptions& options,
|
||||
const Comparator* user_key_comparator, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false);
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const Comparator* user_key_comparator,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number);
|
||||
|
||||
} // namespace rocksdb
|
||||
+554
-163
File diff suppressed because it is too large.
Load diff
@@ -22,6 +22,34 @@ class DBIteratorTest : public DBTestBase {
|
||||
DBIteratorTest() : DBTestBase("/db_iterator_test") {}
|
||||
};
|
||||
|
||||
class FlushBlockEveryKeyPolicy : public FlushBlockPolicy {
|
||||
public:
|
||||
virtual bool Update(const Slice& key, const Slice& value) override {
|
||||
if (!start_) {
|
||||
start_ = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
bool start_ = false;
|
||||
};
|
||||
|
||||
class FlushBlockEveryKeyPolicyFactory : public FlushBlockPolicyFactory {
|
||||
public:
|
||||
explicit FlushBlockEveryKeyPolicyFactory() {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "FlushBlockEveryKeyPolicyFactory";
|
||||
}
|
||||
|
||||
FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new FlushBlockEveryKeyPolicy;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBIteratorTest, IteratorProperty) {
|
||||
// The test needs to be changed if kPersistedTier is supported in iterator.
|
||||
Options options = CurrentOptions();
|
||||
@@ -975,12 +1003,57 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
int upper_bound_hits = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::BlockEntryIteratorState::KeyReachedUpperBound",
|
||||
[&upper_bound_hits](void* arg) {
|
||||
assert(arg != nullptr);
|
||||
upper_bound_hits += (*static_cast<bool*>(arg) ? 1 : 0);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor = nullptr;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.flush_block_policy_factory =
|
||||
std::make_shared<FlushBlockEveryKeyPolicyFactory>();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo1", "bar1"));
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
ASSERT_OK(Put("foo4", "bar4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Slice ub("foo3");
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &ub;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
|
||||
iter->Seek("foo");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("foo1")), 0);
|
||||
ASSERT_EQ(upper_bound_hits, 0);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("foo2")), 0);
|
||||
ASSERT_EQ(upper_bound_hits, 0);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_EQ(upper_bound_hits, 1);
|
||||
}
|
||||
// TODO(3.13): fix the issue of Seek() + Prev() which might not necessary
|
||||
// return the biggest key which is smaller than the seek key.
|
||||
TEST_F(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// write three entries with different keys using Merge()
|
||||
|
||||
@@ -123,6 +123,7 @@ TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
options.memtable_factory.reset(new MockMemTableRepFactory());
|
||||
options.memtable_insert_with_hint_prefix_extractor.reset(
|
||||
new TestPrefixExtractor());
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
MockMemTableRep* rep =
|
||||
reinterpret_cast<MockMemTableRepFactory*>(options.memtable_factory.get())
|
||||
|
||||
@@ -44,6 +44,7 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnRead) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Merge("k1", "corrupted"));
|
||||
@@ -57,6 +58,7 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnWrite) {
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.max_successive_merges = 3;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Merge("k1", "v2"));
|
||||
@@ -71,6 +73,7 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnIteration) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
@@ -89,6 +92,294 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnIteration) {
|
||||
VerifyDBInternal({{"k1", "v1"}, {"k2", "corrupted"}, {"k2", "v2"}});
|
||||
}
|
||||
|
||||
|
||||
class MergeOperatorPinningTest : public DBMergeOperatorTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
MergeOperatorPinningTest() { disable_block_cache_ = GetParam(); }
|
||||
|
||||
bool disable_block_cache_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MergeOperatorPinningTest, MergeOperatorPinningTest,
|
||||
::testing::Bool());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(MergeOperatorPinningTest, OperandsMultiBlocks) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1; // every block will contain one entry
|
||||
table_options.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kKeysPerFile = 10;
|
||||
const int kOperandsPerKeyPerFile = 7;
|
||||
const int kOperandSize = 100;
|
||||
// Filse to write in L0 before compacting to lower level
|
||||
const int kFilesPerLevel = 3;
|
||||
|
||||
Random rnd(301);
|
||||
std::map<std::string, std::string> true_data;
|
||||
int batch_num = 1;
|
||||
int lvl_to_fill = 4;
|
||||
int key_id = 0;
|
||||
while (true) {
|
||||
for (int j = 0; j < kKeysPerFile; j++) {
|
||||
std::string key = Key(key_id % 35);
|
||||
key_id++;
|
||||
for (int k = 0; k < kOperandsPerKeyPerFile; k++) {
|
||||
std::string val = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, val));
|
||||
if (true_data[key].size() == 0) {
|
||||
true_data[key] = val;
|
||||
} else {
|
||||
true_data[key] += "," + val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lvl_to_fill == -1) {
|
||||
// Keep last batch in memtable and stop
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
if (batch_num % kFilesPerLevel == 0) {
|
||||
if (lvl_to_fill != 0) {
|
||||
MoveFilesToLevel(lvl_to_fill);
|
||||
}
|
||||
lvl_to_fill--;
|
||||
}
|
||||
batch_num++;
|
||||
}
|
||||
|
||||
// 3 L0 files
|
||||
// 1 L1 file
|
||||
// 3 L2 files
|
||||
// 1 L3 file
|
||||
// 3 L4 Files
|
||||
ASSERT_EQ(FilesPerLevel(), "3,1,3,1,3");
|
||||
|
||||
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 = 10000;
|
||||
// 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);
|
||||
|
||||
// Skip HashCuckoo since it does not support merge operators
|
||||
} while (ChangeOptions(kSkipMergePut | kSkipHashCuckoo));
|
||||
}
|
||||
|
||||
class MergeOperatorHook : public MergeOperator {
|
||||
public:
|
||||
explicit MergeOperatorHook(std::shared_ptr<MergeOperator> _merge_op)
|
||||
: merge_op_(_merge_op) {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
before_merge_();
|
||||
bool res = merge_op_->FullMergeV2(merge_in, merge_out);
|
||||
after_merge_();
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return merge_op_->Name(); }
|
||||
|
||||
std::shared_ptr<MergeOperator> merge_op_;
|
||||
std::function<void()> before_merge_ = []() {};
|
||||
std::function<void()> after_merge_ = []() {};
|
||||
};
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, EvictCacheBeforeMerge) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
auto merge_hook =
|
||||
std::make_shared<MergeOperatorHook>(MergeOperators::CreateMaxOperator());
|
||||
options.merge_operator = merge_hook;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.max_open_files = 20;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = disable_block_cache_;
|
||||
if (bbto.no_block_cache == false) {
|
||||
bbto.block_cache = NewLRUCache(64 * 1024 * 1024);
|
||||
} else {
|
||||
bbto.block_cache = nullptr;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumOperands = 30;
|
||||
const int kNumKeys = 1000;
|
||||
const int kOperandSize = 100;
|
||||
Random rnd(301);
|
||||
|
||||
// 1000 keys every key have 30 operands, every operand is in a different file
|
||||
std::map<std::string, std::string> true_data;
|
||||
for (int i = 0; i < kNumOperands; i++) {
|
||||
for (int j = 0; j < kNumKeys; j++) {
|
||||
std::string k = Key(j);
|
||||
std::string v = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), k, v));
|
||||
|
||||
true_data[k] = std::max(true_data[k], v);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
std::vector<uint64_t> file_numbers = ListTableFiles(env_, dbname_);
|
||||
ASSERT_EQ(file_numbers.size(), kNumOperands);
|
||||
int merge_cnt = 0;
|
||||
|
||||
// Code executed before merge operation
|
||||
merge_hook->before_merge_ = [&]() {
|
||||
// Evict all tables from cache before every merge operation
|
||||
for (uint64_t num : file_numbers) {
|
||||
TableCache::Evict(dbfull()->TEST_table_cache(), num);
|
||||
}
|
||||
// Decrease cache capacity to force all unrefed blocks to be evicted
|
||||
if (bbto.block_cache) {
|
||||
bbto.block_cache->SetCapacity(1);
|
||||
}
|
||||
merge_cnt++;
|
||||
};
|
||||
|
||||
// Code executed after merge operation
|
||||
merge_hook->after_merge_ = [&]() {
|
||||
// Increase capacity again after doing the merge
|
||||
if (bbto.block_cache) {
|
||||
bbto.block_cache->SetCapacity(64 * 1024 * 1024);
|
||||
}
|
||||
};
|
||||
|
||||
size_t total_reads;
|
||||
VerifyDBFromMap(true_data, &total_reads);
|
||||
ASSERT_EQ(merge_cnt, total_reads);
|
||||
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
VerifyDBFromMap(true_data, &total_reads);
|
||||
}
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, TailingIterator) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateMaxOperator();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumOperands = 100;
|
||||
const int kNumWrites = 100000;
|
||||
|
||||
std::function<void()> writer_func = [&]() {
|
||||
int k = 0;
|
||||
for (int i = 0; i < kNumWrites; i++) {
|
||||
db_->Merge(WriteOptions(), Key(k), Key(k));
|
||||
|
||||
if (i && i % kNumOperands == 0) {
|
||||
k++;
|
||||
}
|
||||
if (i && i % 127 == 0) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
if (i && i % 317 == 0) {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::function<void()> reader_func = [&]() {
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < (kNumWrites / kNumOperands); i++) {
|
||||
while (!iter->Valid()) {
|
||||
// wait for the key to be written
|
||||
env_->SleepForMicroseconds(100);
|
||||
iter->Seek(Key(i));
|
||||
}
|
||||
ASSERT_EQ(iter->key(), Key(i));
|
||||
ASSERT_EQ(iter->value(), Key(i));
|
||||
|
||||
iter->Next();
|
||||
}
|
||||
|
||||
delete iter;
|
||||
};
|
||||
|
||||
rocksdb::port::Thread writer_thread(writer_func);
|
||||
rocksdb::port::Thread reader_thread(reader_func);
|
||||
|
||||
writer_thread.join();
|
||||
reader_thread.join();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+28
-2
@@ -13,9 +13,9 @@
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "util/options_helper.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testutil.h"
|
||||
@@ -62,6 +62,7 @@ class DBOptionsTest : public DBTestBase {
|
||||
std::unordered_map<std::string, std::string> GetRandomizedMutableCFOptionsMap(
|
||||
Random* rnd) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
ImmutableDBOptions db_options(options);
|
||||
test::RandomInitCFOptions(&options, rnd);
|
||||
auto sanitized_options = SanitizeOptions(db_options, options);
|
||||
@@ -87,6 +88,7 @@ TEST_F(DBOptionsTest, GetLatestDBOptions) {
|
||||
// GetOptions should be able to get latest option changed by SetOptions.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
Random rnd(228);
|
||||
Reopen(options);
|
||||
auto new_options = GetRandomizedMutableDBOptionsMap(&rnd);
|
||||
@@ -98,6 +100,7 @@ TEST_F(DBOptionsTest, GetLatestCFOptions) {
|
||||
// GetOptions should be able to get latest option changed by SetOptions.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
Random rnd(228);
|
||||
Reopen(options);
|
||||
CreateColumnFamilies({"foo"}, options);
|
||||
@@ -118,6 +121,7 @@ TEST_F(DBOptionsTest, SetOptionsAndReopen) {
|
||||
ASSERT_OK(dbfull()->SetOptions(rand_opts));
|
||||
// Verify if DB can be reopen after setting options.
|
||||
Options options;
|
||||
options.env = env_;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
|
||||
@@ -137,6 +141,7 @@ TEST_F(DBOptionsTest, EnableAutoCompactionAndTriggerStall) {
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
options.soft_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
options.env = env_;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int i = 0;
|
||||
@@ -228,6 +233,7 @@ TEST_F(DBOptionsTest, SetOptionsMayTriggerCompaction) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 1000;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Need to insert two keys to avoid trivial move.
|
||||
@@ -247,6 +253,7 @@ TEST_F(DBOptionsTest, SetBackgroundCompactionThreads) {
|
||||
options.create_if_missing = true;
|
||||
options.base_background_compactions = 1; // default value
|
||||
options.max_background_compactions = 1; // default value
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"base_background_compactions", "2"},
|
||||
@@ -260,6 +267,7 @@ TEST_F(DBOptionsTest, AvoidFlushDuringShutdown) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
WriteOptions write_without_wal;
|
||||
write_without_wal.disableWAL = true;
|
||||
|
||||
@@ -282,6 +290,7 @@ TEST_F(DBOptionsTest, SetDelayedWriteRateOption) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.delayed_write_rate = 2 * 1024U * 1024U;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(2 * 1024U * 1024U, dbfull()->TEST_write_controler().max_delayed_write_rate());
|
||||
|
||||
@@ -297,6 +306,7 @@ TEST_F(DBOptionsTest, MaxTotalWalSizeChange) {
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
CreateColumnFamilies({"1", "2", "3"}, options);
|
||||
ReopenWithColumnFamilies({"default", "1", "2", "3"}, options);
|
||||
|
||||
@@ -316,6 +326,22 @@ TEST_F(DBOptionsTest, MaxTotalWalSizeChange) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetStatsDumpPeriodSec) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_dump_period_sec = 5;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int num = rand() % 5000 + 1;
|
||||
ASSERT_OK(dbfull()->SetDBOptions(
|
||||
{{"stats_dump_period_sec", std::to_string(num)}}));
|
||||
ASSERT_EQ(num, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
}
|
||||
}
|
||||
|
||||
static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
dbfull->TEST_LockMutex();
|
||||
JobContext job_context(0);
|
||||
@@ -326,7 +352,7 @@ static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
SpecialEnv env(Env::Default());
|
||||
SpecialEnv env(env_);
|
||||
env.time_elapse_only_sleep_ = true;
|
||||
Options options;
|
||||
options.env = &env;
|
||||
|
||||
+51
-10
@@ -342,7 +342,7 @@ TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
int key_index = 0;
|
||||
Random rnd(301);
|
||||
for (int num = 0; num < 8; num++) {
|
||||
@@ -359,25 +359,26 @@ TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
|
||||
for (int key = 0; key < key_index; key++) {
|
||||
Get(Key(key));
|
||||
}
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cfstats", &prop));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
|
||||
// Reopen and issue Get(). See thee latency tracked
|
||||
Reopen(options);
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
for (int key = 0; key < key_index; key++) {
|
||||
Get(Key(key));
|
||||
}
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
|
||||
ASSERT_TRUE(dbfull()->GetProperty(dbfull()->DefaultColumnFamily(),
|
||||
"rocksdb.cf-file-histogram", &prop));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
|
||||
// Reopen and issue iterating. See thee latency tracked
|
||||
Reopen(options);
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cf-file-histogram", &prop));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
@@ -386,25 +387,51 @@ TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
|
||||
for (iter->Seek(Key(0)); iter->Valid(); iter->Next()) {
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cf-file-histogram", &prop));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
|
||||
// CF 1 should show no histogram.
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty(handles_[1], "rocksdb.cf-file-histogram", &prop));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
// put something and read it back , CF 1 should show histogram.
|
||||
Put(1, "foo", "bar");
|
||||
Flush(1);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
|
||||
ASSERT_TRUE(
|
||||
dbfull()->GetProperty(handles_[1], "rocksdb.cf-file-histogram", &prop));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
|
||||
// options.max_open_files preloads table readers.
|
||||
options.max_open_files = -1;
|
||||
Reopen(options);
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_TRUE(dbfull()->GetProperty(dbfull()->DefaultColumnFamily(),
|
||||
"rocksdb.cf-file-histogram", &prop));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
for (int key = 0; key < key_index; key++) {
|
||||
Get(Key(key));
|
||||
}
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cfstats", &prop));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
|
||||
// Clear internal stats
|
||||
dbfull()->ResetStats();
|
||||
ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cfstats", &prop));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 0 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
|
||||
ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
@@ -1156,6 +1183,7 @@ TEST_F(DBPropertiesTest, TablePropertiesNeedCompactTest) {
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.soft_pending_compaction_bytes_limit = 1024 * 1024;
|
||||
options.num_levels = 8;
|
||||
options.env = env_;
|
||||
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> collector_factory =
|
||||
std::make_shared<CountingDeleteTabPropCollectorFactory>();
|
||||
@@ -1227,6 +1255,7 @@ TEST_F(DBPropertiesTest, NeedCompactHintPersistentTest) {
|
||||
options.level0_slowdown_writes_trigger = 10;
|
||||
options.level0_stop_writes_trigger = 10;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> collector_factory =
|
||||
std::make_shared<CountingDeleteTabPropCollectorFactory>();
|
||||
@@ -1271,6 +1300,18 @@ TEST_F(DBPropertiesTest, NeedCompactHintPersistentTest) {
|
||||
SetPerfLevel(kDisable);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, EstimateNumKeysUnderflow) {
|
||||
Options options;
|
||||
Reopen(options);
|
||||
Put("foo", "bar");
|
||||
Delete("foo");
|
||||
Delete("foo");
|
||||
uint64_t num_keys = 0;
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.estimate-num-keys", &num_keys));
|
||||
ASSERT_EQ(0, num_keys);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace rocksdb
|
||||
|
||||
|
||||
@@ -324,6 +324,7 @@ TEST_F(DBRangeDelTest, CompactionRemovesCoveredKeys) {
|
||||
TEST_F(DBRangeDelTest, ValidLevelSubcompactionBoundaries) {
|
||||
const int kNumPerFile = 100, kNumFiles = 4, kFileBytes = 100 << 10;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_file_num_compaction_trigger = kNumFiles;
|
||||
options.max_bytes_for_level_base = 2 * kFileBytes;
|
||||
options.max_subcompactions = 4;
|
||||
@@ -361,7 +362,14 @@ TEST_F(DBRangeDelTest, ValidLevelSubcompactionBoundaries) {
|
||||
// new L1 files must be generated with non-overlapping key ranges even
|
||||
// though multiple subcompactions see the same ranges deleted, else an
|
||||
// assertion will fail.
|
||||
//
|
||||
// Only enable auto-compactions when we're ready; otherwise, the
|
||||
// oversized L0 (relative to base_level) causes the compaction to run
|
||||
// earlier.
|
||||
ASSERT_OK(db_->EnableAutoCompaction({db_->DefaultColumnFamily()}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(db_->SetOptions(db_->DefaultColumnFamily(),
|
||||
{{"disable_auto_compactions", "true"}}));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(2), 0);
|
||||
|
||||
+3
-1
@@ -210,7 +210,9 @@ TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) {
|
||||
blocking_thread.WakeUp();
|
||||
blocking_thread.WaitUntilDone();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("1,0,0,0,1", FilesPerLevel(0));
|
||||
// File just flushed is too big for L0 and L1 so gets moved to L2.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ("0,0,1,0,1", FilesPerLevel(0));
|
||||
|
||||
metadata.clear();
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBStatisticsTest : public DBTestBase {
|
||||
public:
|
||||
DBStatisticsTest() : DBTestBase("/db_statistics_test") {}
|
||||
};
|
||||
|
||||
TEST_F(DBStatisticsTest, CompressionStatsTest) {
|
||||
CompressionType type;
|
||||
|
||||
if (Snappy_Supported()) {
|
||||
type = kSnappyCompression;
|
||||
fprintf(stderr, "using snappy\n");
|
||||
} else if (Zlib_Supported()) {
|
||||
type = kZlibCompression;
|
||||
fprintf(stderr, "using zlib\n");
|
||||
} else if (BZip2_Supported()) {
|
||||
type = kBZip2Compression;
|
||||
fprintf(stderr, "using bzip2\n");
|
||||
} else if (LZ4_Supported()) {
|
||||
type = kLZ4Compression;
|
||||
fprintf(stderr, "using lz4\n");
|
||||
} else if (XPRESS_Supported()) {
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTD;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = type;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.statistics->stats_level_ = StatsLevel::kExceptTimeForMutex;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int kNumKeysWritten = 100000;
|
||||
|
||||
// Check that compressions occur and are counted when compression is turned on
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED), 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED), 0);
|
||||
|
||||
options.compression = kNoCompression;
|
||||
DestroyAndReopen(options);
|
||||
uint64_t currentCompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
uint64_t currentDecompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED);
|
||||
|
||||
// Check that compressions do not occur when turned off
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED)
|
||||
- currentCompressions, 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED)
|
||||
- currentDecompressions, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBStatisticsTest, MutexWaitStatsDisabledByDefault) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
const uint64_t kMutexWaitDelay = 100;
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT,
|
||||
kMutexWaitDelay);
|
||||
ASSERT_OK(Put("hello", "rocksdb"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, DB_MUTEX_WAIT_MICROS), 0);
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBStatisticsTest, MutexWaitStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.statistics->stats_level_ = StatsLevel::kAll;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
const uint64_t kMutexWaitDelay = 100;
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT,
|
||||
kMutexWaitDelay);
|
||||
ASSERT_OK(Put("hello", "rocksdb"));
|
||||
ASSERT_GE(TestGetTickerCount(options, DB_MUTEX_WAIT_MICROS), kMutexWaitDelay);
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBStatisticsTest, ResetStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// pick arbitrary ticker and histogram. On first iteration they're zero
|
||||
// because db is unused. On second iteration they're zero due to Reset().
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, NUMBER_KEYS_WRITTEN));
|
||||
HistogramData histogram_data;
|
||||
options.statistics->histogramData(DB_WRITE, &histogram_data);
|
||||
ASSERT_EQ(0.0, histogram_data.max);
|
||||
|
||||
if (i == 0) {
|
||||
// The Put() makes some of the ticker/histogram stats nonzero until we
|
||||
// Reset().
|
||||
ASSERT_OK(Put("hello", "rocksdb"));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, NUMBER_KEYS_WRITTEN));
|
||||
options.statistics->histogramData(DB_WRITE, &histogram_data);
|
||||
ASSERT_GT(histogram_data.max, 0.0);
|
||||
options.statistics->Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -147,6 +147,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.hard_pending_compaction_bytes_limit = 16 * 1024;
|
||||
options.num_levels = 8;
|
||||
options.env = env_;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
options.write_buffer_size = k150KB;
|
||||
options.max_write_buffer_number = 3;
|
||||
options.min_write_buffer_number_to_merge = 2;
|
||||
options.env = env_;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
@@ -298,7 +299,6 @@ TEST_F(DBTestTailingIterator, TailingIteratorPrefixSeek) {
|
||||
read_options.tailing = true;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
@@ -616,7 +616,6 @@ TEST_F(DBTestTailingIterator, ManagedTailingIteratorPrefixSeek) {
|
||||
read_options.managed = true;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
|
||||
+9
-107
@@ -26,11 +26,12 @@
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
@@ -57,16 +58,15 @@
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mock_env.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "util/thread_status_util.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -2212,7 +2212,7 @@ class ModelDB : public DB {
|
||||
}
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& options, ColumnFamilyHandle* cf,
|
||||
const Slice& key, std::string* value) override {
|
||||
const Slice& key, PinnableSlice* value) override {
|
||||
return Status::NotSupported(key);
|
||||
}
|
||||
|
||||
@@ -2965,7 +2965,6 @@ TEST_F(DBTest, DynamicMemtableOptions) {
|
||||
const uint64_t k64KB = 1 << 16;
|
||||
const uint64_t k128KB = 1 << 17;
|
||||
const uint64_t k5KB = 5 * 1024;
|
||||
const int kNumPutsBeforeWaitForFlush = 64;
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -2980,7 +2979,8 @@ TEST_F(DBTest, DynamicMemtableOptions) {
|
||||
options.level0_stop_writes_trigger = 1024;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
auto gen_l0_kb = [this, kNumPutsBeforeWaitForFlush](int size) {
|
||||
auto gen_l0_kb = [this](int size) {
|
||||
const int kNumPutsBeforeWaitForFlush = 64;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < size; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
@@ -4087,7 +4087,7 @@ TEST_F(DBTest, DynamicMiscOptions) {
|
||||
// sanity check
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[1],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(true, mutable_cf_options.report_bg_io_stats);
|
||||
ASSERT_TRUE(mutable_cf_options.report_bg_io_stats);
|
||||
// Test compression
|
||||
// sanity check
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compression", "kNoCompression"}}));
|
||||
@@ -4104,7 +4104,7 @@ TEST_F(DBTest, DynamicMiscOptions) {
|
||||
dbfull()->SetOptions(handles_[1], {{"paranoid_file_checks", "true"}}));
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[1],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(true, mutable_cf_options.report_bg_io_stats);
|
||||
ASSERT_TRUE(mutable_cf_options.report_bg_io_stats);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -4193,104 +4193,6 @@ TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, CompressionStatsTest) {
|
||||
CompressionType type;
|
||||
|
||||
if (Snappy_Supported()) {
|
||||
type = kSnappyCompression;
|
||||
fprintf(stderr, "using snappy\n");
|
||||
} else if (Zlib_Supported()) {
|
||||
type = kZlibCompression;
|
||||
fprintf(stderr, "using zlib\n");
|
||||
} else if (BZip2_Supported()) {
|
||||
type = kBZip2Compression;
|
||||
fprintf(stderr, "using bzip2\n");
|
||||
} else if (LZ4_Supported()) {
|
||||
type = kLZ4Compression;
|
||||
fprintf(stderr, "using lz4\n");
|
||||
} else if (XPRESS_Supported()) {
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTD;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = type;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.statistics->stats_level_ = StatsLevel::kExceptTimeForMutex;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int kNumKeysWritten = 100000;
|
||||
|
||||
// Check that compressions occur and are counted when compression is turned on
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED), 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED), 0);
|
||||
|
||||
options.compression = kNoCompression;
|
||||
DestroyAndReopen(options);
|
||||
uint64_t currentCompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
uint64_t currentDecompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED);
|
||||
|
||||
// Check that compressions do not occur when turned off
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED)
|
||||
- currentCompressions, 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED)
|
||||
- currentDecompressions, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MutexWaitStatsDisabledByDefault) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
const uint64_t kMutexWaitDelay = 100;
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT,
|
||||
kMutexWaitDelay);
|
||||
ASSERT_OK(Put("hello", "rocksdb"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, DB_MUTEX_WAIT_MICROS), 0);
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MutexWaitStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.statistics->stats_level_ = StatsLevel::kAll;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
const uint64_t kMutexWaitDelay = 100;
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT,
|
||||
kMutexWaitDelay);
|
||||
ASSERT_OK(Put("hello", "rocksdb"));
|
||||
ASSERT_GE(TestGetTickerCount(options, DB_MUTEX_WAIT_MICROS), kMutexWaitDelay);
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, CloseSpeedup) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
@@ -5214,7 +5116,7 @@ TEST_F(DBTest, PauseBackgroundWorkTest) {
|
||||
t.join();
|
||||
}
|
||||
// now it's done
|
||||
ASSERT_EQ(true, done.load());
|
||||
ASSERT_TRUE(done.load());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+246
-508
@@ -183,19 +183,32 @@ TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
options.write_buffer_size = 500000; // this is never hit
|
||||
CreateAndReopenWithCF({"pikachu", "dobrynia", "nikitich"}, options);
|
||||
|
||||
// Trigger a flush on CF "nikitich"
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
ASSERT_OK(Put(1, Key(1), DummyString(1)));
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(90000)));
|
||||
ASSERT_OK(Put(2, Key(2), DummyString(20000)));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1)));
|
||||
WriteOptions wo;
|
||||
wo.disableWAL = true;
|
||||
|
||||
// Create some data and flush "default" and "nikitich" so that they
|
||||
// are newer CFs created.
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1), wo));
|
||||
Flush(3);
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1), wo));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1), wo));
|
||||
Flush(0);
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(1));
|
||||
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(30000), wo));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(60000), wo));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1), wo));
|
||||
// No flush should trigger
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
@@ -204,98 +217,84 @@ TEST_P(DBTestSharedWriteBufferAcrossCFs, SharedWriteBufferAcrossCFs) {
|
||||
static_cast<uint64_t>(1));
|
||||
}
|
||||
|
||||
// "dobrynia": 20KB
|
||||
// Flush 'dobrynia'
|
||||
ASSERT_OK(Put(3, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(Put(2, Key(2), DummyString(70000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(1));
|
||||
}
|
||||
|
||||
// "nikitich" still has data of 80KB
|
||||
// Inserting Data in "dobrynia" triggers "nikitich" flushing.
|
||||
ASSERT_OK(Put(3, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(Put(2, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
|
||||
// "dobrynia" still has 40KB
|
||||
ASSERT_OK(Put(1, Key(2), DummyString(20000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(10000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
// Trigger a flush. Flushing "nikitich".
|
||||
ASSERT_OK(Put(3, Key(2), DummyString(30000), wo));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1), wo));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
// This should triggers no flush
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
|
||||
// "default": 10KB, "pikachu": 20KB, "dobrynia": 40KB
|
||||
ASSERT_OK(Put(1, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
// Without hitting the threshold, no flush should trigger.
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(30000), wo));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1), wo));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1), wo));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
// This should triggers flush of "pikachu"
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(1));
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
|
||||
// "default": 10KB, "dobrynia": 40KB
|
||||
// Some remaining writes so 'default', 'dobrynia' and 'nikitich' flush on
|
||||
// closure.
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1)));
|
||||
ReopenWithColumnFamilies({"default", "pikachu", "dobrynia", "nikitich"},
|
||||
options);
|
||||
// Hit the write buffer limit again. "default"
|
||||
// will have been flushed.
|
||||
ASSERT_OK(Put(2, Key(2), DummyString(10000), wo));
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1), wo));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1), wo));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1), wo));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1), wo));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(2));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(3));
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
|
||||
// Trigger another flush. This time "dobrynia". "pikachu" should not
|
||||
// be flushed, althrough it was never flushed.
|
||||
ASSERT_OK(Put(1, Key(1), DummyString(1), wo));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(80000), wo));
|
||||
ASSERT_OK(Put(1, Key(1), DummyString(1), wo));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1), wo));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(2));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,23 +313,38 @@ TEST_F(DBTest2, SharedWriteBufferLimitAcrossDB) {
|
||||
ASSERT_OK(DB::Open(options, dbname2, &db2));
|
||||
|
||||
WriteOptions wo;
|
||||
wo.disableWAL = true;
|
||||
|
||||
// Trigger a flush on cf2
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
ASSERT_OK(Put(1, Key(1), DummyString(1)));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(90000)));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(70000), wo));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(20000), wo));
|
||||
|
||||
// Insert to DB2
|
||||
ASSERT_OK(db2->Put(wo, Key(2), DummyString(20000)));
|
||||
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1)));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1), wo));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
static_cast<DBImpl*>(db2)->TEST_WaitForFlushMemTable();
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default") +
|
||||
GetNumberOfSstFilesForColumnFamily(db_, "cf1") +
|
||||
GetNumberOfSstFilesForColumnFamily(db_, "cf2"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db2, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
}
|
||||
|
||||
// Triggering to flush another CF in DB1
|
||||
ASSERT_OK(db2->Put(wo, Key(2), DummyString(70000)));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1), wo));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "cf1"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "cf2"),
|
||||
@@ -339,9 +353,8 @@ TEST_F(DBTest2, SharedWriteBufferLimitAcrossDB) {
|
||||
static_cast<uint64_t>(0));
|
||||
}
|
||||
|
||||
// db2: 20KB
|
||||
ASSERT_OK(db2->Put(wo, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(db2->Put(wo, Key(3), DummyString(70000)));
|
||||
// Triggering flush in DB2.
|
||||
ASSERT_OK(db2->Put(wo, Key(3), DummyString(40000)));
|
||||
ASSERT_OK(db2->Put(wo, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
@@ -349,7 +362,7 @@ TEST_F(DBTest2, SharedWriteBufferLimitAcrossDB) {
|
||||
static_cast<DBImpl*>(db2)->TEST_WaitForFlushMemTable();
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "cf1"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "cf2"),
|
||||
@@ -358,26 +371,6 @@ TEST_F(DBTest2, SharedWriteBufferLimitAcrossDB) {
|
||||
static_cast<uint64_t>(1));
|
||||
}
|
||||
|
||||
//
|
||||
// Inserting Data in db2 and db_ triggers flushing in db_.
|
||||
ASSERT_OK(db2->Put(wo, Key(3), DummyString(70000)));
|
||||
ASSERT_OK(Put(2, Key(2), DummyString(45000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
static_cast<DBImpl*>(db2)->TEST_WaitForFlushMemTable();
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "cf1"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "cf2"),
|
||||
static_cast<uint64_t>(2));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db2, "default"),
|
||||
static_cast<uint64_t>(1));
|
||||
}
|
||||
|
||||
delete db2;
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
@@ -1228,7 +1221,7 @@ class PinL0IndexAndFilterBlocksTest : public DBTestBase,
|
||||
PinL0IndexAndFilterBlocksTest() : DBTestBase("/db_pin_l0_index_bloom_test") {}
|
||||
virtual void SetUp() override { infinite_max_files_ = GetParam(); }
|
||||
|
||||
void CreateTwoLevels(Options* options) {
|
||||
void CreateTwoLevels(Options* options, bool close_afterwards) {
|
||||
if (infinite_max_files_) {
|
||||
options->max_open_files = -1;
|
||||
}
|
||||
@@ -1256,6 +1249,9 @@ class PinL0IndexAndFilterBlocksTest : public DBTestBase,
|
||||
Put(1, "z2", "end2");
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
if (close_afterwards) {
|
||||
Close(); // This ensures that there is no ref to block cache entries
|
||||
}
|
||||
table_options.block_cache->EraseUnRefEntries();
|
||||
}
|
||||
|
||||
@@ -1310,7 +1306,7 @@ TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
MultiLevelIndexAndFilterBlocksCachedWithPinning) {
|
||||
Options options = CurrentOptions();
|
||||
PinL0IndexAndFilterBlocksTest::CreateTwoLevels(&options);
|
||||
PinL0IndexAndFilterBlocksTest::CreateTwoLevels(&options, false);
|
||||
// get base cache values
|
||||
uint64_t fm = TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
uint64_t fh = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
@@ -1339,7 +1335,10 @@ TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
|
||||
TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
Options options = CurrentOptions();
|
||||
PinL0IndexAndFilterBlocksTest::CreateTwoLevels(&options);
|
||||
// This ensures that db does not ref anything in the block cache, so
|
||||
// EraseUnRefEntries could clear them up.
|
||||
bool close_afterwards = true;
|
||||
PinL0IndexAndFilterBlocksTest::CreateTwoLevels(&options, close_afterwards);
|
||||
|
||||
// Get base cache values
|
||||
uint64_t fm = TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
@@ -1628,293 +1627,6 @@ TEST_F(DBTest2, SyncPointMarker) {
|
||||
}
|
||||
#endif
|
||||
|
||||
class MergeOperatorPinningTest : public DBTest2,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
MergeOperatorPinningTest() { disable_block_cache_ = GetParam(); }
|
||||
|
||||
bool disable_block_cache_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MergeOperatorPinningTest, MergeOperatorPinningTest,
|
||||
::testing::Bool());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(MergeOperatorPinningTest, OperandsMultiBlocks) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1; // every block will contain one entry
|
||||
table_options.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kKeysPerFile = 10;
|
||||
const int kOperandsPerKeyPerFile = 7;
|
||||
const int kOperandSize = 100;
|
||||
// Filse to write in L0 before compacting to lower level
|
||||
const int kFilesPerLevel = 3;
|
||||
|
||||
Random rnd(301);
|
||||
std::map<std::string, std::string> true_data;
|
||||
int batch_num = 1;
|
||||
int lvl_to_fill = 4;
|
||||
int key_id = 0;
|
||||
while (true) {
|
||||
for (int j = 0; j < kKeysPerFile; j++) {
|
||||
std::string key = Key(key_id % 35);
|
||||
key_id++;
|
||||
for (int k = 0; k < kOperandsPerKeyPerFile; k++) {
|
||||
std::string val = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, val));
|
||||
if (true_data[key].size() == 0) {
|
||||
true_data[key] = val;
|
||||
} else {
|
||||
true_data[key] += "," + val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lvl_to_fill == -1) {
|
||||
// Keep last batch in memtable and stop
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
if (batch_num % kFilesPerLevel == 0) {
|
||||
if (lvl_to_fill != 0) {
|
||||
MoveFilesToLevel(lvl_to_fill);
|
||||
}
|
||||
lvl_to_fill--;
|
||||
}
|
||||
batch_num++;
|
||||
}
|
||||
|
||||
// 3 L0 files
|
||||
// 1 L1 file
|
||||
// 3 L2 files
|
||||
// 1 L3 file
|
||||
// 3 L4 Files
|
||||
ASSERT_EQ(FilesPerLevel(), "3,1,3,1,3");
|
||||
|
||||
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 = 10000;
|
||||
// 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);
|
||||
|
||||
// Skip HashCuckoo since it does not support merge operators
|
||||
} while (ChangeOptions(kSkipMergePut | kSkipHashCuckoo));
|
||||
}
|
||||
|
||||
class MergeOperatorHook : public MergeOperator {
|
||||
public:
|
||||
explicit MergeOperatorHook(std::shared_ptr<MergeOperator> _merge_op)
|
||||
: merge_op_(_merge_op) {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
before_merge_();
|
||||
bool res = merge_op_->FullMergeV2(merge_in, merge_out);
|
||||
after_merge_();
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return merge_op_->Name(); }
|
||||
|
||||
std::shared_ptr<MergeOperator> merge_op_;
|
||||
std::function<void()> before_merge_ = []() {};
|
||||
std::function<void()> after_merge_ = []() {};
|
||||
};
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, EvictCacheBeforeMerge) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
auto merge_hook =
|
||||
std::make_shared<MergeOperatorHook>(MergeOperators::CreateMaxOperator());
|
||||
options.merge_operator = merge_hook;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.max_open_files = 20;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = disable_block_cache_;
|
||||
if (bbto.no_block_cache == false) {
|
||||
bbto.block_cache = NewLRUCache(64 * 1024 * 1024);
|
||||
} else {
|
||||
bbto.block_cache = nullptr;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumOperands = 30;
|
||||
const int kNumKeys = 1000;
|
||||
const int kOperandSize = 100;
|
||||
Random rnd(301);
|
||||
|
||||
// 1000 keys every key have 30 operands, every operand is in a different file
|
||||
std::map<std::string, std::string> true_data;
|
||||
for (int i = 0; i < kNumOperands; i++) {
|
||||
for (int j = 0; j < kNumKeys; j++) {
|
||||
std::string k = Key(j);
|
||||
std::string v = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), k, v));
|
||||
|
||||
true_data[k] = std::max(true_data[k], v);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
std::vector<uint64_t> file_numbers = ListTableFiles(env_, dbname_);
|
||||
ASSERT_EQ(file_numbers.size(), kNumOperands);
|
||||
int merge_cnt = 0;
|
||||
|
||||
// Code executed before merge operation
|
||||
merge_hook->before_merge_ = [&]() {
|
||||
// Evict all tables from cache before every merge operation
|
||||
for (uint64_t num : file_numbers) {
|
||||
TableCache::Evict(dbfull()->TEST_table_cache(), num);
|
||||
}
|
||||
// Decrease cache capacity to force all unrefed blocks to be evicted
|
||||
if (bbto.block_cache) {
|
||||
bbto.block_cache->SetCapacity(1);
|
||||
}
|
||||
merge_cnt++;
|
||||
};
|
||||
|
||||
// Code executed after merge operation
|
||||
merge_hook->after_merge_ = [&]() {
|
||||
// Increase capacity again after doing the merge
|
||||
if (bbto.block_cache) {
|
||||
bbto.block_cache->SetCapacity(64 * 1024 * 1024);
|
||||
}
|
||||
};
|
||||
|
||||
size_t total_reads;
|
||||
VerifyDBFromMap(true_data, &total_reads);
|
||||
ASSERT_EQ(merge_cnt, total_reads);
|
||||
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
VerifyDBFromMap(true_data, &total_reads);
|
||||
}
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, TailingIterator) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateMaxOperator();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumOperands = 100;
|
||||
const int kNumWrites = 100000;
|
||||
|
||||
std::function<void()> writer_func = [&]() {
|
||||
int k = 0;
|
||||
for (int i = 0; i < kNumWrites; i++) {
|
||||
db_->Merge(WriteOptions(), Key(k), Key(k));
|
||||
|
||||
if (i && i % kNumOperands == 0) {
|
||||
k++;
|
||||
}
|
||||
if (i && i % 127 == 0) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
if (i && i % 317 == 0) {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::function<void()> reader_func = [&]() {
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < (kNumWrites / kNumOperands); i++) {
|
||||
while (!iter->Valid()) {
|
||||
// wait for the key to be written
|
||||
env_->SleepForMicroseconds(100);
|
||||
iter->Seek(Key(i));
|
||||
}
|
||||
ASSERT_EQ(iter->key(), Key(i));
|
||||
ASSERT_EQ(iter->value(), Key(i));
|
||||
|
||||
iter->Next();
|
||||
}
|
||||
|
||||
delete iter;
|
||||
};
|
||||
|
||||
rocksdb::port::Thread writer_thread(writer_func);
|
||||
rocksdb::port::Thread reader_thread(reader_func);
|
||||
|
||||
writer_thread.join();
|
||||
reader_thread.join();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
size_t GetEncodedEntrySize(size_t key_size, size_t value_size) {
|
||||
std::string buffer;
|
||||
|
||||
@@ -1928,66 +1640,80 @@ size_t GetEncodedEntrySize(size_t key_size, size_t value_size) {
|
||||
TEST_F(DBTest2, ReadAmpBitmap) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions bbto;
|
||||
// Disable delta encoding to make it easier to calculate read amplification
|
||||
bbto.use_delta_encoding = false;
|
||||
// Huge block cache to make it easier to calculate read amplification
|
||||
bbto.block_cache = NewLRUCache(1024 * 1024 * 1024);
|
||||
bbto.read_amp_bytes_per_bit = 16;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
uint32_t bytes_per_bit[2] = {1, 16};
|
||||
for (size_t k = 0; k < 2; k++) {
|
||||
// Disable delta encoding to make it easier to calculate read amplification
|
||||
bbto.use_delta_encoding = false;
|
||||
// Huge block cache to make it easier to calculate read amplification
|
||||
bbto.block_cache = NewLRUCache(1024 * 1024 * 1024);
|
||||
bbto.read_amp_bytes_per_bit = bytes_per_bit[k];
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kNumEntries = 10000;
|
||||
const size_t kNumEntries = 10000;
|
||||
|
||||
Random rnd(301);
|
||||
for (size_t i = 0; i < kNumEntries; i++) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(i)), RandomString(&rnd, 100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
Random rnd(301);
|
||||
for (size_t i = 0; i < kNumEntries; i++) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(i)), RandomString(&rnd, 100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Close();
|
||||
Reopen(options);
|
||||
Close();
|
||||
Reopen(options);
|
||||
|
||||
// Read keys/values randomly and verify that reported read amp error
|
||||
// is less than 2%
|
||||
uint64_t total_useful_bytes = 0;
|
||||
std::set<int> read_keys;
|
||||
std::string value;
|
||||
for (size_t i = 0; i < kNumEntries * 5; i++) {
|
||||
int key_idx = rnd.Next() % kNumEntries;
|
||||
std::string k = Key(key_idx);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), k, &value));
|
||||
// Read keys/values randomly and verify that reported read amp error
|
||||
// is less than 2%
|
||||
uint64_t total_useful_bytes = 0;
|
||||
std::set<int> read_keys;
|
||||
std::string value;
|
||||
for (size_t i = 0; i < kNumEntries * 5; i++) {
|
||||
int key_idx = rnd.Next() % kNumEntries;
|
||||
std::string key = Key(key_idx);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), key, &value));
|
||||
|
||||
if (read_keys.find(key_idx) == read_keys.end()) {
|
||||
auto ik = InternalKey(k, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes += GetEncodedEntrySize(ik.size(), value.size());
|
||||
read_keys.insert(key_idx);
|
||||
if (read_keys.find(key_idx) == read_keys.end()) {
|
||||
auto internal_key = InternalKey(key, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes +=
|
||||
GetEncodedEntrySize(internal_key.size(), value.size());
|
||||
read_keys.insert(key_idx);
|
||||
}
|
||||
|
||||
double expected_read_amp =
|
||||
static_cast<double>(total_useful_bytes) /
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
double read_amp =
|
||||
static_cast<double>(options.statistics->getTickerCount(
|
||||
READ_AMP_ESTIMATE_USEFUL_BYTES)) /
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
double error_pct = fabs(expected_read_amp - read_amp) * 100;
|
||||
// Error between reported read amp and real read amp should be less than
|
||||
// 2%
|
||||
EXPECT_LE(error_pct, 2);
|
||||
}
|
||||
|
||||
double expected_read_amp =
|
||||
static_cast<double>(total_useful_bytes) /
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
// Make sure we read every thing in the DB (which is smaller than our cache)
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(iter->value().ToString(), Get(iter->key().ToString()));
|
||||
}
|
||||
delete iter;
|
||||
|
||||
double read_amp =
|
||||
static_cast<double>(options.statistics->getTickerCount(
|
||||
READ_AMP_ESTIMATE_USEFUL_BYTES)) /
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
double error_pct = fabs(expected_read_amp - read_amp) * 100;
|
||||
// Error between reported read amp and real read amp should be less than 2%
|
||||
EXPECT_LE(error_pct, 2);
|
||||
// Read amp is on average 100% since we read all what we loaded in memory
|
||||
if (k == 0) {
|
||||
ASSERT_EQ(
|
||||
options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES),
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES));
|
||||
} else {
|
||||
ASSERT_NEAR(
|
||||
options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES) *
|
||||
1.0f /
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES),
|
||||
1, .01);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we read every thing in the DB (which is smaller than our cache)
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(iter->value().ToString(), Get(iter->key().ToString()));
|
||||
}
|
||||
delete iter;
|
||||
|
||||
// Read amp is 100% since we read all what we loaded in memory
|
||||
ASSERT_EQ(options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES),
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
@@ -1997,82 +1723,93 @@ TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
// the blocks again regardless of them being already in the cache
|
||||
return;
|
||||
}
|
||||
uint32_t bytes_per_bit[2] = {1, 16};
|
||||
for (size_t k = 0; k < 2; k++) {
|
||||
std::shared_ptr<Cache> lru_cache = NewLRUCache(1024 * 1024 * 1024);
|
||||
std::shared_ptr<Statistics> stats = rocksdb::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<Cache> lru_cache = NewLRUCache(1024 * 1024 * 1024);
|
||||
std::shared_ptr<Statistics> stats = rocksdb::CreateDBStatistics();
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions bbto;
|
||||
// Disable delta encoding to make it easier to calculate read amplification
|
||||
bbto.use_delta_encoding = false;
|
||||
// Huge block cache to make it easier to calculate read amplification
|
||||
bbto.block_cache = lru_cache;
|
||||
bbto.read_amp_bytes_per_bit = bytes_per_bit[k];
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.statistics = stats;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions bbto;
|
||||
// Disable delta encoding to make it easier to calculate read amplification
|
||||
bbto.use_delta_encoding = false;
|
||||
// Huge block cache to make it easier to calculate read amplification
|
||||
bbto.block_cache = lru_cache;
|
||||
bbto.read_amp_bytes_per_bit = 16;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.statistics = stats;
|
||||
DestroyAndReopen(options);
|
||||
const int kNumEntries = 10000;
|
||||
|
||||
const int kNumEntries = 10000;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumEntries; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumEntries; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
Reopen(options);
|
||||
|
||||
Close();
|
||||
Reopen(options);
|
||||
uint64_t total_useful_bytes = 0;
|
||||
std::set<int> read_keys;
|
||||
std::string value;
|
||||
// Iter1: Read half the DB, Read even keys
|
||||
// Key(0), Key(2), Key(4), Key(6), Key(8), ...
|
||||
for (int i = 0; i < kNumEntries; i += 2) {
|
||||
std::string key = Key(i);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), key, &value));
|
||||
|
||||
uint64_t total_useful_bytes = 0;
|
||||
std::set<int> read_keys;
|
||||
std::string value;
|
||||
// Iter1: Read half the DB, Read even keys
|
||||
// Key(0), Key(2), Key(4), Key(6), Key(8), ...
|
||||
for (int i = 0; i < kNumEntries; i += 2) {
|
||||
std::string k = Key(i);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), k, &value));
|
||||
if (read_keys.find(i) == read_keys.end()) {
|
||||
auto internal_key = InternalKey(key, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes +=
|
||||
GetEncodedEntrySize(internal_key.size(), value.size());
|
||||
read_keys.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (read_keys.find(i) == read_keys.end()) {
|
||||
auto ik = InternalKey(k, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes += GetEncodedEntrySize(ik.size(), value.size());
|
||||
read_keys.insert(i);
|
||||
size_t total_useful_bytes_iter1 =
|
||||
options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES);
|
||||
size_t total_loaded_bytes_iter1 =
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
Close();
|
||||
std::shared_ptr<Statistics> new_statistics = rocksdb::CreateDBStatistics();
|
||||
// Destroy old statistics obj that the blocks in lru_cache are pointing to
|
||||
options.statistics.reset();
|
||||
// Use the statistics object that we just created
|
||||
options.statistics = new_statistics;
|
||||
Reopen(options);
|
||||
|
||||
// Iter2: Read half the DB, Read odd keys
|
||||
// Key(1), Key(3), Key(5), Key(7), Key(9), ...
|
||||
for (int i = 1; i < kNumEntries; i += 2) {
|
||||
std::string key = Key(i);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), key, &value));
|
||||
|
||||
if (read_keys.find(i) == read_keys.end()) {
|
||||
auto internal_key = InternalKey(key, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes +=
|
||||
GetEncodedEntrySize(internal_key.size(), value.size());
|
||||
read_keys.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
size_t total_useful_bytes_iter2 =
|
||||
options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES);
|
||||
size_t total_loaded_bytes_iter2 =
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
|
||||
// Read amp is on average 100% since we read all what we loaded in memory
|
||||
if (k == 0) {
|
||||
ASSERT_EQ(total_useful_bytes_iter1 + total_useful_bytes_iter2,
|
||||
total_loaded_bytes_iter1 + total_loaded_bytes_iter2);
|
||||
} else {
|
||||
ASSERT_NEAR((total_useful_bytes_iter1 + total_useful_bytes_iter2) * 1.0f /
|
||||
(total_loaded_bytes_iter1 + total_loaded_bytes_iter2),
|
||||
1, .01);
|
||||
}
|
||||
}
|
||||
|
||||
size_t total_useful_bytes_iter1 =
|
||||
options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES);
|
||||
size_t total_loaded_bytes_iter1 =
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
Close();
|
||||
std::shared_ptr<Statistics> new_statistics = rocksdb::CreateDBStatistics();
|
||||
// Destroy old statistics obj that the blocks in lru_cache are pointing to
|
||||
options.statistics.reset();
|
||||
// Use the statistics object that we just created
|
||||
options.statistics = new_statistics;
|
||||
Reopen(options);
|
||||
|
||||
// Iter2: Read half the DB, Read odd keys
|
||||
// Key(1), Key(3), Key(5), Key(7), Key(9), ...
|
||||
for (int i = 1; i < kNumEntries; i += 2) {
|
||||
std::string k = Key(i);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), k, &value));
|
||||
|
||||
if (read_keys.find(i) == read_keys.end()) {
|
||||
auto ik = InternalKey(k, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes += GetEncodedEntrySize(ik.size(), value.size());
|
||||
read_keys.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
size_t total_useful_bytes_iter2 =
|
||||
options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES);
|
||||
size_t total_loaded_bytes_iter2 =
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
// We reached read_amp of 100% because we read all the keys in the DB
|
||||
ASSERT_EQ(total_useful_bytes_iter1 + total_useful_bytes_iter2,
|
||||
total_loaded_bytes_iter1 + total_loaded_bytes_iter2);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -2283,7 +2020,8 @@ TEST_F(DBTest2, DirectIO) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.use_direct_reads = options.use_direct_writes = true;
|
||||
options.use_direct_reads = options.use_direct_io_for_flush_and_compaction =
|
||||
true;
|
||||
options.allow_mmap_reads = options.allow_mmap_writes = false;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ DBTestBase::DBTestBase(const std::string path)
|
||||
alternative_wal_dir_ = dbname_ + "/wal";
|
||||
alternative_db_log_dir_ = dbname_ + "/db_log_dir";
|
||||
auto options = CurrentOptions();
|
||||
options.env = env_;
|
||||
auto delete_options = options;
|
||||
delete_options.wal_dir = alternative_wal_dir_;
|
||||
EXPECT_OK(DestroyDB(dbname_, delete_options));
|
||||
@@ -72,6 +73,7 @@ DBTestBase::~DBTestBase() {
|
||||
options.db_paths.emplace_back(dbname_ + "_2", 0);
|
||||
options.db_paths.emplace_back(dbname_ + "_3", 0);
|
||||
options.db_paths.emplace_back(dbname_ + "_4", 0);
|
||||
options.env = env_;
|
||||
|
||||
if (getenv("KEEP_DB")) {
|
||||
printf("DB is still at %s\n", dbname_.c_str());
|
||||
@@ -228,6 +230,8 @@ bool DBTestBase::ChangeFilterOptions() {
|
||||
option_config_ = kFilter;
|
||||
} else if (option_config_ == kFilter) {
|
||||
option_config_ = kFullFilterWithNewTableReaderForCompactions;
|
||||
} else if (option_config_ == kFullFilterWithNewTableReaderForCompactions) {
|
||||
option_config_ = kPartitionedFilterWithNewTableReaderForCompactions;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -323,6 +327,14 @@ Options DBTestBase::CurrentOptions(
|
||||
options.new_table_reader_for_compaction_inputs = true;
|
||||
options.compaction_readahead_size = 10 * 1024 * 1024;
|
||||
break;
|
||||
case kPartitionedFilterWithNewTableReaderForCompactions:
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
table_options.partition_filters = true;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
options.new_table_reader_for_compaction_inputs = true;
|
||||
options.compaction_readahead_size = 10 * 1024 * 1024;
|
||||
break;
|
||||
case kUncompressed:
|
||||
options.compression = kNoCompression;
|
||||
break;
|
||||
@@ -424,6 +436,8 @@ Options DBTestBase::CurrentOptions(
|
||||
|
||||
if (options_override.filter_policy) {
|
||||
table_options.filter_policy = options_override.filter_policy;
|
||||
table_options.partition_filters = options_override.partition_filters;
|
||||
table_options.metadata_block_size = options_override.metadata_block_size;
|
||||
}
|
||||
if (set_block_based_table_factory) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
+11
-5
@@ -14,9 +14,6 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#ifndef OS_WIN
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
@@ -29,7 +26,7 @@
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -48,7 +45,7 @@
|
||||
#include "table/plain_table_factory.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/mock_env.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
#include "util/string_util.h"
|
||||
@@ -112,6 +109,11 @@ class AtomicCounter {
|
||||
|
||||
struct OptionsOverride {
|
||||
std::shared_ptr<const FilterPolicy> filter_policy = nullptr;
|
||||
// These will be used only if filter_policy is set
|
||||
bool partition_filters = false;
|
||||
uint64_t metadata_block_size = 1024;
|
||||
BlockBasedTableOptions::IndexType index_type =
|
||||
BlockBasedTableOptions::IndexType::kBinarySearch;
|
||||
|
||||
// Used as a bit mask of individual enums in which to skip an XF test point
|
||||
int skip_policy = 0;
|
||||
@@ -275,6 +277,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
bool use_direct_io() const override {
|
||||
return base_->use_direct_io();
|
||||
}
|
||||
Status Allocate(uint64_t offset, uint64_t len) override {
|
||||
return base_->Allocate(offset, len);
|
||||
}
|
||||
};
|
||||
class ManifestFile : public WritableFile {
|
||||
public:
|
||||
@@ -620,6 +625,7 @@ class DBTestBase : public testing::Test {
|
||||
kUniversalSubcompactions = 32,
|
||||
kBlockBasedTableWithIndexRestartInterval = 33,
|
||||
kBlockBasedTableWithPartitionedIndex = 34,
|
||||
kPartitionedFilterWithNewTableReaderForCompactions = 35,
|
||||
};
|
||||
int option_config_;
|
||||
|
||||
|
||||
+1
-1
@@ -8,10 +8,10 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/options_helper.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
+5
-4
@@ -14,9 +14,10 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "port/port.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -55,7 +56,7 @@ void AppendInternalKeyFooter(std::string* result, SequenceNumber s,
|
||||
|
||||
std::string ParsedInternalKey::DebugString(bool hex) const {
|
||||
char buf[50];
|
||||
snprintf(buf, sizeof(buf), "' @ %" PRIu64 ": %d", sequence,
|
||||
snprintf(buf, sizeof(buf), "' seq:%" PRIu64 ", type:%d", sequence,
|
||||
static_cast<int>(type));
|
||||
std::string result = "'";
|
||||
result += user_key.ToString(hex);
|
||||
@@ -128,7 +129,7 @@ void InternalKeyComparator::FindShortestSeparator(
|
||||
Slice user_limit = ExtractUserKey(limit);
|
||||
std::string tmp(user_start.data(), user_start.size());
|
||||
user_comparator_->FindShortestSeparator(&tmp, user_limit);
|
||||
if (tmp.size() < user_start.size() &&
|
||||
if (tmp.size() <= user_start.size() &&
|
||||
user_comparator_->Compare(user_start, tmp) < 0) {
|
||||
// User key has become shorter physically, but larger logically.
|
||||
// Tack on the earliest possible number to the shortened user key.
|
||||
@@ -143,7 +144,7 @@ void InternalKeyComparator::FindShortSuccessor(std::string* key) const {
|
||||
Slice user_key = ExtractUserKey(*key);
|
||||
std::string tmp(user_key.data(), user_key.size());
|
||||
user_comparator_->FindShortSuccessor(&tmp);
|
||||
if (tmp.size() < user_key.size() &&
|
||||
if (tmp.size() <= user_key.size() &&
|
||||
user_comparator_->Compare(user_key, tmp) < 0) {
|
||||
// User key has become shorter physically, but larger logically.
|
||||
// Tack on the earliest possible number to the shortened user key.
|
||||
|
||||
+45
-19
@@ -303,15 +303,26 @@ inline LookupKey::~LookupKey() {
|
||||
class IterKey {
|
||||
public:
|
||||
IterKey()
|
||||
: buf_(space_), buf_size_(sizeof(space_)), key_(buf_), key_size_(0) {}
|
||||
: buf_(space_),
|
||||
buf_size_(sizeof(space_)),
|
||||
key_(buf_),
|
||||
key_size_(0),
|
||||
is_user_key_(true) {}
|
||||
|
||||
~IterKey() { ResetBuffer(); }
|
||||
|
||||
Slice GetKey() const { return Slice(key_, key_size_); }
|
||||
Slice GetInternalKey() const {
|
||||
assert(!IsUserKey());
|
||||
return Slice(key_, key_size_);
|
||||
}
|
||||
|
||||
Slice GetUserKey() const {
|
||||
assert(key_size_ >= 8);
|
||||
return Slice(key_, key_size_ - 8);
|
||||
if (IsUserKey()) {
|
||||
return Slice(key_, key_size_);
|
||||
} else {
|
||||
assert(key_size_ >= 8);
|
||||
return Slice(key_, key_size_ - 8);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Size() const { return key_size_; }
|
||||
@@ -349,27 +360,22 @@ class IterKey {
|
||||
key_size_ = total_size;
|
||||
}
|
||||
|
||||
Slice SetKey(const Slice& key, bool copy = true) {
|
||||
size_t size = key.size();
|
||||
if (copy) {
|
||||
// Copy key to buf_
|
||||
EnlargeBufferIfNeeded(size);
|
||||
memcpy(buf_, key.data(), size);
|
||||
key_ = buf_;
|
||||
} else {
|
||||
// Update key_ to point to external memory
|
||||
key_ = key.data();
|
||||
}
|
||||
key_size_ = size;
|
||||
return Slice(key_, key_size_);
|
||||
Slice SetUserKey(const Slice& key, bool copy = true) {
|
||||
is_user_key_ = true;
|
||||
return SetKeyImpl(key, copy);
|
||||
}
|
||||
|
||||
Slice SetInternalKey(const Slice& key, bool copy = true) {
|
||||
is_user_key_ = false;
|
||||
return SetKeyImpl(key, copy);
|
||||
}
|
||||
|
||||
// Copies the content of key, updates the reference to the user key in ikey
|
||||
// and returns a Slice referencing the new copy.
|
||||
Slice SetKey(const Slice& key, ParsedInternalKey* ikey) {
|
||||
Slice SetInternalKey(const Slice& key, ParsedInternalKey* ikey) {
|
||||
size_t key_n = key.size();
|
||||
assert(key_n >= 8);
|
||||
SetKey(key);
|
||||
SetInternalKey(key);
|
||||
ikey->user_key = Slice(key_, key_n - 8);
|
||||
return Slice(key_, key_n);
|
||||
}
|
||||
@@ -408,6 +414,7 @@ class IterKey {
|
||||
|
||||
key_ = buf_;
|
||||
key_size_ = psize + usize + sizeof(uint64_t);
|
||||
is_user_key_ = false;
|
||||
}
|
||||
|
||||
void SetInternalKey(const Slice& user_key, SequenceNumber s,
|
||||
@@ -436,14 +443,33 @@ class IterKey {
|
||||
char* ptr = EncodeVarint32(buf_, static_cast<uint32_t>(size));
|
||||
memcpy(ptr, key.data(), size);
|
||||
key_ = buf_;
|
||||
is_user_key_ = true;
|
||||
}
|
||||
|
||||
bool IsUserKey() const { return is_user_key_; }
|
||||
|
||||
private:
|
||||
char* buf_;
|
||||
size_t buf_size_;
|
||||
const char* key_;
|
||||
size_t key_size_;
|
||||
char space_[32]; // Avoid allocation for short keys
|
||||
bool is_user_key_;
|
||||
|
||||
Slice SetKeyImpl(const Slice& key, bool copy) {
|
||||
size_t size = key.size();
|
||||
if (copy) {
|
||||
// Copy key to buf_
|
||||
EnlargeBufferIfNeeded(size);
|
||||
memcpy(buf_, key.data(), size);
|
||||
key_ = buf_;
|
||||
} else {
|
||||
// Update key_ to point to external memory
|
||||
key_ = key.data();
|
||||
}
|
||||
key_size_ = size;
|
||||
return Slice(key_, key_size_);
|
||||
}
|
||||
|
||||
void ResetBuffer() {
|
||||
if (buf_ != space_) {
|
||||
|
||||
+9
-9
@@ -139,38 +139,38 @@ TEST_F(FormatTest, IterKeyOperation) {
|
||||
const char p[] = "abcdefghijklmnopqrstuvwxyz";
|
||||
const char q[] = "0123456789";
|
||||
|
||||
ASSERT_EQ(std::string(k.GetKey().data(), k.GetKey().size()),
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string(""));
|
||||
|
||||
k.TrimAppend(0, p, 3);
|
||||
ASSERT_EQ(std::string(k.GetKey().data(), k.GetKey().size()),
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string("abc"));
|
||||
|
||||
k.TrimAppend(1, p, 3);
|
||||
ASSERT_EQ(std::string(k.GetKey().data(), k.GetKey().size()),
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string("aabc"));
|
||||
|
||||
k.TrimAppend(0, p, 26);
|
||||
ASSERT_EQ(std::string(k.GetKey().data(), k.GetKey().size()),
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string("abcdefghijklmnopqrstuvwxyz"));
|
||||
|
||||
k.TrimAppend(26, q, 10);
|
||||
ASSERT_EQ(std::string(k.GetKey().data(), k.GetKey().size()),
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string("abcdefghijklmnopqrstuvwxyz0123456789"));
|
||||
|
||||
k.TrimAppend(36, q, 1);
|
||||
ASSERT_EQ(std::string(k.GetKey().data(), k.GetKey().size()),
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string("abcdefghijklmnopqrstuvwxyz01234567890"));
|
||||
|
||||
k.TrimAppend(26, q, 1);
|
||||
ASSERT_EQ(std::string(k.GetKey().data(), k.GetKey().size()),
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string("abcdefghijklmnopqrstuvwxyz0"));
|
||||
|
||||
// Size going up, memory allocation is triggered
|
||||
k.TrimAppend(27, p, 26);
|
||||
ASSERT_EQ(std::string(k.GetKey().data(), k.GetKey().size()),
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string("abcdefghijklmnopqrstuvwxyz0"
|
||||
"abcdefghijklmnopqrstuvwxyz"));
|
||||
"abcdefghijklmnopqrstuvwxyz"));
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, UpdateInternalKey) {
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
@@ -41,8 +41,7 @@ class ExternalSSTFileBasicTest : public DBTestBase {
|
||||
const Options options, std::vector<int> keys, int file_id,
|
||||
std::map<std::string, std::string>* true_data) {
|
||||
std::string file_path = sst_files_dir_ + ToString(file_id);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator,
|
||||
nullptr);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
Status s = sst_file_writer.Open(file_path);
|
||||
if (!s.ok()) {
|
||||
@@ -78,7 +77,7 @@ class ExternalSSTFileBasicTest : public DBTestBase {
|
||||
TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Current file size should be 0 after sst_file_writer init and before open a
|
||||
// file.
|
||||
@@ -121,7 +120,7 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
||||
Options options = CurrentOptions();
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// file1.sst (0 => 99)
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
@@ -286,8 +285,8 @@ TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
||||
std::unique_ptr<SstFileWriter> sst_file_writer;
|
||||
|
||||
std::string sst_file_path = sst_files_dir_ + "file_fadvise_disable.sst";
|
||||
sst_file_writer.reset(new SstFileWriter(EnvOptions(), options,
|
||||
options.comparator, nullptr, false));
|
||||
sst_file_writer.reset(
|
||||
new SstFileWriter(EnvOptions(), options, nullptr, false));
|
||||
ASSERT_OK(sst_file_writer->Open(sst_file_path));
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
ASSERT_OK(sst_file_writer->Add(Key(i), Key(i)));
|
||||
@@ -298,8 +297,8 @@ TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
||||
|
||||
|
||||
sst_file_path = sst_files_dir_ + "file_fadvise_enable.sst";
|
||||
sst_file_writer.reset(new SstFileWriter(EnvOptions(), options,
|
||||
options.comparator, nullptr, true));
|
||||
sst_file_writer.reset(
|
||||
new SstFileWriter(EnvOptions(), options, nullptr, true));
|
||||
ASSERT_OK(sst_file_writer->Open(sst_file_path));
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
ASSERT_OK(sst_file_writer->Add(Key(i), Key(i)));
|
||||
|
||||
@@ -120,9 +120,9 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
}
|
||||
Status s = env_->DeleteFile(f.internal_file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,8 @@ void ExternalSstFileIngestionJob::UpdateStats() {
|
||||
if (f.picked_level == 0) {
|
||||
total_l0_files += 1;
|
||||
}
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[AddFile] External SST file %s was ingested in L%d with path %s "
|
||||
"(global_seqno=%" PRIu64 ")\n",
|
||||
f.external_file_path.c_str(), f.picked_level,
|
||||
@@ -233,9 +234,9 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
Status s = env_->DeleteFile(f.internal_file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
} else if (status.ok() && ingestion_options_.move_files) {
|
||||
@@ -243,7 +244,8 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
Status s = env_->DeleteFile(f.external_file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"%s was added to DB successfully but failed to remove original "
|
||||
"file link : %s",
|
||||
f.external_file_path.c_str(), s.ToString().c_str());
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
#include "db/dbformat.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
#include "options/db_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
@@ -54,8 +54,7 @@ class ExternalSSTFileTest : public DBTestBase {
|
||||
data.resize(uniq_iter - data.begin());
|
||||
}
|
||||
std::string file_path = sst_files_dir_ + ToString(file_id);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator,
|
||||
cfh);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, cfh);
|
||||
|
||||
Status s = sst_file_writer.Open(file_path);
|
||||
if (!s.ok()) {
|
||||
@@ -139,7 +138,7 @@ TEST_F(ExternalSSTFileTest, Basic) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Current file size should be 0 after sst_file_writer init and before open a file.
|
||||
ASSERT_EQ(sst_file_writer.FileSize(), 0);
|
||||
@@ -376,7 +375,7 @@ TEST_F(ExternalSSTFileTest, AddList) {
|
||||
options.table_properties_collector_factories.emplace_back(abc_collector);
|
||||
options.table_properties_collector_factories.emplace_back(xyz_collector);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// file1.sst (0 => 99)
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
@@ -565,7 +564,7 @@ TEST_F(ExternalSSTFileTest, AddListAtomicity) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// files[0].sst (0 => 99)
|
||||
// files[1].sst (100 => 199)
|
||||
@@ -608,7 +607,7 @@ TEST_F(ExternalSSTFileTest, AddListAtomicity) {
|
||||
// This situation may result in deleting the file while it's being added.
|
||||
TEST_F(ExternalSSTFileTest, PurgeObsoleteFilesBug) {
|
||||
Options options = CurrentOptions();
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// file1.sst (0 => 500)
|
||||
std::string sst_file_path = sst_files_dir_ + "file1.sst";
|
||||
@@ -653,7 +652,7 @@ TEST_F(ExternalSSTFileTest, PurgeObsoleteFilesBug) {
|
||||
TEST_F(ExternalSSTFileTest, SkipSnapshot) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// file1.sst (0 => 99)
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
@@ -744,7 +743,7 @@ TEST_F(ExternalSSTFileTest, MultiThreaded) {
|
||||
int range_start = file_idx * keys_per_file;
|
||||
int range_end = range_start + keys_per_file;
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
ASSERT_OK(sst_file_writer.Open(file_names[file_idx]));
|
||||
|
||||
@@ -842,7 +841,7 @@ TEST_F(ExternalSSTFileTest, OverlappingRanges) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
printf("Option config = %d\n", option_config_);
|
||||
std::vector<std::pair<int, int>> key_ranges;
|
||||
@@ -1252,7 +1251,7 @@ TEST_F(ExternalSSTFileTest, AddExternalSstFileWithCustomCompartor) {
|
||||
options.comparator = ReverseBytewiseComparator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Generate files with these key ranges
|
||||
// {14 -> 0}
|
||||
@@ -1354,7 +1353,7 @@ TEST_F(ExternalSSTFileTest, SstFileWriterNonSharedKeys) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
std::string file_path = sst_files_dir_ + "/not_shared";
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
std::string suffix(100, 'X');
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
@@ -1636,14 +1635,12 @@ TEST_F(ExternalSSTFileTest, DirtyExit) {
|
||||
std::unique_ptr<SstFileWriter> sst_file_writer;
|
||||
|
||||
// Destruct SstFileWriter without calling Finish()
|
||||
sst_file_writer.reset(
|
||||
new SstFileWriter(EnvOptions(), options, options.comparator));
|
||||
sst_file_writer.reset(new SstFileWriter(EnvOptions(), options));
|
||||
ASSERT_OK(sst_file_writer->Open(file_path));
|
||||
sst_file_writer.reset();
|
||||
|
||||
// Destruct SstFileWriter with a failing Finish
|
||||
sst_file_writer.reset(
|
||||
new SstFileWriter(EnvOptions(), options, options.comparator));
|
||||
sst_file_writer.reset(new SstFileWriter(EnvOptions(), options));
|
||||
ASSERT_OK(sst_file_writer->Open(file_path));
|
||||
ASSERT_NOK(sst_file_writer->Finish());
|
||||
}
|
||||
@@ -1652,11 +1649,10 @@ TEST_F(ExternalSSTFileTest, FileWithCFInfo) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"koko", "toto"}, options);
|
||||
|
||||
SstFileWriter sfw_default(EnvOptions(), options, options.comparator,
|
||||
handles_[0]);
|
||||
SstFileWriter sfw_cf1(EnvOptions(), options, options.comparator, handles_[1]);
|
||||
SstFileWriter sfw_cf2(EnvOptions(), options, options.comparator, handles_[2]);
|
||||
SstFileWriter sfw_unknown(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sfw_default(EnvOptions(), options, handles_[0]);
|
||||
SstFileWriter sfw_cf1(EnvOptions(), options, handles_[1]);
|
||||
SstFileWriter sfw_cf2(EnvOptions(), options, handles_[2]);
|
||||
SstFileWriter sfw_unknown(EnvOptions(), options);
|
||||
|
||||
// default_cf.sst
|
||||
const std::string cf_default_sst = sst_files_dir_ + "/default_cf.sst";
|
||||
@@ -1774,7 +1770,7 @@ TEST_F(ExternalSSTFileTest, SnapshotInconsistencyBug) {
|
||||
|
||||
// Overwrite all keys using IngestExternalFile
|
||||
std::string sst_file_path = sst_files_dir_ + "file1.sst";
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
ASSERT_OK(sst_file_writer.Open(sst_file_path));
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(i), Key(i) + "_V2"));
|
||||
|
||||
@@ -12,17 +12,17 @@
|
||||
// file data (or entire files) not protected by a "sync".
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mock_env.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
// 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/filename.h"
|
||||
#include "util/filename.h"
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "port/port.h"
|
||||
|
||||
+24
-19
@@ -22,15 +22,17 @@
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/likely.h"
|
||||
#include "port/port.h"
|
||||
#include "db/memtable.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
@@ -44,14 +46,12 @@
|
||||
#include "util/coding.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/iostats_context_imp.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/thread_status_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -158,8 +158,8 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
AutoThreadOperationStageUpdater stage_run(
|
||||
ThreadStatus::STAGE_FLUSH_RUN);
|
||||
if (mems_.empty()) {
|
||||
LogToBuffer(log_buffer_, "[%s] Nothing in memtable to flush",
|
||||
cfd_->GetName().c_str());
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Nothing in memtable to flush",
|
||||
cfd_->GetName().c_str());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -258,7 +258,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
for (MemTable* m : mems_) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Flushing memtable with next log file: %" PRIu64 "\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, m->GetNextLogNumber());
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
@@ -286,15 +287,18 @@ Status FlushJob::WriteLevel0Table() {
|
||||
&cfd_->internal_comparator(),
|
||||
range_del_iters.empty() ? nullptr : &range_del_iters[0],
|
||||
static_cast<int>(range_del_iters.size())));
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta_.fd.GetNumber());
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), job_context_->job_id,
|
||||
meta_.fd.GetNumber());
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:output_compression",
|
||||
&output_compression_);
|
||||
EnvOptions optimized_env_options =
|
||||
db_options_.env->OptimizeForCompactionTableWrite(env_options_, db_options_);
|
||||
s = BuildTable(
|
||||
dbname_, db_options_.env, *cfd_->ioptions(), mutable_cf_options_,
|
||||
env_options_, cfd_->table_cache(), iter.get(),
|
||||
optimized_env_options, cfd_->table_cache(), iter.get(),
|
||||
std::move(range_del_iter), &meta_, cfd_->internal_comparator(),
|
||||
cfd_->int_tbl_prop_collector_factories(), cfd_->GetID(),
|
||||
cfd_->GetName(), existing_snapshots_,
|
||||
@@ -305,13 +309,14 @@ Status FlushJob::WriteLevel0Table() {
|
||||
Env::IO_HIGH, &table_properties_, 0 /* level */);
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": %" PRIu64
|
||||
" bytes %s"
|
||||
"%s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta_.fd.GetNumber(),
|
||||
meta_.fd.GetFileSize(), s.ToString().c_str(),
|
||||
meta_.marked_for_compaction ? " (needs compaction)" : "");
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": %" PRIu64
|
||||
" bytes %s"
|
||||
"%s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id,
|
||||
meta_.fd.GetNumber(), meta_.fd.GetFileSize(),
|
||||
s.ToString().c_str(),
|
||||
meta_.marked_for_compaction ? " (needs compaction)" : "");
|
||||
|
||||
if (output_file_directory_ != nullptr) {
|
||||
output_file_directory_->Fsync();
|
||||
|
||||
+2
-2
@@ -27,6 +27,8 @@
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
#include "options/db_options.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -34,9 +36,7 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
|
||||
Loaded 100 of 439 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user