mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Compare commits
230 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| abd1fdddef | |||
| 3a6e83b56b | |||
| ec2b996b29 | |||
| 9f5cfb8e71 | |||
| 8a008d4170 | |||
| 22ce462450 | |||
| a3c1832e86 | |||
| 74fb7f0ba5 | |||
| 0f4d90e6e4 | |||
| 699a569c52 | |||
| 0acaa1a846 | |||
| 3bde41b5a3 | |||
| cd2520361d | |||
| 6e8a1354a7 | |||
| b0259e45e0 | |||
| f064d74e45 | |||
| 68d43b4d30 | |||
| 61876614dc | |||
| 3e9c5a3523 | |||
| 1a59b6e2a9 | |||
| 82d8ca8ade | |||
| 60d8b19836 | |||
| f786b4a5b4 | |||
| aa0367aabb | |||
| cb19e7411f | |||
| a6a9213a36 | |||
| 872a261ffc | |||
| 6ca3feed5c | |||
| 7c76a7fba2 | |||
| 8d34806972 | |||
| e0d9d57750 | |||
| 2de61d9129 | |||
| 4f66ec977d | |||
| e4dcf5fd22 | |||
| 6edc5d0719 | |||
| 84c5c9aab1 | |||
| 09ea5d8944 | |||
| 0d57d93a06 | |||
| 662ce62044 | |||
| cfdf2116d3 | |||
| 66464d1fde | |||
| 3e6c185381 | |||
| 1e87f2b68b | |||
| f872009237 | |||
| 9f0bd56889 | |||
| 3886dddc3b | |||
| c360675750 | |||
| 7259e28d91 | |||
| 68b46a2e36 | |||
| 10bae8ceb3 | |||
| 5c2f13fb14 | |||
| 15fd3be07b | |||
| c08c0ae731 | |||
| a8975b6245 | |||
| 9dbcda9e3b | |||
| b4d7209428 | |||
| 554a6456aa | |||
| acb80534ca | |||
| e731f44022 | |||
| c92c58f84d | |||
| 68980df89c | |||
| 22028aa9ab | |||
| 2730fe693e | |||
| 1bfeffab2d | |||
| 705b8eecb4 | |||
| 0b0cb6f1a2 | |||
| 24f73436fb | |||
| 68614a9608 | |||
| 5830c619d5 | |||
| 24b118ad98 | |||
| fe90ed7a70 | |||
| 5355e527d9 | |||
| 2e8ad03ab3 | |||
| f46a2a0375 | |||
| 92f631da33 | |||
| d0c6aea192 | |||
| 220870523c | |||
| 5dc9fbd117 | |||
| 4bd0cf541d | |||
| f287f8dc93 | |||
| ddd088c8b9 | |||
| bcfc53b436 | |||
| 2d1dd5bce7 | |||
| 7d8d56413d | |||
| 671d15cbdd | |||
| ee294c24ed | |||
| d43b4cd570 | |||
| d1ae67bdb9 | |||
| 7a8d7358bb | |||
| f1219644ec | |||
| b47cfec5d0 | |||
| 58c78358ef | |||
| 89695bfbaa | |||
| bb4178066d | |||
| a3b8c76d8e | |||
| 2c9df9f9e5 | |||
| ec8111c5a4 | |||
| 5c76ba9dc4 | |||
| 60f3ec2ca5 | |||
| f9842869cf | |||
| f43edff9ac | |||
| 4a285d0dd3 | |||
| 773f914a40 | |||
| ca1aee2a19 | |||
| ba64a4cf52 | |||
| 7177dc46a1 | |||
| 9bbccda01e | |||
| 58c4aee42e | |||
| a94aef6596 | |||
| c8c1a549f0 | |||
| b2584577fa | |||
| 641cc8d541 | |||
| 5efa0d6b0d | |||
| 63ace8ef0e | |||
| 6ce5580882 | |||
| c292dc8540 | |||
| a16d0cc494 | |||
| b703a56e5c | |||
| 0f48e56f96 | |||
| ad52626cf4 | |||
| fd94353ea3 | |||
| d68f9f4580 | |||
| bee2f48a66 | |||
| aa71718ac3 | |||
| 340ed4fac7 | |||
| cb1bf09bfc | |||
| 267b9b1091 | |||
| 0153e14569 | |||
| 227b5d52df | |||
| ebe89ef9d8 | |||
| c8267120d8 | |||
| 5d6e8df1cf | |||
| ae05a83e19 | |||
| 5851cb7fdb | |||
| 349db90497 | |||
| 000b9ec217 | |||
| 79edf0a7a8 | |||
| d7d8605f56 | |||
| cae22c53fb | |||
| 49c5a12dbe | |||
| a3609b7dde | |||
| cb094e13bb | |||
| 0834bbd0b1 | |||
| 83f7a8eed0 | |||
| ab8f6c01a6 | |||
| ff9d286877 | |||
| b9f5900658 | |||
| 8843129ece | |||
| f1302ebab8 | |||
| 1b59a490ef | |||
| 50e470791d | |||
| e62986260f | |||
| 1e35584251 | |||
| bd44ec2006 | |||
| e9e0101ca4 | |||
| a984040f0b | |||
| 87fe4bcab8 | |||
| 545d206040 | |||
| eab4f49a2c | |||
| f5576c3317 | |||
| 4d0c3b1f96 | |||
| b5e4ee2e76 | |||
| e264eebcd7 | |||
| bd9f1d2d0f | |||
| b09c018b4d | |||
| a466120cd5 | |||
| 029b98984e | |||
| eb7647ee6c | |||
| 6267ed251a | |||
| f66026c8c7 | |||
| f69e63dc5f | |||
| 596cc1547a | |||
| 767d1f3ff1 | |||
| 98094f6cac | |||
| 88ff80780b | |||
| 94c78b11e4 | |||
| 5d359fc337 | |||
| 74a334a2eb | |||
| 6a54278b4a | |||
| 09b534cc2f | |||
| 38a06aa225 | |||
| 02830a20f8 | |||
| dc30a9b69b | |||
| 40aa520a51 | |||
| 2095ae8858 | |||
| 3d9d77d900 | |||
| 518cd1a62a | |||
| dda474399a | |||
| b2274da0e5 | |||
| cd43446d01 | |||
| 931c9df886 | |||
| 5c0e304170 | |||
| fb4c6a31ce | |||
| f3a7847598 | |||
| a13026fb2f | |||
| c71f5bb9aa | |||
| f82e693a31 | |||
| 29a198564d | |||
| 1583cb402e | |||
| 8149bb9d6a | |||
| 468ca61105 | |||
| f0e8216197 | |||
| ad27045d14 | |||
| da7c89d79d | |||
| a42757607d | |||
| 189e711b37 | |||
| 3c3252a06a | |||
| 6492430eaf | |||
| f383641a1d | |||
| 92c60547fe | |||
| e626016545 | |||
| 6a6aef25c1 | |||
| f0bf3bf34b | |||
| 6451673f37 | |||
| 9fad3e21eb | |||
| 181bb43f08 | |||
| 25d81e4577 | |||
| bdba6c56dd | |||
| eea1cad850 | |||
| 930bfa5750 | |||
| 6a40ee5eb1 | |||
| 5d27d65bef | |||
| 3e994809a1 | |||
| 5882e847aa | |||
| 434ccf2df4 | |||
| d51eb0b583 | |||
| 4479dff208 | |||
| 4e0f2aadb0 | |||
| 521d234bda | |||
| 36ea379cdc |
@@ -32,6 +32,7 @@ ldb
|
||||
manifest_dump
|
||||
sst_dump
|
||||
blob_dump
|
||||
block_cache_trace_analyzer
|
||||
column_aware_encoding_exp
|
||||
util/build_version.cc
|
||||
build_tools/VALGRIND_LOGS/
|
||||
@@ -47,6 +48,7 @@ rocksdb_undump
|
||||
db_test2
|
||||
trace_analyzer
|
||||
trace_analyzer_test
|
||||
.DS_Store
|
||||
|
||||
java/out
|
||||
java/target
|
||||
|
||||
+8
-5
@@ -1,5 +1,4 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
@@ -9,7 +8,7 @@ compiler:
|
||||
- gcc
|
||||
osx_image: xcode8.3
|
||||
jdk:
|
||||
- oraclejdk7
|
||||
- openjdk7
|
||||
cache:
|
||||
- ccache
|
||||
- apt
|
||||
@@ -71,7 +70,10 @@ install:
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
mkdir cmake-dist && curl -sfSL https://cmake.org/files/v3.8/cmake-3.8.1-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
mkdir cmake-dist && curl -sfSL https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == java_test ]]; then
|
||||
java -version && echo "JAVA_HOME=${JAVA_HOME}";
|
||||
fi
|
||||
|
||||
before_script:
|
||||
@@ -101,7 +103,7 @@ script:
|
||||
esac
|
||||
- case $JOB_NAME in
|
||||
java_test)
|
||||
OPT=-DTRAVIS V=1 make clean jclean && make rocksdbjava jtest
|
||||
OPT=-DTRAVIS V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 static_lib tools
|
||||
@@ -110,6 +112,7 @@ script:
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4
|
||||
;;
|
||||
cmake-mingw)
|
||||
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix;
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
cmake*)
|
||||
|
||||
+108
-71
@@ -44,6 +44,12 @@ endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif(CCACHE_FOUND)
|
||||
|
||||
option(WITH_JEMALLOC "build with JeMalloc" OFF)
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
option(WITH_LZ4 "build with lz4" OFF)
|
||||
@@ -177,6 +183,7 @@ else()
|
||||
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 -Wno-strict-aliasing")
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
@@ -201,6 +208,15 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
endif(HAS_ALTIVEC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
CHECK_C_COMPILER_FLAG("-march=armv8-a+crc" HAS_ARMV8_CRC)
|
||||
if(HAS_ARMV8_CRC)
|
||||
message(STATUS " HAS_ARMV8_CRC yes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+crc -Wno-unused-function")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+crc -Wno-unused-function")
|
||||
endif(HAS_ARMV8_CRC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
@@ -213,7 +229,7 @@ else()
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
if(NOT HAVE_POWER8)
|
||||
if(NOT HAVE_POWER8 AND NOT HAS_ARMV8_CRC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
@@ -317,6 +333,10 @@ if(DISABLE_STALL_NOTIF)
|
||||
add_definitions(-DROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
endif()
|
||||
|
||||
option(WITH_DYNAMIC_EXTENSION "build with dynamic extension support" OFF)
|
||||
if(NOT WITH_DYNAMIC_EXTENSION)
|
||||
add_definitions(-DROCKSDB_NO_DYNAMIC_EXTENSION)
|
||||
endif()
|
||||
|
||||
if(DEFINED USE_RTTI)
|
||||
if(USE_RTTI)
|
||||
@@ -471,23 +491,24 @@ set(SOURCES
|
||||
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_fifo.cc
|
||||
db/compaction_picker_universal.cc
|
||||
db/compaction/compaction.cc
|
||||
db/compaction/compaction_iterator.cc
|
||||
db/compaction/compaction_picker.cc
|
||||
db/compaction/compaction_job.cc
|
||||
db/compaction/compaction_picker_fifo.cc
|
||||
db/compaction/compaction_picker_level.cc
|
||||
db/compaction/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_impl_secondary.cc
|
||||
db/db_impl/db_impl.cc
|
||||
db/db_impl/db_impl_write.cc
|
||||
db/db_impl/db_impl_compaction_flush.cc
|
||||
db/db_impl/db_impl_files.cc
|
||||
db/db_impl/db_impl_open.cc
|
||||
db/db_impl/db_impl_debug.cc
|
||||
db/db_impl/db_impl_experimental.cc
|
||||
db/db_impl/db_impl_readonly.cc
|
||||
db/db_impl/db_impl_secondary.cc
|
||||
db/db_info_dumper.cc
|
||||
db/db_iter.cc
|
||||
db/dbformat.cc
|
||||
@@ -499,8 +520,8 @@ set(SOURCES
|
||||
db/flush_job.cc
|
||||
db/flush_scheduler.cc
|
||||
db/forward_iterator.cc
|
||||
db/import_column_family_job.cc
|
||||
db/internal_stats.cc
|
||||
db/in_memory_stats_history.cc
|
||||
db/logs_with_prep_tracker.cc
|
||||
db/log_reader.cc
|
||||
db/log_writer.cc
|
||||
@@ -529,6 +550,16 @@ set(SOURCES
|
||||
env/env_encryption.cc
|
||||
env/env_hdfs.cc
|
||||
env/mock_env.cc
|
||||
file/delete_scheduler.cc
|
||||
file/file_util.cc
|
||||
file/filename.cc
|
||||
file/sst_file_manager_impl.cc
|
||||
logging/auto_roll_logger.cc
|
||||
logging/event_logger.cc
|
||||
logging/log_buffer.cc
|
||||
memory/arena.cc
|
||||
memory/concurrent_arena.cc
|
||||
memory/jemalloc_nodump_allocator.cc
|
||||
memtable/alloc_tracker.cc
|
||||
memtable/hash_linklist_rep.cc
|
||||
memtable/hash_skiplist_rep.cc
|
||||
@@ -537,10 +568,12 @@ set(SOURCES
|
||||
memtable/write_buffer_manager.cc
|
||||
monitoring/histogram.cc
|
||||
monitoring/histogram_windowing.cc
|
||||
monitoring/in_memory_stats_history.cc
|
||||
monitoring/instrumented_mutex.cc
|
||||
monitoring/iostats_context.cc
|
||||
monitoring/perf_context.cc
|
||||
monitoring/perf_level.cc
|
||||
monitoring/persistent_stats_history.cc
|
||||
monitoring/statistics.cc
|
||||
monitoring/thread_status_impl.cc
|
||||
monitoring/thread_status_updater.cc
|
||||
@@ -553,80 +586,73 @@ set(SOURCES
|
||||
options/options_parser.cc
|
||||
options/options_sanity_check.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/adaptive/adaptive_table_factory.cc
|
||||
table/block_based/block.cc
|
||||
table/block_based/block_based_filter_block.cc
|
||||
table/block_based/block_based_table_builder.cc
|
||||
table/block_based/block_based_table_factory.cc
|
||||
table/block_based/block_based_table_reader.cc
|
||||
table/block_based/block_builder.cc
|
||||
table/block_based/block_prefix_index.cc
|
||||
table/block_based/data_block_hash_index.cc
|
||||
table/block_based/data_block_footer.cc
|
||||
table/block_based/filter_block_reader_common.cc
|
||||
table/block_based/flush_block_policy.cc
|
||||
table/block_based/full_filter_block.cc
|
||||
table/block_based/index_builder.cc
|
||||
table/block_based/partitioned_filter_block.cc
|
||||
table/block_fetcher.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/data_block_hash_index.cc
|
||||
table/data_block_footer.cc
|
||||
table/flush_block_policy.cc
|
||||
table/cuckoo/cuckoo_table_builder.cc
|
||||
table/cuckoo/cuckoo_table_factory.cc
|
||||
table/cuckoo/cuckoo_table_reader.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/plain/plain_table_builder.cc
|
||||
table/plain/plain_table_factory.cc
|
||||
table/plain/plain_table_index.cc
|
||||
table/plain/plain_table_key_coding.cc
|
||||
table/plain/plain_table_reader.cc
|
||||
table/sst_file_reader.cc
|
||||
table/sst_file_writer.cc
|
||||
table/table_properties.cc
|
||||
table/two_level_iterator.cc
|
||||
test_util/sync_point.cc
|
||||
test_util/sync_point_impl.cc
|
||||
test_util/testutil.cc
|
||||
test_util/transaction_test_util.cc
|
||||
tools/block_cache_trace_analyzer.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
|
||||
tools/trace_analyzer_tool.cc
|
||||
util/arena.cc
|
||||
util/auto_roll_logger.cc
|
||||
trace_replay/trace_replay.cc
|
||||
trace_replay/block_cache_tracer.cc
|
||||
util/bloom.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
util/compression_context_cache.cc
|
||||
util/concurrent_arena.cc
|
||||
util/concurrent_task_limiter_impl.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/jemalloc_nodump_allocator.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/string_util.cc
|
||||
util/sync_point.cc
|
||||
util/sync_point_impl.cc
|
||||
util/testutil.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
util/trace_replay.cc
|
||||
util/transaction_test_util.cc
|
||||
util/xxhash.cc
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/blob_db/blob_compaction_filter.cc
|
||||
@@ -661,6 +687,7 @@ set(SOURCES
|
||||
utilities/persistent_cache/block_cache_tier_metadata.cc
|
||||
utilities/persistent_cache/persistent_cache_tier.cc
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/simulator_cache/cache_simulator.cc
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
@@ -694,6 +721,11 @@ if(HAVE_POWER8)
|
||||
util/crc32c_ppc_asm.S)
|
||||
endif(HAVE_POWER8)
|
||||
|
||||
if(HAS_ARMV8_CRC)
|
||||
list(APPEND SOURCES
|
||||
util/crc32c_arm64.cc)
|
||||
endif(HAS_ARMV8_CRC)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES
|
||||
port/win/io_win.cc
|
||||
@@ -847,10 +879,10 @@ if(WITH_TESTS)
|
||||
cache/lru_cache_test.cc
|
||||
db/column_family_test.cc
|
||||
db/compact_files_test.cc
|
||||
db/compaction_iterator_test.cc
|
||||
db/compaction_job_stats_test.cc
|
||||
db/compaction_job_test.cc
|
||||
db/compaction_picker_test.cc
|
||||
db/compaction/compaction_job_stats_test.cc
|
||||
db/compaction/compaction_job_test.cc
|
||||
db/compaction/compaction_iterator_test.cc
|
||||
db/compaction/compaction_picker_test.cc
|
||||
db/comparator_db_test.cc
|
||||
db/corruption_test.cc
|
||||
db/cuckoo_table_db_test.cc
|
||||
@@ -873,7 +905,7 @@ if(WITH_TESTS)
|
||||
db/db_options_test.cc
|
||||
db/db_properties_test.cc
|
||||
db/db_range_del_test.cc
|
||||
db/db_secondary_test.cc
|
||||
db/db_impl/db_secondary_test.cc
|
||||
db/db_sst_test.cc
|
||||
db/db_statistics_test.cc
|
||||
db/db_table_properties_test.cc
|
||||
@@ -917,37 +949,41 @@ if(WITH_TESTS)
|
||||
env/env_basic_test.cc
|
||||
env/env_test.cc
|
||||
env/mock_env_test.cc
|
||||
file/delete_scheduler_test.cc
|
||||
logging/auto_roll_logger_test.cc
|
||||
logging/env_logger_test.cc
|
||||
logging/event_logger_test.cc
|
||||
memory/arena_test.cc
|
||||
memtable/inlineskiplist_test.cc
|
||||
memtable/skiplist_test.cc
|
||||
memtable/write_buffer_manager_test.cc
|
||||
monitoring/histogram_test.cc
|
||||
monitoring/iostats_context_test.cc
|
||||
monitoring/statistics_test.cc
|
||||
monitoring/stats_history_test.cc
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based_filter_block_test.cc
|
||||
table/block_test.cc
|
||||
table/block_based/block_based_filter_block_test.cc
|
||||
table/block_based/block_test.cc
|
||||
table/block_based/data_block_hash_index_test.cc
|
||||
table/block_based/full_filter_block_test.cc
|
||||
table/block_based/partitioned_filter_block_test.cc
|
||||
table/cleanable_test.cc
|
||||
table/cuckoo_table_builder_test.cc
|
||||
table/cuckoo_table_reader_test.cc
|
||||
table/data_block_hash_index_test.cc
|
||||
table/full_filter_block_test.cc
|
||||
table/cuckoo/cuckoo_table_builder_test.cc
|
||||
table/cuckoo/cuckoo_table_reader_test.cc
|
||||
table/merger_test.cc
|
||||
table/sst_file_reader_test.cc
|
||||
table/table_test.cc
|
||||
tools/block_cache_trace_analyzer_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
tools/sst_dump_test.cc
|
||||
tools/trace_analyzer_test.cc
|
||||
util/arena_test.cc
|
||||
util/auto_roll_logger_test.cc
|
||||
util/autovector_test.cc
|
||||
util/bloom_test.cc
|
||||
util/coding_test.cc
|
||||
util/crc32c_test.cc
|
||||
util/delete_scheduler_test.cc
|
||||
util/dynamic_bloom_test.cc
|
||||
util/event_logger_test.cc
|
||||
util/file_reader_writer_test.cc
|
||||
util/filelock_test.cc
|
||||
util/hash_test.cc
|
||||
@@ -972,6 +1008,7 @@ if(WITH_TESTS)
|
||||
utilities/options/options_util_test.cc
|
||||
utilities/persistent_cache/hash_table_test.cc
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/simulator_cache/cache_simulator_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
@@ -992,7 +1029,7 @@ if(WITH_TESTS)
|
||||
tools/db_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
add_library(testharness OBJECT util/testharness.cc)
|
||||
add_library(testharness OBJECT test_util/testharness.cc)
|
||||
foreach(sourcefile ${BENCHMARKS})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
@@ -1006,7 +1043,7 @@ if(WITH_TESTS)
|
||||
db/db_test_util.cc
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
util/fault_injection_test_env.cc
|
||||
test_util/fault_injection_test_env.cc
|
||||
utilities/cassandra/test_utils.cc
|
||||
)
|
||||
# test utilities are only build in debug
|
||||
|
||||
+50
-1
@@ -1,13 +1,61 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Default Option Change
|
||||
* LRUCacheOptions.high_pri_pool_ratio is set to 0.5 (previously 0.0) by default, which means that by default midpoint insertion is enabled. The same change is made for the default value of high_pri_pool_ratio argument in NewLRUCache(). When block cache is not explictly created, the small block cache created by BlockBasedTable will still has this option to be 0.0.
|
||||
* Change BlockBasedTableOptions.cache_index_and_filter_blocks_with_high_priority's default value from false to true.
|
||||
|
||||
### Public API Change
|
||||
* Now DB::Close() will return Aborted() error when there is unreleased snapshot. Users can retry after all snapshots are released.
|
||||
* Index and filter blocks are now handled similarly to data blocks with regards to the block cache: instead of storing reader objects in the cache, only the blocks themselves are cached. In addition, index and filter blocks (as well as filter partitions) no longer get evicted from the cache when a table is closed. Moreover, index blocks can now use the compressed block cache (if any).
|
||||
* Partitions of partitioned indexes no longer affect the read amplification statistics.
|
||||
* Due to the above refactoring, block cache eviction statistics for indexes and filters are temporarily broken. We plan to reintroduce them in a later phase.
|
||||
* options.keep_log_file_num will be enforced strictly all the time. File names of all log files will be tracked, which may take significantly amount of memory if options.keep_log_file_num is large and either of options.max_log_file_size or options.log_file_time_to_roll is set.
|
||||
* Add initial support for Get/Put with user timestamps. Users can specify timestamps via ReadOptions and WriteOptions when calling DB::Get and DB::Put.
|
||||
* Accessing a partition of a partitioned filter or index through a pinned reference is no longer considered a cache hit.
|
||||
* The semantics of the per-block-type block read counts in the performance context now match those of the generic block_read_count.
|
||||
* Add C bindings for secondary instance, i.e. DBImplSecondary.
|
||||
* db_bench adds a "benchmark" stats_history, which prints out the whole stats history.
|
||||
* Rate limited deletion of WALs is only enabled if DBOptions::wal_dir is not set, or explicitly set to db_name passed to DB::Open and DBOptions::db_paths is empty, or same as db_paths[0].path
|
||||
* Overload GetAllKeyVersions() to support non-default column family.
|
||||
* Added new APIs ExportColumnFamily() and CreateColumnFamilyWithImport() to support export and import of a Column Family. https://github.com/facebook/rocksdb/issues/3469
|
||||
|
||||
### New Features
|
||||
* Add an option `snap_refresh_nanos` (default to 0.1s) to periodically refresh the snapshot list in compaction jobs. Assign to 0 to disable the feature.
|
||||
* Add an option `unordered_write` which trades snapshot guarantees with higher write throughput. When used with WRITE_PREPARED transactions with two_write_queues=true, it offers higher throughput with however no compromise on guarantees.
|
||||
* Allow DBImplSecondary to remove memtables with obsolete data after replaying MANIFEST and WAL.
|
||||
* Add an option `failed_move_fall_back_to_copy` (default is true) for external SST ingestion. When `move_files` is true and hard link fails, ingestion falls back to copy if `failed_move_fall_back_to_copy` is true. Otherwise, ingestion reports an error.
|
||||
* Add argument `--secondary_path` to ldb to open the database as the secondary instance. This would keep the original DB intact.
|
||||
|
||||
### Performance Improvements
|
||||
* Reduce binary search when iterator reseek into the same data block.
|
||||
* DBIter::Next() can skip user key checking if previous entry's seqnum is 0.
|
||||
* Merging iterator to avoid child iterator reseek for some cases
|
||||
* Reduce iterator key comparision for upper/lower bound check.
|
||||
* Log Writer will flush after finishing the whole record, rather than a fragment.
|
||||
* Lower MultiGet batching API latency by reading data blocks from disk in parallel
|
||||
|
||||
### General Improvements
|
||||
* Added new status code kColumnFamilyDropped to distinguish between Column Family Dropped and DB Shutdown in progress.
|
||||
* Improve ColumnFamilyOptions validation when creating a new column family.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in WAL replay of secondary instance by skipping write batches with older sequence numbers than the current last sequence number.
|
||||
* Fix flush's/compaction's merge processing logic which allowed `Put`s covered by range tombstones to reappear. Note `Put`s may exist even if the user only ever called `Merge()` due to an internal conversion during compaction to the bottommost level.
|
||||
* Fix/improve memtable earliest sequence assignment and WAL replay so that WAL entries of unflushed column families will not be skipped after replaying the MANIFEST and increasing db sequence due to another flushed/compacted column family.
|
||||
* Fix ingested file and directory not being fsync.
|
||||
* Return TryAgain status in place of Corruption when new tail is not visible to TransactionLogIterator.
|
||||
* Fix a bug caused by secondary not skipping the beginning of new MANIFEST.
|
||||
* On DB open, delete WAL trash files left behind in wal_dir
|
||||
|
||||
## 6.2.0 (4/30/2019)
|
||||
### New Features
|
||||
* Add an option `strict_bytes_per_sync` that causes a file-writing thread to block rather than exceed the limit on bytes pending writeback specified by `bytes_per_sync` or `wal_bytes_per_sync`.
|
||||
* Improve range scan performance by avoiding per-key upper bound check in BlockBasedTableIterator.
|
||||
* Introduce Periodic Compaction for Level style compaction. Files are re-compacted periodically and put in the same level.
|
||||
* Block-based table index now contains exact highest key in the file, rather than an upper bound. This may improve Get() and iterator Seek() performance in some situations, especially when direct IO is enabled and block cache is disabled. A setting BlockBasedTableOptions::index_shortening is introduced to control this behavior. Set it to kShortenSeparatorsAndSuccessor to get the old behavior.
|
||||
* When reading from option file/string/map, customized envs can be filled according to object registry.
|
||||
* Add an option `snap_refresh_nanos` (default to 0.5s) to periodically refresh the snapshot list in compaction jobs. Assign to 0 to disable the feature.
|
||||
* Improve range scan performance when using explicit user readahead by not creating new table readers for every iterator.
|
||||
* Add index type BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey. It significantly reduces read amplification in some setups, especially for iterator seeks. It's not fully implemented yet: IO errors are not handled right.
|
||||
|
||||
### Public API Change
|
||||
* Change the behavior of OptimizeForPointLookup(): move away from hash-based block-based-table index, and use whole key memtable filtering.
|
||||
@@ -19,6 +67,7 @@
|
||||
* Fix a race condition between WritePrepared::Get and ::Put with duplicate keys.
|
||||
* Fix crash when memtable prefix bloom is enabled and read/write a key out of domain of prefix extractor.
|
||||
* Close a WAL file before another thread deletes it.
|
||||
* Fix an assertion failure `IsFlushPending() == true` caused by one bg thread releasing the db mutex in ~ColumnFamilyData and another thread clearing `flush_requested_` flag.
|
||||
|
||||
## 6.1.1 (4/9/2019)
|
||||
### New Features
|
||||
|
||||
@@ -82,17 +82,23 @@ ifeq ($(MAKECMDGOALS),rocksdbjavastatic)
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticrelease)
|
||||
DEBUG_LEVEL=0
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticreleasedocker)
|
||||
DEBUG_LEVEL=0
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticpublish)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
$(info $$DEBUG_LEVEL is ${DEBUG_LEVEL})
|
||||
|
||||
# Lite build flag.
|
||||
LITE ?= 0
|
||||
ifeq ($(LITE), 0)
|
||||
@@ -138,8 +144,8 @@ HAVE_POWER8=1
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -march=armv8-a+crc -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -march=armv8-a+crc
|
||||
CFLAGS += -march=armv8-a+crc
|
||||
CXXFLAGS += -march=armv8-a+crc+crypto
|
||||
CFLAGS += -march=armv8-a+crc+crypto
|
||||
ARMCRC_SOURCE=1
|
||||
endif
|
||||
|
||||
@@ -398,8 +404,8 @@ LIBOBJECTS += $(TOOL_LIB_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_LIB_SOURCES:.cc=.o)
|
||||
|
||||
GTEST = $(GTEST_DIR)/gtest/gtest-all.o
|
||||
TESTUTIL = ./util/testutil.o
|
||||
TESTHARNESS = ./util/testharness.o $(TESTUTIL) $(MOCKOBJECTS) $(GTEST)
|
||||
TESTUTIL = ./test_util/testutil.o
|
||||
TESTHARNESS = ./test_util/testharness.o $(TESTUTIL) $(MOCKOBJECTS) $(GTEST)
|
||||
VALGRIND_ERROR = 2
|
||||
VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
|
||||
|
||||
@@ -426,6 +432,7 @@ TESTS = \
|
||||
inlineskiplist_test \
|
||||
env_basic_test \
|
||||
env_test \
|
||||
env_logger_test \
|
||||
hash_test \
|
||||
thread_local_test \
|
||||
rate_limiter_test \
|
||||
@@ -493,6 +500,7 @@ TESTS = \
|
||||
plain_table_db_test \
|
||||
comparator_db_test \
|
||||
external_sst_file_test \
|
||||
import_column_family_test \
|
||||
prefix_test \
|
||||
skiplist_test \
|
||||
write_buffer_manager_test \
|
||||
@@ -503,6 +511,7 @@ TESTS = \
|
||||
cassandra_serialize_test \
|
||||
ttl_test \
|
||||
backupable_db_test \
|
||||
cache_simulator_test \
|
||||
sim_cache_test \
|
||||
version_edit_test \
|
||||
version_set_test \
|
||||
@@ -542,6 +551,7 @@ TESTS = \
|
||||
ldb_cmd_test \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
stats_history_test \
|
||||
lru_cache_test \
|
||||
object_registry_test \
|
||||
repair_test \
|
||||
@@ -555,6 +565,8 @@ TESTS = \
|
||||
range_del_aggregator_test \
|
||||
sst_file_reader_test \
|
||||
db_secondary_test \
|
||||
block_cache_tracer_test \
|
||||
block_cache_trace_analyzer_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -566,6 +578,7 @@ PARALLEL_TEST = \
|
||||
db_universal_compaction_test \
|
||||
db_wal_test \
|
||||
external_sst_file_test \
|
||||
import_column_family_test \
|
||||
fault_injection_test \
|
||||
inlineskiplist_test \
|
||||
manual_compaction_test \
|
||||
@@ -599,6 +612,7 @@ TOOLS = \
|
||||
rocksdb_undump \
|
||||
blob_dump \
|
||||
trace_analyzer \
|
||||
block_cache_trace_analyzer \
|
||||
|
||||
TEST_LIBS = \
|
||||
librocksdb_env_basic_test.a
|
||||
@@ -1100,6 +1114,9 @@ db_bench: tools/db_bench.o $(BENCHTOOLOBJECTS)
|
||||
trace_analyzer: tools/trace_analyzer.o $(ANALYZETOOLOBJECTS) $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_trace_analyzer: tools/block_cache_trace_analyzer_tool.o $(ANALYZETOOLOBJECTS) $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_bench: cache/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1121,7 +1138,7 @@ db_sanity_test: tools/db_sanity_test.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
db_repl_stress: tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
arena_test: memory/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1259,6 +1276,9 @@ external_sst_file_basic_test: db/external_sst_file_basic_test.o db/db_test_util.
|
||||
external_sst_file_test: db/external_sst_file_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
import_column_family_test: db/import_column_family_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_tailing_iter_test: db/db_tailing_iter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1307,6 +1327,9 @@ backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TE
|
||||
checkpoint_test: utilities/checkpoint/checkpoint_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_simulator_test: utilities/simulator_cache/cache_simulator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sim_cache_test: utilities/simulator_cache/sim_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1333,13 +1356,13 @@ write_batch_with_index_test: utilities/write_batch_with_index/write_batch_with_i
|
||||
flush_job_test: db/flush_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_iterator_test: db/compaction_iterator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compaction_iterator_test: db/compaction/compaction_iterator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_job_test: db/compaction_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compaction_job_test: db/compaction/compaction_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_job_stats_test: db/compaction_job_stats_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compaction_job_stats_test: db/compaction/compaction_job_stats_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_on_deletion_collector_test: utilities/table_properties_collectors/compact_on_deletion_collector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1363,7 +1386,7 @@ fault_injection_test: db/fault_injection_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
rate_limiter_test: util/rate_limiter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
delete_scheduler_test: util/delete_scheduler_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
delete_scheduler_test: file/delete_scheduler_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1372,13 +1395,13 @@ filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
file_reader_writer_test: util/file_reader_writer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_filter_block_test: table/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
block_based_filter_block_test: table/block_based/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
full_filter_block_test: table/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
full_filter_block_test: table/block_based/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
partitioned_filter_block_test: table/partitioned_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
partitioned_filter_block_test: table/block_based/partitioned_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1390,10 +1413,10 @@ cleanable_test: table/cleanable_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_test: table/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
block_test: table/block_based/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
data_block_hash_index_test: table/data_block_hash_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
data_block_hash_index_test: table/block_based/data_block_hash_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
inlineskiplist_test: memtable/inlineskiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1411,7 +1434,7 @@ version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_picker_test: db/compaction_picker_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compaction_picker_test: db/compaction/compaction_picker_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
version_builder_test: db/version_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1459,10 +1482,10 @@ rocksdb_dump: tools/dump/rocksdb_dump.o $(LIBOBJECTS)
|
||||
rocksdb_undump: tools/dump/rocksdb_undump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_builder_test: table/cuckoo_table_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cuckoo_table_builder_test: table/cuckoo/cuckoo_table_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_reader_test: table/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cuckoo_table_reader_test: table/cuckoo/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_db_test: db/cuckoo_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1492,7 +1515,7 @@ db_bench_tool_test: tools/db_bench_tool_test.o $(BENCHTOOLOBJECTS) $(TESTHARNESS
|
||||
trace_analyzer_test: tools/trace_analyzer_test.o $(LIBOBJECTS) $(ANALYZETOOLOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
event_logger_test: util/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
event_logger_test: logging/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
timer_queue_test: util/timer_queue_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1513,7 +1536,10 @@ manual_compaction_test: db/manual_compaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
filelock_test: util/filelock_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
auto_roll_logger_test: util/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
auto_roll_logger_test: logging/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_logger_test: logging/env_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memtable_list_test: db/memtable_list_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1558,6 +1584,9 @@ persistent_cache_test: utilities/persistent_cache/persistent_cache_test.o db/db
|
||||
statistics_test: monitoring/statistics_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
stats_history_test: monitoring/stats_history_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_cache_test: cache/lru_cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1579,7 +1608,13 @@ range_tombstone_fragmenter_test: db/range_tombstone_fragmenter_test.o db/db_test
|
||||
sst_file_reader_test: table/sst_file_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_secondary_test: db/db_secondary_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
db_secondary_test: db/db_impl/db_secondary_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_tracer_test: trace_replay/block_cache_tracer_test.o trace_replay/block_cache_tracer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_trace_analyzer_test: tools/block_cache_trace_analyzer_test.o tools/block_cache_trace_analyzer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
@@ -1627,7 +1662,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
else ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ifneq (,$(filter $(MACHINE), amd64 arm64 sparc64))
|
||||
ifneq (,$(filter $(MACHINE), amd64 arm64 sparc64 aarch64))
|
||||
ARCH := 64
|
||||
else
|
||||
ARCH := 32
|
||||
@@ -1641,6 +1676,9 @@ ifeq (,$(findstring ppc,$(MACHINE)))
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
endif
|
||||
ifneq (,$(findstring aarch64,$(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
endif
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux$(ARCH).jar
|
||||
ROCKSDB_JAR_ALL = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
ROCKSDB_JAVADOCS_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-javadoc.jar
|
||||
@@ -1659,8 +1697,8 @@ SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.8.3
|
||||
LZ4_SHA256 ?= 33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.3.7
|
||||
ZSTD_SHA256 ?= 5dd1e90eb16c25425880c8a91327f63de22891ffed082fcc17e5ae84fce0d5fb
|
||||
ZSTD_VER ?= 1.4.0
|
||||
ZSTD_SHA256 ?= 63be339137d2b683c6d19a9e34f4fb684790e864fee13c7dd40e197a64c705c1
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -1827,27 +1865,15 @@ rocksdbjavastaticreleasedocker: rocksdbjavastatic rocksdbjavastaticdockerx86 roc
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_X86_CONTAINER=`docker ps -aqf name=rocksdb_linux_x86-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X86_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x86-be evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x86-be
|
||||
docker run --rm --name rocksdb_linux_x86-be --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_X64_CONTAINER=`docker ps -aqf name=rocksdb_linux_x64-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X64_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x64-be evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x64-be
|
||||
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64le:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_PPC64LE_CONTAINER=`docker ps -aqf name=rocksdb_linux_ppc64le-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_PPC64LE_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_ppc64le-be evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_ppc64le-be
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ ROCKSDB_COMPILER_FLAGS = [
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
"-Wnarrowing",
|
||||
"-DROCKSDB_NO_DYNAMIC_EXTENSION",
|
||||
]
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS = [
|
||||
@@ -83,23 +84,24 @@ cpp_library(
|
||||
"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_fifo.cc",
|
||||
"db/compaction_picker_universal.cc",
|
||||
"db/compaction/compaction.cc",
|
||||
"db/compaction/compaction_iterator.cc",
|
||||
"db/compaction/compaction_job.cc",
|
||||
"db/compaction/compaction_picker.cc",
|
||||
"db/compaction/compaction_picker_fifo.cc",
|
||||
"db/compaction/compaction_picker_level.cc",
|
||||
"db/compaction/compaction_picker_universal.cc",
|
||||
"db/convenience.cc",
|
||||
"db/db_filesnapshot.cc",
|
||||
"db/db_impl.cc",
|
||||
"db/db_impl_compaction_flush.cc",
|
||||
"db/db_impl_debug.cc",
|
||||
"db/db_impl_experimental.cc",
|
||||
"db/db_impl_files.cc",
|
||||
"db/db_impl_open.cc",
|
||||
"db/db_impl_readonly.cc",
|
||||
"db/db_impl_secondary.cc",
|
||||
"db/db_impl_write.cc",
|
||||
"db/db_impl/db_impl.cc",
|
||||
"db/db_impl/db_impl_compaction_flush.cc",
|
||||
"db/db_impl/db_impl_debug.cc",
|
||||
"db/db_impl/db_impl_experimental.cc",
|
||||
"db/db_impl/db_impl_files.cc",
|
||||
"db/db_impl/db_impl_open.cc",
|
||||
"db/db_impl/db_impl_readonly.cc",
|
||||
"db/db_impl/db_impl_secondary.cc",
|
||||
"db/db_impl/db_impl_write.cc",
|
||||
"db/db_info_dumper.cc",
|
||||
"db/db_iter.cc",
|
||||
"db/dbformat.cc",
|
||||
@@ -111,7 +113,7 @@ cpp_library(
|
||||
"db/flush_job.cc",
|
||||
"db/flush_scheduler.cc",
|
||||
"db/forward_iterator.cc",
|
||||
"db/in_memory_stats_history.cc",
|
||||
"db/import_column_family_job.cc",
|
||||
"db/internal_stats.cc",
|
||||
"db/log_reader.cc",
|
||||
"db/log_writer.cc",
|
||||
@@ -143,6 +145,16 @@ cpp_library(
|
||||
"env/env_posix.cc",
|
||||
"env/io_posix.cc",
|
||||
"env/mock_env.cc",
|
||||
"file/delete_scheduler.cc",
|
||||
"file/file_util.cc",
|
||||
"file/filename.cc",
|
||||
"file/sst_file_manager_impl.cc",
|
||||
"logging/auto_roll_logger.cc",
|
||||
"logging/event_logger.cc",
|
||||
"logging/log_buffer.cc",
|
||||
"memory/arena.cc",
|
||||
"memory/concurrent_arena.cc",
|
||||
"memory/jemalloc_nodump_allocator.cc",
|
||||
"memtable/alloc_tracker.cc",
|
||||
"memtable/hash_linklist_rep.cc",
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
@@ -151,10 +163,12 @@ cpp_library(
|
||||
"memtable/write_buffer_manager.cc",
|
||||
"monitoring/histogram.cc",
|
||||
"monitoring/histogram_windowing.cc",
|
||||
"monitoring/in_memory_stats_history.cc",
|
||||
"monitoring/instrumented_mutex.cc",
|
||||
"monitoring/iostats_context.cc",
|
||||
"monitoring/perf_context.cc",
|
||||
"monitoring/perf_level.cc",
|
||||
"monitoring/persistent_stats_history.cc",
|
||||
"monitoring/statistics.cc",
|
||||
"monitoring/thread_status_impl.cc",
|
||||
"monitoring/thread_status_updater.cc",
|
||||
@@ -169,78 +183,70 @@ cpp_library(
|
||||
"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/adaptive/adaptive_table_factory.cc",
|
||||
"table/block_based/block.cc",
|
||||
"table/block_based/block_based_filter_block.cc",
|
||||
"table/block_based/block_based_table_builder.cc",
|
||||
"table/block_based/block_based_table_factory.cc",
|
||||
"table/block_based/block_based_table_reader.cc",
|
||||
"table/block_based/block_builder.cc",
|
||||
"table/block_based/block_prefix_index.cc",
|
||||
"table/block_based/data_block_footer.cc",
|
||||
"table/block_based/data_block_hash_index.cc",
|
||||
"table/block_based/filter_block_reader_common.cc",
|
||||
"table/block_based/flush_block_policy.cc",
|
||||
"table/block_based/full_filter_block.cc",
|
||||
"table/block_based/index_builder.cc",
|
||||
"table/block_based/partitioned_filter_block.cc",
|
||||
"table/block_fetcher.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/data_block_footer.cc",
|
||||
"table/data_block_hash_index.cc",
|
||||
"table/flush_block_policy.cc",
|
||||
"table/cuckoo/cuckoo_table_builder.cc",
|
||||
"table/cuckoo/cuckoo_table_factory.cc",
|
||||
"table/cuckoo/cuckoo_table_reader.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/plain/plain_table_builder.cc",
|
||||
"table/plain/plain_table_factory.cc",
|
||||
"table/plain/plain_table_index.cc",
|
||||
"table/plain/plain_table_key_coding.cc",
|
||||
"table/plain/plain_table_reader.cc",
|
||||
"table/sst_file_reader.cc",
|
||||
"table/sst_file_writer.cc",
|
||||
"table/table_properties.cc",
|
||||
"table/two_level_iterator.cc",
|
||||
"test_util/sync_point.cc",
|
||||
"test_util/sync_point_impl.cc",
|
||||
"test_util/transaction_test_util.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",
|
||||
"trace_replay/block_cache_tracer.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/bloom.cc",
|
||||
"util/build_version.cc",
|
||||
"util/coding.cc",
|
||||
"util/compaction_job_stats_impl.cc",
|
||||
"util/comparator.cc",
|
||||
"util/compression_context_cache.cc",
|
||||
"util/concurrent_arena.cc",
|
||||
"util/concurrent_task_limiter_impl.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/jemalloc_nodump_allocator.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/string_util.cc",
|
||||
"util/sync_point.cc",
|
||||
"util/sync_point_impl.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/trace_replay.cc",
|
||||
"util/transaction_test_util.cc",
|
||||
"util/xxhash.cc",
|
||||
"utilities/backupable/backupable_db.cc",
|
||||
"utilities/blob_db/blob_compaction_filter.cc",
|
||||
@@ -276,6 +282,7 @@ cpp_library(
|
||||
"utilities/persistent_cache/block_cache_tier_metadata.cc",
|
||||
"utilities/persistent_cache/persistent_cache_tier.cc",
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/simulator_cache/cache_simulator.cc",
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
@@ -309,10 +316,11 @@ cpp_library(
|
||||
srcs = [
|
||||
"db/db_test_util.cc",
|
||||
"table/mock_table.cc",
|
||||
"test_util/fault_injection_test_env.cc",
|
||||
"test_util/testharness.cc",
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_trace_analyzer.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"util/fault_injection_test_env.cc",
|
||||
"util/testharness.cc",
|
||||
"util/testutil.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
@@ -326,9 +334,10 @@ cpp_library(
|
||||
cpp_library(
|
||||
name = "rocksdb_tools_lib",
|
||||
srcs = [
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_trace_analyzer.cc",
|
||||
"tools/db_bench_tool.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"util/testutil.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
@@ -353,12 +362,17 @@ cpp_library(
|
||||
ROCKS_TESTS = [
|
||||
[
|
||||
"arena_test",
|
||||
"util/arena_test.cc",
|
||||
"memory/arena_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"auto_roll_logger_test",
|
||||
"util/auto_roll_logger_test.cc",
|
||||
"logging/auto_roll_logger_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"env_logger_test",
|
||||
"logging/env_logger_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -378,12 +392,22 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"block_based_filter_block_test",
|
||||
"table/block_based_filter_block_test.cc",
|
||||
"table/block_based/block_based_filter_block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"block_cache_trace_analyzer_test",
|
||||
"tools/block_cache_trace_analyzer_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"block_cache_tracer_test",
|
||||
"trace_replay/block_cache_tracer_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"block_test",
|
||||
"table/block_test.cc",
|
||||
"table/block_based/block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -401,6 +425,11 @@ ROCKS_TESTS = [
|
||||
"cache/cache_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"cache_simulator_test",
|
||||
"utilities/simulator_cache/cache_simulator_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"cassandra_format_test",
|
||||
"utilities/cassandra/cassandra_format_test.cc",
|
||||
@@ -453,22 +482,22 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"compaction_iterator_test",
|
||||
"db/compaction_iterator_test.cc",
|
||||
"db/compaction/compaction_iterator_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"compaction_job_stats_test",
|
||||
"db/compaction_job_stats_test.cc",
|
||||
"db/compaction/compaction_job_stats_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"compaction_job_test",
|
||||
"db/compaction_job_test.cc",
|
||||
"db/compaction/compaction_job_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"compaction_picker_test",
|
||||
"db/compaction_picker_test.cc",
|
||||
"db/compaction/compaction_picker_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -488,7 +517,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"cuckoo_table_builder_test",
|
||||
"table/cuckoo_table_builder_test.cc",
|
||||
"table/cuckoo/cuckoo_table_builder_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -498,12 +527,12 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"cuckoo_table_reader_test",
|
||||
"table/cuckoo_table_reader_test.cc",
|
||||
"table/cuckoo/cuckoo_table_reader_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"data_block_hash_index_test",
|
||||
"table/data_block_hash_index_test.cc",
|
||||
"table/block_based/data_block_hash_index_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -524,7 +553,7 @@ ROCKS_TESTS = [
|
||||
[
|
||||
"db_bloom_filter_test",
|
||||
"db/db_bloom_filter_test.cc",
|
||||
"serial",
|
||||
"parallel",
|
||||
],
|
||||
[
|
||||
"db_compaction_filter_test",
|
||||
@@ -608,7 +637,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"db_secondary_test",
|
||||
"db/db_secondary_test.cc",
|
||||
"db/db_impl/db_secondary_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -663,7 +692,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"delete_scheduler_test",
|
||||
"util/delete_scheduler_test.cc",
|
||||
"file/delete_scheduler_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -698,7 +727,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"event_logger_test",
|
||||
"util/event_logger_test.cc",
|
||||
"logging/event_logger_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -743,7 +772,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"full_filter_block_test",
|
||||
"table/full_filter_block_test.cc",
|
||||
"table/block_based/full_filter_block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -873,7 +902,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"partitioned_filter_block_test",
|
||||
"table/partitioned_filter_block_test.cc",
|
||||
"table/block_based/partitioned_filter_block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -956,6 +985,11 @@ ROCKS_TESTS = [
|
||||
"monitoring/statistics_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"stats_history_test",
|
||||
"monitoring/stats_history_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"stringappend_test",
|
||||
"utilities/merge_operators/string_append/stringappend_test.cc",
|
||||
|
||||
@@ -50,6 +50,10 @@ Check out their blog post: http://blog.cloudera.com/blog/2015/08/inside-santande
|
||||
## Airbnb
|
||||
Airbnb is using RocksDB as a storage engine for their personalized search service. You can learn more about it here: https://www.youtube.com/watch?v=ASQ6XMtogMs
|
||||
|
||||
## Alluxio
|
||||
[Alluxio](https://www.alluxio.io) uses RocksDB to serve and scale file system metadata to beyond 1 Billion files. The detailed design and implementation is described in this engineering blog:
|
||||
https://www.alluxio.io/blog/scalable-metadata-service-in-alluxio-storing-billions-of-files/
|
||||
|
||||
## Pinterest
|
||||
Pinterest's Object Retrieval System uses RocksDB for storage: https://www.youtube.com/watch?v=MtFEVEs_2Vo
|
||||
|
||||
@@ -91,4 +95,4 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
[ProfaneDB](https://profanedb.gitlab.io/) is a database for Protocol Buffers, and uses RocksDB for storage. It is accessible via gRPC, and the schema is defined using directly `.proto` files.
|
||||
|
||||
## IOTA Foundation
|
||||
[IOTA Foundation](https://www.iota.org/) is using RocksDB in the [IOTA Reference Implementation (IRI)](https://github.com/iotaledger/iri) to store the local state of the Tangle. The Tangle is the first open-source distributed ledger powering the future of the Internet of Things.
|
||||
[IOTA Foundation](https://www.iota.org/) is using RocksDB in the [IOTA Reference Implementation (IRI)](https://github.com/iotaledger/iri) to store the local state of the Tangle. The Tangle is the first open-source distributed ledger powering the future of the Internet of Things.
|
||||
|
||||
+59
-7
@@ -1,15 +1,67 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
|
||||
THIRDPARTY_HOME: $(APPVEYOR_BUILD_FOLDER)\thirdparty
|
||||
SNAPPY_HOME: $(THIRDPARTY_HOME)\snappy-1.1.7
|
||||
SNAPPY_INCLUDE: $(SNAPPY_HOME);$(SNAPPY_HOME)\build
|
||||
SNAPPY_LIB_DEBUG: $(SNAPPY_HOME)\build\Debug\snappy.lib
|
||||
SNAPPY_LIB_RELEASE: $(SNAPPY_HOME)\build\Release\snappy.lib
|
||||
LZ4_HOME: $(THIRDPARTY_HOME)\lz4-1.8.3
|
||||
LZ4_INCLUDE: $(LZ4_HOME)\lib
|
||||
LZ4_LIB_DEBUG: $(LZ4_HOME)\visual\VS2010\bin\x64_Debug\liblz4_static.lib
|
||||
LZ4_LIB_RELEASE: $(LZ4_HOME)\visual\VS2010\bin\x64_Release\liblz4_static.lib
|
||||
ZSTD_HOME: $(THIRDPARTY_HOME)\zstd-1.4.0
|
||||
ZSTD_INCLUDE: $(ZSTD_HOME)\lib;$(ZSTD_HOME)\lib\dictBuilder
|
||||
ZSTD_LIB_DEBUG: $(ZSTD_HOME)\build\VS2010\bin\x64_Debug\libzstd_static.lib
|
||||
ZSTD_LIB_RELEASE: $(ZSTD_HOME)\build\VS2010\bin\x64_Release\libzstd_static.lib
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
- echo "Building Snappy dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o snappy-1.1.7.zip https://github.com/google/snappy/archive/1.1.7.zip
|
||||
- unzip snappy-1.1.7.zip
|
||||
- cd snappy-1.1.7
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
|
||||
- msbuild Snappy.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild Snappy.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building LZ4 dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o lz4-1.8.3.zip https://github.com/lz4/lz4/archive/v1.8.3.zip
|
||||
- unzip lz4-1.8.3.zip
|
||||
- cd lz4-1.8.3\visual\VS2010
|
||||
- ps: $CMD="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com"; & $CMD lz4.sln /upgrade
|
||||
- msbuild lz4.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild lz4.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building ZStd dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o zstd-1.4.0.zip https://github.com/facebook/zstd/archive/v1.4.0.zip
|
||||
- unzip zstd-1.4.0.zip
|
||||
- cd zstd-1.4.0\build\VS2010
|
||||
- ps: $CMD="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com"; & $CMD zstd.sln /upgrade
|
||||
- msbuild zstd.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild zstd.sln /p:Configuration=Release /p:Platform=x64
|
||||
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
parallel: true
|
||||
verbosity: normal
|
||||
test:
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
test:
|
||||
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a build-failed.zip %APPVEYOR_BUILD_FOLDER%\build\ && appveyor PushArtifact build-failed.zip
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ def generate_targets(repo_path):
|
||||
"rocksdb_tools_lib",
|
||||
src_mk.get("BENCH_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []) +
|
||||
["util/testutil.cc"],
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
|
||||
# test for every test we found in the Makefile
|
||||
|
||||
@@ -35,6 +35,7 @@ ROCKSDB_COMPILER_FLAGS = [
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
"-Wnarrowing",
|
||||
"-DROCKSDB_NO_DYNAMIC_EXTENSION",
|
||||
]
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS = [
|
||||
|
||||
@@ -397,6 +397,7 @@ EOF
|
||||
#include <malloc.h>
|
||||
int main() {
|
||||
size_t res = malloc_usable_size(0);
|
||||
(void)res;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
@@ -411,6 +412,7 @@ EOF
|
||||
#include <pthread.h>
|
||||
int main() {
|
||||
int x = PTHREAD_MUTEX_ADAPTIVE_NP;
|
||||
(void)x;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
@@ -422,7 +424,7 @@ EOF
|
||||
if ! test $ROCKSDB_DISABLE_BACKTRACE; then
|
||||
# Test whether backtrace is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <execinfo.h>>
|
||||
#include <execinfo.h>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
backtrace_symbols(frames, backtrace(frames, 1));
|
||||
@@ -480,6 +482,7 @@ EOF
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
int cpuid = sched_getcpu();
|
||||
(void)cpuid;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -515,7 +518,7 @@ fi
|
||||
|
||||
if test "$USE_HDFS"; then
|
||||
if test -z "$JAVA_HOME"; then
|
||||
echo "JAVA_HOME has to be set for HDFS usage."
|
||||
echo "JAVA_HOME has to be set for HDFS usage." >&2
|
||||
exit 1
|
||||
fi
|
||||
HDFS_CCFLAGS="$HDFS_CCFLAGS -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -DUSE_HDFS -I$HADOOP_HOME/include"
|
||||
@@ -534,9 +537,11 @@ if test -z "$PORTABLE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mcpu=$POWER -mtune=$POWER "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z10 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^arm`"; then
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep -e^arm -e^aarch64`"; then
|
||||
# TODO: Handle this with approprite options.
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^aarch64`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" != "AIX" ] && [ "$TARGET_OS" != "SunOS" ]; then
|
||||
@@ -553,12 +558,13 @@ $CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <nmmintrin.h>
|
||||
int main() {
|
||||
volatile uint32_t x = _mm_crc32_u32(0, 0);
|
||||
(void)x;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SSE42"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling"
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -570,12 +576,13 @@ $CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
(void)d;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_PCLMUL"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling"
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -589,6 +596,7 @@ if [ "$PLATFORM" != IOS ]; then
|
||||
#endif
|
||||
int main() {
|
||||
static __thread int tls;
|
||||
(void)tls;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -596,6 +604,19 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_CFLAGS -x c++ -c - -o test_dl.o 2>/dev/null <<EOF
|
||||
void dummy_func() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_LDFLAGS test_dl.o -o /dev/null 2>/dev/null
|
||||
if [ "$?" = 0 ]; then
|
||||
EXEC_LDFLAGS+="-ldl"
|
||||
rm -f test_dl.o
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
|
||||
|
||||
@@ -63,6 +63,21 @@ CLEANUP_ENV="
|
||||
'user':'root'
|
||||
}"
|
||||
|
||||
UPLOAD_DB_DIR="
|
||||
{
|
||||
'name':'Upload database directory',
|
||||
'shell':'tar -cvzf rocksdb_db.tar.gz /dev/shm/rocksdb/',
|
||||
'user':'root',
|
||||
'cleanup':true,
|
||||
'provide_artifacts': [
|
||||
{
|
||||
'name':'rocksdb_db_dir',
|
||||
'paths': ['rocksdb_db.tar.gz'],
|
||||
'bundle': false,
|
||||
},
|
||||
],
|
||||
}"
|
||||
|
||||
# We will eventually set the RATIO to 1, but we want do this
|
||||
# in steps. RATIO=$(nproc) will make it work as J=1
|
||||
if [ -z $RATIO ]; then
|
||||
@@ -109,13 +124,6 @@ else
|
||||
TASK_CREATION_TOOL="false"
|
||||
fi
|
||||
|
||||
ARTIFACTS=" 'artifacts': [
|
||||
{
|
||||
'name':'database',
|
||||
'paths':[ '/dev/shm/rocksdb' ],
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# A mechanism to disable tests temporarily
|
||||
#
|
||||
@@ -140,6 +148,7 @@ UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -160,6 +169,7 @@ UNIT_TEST_NON_SHM_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -182,6 +192,7 @@ RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -202,6 +213,7 @@ UNIT_TEST_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -222,6 +234,7 @@ RELEASE_BUILD_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Release on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -242,6 +255,7 @@ CLANG_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -262,6 +276,7 @@ CLANG_RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb CLANG Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -282,6 +297,7 @@ CLANG_ANALYZE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb analyze',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -302,6 +318,7 @@ CODE_COV_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test Code Coverage',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -322,6 +339,7 @@ UNITY_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unity',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -342,6 +360,7 @@ LITE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Lite build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -361,6 +380,7 @@ REPORT_LITE_BINARY_SIZE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Lite Binary Size',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -376,8 +396,9 @@ REPORT_LITE_BINARY_SIZE_COMMANDS="[
|
||||
#
|
||||
STRESS_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Stress/Crash Test',
|
||||
'name':'Rocksdb Stress and Crash Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -395,7 +416,6 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
$PARSER
|
||||
}
|
||||
],
|
||||
$ARTIFACTS,
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
@@ -405,8 +425,9 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
#
|
||||
STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Stress/Crash Test (atomic flush)',
|
||||
'name':'Rocksdb Stress and Crash Test with atomic flush',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -422,9 +443,9 @@ STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$ARTIFACTS,
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
@@ -436,6 +457,7 @@ WRITE_STRESS_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Write Stress Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -458,6 +480,7 @@ ASAN_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -478,6 +501,7 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -498,8 +522,9 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
#
|
||||
ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test (atomic flush) under ASAN',
|
||||
'name':'Rocksdb crash test with atomic flush under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -510,6 +535,7 @@ ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
@@ -522,6 +548,7 @@ UBSAN_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -542,6 +569,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -562,8 +590,9 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
#
|
||||
UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test (atomic flush) under UBSAN',
|
||||
'name':'Rocksdb crash test with atomic flush under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -574,6 +603,7 @@ UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
@@ -586,6 +616,7 @@ VALGRIND_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under valgrind',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -608,6 +639,7 @@ TSAN_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -630,6 +662,7 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Crash Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -652,6 +685,7 @@ TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Crash Test with atomic flush under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -662,6 +696,7 @@ TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
@@ -684,6 +719,7 @@ FORMAT_COMPATIBLE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Format Compatible tests',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -717,6 +753,7 @@ NO_COMPRESSION_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb No Compression tests',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -794,6 +831,7 @@ JAVA_BUILD_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Java Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
|
||||
Vendored
+1
-4
@@ -3,9 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#ifndef GFLAGS
|
||||
#include <cstdio>
|
||||
int main() {
|
||||
@@ -14,7 +11,7 @@ int main() {
|
||||
}
|
||||
#else
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
Vendored
+13
-3
@@ -16,9 +16,9 @@
|
||||
#include <vector>
|
||||
#include "cache/clock_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -90,7 +90,7 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
bool strict_capacity_limit) {
|
||||
auto type = GetParam();
|
||||
if (type == kLRU) {
|
||||
return NewLRUCache(capacity, num_shard_bits, strict_capacity_limit);
|
||||
return NewLRUCache(capacity, num_shard_bits, strict_capacity_limit, 0.0);
|
||||
}
|
||||
if (type == kClock) {
|
||||
return NewClockCache(capacity, num_shard_bits, strict_capacity_limit);
|
||||
@@ -562,6 +562,7 @@ TEST_P(CacheTest, SetStrictCapacityLimit) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_NE(nullptr, handles[i]);
|
||||
}
|
||||
ASSERT_EQ(10, cache->GetUsage());
|
||||
|
||||
// test2: set the flag to true. Insert and check if it fails.
|
||||
std::string extra_key = "extra";
|
||||
@@ -571,6 +572,7 @@ TEST_P(CacheTest, SetStrictCapacityLimit) {
|
||||
s = cache->Insert(extra_key, extra_value, 1, &deleter, &handle);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
ASSERT_EQ(nullptr, handle);
|
||||
ASSERT_EQ(10, cache->GetUsage());
|
||||
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
cache->Release(handles[i]);
|
||||
@@ -591,7 +593,7 @@ TEST_P(CacheTest, SetStrictCapacityLimit) {
|
||||
s = cache2->Insert(extra_key, extra_value, 1, &deleter);
|
||||
// AS if the key have been inserted into cache but get evicted immediately.
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(5, cache->GetUsage());
|
||||
ASSERT_EQ(5, cache2->GetUsage());
|
||||
ASSERT_EQ(nullptr, cache2->Lookup(extra_key));
|
||||
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
@@ -686,6 +688,14 @@ TEST_P(CacheTest, DefaultShardBits) {
|
||||
ASSERT_EQ(6, sc->GetNumShardBits());
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, GetCharge) {
|
||||
Insert(1, 2);
|
||||
Cache::Handle* h1 = cache_->Lookup(EncodeKey(1));
|
||||
ASSERT_EQ(2, DecodeValue(cache_->Value(h1)));
|
||||
ASSERT_EQ(1, cache_->GetCharge(h1));
|
||||
cache_->Release(h1);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_CLOCK_CACHE
|
||||
std::shared_ptr<Cache> (*new_clock_cache_func)(size_t, int,
|
||||
bool) = NewClockCache;
|
||||
|
||||
Vendored
+62
-80
@@ -7,10 +7,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
|
||||
#include <assert.h>
|
||||
@@ -28,7 +24,7 @@ LRUHandleTable::LRUHandleTable() : list_(nullptr), length_(0), elems_(0) {
|
||||
|
||||
LRUHandleTable::~LRUHandleTable() {
|
||||
ApplyToAllCacheEntries([](LRUHandle* h) {
|
||||
if (h->refs == 1) {
|
||||
if (!h->HasRefs()) {
|
||||
h->Free();
|
||||
}
|
||||
});
|
||||
@@ -117,29 +113,17 @@ LRUCacheShard::LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
SetCapacity(capacity);
|
||||
}
|
||||
|
||||
LRUCacheShard::~LRUCacheShard() {}
|
||||
|
||||
bool LRUCacheShard::Unref(LRUHandle* e) {
|
||||
assert(e->refs > 0);
|
||||
e->refs--;
|
||||
return e->refs == 0;
|
||||
}
|
||||
|
||||
// Call deleter and free
|
||||
|
||||
void LRUCacheShard::EraseUnRefEntries() {
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
while (lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
assert(old->InCache());
|
||||
assert(old->refs ==
|
||||
1); // LRU list contains elements which may be evicted
|
||||
// LRU list contains only elements which can be evicted
|
||||
assert(old->InCache() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
old->SetInCache(false);
|
||||
Unref(old);
|
||||
usage_ -= old->charge;
|
||||
last_reference_list.push_back(old);
|
||||
}
|
||||
@@ -152,22 +136,27 @@ void LRUCacheShard::EraseUnRefEntries() {
|
||||
|
||||
void LRUCacheShard::ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) {
|
||||
const auto applyCallback = [&]() {
|
||||
table_.ApplyToAllCacheEntries(
|
||||
[callback](LRUHandle* h) { callback(h->value, h->charge); });
|
||||
};
|
||||
|
||||
if (thread_safe) {
|
||||
mutex_.Lock();
|
||||
}
|
||||
table_.ApplyToAllCacheEntries(
|
||||
[callback](LRUHandle* h) { callback(h->value, h->charge); });
|
||||
if (thread_safe) {
|
||||
mutex_.Unlock();
|
||||
MutexLock l(&mutex_);
|
||||
applyCallback();
|
||||
} else {
|
||||
applyCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCacheShard::TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri) {
|
||||
MutexLock l(&mutex_);
|
||||
*lru = &lru_;
|
||||
*lru_low_pri = lru_low_pri_;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::TEST_GetLRUSize() {
|
||||
MutexLock l(&mutex_);
|
||||
LRUHandle* lru_handle = lru_.next;
|
||||
size_t lru_size = 0;
|
||||
while (lru_handle != &lru_) {
|
||||
@@ -235,14 +224,13 @@ void LRUCacheShard::MaintainPoolSize() {
|
||||
|
||||
void LRUCacheShard::EvictFromLRU(size_t charge,
|
||||
autovector<LRUHandle*>* deleted) {
|
||||
while (usage_ + charge > capacity_ && lru_.next != &lru_) {
|
||||
while ((usage_ + charge) > capacity_ && lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
assert(old->InCache());
|
||||
assert(old->refs == 1); // LRU list contains elements which may be evicted
|
||||
// LRU list contains only elements which can be evicted
|
||||
assert(old->InCache() && !old->HasRefs());
|
||||
LRU_Remove(old);
|
||||
table_.Remove(old->key(), old->hash);
|
||||
old->SetInCache(false);
|
||||
Unref(old);
|
||||
usage_ -= old->charge;
|
||||
deleted->push_back(old);
|
||||
}
|
||||
@@ -256,8 +244,8 @@ void LRUCacheShard::SetCapacity(size_t capacity) {
|
||||
high_pri_pool_capacity_ = capacity_ * high_pri_pool_ratio_;
|
||||
EvictFromLRU(0, &last_reference_list);
|
||||
}
|
||||
// we free the entries here outside of mutex for
|
||||
// performance reasons
|
||||
|
||||
// Free the entries outside of mutex for performance reasons
|
||||
for (auto entry : last_reference_list) {
|
||||
entry->Free();
|
||||
}
|
||||
@@ -273,22 +261,22 @@ Cache::Handle* LRUCacheShard::Lookup(const Slice& key, uint32_t hash) {
|
||||
LRUHandle* e = table_.Lookup(key, hash);
|
||||
if (e != nullptr) {
|
||||
assert(e->InCache());
|
||||
if (e->refs == 1) {
|
||||
if (!e->HasRefs()) {
|
||||
// The entry is in LRU since it's in hash and has no external references
|
||||
LRU_Remove(e);
|
||||
}
|
||||
e->refs++;
|
||||
e->Ref();
|
||||
e->SetHit();
|
||||
}
|
||||
return reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
|
||||
bool LRUCacheShard::Ref(Cache::Handle* h) {
|
||||
LRUHandle* handle = reinterpret_cast<LRUHandle*>(h);
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(h);
|
||||
MutexLock l(&mutex_);
|
||||
if (handle->InCache() && handle->refs == 1) {
|
||||
LRU_Remove(handle);
|
||||
}
|
||||
handle->refs++;
|
||||
// To create another reference - entry must be already externally referenced
|
||||
assert(e->HasRefs());
|
||||
e->Ref();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -307,30 +295,27 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
bool last_reference = false;
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
last_reference = Unref(e);
|
||||
last_reference = e->Unref();
|
||||
if (last_reference && e->InCache()) {
|
||||
// The item is still in cache, and nobody else holds a reference to it
|
||||
if (usage_ > capacity_ || force_erase) {
|
||||
// The LRU list must be empty since the cache is full
|
||||
assert(lru_.next == &lru_ || force_erase);
|
||||
// Take this opportunity and remove the item
|
||||
table_.Remove(e->key(), e->hash);
|
||||
e->SetInCache(false);
|
||||
} else {
|
||||
// Put the item back on the LRU list, and don't free it
|
||||
LRU_Insert(e);
|
||||
last_reference = false;
|
||||
}
|
||||
}
|
||||
if (last_reference) {
|
||||
usage_ -= e->charge;
|
||||
}
|
||||
if (e->refs == 1 && e->InCache()) {
|
||||
// The item is still in cache, and nobody else holds a reference to it
|
||||
if (usage_ > capacity_ || force_erase) {
|
||||
// the cache is full
|
||||
// The LRU list must be empty since the cache is full
|
||||
assert(!(usage_ > capacity_) || lru_.next == &lru_);
|
||||
// take this opportunity and remove the item
|
||||
table_.Remove(e->key(), e->hash);
|
||||
e->SetInCache(false);
|
||||
Unref(e);
|
||||
usage_ -= e->charge;
|
||||
last_reference = true;
|
||||
} else {
|
||||
// put the item on the list to be potentially freed
|
||||
LRU_Insert(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free outside of mutex
|
||||
// Free the entry here outside of mutex for performance reasons
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
}
|
||||
@@ -346,7 +331,7 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
// It shouldn't happen very often though.
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(
|
||||
new char[sizeof(LRUHandle) - 1 + key.size()]);
|
||||
Status s;
|
||||
Status s = Status::OK();
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
|
||||
e->value = value;
|
||||
@@ -355,9 +340,7 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
e->key_length = key.size();
|
||||
e->flags = 0;
|
||||
e->hash = hash;
|
||||
e->refs = (handle == nullptr
|
||||
? 1
|
||||
: 2); // One from LRUCache, one for the returned handle
|
||||
e->refs = 0;
|
||||
e->next = e->prev = nullptr;
|
||||
e->SetInCache(true);
|
||||
e->SetPriority(priority);
|
||||
@@ -370,11 +353,12 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
// is freed or the lru list is empty
|
||||
EvictFromLRU(charge, &last_reference_list);
|
||||
|
||||
if (usage_ - lru_usage_ + charge > capacity_ &&
|
||||
if ((usage_ + charge) > capacity_ &&
|
||||
(strict_capacity_limit_ || handle == nullptr)) {
|
||||
if (handle == nullptr) {
|
||||
// Don't insert the entry but still return ok, as if the entry inserted
|
||||
// into cache and get evicted immediately.
|
||||
e->SetInCache(false);
|
||||
last_reference_list.push_back(e);
|
||||
} else {
|
||||
delete[] reinterpret_cast<char*>(e);
|
||||
@@ -382,32 +366,30 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
s = Status::Incomplete("Insert failed due to LRU cache being full.");
|
||||
}
|
||||
} else {
|
||||
// insert into the cache
|
||||
// note that the cache might get larger than its capacity if not enough
|
||||
// space was freed
|
||||
// Insert into the cache. Note that the cache might get larger than its
|
||||
// capacity if not enough space was freed up.
|
||||
LRUHandle* old = table_.Insert(e);
|
||||
usage_ += e->charge;
|
||||
if (old != nullptr) {
|
||||
assert(old->InCache());
|
||||
old->SetInCache(false);
|
||||
if (Unref(old)) {
|
||||
usage_ -= old->charge;
|
||||
// old is on LRU because it's in cache and its reference count
|
||||
// was just 1 (Unref returned 0)
|
||||
if (!old->HasRefs()) {
|
||||
// old is on LRU because it's in cache and its reference count is 0
|
||||
LRU_Remove(old);
|
||||
usage_ -= old->charge;
|
||||
last_reference_list.push_back(old);
|
||||
}
|
||||
}
|
||||
if (handle == nullptr) {
|
||||
LRU_Insert(e);
|
||||
} else {
|
||||
e->Ref();
|
||||
*handle = reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
s = Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
// we free the entries here outside of mutex for
|
||||
// performance reasons
|
||||
// Free the entries here outside of mutex for performance reasons
|
||||
for (auto entry : last_reference_list) {
|
||||
entry->Free();
|
||||
}
|
||||
@@ -422,18 +404,18 @@ void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
MutexLock l(&mutex_);
|
||||
e = table_.Remove(key, hash);
|
||||
if (e != nullptr) {
|
||||
last_reference = Unref(e);
|
||||
if (last_reference) {
|
||||
usage_ -= e->charge;
|
||||
}
|
||||
if (last_reference && e->InCache()) {
|
||||
LRU_Remove(e);
|
||||
}
|
||||
assert(e->InCache());
|
||||
e->SetInCache(false);
|
||||
if (!e->HasRefs()) {
|
||||
// The entry is in LRU since it's in hash and has no external references
|
||||
LRU_Remove(e);
|
||||
usage_ -= e->charge;
|
||||
last_reference = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mutex not held here
|
||||
// Free the entry here outside of mutex for performance reasons
|
||||
// last_reference will only be true if e != nullptr
|
||||
if (last_reference) {
|
||||
e->Free();
|
||||
|
||||
Vendored
+40
-36
@@ -17,31 +17,34 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// LRU cache implementation
|
||||
// LRU cache implementation. This class is not thread-safe.
|
||||
|
||||
// An entry is a variable length heap-allocated structure.
|
||||
// Entries are referenced by cache and/or by any external entity.
|
||||
// The cache keeps all its entries in table. Some elements
|
||||
// The cache keeps all its entries in a hash table. Some elements
|
||||
// are also stored on LRU list.
|
||||
//
|
||||
// LRUHandle can be in these states:
|
||||
// 1. Referenced externally AND in hash table.
|
||||
// In that case the entry is *not* in the LRU. (refs > 1 && in_cache == true)
|
||||
// 2. Not referenced externally and in hash table. In that case the entry is
|
||||
// in the LRU and can be freed. (refs == 1 && in_cache == true)
|
||||
// 3. Referenced externally and not in hash table. In that case the entry is
|
||||
// in not on LRU and not in table. (refs >= 1 && in_cache == false)
|
||||
// In that case the entry is *not* in the LRU list
|
||||
// (refs >= 1 && in_cache == true)
|
||||
// 2. Not referenced externally AND in hash table.
|
||||
// In that case the entry is in the LRU list and can be freed.
|
||||
// (refs == 0 && in_cache == true)
|
||||
// 3. Referenced externally AND not in hash table.
|
||||
// In that case the entry is not in the LRU list and not in hash table.
|
||||
// The entry can be freed when refs becomes 0.
|
||||
// (refs >= 1 && in_cache == false)
|
||||
//
|
||||
// All newly created LRUHandles are in state 1. If you call
|
||||
// LRUCacheShard::Release
|
||||
// on entry in state 1, it will go into state 2. To move from state 1 to
|
||||
// state 3, either call LRUCacheShard::Erase or LRUCacheShard::Insert with the
|
||||
// same key.
|
||||
// LRUCacheShard::Release on entry in state 1, it will go into state 2.
|
||||
// To move from state 1 to state 3, either call LRUCacheShard::Erase or
|
||||
// LRUCacheShard::Insert with the same key (but possibly different value).
|
||||
// To move from state 2 to state 1, use LRUCacheShard::Lookup.
|
||||
// Before destruction, make sure that no handles are in state 1. This means
|
||||
// that any successful LRUCacheShard::Lookup/LRUCacheShard::Insert have a
|
||||
// matching
|
||||
// RUCache::Release (to move into state 2) or LRUCacheShard::Erase (for state 3)
|
||||
// matching LRUCache::Release (to move into state 2) or LRUCacheShard::Erase
|
||||
// (to move into state 3).
|
||||
|
||||
struct LRUHandle {
|
||||
void* value;
|
||||
@@ -51,37 +54,42 @@ struct LRUHandle {
|
||||
LRUHandle* prev;
|
||||
size_t charge; // TODO(opt): Only allow uint32_t?
|
||||
size_t key_length;
|
||||
uint32_t refs; // a number of refs to this entry
|
||||
// cache itself is counted as 1
|
||||
// The hash of key(). Used for fast sharding and comparisons.
|
||||
uint32_t hash;
|
||||
// The number of external refs to this entry. The cache itself is not counted.
|
||||
uint32_t refs;
|
||||
|
||||
// Include the following flags:
|
||||
// IN_CACHE: whether this entry is referenced by the hash table.
|
||||
// IS_HIGH_PRI: whether this entry is high priority entry.
|
||||
// IN_HIGH_PRI_POOL: whether this entry is in high-pri pool.
|
||||
// HAS_HIT: whether this entry has had any lookups (hits).
|
||||
enum Flags : uint8_t {
|
||||
// Whether this entry is referenced by the hash table.
|
||||
IN_CACHE = (1 << 0),
|
||||
// Whether this entry is high priority entry.
|
||||
IS_HIGH_PRI = (1 << 1),
|
||||
// Whether this entry is in high-pri pool.
|
||||
IN_HIGH_PRI_POOL = (1 << 2),
|
||||
// Wwhether this entry has had any lookups (hits).
|
||||
HAS_HIT = (1 << 3),
|
||||
};
|
||||
|
||||
uint8_t flags;
|
||||
|
||||
uint32_t hash; // Hash of key(); used for fast sharding and comparisons
|
||||
// Beginning of the key (MUST BE THE LAST FIELD IN THIS STRUCT!)
|
||||
char key_data[1];
|
||||
|
||||
char key_data[1]; // Beginning of key
|
||||
Slice key() const { return Slice(key_data, key_length); }
|
||||
|
||||
Slice key() const {
|
||||
// For cheaper lookups, we allow a temporary Handle object
|
||||
// to store a pointer to a key in "value".
|
||||
if (next == this) {
|
||||
return *(reinterpret_cast<Slice*>(value));
|
||||
} else {
|
||||
return Slice(key_data, key_length);
|
||||
}
|
||||
// Increase the reference count by 1.
|
||||
void Ref() { refs++; }
|
||||
|
||||
// Just reduce the reference count by 1. Return true if it was last reference.
|
||||
bool Unref() {
|
||||
assert(refs > 0);
|
||||
refs--;
|
||||
return refs == 0;
|
||||
}
|
||||
|
||||
// Return true if there are external refs, false otherwise.
|
||||
bool HasRefs() const { return refs > 0; }
|
||||
|
||||
bool InCache() const { return flags & IN_CACHE; }
|
||||
bool IsHighPri() const { return flags & IS_HIGH_PRI; }
|
||||
bool InHighPriPool() const { return flags & IN_HIGH_PRI_POOL; }
|
||||
@@ -114,7 +122,7 @@ struct LRUHandle {
|
||||
void SetHit() { flags |= HAS_HIT; }
|
||||
|
||||
void Free() {
|
||||
assert((refs == 1 && InCache()) || (refs == 0 && !InCache()));
|
||||
assert(refs == 0);
|
||||
if (deleter) {
|
||||
(*deleter)(key(), value);
|
||||
}
|
||||
@@ -169,7 +177,7 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
public:
|
||||
LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio, bool use_adaptive_mutex);
|
||||
virtual ~LRUCacheShard();
|
||||
virtual ~LRUCacheShard() override = default;
|
||||
|
||||
// Separate from constructor so caller can easily make an array of LRUCache
|
||||
// if current usage is more than new capacity, the function will attempt to
|
||||
@@ -225,10 +233,6 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
// high-pri pool is no larger than the size specify by high_pri_pool_pct.
|
||||
void MaintainPoolSize();
|
||||
|
||||
// Just reduce the reference count by 1.
|
||||
// Return true if last reference
|
||||
bool Unref(LRUHandle* e);
|
||||
|
||||
// Free some space following strict LRU policy until enough space
|
||||
// to hold (usage_ + charge) is freed or the lru list is empty
|
||||
// This function is not thread safe - it needs to be executed while
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "port/port.h"
|
||||
#include "util/testharness.h"
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
Vendored
-4
@@ -7,10 +7,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include "cache/sharded_cache.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
Vendored
+2
-1
@@ -54,7 +54,8 @@ class ShardedCache : public Cache {
|
||||
virtual CacheShard* GetShard(int shard) = 0;
|
||||
virtual const CacheShard* GetShard(int shard) const = 0;
|
||||
virtual void* Value(Handle* handle) override = 0;
|
||||
virtual size_t GetCharge(Handle* handle) const = 0;
|
||||
virtual size_t GetCharge(Handle* handle) const override = 0;
|
||||
|
||||
virtual uint32_t GetHash(Handle* handle) const = 0;
|
||||
virtual void DisownData() override = 0;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
import re
|
||||
import sys
|
||||
|
||||
+7
-6
@@ -13,7 +13,7 @@
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/internal_stats.h"
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "file/filename.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -28,13 +29,12 @@
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block_based_table_builder.h"
|
||||
#include "table/block_based/block_based_table_builder.h"
|
||||
#include "table/format.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -221,8 +221,9 @@ Status BuildTable(
|
||||
mutable_cf_options.prefix_extractor.get(), nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
false /* for_compaction */, nullptr /* arena */,
|
||||
false /* skip_filter */, level));
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
/*skip_filter=*/false, level, /*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr));
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
|
||||
+1
-1
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "logging/event_logger.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -20,7 +21,6 @@
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/event_logger.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
@@ -517,6 +517,21 @@ rocksdb_t* rocksdb_open_for_read_only(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_as_secondary(const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
const char* secondary_path,
|
||||
char** errptr) {
|
||||
DB* db;
|
||||
if (SaveError(errptr,
|
||||
DB::OpenAsSecondary(options->rep, std::string(name),
|
||||
std::string(secondary_path), &db))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_backup_engine_t* rocksdb_backup_engine_open(
|
||||
const rocksdb_options_t* options, const char* path, char** errptr) {
|
||||
BackupEngine* be;
|
||||
@@ -717,6 +732,37 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_as_secondary_column_families(
|
||||
const rocksdb_options_t* db_options, const char* name,
|
||||
const char* secondary_path, int num_column_families,
|
||||
const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i != num_column_families; ++i) {
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
DB* db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
if (SaveError(errptr, DB::OpenAsSecondary(DBOptions(db_options->rep),
|
||||
std::string(name),
|
||||
std::string(secondary_path),
|
||||
column_families, &handles, &db))) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i != handles.size(); ++i) {
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
char** rocksdb_list_column_families(
|
||||
const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
@@ -988,7 +1034,7 @@ void rocksdb_wal_iter_destroy (const rocksdb_wal_iterator_t* iter) {
|
||||
rocksdb_writebatch_t* rocksdb_wal_iter_get_batch (const rocksdb_wal_iterator_t* iter, uint64_t* seq) {
|
||||
rocksdb_writebatch_t* result = rocksdb_writebatch_create();
|
||||
BatchResult wal_batch = iter->rep->GetBatch();
|
||||
result->rep = * wal_batch.writeBatchPtr.release();
|
||||
result->rep = std::move(*wal_batch.writeBatchPtr);
|
||||
if (seq != nullptr) {
|
||||
*seq = wal_batch.sequence;
|
||||
}
|
||||
@@ -2473,6 +2519,11 @@ void rocksdb_options_set_enable_pipelined_write(rocksdb_options_t* opt,
|
||||
opt->rep.enable_pipelined_write = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_unordered_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.unordered_write = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_subcompactions(rocksdb_options_t* opt,
|
||||
uint32_t n) {
|
||||
opt->rep.max_subcompactions = n;
|
||||
@@ -3268,6 +3319,22 @@ void rocksdb_env_join_all_threads(rocksdb_env_t* env) {
|
||||
env->rep->WaitForJoin();
|
||||
}
|
||||
|
||||
void rocksdb_env_lower_thread_pool_io_priority(rocksdb_env_t* env) {
|
||||
env->rep->LowerThreadPoolIOPriority();
|
||||
}
|
||||
|
||||
void rocksdb_env_lower_high_priority_thread_pool_io_priority(rocksdb_env_t* env) {
|
||||
env->rep->LowerThreadPoolIOPriority(Env::HIGH);
|
||||
}
|
||||
|
||||
void rocksdb_env_lower_thread_pool_cpu_priority(rocksdb_env_t* env) {
|
||||
env->rep->LowerThreadPoolCPUPriority();
|
||||
}
|
||||
|
||||
void rocksdb_env_lower_high_priority_thread_pool_cpu_priority(rocksdb_env_t* env) {
|
||||
env->rep->LowerThreadPoolCPUPriority(Env::HIGH);
|
||||
}
|
||||
|
||||
void rocksdb_env_destroy(rocksdb_env_t* env) {
|
||||
if (!env->is_default) delete env->rep;
|
||||
delete env;
|
||||
@@ -3402,6 +3469,10 @@ void rocksdb_ingest_external_file_cf(
|
||||
SaveError(errptr, db->rep->IngestExternalFile(handle->rep, files, opt->rep));
|
||||
}
|
||||
|
||||
void rocksdb_try_catch_up_with_primary(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->TryCatchUpWithPrimary());
|
||||
}
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create(
|
||||
void* state,
|
||||
void (*destructor)(void*),
|
||||
|
||||
+54
@@ -45,6 +45,7 @@ static char sstfilename[200];
|
||||
static char dbbackupname[200];
|
||||
static char dbcheckpointname[200];
|
||||
static char dbpathname[200];
|
||||
static char secondary_path[200];
|
||||
|
||||
static void StartPhase(const char* name) {
|
||||
fprintf(stderr, "=== Test %s\n", name);
|
||||
@@ -1722,6 +1723,59 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
// Check that secondary instance works.
|
||||
StartPhase("open_as_secondary");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
|
||||
rocksdb_options_t* db_options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(db_options, 1);
|
||||
db = rocksdb_open(db_options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_t* db1;
|
||||
rocksdb_options_t* opts = rocksdb_options_create();
|
||||
rocksdb_options_set_max_open_files(opts, -1);
|
||||
rocksdb_options_set_create_if_missing(opts, 1);
|
||||
snprintf(secondary_path, sizeof(secondary_path),
|
||||
"%s/rocksdb_c_test_secondary-%d", GetTempDir(), ((int)geteuid()));
|
||||
db1 = rocksdb_open_as_secondary(opts, dbname, secondary_path, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_writeoptions_set_sync(woptions, 0);
|
||||
rocksdb_writeoptions_disable_WAL(woptions, 1);
|
||||
rocksdb_put(db, woptions, "key0", 4, "value0", 6, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_flushoptions_t* flush_opts = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_opts, 1);
|
||||
rocksdb_flush(db, flush_opts, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_try_catch_up_with_primary(db1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_readoptions_t* ropts = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_verify_checksums(ropts, 1);
|
||||
rocksdb_readoptions_set_snapshot(ropts, NULL);
|
||||
CheckGet(db, ropts, "key0", "value0");
|
||||
CheckGet(db1, ropts, "key0", "value0");
|
||||
|
||||
rocksdb_writeoptions_disable_WAL(woptions, 0);
|
||||
rocksdb_put(db, woptions, "key1", 4, "value1", 6, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_try_catch_up_with_primary(db1, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db1, ropts, "key0", "value0");
|
||||
CheckGet(db1, ropts, "key1", "value1");
|
||||
|
||||
rocksdb_close(db1);
|
||||
rocksdb_destroy_db(opts, secondary_path, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_destroy(db_options);
|
||||
rocksdb_options_destroy(opts);
|
||||
rocksdb_readoptions_destroy(ropts);
|
||||
rocksdb_flushoptions_destroy(flush_opts);
|
||||
}
|
||||
|
||||
// Simple sanity check that options setting db_paths work.
|
||||
StartPhase("open_db_paths");
|
||||
{
|
||||
|
||||
+68
-20
@@ -9,34 +9,31 @@
|
||||
|
||||
#include "db/column_family.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#include "db/compaction_picker_universal.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "file/sst_file_manager_impl.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 "table/block_based/block_based_table_factory.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -408,7 +405,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* _dummy_versions,
|
||||
Cache* _table_cache, WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, ColumnFamilySet* column_family_set)
|
||||
const EnvOptions& env_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(_dummy_versions),
|
||||
@@ -448,7 +446,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
if (_dummy_versions != nullptr) {
|
||||
internal_stats_.reset(
|
||||
new InternalStats(ioptions_.num_levels, db_options.env, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, env_options, _table_cache));
|
||||
table_cache_.reset(new TableCache(ioptions_, env_options, _table_cache,
|
||||
block_cache_tracer));
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
new LevelCompactionPicker(ioptions_, &internal_comparator_));
|
||||
@@ -1147,13 +1146,60 @@ void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
}
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::ValidateOptions(
|
||||
const DBOptions& db_options, const ColumnFamilyOptions& cf_options) {
|
||||
Status s;
|
||||
s = CheckCompressionSupported(cf_options);
|
||||
if (s.ok() && db_options.allow_concurrent_memtable_write) {
|
||||
s = CheckConcurrentWritesSupported(cf_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = CheckCFPathsSupported(db_options, cf_options);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (cf_options.ttl > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cf_options.table_factory->Name() != BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported in Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
|
||||
if (cf_options.periodic_compaction_seconds > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"Periodic Compaction is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cf_options.table_factory->Name() != BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"Periodic Compaction is only supported in "
|
||||
"Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status ColumnFamilyData::SetOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
const DBOptions& db_options,
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
MutableCFOptions new_mutable_cf_options;
|
||||
Status s =
|
||||
GetMutableOptionsFromStrings(mutable_cf_options_, options_map,
|
||||
ioptions_.info_log, &new_mutable_cf_options);
|
||||
if (s.ok()) {
|
||||
ColumnFamilyOptions cf_options =
|
||||
BuildColumnFamilyOptions(initial_cf_options_, new_mutable_cf_options);
|
||||
s = ValidateOptions(db_options, cf_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
mutable_cf_options_ = new_mutable_cf_options;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
@@ -1210,18 +1256,20 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
const EnvOptions& env_options,
|
||||
Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller)
|
||||
WriteController* write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer)
|
||||
: max_column_family_(0),
|
||||
dummy_cfd_(new ColumnFamilyData(0, "", nullptr, nullptr, nullptr,
|
||||
ColumnFamilyOptions(), *db_options,
|
||||
env_options, nullptr)),
|
||||
dummy_cfd_(new ColumnFamilyData(
|
||||
0, "", nullptr, nullptr, nullptr, ColumnFamilyOptions(), *db_options,
|
||||
env_options, nullptr, block_cache_tracer)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
table_cache_(table_cache),
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
write_controller_(write_controller) {
|
||||
write_controller_(write_controller),
|
||||
block_cache_tracer_(block_cache_tracer) {
|
||||
// initialize linked list
|
||||
dummy_cfd_->prev_ = dummy_cfd_;
|
||||
dummy_cfd_->next_ = dummy_cfd_;
|
||||
@@ -1289,7 +1337,7 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
*db_options_, env_options_, this);
|
||||
*db_options_, env_options_, this, block_cache_tracer_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
|
||||
+116
-2
@@ -24,6 +24,7 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "trace_replay/block_cache_tracer.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -45,6 +46,112 @@ class InstrumentedMutexLock;
|
||||
struct SuperVersionContext;
|
||||
|
||||
extern const double kIncSlowdownRatio;
|
||||
// This file contains a list of data structures for managing column family
|
||||
// level metadata.
|
||||
//
|
||||
// The basic relationships among classes declared here are illustrated as
|
||||
// following:
|
||||
//
|
||||
// +----------------------+ +----------------------+ +--------+
|
||||
// +---+ ColumnFamilyHandle 1 | +--+ ColumnFamilyHandle 2 | | DBImpl |
|
||||
// | +----------------------+ | +----------------------+ +----+---+
|
||||
// | +--------------------------+ |
|
||||
// | | +-----------------------------+
|
||||
// | | |
|
||||
// | | +-----------------------------v-------------------------------+
|
||||
// | | | |
|
||||
// | | | ColumnFamilySet |
|
||||
// | | | |
|
||||
// | | +-------------+--------------------------+----------------+---+
|
||||
// | | | | |
|
||||
// | +-------------------------------------+ | |
|
||||
// | | | | v
|
||||
// | +-------------v-------------+ +-----v----v---------+
|
||||
// | | | | |
|
||||
// | | ColumnFamilyData 1 | | ColumnFamilyData 2 | ......
|
||||
// | | | | |
|
||||
// +---> | | |
|
||||
// | +---------+ | |
|
||||
// | | MemTable| | |
|
||||
// | | List | | |
|
||||
// +--------+---+--+-+----+----+ +--------------------++
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | +-----------------------+
|
||||
// | | +-----------+ |
|
||||
// v +--------+ | |
|
||||
// +--------+--------+ | | |
|
||||
// | | | | +----------v----------+
|
||||
// +---> |SuperVersion 1.a +-----------------> |
|
||||
// | +------+ | | MemTableListVersion |
|
||||
// +---+-------------+ | | | | |
|
||||
// | | | | +----+------------+---+
|
||||
// | current | | | | |
|
||||
// | +-------------+ | |mem | |
|
||||
// | | | | | |
|
||||
// +-v---v-------+ +---v--v---+ +-----v----+ +----v-----+
|
||||
// | | | | | | | |
|
||||
// | Version 1.a | | memtable | | memtable | | memtable |
|
||||
// | | | 1.a | | 1.b | | 1.c |
|
||||
// +-------------+ | | | | | |
|
||||
// +----------+ +----------+ +----------+
|
||||
//
|
||||
// DBImpl keeps a ColumnFamilySet, which references to all column families by
|
||||
// pointing to respective ColumnFamilyData object of each column family.
|
||||
// This is how DBImpl can list and operate on all the column families.
|
||||
// ColumnFamilyHandle also points to ColumnFamilyData directly, so that
|
||||
// when a user executes a query, it can directly find memtables and Version
|
||||
// as well as SuperVersion to the column family, without going through
|
||||
// ColumnFamilySet.
|
||||
//
|
||||
// ColumnFamilySet points to the latest view of the LSM-tree (list of memtables
|
||||
// and SST files) indirectly, while ongoing operations may hold references
|
||||
// to a current or an out-of-date SuperVersion, which in turn points to a
|
||||
// point-in-time view of the LSM-tree. This guarantees the memtables and SST
|
||||
// files being operated on will not go away, until the SuperVersion is
|
||||
// unreferenced to 0 and destoryed.
|
||||
//
|
||||
// The following graph illustrates a possible referencing relationships:
|
||||
//
|
||||
// Column +--------------+ current +-----------+
|
||||
// Family +---->+ +------------------->+ |
|
||||
// Data | SuperVersion +----------+ | Version A |
|
||||
// | 3 | imm | | |
|
||||
// Iter2 +----->+ | +-------v------+ +-----------+
|
||||
// +-----+--------+ | MemtableList +----------------> Empty
|
||||
// | | Version r | +-----------+
|
||||
// | +--------------+ | |
|
||||
// +------------------+ current| Version B |
|
||||
// +--------------+ | +----->+ |
|
||||
// | | | | +-----+-----+
|
||||
// Compaction +>+ SuperVersion +-------------+ ^
|
||||
// Job | 2 +------+ | |current
|
||||
// | +----+ | | mem | +------------+
|
||||
// +--------------+ | | +---------------------> |
|
||||
// | +------------------------> MemTable a |
|
||||
// | mem | | |
|
||||
// +--------------+ | | +------------+
|
||||
// | +--------------------------+
|
||||
// Iter1 +-----> SuperVersion | | +------------+
|
||||
// | 1 +------------------------------>+ |
|
||||
// | +-+ | mem | MemTable b |
|
||||
// +--------------+ | | | |
|
||||
// | | +--------------+ +-----^------+
|
||||
// | |imm | MemtableList | |
|
||||
// | +--->+ Version s +------------+
|
||||
// | +--------------+
|
||||
// | +--------------+
|
||||
// | | MemtableList |
|
||||
// +------>+ Version t +--------> Empty
|
||||
// imm +--------------+
|
||||
//
|
||||
// In this example, even if the current LSM-tree consists of Version A and
|
||||
// memtable a, which is also referenced by SuperVersion, two older SuperVersion
|
||||
// SuperVersion2 and Superversion1 still exist, and are referenced by a
|
||||
// compaction job and an old iterator Iter1, respectively. SuperVersion2
|
||||
// contains Version B, memtable a and memtable b; SuperVersion1 contains
|
||||
// Version B and memtable b (mutable). As a result, Version B and memtable b
|
||||
// are prevented from being destroyed or deleted.
|
||||
|
||||
// ColumnFamilyHandleImpl is the class that clients use to access different
|
||||
// column families. It has non-trivial destructor, which gets called when client
|
||||
@@ -232,9 +339,13 @@ class ColumnFamilyData {
|
||||
|
||||
bool is_delete_range_supported() { return is_delete_range_supported_; }
|
||||
|
||||
// Validate CF options against DB options
|
||||
static Status ValidateOptions(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
#ifndef ROCKSDB_LITE
|
||||
// REQUIRES: DB mutex held
|
||||
Status SetOptions(
|
||||
const DBOptions& db_options,
|
||||
const std::unordered_map<std::string, std::string>& options_map);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -394,7 +505,8 @@ class ColumnFamilyData {
|
||||
const ColumnFamilyOptions& options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options,
|
||||
ColumnFamilySet* column_family_set);
|
||||
ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer);
|
||||
|
||||
uint32_t id_;
|
||||
const std::string name_;
|
||||
@@ -522,7 +634,8 @@ class ColumnFamilySet {
|
||||
const ImmutableDBOptions* db_options,
|
||||
const EnvOptions& env_options, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller);
|
||||
WriteController* write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer);
|
||||
~ColumnFamilySet();
|
||||
|
||||
ColumnFamilyData* GetDefault() const;
|
||||
@@ -581,6 +694,7 @@ class ColumnFamilySet {
|
||||
Cache* table_cache_;
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
WriteController* write_controller_;
|
||||
BlockCacheTracer* const block_cache_tracer_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "options/options_parser.h"
|
||||
@@ -20,12 +20,12 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "db/compacted_db_impl.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/version_set.h"
|
||||
#include "table/get_context.h"
|
||||
|
||||
|
||||
+11
-2
@@ -5,9 +5,9 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "db/db_impl.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -85,6 +85,15 @@ class CompactedDBImpl : public DBImpl {
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
@@ -7,19 +7,14 @@
|
||||
// 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.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
|
||||
#pragma once
|
||||
#include "db/version_set.h"
|
||||
#include "memory/arena.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace rocksdb {
|
||||
// The file contains class Compaction, as well as some helper functions
|
||||
// and data structures used by the class.
|
||||
|
||||
// Utility for comparing sstable boundary keys. Returns -1 if either a or b is
|
||||
// null which provides the property that a==null indicates a key that is less
|
||||
@@ -63,7 +65,7 @@ class ColumnFamilyData;
|
||||
class VersionStorageInfo;
|
||||
class CompactionFilter;
|
||||
|
||||
// A Compaction encapsulates information about a compaction.
|
||||
// A Compaction encapsulates metadata about a compaction.
|
||||
class Compaction {
|
||||
public:
|
||||
Compaction(VersionStorageInfo* input_version,
|
||||
@@ -376,7 +378,7 @@ class Compaction {
|
||||
CompactionReason compaction_reason_;
|
||||
};
|
||||
|
||||
// Utility function
|
||||
// Return sum of sizes of all files in `files`.
|
||||
extern uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -3,13 +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_iterator.h"
|
||||
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
#define DEFINITELY_IN_SNAPSHOT(seq, snapshot) \
|
||||
((seq) <= (snapshot) && \
|
||||
@@ -493,20 +492,6 @@ void CompactionIterator::NextFromInput() {
|
||||
// in this snapshot.
|
||||
assert(last_sequence >= current_user_key_sequence_);
|
||||
|
||||
// Note2: if last_snapshot < current_user_key_snapshot, it can only
|
||||
// mean last_snapshot is released between we process last value and
|
||||
// this value, and findEarliestVisibleSnapshot returns the next snapshot
|
||||
// as current_user_key_snapshot. In this case last value and current
|
||||
// value are both in current_user_key_snapshot currently.
|
||||
// Although last_snapshot is released we might still get a definitive
|
||||
// response when key sequence number changes, e.g., when seq is determined
|
||||
// too old and visible in all snapshots.
|
||||
assert(last_snapshot == current_user_key_snapshot_ ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
snapshot_checker_->CheckInSnapshot(current_user_key_sequence_,
|
||||
last_snapshot) !=
|
||||
SnapshotCheckerResult::kNotInSnapshot));
|
||||
|
||||
++iter_stats_.num_record_drop_hidden; // (A)
|
||||
input_->Next();
|
||||
} else if (compaction_ != nullptr && ikey_.type == kTypeDeletion &&
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction_iteration_stats.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/compaction/compaction_iteration_stats.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
@@ -3,15 +3,15 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "port/port.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -7,13 +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/compaction_job.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
@@ -25,7 +19,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/error_handler.h"
|
||||
@@ -38,6 +33,10 @@
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
@@ -47,21 +46,17 @@
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.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"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -324,7 +319,7 @@ CompactionJob::CompactionJob(
|
||||
db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
env_(db_options.env),
|
||||
env_optiosn_for_read_(
|
||||
env_options_for_read_(
|
||||
env_->OptimizeForCompactionTableRead(env_options, db_options_)),
|
||||
versions_(versions),
|
||||
shutting_down_(shutting_down),
|
||||
@@ -415,7 +410,6 @@ void CompactionJob::Prepare() {
|
||||
|
||||
write_hint_ =
|
||||
c->column_family_data()->CalculateSSTWriteHint(c->output_level());
|
||||
// Is this compaction producing files at the bottommost level?
|
||||
bottommost_level_ = c->bottommost_level();
|
||||
|
||||
if (c->ShouldFormSubcompactions()) {
|
||||
@@ -445,11 +439,6 @@ struct RangeWithSize {
|
||||
: range(a, b), size(s) {}
|
||||
};
|
||||
|
||||
// Generates a histogram representing potential divisions of key ranges from
|
||||
// the input. It adds the starting and/or ending keys of certain input files
|
||||
// to the working set and then finds the approximate size of data in between
|
||||
// each consecutive pair of slices. Then it divides these ranges into
|
||||
// consecutive groups such that each group has a similar size.
|
||||
void CompactionJob::GenSubcompactionBoundaries() {
|
||||
auto* c = compact_->compaction;
|
||||
auto* cfd = c->column_family_data();
|
||||
@@ -519,7 +508,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
auto* v = compact_->compaction->input_version();
|
||||
for (auto it = bounds.begin();;) {
|
||||
const Slice a = *it;
|
||||
it++;
|
||||
++it;
|
||||
|
||||
if (it == bounds.end()) {
|
||||
break;
|
||||
@@ -531,7 +520,8 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// to the index block and may incur I/O cost in the process. Unlock db
|
||||
// mutex to reduce contention
|
||||
db_mutex_->Unlock();
|
||||
uint64_t size = versions_->ApproximateSize(v, a, b, start_lvl, out_lvl + 1);
|
||||
uint64_t size = versions_->ApproximateSize(v, a, b, start_lvl, out_lvl + 1,
|
||||
TableReaderCaller::kCompaction);
|
||||
db_mutex_->Lock();
|
||||
ranges.emplace_back(a, b, size);
|
||||
sum += size;
|
||||
@@ -656,12 +646,14 @@ Status CompactionJob::Run() {
|
||||
// to cache it here for further user reads
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
ReadOptions(), env_options_, cfd->internal_comparator(),
|
||||
*files_meta[file_idx], nullptr /* range_del_agg */,
|
||||
prefix_extractor, nullptr,
|
||||
*files_meta[file_idx], /*range_del_agg=*/nullptr, prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr,
|
||||
cfd->internal_stats()->GetFileReadHist(
|
||||
compact_->compaction->output_level()),
|
||||
false, nullptr /* arena */, false /* skip_filters */,
|
||||
compact_->compaction->output_level());
|
||||
TableReaderCaller::kCompactionRefill, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, compact_->compaction->output_level(),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr);
|
||||
auto s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
@@ -842,7 +834,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
// Although the v2 aggregator is what the level iterator(s) know about,
|
||||
// the AddTombstones calls will be propagated down to the v1 aggregator.
|
||||
std::unique_ptr<InternalIterator> input(versions_->MakeInputIterator(
|
||||
sub_compact->compaction, &range_del_agg, env_optiosn_for_read_));
|
||||
sub_compact->compaction, &range_del_agg, env_options_for_read_));
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -893,7 +885,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_), false,
|
||||
&range_del_agg, sub_compact->compaction, compaction_filter,
|
||||
shutting_down_, preserve_deletes_seqnum_, snap_list_callback_));
|
||||
shutting_down_, preserve_deletes_seqnum_,
|
||||
// Currently range_del_agg is incompatible with snapshot refresh feature.
|
||||
range_del_agg.IsEmpty() ? snap_list_callback_ : nullptr));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
@@ -1002,10 +996,13 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
|
||||
RecordCompactionIOStats();
|
||||
|
||||
if (status.ok() &&
|
||||
(shutting_down_->load(std::memory_order_relaxed) || cfd->IsDropped())) {
|
||||
status = Status::ShutdownInProgress(
|
||||
"Database shutdown or Column family drop during compaction");
|
||||
if (status.ok() && cfd->IsDropped()) {
|
||||
status =
|
||||
Status::ColumnFamilyDropped("Column family dropped during compaction");
|
||||
}
|
||||
if ((status.ok() || status.IsColumnFamilyDropped()) &&
|
||||
shutting_down_->load(std::memory_order_relaxed)) {
|
||||
status = Status::ShutdownInProgress("Database shutdown");
|
||||
}
|
||||
if (status.ok()) {
|
||||
status = input->status();
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction_iterator.h"
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/internal_stats.h"
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "logging/event_logger.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "options/db_options.h"
|
||||
#include "port/port.h"
|
||||
@@ -40,7 +41,6 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -55,6 +55,11 @@ class Version;
|
||||
class VersionEdit;
|
||||
class VersionSet;
|
||||
|
||||
// CompactionJob is responsible for executing the compaction. Each (manual or
|
||||
// automated) compaction corresponds to a CompactionJob object, and usually
|
||||
// goes through the stages of `Prepare()`->`Run()`->`Install()`. CompactionJob
|
||||
// will divide the compaction into subcompactions and execute them in parallel
|
||||
// if needed.
|
||||
class CompactionJob {
|
||||
public:
|
||||
CompactionJob(
|
||||
@@ -80,17 +85,28 @@ class CompactionJob {
|
||||
CompactionJob& operator=(const CompactionJob& job) = delete;
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Prepare for the compaction by setting up boundaries for each subcompaction
|
||||
void Prepare();
|
||||
// REQUIRED mutex not held
|
||||
// Launch threads for each subcompaction and wait for them to finish. After
|
||||
// that, verify table is usable and finally do bookkeeping to unify
|
||||
// subcompaction results
|
||||
Status Run();
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Add compaction input/output to the current version
|
||||
Status Install(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
private:
|
||||
struct SubcompactionState;
|
||||
|
||||
void AggregateStatistics();
|
||||
|
||||
// Generates a histogram representing potential divisions of key ranges from
|
||||
// the input. It adds the starting and/or ending keys of certain input files
|
||||
// to the working set and then finds the approximate size of data in between
|
||||
// each consecutive pair of slices. Then it divides these ranges into
|
||||
// consecutive groups such that each group has a similar size.
|
||||
void GenSubcompactionBoundaries();
|
||||
|
||||
// update the thread status for starting a compaction.
|
||||
@@ -135,7 +151,7 @@ class CompactionJob {
|
||||
|
||||
Env* env_;
|
||||
// env_option optimized for compaction table reads
|
||||
EnvOptions env_optiosn_for_read_;
|
||||
EnvOptions env_options_for_read_;
|
||||
VersionSet* versions_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
@@ -163,6 +179,7 @@ class CompactionJob {
|
||||
|
||||
EventLogger* event_logger_;
|
||||
|
||||
// Is this compaction creating a file in the bottom most level?
|
||||
bool bottommost_level_;
|
||||
bool paranoid_file_checks_;
|
||||
bool measure_io_stats_;
|
||||
@@ -7,11 +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.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
@@ -21,12 +17,14 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/logging.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
@@ -47,20 +45,18 @@
|
||||
#include "rocksdb/thread_status.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/plain_table_factory.h"
|
||||
#include "table/plain/plain_table_factory.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/logging.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 "utilities/merge_operators.h"
|
||||
|
||||
#if !defined(IOS_CROSS_COMPILE)
|
||||
@@ -5,11 +5,7 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <map>
|
||||
@@ -17,7 +13,7 @@
|
||||
#include <tuple>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction_job.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/cache.h"
|
||||
@@ -25,10 +21,10 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -81,7 +77,8 @@ class CompactionJobTest : public testing::Test {
|
||||
write_buffer_manager_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_)),
|
||||
&write_controller_,
|
||||
/*block_cache_tracer=*/nullptr)),
|
||||
shutting_down_(false),
|
||||
preserve_deletes_seqnum_(0),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
@@ -204,7 +201,8 @@ class CompactionJobTest : public testing::Test {
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
versions_.reset(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_));
|
||||
&write_controller_,
|
||||
/*block_cache_tracer=*/nullptr));
|
||||
compaction_job_stats_.Reset();
|
||||
|
||||
VersionEdit new_db;
|
||||
@@ -7,25 +7,21 @@
|
||||
// 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.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -42,19 +38,23 @@ uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
|
||||
bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
size_t min_files_to_compact,
|
||||
uint64_t max_compact_bytes_per_del_file,
|
||||
uint64_t max_compaction_bytes,
|
||||
CompactionInputFiles* comp_inputs) {
|
||||
size_t compact_bytes = static_cast<size_t>(level_files[0]->fd.file_size);
|
||||
uint64_t compensated_compact_bytes = level_files[0]->compensated_file_size;
|
||||
size_t compact_bytes_per_del_file = port::kMaxSizet;
|
||||
// compaction range will be [0, span_len).
|
||||
// Compaction range will be [0, span_len).
|
||||
size_t span_len;
|
||||
// pull in files until the amount of compaction work per deleted file begins
|
||||
// increasing.
|
||||
// Pull in files until the amount of compaction work per deleted file begins
|
||||
// increasing or maximum total compaction size is reached.
|
||||
size_t new_compact_bytes_per_del_file = 0;
|
||||
for (span_len = 1; span_len < level_files.size(); ++span_len) {
|
||||
compact_bytes += static_cast<size_t>(level_files[span_len]->fd.file_size);
|
||||
compensated_compact_bytes += level_files[span_len]->compensated_file_size;
|
||||
new_compact_bytes_per_del_file = compact_bytes / span_len;
|
||||
if (level_files[span_len]->being_compacted ||
|
||||
new_compact_bytes_per_del_file > compact_bytes_per_del_file) {
|
||||
new_compact_bytes_per_del_file > compact_bytes_per_del_file ||
|
||||
compensated_compact_bytes > max_compaction_bytes) {
|
||||
break;
|
||||
}
|
||||
compact_bytes_per_del_file = new_compact_bytes_per_del_file;
|
||||
@@ -1107,536 +1107,4 @@ bool CompactionPicker::GetOverlappingL0Files(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevelCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
if (!vstorage->ExpiredTtlFiles().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->BottommostFilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i <= vstorage->MaxInputLevel(); i++) {
|
||||
if (vstorage->CompactionScore(i) >= 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A class to build a leveled compaction step-by-step.
|
||||
class LevelCompactionBuilder {
|
||||
public:
|
||||
LevelCompactionBuilder(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionPicker* compaction_picker,
|
||||
LogBuffer* log_buffer,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableCFOptions& ioptions)
|
||||
: cf_name_(cf_name),
|
||||
vstorage_(vstorage),
|
||||
compaction_picker_(compaction_picker),
|
||||
log_buffer_(log_buffer),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
ioptions_(ioptions) {}
|
||||
|
||||
// Pick and return a compaction.
|
||||
Compaction* PickCompaction();
|
||||
|
||||
// Pick the initial files to compact to the next level. (or together
|
||||
// in Intra-L0 compactions)
|
||||
void SetupInitialFiles();
|
||||
|
||||
// If the initial files are from L0 level, pick other L0
|
||||
// files if needed.
|
||||
bool SetupOtherL0FilesIfNeeded();
|
||||
|
||||
// Based on initial files, setup other files need to be compacted
|
||||
// in this compaction, accordingly.
|
||||
bool SetupOtherInputsIfNeeded();
|
||||
|
||||
Compaction* GetCompaction();
|
||||
|
||||
// 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 PickFileToCompact();
|
||||
|
||||
// For L0->L0, picks the longest span of files that aren't currently
|
||||
// undergoing compaction for which work-per-deleted-file decreases. The span
|
||||
// always starts from the newest L0 file.
|
||||
//
|
||||
// Intra-L0 compaction is independent of all other files, so it can be
|
||||
// performed even when L0->base_level compactions are blocked.
|
||||
//
|
||||
// Returns true if `inputs` is populated with a span of files to be compacted;
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
void PickExpiredTtlFiles();
|
||||
|
||||
void PickFilesMarkedForPeriodicCompaction();
|
||||
|
||||
const std::string& cf_name_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
CompactionPicker* compaction_picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
int start_level_ = -1;
|
||||
int output_level_ = -1;
|
||||
int parent_index_ = -1;
|
||||
int base_index_ = -1;
|
||||
double start_level_score_ = 0;
|
||||
bool is_manual_ = false;
|
||||
CompactionInputFiles start_level_inputs_;
|
||||
std::vector<CompactionInputFiles> compaction_inputs_;
|
||||
CompactionInputFiles output_level_inputs_;
|
||||
std::vector<FileMetaData*> grandparents_;
|
||||
CompactionReason compaction_reason_ = CompactionReason::kUnknown;
|
||||
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
// 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);
|
||||
|
||||
static const int kMinFilesForIntraL0Compaction = 4;
|
||||
};
|
||||
|
||||
void LevelCompactionBuilder::PickExpiredTtlFiles() {
|
||||
if (vstorage_->ExpiredTtlFiles().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
start_level_ = level_file.first;
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
|
||||
if ((start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
(start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->ExpiredTtlFiles()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::PickFilesMarkedForPeriodicCompaction() {
|
||||
if (vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
output_level_ = start_level_ = level_file.first;
|
||||
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->FilesMarkedForPeriodicCompaction()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
// Find the compactions by size on all levels.
|
||||
bool skipped_l0_to_base = false;
|
||||
for (int i = 0; i < compaction_picker_->NumberLevels() - 1; i++) {
|
||||
start_level_score_ = vstorage_->CompactionScore(i);
|
||||
start_level_ = vstorage_->CompactionScoreLevel(i);
|
||||
assert(i == 0 || start_level_score_ <= vstorage_->CompactionScore(i - 1));
|
||||
if (start_level_score_ >= 1) {
|
||||
if (skipped_l0_to_base && start_level_ == vstorage_->base_level()) {
|
||||
// If L0->base_level compaction is pending, don't schedule further
|
||||
// compaction from base level. Otherwise L0->base_level compaction
|
||||
// may starve.
|
||||
continue;
|
||||
}
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
if (PickFileToCompact()) {
|
||||
// found the compaction!
|
||||
if (start_level_ == 0) {
|
||||
// L0 score = `num L0 files` / `level0_file_num_compaction_trigger`
|
||||
compaction_reason_ = CompactionReason::kLevelL0FilesNum;
|
||||
} else {
|
||||
// L1+ score = `Level files size` / `MaxBytesForLevel`
|
||||
compaction_reason_ = CompactionReason::kLevelMaxLevelSize;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// didn't find the compaction, clear the inputs
|
||||
start_level_inputs_.clear();
|
||||
if (start_level_ == 0) {
|
||||
skipped_l0_to_base = true;
|
||||
// L0->base_level may be blocked due to ongoing L0->base_level
|
||||
// compactions. It may also be blocked by an ongoing compaction from
|
||||
// base_level downwards.
|
||||
//
|
||||
// In these cases, to reduce L0 file count and thus reduce likelihood
|
||||
// of write stalls, we can attempt compacting a span of files within
|
||||
// L0.
|
||||
if (PickIntraL0Compaction()) {
|
||||
output_level_ = 0;
|
||||
compaction_reason_ = CompactionReason::kLevelL0FilesNum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find a compaction, check if there are any files marked for
|
||||
// compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
is_manual_ = true;
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Bottommost Files Compaction on deleting tombstones
|
||||
if (start_level_inputs_.empty()) {
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file = vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TTL Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickExpiredTtlFiles();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Periodic Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickFilesMarkedForPeriodicCompaction();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherL0FilesIfNeeded() {
|
||||
if (start_level_ == 0 && output_level_ != 0) {
|
||||
return compaction_picker_->GetOverlappingL0Files(
|
||||
vstorage_, &start_level_inputs_, output_level_, &parent_index_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
// Setup input files from output level. For output to L0, we only compact
|
||||
// spans of files that do not interact with any pending compactions, so don't
|
||||
// need to consider other levels.
|
||||
if (output_level_ != 0) {
|
||||
output_level_inputs_.level = output_level_;
|
||||
if (!compaction_picker_->SetupOtherInputs(
|
||||
cf_name_, mutable_cf_options_, vstorage_, &start_level_inputs_,
|
||||
&output_level_inputs_, &parent_index_, base_index_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
compaction_inputs_.push_back(start_level_inputs_);
|
||||
if (!output_level_inputs_.empty()) {
|
||||
compaction_inputs_.push_back(output_level_inputs_);
|
||||
}
|
||||
|
||||
// In some edge cases we could pick a compaction that will be compacting
|
||||
// a key range that overlap with another running compaction, and both
|
||||
// of them have the same output level. This could happen if
|
||||
// (1) we are running a non-exclusive manual compaction
|
||||
// (2) AddFile ingest a new file into the LSM tree
|
||||
// We need to disallow this from happening.
|
||||
if (compaction_picker_->FilesRangeOverlapWithCompaction(compaction_inputs_,
|
||||
output_level_)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
return false;
|
||||
}
|
||||
compaction_picker_->GetGrandparents(vstorage_, start_level_inputs_,
|
||||
output_level_inputs_, &grandparents_);
|
||||
} else {
|
||||
compaction_inputs_.push_back(start_level_inputs_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::PickCompaction() {
|
||||
// Pick up the first file to start compaction. It may have been extended
|
||||
// to a clean cut.
|
||||
SetupInitialFiles();
|
||||
if (start_level_inputs_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(start_level_ >= 0 && output_level_ >= 0);
|
||||
|
||||
// If it is a L0 -> base level compaction, we need to set up other L0
|
||||
// files if needed.
|
||||
if (!SetupOtherL0FilesIfNeeded()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Pick files in the output level and expand more files in the start level
|
||||
// if needed.
|
||||
if (!SetupOtherInputsIfNeeded()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Form a compaction object containing the files we picked.
|
||||
Compaction* c = GetCompaction();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("LevelCompactionPicker::PickCompaction:Return", c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
auto c = new Compaction(
|
||||
vstorage_, ioptions_, mutable_cf_options_, std::move(compaction_inputs_),
|
||||
output_level_,
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level_,
|
||||
ioptions_.compaction_style, vstorage_->base_level(),
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level_),
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
compaction_picker_->RegisterCompaction(c);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the optimal path to place a file
|
||||
* Given a level, finds the path where levels up to it will fit in levels
|
||||
* up to and including this path
|
||||
*/
|
||||
uint32_t LevelCompactionBuilder::GetPathId(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, int level) {
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.cf_paths.empty());
|
||||
|
||||
// size remaining in the most recent path
|
||||
uint64_t current_path_size = ioptions.cf_paths[0].target_size;
|
||||
|
||||
uint64_t level_size;
|
||||
int cur_level = 0;
|
||||
|
||||
// max_bytes_for_level_base denotes L1 size.
|
||||
// We estimate L0 size to be the same as L1.
|
||||
level_size = mutable_cf_options.max_bytes_for_level_base;
|
||||
|
||||
// Last path is the fallback
|
||||
while (p < ioptions.cf_paths.size() - 1) {
|
||||
if (level_size <= current_path_size) {
|
||||
if (cur_level == level) {
|
||||
// Does desired level fit in this path?
|
||||
return p;
|
||||
} else {
|
||||
current_path_size -= level_size;
|
||||
if (cur_level > 0) {
|
||||
if (ioptions.level_compaction_dynamic_level_bytes) {
|
||||
// Currently, level_compaction_dynamic_level_bytes is ignored when
|
||||
// multiple db paths are specified. https://github.com/facebook/
|
||||
// rocksdb/blob/master/db/column_family.cc.
|
||||
// Still, adding this check to avoid accidentally using
|
||||
// max_bytes_for_level_multiplier_additional
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier);
|
||||
} else {
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier *
|
||||
mutable_cf_options.MaxBytesMultiplerAdditional(cur_level));
|
||||
}
|
||||
}
|
||||
cur_level++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
current_path_size = ioptions.cf_paths[p].target_size;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
// level 0 files are overlapping. So we cannot pick more
|
||||
// than one concurrent compactions at this level. This
|
||||
// could be made better by looking at key-ranges that are
|
||||
// being compacted at level 0.
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
TEST_SYNC_POINT("LevelCompactionPicker::PickCompactionBySize:0");
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.clear();
|
||||
|
||||
assert(start_level_ >= 0);
|
||||
|
||||
// Pick the largest file in this level that is not already
|
||||
// being compacted
|
||||
const std::vector<int>& file_size =
|
||||
vstorage_->FilesByCompactionPri(start_level_);
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
|
||||
unsigned int cmp_idx;
|
||||
for (cmp_idx = vstorage_->NextCompactionIndex(start_level_);
|
||||
cmp_idx < file_size.size(); cmp_idx++) {
|
||||
int index = file_size[cmp_idx];
|
||||
auto* f = level_files[index];
|
||||
|
||||
// do not pick a file to compact if it is being compacted
|
||||
// from n-1 level.
|
||||
if (f->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
start_level_inputs_.files.push_back(f);
|
||||
start_level_inputs_.level = start_level_;
|
||||
if (!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_)) {
|
||||
// A locked (pending compaction) input-level file was pulled in due to
|
||||
// user-key overlap.
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now that input level is fully expanded, we check whether any output files
|
||||
// are locked due to pending compaction.
|
||||
//
|
||||
// Note we rely on ExpandInputsToCleanCut() to tell us whether any output-
|
||||
// level files are locked, not just the extra ones pulled in for user-key
|
||||
// overlap.
|
||||
InternalKey smallest, largest;
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
CompactionInputFiles output_level_inputs;
|
||||
output_level_inputs.level = output_level_;
|
||||
vstorage_->GetOverlappingInputs(output_level_, &smallest, &largest,
|
||||
&output_level_inputs.files);
|
||||
if (!output_level_inputs.empty() &&
|
||||
!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&output_level_inputs)) {
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
base_index_ = index;
|
||||
break;
|
||||
}
|
||||
|
||||
// store where to start the iteration in the next call to PickCompaction
|
||||
vstorage_->SetNextCompactionIndex(start_level_, cmp_idx);
|
||||
|
||||
return start_level_inputs_.size() > 0;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickIntraL0Compaction() {
|
||||
start_level_inputs_.clear();
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(0 /* level */);
|
||||
if (level_files.size() <
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger + 2) ||
|
||||
level_files[0]->being_compacted) {
|
||||
// If L0 isn't accumulating much files beyond the regular trigger, don't
|
||||
// resort to L0->L0 compaction yet.
|
||||
return false;
|
||||
}
|
||||
return FindIntraL0Compaction(level_files, kMinFilesForIntraL0Compaction,
|
||||
port::kMaxUint64, &start_level_inputs_);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
|
||||
mutable_cf_options, ioptions_);
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/version_set.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -24,11 +24,26 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// The file contains an abstract class CompactionPicker, and its two
|
||||
// sub-classes LevelCompactionPicker and NullCompactionPicker, as
|
||||
// well as some helper functions used by them.
|
||||
|
||||
class LogBuffer;
|
||||
class Compaction;
|
||||
class VersionStorageInfo;
|
||||
struct CompactionInputFiles;
|
||||
|
||||
// An abstract class to pick compactions from an existing LSM-tree.
|
||||
//
|
||||
// Each compaction style inherits the class and implement the
|
||||
// interface to form automatic compactions. If NeedCompaction() is true,
|
||||
// then call PickCompaction() to find what files need to be compacted
|
||||
// and where to put the output files.
|
||||
//
|
||||
// Non-virtual functions CompactRange() and CompactFiles() are used to
|
||||
// pick files to compact based on users' DB::CompactRange() and
|
||||
// DB::CompactFiles() requests, respectively. There is little
|
||||
// compaction style specific logic for them.
|
||||
class CompactionPicker {
|
||||
public:
|
||||
CompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
@@ -221,21 +236,9 @@ class CompactionPicker {
|
||||
const InternalKeyComparator* const icmp_;
|
||||
};
|
||||
|
||||
class LevelCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
LevelCompactionPicker(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 bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// A dummy compaction that never triggers any automatic
|
||||
// compaction.
|
||||
class NullCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
NullCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
@@ -273,9 +276,26 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
};
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Attempts to find an intra L0 compaction conforming to the given parameters.
|
||||
//
|
||||
// @param level_files Metadata for L0 files.
|
||||
// @param min_files_to_compact Minimum number of files required to
|
||||
// do the compaction.
|
||||
// @param max_compact_bytes_per_del_file Maximum average size in bytes per
|
||||
// file that is going to get deleted by
|
||||
// the compaction.
|
||||
// @param max_compaction_bytes Maximum total size in bytes (in terms
|
||||
// of compensated file size) for files
|
||||
// to be compacted.
|
||||
// @param [out] comp_inputs If a compaction was found, will be
|
||||
// initialized with corresponding input
|
||||
// files. Cannot be nullptr.
|
||||
//
|
||||
// @return true iff compaction was found.
|
||||
bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
size_t min_files_to_compact,
|
||||
uint64_t max_compact_bytes_per_del_file,
|
||||
uint64_t max_compaction_bytes,
|
||||
CompactionInputFiles* comp_inputs);
|
||||
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
@@ -7,18 +7,14 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -134,7 +130,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
mutable_cf_options
|
||||
.level0_file_num_compaction_trigger /* min_files_to_compact */
|
||||
,
|
||||
max_compact_bytes_per_del_file, &comp_inputs)) {
|
||||
max_compact_bytes_per_del_file,
|
||||
mutable_cf_options.max_compaction_bytes, &comp_inputs)) {
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, {comp_inputs}, 0,
|
||||
16 * 1024 * 1024 /* output file size limit */,
|
||||
@@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
|
||||
namespace rocksdb {
|
||||
class FIFOCompactionPicker : public CompactionPicker {
|
||||
@@ -0,0 +1,553 @@
|
||||
// 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 <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
bool LevelCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
if (!vstorage->ExpiredTtlFiles().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->BottommostFilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i <= vstorage->MaxInputLevel(); i++) {
|
||||
if (vstorage->CompactionScore(i) >= 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A class to build a leveled compaction step-by-step.
|
||||
class LevelCompactionBuilder {
|
||||
public:
|
||||
LevelCompactionBuilder(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionPicker* compaction_picker,
|
||||
LogBuffer* log_buffer,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableCFOptions& ioptions)
|
||||
: cf_name_(cf_name),
|
||||
vstorage_(vstorage),
|
||||
compaction_picker_(compaction_picker),
|
||||
log_buffer_(log_buffer),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
ioptions_(ioptions) {}
|
||||
|
||||
// Pick and return a compaction.
|
||||
Compaction* PickCompaction();
|
||||
|
||||
// Pick the initial files to compact to the next level. (or together
|
||||
// in Intra-L0 compactions)
|
||||
void SetupInitialFiles();
|
||||
|
||||
// If the initial files are from L0 level, pick other L0
|
||||
// files if needed.
|
||||
bool SetupOtherL0FilesIfNeeded();
|
||||
|
||||
// Based on initial files, setup other files need to be compacted
|
||||
// in this compaction, accordingly.
|
||||
bool SetupOtherInputsIfNeeded();
|
||||
|
||||
Compaction* GetCompaction();
|
||||
|
||||
// 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 PickFileToCompact();
|
||||
|
||||
// For L0->L0, picks the longest span of files that aren't currently
|
||||
// undergoing compaction for which work-per-deleted-file decreases. The span
|
||||
// always starts from the newest L0 file.
|
||||
//
|
||||
// Intra-L0 compaction is independent of all other files, so it can be
|
||||
// performed even when L0->base_level compactions are blocked.
|
||||
//
|
||||
// Returns true if `inputs` is populated with a span of files to be compacted;
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
void PickExpiredTtlFiles();
|
||||
|
||||
void PickFilesMarkedForPeriodicCompaction();
|
||||
|
||||
const std::string& cf_name_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
CompactionPicker* compaction_picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
int start_level_ = -1;
|
||||
int output_level_ = -1;
|
||||
int parent_index_ = -1;
|
||||
int base_index_ = -1;
|
||||
double start_level_score_ = 0;
|
||||
bool is_manual_ = false;
|
||||
CompactionInputFiles start_level_inputs_;
|
||||
std::vector<CompactionInputFiles> compaction_inputs_;
|
||||
CompactionInputFiles output_level_inputs_;
|
||||
std::vector<FileMetaData*> grandparents_;
|
||||
CompactionReason compaction_reason_ = CompactionReason::kUnknown;
|
||||
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
// 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);
|
||||
|
||||
static const int kMinFilesForIntraL0Compaction = 4;
|
||||
};
|
||||
|
||||
void LevelCompactionBuilder::PickExpiredTtlFiles() {
|
||||
if (vstorage_->ExpiredTtlFiles().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
start_level_ = level_file.first;
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
|
||||
if ((start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
(start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->ExpiredTtlFiles()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::PickFilesMarkedForPeriodicCompaction() {
|
||||
if (vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
output_level_ = start_level_ = level_file.first;
|
||||
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->FilesMarkedForPeriodicCompaction()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
// Find the compactions by size on all levels.
|
||||
bool skipped_l0_to_base = false;
|
||||
for (int i = 0; i < compaction_picker_->NumberLevels() - 1; i++) {
|
||||
start_level_score_ = vstorage_->CompactionScore(i);
|
||||
start_level_ = vstorage_->CompactionScoreLevel(i);
|
||||
assert(i == 0 || start_level_score_ <= vstorage_->CompactionScore(i - 1));
|
||||
if (start_level_score_ >= 1) {
|
||||
if (skipped_l0_to_base && start_level_ == vstorage_->base_level()) {
|
||||
// If L0->base_level compaction is pending, don't schedule further
|
||||
// compaction from base level. Otherwise L0->base_level compaction
|
||||
// may starve.
|
||||
continue;
|
||||
}
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
if (PickFileToCompact()) {
|
||||
// found the compaction!
|
||||
if (start_level_ == 0) {
|
||||
// L0 score = `num L0 files` / `level0_file_num_compaction_trigger`
|
||||
compaction_reason_ = CompactionReason::kLevelL0FilesNum;
|
||||
} else {
|
||||
// L1+ score = `Level files size` / `MaxBytesForLevel`
|
||||
compaction_reason_ = CompactionReason::kLevelMaxLevelSize;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// didn't find the compaction, clear the inputs
|
||||
start_level_inputs_.clear();
|
||||
if (start_level_ == 0) {
|
||||
skipped_l0_to_base = true;
|
||||
// L0->base_level may be blocked due to ongoing L0->base_level
|
||||
// compactions. It may also be blocked by an ongoing compaction from
|
||||
// base_level downwards.
|
||||
//
|
||||
// In these cases, to reduce L0 file count and thus reduce likelihood
|
||||
// of write stalls, we can attempt compacting a span of files within
|
||||
// L0.
|
||||
if (PickIntraL0Compaction()) {
|
||||
output_level_ = 0;
|
||||
compaction_reason_ = CompactionReason::kLevelL0FilesNum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find a compaction, check if there are any files marked for
|
||||
// compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_,
|
||||
&start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
is_manual_ = true;
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Bottommost Files Compaction on deleting tombstones
|
||||
if (start_level_inputs_.empty()) {
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file = vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TTL Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickExpiredTtlFiles();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Periodic Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickFilesMarkedForPeriodicCompaction();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherL0FilesIfNeeded() {
|
||||
if (start_level_ == 0 && output_level_ != 0) {
|
||||
return compaction_picker_->GetOverlappingL0Files(
|
||||
vstorage_, &start_level_inputs_, output_level_, &parent_index_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
// Setup input files from output level. For output to L0, we only compact
|
||||
// spans of files that do not interact with any pending compactions, so don't
|
||||
// need to consider other levels.
|
||||
if (output_level_ != 0) {
|
||||
output_level_inputs_.level = output_level_;
|
||||
if (!compaction_picker_->SetupOtherInputs(
|
||||
cf_name_, mutable_cf_options_, vstorage_, &start_level_inputs_,
|
||||
&output_level_inputs_, &parent_index_, base_index_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
compaction_inputs_.push_back(start_level_inputs_);
|
||||
if (!output_level_inputs_.empty()) {
|
||||
compaction_inputs_.push_back(output_level_inputs_);
|
||||
}
|
||||
|
||||
// In some edge cases we could pick a compaction that will be compacting
|
||||
// a key range that overlap with another running compaction, and both
|
||||
// of them have the same output level. This could happen if
|
||||
// (1) we are running a non-exclusive manual compaction
|
||||
// (2) AddFile ingest a new file into the LSM tree
|
||||
// We need to disallow this from happening.
|
||||
if (compaction_picker_->FilesRangeOverlapWithCompaction(compaction_inputs_,
|
||||
output_level_)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
return false;
|
||||
}
|
||||
compaction_picker_->GetGrandparents(vstorage_, start_level_inputs_,
|
||||
output_level_inputs_, &grandparents_);
|
||||
} else {
|
||||
compaction_inputs_.push_back(start_level_inputs_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::PickCompaction() {
|
||||
// Pick up the first file to start compaction. It may have been extended
|
||||
// to a clean cut.
|
||||
SetupInitialFiles();
|
||||
if (start_level_inputs_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(start_level_ >= 0 && output_level_ >= 0);
|
||||
|
||||
// If it is a L0 -> base level compaction, we need to set up other L0
|
||||
// files if needed.
|
||||
if (!SetupOtherL0FilesIfNeeded()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Pick files in the output level and expand more files in the start level
|
||||
// if needed.
|
||||
if (!SetupOtherInputsIfNeeded()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Form a compaction object containing the files we picked.
|
||||
Compaction* c = GetCompaction();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("LevelCompactionPicker::PickCompaction:Return", c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
auto c = new Compaction(
|
||||
vstorage_, ioptions_, mutable_cf_options_, std::move(compaction_inputs_),
|
||||
output_level_,
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level_,
|
||||
ioptions_.compaction_style, vstorage_->base_level(),
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level_),
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
compaction_picker_->RegisterCompaction(c);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the optimal path to place a file
|
||||
* Given a level, finds the path where levels up to it will fit in levels
|
||||
* up to and including this path
|
||||
*/
|
||||
uint32_t LevelCompactionBuilder::GetPathId(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, int level) {
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.cf_paths.empty());
|
||||
|
||||
// size remaining in the most recent path
|
||||
uint64_t current_path_size = ioptions.cf_paths[0].target_size;
|
||||
|
||||
uint64_t level_size;
|
||||
int cur_level = 0;
|
||||
|
||||
// max_bytes_for_level_base denotes L1 size.
|
||||
// We estimate L0 size to be the same as L1.
|
||||
level_size = mutable_cf_options.max_bytes_for_level_base;
|
||||
|
||||
// Last path is the fallback
|
||||
while (p < ioptions.cf_paths.size() - 1) {
|
||||
if (level_size <= current_path_size) {
|
||||
if (cur_level == level) {
|
||||
// Does desired level fit in this path?
|
||||
return p;
|
||||
} else {
|
||||
current_path_size -= level_size;
|
||||
if (cur_level > 0) {
|
||||
if (ioptions.level_compaction_dynamic_level_bytes) {
|
||||
// Currently, level_compaction_dynamic_level_bytes is ignored when
|
||||
// multiple db paths are specified. https://github.com/facebook/
|
||||
// rocksdb/blob/master/db/column_family.cc.
|
||||
// Still, adding this check to avoid accidentally using
|
||||
// max_bytes_for_level_multiplier_additional
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier);
|
||||
} else {
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier *
|
||||
mutable_cf_options.MaxBytesMultiplerAdditional(cur_level));
|
||||
}
|
||||
}
|
||||
cur_level++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
current_path_size = ioptions.cf_paths[p].target_size;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
// level 0 files are overlapping. So we cannot pick more
|
||||
// than one concurrent compactions at this level. This
|
||||
// could be made better by looking at key-ranges that are
|
||||
// being compacted at level 0.
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
TEST_SYNC_POINT("LevelCompactionPicker::PickCompactionBySize:0");
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.clear();
|
||||
|
||||
assert(start_level_ >= 0);
|
||||
|
||||
// Pick the largest file in this level that is not already
|
||||
// being compacted
|
||||
const std::vector<int>& file_size =
|
||||
vstorage_->FilesByCompactionPri(start_level_);
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
|
||||
unsigned int cmp_idx;
|
||||
for (cmp_idx = vstorage_->NextCompactionIndex(start_level_);
|
||||
cmp_idx < file_size.size(); cmp_idx++) {
|
||||
int index = file_size[cmp_idx];
|
||||
auto* f = level_files[index];
|
||||
|
||||
// do not pick a file to compact if it is being compacted
|
||||
// from n-1 level.
|
||||
if (f->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
start_level_inputs_.files.push_back(f);
|
||||
start_level_inputs_.level = start_level_;
|
||||
if (!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_)) {
|
||||
// A locked (pending compaction) input-level file was pulled in due to
|
||||
// user-key overlap.
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now that input level is fully expanded, we check whether any output files
|
||||
// are locked due to pending compaction.
|
||||
//
|
||||
// Note we rely on ExpandInputsToCleanCut() to tell us whether any output-
|
||||
// level files are locked, not just the extra ones pulled in for user-key
|
||||
// overlap.
|
||||
InternalKey smallest, largest;
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
CompactionInputFiles output_level_inputs;
|
||||
output_level_inputs.level = output_level_;
|
||||
vstorage_->GetOverlappingInputs(output_level_, &smallest, &largest,
|
||||
&output_level_inputs.files);
|
||||
if (!output_level_inputs.empty() &&
|
||||
!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&output_level_inputs)) {
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
base_index_ = index;
|
||||
break;
|
||||
}
|
||||
|
||||
// store where to start the iteration in the next call to PickCompaction
|
||||
vstorage_->SetNextCompactionIndex(start_level_, cmp_idx);
|
||||
|
||||
return start_level_inputs_.size() > 0;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickIntraL0Compaction() {
|
||||
start_level_inputs_.clear();
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(0 /* level */);
|
||||
if (level_files.size() <
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger + 2) ||
|
||||
level_files[0]->being_compacted) {
|
||||
// If L0 isn't accumulating much files beyond the regular trigger, don't
|
||||
// resort to L0->L0 compaction yet.
|
||||
return false;
|
||||
}
|
||||
return FindIntraL0Compaction(
|
||||
level_files, kMinFilesForIntraL0Compaction, port::kMaxUint64,
|
||||
mutable_cf_options_.max_compaction_bytes, &start_level_inputs_);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
|
||||
mutable_cf_options, ioptions_);
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
|
||||
namespace rocksdb {
|
||||
// Picking compactions for leveled compaction. See wiki page
|
||||
// https://github.com/facebook/rocksdb/wiki/Leveled-Compaction
|
||||
// for description of Leveled compaction.
|
||||
class LevelCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
LevelCompactionPicker(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 bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -3,19 +3,19 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#include "db/compaction_picker_universal.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "logging/logging.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -1478,6 +1478,65 @@ TEST_F(CompactionPickerTest, CacheNextCompactionIndex) {
|
||||
ASSERT_EQ(4, vstorage_->NextCompactionIndex(1 /* level */));
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IntraL0MaxCompactionBytesNotHit) {
|
||||
// Intra L0 compaction triggers only if there are at least
|
||||
// level0_file_num_compaction_trigger + 2 L0 files.
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 3;
|
||||
mutable_cf_options_.max_compaction_bytes = 1000000u;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
|
||||
// All 5 L0 files will be picked for intra L0 compaction. The one L1 file
|
||||
// spans entire L0 key range and is marked as being compacted to avoid
|
||||
// L0->L1 compaction.
|
||||
Add(0, 1U, "100", "150", 200000U);
|
||||
Add(0, 2U, "151", "200", 200000U);
|
||||
Add(0, 3U, "201", "250", 200000U);
|
||||
Add(0, 4U, "251", "300", 200000U);
|
||||
Add(0, 5U, "301", "350", 200000U);
|
||||
Add(1, 6U, "100", "350", 200000U);
|
||||
vstorage_->LevelFiles(1)[0]->being_compacted = true;
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(5U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0U, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IntraL0MaxCompactionBytesHit) {
|
||||
// Intra L0 compaction triggers only if there are at least
|
||||
// level0_file_num_compaction_trigger + 2 L0 files.
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 3;
|
||||
mutable_cf_options_.max_compaction_bytes = 999999u;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
|
||||
// 4 out of 5 L0 files will be picked for intra L0 compaction due to
|
||||
// max_compaction_bytes limit (the minimum number of files for triggering
|
||||
// intra L0 compaction is 4). The one L1 file spans entire L0 key range and
|
||||
// is marked as being compacted to avoid L0->L1 compaction.
|
||||
Add(0, 1U, "100", "150", 200000U);
|
||||
Add(0, 2U, "151", "200", 200000U);
|
||||
Add(0, 3U, "201", "250", 200000U);
|
||||
Add(0, 4U, "251", "300", 200000U);
|
||||
Add(0, 5U, "301", "350", 200000U);
|
||||
Add(1, 6U, "100", "350", 200000U);
|
||||
vstorage_->LevelFiles(1)[0]->being_compacted = true;
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0U, compaction->output_level());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -7,25 +7,21 @@
|
||||
// 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"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "db/column_family.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace {
|
||||
@@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
|
||||
namespace rocksdb {
|
||||
class UniversalCompactionPicker : public CompactionPicker {
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "memtable/stl_wrappers.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/kv_map.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "rocksdb/convenience.h"
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -59,7 +59,7 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
s = table_reader->VerifyChecksum();
|
||||
s = table_reader->VerifyChecksum(TableReaderCaller::kUserVerifyChecksum);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,23 +13,23 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/filename.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "table/block_based_table_builder.h"
|
||||
#include "table/block_based/block_based_table_builder.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "util/filename.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "table/cuckoo_table_factory.h"
|
||||
#include "table/cuckoo_table_reader.h"
|
||||
#include "table/cuckoo/cuckoo_table_factory.h"
|
||||
#include "table/cuckoo/cuckoo_table_reader.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+492
-2
@@ -10,9 +10,11 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "table/block_based/block_builder.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#endif
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -1284,6 +1286,494 @@ TEST_F(DBBasicTest, MultiGetBatchedMultiLevel) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
const size_t kNumInserts = 4;
|
||||
const size_t kNumDeletes = 4;
|
||||
const size_t kNumUpdates = 4;
|
||||
|
||||
// Check default column family
|
||||
for (size_t i = 0; i != kNumInserts; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(i), "value"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumUpdates; ++i) {
|
||||
ASSERT_OK(Put(std::to_string(i), "value1"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumDeletes; ++i) {
|
||||
ASSERT_OK(Delete(std::to_string(i)));
|
||||
}
|
||||
std::vector<KeyVersion> key_versions;
|
||||
ASSERT_OK(rocksdb::GetAllKeyVersions(db_, Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
ASSERT_OK(rocksdb::GetAllKeyVersions(db_, handles_[0], Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
|
||||
// Check non-default column family
|
||||
for (size_t i = 0; i != kNumInserts - 1; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "value"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumUpdates - 1; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "value1"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumDeletes - 1; ++i) {
|
||||
ASSERT_OK(Delete(1, std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(rocksdb::GetAllKeyVersions(db_, handles_[1], Slice(), Slice(),
|
||||
std::numeric_limits<size_t>::max(),
|
||||
&key_versions));
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates - 3, key_versions.size());
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
class DBBasicTestWithParallelIO
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool,bool,bool,bool>> {
|
||||
public:
|
||||
DBBasicTestWithParallelIO()
|
||||
: DBTestBase("/db_basic_test_with_parallel_io") {
|
||||
bool compressed_cache = std::get<0>(GetParam());
|
||||
bool uncompressed_cache = std::get<1>(GetParam());
|
||||
compression_enabled_ = std::get<2>(GetParam());
|
||||
fill_cache_ = std::get<3>(GetParam());
|
||||
|
||||
if (compressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
compressed_cache_ = std::make_shared<MyBlockCache>(cache);
|
||||
}
|
||||
if (uncompressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
uncompressed_cache_ = std::make_shared<MyBlockCache>(cache);
|
||||
}
|
||||
|
||||
env_->count_random_reads_ = true;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
Random rnd(301);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.block_cache = uncompressed_cache_;
|
||||
table_options.block_cache_compressed = compressed_cache_;
|
||||
table_options.flush_block_policy_factory.reset(
|
||||
new MyFlushBlockPolicyFactory());
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
if (!compression_enabled_) {
|
||||
options.compression = kNoCompression;
|
||||
}
|
||||
Reopen(options);
|
||||
|
||||
std::string zero_str(128, '\0');
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// Make the value compressible. A purely random string doesn't compress
|
||||
// and the resultant data block will not be compressed
|
||||
values_.emplace_back(RandomString(&rnd, 128) + zero_str);
|
||||
assert(Put(Key(i), values_[i]) == Status::OK());
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
bool CheckValue(int i, const std::string& value) {
|
||||
if (values_[i].compare(value) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int num_lookups() { return uncompressed_cache_->num_lookups(); }
|
||||
int num_found() { return uncompressed_cache_->num_found(); }
|
||||
int num_inserts() { return uncompressed_cache_->num_inserts(); }
|
||||
|
||||
int num_lookups_compressed() {
|
||||
return compressed_cache_->num_lookups();
|
||||
}
|
||||
int num_found_compressed() {
|
||||
return compressed_cache_->num_found();
|
||||
}
|
||||
int num_inserts_compressed() {
|
||||
return compressed_cache_->num_inserts();
|
||||
}
|
||||
|
||||
bool fill_cache() { return fill_cache_; }
|
||||
|
||||
static void SetUpTestCase() {}
|
||||
static void TearDownTestCase() {}
|
||||
|
||||
private:
|
||||
class MyFlushBlockPolicyFactory
|
||||
: public FlushBlockPolicyFactory {
|
||||
public:
|
||||
MyFlushBlockPolicyFactory() {}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "MyFlushBlockPolicyFactory";
|
||||
}
|
||||
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new MyFlushBlockPolicy(data_block_builder);
|
||||
}
|
||||
};
|
||||
|
||||
class MyFlushBlockPolicy
|
||||
: public FlushBlockPolicy {
|
||||
public:
|
||||
explicit MyFlushBlockPolicy(const BlockBuilder& data_block_builder)
|
||||
: num_keys_(0), data_block_builder_(data_block_builder) {}
|
||||
|
||||
bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (data_block_builder_.empty()) {
|
||||
// First key in this block
|
||||
num_keys_ = 1;
|
||||
return false;
|
||||
}
|
||||
// Flush every 10 keys
|
||||
if (num_keys_ == 10) {
|
||||
num_keys_ = 1;
|
||||
return true;
|
||||
}
|
||||
num_keys_++;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int num_keys_;
|
||||
const BlockBuilder& data_block_builder_;
|
||||
};
|
||||
|
||||
class MyBlockCache
|
||||
: public Cache {
|
||||
public:
|
||||
explicit MyBlockCache(std::shared_ptr<Cache>& target)
|
||||
: target_(target), num_lookups_(0), num_found_(0), num_inserts_(0) {}
|
||||
|
||||
virtual const char* Name() const override { return "MyBlockCache"; }
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
num_inserts_++;
|
||||
return target_->Insert(key, value, charge, deleter, handle, priority);
|
||||
}
|
||||
|
||||
virtual Handle* Lookup(const Slice& key,
|
||||
Statistics* stats = nullptr) override {
|
||||
num_lookups_++;
|
||||
Handle* handle = target_->Lookup(key, stats);
|
||||
if (handle != nullptr) {
|
||||
num_found_++;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
virtual bool Ref(Handle* handle) override {
|
||||
return target_->Ref(handle);
|
||||
}
|
||||
|
||||
virtual bool Release(Handle* handle, bool force_erase = false) override {
|
||||
return target_->Release(handle, force_erase);
|
||||
}
|
||||
|
||||
virtual void* Value(Handle* handle) override {
|
||||
return target_->Value(handle);
|
||||
}
|
||||
|
||||
virtual void Erase(const Slice& key) override {
|
||||
target_->Erase(key);
|
||||
}
|
||||
virtual uint64_t NewId() override {
|
||||
return target_->NewId();
|
||||
}
|
||||
|
||||
virtual void SetCapacity(size_t capacity) override {
|
||||
target_->SetCapacity(capacity);
|
||||
}
|
||||
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override {
|
||||
target_->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
virtual bool HasStrictCapacityLimit() const override {
|
||||
return target_->HasStrictCapacityLimit();
|
||||
}
|
||||
|
||||
virtual size_t GetCapacity() const override {
|
||||
return target_->GetCapacity();
|
||||
}
|
||||
|
||||
virtual size_t GetUsage() const override {
|
||||
return target_->GetUsage();
|
||||
}
|
||||
|
||||
virtual size_t GetUsage(Handle* handle) const override {
|
||||
return target_->GetUsage(handle);
|
||||
}
|
||||
|
||||
virtual size_t GetPinnedUsage() const override {
|
||||
return target_->GetPinnedUsage();
|
||||
}
|
||||
|
||||
virtual size_t GetCharge(Handle* /*handle*/) const override { return 0; }
|
||||
|
||||
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override {
|
||||
return target_->ApplyToAllCacheEntries(callback, thread_safe);
|
||||
}
|
||||
|
||||
virtual void EraseUnRefEntries() override {
|
||||
return target_->EraseUnRefEntries();
|
||||
}
|
||||
|
||||
int num_lookups() { return num_lookups_; }
|
||||
|
||||
int num_found() { return num_found_; }
|
||||
|
||||
int num_inserts() { return num_inserts_; }
|
||||
private:
|
||||
std::shared_ptr<Cache> target_;
|
||||
int num_lookups_;
|
||||
int num_found_;
|
||||
int num_inserts_;
|
||||
};
|
||||
|
||||
std::shared_ptr<MyBlockCache> compressed_cache_;
|
||||
std::shared_ptr<MyBlockCache> uncompressed_cache_;
|
||||
bool compression_enabled_;
|
||||
std::vector<std::string> values_;
|
||||
bool fill_cache_;
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithParallelIO, MultiGet) {
|
||||
std::vector<std::string> key_data(10);
|
||||
std::vector<Slice> keys;
|
||||
// We cannot resize a PinnableSlice vector, so just set initial size to
|
||||
// largest we think we will need
|
||||
std::vector<PinnableSlice> values(10);
|
||||
std::vector<Status> statuses;
|
||||
ReadOptions ro;
|
||||
ro.fill_cache = fill_cache();
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
ASSERT_TRUE(CheckValue(0, values[0].ToString()));
|
||||
ASSERT_TRUE(CheckValue(50, values[1].ToString()));
|
||||
|
||||
int random_reads = env_->random_read_counter_.Read();
|
||||
key_data[0] = Key(1);
|
||||
key_data[1] = Key(51);
|
||||
keys[0] = Slice(key_data[0]);
|
||||
keys[1] = Slice(key_data[1]);
|
||||
values[0].Reset();
|
||||
values[1].Reset();
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
ASSERT_TRUE(CheckValue(1, values[0].ToString()));
|
||||
ASSERT_TRUE(CheckValue(51, values[1].ToString()));
|
||||
|
||||
int expected_reads = random_reads + (fill_cache() ? 0 : 2);
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), expected_reads);
|
||||
|
||||
keys.resize(10);
|
||||
statuses.resize(10);
|
||||
std::vector<int> key_ints{1,2,15,16,55,81,82,83,84,85};
|
||||
for (size_t i = 0; i < key_ints.size(); ++i) {
|
||||
key_data[i] = Key(key_ints[i]);
|
||||
keys[i] = Slice(key_data[i]);
|
||||
statuses[i] = Status::OK();
|
||||
values[i].Reset();
|
||||
}
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
for (size_t i = 0; i < key_ints.size(); ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
ASSERT_TRUE(CheckValue(key_ints[i], values[i].ToString()));
|
||||
}
|
||||
expected_reads += (fill_cache() ? 2 : 4);
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), expected_reads);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ParallelIO, DBBasicTestWithParallelIO,
|
||||
// Params are as follows -
|
||||
// Param 0 - Compressed cache enabled
|
||||
// Param 1 - Uncompressed cache enabled
|
||||
// Param 2 - Data compression enabled
|
||||
// Param 3 - ReadOptions::fill_cache
|
||||
::testing::Values(std::make_tuple(false, true, true, true),
|
||||
std::make_tuple(true, true, true, true),
|
||||
std::make_tuple(false, true, false, true),
|
||||
std::make_tuple(false, true, true, false),
|
||||
std::make_tuple(true, true, true, false),
|
||||
std::make_tuple(false, true, false, false)));
|
||||
|
||||
class DBBasicTestWithTimestampWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBBasicTestWithTimestampWithParam()
|
||||
: DBTestBase("/db_basic_test_with_timestamp") {}
|
||||
|
||||
protected:
|
||||
class TestComparator : public Comparator {
|
||||
private:
|
||||
const Comparator* cmp_without_ts_;
|
||||
|
||||
public:
|
||||
explicit TestComparator(size_t ts_sz)
|
||||
: Comparator(ts_sz), cmp_without_ts_(nullptr) {
|
||||
cmp_without_ts_ = BytewiseComparator();
|
||||
}
|
||||
|
||||
const char* Name() const override { return "TestComparator"; }
|
||||
|
||||
void FindShortSuccessor(std::string*) const override {}
|
||||
|
||||
void FindShortestSeparator(std::string*, const Slice&) const override {}
|
||||
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
int r = CompareWithoutTimestamp(a, b);
|
||||
if (r != 0 || 0 == timestamp_size()) {
|
||||
return r;
|
||||
}
|
||||
return CompareTimestamp(
|
||||
Slice(a.data() + a.size() - timestamp_size(), timestamp_size()),
|
||||
Slice(b.data() + b.size() - timestamp_size(), timestamp_size()));
|
||||
}
|
||||
|
||||
int CompareWithoutTimestamp(const Slice& a, const Slice& b) const override {
|
||||
assert(a.size() >= timestamp_size());
|
||||
assert(b.size() >= timestamp_size());
|
||||
Slice k1 = StripTimestampFromUserKey(a, timestamp_size());
|
||||
Slice k2 = StripTimestampFromUserKey(b, timestamp_size());
|
||||
|
||||
return cmp_without_ts_->Compare(k1, k2);
|
||||
}
|
||||
|
||||
int CompareTimestamp(const Slice& ts1, const Slice& ts2) const override {
|
||||
if (!ts1.data() && !ts2.data()) {
|
||||
return 0;
|
||||
} else if (ts1.data() && !ts2.data()) {
|
||||
return 1;
|
||||
} else if (!ts1.data() && ts2.data()) {
|
||||
return -1;
|
||||
}
|
||||
assert(ts1.size() == ts2.size());
|
||||
uint64_t low1 = 0;
|
||||
uint64_t low2 = 0;
|
||||
uint64_t high1 = 0;
|
||||
uint64_t high2 = 0;
|
||||
auto* ptr1 = const_cast<Slice*>(&ts1);
|
||||
auto* ptr2 = const_cast<Slice*>(&ts2);
|
||||
if (!GetFixed64(ptr1, &low1) || !GetFixed64(ptr1, &high1) ||
|
||||
!GetFixed64(ptr2, &low2) || !GetFixed64(ptr2, &high2)) {
|
||||
assert(false);
|
||||
}
|
||||
if (high1 < high2) {
|
||||
return 1;
|
||||
} else if (high1 > high2) {
|
||||
return -1;
|
||||
}
|
||||
if (low1 < low2) {
|
||||
return 1;
|
||||
} else if (low1 > low2) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
Slice EncodeTimestamp(uint64_t low, uint64_t high, std::string* ts) {
|
||||
assert(nullptr != ts);
|
||||
ts->clear();
|
||||
PutFixed64(ts, low);
|
||||
PutFixed64(ts, high);
|
||||
assert(ts->size() == sizeof(low) + sizeof(high));
|
||||
return Slice(*ts);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampWithParam, PutAndGet) {
|
||||
const int kNumKeysPerFile = 8192;
|
||||
const size_t kNumTimestamps = 6;
|
||||
bool memtable_only = GetParam();
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
std::string tmp;
|
||||
size_t ts_sz = EncodeTimestamp(0, 0, &tmp).size();
|
||||
TestComparator test_cmp(ts_sz);
|
||||
options.comparator = &test_cmp;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(
|
||||
10 /*bits_per_key*/, false /*use_block_based_builder*/));
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(2, num_cfs);
|
||||
std::vector<std::string> write_ts_strs(kNumTimestamps);
|
||||
std::vector<std::string> read_ts_strs(kNumTimestamps);
|
||||
std::vector<Slice> write_ts_list;
|
||||
std::vector<Slice> read_ts_list;
|
||||
|
||||
for (size_t i = 0; i != kNumTimestamps; ++i) {
|
||||
write_ts_list.emplace_back(EncodeTimestamp(i * 2, 0, &write_ts_strs[i]));
|
||||
read_ts_list.emplace_back(EncodeTimestamp(1 + i * 2, 0, &read_ts_strs[i]));
|
||||
const Slice& write_ts = write_ts_list.back();
|
||||
WriteOptions wopts;
|
||||
wopts.timestamp = &write_ts;
|
||||
for (int cf = 0; cf != static_cast<int>(num_cfs); ++cf) {
|
||||
for (size_t j = 0; j != (kNumKeysPerFile - 1) / kNumTimestamps; ++j) {
|
||||
ASSERT_OK(Put(cf, "key" + std::to_string(j),
|
||||
"value_" + std::to_string(j) + "_" + std::to_string(i),
|
||||
wopts));
|
||||
}
|
||||
if (!memtable_only) {
|
||||
ASSERT_OK(Flush(cf));
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto& verify_db_func = [&]() {
|
||||
for (size_t i = 0; i != kNumTimestamps; ++i) {
|
||||
ReadOptions ropts;
|
||||
ropts.timestamp = &read_ts_list[i];
|
||||
for (int cf = 0; cf != static_cast<int>(num_cfs); ++cf) {
|
||||
ColumnFamilyHandle* cfh = handles_[cf];
|
||||
for (size_t j = 0; j != (kNumKeysPerFile - 1) / kNumTimestamps; ++j) {
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ropts, cfh, "key" + std::to_string(j), &value));
|
||||
ASSERT_EQ("value_" + std::to_string(j) + "_" + std::to_string(i),
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
verify_db_func();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Timestamp, DBBasicTestWithTimestampWithParam,
|
||||
::testing::Bool());
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -365,8 +365,11 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
ASSERT_EQ(cache->GetUsage(), index_bytes_insert + filter_bytes_insert);
|
||||
// set the cache capacity to the current usage
|
||||
cache->SetCapacity(index_bytes_insert + filter_bytes_insert);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT), 0);
|
||||
// The index and filter eviction statistics were broken by the refactoring
|
||||
// that moved the readers out of the block cache. Disabling these until we can
|
||||
// bring the stats back.
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT), 0);
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT), 0);
|
||||
// Note that the second key needs to be no longer than the first one.
|
||||
// Otherwise the second index block may not fit in cache.
|
||||
ASSERT_OK(Put(1, "key", "val"));
|
||||
@@ -377,10 +380,13 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
index_bytes_insert);
|
||||
ASSERT_GT(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_INSERT),
|
||||
filter_bytes_insert);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT),
|
||||
index_bytes_insert);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT),
|
||||
filter_bytes_insert);
|
||||
// The index and filter eviction statistics were broken by the refactoring
|
||||
// that moved the readers out of the block cache. Disabling these until we can
|
||||
// bring the stats back.
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT),
|
||||
// index_bytes_insert);
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT),
|
||||
// filter_bytes_insert);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -1095,6 +1095,8 @@ TEST_F(DBBloomFilterTest, PrefixScan) {
|
||||
options.max_background_compactions = 2;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
assert(!options.unordered_write);
|
||||
// It is incompatible with allow_concurrent_memtable_write=false
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
+49
-18
@@ -13,9 +13,9 @@
|
||||
#include "rocksdb/concurrent_task_limiter.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/utilities/convenience.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/concurrent_task_limiter_impl.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -497,14 +497,14 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
|
||||
// Create new iterator for:
|
||||
// (1) 1 for verifying flush results
|
||||
// (2) 3 for compaction input files
|
||||
// (3) 1 for verifying compaction results.
|
||||
ASSERT_EQ(num_new_table_reader, 5);
|
||||
// (2) 1 for verifying compaction results.
|
||||
// (3) New TableReaders will not be created for compaction inputs
|
||||
ASSERT_EQ(num_new_table_reader, 2);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(1), Get(Key(1)));
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 3);
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 5);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
@@ -519,13 +519,14 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
// May preload table cache too.
|
||||
ASSERT_GE(num_table_cache_lookup, 1);
|
||||
old_num_table_cache_lookup2 = num_table_cache_lookup;
|
||||
// One for compaction input, one for verifying compaction results.
|
||||
ASSERT_EQ(num_new_table_reader, 2);
|
||||
// One for verifying compaction results.
|
||||
// No new iterator created for compaction.
|
||||
ASSERT_EQ(num_new_table_reader, 1);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(1), Get(Key(1)));
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 2);
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 3);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -3890,11 +3891,17 @@ TEST_F(DBCompactionTest, CompactRangeShutdownWhileDelayed) {
|
||||
}
|
||||
Flush(1);
|
||||
}
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
auto manual_compaction_thread = port::Thread([this, i]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.allow_write_stall = false;
|
||||
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
|
||||
.IsShutdownInProgress());
|
||||
Status s = db_->CompactRange(cro, handles_[1], nullptr, nullptr);
|
||||
if (i == 0) {
|
||||
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
|
||||
.IsColumnFamilyDropped());
|
||||
} else {
|
||||
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
|
||||
.IsShutdownInProgress());
|
||||
}
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
@@ -4333,12 +4340,6 @@ TEST_P(DBCompactionDirectIOTest, DirectIO) {
|
||||
options.env = new MockEnv(Env::Default());
|
||||
Reopen(options);
|
||||
bool readahead = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::NewIterator:for_compaction", [&](void* arg) {
|
||||
bool* use_direct_reads = static_cast<bool*>(arg);
|
||||
ASSERT_EQ(*use_direct_reads,
|
||||
options.use_direct_reads);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::OpenCompactionOutputFile", [&](void* arg) {
|
||||
bool* use_direct_writes = static_cast<bool*>(arg);
|
||||
@@ -4551,6 +4552,36 @@ TEST_F(DBCompactionTest, ManualCompactionBottomLevelOptimized) {
|
||||
ASSERT_EQ(num, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionDuringShutdown) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.level0_file_num_compaction_trigger = 2;
|
||||
opts.disable_auto_compactions = true;
|
||||
DestroyAndReopen(opts);
|
||||
ColumnFamilyHandleImpl* cfh =
|
||||
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily());
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
InternalStats* internal_stats_ptr = cfd->internal_stats();
|
||||
ASSERT_NE(internal_stats_ptr, nullptr);
|
||||
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
ASSERT_OK(
|
||||
Put("foo" + std::to_string(i * 10 + j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun",
|
||||
[&](void* /*arg*/) {
|
||||
dbfull()->shutting_down_.store(true);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->error_handler_.GetBGError());
|
||||
}
|
||||
|
||||
// FixFileIngestionCompactionDeadlock tests and verifies that compaction and
|
||||
// file ingestion do not cause deadlock in the event of write stall triggered
|
||||
// by number of L0 files reaching level0_stop_writes_trigger.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
+8
-10
@@ -6,24 +6,20 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/filename.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -61,7 +57,9 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
}
|
||||
if (file_deletion_enabled) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Enabled");
|
||||
PurgeObsoleteFiles(job_context);
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
|
||||
+110
-3
@@ -9,8 +9,8 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -290,6 +290,39 @@ TEST_F(DBFlushTest, ManualFlushFailsInReadOnlyMode) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, CFDropRaceWithWaitForFlushMemTables) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:AfterScheduleFlush",
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop"},
|
||||
{"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BackgroundCallFlush:start",
|
||||
"DBImpl::FlushMemTable:BeforeWaitForBgFlush"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(Put(1, "key", "value"));
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
|
||||
port::Thread drop_cf_thr([&]() {
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop");
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_OK(dbfull()->DestroyColumnFamilyHandle(handles_[1]));
|
||||
handles_.resize(1);
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree");
|
||||
});
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
ASSERT_NOK(dbfull()->TEST_FlushMemTable(cfd, flush_opts));
|
||||
drop_cf_thr.join();
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -431,7 +464,7 @@ TEST_P(DBAtomicFlushTest, FlushMultipleCFs_DropSomeBeforeRequestFlush) {
|
||||
cf_ids.push_back(cf_id);
|
||||
}
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_TRUE(Flush(cf_ids).IsShutdownInProgress());
|
||||
ASSERT_TRUE(Flush(cf_ids).IsColumnFamilyDropped());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
@@ -514,6 +547,80 @@ TEST_P(DBAtomicFlushTest, TriggerFlushAndClose) {
|
||||
ASSERT_EQ("value", Get(0, "key"));
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, PickMemtablesRaceWithBackgroundFlush) {
|
||||
bool atomic_flush = GetParam();
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
options.max_write_buffer_number = 4;
|
||||
// Set min_write_buffer_number_to_merge to be greater than 1, so that
|
||||
// a column family with one memtable in the imm will not cause IsFlushPending
|
||||
// to return true when flush_requested_ is false.
|
||||
options.min_write_buffer_number_to_merge = 2;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(dbfull()->PauseBackgroundWork());
|
||||
ASSERT_OK(Put(0, "key00", "value00"));
|
||||
ASSERT_OK(Put(1, "key10", "value10"));
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts, handles_));
|
||||
ASSERT_OK(Put(0, "key01", "value01"));
|
||||
// Since max_write_buffer_number is 4, the following flush won't cause write
|
||||
// stall.
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts));
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_OK(dbfull()->DestroyColumnFamilyHandle(handles_[1]));
|
||||
handles_[1] = nullptr;
|
||||
ASSERT_OK(dbfull()->ContinueBackgroundWork());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[0]));
|
||||
delete handles_[0];
|
||||
handles_.clear();
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, CFDropRaceWithWaitForFlushMemTables) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::AtomicFlushMemTables:AfterScheduleFlush",
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop"},
|
||||
{"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BackgroundCallFlush:start",
|
||||
"DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(Put(0, "key", "value"));
|
||||
ASSERT_OK(Put(1, "key", "value"));
|
||||
auto* cfd_default =
|
||||
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily())
|
||||
->cfd();
|
||||
auto* cfd_pikachu = static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
|
||||
port::Thread drop_cf_thr([&]() {
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop");
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
delete handles_[1];
|
||||
handles_.resize(1);
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree");
|
||||
});
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
ASSERT_OK(dbfull()->TEST_AtomicFlushMemTables({cfd_default, cfd_pikachu},
|
||||
flush_opts));
|
||||
drop_cf_thr.join();
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBFlushDirectIOTest, DBFlushDirectIOTest,
|
||||
testing::Bool());
|
||||
|
||||
|
||||
@@ -6,17 +6,15 @@
|
||||
// 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"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
#ifdef OS_SOLARIS
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -28,16 +26,16 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/compaction_job.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/db_info_dumper.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/external_sst_file_ingestion_job.h"
|
||||
#include "db/import_column_family_job.h"
|
||||
#include "db/flush_job.h"
|
||||
#include "db/forward_iterator.h"
|
||||
#include "db/in_memory_stats_history.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
@@ -53,10 +51,18 @@
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_callback.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "logging/auto_roll_logger.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "monitoring/in_memory_stats_history.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/persistent_stats_history.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -74,33 +80,29 @@
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/get_context.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/multiget_context.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "tools/sst_dump_tool_imp.h"
|
||||
#include "util/auto_roll_logger.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/build_version.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
const std::string kDefaultColumnFamilyName("default");
|
||||
const std::string kPersistentStatsColumnFamilyName(
|
||||
"___rocksdb_stats_history___");
|
||||
void DumpRocksDBBuildVersion(Logger* log);
|
||||
|
||||
CompressionType GetCompressionFlush(
|
||||
@@ -165,6 +167,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
logfile_number_(0),
|
||||
log_dir_synced_(false),
|
||||
log_empty_(true),
|
||||
persist_stats_cf_handle_(nullptr),
|
||||
log_sync_cv_(&mutex_),
|
||||
total_log_size_(0),
|
||||
is_snapshot_supported_(true),
|
||||
@@ -240,7 +243,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
|
||||
versions_.reset(new VersionSet(dbname_, &immutable_db_options_, env_options_,
|
||||
table_cache_.get(), write_buffer_manager_,
|
||||
&write_controller_));
|
||||
&write_controller_, &block_cache_tracer_));
|
||||
column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
|
||||
@@ -485,10 +488,17 @@ Status DBImpl::CloseHelper() {
|
||||
}
|
||||
}
|
||||
|
||||
if (default_cf_handle_ != nullptr) {
|
||||
if (default_cf_handle_ != nullptr || persist_stats_cf_handle_ != nullptr) {
|
||||
// we need to delete handle outside of lock because it does its own locking
|
||||
mutex_.Unlock();
|
||||
delete default_cf_handle_;
|
||||
if (default_cf_handle_) {
|
||||
delete default_cf_handle_;
|
||||
default_cf_handle_ = nullptr;
|
||||
}
|
||||
if (persist_stats_cf_handle_) {
|
||||
delete persist_stats_cf_handle_;
|
||||
persist_stats_cf_handle_ = nullptr;
|
||||
}
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
@@ -582,6 +592,12 @@ Status DBImpl::CloseHelper() {
|
||||
ret = s;
|
||||
}
|
||||
}
|
||||
if (ret.IsAborted()) {
|
||||
// Reserve IsAborted() error for those where users didn't release
|
||||
// certain resource and they can release them and come back and
|
||||
// retry. In this case, we wrap this exception to something else.
|
||||
return Status::Incomplete(ret.ToString());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -631,7 +647,7 @@ void DBImpl::StartTimedTasks() {
|
||||
if (!thread_dump_stats_) {
|
||||
thread_dump_stats_.reset(new rocksdb::RepeatableThread(
|
||||
[this]() { DBImpl::DumpStats(); }, "dump_st", env_,
|
||||
stats_dump_period_sec * 1000000));
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond));
|
||||
}
|
||||
}
|
||||
stats_persist_period_sec = mutable_db_options_.stats_persist_period_sec;
|
||||
@@ -639,14 +655,14 @@ void DBImpl::StartTimedTasks() {
|
||||
if (!thread_persist_stats_) {
|
||||
thread_persist_stats_.reset(new rocksdb::RepeatableThread(
|
||||
[this]() { DBImpl::PersistStats(); }, "pst_st", env_,
|
||||
stats_persist_period_sec * 1000000));
|
||||
static_cast<uint64_t>(stats_persist_period_sec) * kMicrosInSecond));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// esitmate the total size of stats_history_
|
||||
size_t DBImpl::EstiamteStatsHistorySize() const {
|
||||
size_t DBImpl::EstimateInMemoryStatsHistorySize() const {
|
||||
size_t size_total =
|
||||
sizeof(std::map<uint64_t, std::map<std::string, uint64_t>>);
|
||||
if (stats_history_.size() == 0) return size_total;
|
||||
@@ -668,7 +684,7 @@ void DBImpl::PersistStats() {
|
||||
if (shutdown_initiated_) {
|
||||
return;
|
||||
}
|
||||
uint64_t now_micros = env_->NowMicros();
|
||||
uint64_t now_seconds = env_->NowMicros() / kMicrosInSecond;
|
||||
Statistics* statistics = immutable_db_options_.statistics.get();
|
||||
if (!statistics) {
|
||||
return;
|
||||
@@ -679,12 +695,50 @@ void DBImpl::PersistStats() {
|
||||
stats_history_size_limit = mutable_db_options_.stats_history_buffer_size;
|
||||
}
|
||||
|
||||
// TODO(Zhongyi): also persist immutable_db_options_.statistics
|
||||
{
|
||||
std::map<std::string, uint64_t> stats_map;
|
||||
if (!statistics->getTickerMap(&stats_map)) {
|
||||
return;
|
||||
std::map<std::string, uint64_t> stats_map;
|
||||
if (!statistics->getTickerMap(&stats_map)) {
|
||||
return;
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"------- PERSISTING STATS -------");
|
||||
|
||||
if (immutable_db_options_.persist_stats_to_disk) {
|
||||
WriteBatch batch;
|
||||
if (stats_slice_initialized_) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Reading %" ROCKSDB_PRIszt " stats from statistics\n",
|
||||
stats_slice_.size());
|
||||
for (const auto& stat : stats_map) {
|
||||
char key[100];
|
||||
int length =
|
||||
EncodePersistentStatsKey(now_seconds, stat.first, 100, key);
|
||||
// calculate the delta from last time
|
||||
if (stats_slice_.find(stat.first) != stats_slice_.end()) {
|
||||
uint64_t delta = stat.second - stats_slice_[stat.first];
|
||||
batch.Put(persist_stats_cf_handle_, Slice(key, std::min(100, length)),
|
||||
ToString(delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
stats_slice_initialized_ = true;
|
||||
std::swap(stats_slice_, stats_map);
|
||||
WriteOptions wo;
|
||||
wo.low_pri = true;
|
||||
wo.no_slowdown = true;
|
||||
wo.sync = false;
|
||||
Status s = Write(wo, &batch);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Writing to persistent stats CF failed -- %s",
|
||||
s.ToString().c_str());
|
||||
} else {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Writing %" ROCKSDB_PRIszt " stats with timestamp %" PRIu64
|
||||
" to persistent stats CF succeeded",
|
||||
stats_slice_.size(), now_seconds);
|
||||
}
|
||||
// TODO(Zhongyi): add purging for persisted data
|
||||
} else {
|
||||
InstrumentedMutexLock l(&stats_history_mutex_);
|
||||
// calculate the delta from last time
|
||||
if (stats_slice_initialized_) {
|
||||
@@ -694,20 +748,33 @@ void DBImpl::PersistStats() {
|
||||
stats_delta[stat.first] = stat.second - stats_slice_[stat.first];
|
||||
}
|
||||
}
|
||||
stats_history_[now_micros] = stats_delta;
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Storing %" ROCKSDB_PRIszt " stats with timestamp %" PRIu64
|
||||
" to in-memory stats history",
|
||||
stats_slice_.size(), now_seconds);
|
||||
stats_history_[now_seconds] = stats_delta;
|
||||
}
|
||||
stats_slice_initialized_ = true;
|
||||
std::swap(stats_slice_, stats_map);
|
||||
TEST_SYNC_POINT("DBImpl::PersistStats:StatsCopied");
|
||||
|
||||
// delete older stats snapshots to control memory consumption
|
||||
bool purge_needed = EstiamteStatsHistorySize() > stats_history_size_limit;
|
||||
size_t stats_history_size = EstimateInMemoryStatsHistorySize();
|
||||
bool purge_needed = stats_history_size > stats_history_size_limit;
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[Pre-GC] In-memory stats history size: %" ROCKSDB_PRIszt
|
||||
" bytes, slice count: %" ROCKSDB_PRIszt,
|
||||
stats_history_size, stats_history_.size());
|
||||
while (purge_needed && !stats_history_.empty()) {
|
||||
stats_history_.erase(stats_history_.begin());
|
||||
purge_needed = EstiamteStatsHistorySize() > stats_history_size_limit;
|
||||
purge_needed =
|
||||
EstimateInMemoryStatsHistorySize() > stats_history_size_limit;
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[Post-GC] In-memory stats history size: %" ROCKSDB_PRIszt
|
||||
" bytes, slice count: %" ROCKSDB_PRIszt,
|
||||
stats_history_size, stats_history_.size());
|
||||
}
|
||||
// TODO: persist stats to disk
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -738,8 +805,13 @@ Status DBImpl::GetStatsHistory(
|
||||
if (!stats_iterator) {
|
||||
return Status::InvalidArgument("stats_iterator not preallocated.");
|
||||
}
|
||||
stats_iterator->reset(
|
||||
new InMemoryStatsHistoryIterator(start_time, end_time, this));
|
||||
if (immutable_db_options_.persist_stats_to_disk) {
|
||||
stats_iterator->reset(
|
||||
new PersistentStatsHistoryIterator(start_time, end_time, this));
|
||||
} else {
|
||||
stats_iterator->reset(
|
||||
new InMemoryStatsHistoryIterator(start_time, end_time, this));
|
||||
}
|
||||
return (*stats_iterator)->status();
|
||||
}
|
||||
|
||||
@@ -811,16 +883,6 @@ Directory* DBImpl::GetDataDir(ColumnFamilyData* cfd, size_t path_id) const {
|
||||
return ret_dir;
|
||||
}
|
||||
|
||||
Directory* DBImpl::Directories::GetDataDir(size_t path_id) const {
|
||||
assert(path_id < data_dirs_.size());
|
||||
Directory* ret_dir = data_dirs_[path_id].get();
|
||||
if (ret_dir == nullptr) {
|
||||
// Should use db_dir_
|
||||
return db_dir_.get();
|
||||
}
|
||||
return ret_dir;
|
||||
}
|
||||
|
||||
Status DBImpl::SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
@@ -842,8 +904,9 @@ Status DBImpl::SetOptions(
|
||||
Status persist_options_status;
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
{
|
||||
auto db_options = GetDBOptions();
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
s = cfd->SetOptions(options_map);
|
||||
s = cfd->SetOptions(db_options, options_map);
|
||||
if (s.ok()) {
|
||||
new_options = *cfd->GetLatestMutableCFOptions();
|
||||
// Append new version to recompute compaction score.
|
||||
@@ -906,6 +969,25 @@ Status DBImpl::SetDBOptions(
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
s = GetMutableDBOptionsFromStrings(mutable_db_options_, options_map,
|
||||
&new_options);
|
||||
if (new_options.bytes_per_sync == 0) {
|
||||
new_options.bytes_per_sync = 1024 * 1024;
|
||||
}
|
||||
DBOptions new_db_options =
|
||||
BuildDBOptions(immutable_db_options_, new_options);
|
||||
if (s.ok()) {
|
||||
s = ValidateOptions(new_db_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
for (auto c : *versions_->GetColumnFamilySet()) {
|
||||
if (!c->IsDropped()) {
|
||||
auto cf_options = c->GetLatestCFOptions();
|
||||
s = ColumnFamilyData::ValidateOptions(new_db_options, cf_options);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
if (new_options.max_background_compactions >
|
||||
mutable_db_options_.max_background_compactions) {
|
||||
@@ -923,7 +1005,8 @@ Status DBImpl::SetDBOptions(
|
||||
if (new_options.stats_dump_period_sec > 0) {
|
||||
thread_dump_stats_.reset(new rocksdb::RepeatableThread(
|
||||
[this]() { DBImpl::DumpStats(); }, "dump_st", env_,
|
||||
new_options.stats_dump_period_sec * 1000000));
|
||||
static_cast<uint64_t>(new_options.stats_dump_period_sec) *
|
||||
kMicrosInSecond));
|
||||
} else {
|
||||
thread_dump_stats_.reset();
|
||||
}
|
||||
@@ -938,7 +1021,8 @@ Status DBImpl::SetDBOptions(
|
||||
if (new_options.stats_persist_period_sec > 0) {
|
||||
thread_persist_stats_.reset(new rocksdb::RepeatableThread(
|
||||
[this]() { DBImpl::PersistStats(); }, "pst_st", env_,
|
||||
new_options.stats_persist_period_sec * 1000000));
|
||||
static_cast<uint64_t>(new_options.stats_persist_period_sec) *
|
||||
kMicrosInSecond));
|
||||
} else {
|
||||
thread_persist_stats_.reset();
|
||||
}
|
||||
@@ -950,15 +1034,12 @@ Status DBImpl::SetDBOptions(
|
||||
: new_options.max_open_files - 10);
|
||||
wal_changed = mutable_db_options_.wal_bytes_per_sync !=
|
||||
new_options.wal_bytes_per_sync;
|
||||
if (new_options.bytes_per_sync == 0) {
|
||||
new_options.bytes_per_sync = 1024 * 1024;
|
||||
}
|
||||
mutable_db_options_ = new_options;
|
||||
env_options_for_compaction_ = EnvOptions(
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_));
|
||||
env_options_for_compaction_ = EnvOptions(new_db_options);
|
||||
env_options_for_compaction_ = env_->OptimizeForCompactionTableWrite(
|
||||
env_options_for_compaction_, immutable_db_options_);
|
||||
versions_->ChangeEnvOptions(mutable_db_options_);
|
||||
//TODO(xiez): clarify why apply optimize for read to write options
|
||||
env_options_for_compaction_ = env_->OptimizeForCompactionTableRead(
|
||||
env_options_for_compaction_, immutable_db_options_);
|
||||
env_options_for_compaction_.compaction_readahead_size =
|
||||
@@ -1026,10 +1107,13 @@ int DBImpl::FindMinimumEmptyLevelFitting(
|
||||
|
||||
Status DBImpl::FlushWAL(bool sync) {
|
||||
if (manual_wal_flush_) {
|
||||
// We need to lock log_write_mutex_ since logs_ might change concurrently
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
auto s = cur_log_writer->WriteBuffer();
|
||||
Status s;
|
||||
{
|
||||
// We need to lock log_write_mutex_ since logs_ might change concurrently
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
s = cur_log_writer->WriteBuffer();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "WAL flush error %s",
|
||||
s.ToString().c_str());
|
||||
@@ -1350,10 +1434,23 @@ ColumnFamilyHandle* DBImpl::DefaultColumnFamily() const {
|
||||
return default_cf_handle_;
|
||||
}
|
||||
|
||||
ColumnFamilyHandle* DBImpl::PersistentStatsColumnFamily() const {
|
||||
return persist_stats_cf_handle_;
|
||||
}
|
||||
|
||||
Status DBImpl::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) {
|
||||
return GetImpl(read_options, column_family, key, value);
|
||||
if (nullptr == read_options.timestamp) {
|
||||
return GetImpl(read_options, column_family, key, value);
|
||||
}
|
||||
Slice akey;
|
||||
std::string buf;
|
||||
Status s = AppendTimestamp(key, *(read_options.timestamp), &akey, &buf);
|
||||
if (s.ok()) {
|
||||
s = GetImpl(read_options, column_family, akey, value);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
@@ -1912,13 +2009,9 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
Status persist_options_status;
|
||||
*handle = nullptr;
|
||||
|
||||
s = CheckCompressionSupported(cf_options);
|
||||
if (s.ok() && immutable_db_options_.allow_concurrent_memtable_write) {
|
||||
s = CheckConcurrentWritesSupported(cf_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = CheckCFPathsSupported(initial_db_options_, cf_options);
|
||||
}
|
||||
DBOptions db_options =
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_);
|
||||
s = ColumnFamilyData::ValidateOptions(db_options, cf_options);
|
||||
if (s.ok()) {
|
||||
for (auto& cf_path : cf_options.cf_paths) {
|
||||
s = env_->CreateDirIfMissing(cf_path.path);
|
||||
@@ -2688,7 +2781,9 @@ void DBImpl::GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
sizes[i] = 0;
|
||||
if (include_flags & DB::SizeApproximationFlags::INCLUDE_FILES) {
|
||||
sizes[i] += versions_->ApproximateSize(v, k1.Encode(), k2.Encode());
|
||||
sizes[i] += versions_->ApproximateSize(
|
||||
v, k1.Encode(), k2.Encode(), /*start_level=*/0, /*end_level=*/-1,
|
||||
TableReaderCaller::kUserApproximateSize);
|
||||
}
|
||||
if (include_flags & DB::SizeApproximationFlags::INCLUDE_MEMTABLES) {
|
||||
sizes[i] += sv->mem->ApproximateStats(k1.Encode(), k2.Encode()).size;
|
||||
@@ -2934,6 +3029,7 @@ Status DBImpl::CheckConsistency() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
versions_->GetLiveFilesMetaData(&metadata);
|
||||
TEST_SYNC_POINT("DBImpl::CheckConsistency:AfterGetLiveFilesMetaData");
|
||||
|
||||
std::string corruption_messages;
|
||||
for (const auto& md : metadata) {
|
||||
@@ -2941,6 +3037,7 @@ Status DBImpl::CheckConsistency() {
|
||||
std::string file_path = md.db_path + md.name;
|
||||
|
||||
uint64_t fsize = 0;
|
||||
TEST_SYNC_POINT("DBImpl::CheckConsistency:BeforeGetFileSize");
|
||||
Status s = env_->GetFileSize(file_path, &fsize);
|
||||
if (!s.ok() &&
|
||||
env_->GetFileSize(Rocks2LevelTableFileName(file_path), &fsize).ok()) {
|
||||
@@ -3036,6 +3133,14 @@ DB::~DB() {}
|
||||
|
||||
Status DBImpl::Close() {
|
||||
if (!closed_) {
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// If there is unreleased snapshot, fail the close call
|
||||
if (!snapshots_.empty()) {
|
||||
return Status::Aborted("Cannot close DB with unreleased snapshot.");
|
||||
}
|
||||
}
|
||||
|
||||
closed_ = true;
|
||||
return CloseImpl();
|
||||
}
|
||||
@@ -3055,6 +3160,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
ImmutableDBOptions soptions(SanitizeOptions(dbname, options));
|
||||
Env* env = soptions.env;
|
||||
std::vector<std::string> filenames;
|
||||
bool wal_in_db_path = IsWalDirSameAsDBPath(&soptions);
|
||||
|
||||
// Reset the logger because it holds a handle to the
|
||||
// log file and prevents cleanup and directory removal
|
||||
@@ -3077,7 +3183,9 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
if (type == kMetaDatabase) {
|
||||
del = DestroyDB(path_to_delete, options);
|
||||
} else if (type == kTableFile || type == kLogFile) {
|
||||
del = DeleteDBFile(&soptions, path_to_delete, dbname);
|
||||
del =
|
||||
DeleteDBFile(&soptions, path_to_delete, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
|
||||
} else {
|
||||
del = env->DeleteFile(path_to_delete);
|
||||
}
|
||||
@@ -3111,7 +3219,8 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
if (ParseFileName(fname, &number, &type) &&
|
||||
type == kTableFile) { // Lock file will be deleted at end
|
||||
std::string table_path = path + "/" + fname;
|
||||
Status del = DeleteDBFile(&soptions, table_path, dbname);
|
||||
Status del = DeleteDBFile(&soptions, table_path, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/false);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
@@ -3138,7 +3247,8 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
for (const auto& file : archiveFiles) {
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
Status del =
|
||||
DeleteDBFile(&soptions, archivedir + "/" + file, archivedir);
|
||||
DeleteDBFile(&soptions, archivedir + "/" + file, archivedir,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
@@ -3153,7 +3263,8 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
Status del =
|
||||
DeleteDBFile(&soptions, LogFileName(soptions.wal_dir, number),
|
||||
soptions.wal_dir);
|
||||
soptions.wal_dir, /*force_bg=*/false,
|
||||
/*force_fg=*/!wal_in_db_path);
|
||||
if (result.ok() && !del.ok()) {
|
||||
result = del;
|
||||
}
|
||||
@@ -3370,7 +3481,9 @@ SequenceNumber DBImpl::GetEarliestMemTableSequenceNumber(SuperVersion* sv,
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
bool cache_only, SequenceNumber* seq,
|
||||
bool cache_only,
|
||||
SequenceNumber lower_bound_seq,
|
||||
SequenceNumber* seq,
|
||||
bool* found_record_for_key,
|
||||
bool* is_blob_index) {
|
||||
Status s;
|
||||
@@ -3403,6 +3516,13 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
SequenceNumber lower_bound_in_mem = sv->mem->GetEarliestSequenceNumber();
|
||||
if (lower_bound_in_mem != kMaxSequenceNumber &&
|
||||
lower_bound_in_mem < lower_bound_seq) {
|
||||
*found_record_for_key = false;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->Get(lkey, nullptr, &s, &merge_context, &max_covering_tombstone_seq,
|
||||
seq, read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
@@ -3422,6 +3542,13 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
SequenceNumber lower_bound_in_imm = sv->imm->GetEarliestSequenceNumber();
|
||||
if (lower_bound_in_imm != kMaxSequenceNumber &&
|
||||
lower_bound_in_imm < lower_bound_seq) {
|
||||
*found_record_for_key = false;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->GetFromHistory(lkey, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, seq, read_options,
|
||||
@@ -3443,6 +3570,10 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// We could do a sv->imm->GetEarliestSequenceNumber(/*include_history*/ true)
|
||||
// check here to skip the history if possible. But currently the caller
|
||||
// already does that. Maybe we should move the logic here later.
|
||||
|
||||
// TODO(agiardullo): possible optimization: consider checking cached
|
||||
// SST files if cache_only=true?
|
||||
if (!cache_only) {
|
||||
@@ -3531,7 +3662,7 @@ Status DBImpl::IngestExternalFiles(
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(arg.column_family)->cfd();
|
||||
ingestion_jobs.emplace_back(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, env_options_,
|
||||
&snapshots_, arg.options);
|
||||
&snapshots_, arg.options, &directories_);
|
||||
}
|
||||
std::vector<std::pair<bool, Status>> exec_results;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
@@ -3764,6 +3895,127 @@ Status DBImpl::IngestExternalFiles(
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const ExportImportFilesMetaData& metadata,
|
||||
ColumnFamilyHandle** handle) {
|
||||
assert(handle != nullptr);
|
||||
assert(*handle == nullptr);
|
||||
std::string cf_comparator_name = options.comparator->Name();
|
||||
if (cf_comparator_name != metadata.db_comparator_name) {
|
||||
return Status::InvalidArgument("Comparator name mismatch");
|
||||
}
|
||||
|
||||
// Create column family.
|
||||
auto status = CreateColumnFamily(options, column_family_name, handle);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Import sst files from metadata.
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(*handle);
|
||||
auto cfd = cfh->cfd();
|
||||
ImportColumnFamilyJob import_job(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, env_options_,
|
||||
import_options, metadata.files);
|
||||
|
||||
SuperVersionContext dummy_sv_ctx(/* create_superversion */ true);
|
||||
VersionEdit dummy_edit;
|
||||
uint64_t next_file_number = 0;
|
||||
std::list<uint64_t>::iterator pending_output_elem;
|
||||
{
|
||||
// Lock db mutex
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
// Don't import files when there is a bg_error
|
||||
status = error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
// Make sure that bg cleanup wont delete the files that we are importing
|
||||
pending_output_elem = CaptureCurrentFileNumberInPendingOutputs();
|
||||
|
||||
if (status.ok()) {
|
||||
// If crash happen after a hard link established, Recover function may
|
||||
// reuse the file number that has already assigned to the internal file,
|
||||
// and this will overwrite the external file. To protect the external
|
||||
// file, we have to make sure the file number will never being reused.
|
||||
next_file_number =
|
||||
versions_->FetchAddFileNumber(metadata.files.size());
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status = versions_->LogAndApply(cfd, *cf_options, &dummy_edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx, *cf_options);
|
||||
}
|
||||
}
|
||||
}
|
||||
dummy_sv_ctx.Clean();
|
||||
|
||||
if (status.ok()) {
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
status = import_job.Prepare(next_file_number, sv);
|
||||
CleanupSuperVersion(sv);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
SuperVersionContext sv_context(true /*create_superversion*/);
|
||||
{
|
||||
// Lock db mutex
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
// Stop writes to the DB by entering both write threads
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
|
||||
num_running_ingest_file_++;
|
||||
assert(!cfd->IsDropped());
|
||||
status = import_job.Run();
|
||||
|
||||
// Install job edit [Mutex will be unlocked here]
|
||||
if (status.ok()) {
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status = versions_->LogAndApply(cfd, *cf_options, import_job.edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, *cf_options);
|
||||
}
|
||||
}
|
||||
|
||||
// Resume writes to the DB
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
|
||||
num_running_ingest_file_--;
|
||||
if (num_running_ingest_file_ == 0) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
}
|
||||
// mutex_ is unlocked here
|
||||
|
||||
sv_context.Clean();
|
||||
}
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
ReleaseFileNumberFromPendingOutputs(pending_output_elem);
|
||||
}
|
||||
|
||||
import_job.Cleanup(status);
|
||||
if (!status.ok()) {
|
||||
DropColumnFamily(*handle);
|
||||
DestroyColumnFamilyHandle(*handle);
|
||||
*handle = nullptr;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifyChecksum() {
|
||||
Status s;
|
||||
std::vector<ColumnFamilyData*> cfd_list;
|
||||
@@ -3862,6 +4114,18 @@ Status DBImpl::EndTrace() {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::StartBlockCacheTrace(
|
||||
const TraceOptions& trace_options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) {
|
||||
return block_cache_tracer_.StartTrace(env_, trace_options,
|
||||
std::move(trace_writer));
|
||||
}
|
||||
|
||||
Status DBImpl::EndBlockCacheTrace() {
|
||||
block_cache_tracer_.EndTrace();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::TraceIteratorSeek(const uint32_t& cf_id, const Slice& key) {
|
||||
Status s;
|
||||
if (tracer_) {
|
||||
+515
-364
File diff suppressed because it is too large
Load Diff
@@ -6,23 +6,20 @@
|
||||
// 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"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/concurrent_task_limiter_impl.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -110,6 +107,13 @@ Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (immutable_db_options_.recycle_log_file_num > 0) {
|
||||
s = log->Close();
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = directories_.GetWalDir()->Fsync();
|
||||
@@ -201,7 +205,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
cfd->current()->storage_info()->LevelSummary(&tmp));
|
||||
}
|
||||
|
||||
if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
if (!s.ok() && !s.IsShutdownInProgress() && !s.IsColumnFamilyDropped()) {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
@@ -254,7 +258,7 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
snapshot_checker, log_buffer, thread_pri);
|
||||
if (!s.ok()) {
|
||||
status = s;
|
||||
if (!s.IsShutdownInProgress()) {
|
||||
if (!s.IsShutdownInProgress() && !s.IsColumnFamilyDropped()) {
|
||||
// At this point, DB is not shutting down, nor is cfd dropped.
|
||||
// Something is wrong, thus we break out of the loop.
|
||||
break;
|
||||
@@ -385,7 +389,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
for (const auto& e : exec_status) {
|
||||
if (!e.second.ok()) {
|
||||
s = e.second;
|
||||
if (!e.second.IsShutdownInProgress()) {
|
||||
if (!e.second.IsShutdownInProgress() &&
|
||||
!e.second.IsColumnFamilyDropped()) {
|
||||
// If a flush job did not return OK, and the CF is not dropped, and
|
||||
// the DB is not shutting down, then we have to return this result to
|
||||
// caller later.
|
||||
@@ -397,15 +402,11 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
s = error_status.ok() ? s : error_status;
|
||||
}
|
||||
|
||||
// If db is NOT shutting down, and one or more column families have been
|
||||
// dropped.
|
||||
// TODO: use separate status code for db shutdown and column family dropped.
|
||||
if (s.IsShutdownInProgress() &&
|
||||
!shutting_down_.load(std::memory_order_acquire)) {
|
||||
if (s.IsColumnFamilyDropped()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsShutdownInProgress()) {
|
||||
if (s.ok() || s.IsShutdownInProgress() || s.IsColumnFamilyDropped()) {
|
||||
// Sync on all distinct output directories.
|
||||
for (auto dir : distinct_output_dirs) {
|
||||
if (dir != nullptr) {
|
||||
@@ -523,7 +524,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
if (!s.ok() && !s.IsColumnFamilyDropped()) {
|
||||
// Have to cancel the flush jobs that have NOT executed because we need to
|
||||
// unref the versions.
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
@@ -798,6 +799,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
return s;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class SnapshotListFetchCallbackImpl : public SnapshotListFetchCallback {
|
||||
public:
|
||||
SnapshotListFetchCallbackImpl(DBImpl* db_impl, Env* env,
|
||||
@@ -820,6 +822,7 @@ class SnapshotListFetchCallbackImpl : public SnapshotListFetchCallback {
|
||||
DBImpl* db_impl_;
|
||||
Logger* info_log_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Status DBImpl::CompactFiles(const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -1050,7 +1053,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
if (status.ok()) {
|
||||
// Done
|
||||
} else if (status.IsShutdownInProgress()) {
|
||||
} else if (status.IsColumnFamilyDropped() || status.IsShutdownInProgress()) {
|
||||
// Ignore compaction errors found during shutting down
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
@@ -1548,13 +1551,39 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load()) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
}
|
||||
if (immutable_db_options_.persist_stats_to_disk) {
|
||||
ColumnFamilyData* cfd_stats =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(
|
||||
kPersistentStatsColumnFamilyName);
|
||||
if (cfd_stats != nullptr && cfd_stats != cfd &&
|
||||
!cfd_stats->mem()->IsEmpty()) {
|
||||
// only force flush stats CF when it will be the only CF lagging
|
||||
// behind after the current flush
|
||||
bool stats_cf_flush_needed = true;
|
||||
for (auto* loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (loop_cfd == cfd_stats || loop_cfd == cfd) {
|
||||
continue;
|
||||
}
|
||||
if (loop_cfd->GetLogNumber() <= cfd_stats->GetLogNumber()) {
|
||||
stats_cf_flush_needed = false;
|
||||
}
|
||||
}
|
||||
if (stats_cf_flush_needed) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Force flushing stats CF with manual flush of %s "
|
||||
"to avoid holding old logs", cfd->GetName().c_str());
|
||||
s = SwitchMemtable(cfd_stats, &context);
|
||||
flush_memtable_id = cfd_stats->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd_stats, flush_memtable_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && !flush_req.empty()) {
|
||||
@@ -1562,6 +1591,16 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
ColumnFamilyData* loop_cfd = elem.first;
|
||||
loop_cfd->imm()->FlushRequested();
|
||||
}
|
||||
// If the caller wants to wait for this flush to complete, it indicates
|
||||
// that the caller expects the ColumnFamilyData not to be free'ed by
|
||||
// other threads which may drop the column family concurrently.
|
||||
// Therefore, we increase the cfd's ref count.
|
||||
if (flush_options.wait) {
|
||||
for (auto& elem : flush_req) {
|
||||
ColumnFamilyData* loop_cfd = elem.first;
|
||||
loop_cfd->Ref();
|
||||
}
|
||||
}
|
||||
SchedulePendingFlush(flush_req, flush_reason);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -1570,7 +1609,8 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:AfterScheduleFlush");
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
@@ -1580,6 +1620,13 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery));
|
||||
for (auto* tmp_cfd : cfds) {
|
||||
if (tmp_cfd->Unref()) {
|
||||
// Only one thread can reach here.
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
delete tmp_cfd;
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("FlushMemTableFinished");
|
||||
return s;
|
||||
@@ -1643,6 +1690,15 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
for (auto cfd : cfds) {
|
||||
cfd->imm()->FlushRequested();
|
||||
}
|
||||
// If the caller wants to wait for this flush to complete, it indicates
|
||||
// that the caller expects the ColumnFamilyData not to be free'ed by
|
||||
// other threads which may drop the column family concurrently.
|
||||
// Therefore, we increase the cfd's ref count.
|
||||
if (flush_options.wait) {
|
||||
for (auto cfd : cfds) {
|
||||
cfd->Ref();
|
||||
}
|
||||
}
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, flush_reason);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -1653,7 +1709,7 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
for (auto& iter : flush_req) {
|
||||
@@ -1661,6 +1717,13 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
s = WaitForFlushMemTables(cfds, flush_memtable_ids,
|
||||
(flush_reason == FlushReason::kErrorRecovery));
|
||||
for (auto* cfd : cfds) {
|
||||
if (cfd->Unref()) {
|
||||
// Only one thread can reach here.
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
delete cfd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -1695,7 +1758,10 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
cfd->GetName().c_str());
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
if (cfd->IsDropped() || shutting_down_.load(std::memory_order_acquire)) {
|
||||
if (cfd->IsDropped()) {
|
||||
return Status::ColumnFamilyDropped();
|
||||
}
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
|
||||
@@ -2080,6 +2146,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
autovector<BGFlushArg> bg_flush_args;
|
||||
std::vector<SuperVersionContext>& superversion_contexts =
|
||||
job_context->superversion_contexts;
|
||||
autovector<ColumnFamilyData*> column_families_not_to_flush;
|
||||
while (!flush_queue_.empty()) {
|
||||
// This cfd is already referenced
|
||||
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
||||
@@ -2090,9 +2157,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
if (cfd->IsDropped() || !cfd->imm()->IsFlushPending()) {
|
||||
// can't flush this CF, try next one
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
}
|
||||
column_families_not_to_flush.push_back(cfd);
|
||||
continue;
|
||||
}
|
||||
superversion_contexts.emplace_back(SuperVersionContext(true));
|
||||
@@ -2120,6 +2185,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
}
|
||||
status = FlushMemTablesToOutputFiles(bg_flush_args, made_progress,
|
||||
job_context, log_buffer, thread_pri);
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundFlush:BeforeFlush");
|
||||
// All the CFDs in the FlushReq must have the same flush reason, so just
|
||||
// grab the first one
|
||||
*reason = bg_flush_args[0].cfd_->GetFlushReason();
|
||||
@@ -2131,6 +2197,11 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto cfd : column_families_not_to_flush) {
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -2153,7 +2224,7 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
|
||||
Status s = BackgroundFlush(&made_progress, &job_context, &log_buffer,
|
||||
&reason, thread_pri);
|
||||
if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
if (!s.ok() && !s.IsShutdownInProgress() && !s.IsColumnFamilyDropped() &&
|
||||
reason != FlushReason::kErrorRecovery) {
|
||||
// Wait a little bit before retrying background flush in
|
||||
// case this is an environmental problem and we do not want to
|
||||
@@ -2178,7 +2249,8 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
|
||||
// If flush failed, we want to delete all temporary files that we might have
|
||||
// created. Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsColumnFamilyDropped());
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
@@ -2242,7 +2314,8 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
mutex_.Unlock();
|
||||
env_->SleepForMicroseconds(10000); // prevent hot loop
|
||||
mutex_.Lock();
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress()) {
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsColumnFamilyDropped()) {
|
||||
// Wait a little bit before retrying background compaction in
|
||||
// case this is an environmental problem and we do not want to
|
||||
// chew up resources for failed compactions for the duration of
|
||||
@@ -2266,7 +2339,8 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
// If compaction failed, we want to delete all temporary files that we might
|
||||
// have created (they might not be all recorded in job_context in case of a
|
||||
// failure). Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsColumnFamilyDropped());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:FoundObsoleteFiles");
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
@@ -2671,6 +2745,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun", nullptr);
|
||||
compaction_job.Run();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:NonTrivial:AfterRun");
|
||||
mutex_.Lock();
|
||||
@@ -2704,7 +2780,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
|
||||
if (status.ok() || status.IsCompactionTooLarge()) {
|
||||
// Done
|
||||
} else if (status.IsShutdownInProgress()) {
|
||||
} else if (status.IsColumnFamilyDropped() || status.IsShutdownInProgress()) {
|
||||
// Ignore compaction errors found during shutting down
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "Compaction error: %s",
|
||||
@@ -2788,7 +2864,7 @@ void DBImpl::RemoveManualCompaction(DBImpl::ManualCompactionState* m) {
|
||||
it = manual_compaction_dequeue_.erase(it);
|
||||
return;
|
||||
}
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
assert(false);
|
||||
return;
|
||||
@@ -2809,7 +2885,7 @@ bool DBImpl::ShouldntRunManualCompaction(ManualCompactionState* m) {
|
||||
bool seen = false;
|
||||
while (it != manual_compaction_dequeue_.end()) {
|
||||
if (m == (*it)) {
|
||||
it++;
|
||||
++it;
|
||||
seen = true;
|
||||
continue;
|
||||
} else if (MCOverlap(m, (*it)) && (!seen && !(*it)->in_progress)) {
|
||||
@@ -2818,7 +2894,7 @@ bool DBImpl::ShouldntRunManualCompaction(ManualCompactionState* m) {
|
||||
// and (*it) is ahead in the queue and is not yet in progress
|
||||
return true;
|
||||
}
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2836,7 +2912,7 @@ bool DBImpl::HaveManualCompaction(ColumnFamilyData* cfd) {
|
||||
// in progress
|
||||
return true;
|
||||
}
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2849,7 +2925,7 @@ bool DBImpl::HasExclusiveManualCompaction() {
|
||||
if ((*it)->exclusive) {
|
||||
return true;
|
||||
}
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
|
||||
@@ -122,6 +122,16 @@ Status DBImpl::TEST_FlushMemTable(bool wait, bool allow_write_stall,
|
||||
return FlushMemTable(cfd, fo, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_opts) {
|
||||
return FlushMemTable(cfd, flush_opts, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_AtomicFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& cfds, const FlushOptions& flush_opts) {
|
||||
return AtomicFlushMemTables(cfds, flush_opts, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
@@ -262,8 +272,8 @@ bool DBImpl::TEST_IsPersistentStatsEnabled() const {
|
||||
return thread_persist_stats_ && thread_persist_stats_->IsRunning();
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_EstiamteStatsHistorySize() const {
|
||||
return EstiamteStatsHistorySize();
|
||||
size_t DBImpl::TEST_EstimateInMemoryStatsHistorySize() const {
|
||||
return EstimateInMemoryStatsHistorySize();
|
||||
}
|
||||
} // namespace rocksdb
|
||||
#endif // NDEBUG
|
||||
@@ -7,13 +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 "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
@@ -6,18 +6,15 @@
|
||||
// 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"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -261,7 +258,8 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile || type == kLogFile) {
|
||||
file_deletion_status =
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync);
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path_);
|
||||
} else {
|
||||
file_deletion_status = env_->DeleteFile(fname);
|
||||
}
|
||||
@@ -6,21 +6,19 @@
|
||||
// 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"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "monitoring/persistent_stats_history.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
Options SanitizeOptions(const std::string& dbname, const Options& src) {
|
||||
@@ -124,6 +122,25 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ImmutableDBOptions immutable_db_options(result);
|
||||
if (!IsWalDirSameAsDBPath(&immutable_db_options)) {
|
||||
// Either the WAL dir and db_paths[0]/db_name are not the same, or we
|
||||
// cannot tell for sure. In either case, assume they're different and
|
||||
// explicitly cleanup the trash log files (bypass DeleteScheduler)
|
||||
// Do this first so even if we end up calling
|
||||
// DeleteScheduler::CleanupDirectory on the same dir later, it will be
|
||||
// safe
|
||||
std::vector<std::string> filenames;
|
||||
result.env->GetChildren(result.wal_dir, &filenames);
|
||||
for (std::string& filename : filenames) {
|
||||
if (filename.find(".log.trash",
|
||||
filename.length() - std::string(".log.trash").length()) !=
|
||||
std::string::npos) {
|
||||
std::string trash_file = result.wal_dir + "/" + filename;
|
||||
result.env->DeleteFile(trash_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
// When the DB is stopped, it's possible that there are some .trash files that
|
||||
// were not deleted yet, when we open the DB we will find these .trash files
|
||||
// and schedule them to be deleted (or delete immediately if SstFileManager
|
||||
@@ -145,7 +162,6 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Status SanitizeOptionsByTable(
|
||||
const DBOptions& db_opts,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families) {
|
||||
@@ -158,52 +174,23 @@ Status SanitizeOptionsByTable(
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static Status ValidateOptions(
|
||||
Status DBImpl::ValidateOptions(
|
||||
const DBOptions& db_options,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families) {
|
||||
Status s;
|
||||
|
||||
for (auto& cfd : column_families) {
|
||||
s = CheckCompressionSupported(cfd.options);
|
||||
if (s.ok() && db_options.allow_concurrent_memtable_write) {
|
||||
s = CheckConcurrentWritesSupported(cfd.options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = CheckCFPathsSupported(db_options, cfd.options);
|
||||
}
|
||||
s = ColumnFamilyData::ValidateOptions(db_options, cfd.options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (cfd.options.ttl > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cfd.options.table_factory->Name() !=
|
||||
BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported in Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
|
||||
if (cfd.options.periodic_compaction_seconds > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"Periodic Compaction is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cfd.options.table_factory->Name() !=
|
||||
BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"Periodic Compaction is only supported in "
|
||||
"Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
}
|
||||
s = ValidateOptions(db_options);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::ValidateOptions(const DBOptions& db_options) {
|
||||
if (db_options.db_paths.size() > 4) {
|
||||
return Status::NotSupported(
|
||||
"More than four DB paths are not supported yet. ");
|
||||
@@ -228,9 +215,20 @@ static Status ValidateOptions(
|
||||
return Status::InvalidArgument("keep_log_file_num must be greater than 0");
|
||||
}
|
||||
|
||||
if (db_options.unordered_write &&
|
||||
!db_options.allow_concurrent_memtable_write) {
|
||||
return Status::InvalidArgument(
|
||||
"unordered_write is incompatible with !allow_concurrent_memtable_write");
|
||||
}
|
||||
|
||||
if (db_options.unordered_write && db_options.enable_pipelined_write) {
|
||||
return Status::InvalidArgument(
|
||||
"unordered_write is incompatible with enable_pipelined_write");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status DBImpl::NewDB() {
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -286,9 +284,9 @@ Status DBImpl::CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
return env->NewDirectory(dirname, directory);
|
||||
}
|
||||
|
||||
Status DBImpl::Directories::SetDirectories(
|
||||
Env* env, const std::string& dbname, const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths) {
|
||||
Status Directories::SetDirectories(Env* env, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths) {
|
||||
Status s = DBImpl::CreateAndNewDirectory(env, dbname, &db_dir_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -397,6 +395,7 @@ Status DBImpl::Recover(
|
||||
}
|
||||
|
||||
Status s = versions_->Recover(column_families, read_only);
|
||||
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
@@ -408,6 +407,10 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
}
|
||||
// DB mutex is already held
|
||||
if (s.ok() && immutable_db_options_.persist_stats_to_disk) {
|
||||
s = InitPersistStatsColumnFamily();
|
||||
}
|
||||
|
||||
// Initial max_total_in_memory_state_ before recovery logs. Log recovery
|
||||
// may check this value to decide whether to flush.
|
||||
@@ -423,6 +426,8 @@ Status DBImpl::Recover(
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), this, &mutex_);
|
||||
default_cf_internal_stats_ = default_cf_handle_->cfd()->internal_stats();
|
||||
// TODO(Zhongyi): handle single_column_family_mode_ when
|
||||
// persistent_stats is enabled
|
||||
single_column_family_mode_ =
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1;
|
||||
|
||||
@@ -518,6 +523,98 @@ Status DBImpl::Recover(
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::PersistentStatsProcessFormatVersion() {
|
||||
mutex_.AssertHeld();
|
||||
Status s;
|
||||
// persist version when stats CF doesn't exist
|
||||
bool should_persist_format_version = !persistent_stats_cfd_exists_;
|
||||
mutex_.Unlock();
|
||||
if (persistent_stats_cfd_exists_) {
|
||||
// Check persistent stats format version compatibility. Drop and recreate
|
||||
// persistent stats CF if format version is incompatible
|
||||
uint64_t format_version_recovered = 0;
|
||||
Status s_format = DecodePersistentStatsVersionNumber(
|
||||
this, StatsVersionKeyType::kFormatVersion, &format_version_recovered);
|
||||
uint64_t compatible_version_recovered = 0;
|
||||
Status s_compatible = DecodePersistentStatsVersionNumber(
|
||||
this, StatsVersionKeyType::kCompatibleVersion,
|
||||
&compatible_version_recovered);
|
||||
// abort reading from existing stats CF if any of following is true:
|
||||
// 1. failed to read format version or compatible version from disk
|
||||
// 2. sst's format version is greater than current format version, meaning
|
||||
// this sst is encoded with a newer RocksDB release, and current compatible
|
||||
// version is below the sst's compatible version
|
||||
if (!s_format.ok() || !s_compatible.ok() ||
|
||||
(kStatsCFCurrentFormatVersion < format_version_recovered &&
|
||||
kStatsCFCompatibleFormatVersion < compatible_version_recovered)) {
|
||||
if (!s_format.ok() || !s_compatible.ok()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Reading persistent stats version key failed. Format key: %s, "
|
||||
"compatible key: %s",
|
||||
s_format.ToString().c_str(), s_compatible.ToString().c_str());
|
||||
} else {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Disable persistent stats due to corrupted or incompatible format "
|
||||
"version\n");
|
||||
}
|
||||
DropColumnFamily(persist_stats_cf_handle_);
|
||||
DestroyColumnFamilyHandle(persist_stats_cf_handle_);
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ColumnFamilyOptions cfo;
|
||||
OptimizeForPersistentStats(&cfo);
|
||||
s = CreateColumnFamily(cfo, kPersistentStatsColumnFamilyName, &handle);
|
||||
persist_stats_cf_handle_ = static_cast<ColumnFamilyHandleImpl*>(handle);
|
||||
// should also persist version here because old stats CF is discarded
|
||||
should_persist_format_version = true;
|
||||
}
|
||||
}
|
||||
if (s.ok() && should_persist_format_version) {
|
||||
// Persistent stats CF being created for the first time, need to write
|
||||
// format version key
|
||||
WriteBatch batch;
|
||||
batch.Put(persist_stats_cf_handle_, kFormatVersionKeyString,
|
||||
ToString(kStatsCFCurrentFormatVersion));
|
||||
batch.Put(persist_stats_cf_handle_, kCompatibleVersionKeyString,
|
||||
ToString(kStatsCFCompatibleFormatVersion));
|
||||
WriteOptions wo;
|
||||
wo.low_pri = true;
|
||||
wo.no_slowdown = true;
|
||||
wo.sync = false;
|
||||
s = Write(wo, &batch);
|
||||
}
|
||||
mutex_.Lock();
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::InitPersistStatsColumnFamily() {
|
||||
mutex_.AssertHeld();
|
||||
assert(!persist_stats_cf_handle_);
|
||||
ColumnFamilyData* persistent_stats_cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(
|
||||
kPersistentStatsColumnFamilyName);
|
||||
persistent_stats_cfd_exists_ = persistent_stats_cfd != nullptr;
|
||||
|
||||
Status s;
|
||||
if (persistent_stats_cfd != nullptr) {
|
||||
// We are recovering from a DB which already contains persistent stats CF,
|
||||
// the CF is already created in VersionSet::ApplyOneVersionEdit, but
|
||||
// column family handle was not. Need to explicitly create handle here.
|
||||
persist_stats_cf_handle_ =
|
||||
new ColumnFamilyHandleImpl(persistent_stats_cfd, this, &mutex_);
|
||||
} else {
|
||||
mutex_.Unlock();
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ColumnFamilyOptions cfo;
|
||||
OptimizeForPersistentStats(&cfo);
|
||||
s = CreateColumnFamily(cfo, kPersistentStatsColumnFamilyName, &handle);
|
||||
persist_stats_cf_handle_ = static_cast<ColumnFamilyHandleImpl*>(handle);
|
||||
mutex_.Lock();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only) {
|
||||
@@ -577,12 +674,13 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
bool stop_replay_for_corruption = false;
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_log_number = kMaxSequenceNumber;
|
||||
uint64_t min_log_number = MinLogNumberToKeep();
|
||||
for (auto log_number : log_numbers) {
|
||||
if (log_number < versions_->min_log_number_to_keep_2pc()) {
|
||||
if (log_number < min_log_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Skipping log #%" PRIu64
|
||||
" since it is older than min log to keep #%" PRIu64,
|
||||
log_number, versions_->min_log_number_to_keep_2pc());
|
||||
log_number, min_log_number);
|
||||
continue;
|
||||
}
|
||||
// The previous incarnation may not have written any MANIFEST
|
||||
@@ -1086,12 +1184,23 @@ Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
if (db_options.persist_stats_to_disk) {
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kPersistentStatsColumnFamilyName, cf_options));
|
||||
}
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
Status s = DB::Open(db_options, dbname, column_families, &handles, dbptr);
|
||||
if (s.ok()) {
|
||||
assert(handles.size() == 1);
|
||||
if (db_options.persist_stats_to_disk) {
|
||||
assert(handles.size() == 2);
|
||||
} else {
|
||||
assert(handles.size() == 1);
|
||||
}
|
||||
// i can delete the handle since DBImpl is always holding a reference to
|
||||
// default column family
|
||||
if (db_options.persist_stats_to_disk && handles[1] != nullptr) {
|
||||
delete handles[1];
|
||||
}
|
||||
delete handles[0];
|
||||
}
|
||||
return s;
|
||||
@@ -1204,6 +1313,10 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
delete impl;
|
||||
return s;
|
||||
}
|
||||
|
||||
impl->wal_in_db_path_ =
|
||||
IsWalDirSameAsDBPath(&impl->immutable_db_options_);
|
||||
|
||||
impl->mutex_.Lock();
|
||||
// Handles create_if_missing, error_if_exists
|
||||
s = impl->Recover(column_families);
|
||||
@@ -1268,6 +1381,10 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
s = impl->directories_.GetDbDir()->Fsync();
|
||||
}
|
||||
}
|
||||
if (s.ok() && impl->immutable_db_options_.persist_stats_to_disk) {
|
||||
// try to read format version but no need to fail Open() even if it fails
|
||||
s = impl->PersistentStatsProcessFormatVersion();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
@@ -3,10 +3,10 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_impl_readonly.h"
|
||||
#include "db/db_impl/db_impl_readonly.h"
|
||||
|
||||
#include "db/compacted_db_impl.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -115,6 +115,16 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
@@ -3,22 +3,18 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_impl_secondary.h"
|
||||
#include "db/db_impl/db_impl_secondary.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "logging/auto_roll_logger.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "util/auto_roll_logger.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
DBImplSecondary::DBImplSecondary(const DBOptions& db_options,
|
||||
const std::string& dbname)
|
||||
: DBImpl(db_options, dbname) {
|
||||
@@ -35,6 +31,7 @@ Status DBImplSecondary::Recover(
|
||||
bool /*error_if_data_exists_in_logs*/) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
JobContext job_context(0);
|
||||
Status s;
|
||||
s = static_cast<ReactiveVersionSet*>(versions_.get())
|
||||
->Recover(column_families, &manifest_reader_, &manifest_reporter_,
|
||||
@@ -59,49 +56,73 @@ Status DBImplSecondary::Recover(
|
||||
single_column_family_mode_ =
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1;
|
||||
|
||||
// Recover from all newer log files than the ones named in the
|
||||
// descriptor.
|
||||
std::vector<std::string> filenames;
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &filenames);
|
||||
if (s.IsNotFound()) {
|
||||
return Status::InvalidArgument("Failed to open wal_dir",
|
||||
immutable_db_options_.wal_dir);
|
||||
} else if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> logs;
|
||||
// if log_readers_ is non-empty, it means we have applied all logs with log
|
||||
// numbers smaller than the smallest log in log_readers_, so there is no
|
||||
// need to pass these logs to RecoverLogFiles
|
||||
uint64_t log_number_min = 0;
|
||||
if (log_readers_.size() > 0) {
|
||||
log_number_min = log_readers_.begin()->first;
|
||||
}
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == kLogFile &&
|
||||
number >= log_number_min) {
|
||||
logs.push_back(number);
|
||||
}
|
||||
}
|
||||
|
||||
if (!logs.empty()) {
|
||||
// Recover in the order in which the logs were generated
|
||||
std::sort(logs.begin(), logs.end());
|
||||
SequenceNumber next_sequence(kMaxSequenceNumber);
|
||||
s = RecoverLogFiles(logs, &next_sequence, true /*read_only*/);
|
||||
}
|
||||
std::unordered_set<ColumnFamilyData*> cfds_changed;
|
||||
s = FindAndRecoverLogFiles(&cfds_changed, &job_context);
|
||||
}
|
||||
|
||||
if (s.IsPathNotFound()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Secondary tries to read WAL, but WAL file(s) have already "
|
||||
"been purged by primary.");
|
||||
s = Status::OK();
|
||||
}
|
||||
// TODO: update options_file_number_ needed?
|
||||
|
||||
job_context.Clean();
|
||||
return s;
|
||||
}
|
||||
|
||||
// find new WAL and apply them in order to the secondary instance
|
||||
Status DBImplSecondary::FindAndRecoverLogFiles(
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
JobContext* job_context) {
|
||||
assert(nullptr != cfds_changed);
|
||||
assert(nullptr != job_context);
|
||||
Status s;
|
||||
std::vector<uint64_t> logs;
|
||||
s = FindNewLogNumbers(&logs);
|
||||
if (s.ok() && !logs.empty()) {
|
||||
SequenceNumber next_sequence(kMaxSequenceNumber);
|
||||
s = RecoverLogFiles(logs, &next_sequence, cfds_changed, job_context);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// List wal_dir and find all new WALs, return these log numbers
|
||||
Status DBImplSecondary::FindNewLogNumbers(std::vector<uint64_t>* logs) {
|
||||
assert(logs != nullptr);
|
||||
std::vector<std::string> filenames;
|
||||
Status s;
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &filenames);
|
||||
if (s.IsNotFound()) {
|
||||
return Status::InvalidArgument("Failed to open wal_dir",
|
||||
immutable_db_options_.wal_dir);
|
||||
} else if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// if log_readers_ is non-empty, it means we have applied all logs with log
|
||||
// numbers smaller than the smallest log in log_readers_, so there is no
|
||||
// need to pass these logs to RecoverLogFiles
|
||||
uint64_t log_number_min = 0;
|
||||
if (!log_readers_.empty()) {
|
||||
log_number_min = log_readers_.begin()->first;
|
||||
}
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == kLogFile &&
|
||||
number >= log_number_min) {
|
||||
logs->push_back(number);
|
||||
}
|
||||
}
|
||||
// Recover logs in the order that they were generated
|
||||
if (!logs->empty()) {
|
||||
std::sort(logs->begin(), logs->end());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// try to find log reader using log_number from log_readers_ map, initialize
|
||||
// if it doesn't exist
|
||||
Status DBImplSecondary::MaybeInitLogReader(
|
||||
uint64_t log_number, log::FragmentBufferedReader** log_reader) {
|
||||
auto iter = log_readers_.find(log_number);
|
||||
@@ -149,7 +170,10 @@ Status DBImplSecondary::MaybeInitLogReader(
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status DBImplSecondary::RecoverLogFiles(
|
||||
const std::vector<uint64_t>& log_numbers, SequenceNumber* next_sequence,
|
||||
bool /*read_only*/) {
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
JobContext* job_context) {
|
||||
assert(nullptr != cfds_changed);
|
||||
assert(nullptr != job_context);
|
||||
mutex_.AssertHeld();
|
||||
Status status;
|
||||
for (auto log_number : log_numbers) {
|
||||
@@ -182,9 +206,56 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
continue;
|
||||
}
|
||||
WriteBatchInternal::SetContents(&batch, record);
|
||||
// do not check sequence number because user may toggle disableWAL
|
||||
// between writes which breaks sequence number continuity guarantee
|
||||
|
||||
SequenceNumber seq_of_batch = WriteBatchInternal::Sequence(&batch);
|
||||
std::vector<uint32_t> column_family_ids;
|
||||
status = CollectColumnFamilyIdsFromWriteBatch(batch, &column_family_ids);
|
||||
if (status.ok()) {
|
||||
for (const auto id : column_family_ids) {
|
||||
ColumnFamilyData* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(id);
|
||||
if (cfd == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
}
|
||||
const std::vector<FileMetaData*>& l0_files =
|
||||
cfd->current()->storage_info()->LevelFiles(0);
|
||||
SequenceNumber seq =
|
||||
l0_files.empty() ? 0 : l0_files.back()->fd.largest_seqno;
|
||||
// If the write batch's sequence number is smaller than the last
|
||||
// sequence number of the largest sequence persisted for this column
|
||||
// family, then its data must reside in an SST that has already been
|
||||
// added in the prior MANIFEST replay.
|
||||
if (seq_of_batch <= seq) {
|
||||
continue;
|
||||
}
|
||||
auto curr_log_num = port::kMaxUint64;
|
||||
if (cfd_to_current_log_.count(cfd) > 0) {
|
||||
curr_log_num = cfd_to_current_log_[cfd];
|
||||
}
|
||||
// If the active memtable contains records added by replaying an
|
||||
// earlier WAL, then we need to seal the memtable, add it to the
|
||||
// immutable memtable list and create a new active memtable.
|
||||
if (!cfd->mem()->IsEmpty() && (curr_log_num == port::kMaxUint64 ||
|
||||
curr_log_num != log_number)) {
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MemTable* new_mem =
|
||||
cfd->ConstructNewMemtable(mutable_cf_options, seq_of_batch);
|
||||
cfd->mem()->SetNextLogNumber(log_number);
|
||||
cfd->imm()->Add(cfd->mem(), &job_context->memtables_to_free);
|
||||
new_mem->Ref();
|
||||
cfd->SetMemtable(new_mem);
|
||||
}
|
||||
}
|
||||
bool has_valid_writes = false;
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
&batch, column_family_memtables_.get(),
|
||||
nullptr /* flush_scheduler */, true, log_number, this,
|
||||
false /* concurrent_memtable_writes */, next_sequence,
|
||||
&has_valid_writes, seq_per_batch_, batch_per_txn_);
|
||||
}
|
||||
// If column family was not found, it might mean that the WAL write
|
||||
// batch references to the column family that was dropped after the
|
||||
// insert. We don't want to fail the whole write batch in that case --
|
||||
@@ -192,36 +263,43 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
// That's why we set ignore missing column families to true
|
||||
// passing null flush_scheduler will disable memtable flushing which is
|
||||
// needed for secondary instances
|
||||
bool has_valid_writes = false;
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
&batch, column_family_memtables_.get(), nullptr /* flush_scheduler */,
|
||||
true, log_number, this, false /* concurrent_memtable_writes */,
|
||||
next_sequence, &has_valid_writes, seq_per_batch_, batch_per_txn_);
|
||||
if (!status.ok()) {
|
||||
if (status.ok()) {
|
||||
for (const auto id : column_family_ids) {
|
||||
ColumnFamilyData* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(id);
|
||||
if (cfd == nullptr) {
|
||||
continue;
|
||||
}
|
||||
std::unordered_map<ColumnFamilyData*, uint64_t>::iterator iter =
|
||||
cfd_to_current_log_.find(cfd);
|
||||
if (iter == cfd_to_current_log_.end()) {
|
||||
cfd_to_current_log_.insert({cfd, log_number});
|
||||
} else if (log_number > iter->second) {
|
||||
iter->second = log_number;
|
||||
}
|
||||
}
|
||||
auto last_sequence = *next_sequence - 1;
|
||||
if ((*next_sequence != kMaxSequenceNumber) &&
|
||||
(versions_->LastSequence() <= last_sequence)) {
|
||||
versions_->SetLastAllocatedSequence(last_sequence);
|
||||
versions_->SetLastPublishedSequence(last_sequence);
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
} else {
|
||||
// We are treating this as a failure while reading since we read valid
|
||||
// blocks that do not form coherent data
|
||||
reader->GetReporter()->Corruption(record.size(), status);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
auto last_sequence = *next_sequence - 1;
|
||||
if ((*next_sequence != kMaxSequenceNumber) &&
|
||||
(versions_->LastSequence() <= last_sequence)) {
|
||||
versions_->SetLastAllocatedSequence(last_sequence);
|
||||
versions_->SetLastPublishedSequence(last_sequence);
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
}
|
||||
// remove logreaders from map after successfully recovering the WAL
|
||||
if (log_readers_.size() > 1) {
|
||||
auto eraseIter = log_readers_.begin();
|
||||
std::advance(eraseIter, log_readers_.size() - 1);
|
||||
log_readers_.erase(log_readers_.begin(), eraseIter);
|
||||
auto erase_iter = log_readers_.begin();
|
||||
std::advance(erase_iter, log_readers_.size() - 1);
|
||||
log_readers_.erase(log_readers_.begin(), erase_iter);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -373,21 +451,89 @@ Status DBImplSecondary::NewIterators(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImplSecondary::CheckConsistency() {
|
||||
mutex_.AssertHeld();
|
||||
Status s = DBImpl::CheckConsistency();
|
||||
// If DBImpl::CheckConsistency() which is stricter returns success, then we
|
||||
// do not need to give a second chance.
|
||||
if (s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// It's possible that DBImpl::CheckConssitency() can fail because the primary
|
||||
// may have removed certain files, causing the GetFileSize(name) call to
|
||||
// fail and returning a PathNotFound. In this case, we take a best-effort
|
||||
// approach and just proceed.
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImplSecondary::CheckConsistency:AfterFirstAttempt", &s);
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
versions_->GetLiveFilesMetaData(&metadata);
|
||||
|
||||
std::string corruption_messages;
|
||||
for (const auto& md : metadata) {
|
||||
// md.name has a leading "/".
|
||||
std::string file_path = md.db_path + md.name;
|
||||
|
||||
uint64_t fsize = 0;
|
||||
s = env_->GetFileSize(file_path, &fsize);
|
||||
if (!s.ok() &&
|
||||
(env_->GetFileSize(Rocks2LevelTableFileName(file_path), &fsize).ok() ||
|
||||
s.IsPathNotFound())) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
corruption_messages +=
|
||||
"Can't access " + md.name + ": " + s.ToString() + "\n";
|
||||
}
|
||||
}
|
||||
return corruption_messages.empty() ? Status::OK()
|
||||
: Status::Corruption(corruption_messages);
|
||||
}
|
||||
|
||||
Status DBImplSecondary::TryCatchUpWithPrimary() {
|
||||
assert(versions_.get() != nullptr);
|
||||
assert(manifest_reader_.get() != nullptr);
|
||||
Status s;
|
||||
// read the manifest and apply new changes to the secondary instance
|
||||
std::unordered_set<ColumnFamilyData*> cfds_changed;
|
||||
JobContext job_context(0, true /*create_superversion*/);
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
s = static_cast<ReactiveVersionSet*>(versions_.get())
|
||||
->ReadAndApply(&mutex_, &manifest_reader_, &cfds_changed);
|
||||
if (s.ok()) {
|
||||
SuperVersionContext sv_context(true /* create_superversion */);
|
||||
for (auto cfd : cfds_changed) {
|
||||
sv_context.NewSuperVersion();
|
||||
cfd->InstallSuperVersion(&sv_context, &mutex_);
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Last sequence is %" PRIu64,
|
||||
static_cast<uint64_t>(versions_->LastSequence()));
|
||||
for (ColumnFamilyData* cfd : cfds_changed) {
|
||||
if (cfd->IsDropped()) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] is dropped\n",
|
||||
cfd->GetName().c_str());
|
||||
continue;
|
||||
}
|
||||
sv_context.Clean();
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] Level summary: %s\n",
|
||||
cfd->GetName().c_str(),
|
||||
cfd->current()->storage_info()->LevelSummary(&tmp));
|
||||
}
|
||||
|
||||
// list wal_dir to discover new WALs and apply new changes to the secondary
|
||||
// instance
|
||||
if (s.ok()) {
|
||||
s = FindAndRecoverLogFiles(&cfds_changed, &job_context);
|
||||
}
|
||||
if (s.IsPathNotFound()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Secondary tries to read WAL, but WAL file(s) have already "
|
||||
"been purged by primary.");
|
||||
s = Status::OK();
|
||||
}
|
||||
if (s.ok()) {
|
||||
for (auto cfd : cfds_changed) {
|
||||
cfd->imm()->RemoveOldMemTables(cfd->GetLogNumber(),
|
||||
&job_context.memtables_to_free);
|
||||
auto& sv_context = job_context.superversion_contexts.back();
|
||||
cfd->InstallSuperVersion(&sv_context, &mutex_);
|
||||
sv_context.NewSuperVersion();
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -425,39 +571,14 @@ Status DB::OpenAsSecondary(
|
||||
}
|
||||
|
||||
DBOptions tmp_opts(db_options);
|
||||
Status s;
|
||||
if (nullptr == tmp_opts.info_log) {
|
||||
Env* env = tmp_opts.env;
|
||||
assert(env != nullptr);
|
||||
std::string secondary_abs_path;
|
||||
env->GetAbsolutePath(secondary_path, &secondary_abs_path);
|
||||
std::string fname = InfoLogFileName(secondary_path, secondary_abs_path,
|
||||
tmp_opts.db_log_dir);
|
||||
|
||||
env->CreateDirIfMissing(secondary_path);
|
||||
if (tmp_opts.log_file_time_to_roll > 0 || tmp_opts.max_log_file_size > 0) {
|
||||
AutoRollLogger* result = new AutoRollLogger(
|
||||
env, secondary_path, tmp_opts.db_log_dir, tmp_opts.max_log_file_size,
|
||||
tmp_opts.log_file_time_to_roll, tmp_opts.info_log_level);
|
||||
Status s = result->GetStatus();
|
||||
if (!s.ok()) {
|
||||
delete result;
|
||||
} else {
|
||||
tmp_opts.info_log.reset(result);
|
||||
}
|
||||
}
|
||||
if (nullptr == tmp_opts.info_log) {
|
||||
env->RenameFile(
|
||||
fname, OldInfoLogFileName(secondary_path, env->NowMicros(),
|
||||
secondary_abs_path, tmp_opts.db_log_dir));
|
||||
Status s = env->NewLogger(fname, &(tmp_opts.info_log));
|
||||
if (tmp_opts.info_log != nullptr) {
|
||||
tmp_opts.info_log->SetInfoLogLevel(tmp_opts.info_log_level);
|
||||
}
|
||||
s = CreateLoggerFromOptions(secondary_path, tmp_opts, &tmp_opts.info_log);
|
||||
if (!s.ok()) {
|
||||
tmp_opts.info_log = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
assert(tmp_opts.info_log != nullptr);
|
||||
|
||||
handles->clear();
|
||||
DBImplSecondary* impl = new DBImplSecondary(tmp_opts, dbname);
|
||||
impl->versions_.reset(new ReactiveVersionSet(
|
||||
@@ -466,8 +587,11 @@ Status DB::OpenAsSecondary(
|
||||
&impl->write_controller_));
|
||||
impl->column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
|
||||
impl->wal_in_db_path_ =
|
||||
IsWalDirSameAsDBPath(&impl->immutable_db_options_);
|
||||
|
||||
impl->mutex_.Lock();
|
||||
Status s = impl->Recover(column_families, true, false, false);
|
||||
s = impl->Recover(column_families, true, false, false);
|
||||
if (s.ok()) {
|
||||
for (auto cf : column_families) {
|
||||
auto cfd =
|
||||
@@ -9,10 +9,11 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// A wrapper class to hold log reader, log reporter, log status.
|
||||
class LogReaderContainer {
|
||||
public:
|
||||
LogReaderContainer()
|
||||
@@ -62,11 +63,19 @@ class LogReaderContainer {
|
||||
};
|
||||
};
|
||||
|
||||
// The secondary instance shares access to the storage as the primary.
|
||||
// The secondary is able to read and replay changes described in both the
|
||||
// MANIFEST and the WAL files without coordination with the primary.
|
||||
// The secondary instance can be opened using `DB::OpenAsSecondary`. After
|
||||
// that, it can call `DBImplSecondary::TryCatchUpWithPrimary` to make best
|
||||
// effort attempts to catch up with the primary.
|
||||
class DBImplSecondary : public DBImpl {
|
||||
public:
|
||||
DBImplSecondary(const DBOptions& options, const std::string& dbname);
|
||||
~DBImplSecondary() override;
|
||||
|
||||
// Recover by replaying MANIFEST and WAL. Also initialize manifest_reader_
|
||||
// and log_readers_ to facilitate future operations.
|
||||
Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only, bool error_if_log_file_exist,
|
||||
bool error_if_data_exists_in_logs) override;
|
||||
@@ -96,40 +105,40 @@ class DBImplSecondary : public DBImpl {
|
||||
Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Merge;
|
||||
Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Delete;
|
||||
Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::SingleDelete;
|
||||
Status SingleDelete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::CompactRange;
|
||||
Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/, const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::CompactFiles;
|
||||
@@ -140,32 +149,32 @@ class DBImplSecondary : public DBImpl {
|
||||
const int /*output_level*/, const int /*output_path_id*/ = -1,
|
||||
std::vector<std::string>* const /*output_file_names*/ = nullptr,
|
||||
CompactionJobInfo* /*compaction_job_info*/ = nullptr) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* /*manifest_file_size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Flush;
|
||||
Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::SyncWAL;
|
||||
Status SyncWAL() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
@@ -173,7 +182,7 @@ class DBImplSecondary : public DBImpl {
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
// Try to catch up with the primary by reading as much as possible from the
|
||||
@@ -182,9 +191,84 @@ class DBImplSecondary : public DBImpl {
|
||||
// method can take long time due to all the I/O and CPU costs.
|
||||
Status TryCatchUpWithPrimary() override;
|
||||
|
||||
|
||||
// Try to find log reader using log_number from log_readers_ map, initialize
|
||||
// if it doesn't exist
|
||||
Status MaybeInitLogReader(uint64_t log_number,
|
||||
log::FragmentBufferedReader** log_reader);
|
||||
|
||||
// Check if all live files exist on file system and that their file sizes
|
||||
// matche to the in-memory records. It is possible that some live files may
|
||||
// have been deleted by the primary. In this case, CheckConsistency() does
|
||||
// not flag the missing file as inconsistency.
|
||||
Status CheckConsistency() override;
|
||||
|
||||
protected:
|
||||
// ColumnFamilyCollector is a write batch handler which does nothing
|
||||
// except recording unique column family IDs
|
||||
class ColumnFamilyCollector : public WriteBatch::Handler {
|
||||
std::unordered_set<uint32_t> column_family_ids_;
|
||||
|
||||
Status AddColumnFamilyId(uint32_t column_family_id) {
|
||||
if (column_family_ids_.find(column_family_id) ==
|
||||
column_family_ids_.end()) {
|
||||
column_family_ids_.insert(column_family_id);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ColumnFamilyCollector() {}
|
||||
|
||||
~ColumnFamilyCollector() override {}
|
||||
|
||||
Status PutCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status DeleteCF(uint32_t column_family_id, const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status SingleDeleteCF(uint32_t column_family_id, const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status DeleteRangeCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status MergeCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status PutBlobIndexCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
const std::unordered_set<uint32_t>& column_families() const {
|
||||
return column_family_ids_;
|
||||
}
|
||||
};
|
||||
|
||||
Status CollectColumnFamilyIdsFromWriteBatch(
|
||||
const WriteBatch& batch, std::vector<uint32_t>* column_family_ids) {
|
||||
assert(column_family_ids != nullptr);
|
||||
column_family_ids->clear();
|
||||
ColumnFamilyCollector handler;
|
||||
Status s = batch.Iterate(&handler);
|
||||
if (s.ok()) {
|
||||
for (const auto& cf : handler.column_families()) {
|
||||
column_family_ids->push_back(cf);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
@@ -194,17 +278,27 @@ class DBImplSecondary : public DBImpl {
|
||||
|
||||
using DBImpl::Recover;
|
||||
|
||||
Status FindAndRecoverLogFiles(
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
JobContext* job_context);
|
||||
Status FindNewLogNumbers(std::vector<uint64_t>* logs);
|
||||
// After manifest recovery, replay WALs and refresh log_readers_ if necessary
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* next_sequence,
|
||||
bool read_only) override;
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
JobContext* job_context);
|
||||
|
||||
std::unique_ptr<log::FragmentBufferedReader> manifest_reader_;
|
||||
std::unique_ptr<log::Reader::Reporter> manifest_reporter_;
|
||||
std::unique_ptr<Status> manifest_reader_status_;
|
||||
|
||||
// cache log readers for each log number, used for continue WAL replay
|
||||
// Cache log readers for each log number, used for continue WAL replay
|
||||
// after recovery
|
||||
std::map<uint64_t, std::unique_ptr<LogReaderContainer>> log_readers_;
|
||||
|
||||
// Current WAL number replayed for each column family.
|
||||
std::unordered_map<ColumnFamilyData*, uint64_t> cfd_to_current_log_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -6,17 +6,14 @@
|
||||
// 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"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include "db/error_handler.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
// Convenience methods
|
||||
@@ -94,6 +91,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
return Status::NotSupported(
|
||||
"pipelined_writes is not compatible with seq_per_batch");
|
||||
}
|
||||
if (immutable_db_options_.unordered_write &&
|
||||
immutable_db_options_.enable_pipelined_write) {
|
||||
return Status::NotSupported(
|
||||
"pipelined_writes is not compatible with unordered_write");
|
||||
}
|
||||
// Otherwise IsLatestPersistentState optimization does not make sense
|
||||
assert(!WriteBatchInternal::IsLatestPersistentState(my_batch) ||
|
||||
disable_memtable);
|
||||
@@ -107,8 +109,39 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
if (two_write_queues_ && disable_memtable) {
|
||||
return WriteImplWALOnly(write_options, my_batch, callback, log_used,
|
||||
log_ref, seq_used, batch_cnt, pre_release_callback);
|
||||
AssignOrder assign_order =
|
||||
seq_per_batch_ ? kDoAssignOrder : kDontAssignOrder;
|
||||
// Otherwise it is WAL-only Prepare batches in WriteCommitted policy and
|
||||
// they don't consume sequence.
|
||||
return WriteImplWALOnly(&nonmem_write_thread_, write_options, my_batch,
|
||||
callback, log_used, log_ref, seq_used, batch_cnt,
|
||||
pre_release_callback, assign_order,
|
||||
kDontPublishLastSeq, disable_memtable);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.unordered_write) {
|
||||
const size_t sub_batch_cnt = batch_cnt != 0
|
||||
? batch_cnt
|
||||
// every key is a sub-batch consuming a seq
|
||||
: WriteBatchInternal::Count(my_batch);
|
||||
uint64_t seq;
|
||||
// Use a write thread to i) optimize for WAL write, ii) publish last
|
||||
// sequence in in increasing order, iii) call pre_release_callback serially
|
||||
status = WriteImplWALOnly(&write_thread_, write_options, my_batch, callback,
|
||||
log_used, log_ref, &seq, sub_batch_cnt,
|
||||
pre_release_callback, kDoAssignOrder,
|
||||
kDoPublishLastSeq, disable_memtable);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (seq_used) {
|
||||
*seq_used = seq;
|
||||
}
|
||||
if (!disable_memtable) {
|
||||
status = UnorderedWriteMemtable(write_options, my_batch, callback,
|
||||
log_ref, seq, sub_batch_cnt);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
@@ -178,9 +211,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteThread::WriteGroup write_group;
|
||||
bool in_parallel_group = false;
|
||||
uint64_t last_sequence = kMaxSequenceNumber;
|
||||
if (!two_write_queues_) {
|
||||
last_sequence = versions_->LastSequence();
|
||||
}
|
||||
|
||||
mutex_.Lock();
|
||||
|
||||
@@ -195,6 +225,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
if (!two_write_queues_) {
|
||||
// Assign it after ::PreprocessWrite since the sequence might advance
|
||||
// inside it by WriteRecoverableState
|
||||
last_sequence = versions_->LastSequence();
|
||||
}
|
||||
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
}
|
||||
@@ -228,6 +263,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
size_t total_count = 0;
|
||||
size_t valid_batches = 0;
|
||||
size_t total_byte_size = 0;
|
||||
size_t pre_release_callback_cnt = 0;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
valid_batches += writer->batch_cnt;
|
||||
@@ -235,9 +271,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
total_count += WriteBatchInternal::Count(writer->batch);
|
||||
parallel = parallel && !writer->batch->HasMerge();
|
||||
}
|
||||
|
||||
total_byte_size = WriteBatchInternal::AppendedByteSize(
|
||||
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
|
||||
if (writer->pre_release_callback) {
|
||||
pre_release_callback_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note about seq_per_batch_: either disableWAL is set for the entire write
|
||||
@@ -301,6 +339,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// PreReleaseCallback is called after WAL write and before memtable write
|
||||
if (status.ok()) {
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
size_t index = 0;
|
||||
// Note: the logic for advancing seq here must be consistent with the
|
||||
// logic in WriteBatchInternal::InsertInto(write_group...) as well as
|
||||
// with WriteBatchInternal::InsertInto(write_batch...) that is called on
|
||||
@@ -312,7 +351,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
writer->sequence = next_sequence;
|
||||
if (writer->pre_release_callback) {
|
||||
Status ws = writer->pre_release_callback->Callback(
|
||||
writer->sequence, disable_memtable, writer->log_used);
|
||||
writer->sequence, disable_memtable, writer->log_used, index++,
|
||||
pre_release_callback_cnt);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
break;
|
||||
@@ -534,23 +574,70 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
return w.FinalStatus();
|
||||
}
|
||||
|
||||
Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch,
|
||||
WriteCallback* callback, uint64_t log_ref,
|
||||
SequenceNumber seq,
|
||||
const size_t sub_batch_cnt) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
false /*disable_memtable*/);
|
||||
|
||||
if (w.CheckCallback(this) && w.ShouldWriteToMemtable()) {
|
||||
w.sequence = seq;
|
||||
size_t total_count = WriteBatchInternal::Count(my_batch);
|
||||
InternalStats* stats = default_cf_internal_stats_;
|
||||
stats->AddDBStats(InternalStats::NUMBER_KEYS_WRITTEN, total_count);
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
true /*concurrent_memtable_writes*/, seq_per_batch_, sub_batch_cnt);
|
||||
|
||||
WriteStatusCheck(w.status);
|
||||
if (write_options.disableWAL) {
|
||||
has_unpersisted_data_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
size_t pending_cnt = pending_memtable_writes_.fetch_sub(1) - 1;
|
||||
if (pending_cnt == 0) {
|
||||
// switch_cv_ waits until pending_memtable_writes_ = 0. Locking its mutex
|
||||
// before notify ensures that cv is in waiting state when it is notified
|
||||
// thus not missing the update to pending_memtable_writes_ even though it is
|
||||
// not modified under the mutex.
|
||||
std::lock_guard<std::mutex> lck(switch_mutex_);
|
||||
switch_cv_.notify_all();
|
||||
}
|
||||
|
||||
if (!w.FinalStatus().ok()) {
|
||||
return w.FinalStatus();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// The 2nd write queue. If enabled it will be used only for WAL-only writes.
|
||||
// This is the only queue that updates LastPublishedSequence which is only
|
||||
// applicable in a two-queue setting.
|
||||
Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
uint64_t* seq_used, size_t batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback) {
|
||||
Status DBImpl::WriteImplWALOnly(
|
||||
WriteThread* write_thread, const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback, uint64_t* log_used,
|
||||
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
|
||||
const PublishLastSeq publish_last_seq, const bool disable_memtable) {
|
||||
Status status;
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
true /* disable_memtable */, batch_cnt,
|
||||
pre_release_callback);
|
||||
disable_memtable, sub_batch_cnt, pre_release_callback);
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
nonmem_write_thread_.JoinBatchGroup(&w);
|
||||
write_thread->JoinBatchGroup(&w);
|
||||
assert(w.state != WriteThread::STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
if (w.state == WriteThread::STATE_COMPLETED) {
|
||||
if (log_used != nullptr) {
|
||||
@@ -563,17 +650,45 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
}
|
||||
// else we are the leader of the write batch group
|
||||
assert(w.state == WriteThread::STATE_GROUP_LEADER);
|
||||
|
||||
if (publish_last_seq == kDoPublishLastSeq) {
|
||||
// Currently we only use kDoPublishLastSeq in unordered_write
|
||||
assert(immutable_db_options_.unordered_write);
|
||||
WriteContext write_context;
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
status = error_handler_.GetBGError();
|
||||
}
|
||||
// TODO(myabandeh): Make preliminary checks thread-safe so we could do them
|
||||
// without paying the cost of obtaining the mutex.
|
||||
if (status.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
bool need_log_sync = false;
|
||||
status = PreprocessWrite(write_options, &need_log_sync, &write_context);
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
WriteThread::WriteGroup write_group;
|
||||
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
|
||||
write_thread->ExitAsBatchGroupLeader(write_group, status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
WriteThread::WriteGroup write_group;
|
||||
uint64_t last_sequence;
|
||||
nonmem_write_thread_.EnterAsBatchGroupLeader(&w, &write_group);
|
||||
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
|
||||
// Note: no need to update last_batch_group_size_ here since the batch writes
|
||||
// to WAL only
|
||||
|
||||
size_t pre_release_callback_cnt = 0;
|
||||
size_t total_byte_size = 0;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
total_byte_size = WriteBatchInternal::AppendedByteSize(
|
||||
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
|
||||
if (writer->pre_release_callback) {
|
||||
pre_release_callback_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,11 +717,13 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
// LastAllocatedSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
size_t seq_inc = 0 /* total_count */;
|
||||
if (seq_per_batch_) {
|
||||
if (assign_order == kDoAssignOrder) {
|
||||
size_t total_batch_cnt = 0;
|
||||
for (auto* writer : write_group) {
|
||||
assert(writer->batch_cnt);
|
||||
total_batch_cnt += writer->batch_cnt;
|
||||
assert(writer->batch_cnt || !seq_per_batch_);
|
||||
if (!writer->CallbackFailed()) {
|
||||
total_batch_cnt += writer->batch_cnt;
|
||||
}
|
||||
}
|
||||
seq_inc = total_batch_cnt;
|
||||
}
|
||||
@@ -617,16 +734,21 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
// Otherwise we inc seq number to do solely the seq allocation
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
}
|
||||
|
||||
size_t memtable_write_cnt = 0;
|
||||
auto curr_seq = last_sequence + 1;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CallbackFailed()) {
|
||||
continue;
|
||||
}
|
||||
writer->sequence = curr_seq;
|
||||
if (seq_per_batch_) {
|
||||
assert(writer->batch_cnt);
|
||||
if (assign_order == kDoAssignOrder) {
|
||||
assert(writer->batch_cnt || !seq_per_batch_);
|
||||
curr_seq += writer->batch_cnt;
|
||||
}
|
||||
if (!writer->disable_memtable) {
|
||||
memtable_write_cnt++;
|
||||
}
|
||||
// else seq advances only by memtable writes
|
||||
}
|
||||
if (status.ok() && write_options.sync) {
|
||||
@@ -645,12 +767,13 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
WriteStatusCheck(status);
|
||||
}
|
||||
if (status.ok()) {
|
||||
size_t index = 0;
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed() && writer->pre_release_callback) {
|
||||
assert(writer->sequence != kMaxSequenceNumber);
|
||||
const bool DISABLE_MEMTABLE = true;
|
||||
Status ws = writer->pre_release_callback->Callback(
|
||||
writer->sequence, DISABLE_MEMTABLE, writer->log_used);
|
||||
writer->sequence, disable_memtable, writer->log_used, index++,
|
||||
pre_release_callback_cnt);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
break;
|
||||
@@ -658,7 +781,15 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
}
|
||||
}
|
||||
}
|
||||
nonmem_write_thread_.ExitAsBatchGroupLeader(write_group, status);
|
||||
if (publish_last_seq == kDoPublishLastSeq) {
|
||||
versions_->SetLastSequence(last_sequence + seq_inc);
|
||||
// Currently we only use kDoPublishLastSeq in unordered_write
|
||||
assert(immutable_db_options_.unordered_write);
|
||||
}
|
||||
if (immutable_db_options_.unordered_write && status.ok()) {
|
||||
pending_memtable_writes_ += memtable_write_cnt;
|
||||
}
|
||||
write_thread->ExitAsBatchGroupLeader(write_group, status);
|
||||
if (status.ok()) {
|
||||
status = w.FinalStatus();
|
||||
}
|
||||
@@ -710,6 +841,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1);
|
||||
if (UNLIKELY(status.ok() && !single_column_family_mode_ &&
|
||||
total_log_size_ > GetMaxTotalWalSize())) {
|
||||
WaitForPendingWrites();
|
||||
status = SwitchWAL(write_context);
|
||||
}
|
||||
|
||||
@@ -719,10 +851,12 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
// 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.
|
||||
WaitForPendingWrites();
|
||||
status = HandleWriteBufferFull(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !flush_scheduler_.Empty())) {
|
||||
WaitForPendingWrites();
|
||||
status = ScheduleFlushes(write_context);
|
||||
}
|
||||
|
||||
@@ -994,8 +1128,12 @@ Status DBImpl::WriteRecoverableState() {
|
||||
for (uint64_t sub_batch_seq = seq + 1;
|
||||
sub_batch_seq < next_seq && status.ok(); sub_batch_seq++) {
|
||||
uint64_t const no_log_num = 0;
|
||||
// Unlock it since the callback might end up locking mutex. e.g.,
|
||||
// AddCommitted -> AdvanceMaxEvictedSeq -> GetSnapshotListFromDB
|
||||
mutex_.Unlock();
|
||||
status = recoverable_state_pre_release_callback_->Callback(
|
||||
sub_batch_seq, !DISABLE_MEMTABLE, no_log_num);
|
||||
sub_batch_seq, !DISABLE_MEMTABLE, no_log_num, 0, 1);
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
@@ -1090,6 +1228,7 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
cfds.push_back(cfd);
|
||||
}
|
||||
}
|
||||
MaybeFlushStatsCF(&cfds);
|
||||
}
|
||||
for (const auto cfd : cfds) {
|
||||
cfd->Ref();
|
||||
@@ -1156,6 +1295,7 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
if (cfd_picked != nullptr) {
|
||||
cfds.push_back(cfd_picked);
|
||||
}
|
||||
MaybeFlushStatsCF(&cfds);
|
||||
}
|
||||
|
||||
for (const auto cfd : cfds) {
|
||||
@@ -1299,6 +1439,40 @@ Status DBImpl::ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::MaybeFlushStatsCF(autovector<ColumnFamilyData*>* cfds) {
|
||||
assert(cfds != nullptr);
|
||||
if (!cfds->empty() && immutable_db_options_.persist_stats_to_disk) {
|
||||
ColumnFamilyData* cfd_stats =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(
|
||||
kPersistentStatsColumnFamilyName);
|
||||
if (cfd_stats != nullptr && !cfd_stats->mem()->IsEmpty()) {
|
||||
for (ColumnFamilyData* cfd : *cfds) {
|
||||
if (cfd == cfd_stats) {
|
||||
// stats CF already included in cfds
|
||||
return;
|
||||
}
|
||||
}
|
||||
// force flush stats CF when its log number is less than all other CF's
|
||||
// log numbers
|
||||
bool force_flush_stats_cf = true;
|
||||
for (auto* loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (loop_cfd == cfd_stats) {
|
||||
continue;
|
||||
}
|
||||
if (loop_cfd->GetLogNumber() <= cfd_stats->GetLogNumber()) {
|
||||
force_flush_stats_cf = false;
|
||||
}
|
||||
}
|
||||
if (force_flush_stats_cf) {
|
||||
cfds->push_back(cfd_stats);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Force flushing stats CF with automated flush "
|
||||
"to avoid holding old logs");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
@@ -1312,6 +1486,7 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
while ((tmp_cfd = flush_scheduler_.TakeNextColumnFamily()) != nullptr) {
|
||||
cfds.push_back(tmp_cfd);
|
||||
}
|
||||
MaybeFlushStatsCF(&cfds);
|
||||
}
|
||||
Status status;
|
||||
for (auto& cfd : cfds) {
|
||||
@@ -1547,11 +1722,25 @@ size_t DBImpl::GetWalPreallocateBlockSize(uint64_t write_buffer_size) const {
|
||||
// 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);
|
||||
Status s = batch.Put(column_family, key, value);
|
||||
if (nullptr == opt.timestamp) {
|
||||
// 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);
|
||||
Status s = batch.Put(column_family, key, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
Slice akey;
|
||||
std::string buf;
|
||||
Status s = AppendTimestamp(key, *(opt.timestamp), &akey, &buf);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
WriteBatch batch(akey.size() + value.size() + 24);
|
||||
s = batch.Put(column_family, akey, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -7,11 +7,11 @@
|
||||
// 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_secondary.h"
|
||||
#include "db/db_impl/db_impl_secondary.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -237,6 +237,17 @@ TEST_F(DBSecondaryTest, OpenAsSecondaryWALTailing) {
|
||||
};
|
||||
|
||||
verify_db_func("foo_value2", "bar_value2");
|
||||
|
||||
ASSERT_OK(Put("foo", "new_foo_value"));
|
||||
ASSERT_OK(Put("bar", "new_bar_value"));
|
||||
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
verify_db_func("new_foo_value", "new_bar_value");
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "new_foo_value_1"));
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
verify_db_func("new_foo_value_1", "new_bar_value");
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, OpenWithNonExistColumnFamily) {
|
||||
@@ -362,7 +373,7 @@ TEST_F(DBSecondaryTest, MissingTableFile) {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"ReactiveVersionSet::ReadAndApply:AfterLoadTableHandlers",
|
||||
"ReactiveVersionSet::ApplyOneVersionEditToBuilder:AfterLoadTableHandlers",
|
||||
[&](void* arg) {
|
||||
Status s = *reinterpret_cast<Status*>(arg);
|
||||
if (s.IsPathNotFound()) {
|
||||
@@ -513,6 +524,255 @@ TEST_F(DBSecondaryTest, SwitchManifest) {
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
range_scan_db();
|
||||
}
|
||||
|
||||
// Here, "Snapshot" refers to the version edits written by
|
||||
// VersionSet::WriteSnapshot() at the beginning of the new MANIFEST after
|
||||
// switching from the old one.
|
||||
TEST_F(DBSecondaryTest, SkipSnapshotAfterManifestSwitch) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
options1.max_open_files = -1;
|
||||
OpenSecondary(options1);
|
||||
|
||||
ASSERT_OK(Put("0", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
std::string value;
|
||||
ReadOptions ropts;
|
||||
ropts.verify_checksums = true;
|
||||
ASSERT_OK(db_secondary_->Get(ropts, "0", &value));
|
||||
ASSERT_EQ("value0", value);
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, SwitchWAL) {
|
||||
const int kNumKeysPerMemtable = 1;
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 2;
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(kNumKeysPerMemtable));
|
||||
Reopen(options);
|
||||
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
options1.max_open_files = -1;
|
||||
OpenSecondary(options1);
|
||||
|
||||
const auto& verify_db = [](DB* db1, DB* db2) {
|
||||
ASSERT_NE(nullptr, db1);
|
||||
ASSERT_NE(nullptr, db2);
|
||||
ReadOptions read_opts;
|
||||
read_opts.verify_checksums = true;
|
||||
std::unique_ptr<Iterator> it1(db1->NewIterator(read_opts));
|
||||
std::unique_ptr<Iterator> it2(db2->NewIterator(read_opts));
|
||||
it1->SeekToFirst();
|
||||
it2->SeekToFirst();
|
||||
for (; it1->Valid() && it2->Valid(); it1->Next(), it2->Next()) {
|
||||
ASSERT_EQ(it1->key(), it2->key());
|
||||
ASSERT_EQ(it1->value(), it2->value());
|
||||
}
|
||||
ASSERT_FALSE(it1->Valid());
|
||||
ASSERT_FALSE(it2->Valid());
|
||||
|
||||
for (it1->SeekToFirst(); it1->Valid(); it1->Next()) {
|
||||
std::string value;
|
||||
ASSERT_OK(db2->Get(read_opts, it1->key(), &value));
|
||||
ASSERT_EQ(it1->value(), value);
|
||||
}
|
||||
for (it2->SeekToFirst(); it2->Valid(); it2->Next()) {
|
||||
std::string value;
|
||||
ASSERT_OK(db1->Get(read_opts, it2->key(), &value));
|
||||
ASSERT_EQ(it2->value(), value);
|
||||
}
|
||||
};
|
||||
for (int k = 0; k != 16; ++k) {
|
||||
ASSERT_OK(Put("key" + std::to_string(k), "value" + std::to_string(k)));
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
verify_db(dbfull(), db_secondary_);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, SwitchWALMultiColumnFamilies) {
|
||||
const int kNumKeysPerMemtable = 1;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::BackgroundCallFlush:ContextCleanedUp",
|
||||
"DBSecondaryTest::SwitchWALMultipleColumnFamilies:BeforeCatchUp"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
const std::string kCFName1 = "pikachu";
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 2;
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(kNumKeysPerMemtable));
|
||||
CreateAndReopenWithCF({kCFName1}, options);
|
||||
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
options1.max_open_files = -1;
|
||||
OpenSecondaryWithColumnFamilies({kCFName1}, options1);
|
||||
ASSERT_EQ(2, handles_secondary_.size());
|
||||
|
||||
const auto& verify_db = [](DB* db1,
|
||||
const std::vector<ColumnFamilyHandle*>& handles1,
|
||||
DB* db2,
|
||||
const std::vector<ColumnFamilyHandle*>& handles2) {
|
||||
ASSERT_NE(nullptr, db1);
|
||||
ASSERT_NE(nullptr, db2);
|
||||
ReadOptions read_opts;
|
||||
read_opts.verify_checksums = true;
|
||||
ASSERT_EQ(handles1.size(), handles2.size());
|
||||
for (size_t i = 0; i != handles1.size(); ++i) {
|
||||
std::unique_ptr<Iterator> it1(db1->NewIterator(read_opts, handles1[i]));
|
||||
std::unique_ptr<Iterator> it2(db2->NewIterator(read_opts, handles2[i]));
|
||||
it1->SeekToFirst();
|
||||
it2->SeekToFirst();
|
||||
for (; it1->Valid() && it2->Valid(); it1->Next(), it2->Next()) {
|
||||
ASSERT_EQ(it1->key(), it2->key());
|
||||
ASSERT_EQ(it1->value(), it2->value());
|
||||
}
|
||||
ASSERT_FALSE(it1->Valid());
|
||||
ASSERT_FALSE(it2->Valid());
|
||||
|
||||
for (it1->SeekToFirst(); it1->Valid(); it1->Next()) {
|
||||
std::string value;
|
||||
ASSERT_OK(db2->Get(read_opts, handles2[i], it1->key(), &value));
|
||||
ASSERT_EQ(it1->value(), value);
|
||||
}
|
||||
for (it2->SeekToFirst(); it2->Valid(); it2->Next()) {
|
||||
std::string value;
|
||||
ASSERT_OK(db1->Get(read_opts, handles1[i], it2->key(), &value));
|
||||
ASSERT_EQ(it2->value(), value);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (int k = 0; k != 8; ++k) {
|
||||
ASSERT_OK(
|
||||
Put(0 /*cf*/, "key" + std::to_string(k), "value" + std::to_string(k)));
|
||||
ASSERT_OK(
|
||||
Put(1 /*cf*/, "key" + std::to_string(k), "value" + std::to_string(k)));
|
||||
TEST_SYNC_POINT("DBSecondaryTest::SwitchWALMultipleColumnFamilies:BeforeCatchUp");
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
verify_db(dbfull(), handles_, db_secondary_, handles_secondary_);
|
||||
SyncPoint::GetInstance()->ClearTrace();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, CatchUpAfterFlush) {
|
||||
const int kNumKeysPerMemtable = 16;
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 2;
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(kNumKeysPerMemtable));
|
||||
Reopen(options);
|
||||
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
options1.max_open_files = -1;
|
||||
OpenSecondary(options1);
|
||||
|
||||
WriteOptions write_opts;
|
||||
WriteBatch wb;
|
||||
wb.Put("key0", "value0");
|
||||
wb.Put("key1", "value1");
|
||||
ASSERT_OK(dbfull()->Write(write_opts, &wb));
|
||||
ReadOptions read_opts;
|
||||
std::unique_ptr<Iterator> iter1(db_secondary_->NewIterator(read_opts));
|
||||
iter1->Seek("key0");
|
||||
ASSERT_FALSE(iter1->Valid());
|
||||
iter1->Seek("key1");
|
||||
ASSERT_FALSE(iter1->Valid());
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
iter1->Seek("key0");
|
||||
ASSERT_FALSE(iter1->Valid());
|
||||
iter1->Seek("key1");
|
||||
ASSERT_FALSE(iter1->Valid());
|
||||
std::unique_ptr<Iterator> iter2(db_secondary_->NewIterator(read_opts));
|
||||
iter2->Seek("key0");
|
||||
ASSERT_TRUE(iter2->Valid());
|
||||
ASSERT_EQ("value0", iter2->value());
|
||||
iter2->Seek("key1");
|
||||
ASSERT_TRUE(iter2->Valid());
|
||||
ASSERT_EQ("value1", iter2->value());
|
||||
|
||||
{
|
||||
WriteBatch wb1;
|
||||
wb1.Put("key0", "value01");
|
||||
wb1.Put("key1", "value11");
|
||||
ASSERT_OK(dbfull()->Write(write_opts, &wb1));
|
||||
}
|
||||
|
||||
{
|
||||
WriteBatch wb2;
|
||||
wb2.Put("key0", "new_value0");
|
||||
wb2.Delete("key1");
|
||||
ASSERT_OK(dbfull()->Write(write_opts, &wb2));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
std::unique_ptr<Iterator> iter3(db_secondary_->NewIterator(read_opts));
|
||||
// iter3 should not see value01 and value11 at all.
|
||||
iter3->Seek("key0");
|
||||
ASSERT_TRUE(iter3->Valid());
|
||||
ASSERT_EQ("new_value0", iter3->value());
|
||||
iter3->Seek("key1");
|
||||
ASSERT_FALSE(iter3->Valid());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, CheckConsistencyWhenOpen) {
|
||||
bool called = false;
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImplSecondary::CheckConsistency:AfterFirstAttempt", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
called = true;
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
ASSERT_NOK(*s);
|
||||
});
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::CheckConsistency:AfterGetLiveFilesMetaData",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
|
||||
"DBImpl::CheckConsistency:BeforeGetFileSize"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("a", "value0"));
|
||||
ASSERT_OK(Put("c", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("b", "value1"));
|
||||
ASSERT_OK(Put("d", "value1"));
|
||||
ASSERT_OK(Flush());
|
||||
port::Thread thread([this]() {
|
||||
Options opts;
|
||||
opts.env = env_;
|
||||
opts.max_open_files = -1;
|
||||
OpenSecondary(opts);
|
||||
});
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
thread.join();
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -3,20 +3,16 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include "db/db_info_dumper.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "file/filename.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/filename.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+38
-8
@@ -16,6 +16,9 @@
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/logging.h"
|
||||
#include "memory/arena.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -23,12 +26,9 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/logging.h"
|
||||
#include "trace_replay/trace_replay.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/trace_replay.h"
|
||||
#include "util/user_comparator_wrapper.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -133,6 +133,7 @@ class DBIter final: public Iterator {
|
||||
direction_(kForward),
|
||||
valid_(false),
|
||||
current_entry_is_merged_(false),
|
||||
is_key_seqnum_zero_(false),
|
||||
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),
|
||||
@@ -333,6 +334,10 @@ class DBIter final: public Iterator {
|
||||
Direction direction_;
|
||||
bool valid_;
|
||||
bool current_entry_is_merged_;
|
||||
// True if we know that the current entry's seqnum is 0.
|
||||
// This information is used as that the next entry will be for another
|
||||
// user key.
|
||||
bool is_key_seqnum_zero_;
|
||||
const bool prefix_same_as_start_;
|
||||
// Means that we will pin all data blocks we read as long the Iterator
|
||||
// is not deleted, will be true if ReadOptions::pin_data is true
|
||||
@@ -381,6 +386,7 @@ void DBIter::Next() {
|
||||
num_internal_keys_skipped_ = 0;
|
||||
bool ok = true;
|
||||
if (direction_ == kReverse) {
|
||||
is_key_seqnum_zero_ = false;
|
||||
if (!ReverseToForward()) {
|
||||
ok = false;
|
||||
}
|
||||
@@ -400,6 +406,7 @@ void DBIter::Next() {
|
||||
FindNextUserEntry(true /* skipping the current user key */,
|
||||
prefix_same_as_start_);
|
||||
} else {
|
||||
is_key_seqnum_zero_ = false;
|
||||
valid_ = false;
|
||||
}
|
||||
if (statistics_ != nullptr && valid_) {
|
||||
@@ -450,11 +457,19 @@ inline bool DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check)
|
||||
is_blob_ = false;
|
||||
|
||||
do {
|
||||
// Will update is_key_seqnum_zero_ as soon as we parsed the current key
|
||||
// but we need to save the previous value to be used in the loop.
|
||||
bool is_prev_key_seqnum_zero = is_key_seqnum_zero_;
|
||||
if (!ParseKey(&ikey_)) {
|
||||
is_key_seqnum_zero_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iterate_upper_bound_ != nullptr &&
|
||||
is_key_seqnum_zero_ = (ikey_.sequence == 0);
|
||||
|
||||
assert(iterate_upper_bound_ == nullptr || iter_.MayBeOutOfUpperBound() ||
|
||||
user_comparator_.Compare(ikey_.user_key, *iterate_upper_bound_) < 0);
|
||||
if (iterate_upper_bound_ != nullptr && iter_.MayBeOutOfUpperBound() &&
|
||||
user_comparator_.Compare(ikey_.user_key, *iterate_upper_bound_) >= 0) {
|
||||
break;
|
||||
}
|
||||
@@ -470,11 +485,18 @@ inline bool DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check)
|
||||
}
|
||||
|
||||
if (IsVisible(ikey_.sequence)) {
|
||||
if (skipping && user_comparator_.Compare(ikey_.user_key,
|
||||
saved_key_.GetUserKey()) <= 0) {
|
||||
// If the previous entry is of seqnum 0, the current entry will not
|
||||
// possibly be skipped. This condition can potentially be relaxed to
|
||||
// prev_key.seq <= ikey_.sequence. We are cautious because it will be more
|
||||
// prone to bugs causing the same user key with the same sequence number.
|
||||
if (!is_prev_key_seqnum_zero && skipping &&
|
||||
user_comparator_.Compare(ikey_.user_key, saved_key_.GetUserKey()) <=
|
||||
0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
} else {
|
||||
assert(!skipping || user_comparator_.Compare(
|
||||
ikey_.user_key, saved_key_.GetUserKey()) > 0);
|
||||
num_skipped = 0;
|
||||
switch (ikey_.type) {
|
||||
case kTypeDeletion:
|
||||
@@ -595,6 +617,7 @@ inline bool DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check)
|
||||
// If we have sequentially iterated via numerous equal keys, then it's
|
||||
// better to seek so that we can avoid too many key comparisons.
|
||||
if (num_skipped > max_skip_ && CanReseekToSkip()) {
|
||||
is_key_seqnum_zero_ = false;
|
||||
num_skipped = 0;
|
||||
std::string last_key;
|
||||
if (skipping) {
|
||||
@@ -838,7 +861,10 @@ void DBIter::PrevInternal() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (iterate_lower_bound_ != nullptr &&
|
||||
assert(iterate_lower_bound_ == nullptr || iter_.MayBeOutOfLowerBound() ||
|
||||
user_comparator_.Compare(saved_key_.GetUserKey(),
|
||||
*iterate_lower_bound_) >= 0);
|
||||
if (iterate_lower_bound_ != nullptr && iter_.MayBeOutOfLowerBound() &&
|
||||
user_comparator_.Compare(saved_key_.GetUserKey(),
|
||||
*iterate_lower_bound_) < 0) {
|
||||
// We've iterated earlier than the user-specified lower bound.
|
||||
@@ -1265,6 +1291,7 @@ void DBIter::Seek(const Slice& target) {
|
||||
status_ = Status::OK();
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
is_key_seqnum_zero_ = false;
|
||||
|
||||
SequenceNumber seq = sequence_;
|
||||
saved_key_.Clear();
|
||||
@@ -1323,6 +1350,7 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
status_ = Status::OK();
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
is_key_seqnum_zero_ = false;
|
||||
saved_key_.Clear();
|
||||
// now saved_key is used to store internal key.
|
||||
saved_key_.SetInternalKey(target, 0 /* sequence_number */,
|
||||
@@ -1390,6 +1418,7 @@ void DBIter::SeekToFirst() {
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
ClearSavedValue();
|
||||
is_key_seqnum_zero_ = false;
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
@@ -1442,6 +1471,7 @@ void DBIter::SeekToLast() {
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
ClearSavedValue();
|
||||
is_key_seqnum_zero_ = false;
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
|
||||
+30
-3
@@ -10,22 +10,47 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "memory/arena.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// This file declares the factory functions of DBIter, in its original form
|
||||
// or a wrapped form with class ArenaWrappedDBIter, which is defined here.
|
||||
// Class DBIter, which is declared and implemented inside db_iter.cc, is
|
||||
// a iterator that converts internal keys (yielded by an InternalIterator)
|
||||
// that were live at the specified sequence number into appropriate user
|
||||
// keys.
|
||||
// Each internal key is consist of a user key, a sequence number, and a value
|
||||
// type. DBIter deals with multiple key versions, tombstones, merge operands,
|
||||
// etc, and exposes an Iterator.
|
||||
// For example, DBIter may wrap following InternalIterator:
|
||||
// user key: AAA value: v3 seqno: 100 type: Put
|
||||
// user key: AAA value: v2 seqno: 97 type: Put
|
||||
// user key: AAA value: v1 seqno: 95 type: Put
|
||||
// user key: BBB value: v1 seqno: 90 type: Put
|
||||
// user key: BBC value: N/A seqno: 98 type: Delete
|
||||
// user key: BBC value: v1 seqno: 95 type: Put
|
||||
// If the snapshot passed in is 102, then the DBIter is expected to
|
||||
// expose the following iterator:
|
||||
// key: AAA value: v3
|
||||
// key: BBB value: v1
|
||||
// If the snapshot passed in is 96, then it should expose:
|
||||
// key: AAA value: v1
|
||||
// key: BBB value: v1
|
||||
// key: BBC value: v1
|
||||
//
|
||||
class Arena;
|
||||
class DBIter;
|
||||
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified "sequence" number
|
||||
// "*internal_iter") that were live at the specified `sequence` number
|
||||
// into appropriate user keys.
|
||||
extern Iterator* NewDBIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
@@ -41,6 +66,8 @@ extern Iterator* NewDBIterator(
|
||||
// a iterator hierarchy whose memory can be allocated inline. In that way,
|
||||
// accessing the iterator tree can be more cache friendly. It is also faster
|
||||
// to allocate.
|
||||
// When using the class's Iterator interface, the behavior is exactly
|
||||
// the same as the inner DBIter.
|
||||
class ArenaWrappedDBIter : public Iterator {
|
||||
public:
|
||||
virtual ~ArenaWrappedDBIter();
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
#ifdef GFLAGS
|
||||
|
||||
+2
-2
@@ -17,9 +17,9 @@
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
+398
-1
@@ -15,7 +15,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "table/flush_block_policy.h"
|
||||
#include "table/block_based/flush_block_policy.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -1049,6 +1049,148 @@ TEST_P(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
ASSERT_EQ(upper_bound_hits, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable kBinarySearchWithFirstKey, do some iterator operations and check that
|
||||
// they don't do unnecessary block reads.
|
||||
TEST_P(DBIteratorTest, IndexWithFirstKey) {
|
||||
for (int tailing = 0; tailing < 2; ++tailing) {
|
||||
SCOPED_TRACE("tailing = " + std::to_string(tailing));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor = nullptr;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
Statistics* stats = options.statistics.get();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey;
|
||||
table_options.index_shortening =
|
||||
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
|
||||
table_options.flush_block_policy_factory =
|
||||
std::make_shared<FlushBlockEveryKeyPolicyFactory>();
|
||||
table_options.block_cache = NewLRUCache(1000); // fits all blocks
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Merge("a1", "x1"));
|
||||
ASSERT_OK(Merge("b1", "y1"));
|
||||
ASSERT_OK(Merge("c0", "z1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("a2", "x2"));
|
||||
ASSERT_OK(Merge("b2", "y2"));
|
||||
ASSERT_OK(Merge("c0", "z2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("a3", "x3"));
|
||||
ASSERT_OK(Merge("b3", "y3"));
|
||||
ASSERT_OK(Merge("c3", "z3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Block cache is not important for this test.
|
||||
// We use BLOCK_CACHE_DATA_* counters just because they're the most readily
|
||||
// available way of counting block accesses.
|
||||
|
||||
ReadOptions ropt;
|
||||
ropt.tailing = tailing;
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ropt));
|
||||
|
||||
iter->Seek("b10");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b2", iter->key().ToString());
|
||||
EXPECT_EQ("y2", iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b3", iter->key().ToString());
|
||||
EXPECT_EQ("y3", iter->value().ToString());
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
iter->Seek("c0");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("c0", iter->key().ToString());
|
||||
EXPECT_EQ("z1,z2", iter->value().ToString());
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(4, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("c3", iter->key().ToString());
|
||||
EXPECT_EQ("z3", iter->value().ToString());
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter.reset();
|
||||
|
||||
// Enable iterate_upper_bound and check that iterator is not trying to read
|
||||
// blocks that are fully above upper bound.
|
||||
std::string ub = "b3";
|
||||
Slice ub_slice(ub);
|
||||
ropt.iterate_upper_bound = &ub_slice;
|
||||
iter.reset(NewIterator(ropt));
|
||||
|
||||
iter->Seek("b2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b2", iter->key().ToString());
|
||||
EXPECT_EQ("y2", iter->value().ToString());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
EXPECT_EQ(1, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
EXPECT_EQ(5, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, IndexWithFirstKeyGet) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor = nullptr;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
Statistics* stats = options.statistics.get();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey;
|
||||
table_options.index_shortening =
|
||||
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
|
||||
table_options.flush_block_policy_factory =
|
||||
std::make_shared<FlushBlockEveryKeyPolicyFactory>();
|
||||
table_options.block_cache = NewLRUCache(1000); // fits all blocks
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Merge("a", "x1"));
|
||||
ASSERT_OK(Merge("c", "y1"));
|
||||
ASSERT_OK(Merge("e", "z1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("c", "y2"));
|
||||
ASSERT_OK(Merge("e", "z2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Get() between blocks shouldn't read any blocks.
|
||||
ASSERT_EQ("NOT_FOUND", Get("b"));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
// Get() of an existing key shouldn't read any unnecessary blocks when there's
|
||||
// only one key per block.
|
||||
|
||||
ASSERT_EQ("y1,y2", Get("c"));
|
||||
EXPECT_EQ(2, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
ASSERT_EQ("x1", Get("a"));
|
||||
EXPECT_EQ(3, stats->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
EXPECT_EQ(0, stats->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
EXPECT_EQ(std::vector<std::string>({"NOT_FOUND", "z1,z2"}),
|
||||
MultiGet({"b", "e"}));
|
||||
}
|
||||
|
||||
// TODO(3.13): fix the issue of Seek() + Prev() which might not necessary
|
||||
// return the biggest key which is smaller than the seek key.
|
||||
TEST_P(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
@@ -2450,6 +2592,261 @@ TEST_P(DBIteratorTest, SeekBackwardAfterOutOfUpperBound) {
|
||||
ASSERT_EQ("a", it->key().ToString());
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, AvoidReseekLevelIterator) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 800;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::string random_str = RandomString(&rnd, 180);
|
||||
|
||||
ASSERT_OK(Put("1", random_str));
|
||||
ASSERT_OK(Put("2", random_str));
|
||||
ASSERT_OK(Put("3", random_str));
|
||||
ASSERT_OK(Put("4", random_str));
|
||||
// A new block
|
||||
ASSERT_OK(Put("5", random_str));
|
||||
ASSERT_OK(Put("6", random_str));
|
||||
ASSERT_OK(Put("7", random_str));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("8", random_str));
|
||||
ASSERT_OK(Put("9", random_str));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
int num_find_file_in_level = 0;
|
||||
int num_idx_blk_seek = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelIterator::Seek:BeforeFindFile",
|
||||
[&](void* /*arg*/) { num_find_file_in_level++; });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"IndexBlockIter::Seek:0", [&](void* /*arg*/) { num_idx_blk_seek++; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ReadOptions()));
|
||||
iter->Seek("1");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(1, num_find_file_in_level);
|
||||
ASSERT_EQ(1, num_idx_blk_seek);
|
||||
|
||||
iter->Seek("2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(1, num_find_file_in_level);
|
||||
ASSERT_EQ(1, num_idx_blk_seek);
|
||||
|
||||
iter->Seek("3");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(1, num_find_file_in_level);
|
||||
ASSERT_EQ(1, num_idx_blk_seek);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(1, num_find_file_in_level);
|
||||
ASSERT_EQ(1, num_idx_blk_seek);
|
||||
|
||||
iter->Seek("5");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(1, num_find_file_in_level);
|
||||
ASSERT_EQ(2, num_idx_blk_seek);
|
||||
|
||||
iter->Seek("6");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(1, num_find_file_in_level);
|
||||
ASSERT_EQ(2, num_idx_blk_seek);
|
||||
|
||||
iter->Seek("7");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(1, num_find_file_in_level);
|
||||
ASSERT_EQ(3, num_idx_blk_seek);
|
||||
|
||||
iter->Seek("8");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(2, num_find_file_in_level);
|
||||
// Still re-seek because "8" is the boundary key, which has
|
||||
// the same user key as the seek key.
|
||||
ASSERT_EQ(4, num_idx_blk_seek);
|
||||
|
||||
iter->Seek("5");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(3, num_find_file_in_level);
|
||||
ASSERT_EQ(5, num_idx_blk_seek);
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(3, num_find_file_in_level);
|
||||
ASSERT_EQ(5, num_idx_blk_seek);
|
||||
|
||||
// Seek backward never triggers the index block seek to be skipped
|
||||
iter->Seek("5");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(3, num_find_file_in_level);
|
||||
ASSERT_EQ(6, num_idx_blk_seek);
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, AvoidReseekChildIterator) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 800;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::string random_str = RandomString(&rnd, 180);
|
||||
|
||||
ASSERT_OK(Put("1", random_str));
|
||||
ASSERT_OK(Put("2", random_str));
|
||||
ASSERT_OK(Put("3", random_str));
|
||||
ASSERT_OK(Put("4", random_str));
|
||||
ASSERT_OK(Put("8", random_str));
|
||||
ASSERT_OK(Put("9", random_str));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("5", random_str));
|
||||
ASSERT_OK(Put("6", random_str));
|
||||
ASSERT_OK(Put("7", random_str));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// These two keys will be kept in memtable.
|
||||
ASSERT_OK(Put("0", random_str));
|
||||
ASSERT_OK(Put("8", random_str));
|
||||
|
||||
int num_iter_wrapper_seek = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"IteratorWrapper::Seek:0",
|
||||
[&](void* /*arg*/) { num_iter_wrapper_seek++; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ReadOptions()));
|
||||
iter->Seek("1");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
// DBIter always wraps internal iterator with IteratorWrapper,
|
||||
// and in merging iterator each child iterator will be wrapped
|
||||
// with IteratorWrapper.
|
||||
ASSERT_EQ(4, num_iter_wrapper_seek);
|
||||
|
||||
// child position: 1 and 5
|
||||
num_iter_wrapper_seek = 0;
|
||||
iter->Seek("2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(3, num_iter_wrapper_seek);
|
||||
|
||||
// child position: 2 and 5
|
||||
num_iter_wrapper_seek = 0;
|
||||
iter->Seek("6");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(4, num_iter_wrapper_seek);
|
||||
|
||||
// child position: 8 and 6
|
||||
num_iter_wrapper_seek = 0;
|
||||
iter->Seek("7");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(3, num_iter_wrapper_seek);
|
||||
|
||||
// child position: 8 and 7
|
||||
num_iter_wrapper_seek = 0;
|
||||
iter->Seek("5");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(4, num_iter_wrapper_seek);
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// MyRocks may change iterate bounds before seek. Simply test to make sure such
|
||||
// usage doesn't break iterator.
|
||||
TEST_P(DBIteratorTest, IterateBoundChangedBeforeSeek) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 100;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
std::string value(50, 'v');
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("aaa", value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("bbb", "v"));
|
||||
ASSERT_OK(Put("ccc", "v"));
|
||||
ASSERT_OK(Put("ddd", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("eee", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
std::string ub1 = "e";
|
||||
std::string ub2 = "c";
|
||||
Slice ub(ub1);
|
||||
ReadOptions read_opts1;
|
||||
read_opts1.iterate_upper_bound = &ub;
|
||||
Iterator* iter = NewIterator(read_opts1);
|
||||
// Seek and iterate accross block boundary.
|
||||
iter->Seek("b");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bbb", iter->key());
|
||||
ub = Slice(ub2);
|
||||
iter->Seek("b");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bbb", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
|
||||
std::string lb1 = "a";
|
||||
std::string lb2 = "c";
|
||||
Slice lb(lb1);
|
||||
ReadOptions read_opts2;
|
||||
read_opts2.iterate_lower_bound = &lb;
|
||||
iter = NewIterator(read_opts2);
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("ccc", iter->key());
|
||||
lb = Slice(lb2);
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("ccc", iter->key());
|
||||
iter->Prev();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, IterateWithLowerBoundAcrossFileBoundary) {
|
||||
ASSERT_OK(Put("aaa", "v"));
|
||||
ASSERT_OK(Put("bbb", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("ccc", "v"));
|
||||
ASSERT_OK(Put("ddd", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
// Move both files to bottom level.
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
Slice lower_bound("b");
|
||||
ReadOptions read_opts;
|
||||
read_opts.iterate_lower_bound = &lower_bound;
|
||||
std::unique_ptr<Iterator> iter(NewIterator(read_opts));
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("ccc", iter->key());
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bbb", iter->key());
|
||||
iter->Prev();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBIteratorTestInstance, DBIteratorTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
|
||||
@@ -204,6 +204,76 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
delete mem;
|
||||
}
|
||||
|
||||
// A simple test to verify that the concurrent merge writes is functional
|
||||
TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
int num_ops = 1000;
|
||||
std::string value;
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
Options options;
|
||||
// A merge operator that is not sensitive to concurrent writes since in this
|
||||
// test we don't order the writes.
|
||||
options.merge_operator = MergeOperators::CreateUInt64AddOperator();
|
||||
|
||||
// Create a MemTable
|
||||
InternalKeyComparator cmp(BytewiseComparator());
|
||||
auto factory = std::make_shared<SkipListFactory>();
|
||||
options.memtable_factory = factory;
|
||||
options.allow_concurrent_memtable_write = true;
|
||||
ImmutableCFOptions ioptions(options);
|
||||
WriteBufferManager wb(options.db_write_buffer_size);
|
||||
MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
|
||||
// Put 0 as the base
|
||||
PutFixed64(&value, static_cast<uint64_t>(0));
|
||||
bool res = mem->Add(0, kTypeValue, "key", value);
|
||||
ASSERT_TRUE(res);
|
||||
value.clear();
|
||||
|
||||
// Write Merge concurrently
|
||||
rocksdb::port::Thread write_thread1([&]() {
|
||||
MemTablePostProcessInfo post_process_info1;
|
||||
std::string v1;
|
||||
for (int seq = 1; seq < num_ops / 2; seq++) {
|
||||
PutFixed64(&v1, seq);
|
||||
bool res1 =
|
||||
mem->Add(seq, kTypeMerge, "key", v1, true, &post_process_info1);
|
||||
ASSERT_TRUE(res1);
|
||||
v1.clear();
|
||||
}
|
||||
});
|
||||
rocksdb::port::Thread write_thread2([&]() {
|
||||
MemTablePostProcessInfo post_process_info2;
|
||||
std::string v2;
|
||||
for (int seq = num_ops / 2; seq < num_ops; seq++) {
|
||||
PutFixed64(&v2, seq);
|
||||
bool res2 =
|
||||
mem->Add(seq, kTypeMerge, "key", v2, true, &post_process_info2);
|
||||
ASSERT_TRUE(res2);
|
||||
v2.clear();
|
||||
}
|
||||
});
|
||||
write_thread1.join();
|
||||
write_thread2.join();
|
||||
|
||||
Status status;
|
||||
ReadOptions roptions;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
LookupKey lkey("key", kMaxSequenceNumber);
|
||||
res = mem->Get(lkey, &value, &status, &merge_context,
|
||||
&max_covering_tombstone_seq, roptions);
|
||||
ASSERT_TRUE(res);
|
||||
uint64_t ivalue = DecodeFixed64(Slice(value).data());
|
||||
uint64_t sum = 0;
|
||||
for (int seq = 0; seq < num_ops; seq++) {
|
||||
sum += seq;
|
||||
}
|
||||
ASSERT_EQ(ivalue, sum);
|
||||
|
||||
delete mem;
|
||||
}
|
||||
|
||||
TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
Options options;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
|
||||
+52
-272
@@ -11,7 +11,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -19,14 +19,12 @@
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/stats_history.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
const int kMicrosInSec = 1000000;
|
||||
|
||||
class DBOptionsTest : public DBTestBase {
|
||||
public:
|
||||
DBOptionsTest() : DBTestBase("/db_options_test") {}
|
||||
@@ -66,10 +64,10 @@ class DBOptionsTest : public DBTestBase {
|
||||
|
||||
std::unordered_map<std::string, std::string> GetRandomizedMutableCFOptionsMap(
|
||||
Random* rnd) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
ImmutableDBOptions db_options(options);
|
||||
test::RandomInitCFOptions(&options, rnd);
|
||||
test::RandomInitCFOptions(&options, options, rnd);
|
||||
auto sanitized_options = SanitizeOptions(db_options, options);
|
||||
auto opt_map = GetMutableCFOptionsMap(sanitized_options);
|
||||
delete options.compaction_filter;
|
||||
@@ -518,114 +516,6 @@ TEST_F(DBOptionsTest, SetStatsDumpPeriodSec) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, RunStatsDumpPeriodSec) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_dump_period_sec = 5;
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
int counter = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
#if defined(OS_MACOSX) && !defined(NDEBUG)
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
|
||||
if (time_us < mock_env->RealNowMicros()) {
|
||||
*reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
|
||||
}
|
||||
});
|
||||
#endif // OS_MACOSX && !NDEBUG
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DumpStats:1", [&](void* /*arg*/) {
|
||||
counter++;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_dump_period_sec);
|
||||
dbfull()->TEST_WaitForDumpStatsRun([&] { mock_env->set_current_time(5); });
|
||||
ASSERT_GE(counter, 1);
|
||||
|
||||
// Test cacel job through SetOptions
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_dump_period_sec", "0"}}));
|
||||
int old_val = counter;
|
||||
for (int i = 6; i < 20; ++i) {
|
||||
dbfull()->TEST_WaitForDumpStatsRun([&] { mock_env->set_current_time(i); });
|
||||
}
|
||||
ASSERT_EQ(counter, old_val);
|
||||
Close();
|
||||
}
|
||||
|
||||
// Test persistent stats background thread scheduling and cancelling
|
||||
TEST_F(DBOptionsTest, StatsPersistScheduling) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_persist_period_sec = 5;
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
#if defined(OS_MACOSX) && !defined(NDEBUG)
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
|
||||
if (time_us < mock_env->RealNowMicros()) {
|
||||
*reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
|
||||
}
|
||||
});
|
||||
#endif // OS_MACOSX && !NDEBUG
|
||||
int counter = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::PersistStats:Entry", [&](void* /*arg*/) { counter++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
|
||||
ASSERT_GE(counter, 1);
|
||||
|
||||
// Test cacel job through SetOptions
|
||||
ASSERT_TRUE(dbfull()->TEST_IsPersistentStatsEnabled());
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
|
||||
ASSERT_FALSE(dbfull()->TEST_IsPersistentStatsEnabled());
|
||||
Close();
|
||||
}
|
||||
|
||||
// Test enabling persistent stats for the first time
|
||||
TEST_F(DBOptionsTest, PersistentStatsFreshInstall) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_persist_period_sec = 0;
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
#if defined(OS_MACOSX) && !defined(NDEBUG)
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
|
||||
if (time_us < mock_env->RealNowMicros()) {
|
||||
*reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
|
||||
}
|
||||
});
|
||||
#endif // OS_MACOSX && !NDEBUG
|
||||
int counter = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::PersistStats:Entry", [&](void* /*arg*/) { counter++; });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "5"}}));
|
||||
ASSERT_EQ(5, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
|
||||
ASSERT_GE(counter, 1);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsStatsPersistPeriodSec) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -640,163 +530,6 @@ TEST_F(DBOptionsTest, SetOptionsStatsPersistPeriodSec) {
|
||||
ASSERT_EQ(12345, dbfull()->GetDBOptions().stats_persist_period_sec);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, GetStatsHistory) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.stats_persist_period_sec = 5;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
#if defined(OS_MACOSX) && !defined(NDEBUG)
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
|
||||
if (time_us < mock_env->RealNowMicros()) {
|
||||
*reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
#endif // OS_MACOSX && !NDEBUG
|
||||
|
||||
CreateColumnFamilies({"pikachu"}, options);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
int mock_time = 1;
|
||||
// Wait for stats persist to finish
|
||||
dbfull()->TEST_WaitForPersistStatsRun([&] { mock_env->set_current_time(5); });
|
||||
std::unique_ptr<StatsHistoryIterator> stats_iter;
|
||||
db_->GetStatsHistory(0, 6 * kMicrosInSec, &stats_iter);
|
||||
ASSERT_TRUE(stats_iter != nullptr);
|
||||
// disabled stats snapshots
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_persist_period_sec", "0"}}));
|
||||
size_t stats_count = 0;
|
||||
for (; stats_iter->Valid(); stats_iter->Next()) {
|
||||
auto stats_map = stats_iter->GetStatsMap();
|
||||
stats_count += stats_map.size();
|
||||
}
|
||||
ASSERT_GT(stats_count, 0);
|
||||
// Wait a bit and verify no more stats are found
|
||||
for (mock_time = 6; mock_time < 20; ++mock_time) {
|
||||
dbfull()->TEST_WaitForPersistStatsRun(
|
||||
[&] { mock_env->set_current_time(mock_time); });
|
||||
}
|
||||
db_->GetStatsHistory(0, 20 * kMicrosInSec, &stats_iter);
|
||||
ASSERT_TRUE(stats_iter != nullptr);
|
||||
size_t stats_count_new = 0;
|
||||
for (; stats_iter->Valid(); stats_iter->Next()) {
|
||||
stats_count_new += stats_iter->GetStatsMap().size();
|
||||
}
|
||||
ASSERT_EQ(stats_count_new, stats_count);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, InMemoryStatsHistoryPurging) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.stats_persist_period_sec = 1;
|
||||
std::unique_ptr<rocksdb::MockTimeEnv> mock_env;
|
||||
mock_env.reset(new rocksdb::MockTimeEnv(env_));
|
||||
mock_env->set_current_time(0); // in seconds
|
||||
options.env = mock_env.get();
|
||||
#if defined(OS_MACOSX) && !defined(NDEBUG)
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
uint64_t time_us = *reinterpret_cast<uint64_t*>(arg);
|
||||
if (time_us < mock_env->RealNowMicros()) {
|
||||
*reinterpret_cast<uint64_t*>(arg) = mock_env->RealNowMicros() + 1000;
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
#endif // OS_MACOSX && !NDEBUG
|
||||
|
||||
CreateColumnFamilies({"pikachu"}, options);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
// some random operation to populate statistics
|
||||
ASSERT_OK(Delete("foo"));
|
||||
ASSERT_OK(Put("sol", "sol"));
|
||||
ASSERT_OK(Put("epic", "epic"));
|
||||
ASSERT_OK(Put("ltd", "ltd"));
|
||||
ASSERT_EQ("sol", Get("sol"));
|
||||
ASSERT_EQ("epic", Get("epic"));
|
||||
ASSERT_EQ("ltd", Get("ltd"));
|
||||
Iterator* iterator = db_->NewIterator(ReadOptions());
|
||||
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
|
||||
ASSERT_TRUE(iterator->key() == iterator->value());
|
||||
}
|
||||
delete iterator;
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Delete("sol"));
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
int mock_time = 1;
|
||||
// Wait for stats persist to finish
|
||||
for (; mock_time < 5; ++mock_time) {
|
||||
dbfull()->TEST_WaitForPersistStatsRun(
|
||||
[&] { mock_env->set_current_time(mock_time); });
|
||||
}
|
||||
|
||||
// second round of ops
|
||||
ASSERT_OK(Put("saigon", "saigon"));
|
||||
ASSERT_OK(Put("noodle talk", "noodle talk"));
|
||||
ASSERT_OK(Put("ping bistro", "ping bistro"));
|
||||
iterator = db_->NewIterator(ReadOptions());
|
||||
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
|
||||
ASSERT_TRUE(iterator->key() == iterator->value());
|
||||
}
|
||||
delete iterator;
|
||||
ASSERT_OK(Flush());
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
for (; mock_time < 10; ++mock_time) {
|
||||
dbfull()->TEST_WaitForPersistStatsRun(
|
||||
[&] { mock_env->set_current_time(mock_time); });
|
||||
}
|
||||
std::unique_ptr<StatsHistoryIterator> stats_iter;
|
||||
db_->GetStatsHistory(0, 10 * kMicrosInSec, &stats_iter);
|
||||
ASSERT_TRUE(stats_iter != nullptr);
|
||||
size_t stats_count = 0;
|
||||
int slice_count = 0;
|
||||
for (; stats_iter->Valid(); stats_iter->Next()) {
|
||||
slice_count++;
|
||||
auto stats_map = stats_iter->GetStatsMap();
|
||||
stats_count += stats_map.size();
|
||||
}
|
||||
size_t stats_history_size = dbfull()->TEST_EstiamteStatsHistorySize();
|
||||
ASSERT_GE(slice_count, 9);
|
||||
ASSERT_GE(stats_history_size, 12000);
|
||||
// capping memory cost at 12000 bytes since one slice is around 10000~12000
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"stats_history_buffer_size", "12000"}}));
|
||||
ASSERT_EQ(12000, dbfull()->GetDBOptions().stats_history_buffer_size);
|
||||
// Wait for stats persist to finish
|
||||
for (; mock_time < 20; ++mock_time) {
|
||||
dbfull()->TEST_WaitForPersistStatsRun(
|
||||
[&] { mock_env->set_current_time(mock_time); });
|
||||
}
|
||||
db_->GetStatsHistory(0, 20 * kMicrosInSec, &stats_iter);
|
||||
ASSERT_TRUE(stats_iter != nullptr);
|
||||
size_t stats_count_reopen = 0;
|
||||
slice_count = 0;
|
||||
for (; stats_iter->Valid(); stats_iter->Next()) {
|
||||
slice_count++;
|
||||
auto stats_map = stats_iter->GetStatsMap();
|
||||
stats_count_reopen += stats_map.size();
|
||||
}
|
||||
size_t stats_history_size_reopen = dbfull()->TEST_EstiamteStatsHistorySize();
|
||||
// only one slice can fit under the new stats_history_buffer_size
|
||||
ASSERT_LT(slice_count, 2);
|
||||
ASSERT_TRUE(stats_history_size_reopen < 12000 &&
|
||||
stats_history_size_reopen > 0);
|
||||
ASSERT_TRUE(stats_count_reopen < stats_count && stats_count_reopen > 0);
|
||||
Close();
|
||||
}
|
||||
|
||||
static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
dbfull->TEST_LockMutex();
|
||||
JobContext job_context(0);
|
||||
@@ -1007,6 +740,53 @@ TEST_F(DBOptionsTest, CompactionReadaheadSizeChange) {
|
||||
ASSERT_EQ(256, env_->compaction_readahead_size_);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, FIFOTtlBackwardCompatible) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.create_if_missing = true;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// In release 6.0, ttl was promoted from a secondary level option under
|
||||
// compaction_options_fifo to a top level option under ColumnFamilyOptions.
|
||||
// We still need to handle old SetOptions calls but should ignore
|
||||
// ttl under compaction_options_fifo.
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo",
|
||||
"{allow_compaction=true;max_table_files_size=1024;ttl=731;}"},
|
||||
{"ttl", "60"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
1024);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 60);
|
||||
|
||||
// Put ttl as the first option inside compaction_options_fifo. That works as
|
||||
// it doesn't overwrite any other option.
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo",
|
||||
"{ttl=985;allow_compaction=true;max_table_files_size=1024;}"},
|
||||
{"ttl", "191"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
1024);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 191);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+25
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/testutil.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -491,6 +491,30 @@ TEST_F(DBRangeDelTest, CompactionRemovesCoveredMergeOperands) {
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, PutDeleteRangeMergeFlush) {
|
||||
// Test the sequence of operations: (1) Put, (2) DeleteRange, (3) Merge, (4)
|
||||
// Flush. The `CompactionIterator` previously had a bug where we forgot to
|
||||
// check for covering range tombstones when processing the (1) Put, causing
|
||||
// it to reappear after the flush.
|
||||
Options opts = CurrentOptions();
|
||||
opts.merge_operator = MergeOperators::CreateUInt64AddOperator();
|
||||
Reopen(opts);
|
||||
|
||||
std::string val;
|
||||
PutFixed64(&val, 1);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key", val));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
"key", "key_"));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "key", val));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string expected, actual;
|
||||
ASSERT_OK(db_->Get(read_opts, "key", &actual));
|
||||
PutFixed64(&expected, 1);
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
// NumTableFilesAtLevel() is not supported in ROCKSDB_LITE
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBRangeDelTest, ObsoleteTombstoneCleanup) {
|
||||
|
||||
+108
-2
@@ -8,10 +8,10 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/sst_file_manager.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -430,6 +430,7 @@ TEST_F(DBSSTTest, RateLimitedWALDelete) {
|
||||
env_->time_elapse_only_sleep_ = true;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
options.env = env_;
|
||||
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
@@ -439,7 +440,7 @@ TEST_F(DBSSTTest, RateLimitedWALDelete) {
|
||||
ASSERT_OK(s);
|
||||
options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->SetMaxTrashDBRatio(2.1);
|
||||
sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -469,6 +470,111 @@ TEST_F(DBSSTTest, RateLimitedWALDelete) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
class DBWALTestWithParam
|
||||
: public DBSSTTest,
|
||||
public testing::WithParamInterface<std::tuple<std::string, bool>> {
|
||||
public:
|
||||
DBWALTestWithParam() {
|
||||
wal_dir_ = std::get<0>(GetParam());
|
||||
wal_dir_same_as_dbname_ = std::get<1>(GetParam());
|
||||
}
|
||||
|
||||
std::string wal_dir_;
|
||||
bool wal_dir_same_as_dbname_;
|
||||
};
|
||||
|
||||
TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
|
||||
class MyEnv : public EnvWrapper {
|
||||
public:
|
||||
MyEnv(Env* t) : EnvWrapper(t), fake_log_delete(false) {}
|
||||
|
||||
Status DeleteFile(const std::string& fname) {
|
||||
if (fname.find(".log.trash") != std::string::npos && fake_log_delete) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
void set_fake_log_delete(bool fake) { fake_log_delete = fake; }
|
||||
|
||||
private:
|
||||
bool fake_log_delete;
|
||||
};
|
||||
|
||||
std::unique_ptr<MyEnv> env(new MyEnv(Env::Default()));
|
||||
Destroy(last_options_);
|
||||
|
||||
env->set_fake_log_delete(true);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
options.env = env.get();
|
||||
options.wal_dir = dbname_ + wal_dir_;
|
||||
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
Status s;
|
||||
options.sst_file_manager.reset(
|
||||
NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
|
||||
ASSERT_OK(s);
|
||||
options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
// Create 4 files in L0
|
||||
for (char v = 'a'; v <= 'd'; v++) {
|
||||
ASSERT_OK(Put("Key2", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key3", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key4", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key1", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key4", DummyString(1024, v)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
// We created 4 sst files in L0
|
||||
ASSERT_EQ("4", FilesPerLevel(0));
|
||||
|
||||
Close();
|
||||
|
||||
options.sst_file_manager.reset();
|
||||
std::vector<std::string> filenames;
|
||||
int trash_log_count = 0;
|
||||
if (!wal_dir_same_as_dbname_) {
|
||||
// Forcibly create some trash log files
|
||||
std::unique_ptr<WritableFile> result;
|
||||
env->NewWritableFile(options.wal_dir + "/1000.log.trash", &result,
|
||||
EnvOptions());
|
||||
result.reset();
|
||||
}
|
||||
env->GetChildren(options.wal_dir, &filenames);
|
||||
for (const std::string& fname : filenames) {
|
||||
if (fname.find(".log.trash") != std::string::npos) {
|
||||
trash_log_count++;
|
||||
}
|
||||
}
|
||||
ASSERT_GE(trash_log_count, 1);
|
||||
|
||||
env->set_fake_log_delete(false);
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
filenames.clear();
|
||||
trash_log_count = 0;
|
||||
env->GetChildren(options.wal_dir, &filenames);
|
||||
for (const std::string& fname : filenames) {
|
||||
if (fname.find(".log.trash") != std::string::npos) {
|
||||
trash_log_count++;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(trash_log_count, 0);
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBWALTestWithParam, DBWALTestWithParam,
|
||||
::testing::Values(std::make_tuple("", true),
|
||||
std::make_tuple("_wal_dir", false)));
|
||||
|
||||
TEST_F(DBSSTTest, OpenDBWithExistingTrash) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
@@ -139,12 +139,12 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 4096;
|
||||
options.max_write_buffer_number = 3;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
options.level0_stop_writes_trigger = 4;
|
||||
options.level0_stop_writes_trigger = 2;
|
||||
options.target_file_size_base = 2048;
|
||||
options.max_bytes_for_level_base = 10240;
|
||||
options.max_bytes_for_level_base = 40960;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.hard_pending_compaction_bytes_limit = 16 * 1024;
|
||||
options.num_levels = 8;
|
||||
|
||||
+51
-12
@@ -24,13 +24,14 @@
|
||||
#endif
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/port.h"
|
||||
@@ -53,19 +54,18 @@
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/plain_table_factory.h"
|
||||
#include "table/plain/plain_table_factory.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/filename.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 "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -2285,6 +2285,7 @@ class MultiThreadedDBTest
|
||||
};
|
||||
|
||||
TEST_P(MultiThreadedDBTest, MultiThreaded) {
|
||||
if (option_config_ == kPipelinedWrite) return;
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
Options options = CurrentOptions(options_override);
|
||||
@@ -2491,6 +2492,16 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported("Not implemented");
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
Status VerifyChecksum() override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
@@ -4884,11 +4895,15 @@ TEST_F(DBTest, DynamicMiscOptions) {
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[0],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(CompressionType::kNoCompression, mutable_cf_options.compression);
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compression", "kSnappyCompression"}}));
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[0],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(CompressionType::kSnappyCompression,
|
||||
mutable_cf_options.compression);
|
||||
|
||||
if (Snappy_Supported()) {
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compression", "kSnappyCompression"}}));
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[0],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(CompressionType::kSnappyCompression,
|
||||
mutable_cf_options.compression);
|
||||
}
|
||||
|
||||
// Test paranoid_file_checks already done in db_block_cache_test
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions(handles_[1], {{"paranoid_file_checks", "true"}}));
|
||||
@@ -5973,6 +5988,19 @@ TEST_F(DBTest, FailWhenCompressionNotSupportedTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, CreateColumnFamilyShouldFailOnIncompatibleOptions) {
|
||||
Options options = CurrentOptions();
|
||||
options.max_open_files = 100;
|
||||
Reopen(options);
|
||||
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
// ttl is only supported when max_open_files is -1.
|
||||
cf_options.ttl = 3600;
|
||||
ColumnFamilyHandle* handle;
|
||||
ASSERT_NOK(db_->CreateColumnFamily(cf_options, "pikachu", &handle));
|
||||
delete handle;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, RowCache) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -6138,6 +6166,17 @@ TEST_F(DBTest, ThreadLocalPtrDeadlock) {
|
||||
fprintf(stderr, "Done. Flushed %d times, destroyed %d threads\n",
|
||||
flushes_done.load(), threads_destroyed.load());
|
||||
}
|
||||
|
||||
TEST_F(DBTest, LargeBlockSizeTest) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(Put(0, "foo", "bar"));
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 8LL*1024*1024*1024LL;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+53
-20
@@ -1036,8 +1036,7 @@ TEST_F(DBTest2, WalFilterTestWithColumnFamilies) {
|
||||
ASSERT_TRUE(index == keys_cf.size());
|
||||
}
|
||||
|
||||
// Temporarily disable it because the test is flaky.
|
||||
TEST_F(DBTest2, DISABLED_PresetCompressionDict) {
|
||||
TEST_F(DBTest2, PresetCompressionDict) {
|
||||
// Verifies that compression ratio improves when dictionary is enabled, and
|
||||
// improves even further when the dictionary is trained by ZSTD.
|
||||
const size_t kBlockSizeBytes = 4 << 10;
|
||||
@@ -1046,7 +1045,8 @@ TEST_F(DBTest2, DISABLED_PresetCompressionDict) {
|
||||
const int kNumL0Files = 5;
|
||||
|
||||
Options options;
|
||||
options.env = CurrentOptions().env; // Make sure to use any custom env that the test is configured with.
|
||||
// Make sure to use any custom env that the test is configured with.
|
||||
options.env = CurrentOptions().env;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.arena_block_size = kBlockSizeBytes;
|
||||
options.create_if_missing = true;
|
||||
@@ -1072,10 +1072,19 @@ TEST_F(DBTest2, DISABLED_PresetCompressionDict) {
|
||||
compression_types.push_back(kZSTD);
|
||||
}
|
||||
|
||||
enum DictionaryTypes : int {
|
||||
kWithoutDict,
|
||||
kWithDict,
|
||||
kWithZSTDTrainedDict,
|
||||
kDictEnd,
|
||||
};
|
||||
|
||||
for (auto compression_type : compression_types) {
|
||||
options.compression = compression_type;
|
||||
size_t prev_out_bytes;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
size_t bytes_without_dict = 0;
|
||||
size_t bytes_with_dict = 0;
|
||||
size_t bytes_with_zstd_trained_dict = 0;
|
||||
for (int i = kWithoutDict; i < kDictEnd; i++) {
|
||||
// First iteration: compress without preset dictionary
|
||||
// Second iteration: compress with preset dictionary
|
||||
// Third iteration (zstd only): compress with zstd-trained dictionary
|
||||
@@ -1085,19 +1094,19 @@ TEST_F(DBTest2, DISABLED_PresetCompressionDict) {
|
||||
// the non-first iterations, verify the data we get out is the same data
|
||||
// we put in.
|
||||
switch (i) {
|
||||
case 0:
|
||||
case kWithoutDict:
|
||||
options.compression_opts.max_dict_bytes = 0;
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case 1:
|
||||
options.compression_opts.max_dict_bytes = 4 * kBlockSizeBytes;
|
||||
case kWithDict:
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case 2:
|
||||
case kWithZSTDTrainedDict:
|
||||
if (compression_type != kZSTD) {
|
||||
continue;
|
||||
}
|
||||
options.compression_opts.max_dict_bytes = 4 * kBlockSizeBytes;
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = kL0FileBytes;
|
||||
break;
|
||||
default:
|
||||
@@ -1129,23 +1138,32 @@ TEST_F(DBTest2, DISABLED_PresetCompressionDict) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0, 1));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1, 1), 0);
|
||||
|
||||
size_t out_bytes = 0;
|
||||
std::vector<std::string> files;
|
||||
GetSstFiles(env_, dbname_, &files);
|
||||
for (const auto& file : files) {
|
||||
uint64_t curr_bytes;
|
||||
env_->GetFileSize(dbname_ + "/" + file, &curr_bytes);
|
||||
out_bytes += static_cast<size_t>(curr_bytes);
|
||||
// Get the live sst files size
|
||||
size_t total_sst_bytes = TotalSize(1);
|
||||
if (i == kWithoutDict) {
|
||||
bytes_without_dict = total_sst_bytes;
|
||||
} else if (i == kWithDict) {
|
||||
bytes_with_dict = total_sst_bytes;
|
||||
} else if (i == kWithZSTDTrainedDict) {
|
||||
bytes_with_zstd_trained_dict = total_sst_bytes;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < kNumL0Files * (kL0FileBytes / kBlockSizeBytes);
|
||||
j++) {
|
||||
ASSERT_EQ(seq_datas[(j / 10) % 10], Get(1, Key(static_cast<int>(j))));
|
||||
}
|
||||
if (i) {
|
||||
ASSERT_GT(prev_out_bytes, out_bytes);
|
||||
if (i == kWithDict) {
|
||||
ASSERT_GT(bytes_without_dict, bytes_with_dict);
|
||||
} else if (i == kWithZSTDTrainedDict) {
|
||||
// In zstd compression, it is sometimes possible that using a trained
|
||||
// dictionary does not get as good a compression ratio as without
|
||||
// training.
|
||||
// But using a dictionary (with or without training) should always get
|
||||
// better compression ratio than not using one.
|
||||
ASSERT_TRUE(bytes_with_dict > bytes_with_zstd_trained_dict ||
|
||||
bytes_without_dict > bytes_with_zstd_trained_dict);
|
||||
}
|
||||
prev_out_bytes = out_bytes;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
}
|
||||
}
|
||||
@@ -3738,6 +3756,21 @@ TEST_F(DBTest2, OldStatsInterface) {
|
||||
ASSERT_GT(dos->num_rt, 0);
|
||||
ASSERT_GT(dos->num_mt, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CloseWithUnreleasedSnapshot) {
|
||||
const Snapshot* ss = db_->GetSnapshot();
|
||||
|
||||
for (auto h : handles_) {
|
||||
db_->DestroyColumnFamilyHandle(h);
|
||||
}
|
||||
handles_.clear();
|
||||
|
||||
ASSERT_NOK(db_->Close());
|
||||
db_->ReleaseSnapshot(ss);
|
||||
ASSERT_OK(db_->Close());
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -341,6 +341,7 @@ Options DBTestBase::GetOptions(
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.unordered_write = false;
|
||||
break;
|
||||
case kPlainTableFirstBytePrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
@@ -373,12 +374,14 @@ Options DBTestBase::GetOptions(
|
||||
case kVectorRep:
|
||||
options.memtable_factory.reset(new VectorRepFactory(100));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.unordered_write = false;
|
||||
break;
|
||||
case kHashLinkList:
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.memtable_factory.reset(
|
||||
NewHashLinkListRepFactory(4, 0, 3, true, 4));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.unordered_write = false;
|
||||
break;
|
||||
case kDirectIO: {
|
||||
options.use_direct_reads = true;
|
||||
@@ -540,6 +543,11 @@ Options DBTestBase::GetOptions(
|
||||
options.manual_wal_flush = true;
|
||||
break;
|
||||
}
|
||||
case kUnorderedWrite: {
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.unordered_write = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
+15
-12
@@ -8,12 +8,9 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
@@ -24,9 +21,10 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -40,19 +38,18 @@
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/plain_table_factory.h"
|
||||
#include "table/plain/plain_table_factory.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/mock_time_env.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/mock_time_env.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -140,6 +137,11 @@ class SpecialMemTableRep : public MemTableRep {
|
||||
memtable_->Insert(handle);
|
||||
}
|
||||
|
||||
void InsertConcurrently(KeyHandle handle) override {
|
||||
num_entries_++;
|
||||
memtable_->Insert(handle);
|
||||
}
|
||||
|
||||
// Returns true iff an entry that compares equal to key is in the list.
|
||||
virtual bool Contains(const char* key) const override {
|
||||
return memtable_->Contains(key);
|
||||
@@ -688,6 +690,7 @@ class DBTestBase : public testing::Test {
|
||||
kPartitionedFilterWithNewTableReaderForCompactions,
|
||||
kUniversalSubcompactions,
|
||||
kxxHash64Checksum,
|
||||
kUnorderedWrite,
|
||||
// This must be the last line
|
||||
kEnd,
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+7
-4
@@ -11,8 +11,8 @@
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
class DBWALTest : public DBTestBase {
|
||||
@@ -824,7 +824,9 @@ class RecoveryTestHelper {
|
||||
// Create WAL files with values filled in
|
||||
static void FillData(DBWALTest* test, const Options& options,
|
||||
const size_t wal_count, size_t* count) {
|
||||
const ImmutableDBOptions db_options(options);
|
||||
// Calling internal functions requires sanitized options.
|
||||
Options sanitized_options = SanitizeOptions(test->dbname_, options);
|
||||
const ImmutableDBOptions db_options(sanitized_options);
|
||||
|
||||
*count = 0;
|
||||
|
||||
@@ -838,7 +840,8 @@ class RecoveryTestHelper {
|
||||
|
||||
versions.reset(new VersionSet(test->dbname_, &db_options, env_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller));
|
||||
&write_controller,
|
||||
/*block_cache_tracer=*/nullptr));
|
||||
|
||||
wal_manager.reset(new WalManager(db_options, env_options));
|
||||
|
||||
|
||||
+2
-2
@@ -12,9 +12,9 @@
|
||||
#include "db/write_thread.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+1
-5
@@ -8,11 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#include "db/dbformat.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <stdio.h>
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "port/port.h"
|
||||
|
||||
+46
-4
@@ -14,6 +14,7 @@
|
||||
#include <utility>
|
||||
#include "db/lookup_key.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -23,11 +24,16 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/user_comparator_wrapper.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// The file declares data structures and functions that deal with internal
|
||||
// keys.
|
||||
// Each internal key contains a user key, a sequence number (SequenceNumber)
|
||||
// and a type (ValueType), and they are usually encoded together.
|
||||
// There are some related helper classes here.
|
||||
|
||||
class InternalKey;
|
||||
|
||||
// Value types encoded as the last component of internal keys.
|
||||
@@ -88,6 +94,8 @@ static const SequenceNumber kMaxSequenceNumber = ((0x1ull << 56) - 1);
|
||||
|
||||
static const SequenceNumber kDisableGlobalSequenceNumber = port::kMaxUint64;
|
||||
|
||||
// The data structure that represents an internal key in the way that user_key,
|
||||
// sequence number and type are stored in separated forms.
|
||||
struct ParsedInternalKey {
|
||||
Slice user_key;
|
||||
SequenceNumber sequence;
|
||||
@@ -143,6 +151,17 @@ inline Slice ExtractUserKey(const Slice& internal_key) {
|
||||
return Slice(internal_key.data(), internal_key.size() - 8);
|
||||
}
|
||||
|
||||
inline Slice ExtractUserKeyAndStripTimestamp(const Slice& internal_key,
|
||||
size_t ts_sz) {
|
||||
assert(internal_key.size() >= 8 + ts_sz);
|
||||
return Slice(internal_key.data(), internal_key.size() - 8 - ts_sz);
|
||||
}
|
||||
|
||||
inline Slice StripTimestampFromUserKey(const Slice& user_key, size_t ts_sz) {
|
||||
assert(user_key.size() >= ts_sz);
|
||||
return Slice(user_key.data(), user_key.size() - ts_sz);
|
||||
}
|
||||
|
||||
inline uint64_t ExtractInternalKeyFooter(const Slice& internal_key) {
|
||||
assert(internal_key.size() >= 8);
|
||||
const size_t n = internal_key.size();
|
||||
@@ -192,9 +211,7 @@ class InternalKeyComparator
|
||||
}
|
||||
};
|
||||
|
||||
// Modules in this directory should keep internal keys wrapped inside
|
||||
// the following class instead of plain strings so that we do not
|
||||
// incorrectly use string comparisons instead of an InternalKeyComparator.
|
||||
// The class represent the internal key in encoded form.
|
||||
class InternalKey {
|
||||
private:
|
||||
std::string rep_;
|
||||
@@ -295,6 +312,12 @@ inline uint64_t GetInternalKeySeqno(const Slice& internal_key) {
|
||||
return num >> 8;
|
||||
}
|
||||
|
||||
// The class to store keys in an efficient way. It allows:
|
||||
// 1. Users can either copy the key into it, or have it point to an unowned
|
||||
// address.
|
||||
// 2. For copied key, a short inline buffer is kept to reduce memory
|
||||
// allocation for smaller keys.
|
||||
// 3. It tracks user key or internal key, and allow conversion between them.
|
||||
class IterKey {
|
||||
public:
|
||||
IterKey()
|
||||
@@ -506,6 +529,8 @@ class IterKey {
|
||||
void operator=(const IterKey&) = delete;
|
||||
};
|
||||
|
||||
// Convert from a SliceTranform of user keys, to a SliceTransform of
|
||||
// user keys.
|
||||
class InternalKeySliceTransform : public SliceTransform {
|
||||
public:
|
||||
explicit InternalKeySliceTransform(const SliceTransform* transform)
|
||||
@@ -631,6 +656,7 @@ inline int InternalKeyComparator::CompareKeySeq(const Slice& akey,
|
||||
return r;
|
||||
}
|
||||
|
||||
// Wrap InternalKeyComparator as a comparator class for ParsedInternalKey.
|
||||
struct ParsedInternalKeyComparator {
|
||||
explicit ParsedInternalKeyComparator(const InternalKeyComparator* c)
|
||||
: cmp(c) {}
|
||||
@@ -643,4 +669,20 @@ struct ParsedInternalKeyComparator {
|
||||
const InternalKeyComparator* cmp;
|
||||
};
|
||||
|
||||
// TODO (yanqin): this causes extra memory allocation and copy. Should be
|
||||
// addressed in the future.
|
||||
inline Status AppendTimestamp(const Slice& key, const Slice& timestamp,
|
||||
Slice* ret_key, std::string* ret_buf) {
|
||||
assert(ret_key != nullptr);
|
||||
assert(ret_buf != nullptr);
|
||||
if (key.data() + key.size() == timestamp.data()) {
|
||||
*ret_key = Slice(key.data(), key.size() + timestamp.size());
|
||||
} else {
|
||||
ret_buf->assign(key.data(), key.size());
|
||||
ret_buf->append(timestamp.data(), timestamp.size());
|
||||
*ret_key = Slice(*ret_buf);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/testharness.h"
|
||||
#include "logging/logging.h"
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+14
-5
@@ -13,17 +13,17 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "file/filename.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/filename.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -305,6 +305,7 @@ TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
|
||||
&sleeping_task_after, Env::Priority::HIGH);
|
||||
// If background purge is enabled, the file should still be there.
|
||||
CheckFileTypeCounts(dbname_, 0, bg_purge ? 1 : 0, 1);
|
||||
TEST_SYNC_POINT("DeleteFileTest::BackgroundPurgeCFDropTest:1");
|
||||
|
||||
// Execute background purges.
|
||||
sleeping_task_after.WakeUp();
|
||||
@@ -318,6 +319,13 @@ TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
|
||||
do_test(false);
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DeleteFileTest::BackgroundPurgeCFDropTest:1",
|
||||
"DBImpl::BGWorkPurge:start"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options_.avoid_unnecessary_blocking_io = true;
|
||||
ASSERT_OK(ReopenDB(false));
|
||||
{
|
||||
@@ -326,6 +334,7 @@ TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
|
||||
}
|
||||
|
||||
CloseDB();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// This test is to reproduce a bug that read invalid ReadOption in iterator
|
||||
|
||||
+2
-2
@@ -4,9 +4,9 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#include "db/error_handler.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/sst_file_manager.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#endif
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
+1
-1
@@ -10,9 +10,9 @@
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "logging/event_logger.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "util/event_logger.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "rocksdb/experimental.h"
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace experimental {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "util/testutil.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -20,6 +21,7 @@ class ExternalSSTFileBasicTest
|
||||
public:
|
||||
ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_basic_test") {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
fault_injection_test_env_.reset(new FaultInjectionTestEnv(Env::Default()));
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
@@ -140,6 +142,7 @@ class ExternalSSTFileBasicTest
|
||||
|
||||
protected:
|
||||
std::string sst_files_dir_;
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_test_env_;
|
||||
};
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
@@ -689,6 +692,59 @@ TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, SyncFailure) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = fault_injection_test_env_.get();
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> test_cases = {
|
||||
{"ExternalSstFileIngestionJob::BeforeSyncIngestedFile",
|
||||
"ExternalSstFileIngestionJob::AfterSyncIngestedFile"},
|
||||
{"ExternalSstFileIngestionJob::BeforeSyncDir",
|
||||
"ExternalSstFileIngestionJob::AfterSyncDir"},
|
||||
{"ExternalSstFileIngestionJob::BeforeSyncGlobalSeqno",
|
||||
"ExternalSstFileIngestionJob::AfterSyncGlobalSeqno"}};
|
||||
|
||||
for (size_t i = 0; i < test_cases.size(); i++) {
|
||||
SyncPoint::GetInstance()->SetCallBack(test_cases[i].first, [&](void*) {
|
||||
fault_injection_test_env_->SetFilesystemActive(false);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(test_cases[i].second, [&](void*) {
|
||||
fault_injection_test_env_->SetFilesystemActive(true);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
if (i == 2) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
}
|
||||
|
||||
Options sst_file_writer_options;
|
||||
std::unique_ptr<SstFileWriter> sst_file_writer(
|
||||
new SstFileWriter(EnvOptions(), sst_file_writer_options));
|
||||
std::string file_name =
|
||||
sst_files_dir_ + "sync_failure_test_" + ToString(i) + ".sst";
|
||||
ASSERT_OK(sst_file_writer->Open(file_name));
|
||||
ASSERT_OK(sst_file_writer->Put("bar", "v2"));
|
||||
ASSERT_OK(sst_file_writer->Finish());
|
||||
|
||||
IngestExternalFileOptions ingest_opt;
|
||||
if (i == 0) {
|
||||
ingest_opt.move_files = true;
|
||||
}
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
if (i == 2) {
|
||||
ingest_opt.write_global_seqno = true;
|
||||
}
|
||||
ASSERT_FALSE(db_->IngestExternalFile({file_name}, ingest_opt).ok());
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
Destroy(options);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
int kNumLevels = 7;
|
||||
Options options = CurrentOptions();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user