mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4be3b1c3e5 | ||
|
|
f54a9a6e7f | ||
|
|
eb84ad3151 | ||
|
|
50c552c5bc | ||
|
|
5b74443720 | ||
|
|
073cdbaa49 | ||
|
|
e52d6ad227 | ||
|
|
25ea289819 | ||
|
|
4dd267d497 | ||
|
|
32be357ef7 | ||
|
|
3eb6956728 | ||
|
|
18ba3e2edd | ||
|
|
1c91104f43 | ||
|
|
e47027d92d | ||
|
|
e5fd8bdb83 | ||
|
|
ac2a77a746 | ||
|
|
c4a37dcb44 | ||
|
|
a12818afc0 | ||
|
|
3b4ac8076b | ||
|
|
d70ce7ee0b | ||
|
|
186c7eedb0 | ||
|
|
53bb01516d | ||
|
|
69d5262c81 | ||
|
|
0a4cdde50a | ||
|
|
1aaa898cf1 | ||
|
|
9fc23c55f2 | ||
|
|
b797e42157 | ||
|
|
036d668b19 | ||
|
|
574b543f80 | ||
|
|
add8b50cc9 | ||
|
|
71d2496afc | ||
|
|
f289d9f4ac | ||
|
|
4a3e7d320c | ||
|
|
0513e21f9b | ||
|
|
8b369ae5bd | ||
|
|
ec79a7b53c | ||
|
|
b96372dead | ||
|
|
04c4ec41d1 | ||
|
|
2d75cd40d3 | ||
|
|
f25f1ec60b | ||
|
|
37d4a79e99 | ||
|
|
3b35134e4b | ||
|
|
94a0c32e73 | ||
|
|
17c1180603 | ||
|
|
07dddd5f7e | ||
|
|
5dad9d6d28 | ||
|
|
a7b13919bf | ||
|
|
616a1464ea | ||
|
|
c918c4b76a | ||
|
|
03ca2ac8a9 | ||
|
|
b0029bc7fa | ||
|
|
d438e1ec17 | ||
|
|
fba726e555 | ||
|
|
9da4d542fe | ||
|
|
f2b4939da4 | ||
|
|
973f1b78fd | ||
|
|
aebfd1703b | ||
|
|
753ff84a3d | ||
|
|
d7ea44f2f9 | ||
|
|
537da370da | ||
|
|
af6ec4d78e | ||
|
|
e29bb934f7 | ||
|
|
5ac97314e7 | ||
|
|
4e35ffdfab | ||
|
|
3c0852d1da | ||
|
|
0e8dfd6062 | ||
|
|
e840213d6e | ||
|
|
9239103cd4 | ||
|
|
da54d36a96 | ||
|
|
5cf176ca15 | ||
|
|
4a73bb0b43 | ||
|
|
c70d3c7ade | ||
|
|
602c13a964 | ||
|
|
f9d18e22d2 | ||
|
|
3ce091fd73 | ||
|
|
77b4806625 | ||
|
|
e8a096000b | ||
|
|
324a0f988e |
No files matched your search
+4
-2
@@ -20,8 +20,10 @@ addons:
|
||||
env:
|
||||
# Run all tests before db_block_cache_test (db_test, db_test2)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_END=db_block_cache_test
|
||||
# Run all tests starting from db_block_cache_test (db_block_cache_test, db_iter_test, ...)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_START=db_block_cache_test
|
||||
# Run all tests starting from db_block_cache_test (db_block_cache_test, ..., plain_table_db_test)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_START=db_block_cache_test ROCKSDBTESTS_END=comparator_db_test
|
||||
# Run all tests starting from db_block_cache_test (comparator_db_test, ...)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_START=comparator_db_test
|
||||
# Run java tests
|
||||
- JOB_NAME=java_test
|
||||
# Build ROCKSDB_LITE
|
||||
|
||||
+82
-28
@@ -1,4 +1,5 @@
|
||||
# This cmake build is for Windows 64-bit only.
|
||||
# Prerequisites for Windows:
|
||||
# This cmake build is for Windows 64-bit only.
|
||||
#
|
||||
# Prerequisites:
|
||||
# You must have at least Visual Studio 2015 Update 3. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
@@ -24,6 +25,12 @@
|
||||
# 5. And release mode (/m[:<N>] is also supported)
|
||||
# msbuild rocksdb.sln /p:Configuration=Release
|
||||
#
|
||||
# Linux:
|
||||
#
|
||||
# 1. Install a recent toolchain such as devtoolset-3 if you're on a older distro. C++11 required.
|
||||
# 2. mkdir build; cd build
|
||||
# 3. cmake ..
|
||||
# 4. make -j
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(rocksdb)
|
||||
@@ -83,6 +90,21 @@ endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
if (NOT WIN32)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION}" ROCKSDB_VERSION)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "major"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION_MAJOR
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
endif()
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
add_library(build_version OBJECT ${BUILD_VERSION_CC})
|
||||
@@ -96,7 +118,14 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -momit-leaf-frame-pointer")
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-momit-leaf-frame-pointer" HAVE_OMIT_LEAF_FRAME_POINTER)
|
||||
if(HAVE_OMIT_LEAF_FRAME_POINTER)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -momit-leaf-frame-pointer")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(FAIL_ON_WARNINGS "Treat compile warnings as errors" ON)
|
||||
@@ -229,19 +258,9 @@ endif()
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.7.0/fused-src)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
if(WIN32)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} Shlwapi.lib Rpcrt4.lib)
|
||||
else()
|
||||
set(SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_LIBS rocksdblib${ARTIFACT_SUFFIX})
|
||||
set(LIBS ${ROCKSDB_LIBS} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
add_subdirectory(third-party/gtest-1.7.0/fused-src/gtest)
|
||||
add_subdirectory(tools)
|
||||
|
||||
# Main library source code
|
||||
|
||||
@@ -319,7 +338,7 @@ set(SOURCES
|
||||
table/full_filter_block.cc
|
||||
table/get_context.cc
|
||||
table/iterator.cc
|
||||
table/merger.cc
|
||||
table/merging_iterator.cc
|
||||
table/sst_file_writer.cc
|
||||
table/meta_blocks.cc
|
||||
table/plain_table_builder.cc
|
||||
@@ -358,6 +377,7 @@ set(SOURCES
|
||||
util/histogram_windowing.cc
|
||||
util/instrumented_mutex.cc
|
||||
util/iostats_context.cc
|
||||
|
||||
util/lru_cache.cc
|
||||
tools/ldb_cmd.cc
|
||||
tools/ldb_tool.cc
|
||||
@@ -399,7 +419,6 @@ set(SOURCES
|
||||
utilities/document/json_document.cc
|
||||
utilities/document/json_document_builder.cc
|
||||
utilities/env_mirror.cc
|
||||
utilities/env_registry.cc
|
||||
utilities/geodb/geodb_impl.cc
|
||||
utilities/leveldb_options/leveldb_options.cc
|
||||
utilities/lua/rocks_lua_compaction_filter.cc
|
||||
@@ -443,6 +462,7 @@ if(WIN32)
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/win_thread.cc
|
||||
port/win/xpress_win.cc)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
@@ -451,6 +471,29 @@ else()
|
||||
util/io_posix.cc)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} Shlwapi.lib Rpcrt4.lib)
|
||||
set(ROCKSDB_STATIC_LIB rocksdblib${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_IMPORT_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(LIBS ${ROCKSDB_STATIC_LIB} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
else()
|
||||
set(SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT} rt)
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_SHARED_LIB rocksdb-shared)
|
||||
set(ROCKSDB_IMPORT_LIB ${ROCKSDB_SHARED_LIB})
|
||||
set(LIBS ${ROCKSDB_SHARED_LIB} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
set_target_properties(${ROCKSDB_SHARED_LIB} PROPERTIES
|
||||
LINKER_LANGUAGE CXX
|
||||
VERSION ${ROCKSDB_VERSION}
|
||||
SOVERSION ${ROCKSDB_VERSION_MAJOR}
|
||||
CXX_STANDARD 11
|
||||
OUTPUT_NAME "rocksdb")
|
||||
endif()
|
||||
|
||||
option(WITH_LIBRADOS "Build with librados" OFF)
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND SOURCES
|
||||
@@ -458,22 +501,22 @@ if(WITH_LIBRADOS)
|
||||
list(APPEND THIRDPARTY_LIBS rados)
|
||||
endif()
|
||||
|
||||
add_library(rocksdblib${ARTIFACT_SUFFIX} STATIC ${SOURCES})
|
||||
target_link_libraries(rocksdblib${ARTIFACT_SUFFIX}
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(rocksdblib${ARTIFACT_SUFFIX} PROPERTIES
|
||||
COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/rocksdblib${ARTIFACT_SUFFIX}.pdb")
|
||||
set_target_properties(${ROCKSDB_STATIC_LIB} PROPERTIES
|
||||
COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/${ROCKSDB_STATIC_LIB}.pdb")
|
||||
endif()
|
||||
|
||||
add_library(rocksdb${ARTIFACT_SUFFIX} SHARED ${SOURCES})
|
||||
target_link_libraries(rocksdb${ARTIFACT_SUFFIX}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(rocksdb${ARTIFACT_SUFFIX} PROPERTIES
|
||||
COMPILE_FLAGS "-DROCKSDB_DLL -DROCKSDB_LIBRARY_EXPORTS /Fd${CMAKE_CFG_INTDIR}/rocksdb${ARTIFACT_SUFFIX}.pdb")
|
||||
add_library(${ROCKSDB_IMPORT_LIB} SHARED ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_IMPORT_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
set_target_properties(${ROCKSDB_IMPORT_LIB} PROPERTIES
|
||||
COMPILE_FLAGS "-DROCKSDB_DLL -DROCKSDB_LIBRARY_EXPORTS /Fd${CMAKE_CFG_INTDIR}/${ROCKSDB_IMPORT_LIB}.pdb")
|
||||
else()
|
||||
endif()
|
||||
|
||||
option(WITH_JNI "build with JNI" OFF)
|
||||
@@ -587,7 +630,7 @@ set(TESTS
|
||||
utilities/date_tiered/date_tiered_test.cc
|
||||
utilities/document/document_db_test.cc
|
||||
utilities/document/json_document_test.cc
|
||||
utilities/env_registry_test.cc
|
||||
utilities/object_registry_test.cc
|
||||
utilities/geodb/geodb_test.cc
|
||||
utilities/memory/memory_test.cc
|
||||
utilities/merge_operators/string_append/stringappend_test.cc
|
||||
@@ -621,7 +664,7 @@ foreach(sourcefile ${BENCHMARKS})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
$<TARGET_OBJECTS:testharness>)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${LIBS} gtest)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} gtest ${LIBS})
|
||||
endforeach(sourcefile ${BENCHMARKS})
|
||||
|
||||
# For test util library that is build only in DEBUG mode
|
||||
@@ -659,7 +702,7 @@ foreach(sourcefile ${TEST_EXES})
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} ${LIBS} gtest)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} gtest ${LIBS})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
@@ -679,7 +722,18 @@ foreach(sourcefile ${C_TEST_EXES})
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} rocksdb${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX})
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${ROCKSDB_IMPORT_LIB} testutillib${ARTIFACT_SUFFIX})
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endforeach(sourcefile ${C_TEST_EXES})
|
||||
add_subdirectory(tools)
|
||||
|
||||
# Installation and packaging for Linux
|
||||
if (NOT WIN32)
|
||||
install(TARGETS ${ROCKSDB_STATIC_LIB} COMPONENT devel ARCHIVE DESTINATION lib64)
|
||||
install(TARGETS ${ROCKSDB_SHARED_LIB} COMPONENT runtime DESTINATION lib64)
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/rocksdb/"
|
||||
COMPONENT devel
|
||||
DESTINATION include/rocksdb)
|
||||
set(CMAKE_INSTALL_PREFIX /usr)
|
||||
endif()
|
||||
@@ -1,4 +1,11 @@
|
||||
# RocksDB default options change log
|
||||
## Unreleased
|
||||
* Change the default of delayed slowdown value to 16MB/s and further increase the L0 stop condition to 36 files.
|
||||
|
||||
## 5.0 (11/17/2016)
|
||||
* Options::allow_concurrent_memtable_write and Options::enable_write_thread_adaptive_yield are now true by default
|
||||
* Options.level0_stop_writes_trigger default value changes from 24 to 32.
|
||||
|
||||
## 4.8.0 (5/2/2016)
|
||||
* options.max_open_files changes from 5000 to -1. It improves performance, but users need to set file descriptor limit to be large enough and watch memory usage for index and bloom filters.
|
||||
* options.base_background_compactions changes from max_background_compactions to 1. When users set higher max_background_compactions but the write throughput is not high, the writes are less spiky to disks.
|
||||
|
||||
+17
@@ -1,6 +1,23 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
|
||||
## 5.2.0 (02/08/2017)
|
||||
### Public API Change
|
||||
* NewLRUCache() will determine number of shard bits automatically based on capacity, if the user doesn't pass one. This also impacts the default block cache when the user doesn't explict provide one.
|
||||
* Change the default of delayed slowdown value to 16MB/s and further increase the L0 stop condition to 36 files.
|
||||
* (Experimental) Two-level indexing that partition the index and creates a 2nd level index on the partitions. The feature can be enabled by setting kTwoLevelIndexSearch as IndexType and configuring index_per_partition.
|
||||
|
||||
### New Features
|
||||
* Added new overloaded function GetApproximateSizes that allows to specify if memtable stats should be computed only without computing SST files' stats approximations.
|
||||
* Added new function GetApproximateMemTableStats that approximates both number of records and size of memtables.
|
||||
|
||||
### Bug Fixes
|
||||
* RangeSync() should work if ROCKSDB_FALLOCATE_PRESENT is not set
|
||||
* Fix wrong results in a data race case in Get()
|
||||
* Some fixes related to 2PC.
|
||||
* Fix several bugs in Direct I/O supports.
|
||||
|
||||
## 5.1.0 (01/13/2017)
|
||||
* Support dynamically change `delete_obsolete_files_period_micros` option via SetDBOptions().
|
||||
* Added EventListener::OnExternalFileIngested which will be called when IngestExternalFile() add a file successfully.
|
||||
* BackupEngine::Open and BackupEngineReadOnly::Open now always return error statuses matching those of the backup Env.
|
||||
|
||||
@@ -223,11 +223,6 @@ default: all
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
|
||||
ifndef DISABLE_WARNING_AS_ERROR
|
||||
WARNING_FLAGS += -Werror
|
||||
endif
|
||||
|
||||
|
||||
ifdef LUA_PATH
|
||||
|
||||
ifndef LUA_INCLUDE
|
||||
@@ -421,6 +416,7 @@ TESTS = \
|
||||
lua_test \
|
||||
range_del_aggregator_test \
|
||||
lru_cache_test \
|
||||
object_registry_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -1085,7 +1081,7 @@ env_librados_test: utilities/env_librados_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
endif
|
||||
|
||||
env_registry_test: utilities/env_registry_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
object_registry_test: utilities/object_registry_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1390,11 +1386,11 @@ ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
endif
|
||||
|
||||
libz.a:
|
||||
-rm -rf zlib-1.2.8
|
||||
curl -O -L http://zlib.net/zlib-1.2.8.tar.gz
|
||||
tar xvzf zlib-1.2.8.tar.gz
|
||||
cd zlib-1.2.8 && CFLAGS='-fPIC' ./configure --static && make
|
||||
cp zlib-1.2.8/libz.a .
|
||||
-rm -rf zlib-1.2.10
|
||||
curl -O -L http://zlib.net/zlib-1.2.10.tar.gz
|
||||
tar xvzf zlib-1.2.10.tar.gz
|
||||
cd zlib-1.2.10 && CFLAGS='-fPIC' ./configure --static && make
|
||||
cp zlib-1.2.10/libz.a .
|
||||
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-1.0.6
|
||||
@@ -1424,7 +1420,7 @@ java_static_libobjects = $(patsubst %,jls/%,$(LIBOBJECTS))
|
||||
CLEAN_FILES += jls
|
||||
|
||||
JAVA_STATIC_FLAGS = -DZLIB -DBZIP2 -DSNAPPY -DLZ4
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-1.2.8 -I./bzip2-1.0.6 -I./snappy-1.1.3 -I./lz4-r127/lib
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-1.2.10 -I./bzip2-1.0.6 -I./snappy-1.1.3 -I./lz4-r127/lib
|
||||
|
||||
$(java_static_libobjects): jls/%.o: %.cc libz.a libbz2.a libsnappy.a liblz4.a
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES) -fPIC -c $< -o $@ $(COVERAGEFLAGS)
|
||||
|
||||
@@ -77,3 +77,6 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
|
||||
## Uber
|
||||
[Uber](http://eng.uber.com/cherami/) uses RocksDB as a durable and and scalable task queue.
|
||||
|
||||
## 360 Pika
|
||||
[360](http://www.360.cn/) [Pika](https://github.com/Qihoo360/pika) is a nosql compatible with redis. With the huge amount of data stored, redis may suffer for a capacity bottleneck, and pika was born for solving it. It has widely been widely used in many company
|
||||
+3
-3
@@ -11,7 +11,7 @@ build:
|
||||
verbosity: minimal
|
||||
test:
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -EnableRerun -Run db_test2 -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -EnableRerun -Run db_test -Exclude DBTest.GroupCommitTest -Concurrency 10
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run env_test -Concurrency 1
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run db_test2 -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run db_test -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run env_test,env_basic_test -Concurrency 2
|
||||
|
||||
@@ -9,6 +9,13 @@ require('RocksDBCommonHelper.php');
|
||||
define("DIFF_COMMAND", "diff");
|
||||
|
||||
class FacebookArcanistConfiguration extends ArcanistConfiguration {
|
||||
public function getCustomArgumentsForCommand($command) {
|
||||
if ($command == "land") {
|
||||
return array(
|
||||
'async' => array('help' => 'Just to make tools happy'));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function didRunWorkflow($command,
|
||||
ArcanistWorkflow $workflow,
|
||||
|
||||
@@ -9,6 +9,13 @@ require('RocksDBCommonHelper.php');
|
||||
define("DIFF_COMMAND", "diff");
|
||||
|
||||
class FacebookArcanistConfiguration extends ArcanistConfiguration {
|
||||
public function getCustomArgumentsForCommand($command) {
|
||||
if ($command == "land") {
|
||||
return array(
|
||||
'async' => array('help' => 'Just to make tools happy'));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function didRunWorkflow($command,
|
||||
ArcanistBaseWorkflow $workflow,
|
||||
|
||||
@@ -51,12 +51,7 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
fi
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
|
||||
+19
-18
@@ -1,18 +1,19 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.9.x/centos6-native/108cf83
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.9.x/gcc-4.9-glibc-2.20/024dbc3
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/d282e6e8f3d20f4e40a516834847bdc038e07973/2.20/gcc-4.9-glibc-2.20/500e281
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.9-glibc-2.20/e9936bf
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0882df3713c7a84f15abe368dc004581f20b39d7/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/740325875f6729f42d28deaa2147b0854f3a347e/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/9455f75ff7f4831dc9fda02a6a0f8c68922fad8f/1.0.0/gcc-5-glibc-2.23/9bc6787
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f001a51b2854957676d07306ef3abf67186b5c8b/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/fc8a13ca1fffa4d0765c716c5a0b49f0c107518f/master/gcc-5-glibc-2.23/1c32b4b
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/17c514c4d102a25ca15f4558be564eeed76f4b6a/2.0.8/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-5-glibc-2.23/b1847cb
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-4.9-glibc-2.20/e9936bf
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/7c111ff27e0c466235163f00f280a9d617c3d2ec/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b7fd454c4b10c6a81015d4524ed06cdeab558490/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.10.0/gcc-4.9-glibc-2.20/e9936bf
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/61e4abf5813bbc39bc4f548757ccfcadde175a48/5.2.3/gcc-4.9-glibc-2.20/690f0d7
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/963d9aeda70cc4779885b1277484fe7544a04e3e/9.0.0/platform007/9e92d53/
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/ab9f09bba370e7066cafd4eb59752db93f2e8312/2.29.1/platform007/15a3614
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -13,7 +13,7 @@ source "$BASEDIR/dependencies.sh"
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
@@ -43,12 +43,16 @@ if test -z $PIC_BUILD; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
@@ -56,7 +60,7 @@ if test -z $PIC_BUILD; then
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=google"
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
@@ -104,8 +108,8 @@ if [ -z "$USE_CLANG" ]; then
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
@@ -116,8 +120,8 @@ else
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.9.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.9.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
@@ -128,13 +132,14 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.9-glibc-2.20/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.9-glibc-2.20/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
@@ -144,12 +149,4 @@ EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GF
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
LUA_PATH="$LUA_BASE"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua.a"
|
||||
else
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua_pic.a"
|
||||
fi
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/local/fbcode/gcc-4.8.1-glibc-2.17-fb/bin/python2.7
|
||||
#!/usr/local/fbcode/gcc-4.9-glibc-2.20-fb/bin/python2.7
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/bin/sh
|
||||
if [ "$#" = "0" ]; then
|
||||
echo "Usage: $0 major|minor|patch"
|
||||
echo "Usage: $0 major|minor|patch|full"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$1" = "major" ]; then
|
||||
cat include/rocksdb/version.h | grep MAJOR | head -n1 | awk '{print $3}'
|
||||
fi
|
||||
@@ -12,3 +13,10 @@ fi
|
||||
if [ "$1" = "patch" ]; then
|
||||
cat include/rocksdb/version.h | grep PATCH | head -n1 | awk '{print $3}'
|
||||
fi
|
||||
if [ "$1" = "full" ]; then
|
||||
awk '/#define ROCKSDB/ { env[$2] = $3 }
|
||||
END { printf "%s.%s.%s\n", env["ROCKSDB_MAJOR"],
|
||||
env["ROCKSDB_MINOR"],
|
||||
env["ROCKSDB_PATCH"] }' \
|
||||
include/rocksdb/version.h
|
||||
fi
|
||||
@@ -271,7 +271,7 @@ TEST_F(AutoRollLoggerTest, LogFlushWhileRolling) {
|
||||
AutoRollLogger* auto_roll_logger =
|
||||
dynamic_cast<AutoRollLogger*>(logger.get());
|
||||
ASSERT_TRUE(auto_roll_logger);
|
||||
std::thread flush_thread;
|
||||
rocksdb::port::Thread flush_thread;
|
||||
|
||||
// Notes:
|
||||
// (1) Need to pin the old logger before beginning the roll, as rolling grabs
|
||||
@@ -293,7 +293,7 @@ TEST_F(AutoRollLoggerTest, LogFlushWhileRolling) {
|
||||
{"AutoRollLogger::Flush:PinnedLogger", "PosixLogger::Flush:Begin2"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
flush_thread = std::thread([&]() { auto_roll_logger->Flush(); });
|
||||
flush_thread = port::Thread ([&]() { auto_roll_logger->Flush(); });
|
||||
TEST_SYNC_POINT(
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:PreRollAndPostThreadInit");
|
||||
RollLogFileBySizeTest(auto_roll_logger, options.max_log_file_size,
|
||||
|
||||
@@ -901,6 +901,17 @@ char* rocksdb_property_value(
|
||||
}
|
||||
}
|
||||
|
||||
int rocksdb_property_int(
|
||||
rocksdb_t* db,
|
||||
const char* propname,
|
||||
uint64_t *out_val) {
|
||||
if (db->rep->GetIntProperty(Slice(propname), out_val)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
char* rocksdb_property_value_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -1336,6 +1347,15 @@ const char* rocksdb_writebatch_data(rocksdb_writebatch_t* b, size_t* size) {
|
||||
return b->rep.Data().c_str();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_set_save_point(rocksdb_writebatch_t* b) {
|
||||
b->rep.SetSavePoint();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_rollback_to_save_point(rocksdb_writebatch_t* b,
|
||||
char** errptr) {
|
||||
SaveError(errptr, b->rep.RollbackToSavePoint());
|
||||
}
|
||||
|
||||
rocksdb_block_based_table_options_t*
|
||||
rocksdb_block_based_options_create() {
|
||||
return new rocksdb_block_based_table_options_t;
|
||||
@@ -1478,6 +1498,14 @@ void rocksdb_options_set_cuckoo_table_factory(
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_set_options(
|
||||
rocksdb_t* db, int count, const char* const keys[], const char* const values[], char** errptr) {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
for (int i=0; i<count; i++)
|
||||
options_map[keys[i]] = values[i];
|
||||
SaveError(errptr,
|
||||
db->rep->SetOptions(options_map));
|
||||
}
|
||||
|
||||
rocksdb_options_t* rocksdb_options_create() {
|
||||
return new rocksdb_options_t;
|
||||
@@ -1862,6 +1890,14 @@ void rocksdb_options_set_hard_rate_limit(rocksdb_options_t* opt, double v) {
|
||||
opt->rep.hard_rate_limit = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_soft_pending_compaction_bytes_limit(rocksdb_options_t* opt, size_t v) {
|
||||
opt->rep.soft_pending_compaction_bytes_limit = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_hard_pending_compaction_bytes_limit(rocksdb_options_t* opt, size_t v) {
|
||||
opt->rep.hard_pending_compaction_bytes_limit = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_rate_limit_delay_max_milliseconds(
|
||||
rocksdb_options_t* opt, unsigned int v) {
|
||||
opt->rep.rate_limit_delay_max_milliseconds = v;
|
||||
@@ -1891,6 +1927,10 @@ void rocksdb_options_set_disable_auto_compactions(rocksdb_options_t* opt, int di
|
||||
opt->rep.disable_auto_compactions = disable;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_optimize_filters_for_hits(rocksdb_options_t* opt, int v) {
|
||||
opt->rep.optimize_filters_for_hits = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_delete_obsolete_files_period_micros(
|
||||
rocksdb_options_t* opt, uint64_t v) {
|
||||
opt->rep.delete_obsolete_files_period_micros = v;
|
||||
|
||||
+17
@@ -573,6 +573,23 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
}
|
||||
|
||||
StartPhase("writebatch_savepoint");
|
||||
{
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
rocksdb_writebatch_set_save_point(wb);
|
||||
const char* k_list[2] = {"z", "ap"};
|
||||
const size_t k_sizes[2] = {1, 2};
|
||||
const char* v_list[3] = {"x", "y", "z"};
|
||||
const size_t v_sizes[3] = {1, 1, 1};
|
||||
rocksdb_writebatch_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
|
||||
rocksdb_writebatch_rollback_to_save_point(wb, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_write(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "zap", NULL);
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
}
|
||||
|
||||
StartPhase("writebatch_rep");
|
||||
{
|
||||
rocksdb_writebatch_t* wb1 = rocksdb_writebatch_create();
|
||||
|
||||
+21
-1
@@ -370,7 +370,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
column_family_set_(column_family_set),
|
||||
pending_flush_(false),
|
||||
pending_compaction_(false),
|
||||
prev_compaction_needed_bytes_(0) {
|
||||
prev_compaction_needed_bytes_(0),
|
||||
allow_2pc_(db_options.allow_2pc) {
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
@@ -492,6 +493,25 @@ ColumnFamilyOptions ColumnFamilyData::GetLatestCFOptions() const {
|
||||
return BuildColumnFamilyOptions(initial_cf_options_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
uint64_t ColumnFamilyData::OldestLogToKeep() {
|
||||
auto current_log = GetLogNumber();
|
||||
|
||||
if (allow_2pc_) {
|
||||
auto imm_prep_log = imm()->GetMinLogContainingPrepSection();
|
||||
auto mem_prep_log = mem()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (imm_prep_log > 0 && imm_prep_log < current_log) {
|
||||
current_log = imm_prep_log;
|
||||
}
|
||||
|
||||
if (mem_prep_log > 0 && mem_prep_log < current_log) {
|
||||
current_log = mem_prep_log;
|
||||
}
|
||||
}
|
||||
|
||||
return current_log;
|
||||
}
|
||||
|
||||
const double kIncSlowdownRatio = 0.8;
|
||||
const double kDecSlowdownRatio = 1 / kIncSlowdownRatio;
|
||||
const double kNearStopSlowdownRatio = 0.6;
|
||||
|
||||
@@ -239,6 +239,9 @@ class ColumnFamilyData {
|
||||
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
void SetMemtable(MemTable* new_mem) { mem_ = new_mem; }
|
||||
|
||||
// calculate the oldest log needed for the durability of this column family
|
||||
uint64_t OldestLogToKeep();
|
||||
|
||||
// See Memtable constructor for explanation of earliest_seq param.
|
||||
MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq);
|
||||
@@ -404,6 +407,9 @@ class ColumnFamilyData {
|
||||
bool pending_compaction_;
|
||||
|
||||
uint64_t prev_compaction_needed_bytes_;
|
||||
|
||||
// if the database was opened with 2pc enabled
|
||||
bool allow_2pc_;
|
||||
};
|
||||
|
||||
// ColumnFamilySet has interesting thread-safety requirements
|
||||
|
||||
+12
-11
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -1268,7 +1269,7 @@ TEST_F(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
threads.emplace_back([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
@@ -1376,7 +1377,7 @@ TEST_F(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
WaitForFlush(2);
|
||||
AssertFilesPerLevel(ToString(i + 1), 2);
|
||||
}
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
@@ -1464,7 +1465,7 @@ TEST_F(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
@@ -1557,7 +1558,7 @@ TEST_F(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = true;
|
||||
ASSERT_OK(
|
||||
@@ -1577,7 +1578,7 @@ TEST_F(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
1);
|
||||
}
|
||||
|
||||
std::thread threads1([&] {
|
||||
rocksdb::port::Thread threads1([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
@@ -1655,7 +1656,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
@@ -1747,7 +1748,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
@@ -1858,7 +1859,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticConflict) {
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
@@ -2323,7 +2324,7 @@ TEST_F(ColumnFamilyTest, FlushAndDropRaceCondition) {
|
||||
int kKeysNum = 10000;
|
||||
PutRandomData(1, kKeysNum, 100);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
threads.emplace_back([&] { ASSERT_OK(db_->DropColumnFamily(handles_[1])); });
|
||||
|
||||
sleeping_task.WakeUp();
|
||||
@@ -2424,7 +2425,7 @@ TEST_F(ColumnFamilyTest, CreateAndDropRace) {
|
||||
|
||||
// Start a thread that will drop the first column family
|
||||
// and its comparator
|
||||
std::thread drop_cf_thread(DropSingleColumnFamily, this, 1, &comparators);
|
||||
rocksdb::port::Thread drop_cf_thread(DropSingleColumnFamily, this, 1, &comparators);
|
||||
|
||||
DropColumnFamilies({2});
|
||||
|
||||
@@ -3078,7 +3079,7 @@ TEST_F(ColumnFamilyTest, LogSyncConflictFlush) {
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::thread thread([&] { db_->SyncWAL(); });
|
||||
rocksdb::port::Thread thread([&] { db_->SyncWAL(); });
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::LogSyncConflictFlush:1");
|
||||
Flush(1);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -231,7 +232,7 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Start compacting files.
|
||||
std::thread compaction_thread(
|
||||
rocksdb::port::Thread compaction_thread(
|
||||
[&] { EXPECT_OK(db->CompactFiles(CompactionOptions(), l0_files, 1)); });
|
||||
|
||||
// In the meantime flush another file.
|
||||
@@ -253,6 +254,61 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
class FilterWithGet : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
if (db_ == nullptr) {
|
||||
return true;
|
||||
}
|
||||
std::string res;
|
||||
db_->Get(ReadOptions(), "", &res);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetDB(DB* db) {
|
||||
db_ = db;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "FilterWithGet"; }
|
||||
|
||||
private:
|
||||
DB* db_;
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<FilterWithGet> cf(new FilterWithGet());
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_filter = cf.get();
|
||||
|
||||
DB* db = nullptr;
|
||||
DestroyDB(db_name_, options);
|
||||
Status s = DB::Open(options, db_name_, &db);
|
||||
ASSERT_OK(s);
|
||||
|
||||
cf->SetDB(db);
|
||||
|
||||
// Write one L0 file
|
||||
db->Put(WriteOptions(), "K1", "V1");
|
||||
db->Flush(FlushOptions());
|
||||
|
||||
// Compact all L0 files using CompactFiles
|
||||
rocksdb::ColumnFamilyMetaData meta;
|
||||
db->GetColumnFamilyMetaData(&meta);
|
||||
for (auto& file : meta.levels[0].files) {
|
||||
std::string fname = file.db_path + "/" + file.name;
|
||||
ASSERT_OK(
|
||||
db->CompactFiles(rocksdb::CompactionOptions(), {fname}, 0));
|
||||
}
|
||||
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -76,6 +76,13 @@ class CompactedDBImpl : public DBImpl {
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
@@ -393,7 +394,7 @@ TEST_F(CompactionIteratorTest, ShuttingDownInFilter) {
|
||||
compaction_proxy_->key_not_exists_beyond_output_level = true;
|
||||
|
||||
std::atomic<bool> seek_done{false};
|
||||
std::thread compaction_thread([&] {
|
||||
rocksdb::port::Thread compaction_thread([&] {
|
||||
c_iter_->SeekToFirst();
|
||||
EXPECT_FALSE(c_iter_->Valid());
|
||||
EXPECT_TRUE(c_iter_->status().IsShutdownInProgress());
|
||||
@@ -429,7 +430,7 @@ TEST_F(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
compaction_proxy_->key_not_exists_beyond_output_level = true;
|
||||
|
||||
std::atomic<bool> seek_done{false};
|
||||
std::thread compaction_thread([&] {
|
||||
rocksdb::port::Thread compaction_thread([&] {
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
ASSERT_TRUE(c_iter_->status().IsShutdownInProgress());
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
@@ -522,7 +522,7 @@ Status CompactionJob::Run() {
|
||||
const uint64_t start_micros = env_->NowMicros();
|
||||
|
||||
// Launch a thread for each of subcompactions 1...num_threads-1
|
||||
std::vector<std::thread> thread_pool;
|
||||
std::vector<port::Thread> thread_pool;
|
||||
thread_pool.reserve(num_threads - 1);
|
||||
for (size_t i = 1; i < compact_->sub_compact_states.size(); i++) {
|
||||
thread_pool.emplace_back(&CompactionJob::ProcessKeyValueCompaction, this,
|
||||
@@ -771,9 +771,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
key, sub_compact->current_output_file_size) &&
|
||||
sub_compact->builder != nullptr) {
|
||||
CompactionIterationStats range_del_out_stats;
|
||||
status =
|
||||
FinishCompactionOutputFile(input->status(), sub_compact,
|
||||
range_del_agg.get(), &range_del_out_stats);
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact,
|
||||
range_del_agg.get(),
|
||||
&range_del_out_stats, &key);
|
||||
RecordDroppedKeys(range_del_out_stats,
|
||||
&sub_compact->compaction_job_stats);
|
||||
if (!status.ok()) {
|
||||
|
||||
@@ -41,7 +41,10 @@ class CorruptionTest : public testing::Test {
|
||||
DB* db_;
|
||||
|
||||
CorruptionTest() {
|
||||
tiny_cache_ = NewLRUCache(100);
|
||||
// If LRU cache shard bit is smaller than 2 (or -1 which will automatically
|
||||
// set it to 0), test SequenceNumberRecovery will fail, likely because of a
|
||||
// bug in recovery code. Keep it 4 for now to make the test passes.
|
||||
tiny_cache_ = NewLRUCache(100, 4);
|
||||
options_.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
options_.env = &env_;
|
||||
dbname_ = test::TmpDir() + "/corruption_test";
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/utilities/convenience.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -1101,7 +1102,7 @@ TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
|
||||
ASSERT_EQ(trivial_move, 6);
|
||||
ASSERT_EQ(non_trivial_move, 0);
|
||||
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
compact_options.change_level = false;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
std::string begin_string = Key(0);
|
||||
@@ -1233,7 +1234,7 @@ TEST_F(DBCompactionTest, ManualPartialFill) {
|
||||
ASSERT_EQ(trivial_move, 2);
|
||||
ASSERT_EQ(non_trivial_move, 0);
|
||||
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
compact_options.change_level = false;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
std::string begin_string = Key(0);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -251,7 +252,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::thread thread([this] {
|
||||
rocksdb::port::Thread thread([this] {
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_start");
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_finish");
|
||||
@@ -462,7 +463,7 @@ TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
|
||||
compaction_finished = false;
|
||||
// Issue manual compaction in one thread and still verify DB state
|
||||
// in main thread.
|
||||
std::thread t([&]() {
|
||||
rocksdb::port::Thread t([&]() {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = options.num_levels - 1;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -47,6 +48,38 @@ TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, SyncFail) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{"DBImpl::SyncClosedLogs:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
Put("key", "value");
|
||||
auto* cfd =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
->cfd();
|
||||
int refs_before = cfd->current()->TEST_refs();
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = false;
|
||||
ASSERT_OK(dbfull()->Flush(flush_options));
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:1");
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:2");
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("", FilesPerLevel()); // flush failed.
|
||||
// Flush job should release ref count to current version.
|
||||
ASSERT_EQ(refs_before, cfd->current()->TEST_refs());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+117
-64
@@ -76,7 +76,7 @@
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
@@ -268,6 +268,10 @@ static Status ValidateOptions(
|
||||
"then direct I/O writes (use_direct_writes) must be disabled. ");
|
||||
}
|
||||
|
||||
if (db_options.keep_log_file_num == 0) {
|
||||
return Status::InvalidArgument("keep_log_file_num must be greater than 0");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -342,6 +346,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
last_stats_dump_time_microsec_(0),
|
||||
next_job_id_(1),
|
||||
has_unpersisted_data_(false),
|
||||
unable_to_flush_oldest_log_(false),
|
||||
env_options_(BuildDBOptions(immutable_db_options_, mutable_db_options_)),
|
||||
num_running_ingest_file_(0),
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -663,6 +668,10 @@ void DBImpl::MaybeDumpStats() {
|
||||
}
|
||||
|
||||
uint64_t DBImpl::FindMinPrepLogReferencedByMemTable() {
|
||||
if (!allow_2pc()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t min_log = 0;
|
||||
|
||||
// we must look through the memtables for two phase transactions
|
||||
@@ -707,6 +716,11 @@ void DBImpl::MarkLogAsContainingPrepSection(uint64_t log) {
|
||||
}
|
||||
|
||||
uint64_t DBImpl::FindMinLogContainingOutstandingPrep() {
|
||||
|
||||
if (!allow_2pc()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
uint64_t min_log = 0;
|
||||
|
||||
@@ -1832,6 +1846,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
}
|
||||
|
||||
Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
mutex_.AssertHeld();
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
uint64_t current_log_number = logfile_number_;
|
||||
@@ -1868,6 +1883,7 @@ Status DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
MarkLogsSynced(current_log_number - 1, true, s);
|
||||
if (!s.ok()) {
|
||||
bg_error_ = s;
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -1918,6 +1934,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// is unlocked by the current thread.
|
||||
if (s.ok()) {
|
||||
s = flush_job.Run(&file_meta);
|
||||
} else {
|
||||
flush_job.Cancel();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -2135,7 +2153,7 @@ Status DBImpl::CompactFiles(
|
||||
immutable_db_options_.info_log.get());
|
||||
|
||||
// Perform CompactFiles
|
||||
SuperVersion* sv = GetAndRefSuperVersion(cfd);
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
@@ -2147,7 +2165,12 @@ Status DBImpl::CompactFiles(
|
||||
input_file_names, output_level,
|
||||
output_path_id, &job_context, &log_buffer);
|
||||
}
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
if (sv->Unref()) {
|
||||
mutex_.Lock();
|
||||
sv->Cleanup();
|
||||
mutex_.Unlock();
|
||||
delete sv;
|
||||
}
|
||||
|
||||
// Find and delete obsolete files
|
||||
{
|
||||
@@ -2505,7 +2528,7 @@ Status DBImpl::SetDBOptions(
|
||||
mutable_db_options_ = new_options;
|
||||
|
||||
if (total_log_size_ > GetMaxTotalWalSize()) {
|
||||
FlushColumnFamilies();
|
||||
MaybeFlushColumnFamilies();
|
||||
}
|
||||
|
||||
persist_options_status = PersistOptions();
|
||||
@@ -2752,7 +2775,7 @@ void DBImpl::MarkLogsSynced(
|
||||
++it;
|
||||
}
|
||||
}
|
||||
assert(logs_.empty() || logs_[0].number > up_to ||
|
||||
assert(!status.ok() || logs_.empty() || logs_[0].number > up_to ||
|
||||
(logs_.size() == 1 && !logs_[0].getting_synced));
|
||||
log_sync_cv_.SignalAll();
|
||||
}
|
||||
@@ -3977,15 +4000,32 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
// Acquire SuperVersion
|
||||
SuperVersion* sv = GetAndRefSuperVersion(cfd);
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:2");
|
||||
|
||||
SequenceNumber snapshot;
|
||||
if (read_options.snapshot != nullptr) {
|
||||
snapshot = reinterpret_cast<const SnapshotImpl*>(
|
||||
read_options.snapshot)->number_;
|
||||
} else {
|
||||
// Since we get and reference the super version before getting
|
||||
// the snapshot number, without a mutex protection, it is possible
|
||||
// that a memtable switch happened in the middle and not all the
|
||||
// data for this snapshot is available. But it will contain all
|
||||
// the data available in the super version we have, which is also
|
||||
// a valid snapshot to read from.
|
||||
// We shouldn't get snapshot before finding and referencing the
|
||||
// super versipon because a flush happening in between may compact
|
||||
// away data for the snapshot, but the snapshot is earlier than the
|
||||
// data overwriting it, so users may see wrong results.
|
||||
snapshot = versions_->LastSequence();
|
||||
}
|
||||
// Acquire SuperVersion
|
||||
SuperVersion* sv = GetAndRefSuperVersion(cfd);
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:3");
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:4");
|
||||
|
||||
// Prepare to store a list of merge operations if merge occurs.
|
||||
MergeContext merge_context;
|
||||
RangeDelAggregator range_del_agg(cfd->internal_comparator(), snapshot);
|
||||
@@ -4698,9 +4738,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1);
|
||||
|
||||
if (UNLIKELY(!single_column_family_mode_ &&
|
||||
!alive_log_files_.begin()->getting_flushed &&
|
||||
total_log_size_ > GetMaxTotalWalSize())) {
|
||||
FlushColumnFamilies();
|
||||
MaybeFlushColumnFamilies();
|
||||
} else if (UNLIKELY(write_buffer_manager_->ShouldFlush())) {
|
||||
// Before a new memtable is added in SwitchMemtable(),
|
||||
// write_buffer_manager_->ShouldFlush() will keep returning true. If another
|
||||
@@ -4785,6 +4824,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// and protects against concurrent loggers and concurrent writes
|
||||
// into memtables
|
||||
}
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -4872,7 +4912,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteBatchInternal::SetSequence(merged_batch, current_sequence);
|
||||
|
||||
Slice log_entry = WriteBatchInternal::Contents(merged_batch);
|
||||
status = logs_.back().writer->AddRecord(log_entry);
|
||||
status = cur_log_writer->AddRecord(log_entry);
|
||||
total_log_size_ += log_entry.size();
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
log_empty_ = false;
|
||||
@@ -5018,28 +5058,40 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
return status;
|
||||
}
|
||||
|
||||
void DBImpl::FlushColumnFamilies() {
|
||||
void DBImpl::MaybeFlushColumnFamilies() {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
WriteContext context;
|
||||
|
||||
if (alive_log_files_.begin()->getting_flushed) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t flush_column_family_if_log_file = alive_log_files_.begin()->number;
|
||||
alive_log_files_.begin()->getting_flushed = true;
|
||||
auto oldest_alive_log = alive_log_files_.begin()->number;
|
||||
auto oldest_log_with_uncommited_prep = FindMinLogContainingOutstandingPrep();
|
||||
|
||||
if (allow_2pc() &&
|
||||
unable_to_flush_oldest_log_ &&
|
||||
oldest_log_with_uncommited_prep > 0 &&
|
||||
oldest_log_with_uncommited_prep <= oldest_alive_log) {
|
||||
// we already attempted to flush all column families dependent on
|
||||
// the oldest alive log but the log still contained uncommited transactions.
|
||||
// the oldest alive log STILL contains uncommited transaction so there
|
||||
// is still nothing that we can do.
|
||||
return;
|
||||
}
|
||||
|
||||
WriteContext context;
|
||||
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"Flushing all column families with data in WAL number %" PRIu64
|
||||
". Total log size is %" PRIu64 " while max_total_wal_size is %" PRIu64,
|
||||
flush_column_family_if_log_file, total_log_size_, GetMaxTotalWalSize());
|
||||
oldest_alive_log, total_log_size_, GetMaxTotalWalSize());
|
||||
// no need to refcount because drop is happening in write thread, so can't
|
||||
// happen while we're in the write thread
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
if (cfd->GetLogNumber() <= flush_column_family_if_log_file) {
|
||||
if (cfd->OldestLogToKeep() <= oldest_alive_log) {
|
||||
auto status = SwitchMemtable(cfd, &context);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
@@ -5049,6 +5101,26 @@ void DBImpl::FlushColumnFamilies() {
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
|
||||
// we only mark this log as getting flushed if we have successfully
|
||||
// flushed all data in this log. If this log contains outstanding prepred
|
||||
// transactions then we cannot flush this log until those transactions are commited.
|
||||
|
||||
unable_to_flush_oldest_log_ = false;
|
||||
|
||||
if (allow_2pc()) {
|
||||
if (oldest_log_with_uncommited_prep == 0 ||
|
||||
oldest_log_with_uncommited_prep > oldest_alive_log) {
|
||||
// this log contains no outstanding prepared transactions
|
||||
alive_log_files_.begin()->getting_flushed = true;
|
||||
} else {
|
||||
Log(InfoLogLevel::WARN_LEVEL, immutable_db_options_.info_log,
|
||||
"Unable to release oldest log due to uncommited transaction");
|
||||
unable_to_flush_oldest_log_ = true;
|
||||
}
|
||||
} else {
|
||||
alive_log_files_.begin()->getting_flushed = true;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DBImpl::GetMaxTotalWalSize() const {
|
||||
@@ -5444,22 +5516,6 @@ SuperVersion* DBImpl::GetAndRefSuperVersion(uint32_t column_family_id) {
|
||||
return GetAndRefSuperVersion(cfd);
|
||||
}
|
||||
|
||||
// REQUIRED: mutex is NOT held
|
||||
SuperVersion* DBImpl::GetAndRefSuperVersionUnlocked(uint32_t column_family_id) {
|
||||
ColumnFamilyData* cfd;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto column_family_set = versions_->GetColumnFamilySet();
|
||||
cfd = column_family_set->GetColumnFamily(column_family_id);
|
||||
}
|
||||
|
||||
if (!cfd) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return GetAndRefSuperVersion(cfd);
|
||||
}
|
||||
|
||||
void DBImpl::ReturnAndCleanupSuperVersion(ColumnFamilyData* cfd,
|
||||
SuperVersion* sv) {
|
||||
bool unref_sv = !cfd->ReturnThreadLocalSuperVersion(sv);
|
||||
@@ -5490,22 +5546,6 @@ void DBImpl::ReturnAndCleanupSuperVersion(uint32_t column_family_id,
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
|
||||
// REQUIRED: Mutex should NOT be held.
|
||||
void DBImpl::ReturnAndCleanupSuperVersionUnlocked(uint32_t column_family_id,
|
||||
SuperVersion* sv) {
|
||||
ColumnFamilyData* cfd;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
auto column_family_set = versions_->GetColumnFamilySet();
|
||||
cfd = column_family_set->GetColumnFamily(column_family_id);
|
||||
}
|
||||
|
||||
// If SuperVersion is held, and we successfully fetched a cfd using
|
||||
// GetAndRefSuperVersion(), it must still exist.
|
||||
assert(cfd != nullptr);
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
|
||||
// REQUIRED: this function should only be called on the write thread or if the
|
||||
// mutex is held.
|
||||
ColumnFamilyHandle* DBImpl::GetColumnFamilyHandle(uint32_t column_family_id) {
|
||||
@@ -5518,23 +5558,33 @@ ColumnFamilyHandle* DBImpl::GetColumnFamilyHandle(uint32_t column_family_id) {
|
||||
return cf_memtables->GetColumnFamilyHandle();
|
||||
}
|
||||
|
||||
// REQUIRED: mutex is NOT held.
|
||||
ColumnFamilyHandle* DBImpl::GetColumnFamilyHandleUnlocked(
|
||||
uint32_t column_family_id) {
|
||||
ColumnFamilyMemTables* cf_memtables = column_family_memtables_.get();
|
||||
void DBImpl::GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) {
|
||||
ColumnFamilyHandleImpl* cfh =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
SuperVersion* sv = GetAndRefSuperVersion(cfd);
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// Convert user_key into a corresponding internal key.
|
||||
InternalKey k1(range.start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey k2(range.limit, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
MemTable::MemTableStats memStats =
|
||||
sv->mem->ApproximateStats(k1.Encode(), k2.Encode());
|
||||
MemTable::MemTableStats immStats =
|
||||
sv->imm->ApproximateStats(k1.Encode(), k2.Encode());
|
||||
*count = memStats.count + immStats.count;
|
||||
*size = memStats.size + immStats.size;
|
||||
|
||||
if (!cf_memtables->Seek(column_family_id)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return cf_memtables->GetColumnFamilyHandle();
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
|
||||
void DBImpl::GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable) {
|
||||
uint8_t include_flags) {
|
||||
assert(include_flags & DB::SizeApproximationFlags::INCLUDE_FILES ||
|
||||
include_flags & DB::SizeApproximationFlags::INCLUDE_MEMTABLES);
|
||||
Version* v;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
@@ -5545,10 +5595,13 @@ void DBImpl::GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
// Convert user_key into a corresponding internal key.
|
||||
InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
sizes[i] = versions_->ApproximateSize(v, k1.Encode(), k2.Encode());
|
||||
if (include_memtable) {
|
||||
sizes[i] += sv->mem->ApproximateSize(k1.Encode(), k2.Encode());
|
||||
sizes[i] += sv->imm->ApproximateSize(k1.Encode(), k2.Encode());
|
||||
sizes[i] = 0;
|
||||
if (include_flags & DB::SizeApproximationFlags::INCLUDE_FILES) {
|
||||
sizes[i] += versions_->ApproximateSize(v, k1.Encode(), k2.Encode());
|
||||
}
|
||||
if (include_flags & DB::SizeApproximationFlags::INCLUDE_MEMTABLES) {
|
||||
sizes[i] += sv->mem->ApproximateStats(k1.Encode(), k2.Encode()).size;
|
||||
sizes[i] += sv->imm->ApproximateStats(k1.Encode(), k2.Encode()).size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-12
@@ -138,7 +138,13 @@ class DBImpl : public DB {
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable = false) override;
|
||||
uint8_t include_flags
|
||||
= INCLUDE_FILES) override;
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override;
|
||||
using DB::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -308,6 +314,16 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool disallow_trivial_move = false);
|
||||
|
||||
void TEST_MaybeFlushColumnFamilies();
|
||||
|
||||
bool TEST_UnableToFlushOldestLog() {
|
||||
return unable_to_flush_oldest_log_;
|
||||
}
|
||||
|
||||
bool TEST_IsLogGettingFlushed() {
|
||||
return alive_log_files_.begin()->getting_flushed;
|
||||
}
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status TEST_FlushMemTable(bool wait = true,
|
||||
ColumnFamilyHandle* cfh = nullptr);
|
||||
@@ -420,9 +436,6 @@ class DBImpl : public DB {
|
||||
// mutex is held.
|
||||
SuperVersion* GetAndRefSuperVersion(uint32_t column_family_id);
|
||||
|
||||
// Same as above, should called without mutex held and not on write thread.
|
||||
SuperVersion* GetAndRefSuperVersionUnlocked(uint32_t column_family_id);
|
||||
|
||||
// Un-reference the super version and return it to thread local cache if
|
||||
// needed. If it is the last reference of the super version. Clean it up
|
||||
// after un-referencing it.
|
||||
@@ -433,18 +446,11 @@ class DBImpl : public DB {
|
||||
// REQUIRED: this function should only be called on the write thread.
|
||||
void ReturnAndCleanupSuperVersion(uint32_t colun_family_id, SuperVersion* sv);
|
||||
|
||||
// Same as above, should called without mutex held and not on write thread.
|
||||
void ReturnAndCleanupSuperVersionUnlocked(uint32_t colun_family_id,
|
||||
SuperVersion* sv);
|
||||
|
||||
// REQUIRED: this function should only be called on the write thread or if the
|
||||
// mutex is held. Return value only valid until next call to this function or
|
||||
// mutex is released.
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle(uint32_t column_family_id);
|
||||
|
||||
// Same as above, should called without mutex held and not on write thread.
|
||||
ColumnFamilyHandle* GetColumnFamilyHandleUnlocked(uint32_t column_family_id);
|
||||
|
||||
// Returns the number of currently running flushes.
|
||||
// REQUIREMENT: mutex_ must be held when calling this function.
|
||||
int num_running_flushes() {
|
||||
@@ -734,7 +740,7 @@ class DBImpl : public DB {
|
||||
// REQUIRES: mutex locked
|
||||
Status PersistOptions();
|
||||
|
||||
void FlushColumnFamilies();
|
||||
void MaybeFlushColumnFamilies();
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
@@ -994,6 +1000,15 @@ class DBImpl : public DB {
|
||||
// Used when disableWAL is true.
|
||||
bool has_unpersisted_data_;
|
||||
|
||||
|
||||
// if an attempt was made to flush all column families that
|
||||
// the oldest log depends on but uncommited data in the oldest
|
||||
// log prevents the log from being released.
|
||||
// We must attempt to free the dependent memtables again
|
||||
// at a later time after the transaction in the oldest
|
||||
// log is fully commited.
|
||||
bool unable_to_flush_oldest_log_;
|
||||
|
||||
static const int KEEP_LOG_FILE_NUM = 1000;
|
||||
// MSVC version 1800 still does not have constexpr for ::max()
|
||||
static const uint64_t kNoTimeOut = port::kMaxUint64;
|
||||
|
||||
@@ -19,6 +19,11 @@ uint64_t DBImpl::TEST_GetLevel0TotalSize() {
|
||||
return default_cf_handle_->cfd()->current()->storage_info()->NumLevelBytes(0);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_MaybeFlushColumnFamilies() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
MaybeFlushColumnFamilies();
|
||||
}
|
||||
|
||||
int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
|
||||
@@ -79,7 +79,6 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
virtual Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
@@ -92,7 +91,6 @@ class DBImplReadOnly : public DBImpl {
|
||||
bool flush_memtable = true) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
using DBImpl::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
@@ -105,6 +103,14 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
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.");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
|
||||
+6
-1
@@ -341,6 +341,7 @@ void DBIter::Next() {
|
||||
//
|
||||
// NOTE: In between, saved_key_ can point to a user key that has
|
||||
// a delete marker or a sequence number higher than sequence_
|
||||
// saved_key_ MUST have a proper user_key before calling this function
|
||||
//
|
||||
// The prefix_check parameter controls whether we check the iterated
|
||||
// keys against the prefix of the seeked key. Set to false when
|
||||
@@ -946,7 +947,6 @@ void DBIter::Seek(const Slice& target) {
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
ReleaseTempPinnedData();
|
||||
saved_key_.Clear();
|
||||
// now saved_key is used to store internal key.
|
||||
saved_key_.SetInternalKey(target, sequence_);
|
||||
|
||||
{
|
||||
@@ -961,6 +961,9 @@ void DBIter::Seek(const Slice& target) {
|
||||
}
|
||||
direction_ = kForward;
|
||||
ClearSavedValue();
|
||||
// convert the InternalKey to UserKey in saved_key_ before
|
||||
// passed to FindNextUserEntry
|
||||
saved_key_.Reserve(saved_key_.Size() - 8);
|
||||
FindNextUserEntry(false /* not skipping */, prefix_same_as_start_);
|
||||
if (!valid_) {
|
||||
prefix_start_key_.clear();
|
||||
@@ -1039,6 +1042,8 @@ void DBIter::SeekToFirst() {
|
||||
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (iter_->Valid()) {
|
||||
saved_key_.SetKey(ExtractUserKey(iter_->key()),
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
FindNextUserEntry(false /* not skipping */, false /* no prefix check */);
|
||||
if (statistics_ != nullptr) {
|
||||
if (valid_) {
|
||||
|
||||
+61
-1
@@ -16,7 +16,7 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
@@ -1897,6 +1897,66 @@ TEST_F(DBIteratorTest, DBIterator12) {
|
||||
ASSERT_FALSE(db_iter->Valid());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIterator13) {
|
||||
Options options;
|
||||
options.merge_operator = nullptr;
|
||||
|
||||
std::string key;
|
||||
key.resize(9);
|
||||
key.assign(9, static_cast<char>(0));
|
||||
key[0] = 'b';
|
||||
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddPut(key, "0");
|
||||
internal_iter->AddPut(key, "1");
|
||||
internal_iter->AddPut(key, "2");
|
||||
internal_iter->AddPut(key, "3");
|
||||
internal_iter->AddPut(key, "4");
|
||||
internal_iter->AddPut(key, "5");
|
||||
internal_iter->AddPut(key, "6");
|
||||
internal_iter->AddPut(key, "7");
|
||||
internal_iter->AddPut(key, "8");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 2, 3, 0));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), key);
|
||||
ASSERT_EQ(db_iter->value().ToString(), "2");
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIterator14) {
|
||||
Options options;
|
||||
options.merge_operator = nullptr;
|
||||
|
||||
std::string key("b");
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddPut("b", "0");
|
||||
internal_iter->AddPut("b", "1");
|
||||
internal_iter->AddPut("b", "2");
|
||||
internal_iter->AddPut("b", "3");
|
||||
internal_iter->AddPut("a", "4");
|
||||
internal_iter->AddPut("a", "5");
|
||||
internal_iter->AddPut("a", "6");
|
||||
internal_iter->AddPut("c", "7");
|
||||
internal_iter->AddPut("c", "8");
|
||||
internal_iter->AddPut("c", "9");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 4, 1, 0));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "3");
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "4");
|
||||
}
|
||||
|
||||
class DBIterWithMergeIterTest : public testing::Test {
|
||||
public:
|
||||
DBIterWithMergeIterTest()
|
||||
|
||||
@@ -1644,7 +1644,7 @@ TEST_F(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
std::atomic<uint64_t> total_prev_found(0);
|
||||
std::atomic<uint64_t> total_bytes(0);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
std::function<void()> reader_func_next = [&]() {
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
|
||||
|
||||
+207
-1
@@ -112,6 +112,81 @@ TEST_F(DBRangeDelTest, CompactionOutputFilesExactlyFilled) {
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, MaxCompactionBytesCutsOutputFiles) {
|
||||
// Ensures range deletion spanning multiple compaction output files that are
|
||||
// cut by max_compaction_bytes will have non-overlapping key-ranges.
|
||||
// https://github.com/facebook/rocksdb/issues/1778
|
||||
const int kNumFiles = 2, kNumPerFile = 1 << 8, kBytesPerVal = 1 << 12;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.level0_file_num_compaction_trigger = kNumFiles;
|
||||
opts.max_compaction_bytes = kNumPerFile * kBytesPerVal;
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
// Want max_compaction_bytes to trigger the end of compaction output file, not
|
||||
// target_file_size_base, so make the latter much bigger
|
||||
opts.target_file_size_base = 100 * opts.max_compaction_bytes;
|
||||
Reopen(opts);
|
||||
|
||||
// snapshot protects range tombstone from dropping due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// It spans the whole key-range, thus will be included in all output files
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
GetNumericStr(0),
|
||||
GetNumericStr(kNumFiles * kNumPerFile - 1)));
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
std::vector<std::string> values;
|
||||
// Write 1MB (256 values, each 4K)
|
||||
for (int j = 0; j < kNumPerFile; j++) {
|
||||
values.push_back(RandomString(&rnd, kBytesPerVal));
|
||||
ASSERT_OK(Put(GetNumericStr(kNumPerFile * i + j), values[j]));
|
||||
}
|
||||
// extra entry to trigger SpecialSkipListFactory's flush
|
||||
ASSERT_OK(Put(GetNumericStr(kNumPerFile), ""));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr,
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GE(NumTableFilesAtLevel(1), 2);
|
||||
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
|
||||
for (size_t i = 0; i < files[1].size() - 1; ++i) {
|
||||
ASSERT_TRUE(InternalKeyComparator(opts.comparator)
|
||||
.Compare(files[1][i].largest, files[1][i + 1].smallest) <
|
||||
0);
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, SentinelsOmittedFromOutputFile) {
|
||||
// Regression test for bug where sentinel range deletions (i.e., ones with
|
||||
// sequence number of zero) were included in output files.
|
||||
// snapshot protects range tombstone from dropping due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// gaps between ranges creates sentinels in our internal representation
|
||||
std::vector<std::pair<std::string, std::string>> range_dels = {{"a", "b"}, {"c", "d"}, {"e", "f"}};
|
||||
for (const auto& range_del : range_dels) {
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
range_del.first, range_del.second));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
ASSERT_GT(files[0][0].smallest_seqno, 0);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, FlushRangeDelsSameStartKey) {
|
||||
db_->Put(WriteOptions(), "b1", "val");
|
||||
ASSERT_OK(
|
||||
@@ -428,7 +503,80 @@ TEST_F(DBRangeDelTest, ObsoleteTombstoneCleanup) {
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBRangeDelTest, TableEvictedDuringScan) {
|
||||
// The RangeDelAggregator holds pointers into range deletion blocks created by
|
||||
// table readers. This test ensures the aggregator can still access those
|
||||
// blocks even if it outlives the table readers that created them.
|
||||
//
|
||||
// DBIter always keeps readers open for L0 files. So, in order to test
|
||||
// aggregator outliving reader, we need to have deletions in L1 files, which
|
||||
// are opened/closed on-demand during the scan. This is accomplished by
|
||||
// setting kNumRanges > level0_stop_writes_trigger, which prevents deletions
|
||||
// from all lingering in L0 (there is at most one range deletion per L0 file).
|
||||
//
|
||||
// The first L1 file will contain a range deletion since its begin key is 0.
|
||||
// SeekToFirst() references that table's reader and adds its range tombstone
|
||||
// to the aggregator. Upon advancing beyond that table's key-range via Next(),
|
||||
// the table reader will be unreferenced by the iterator. Since we manually
|
||||
// call Evict() on all readers before the full scan, this unreference causes
|
||||
// the reader's refcount to drop to zero and thus be destroyed.
|
||||
//
|
||||
// When it is destroyed, we do not remove its range deletions from the
|
||||
// aggregator. So, subsequent calls to Next() must be able to use these
|
||||
// deletions to decide whether a key is covered. This will work as long as
|
||||
// the aggregator properly references the range deletion block.
|
||||
const int kNum = 25, kRangeBegin = 0, kRangeEnd = 7, kNumRanges = 5;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.level0_file_num_compaction_trigger = 4;
|
||||
opts.level0_stop_writes_trigger = 4;
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(1));
|
||||
opts.num_levels = 2;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.block_cache = NewLRUCache(8 << 20);
|
||||
opts.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(opts);
|
||||
|
||||
// Hold a snapshot so range deletions can't become obsolete during compaction
|
||||
// to bottommost level (i.e., L1).
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
db_->Put(WriteOptions(), GetNumericStr(i), "val");
|
||||
if (i > 0) {
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
if (i >= kNum / 2 && i < kNum / 2 + kNumRanges) {
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
GetNumericStr(kRangeBegin), GetNumericStr(kRangeEnd));
|
||||
}
|
||||
}
|
||||
// Must be > 1 so the first L1 file can be closed before scan finishes
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 1);
|
||||
std::vector<uint64_t> file_numbers = ListTableFiles(env_, dbname_);
|
||||
|
||||
ReadOptions read_opts;
|
||||
auto* iter = db_->NewIterator(read_opts);
|
||||
int expected = kRangeEnd;
|
||||
iter->SeekToFirst();
|
||||
for (auto file_number : file_numbers) {
|
||||
// This puts table caches in the state of being externally referenced only
|
||||
// so they are destroyed immediately upon iterator unreferencing.
|
||||
TableCache::Evict(dbfull()->TEST_table_cache(), file_number);
|
||||
}
|
||||
for (; iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(GetNumericStr(expected), iter->key());
|
||||
++expected;
|
||||
// Keep clearing block cache's LRU so range deletion block can be freed as
|
||||
// soon as its refcount drops to zero.
|
||||
bbto.block_cache->EraseUnRefEntries();
|
||||
}
|
||||
ASSERT_EQ(kNum, expected);
|
||||
delete iter;
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetCoveredKeyFromMutableMemtable) {
|
||||
db_->Put(WriteOptions(), "key", "val");
|
||||
@@ -475,6 +623,36 @@ TEST_F(DBRangeDelTest, GetCoveredKeyFromSst) {
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetCoveredMergeOperandFromMemtable) {
|
||||
const int kNumMergeOps = 10;
|
||||
Options opts = CurrentOptions();
|
||||
opts.merge_operator = MergeOperators::CreateUInt64AddOperator();
|
||||
Reopen(opts);
|
||||
|
||||
for (int i = 0; i < kNumMergeOps; ++i) {
|
||||
std::string val;
|
||||
PutFixed64(&val, i);
|
||||
db_->Merge(WriteOptions(), "key", val);
|
||||
if (i == kNumMergeOps / 2) {
|
||||
// deletes [0, 5]
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "key",
|
||||
"key_");
|
||||
}
|
||||
}
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string expected, actual;
|
||||
ASSERT_OK(db_->Get(read_opts, "key", &actual));
|
||||
PutFixed64(&expected, 30); // 6+7+8+9
|
||||
ASSERT_EQ(expected, actual);
|
||||
|
||||
expected.clear();
|
||||
read_opts.ignore_range_deletions = true;
|
||||
ASSERT_OK(db_->Get(read_opts, "key", &actual));
|
||||
PutFixed64(&expected, 45); // 0+1+2+...+9
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetIgnoresRangeDeletions) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.max_write_buffer_number = 4;
|
||||
@@ -603,6 +781,34 @@ TEST_F(DBRangeDelTest, IteratorIgnoresRangeDeletions) {
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, TailingIteratorRangeTombstoneUnsupported) {
|
||||
db_->Put(WriteOptions(), "key", "val");
|
||||
// snapshot prevents key from being deleted during flush
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
|
||||
// iterations check unsupported in memtable, l0, and then l1
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
ReadOptions read_opts;
|
||||
read_opts.tailing = true;
|
||||
auto* iter = db_->NewIterator(read_opts);
|
||||
if (i == 2) {
|
||||
// For L1+, iterators over files are created on-demand, so need seek
|
||||
iter->SeekToFirst();
|
||||
}
|
||||
ASSERT_TRUE(iter->status().IsNotSupported());
|
||||
delete iter;
|
||||
if (i == 0) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
} else if (i == 1) {
|
||||
MoveFilesToLevel(1);
|
||||
}
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+1
-1
@@ -613,7 +613,7 @@ TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) {
|
||||
}
|
||||
Close();
|
||||
|
||||
// Reopening the DB will load all exisitng files
|
||||
// Reopening the DB will load all existing files
|
||||
Reopen(options);
|
||||
ASSERT_EQ("12,0,12", FilesPerLevel(0));
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
|
||||
+82
-17
@@ -31,6 +31,7 @@
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -998,7 +999,7 @@ TEST_F(DBTest, FlushSchedule) {
|
||||
options.max_write_buffer_number = 2;
|
||||
options.write_buffer_size = 120 * 1024;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
|
||||
std::atomic<int> thread_num(0);
|
||||
// each column family will have 5 thread, each thread generating 2 memtables.
|
||||
@@ -1407,17 +1408,19 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
std::string start = Key(50);
|
||||
std::string end = Key(60);
|
||||
Range r(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
uint8_t include_both = DB::SizeApproximationFlags::INCLUDE_FILES |
|
||||
DB::SizeApproximationFlags::INCLUDE_MEMTABLES;
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_GT(size, 6000);
|
||||
ASSERT_LT(size, 204800);
|
||||
// Zero if not including mem table
|
||||
db_->GetApproximateSizes(&r, 1, &size, false);
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -1427,13 +1430,13 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(100);
|
||||
end = Key(1020);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_GT(size, 6000);
|
||||
|
||||
options.max_write_buffer_number = 8;
|
||||
@@ -1456,28 +1459,28 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(100);
|
||||
end = Key(300);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_GT(size, 6000);
|
||||
|
||||
start = Key(2100);
|
||||
end = Key(2300);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
uint64_t size_with_mt, size_without_mt;
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, include_both);
|
||||
ASSERT_GT(size_with_mt, 6000);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt, false);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt);
|
||||
ASSERT_EQ(size_without_mt, 0);
|
||||
|
||||
Flush();
|
||||
@@ -1489,12 +1492,65 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt, false);
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, include_both);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt);
|
||||
ASSERT_GT(size_with_mt, size_without_mt);
|
||||
ASSERT_GT(size_without_mt, 6000);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetApproximateMemTableStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100000000;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int N = 128;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
|
||||
uint64_t count;
|
||||
uint64_t size;
|
||||
|
||||
std::string start = Key(50);
|
||||
std::string end = Key(60);
|
||||
Range r(start, end);
|
||||
db_->GetApproximateMemTableStats(r, &count, &size);
|
||||
ASSERT_GT(count, 0);
|
||||
ASSERT_LE(count, N);
|
||||
ASSERT_GT(size, 6000);
|
||||
ASSERT_LT(size, 204800);
|
||||
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateMemTableStats(r, &count, &size);
|
||||
ASSERT_EQ(count, 0);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
Flush();
|
||||
|
||||
start = Key(50);
|
||||
end = Key(60);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateMemTableStats(r, &count, &size);
|
||||
ASSERT_EQ(count, 0);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(Put(Key(1000 + i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
|
||||
start = Key(100);
|
||||
end = Key(1020);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateMemTableStats(r, &count, &size);
|
||||
ASSERT_GT(count, 20);
|
||||
ASSERT_GT(size, 6000);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ApproximateSizes) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
@@ -2813,11 +2869,20 @@ class ModelDB : public DB {
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable) override {
|
||||
uint8_t include_flags
|
||||
= INCLUDE_FILES) override {
|
||||
for (int i = 0; i < n; i++) {
|
||||
sizes[i] = 0;
|
||||
}
|
||||
}
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override {
|
||||
*count = 0;
|
||||
*size = 0;
|
||||
}
|
||||
using DB::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -3492,7 +3557,7 @@ TEST_F(DBTest, SanitizeNumThreads) {
|
||||
}
|
||||
|
||||
TEST_F(DBTest, WriteSingleThreadEntry) {
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
dbfull()->TEST_LockMutex();
|
||||
auto w = dbfull()->TEST_BeginWrite();
|
||||
threads.emplace_back([&] { Put("a", "b"); });
|
||||
@@ -5370,7 +5435,7 @@ TEST_F(DBTest, FlushesInParallelWithCompactRange) {
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
threads.emplace_back([&]() { Compact("a", "z"); });
|
||||
|
||||
TEST_SYNC_POINT("DBTest::FlushesInParallelWithCompactRange:1");
|
||||
@@ -5776,7 +5841,7 @@ TEST_F(DBTest, PauseBackgroundWorkTest) {
|
||||
options.write_buffer_size = 100000; // Small write buffer
|
||||
Reopen(options);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
std::atomic<bool> done(false);
|
||||
db_->PauseBackgroundWork();
|
||||
threads.emplace_back([&]() {
|
||||
|
||||
+86
-6
@@ -11,6 +11,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/persistent_cache.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
@@ -1616,8 +1617,8 @@ TEST_F(DBTest2, SyncPointMarker) {
|
||||
CountSyncPoint();
|
||||
};
|
||||
|
||||
auto thread1 = std::thread(func1);
|
||||
auto thread2 = std::thread(func2);
|
||||
auto thread1 = port::Thread(func1);
|
||||
auto thread2 = port::Thread(func2);
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
|
||||
@@ -1906,8 +1907,8 @@ TEST_P(MergeOperatorPinningTest, TailingIterator) {
|
||||
delete iter;
|
||||
};
|
||||
|
||||
std::thread writer_thread(writer_func);
|
||||
std::thread reader_thread(reader_func);
|
||||
rocksdb::port::Thread writer_thread(writer_func);
|
||||
rocksdb::port::Thread reader_thread(reader_func);
|
||||
|
||||
writer_thread.join();
|
||||
reader_thread.join();
|
||||
@@ -2178,7 +2179,7 @@ TEST_F(DBTest2, ManualCompactionOverlapManualCompaction) {
|
||||
cro.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(db_->CompactRange(cro, &k1s, &k2s));
|
||||
};
|
||||
std::thread bg_thread;
|
||||
rocksdb::port::Thread bg_thread;
|
||||
|
||||
// While the compaction is running, we will create 2 new files that
|
||||
// can fit in L1, these 2 files will be moved to L1 and overlap with
|
||||
@@ -2199,7 +2200,7 @@ TEST_F(DBTest2, ManualCompactionOverlapManualCompaction) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Start a non-exclusive manual compaction in a bg thread
|
||||
bg_thread = std::thread(bg_manual_compact);
|
||||
bg_thread = port::Thread(bg_manual_compact);
|
||||
// This manual compaction conflict with the other manual compaction
|
||||
// so it should wait until the first compaction finish
|
||||
env_->SleepForMicroseconds(1000000);
|
||||
@@ -2216,8 +2217,87 @@ TEST_F(DBTest2, ManualCompactionOverlapManualCompaction) {
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, OptimizeForPointLookup) {
|
||||
Options options = CurrentOptions();
|
||||
Close();
|
||||
options.OptimizeForPointLookup(2);
|
||||
ASSERT_OK(DB::Open(options, dbname_, &db_));
|
||||
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_EQ("v1", Get("foo"));
|
||||
Flush();
|
||||
ASSERT_EQ("v1", Get("foo"));
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, GetRaceFlush1) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::GetImpl:1", "DBTest2::GetRaceFlush:1"},
|
||||
{"DBTest2::GetRaceFlush:2", "DBImpl::GetImpl:2"}});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
rocksdb::port::Thread t1([&] {
|
||||
TEST_SYNC_POINT("DBTest2::GetRaceFlush:1");
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
Flush();
|
||||
TEST_SYNC_POINT("DBTest2::GetRaceFlush:2");
|
||||
});
|
||||
|
||||
// Get() is issued after the first Put(), so it should see either
|
||||
// "v1" or "v2".
|
||||
ASSERT_NE("NOT_FOUND", Get("foo"));
|
||||
t1.join();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, GetRaceFlush2) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::GetImpl:3", "DBTest2::GetRaceFlush:1"},
|
||||
{"DBTest2::GetRaceFlush:2", "DBImpl::GetImpl:4"}});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
port::Thread t1([&] {
|
||||
TEST_SYNC_POINT("DBTest2::GetRaceFlush:1");
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
Flush();
|
||||
TEST_SYNC_POINT("DBTest2::GetRaceFlush:2");
|
||||
});
|
||||
|
||||
// Get() is issued after the first Put(), so it should see either
|
||||
// "v1" or "v2".
|
||||
ASSERT_NE("NOT_FOUND", Get("foo"));
|
||||
t1.join();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, DirectIO) {
|
||||
if (!IsDirectIOSupported()) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.use_direct_reads = options.use_direct_writes = true;
|
||||
options.allow_mmap_reads = options.allow_mmap_writes = false;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "a"));
|
||||
ASSERT_OK(Put(Key(5), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(10), "a"));
|
||||
ASSERT_OK(Put(Key(15), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
Reopen(options);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+11
-1
@@ -72,7 +72,12 @@ DBTestBase::~DBTestBase() {
|
||||
options.db_paths.emplace_back(dbname_ + "_2", 0);
|
||||
options.db_paths.emplace_back(dbname_ + "_3", 0);
|
||||
options.db_paths.emplace_back(dbname_ + "_4", 0);
|
||||
EXPECT_OK(DestroyDB(dbname_, options));
|
||||
|
||||
if (getenv("KEEP_DB")) {
|
||||
printf("DB is still at %s\n", dbname_.c_str());
|
||||
} else {
|
||||
EXPECT_OK(DestroyDB(dbname_, options));
|
||||
}
|
||||
delete env_;
|
||||
}
|
||||
|
||||
@@ -348,6 +353,11 @@ Options DBTestBase::CurrentOptions(
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithPartitionedIndex: {
|
||||
table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithIndexRestartInterval: {
|
||||
table_options.index_block_restart_interval = 8;
|
||||
break;
|
||||
|
||||
+26
-1
@@ -231,6 +231,20 @@ class SpecialEnv : public EnvWrapper {
|
||||
return base_->Append(data);
|
||||
}
|
||||
}
|
||||
Status PositionedAppend(const Slice& data, uint64_t offset) override {
|
||||
if (env_->table_write_callback_) {
|
||||
(*env_->table_write_callback_)();
|
||||
}
|
||||
if (env_->drop_writes_.load(std::memory_order_acquire)) {
|
||||
// Drop writes on the floor
|
||||
return Status::OK();
|
||||
} else if (env_->no_space_.load(std::memory_order_acquire)) {
|
||||
return Status::NoSpace("No space left on device");
|
||||
} else {
|
||||
env_->bytes_written_ += data.size();
|
||||
return base_->PositionedAppend(data, offset);
|
||||
}
|
||||
}
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status Close() override {
|
||||
// SyncPoint is not supported in Released Windows Mode.
|
||||
@@ -257,6 +271,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
Env::IOPriority GetIOPriority() override {
|
||||
return base_->GetIOPriority();
|
||||
}
|
||||
bool use_direct_io() const override {
|
||||
return base_->use_direct_io();
|
||||
}
|
||||
};
|
||||
class ManifestFile : public WritableFile {
|
||||
public:
|
||||
@@ -358,7 +375,14 @@ class SpecialEnv : public EnvWrapper {
|
||||
return Status::IOError("simulated write error");
|
||||
}
|
||||
|
||||
Status s = target()->NewWritableFile(f, r, soptions);
|
||||
EnvOptions optimized = soptions;
|
||||
if (strstr(f.c_str(), "MANIFEST") != nullptr ||
|
||||
strstr(f.c_str(), "log") != nullptr) {
|
||||
optimized.use_mmap_writes = false;
|
||||
optimized.use_direct_writes = false;
|
||||
}
|
||||
|
||||
Status s = target()->NewWritableFile(f, r, optimized);
|
||||
if (s.ok()) {
|
||||
if (strstr(f.c_str(), ".sst") != nullptr) {
|
||||
r->reset(new SSTableFile(this, std::move(*r)));
|
||||
@@ -594,6 +618,7 @@ class DBTestBase : public testing::Test {
|
||||
kLevelSubcompactions = 31,
|
||||
kUniversalSubcompactions = 32,
|
||||
kBlockBasedTableWithIndexRestartInterval = 33,
|
||||
kBlockBasedTableWithPartitionedIndex = 34,
|
||||
};
|
||||
int option_config_;
|
||||
|
||||
|
||||
+3
-2
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/options_helper.h"
|
||||
@@ -86,7 +87,7 @@ TEST_F(DBWALTest, SyncWALNotBlockWrite) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::thread thread([&]() { ASSERT_OK(db_->SyncWAL()); });
|
||||
rocksdb::port::Thread thread([&]() { ASSERT_OK(db_->SyncWAL()); });
|
||||
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotBlockWrite:1");
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
@@ -118,7 +119,7 @@ TEST_F(DBWALTest, SyncWALNotWaitWrite) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::thread thread([&]() { ASSERT_OK(Put("foo2", "bar2")); });
|
||||
rocksdb::port::Thread thread([&]() { ASSERT_OK(Put("foo2", "bar2")); });
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/version_edit.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "table/sst_file_writer_collectors.h"
|
||||
#include "table/table_builder.h"
|
||||
@@ -306,8 +306,16 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
if (file_to_ingest->global_seqno_offset == 0) {
|
||||
return Status::Corruption("Was not able to find file global seqno field");
|
||||
}
|
||||
} else if (file_to_ingest->version == 1) {
|
||||
// SST file V1 should not have global seqno field
|
||||
assert(seqno_iter == uprops.end());
|
||||
if (ingestion_options_.allow_blocking_flush ||
|
||||
ingestion_options_.allow_global_seqno) {
|
||||
return Status::InvalidArgument(
|
||||
"External SST file V1 does not support global seqno");
|
||||
}
|
||||
} else {
|
||||
return Status::InvalidArgument("external file version is not supported");
|
||||
return Status::InvalidArgument("External file version is not supported");
|
||||
}
|
||||
// Get number of entries in table
|
||||
file_to_ingest->num_entries = props->num_entries;
|
||||
|
||||
@@ -258,11 +258,11 @@ TEST_F(ExternalSSTFileTest, Basic) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
|
||||
// This file has overlapping values with the exisitng data
|
||||
// This file has overlapping values with the existing data
|
||||
s = DeprecatedAddFile({file3});
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
// This file has overlapping values with the exisitng data
|
||||
// This file has overlapping values with the existing data
|
||||
s = DeprecatedAddFile({file4});
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
@@ -510,7 +510,7 @@ TEST_F(ExternalSSTFileTest, AddList) {
|
||||
ASSERT_EQ(user_props["xyz_Count"], "100");
|
||||
}
|
||||
|
||||
// This file list has overlapping values with the exisitng data
|
||||
// This file list has overlapping values with the existing data
|
||||
s = DeprecatedAddFile(file_list3);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
@@ -705,7 +705,7 @@ TEST_F(ExternalSSTFileTest, NoCopy) {
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file2));
|
||||
|
||||
// This file have overlapping values with the exisitng data
|
||||
// This file have overlapping values with the existing data
|
||||
s = DeprecatedAddFile({file2}, true /* move file */);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file3));
|
||||
@@ -821,7 +821,7 @@ TEST_F(ExternalSSTFileTest, MultiThreaded) {
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
};
|
||||
// Write num_files files in parallel
|
||||
std::vector<std::thread> sst_writer_threads;
|
||||
std::vector<port::Thread> sst_writer_threads;
|
||||
for (int i = 0; i < num_files; ++i) {
|
||||
sst_writer_threads.emplace_back(write_file_func);
|
||||
}
|
||||
@@ -864,7 +864,7 @@ TEST_F(ExternalSSTFileTest, MultiThreaded) {
|
||||
};
|
||||
|
||||
// Bulk load num_files files in parallel
|
||||
std::vector<std::thread> add_file_threads;
|
||||
std::vector<port::Thread> add_file_threads;
|
||||
DestroyAndReopen(options);
|
||||
for (int i = 0; i < num_files; ++i) {
|
||||
add_file_threads.emplace_back(load_file_func);
|
||||
@@ -1108,13 +1108,13 @@ TEST_F(ExternalSSTFileTest, PickedLevelBug) {
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// While writing the MANIFEST start a thread that will ask for compaction
|
||||
std::thread bg_compact([&]() {
|
||||
rocksdb::port::Thread bg_compact([&]() {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
});
|
||||
TEST_SYNC_POINT("ExternalSSTFileTest::PickedLevelBug:2");
|
||||
|
||||
// Start a thread that will ingest a new file
|
||||
std::thread bg_addfile([&]() {
|
||||
rocksdb::port::Thread bg_addfile([&]() {
|
||||
file_keys = {1, 2, 3};
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_keys, 1));
|
||||
});
|
||||
@@ -1169,7 +1169,7 @@ TEST_F(ExternalSSTFileTest, CompactDuringAddFileRandom) {
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_keys, range_id));
|
||||
};
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
while (range_id < 5000) {
|
||||
int range_start = range_id * 10;
|
||||
int range_end = range_start + 10;
|
||||
@@ -1728,7 +1728,7 @@ TEST_F(ExternalSSTFileTest, CompactionDeadlock) {
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Start ingesting and extrnal file in the background
|
||||
std::thread bg_ingest_file([&]() {
|
||||
rocksdb::port::Thread bg_ingest_file([&]() {
|
||||
running_threads += 1;
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, {5, 6}));
|
||||
running_threads -= 1;
|
||||
@@ -1748,7 +1748,7 @@ TEST_F(ExternalSSTFileTest, CompactionDeadlock) {
|
||||
|
||||
// This thread will try to insert into the memtable but since we have 4 L0
|
||||
// files this thread will be blocked and hold the writer thread
|
||||
std::thread bg_block_put([&]() {
|
||||
rocksdb::port::Thread bg_block_put([&]() {
|
||||
running_threads += 1;
|
||||
ASSERT_OK(Put(Key(10), "memtable"));
|
||||
running_threads -= 1;
|
||||
|
||||
+7
-1
@@ -38,7 +38,7 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "util/coding.h"
|
||||
@@ -230,6 +230,12 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void FlushJob::Cancel() {
|
||||
db_mutex_->AssertHeld();
|
||||
assert(base_ != nullptr);
|
||||
base_->Unref();
|
||||
}
|
||||
|
||||
Status FlushJob::WriteLevel0Table() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
|
||||
+3
-1
@@ -67,9 +67,11 @@ class FlushJob {
|
||||
|
||||
~FlushJob();
|
||||
|
||||
// Require db_mutex held
|
||||
// Require db_mutex held.
|
||||
// Once PickMemTable() is called, either Run() or Cancel() has to be call.
|
||||
void PickMemTable();
|
||||
Status Run(FileMetaData* file_meta = nullptr);
|
||||
void Cancel();
|
||||
TableProperties GetTableProperties() const { return table_properties_; }
|
||||
|
||||
private:
|
||||
|
||||
+40
-5
@@ -18,7 +18,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
@@ -69,12 +69,18 @@ class LevelIterator : public InternalIterator {
|
||||
delete file_iter_;
|
||||
}
|
||||
|
||||
RangeDelAggregator range_del_agg(
|
||||
cfd_->internal_comparator(), {} /* snapshots */);
|
||||
file_iter_ = cfd_->table_cache()->NewIterator(
|
||||
read_options_, *(cfd_->soptions()), cfd_->internal_comparator(),
|
||||
files_[file_index_]->fd, nullptr /* range_del_agg */,
|
||||
files_[file_index_]->fd, read_options_.ignore_range_deletions ? nullptr : &range_del_agg,
|
||||
nullptr /* table_reader_ptr */, nullptr, false);
|
||||
|
||||
file_iter_->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
if (!range_del_agg.IsEmpty()) {
|
||||
status_ = Status::NotSupported(
|
||||
"Range tombstones unsupported with ForwardIterator");
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
void SeekToLast() override {
|
||||
status_ = Status::NotSupported("LevelIterator::SeekToLast()");
|
||||
@@ -558,8 +564,17 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
// New
|
||||
sv_ = cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
}
|
||||
RangeDelAggregator range_del_agg(
|
||||
InternalKeyComparator(cfd_->internal_comparator()), {} /* snapshots */);
|
||||
mutable_iter_ = sv_->mem->NewIterator(read_options_, &arena_);
|
||||
sv_->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
sv_->mem->NewRangeTombstoneIterator(read_options_));
|
||||
range_del_agg.AddTombstones(std::move(range_del_iter));
|
||||
sv_->imm->AddRangeTombstoneIterators(read_options_, &arena_,
|
||||
&range_del_agg);
|
||||
}
|
||||
has_iter_trimmed_for_upper_bound_ = false;
|
||||
|
||||
const auto* vstorage = sv_->current->storage_info();
|
||||
@@ -575,13 +590,18 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
}
|
||||
l0_iters_.push_back(cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(), l0->fd,
|
||||
nullptr /* range_del_agg */));
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg));
|
||||
}
|
||||
BuildLevelIterators(vstorage);
|
||||
current_ = nullptr;
|
||||
is_prev_set_ = false;
|
||||
|
||||
UpdateChildrenPinnedItersMgr();
|
||||
if (!range_del_agg.IsEmpty()) {
|
||||
status_ = Status::NotSupported(
|
||||
"Range tombstones unsupported with ForwardIterator");
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ForwardIterator::RenewIterators() {
|
||||
@@ -599,6 +619,15 @@ void ForwardIterator::RenewIterators() {
|
||||
|
||||
mutable_iter_ = svnew->mem->NewIterator(read_options_, &arena_);
|
||||
svnew->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
RangeDelAggregator range_del_agg(
|
||||
InternalKeyComparator(cfd_->internal_comparator()), {} /* snapshots */);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
svnew->mem->NewRangeTombstoneIterator(read_options_));
|
||||
range_del_agg.AddTombstones(std::move(range_del_iter));
|
||||
sv_->imm->AddRangeTombstoneIterators(read_options_, &arena_,
|
||||
&range_del_agg);
|
||||
}
|
||||
|
||||
const auto* vstorage = sv_->current->storage_info();
|
||||
const auto& l0_files = vstorage->LevelFiles(0);
|
||||
@@ -630,7 +659,8 @@ void ForwardIterator::RenewIterators() {
|
||||
}
|
||||
l0_iters_new.push_back(cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
|
||||
l0_files_new[inew]->fd, nullptr /* range_del_agg */));
|
||||
l0_files_new[inew]->fd,
|
||||
read_options_.ignore_range_deletions ? nullptr : &range_del_agg));
|
||||
}
|
||||
|
||||
for (auto* f : l0_iters_) {
|
||||
@@ -650,6 +680,11 @@ void ForwardIterator::RenewIterators() {
|
||||
sv_ = svnew;
|
||||
|
||||
UpdateChildrenPinnedItersMgr();
|
||||
if (!range_del_agg.IsEmpty()) {
|
||||
status_ = Status::NotSupported(
|
||||
"Range tombstones unsupported with ForwardIterator");
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage) {
|
||||
|
||||
@@ -34,6 +34,7 @@ int main() { return 0; }
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "port/port.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
const int MAX_SHARDS = 100000;
|
||||
@@ -94,7 +95,7 @@ struct Reader {
|
||||
explicit Reader(std::vector<ShardState>* shard_states, rocksdb::DB* db)
|
||||
: shard_states_(shard_states), db_(db) {
|
||||
sem_init(&sem_, 0, 0);
|
||||
thread_ = std::thread(&Reader::run, this);
|
||||
thread_ = port::Thread(&Reader::run, this);
|
||||
}
|
||||
|
||||
void run() {
|
||||
@@ -193,7 +194,7 @@ struct Reader {
|
||||
char pad1[128] __attribute__((__unused__));
|
||||
std::vector<ShardState>* shard_states_;
|
||||
rocksdb::DB* db_;
|
||||
std::thread thread_;
|
||||
rocksdb::port::Thread thread_;
|
||||
sem_t sem_;
|
||||
std::mutex queue_mutex_;
|
||||
std::bitset<MAX_SHARDS + 1> shards_pending_set_;
|
||||
@@ -206,7 +207,7 @@ struct Writer {
|
||||
explicit Writer(std::vector<ShardState>* shard_states, rocksdb::DB* db)
|
||||
: shard_states_(shard_states), db_(db) {}
|
||||
|
||||
void start() { thread_ = std::thread(&Writer::run, this); }
|
||||
void start() { thread_ = port::Thread(&Writer::run, this); }
|
||||
|
||||
void run() {
|
||||
std::queue<std::chrono::steady_clock::time_point> workq;
|
||||
@@ -263,7 +264,7 @@ struct Writer {
|
||||
char pad1[128] __attribute__((__unused__));
|
||||
std::vector<ShardState>* shard_states_;
|
||||
rocksdb::DB* db_;
|
||||
std::thread thread_;
|
||||
rocksdb::port::Thread thread_;
|
||||
char pad2[128] __attribute__((__unused__));
|
||||
};
|
||||
|
||||
@@ -313,7 +314,7 @@ struct StatsThread {
|
||||
rocksdb::DB* db_;
|
||||
std::mutex cvm_;
|
||||
std::condition_variable cv_;
|
||||
std::thread thread_;
|
||||
rocksdb::port::Thread thread_;
|
||||
std::atomic<bool> done_{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -865,7 +865,7 @@ void InternalStats::DumpCFMapStats(std::map<std::string, double>* cf_stats) {
|
||||
}
|
||||
}
|
||||
|
||||
int InternalStats::DumpCFMapStats(
|
||||
void InternalStats::DumpCFMapStats(
|
||||
std::map<int, std::map<LevelStatType, double>>* levels_stats,
|
||||
CompactionStats* compaction_stats_sum) {
|
||||
const VersionStorageInfo* vstorage = cfd_->current()->storage_info();
|
||||
@@ -925,7 +925,6 @@ int InternalStats::DumpCFMapStats(
|
||||
PrepareLevelStats(&sum_stats, total_files, total_files_being_compacted,
|
||||
total_file_size, 0, w_amp, *compaction_stats_sum);
|
||||
(*levels_stats)[-1] = sum_stats; // -1 is for the Sum level
|
||||
return num_levels_to_check;
|
||||
}
|
||||
|
||||
void InternalStats::DumpCFStats(std::string* value) {
|
||||
@@ -937,8 +936,8 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
// Print stats for each level
|
||||
std::map<int, std::map<LevelStatType, double>> levels_stats;
|
||||
CompactionStats compaction_stats_sum(0);
|
||||
int levels = DumpCFMapStats(&levels_stats, &compaction_stats_sum);
|
||||
for (int l = 0; l < levels; ++l) {
|
||||
DumpCFMapStats(&levels_stats, &compaction_stats_sum);
|
||||
for (int l = 0; l < number_levels_; ++l) {
|
||||
if (levels_stats.find(l) != levels_stats.end()) {
|
||||
PrintLevelStats(buf, sizeof(buf), "L" + ToString(l), levels_stats[l]);
|
||||
value->append(buf);
|
||||
|
||||
+1
-1
@@ -273,7 +273,7 @@ class InternalStats {
|
||||
private:
|
||||
void DumpDBStats(std::string* value);
|
||||
void DumpCFMapStats(std::map<std::string, double>* cf_stats);
|
||||
int DumpCFMapStats(
|
||||
void DumpCFMapStats(
|
||||
std::map<int, std::map<LevelStatType, double>>* level_stats,
|
||||
CompactionStats* compaction_stats_sum);
|
||||
void DumpCFStats(std::string* value);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/xfunc.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
+7
-7
@@ -26,7 +26,7 @@
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/coding.h"
|
||||
@@ -390,16 +390,16 @@ port::RWMutex* MemTable::GetLock(const Slice& key) {
|
||||
return &locks_[hash(key) % locks_.size()];
|
||||
}
|
||||
|
||||
uint64_t MemTable::ApproximateSize(const Slice& start_ikey,
|
||||
const Slice& end_ikey) {
|
||||
MemTable::MemTableStats MemTable::ApproximateStats(const Slice& start_ikey,
|
||||
const Slice& end_ikey) {
|
||||
uint64_t entry_count = table_->ApproximateNumEntries(start_ikey, end_ikey);
|
||||
entry_count += range_del_table_->ApproximateNumEntries(start_ikey, end_ikey);
|
||||
if (entry_count == 0) {
|
||||
return 0;
|
||||
return {0, 0};
|
||||
}
|
||||
uint64_t n = num_entries_.load(std::memory_order_relaxed);
|
||||
if (n == 0) {
|
||||
return 0;
|
||||
return {0, 0};
|
||||
}
|
||||
if (entry_count > n) {
|
||||
// (range_del_)table_->ApproximateNumEntries() is just an estimate so it can
|
||||
@@ -408,7 +408,7 @@ uint64_t MemTable::ApproximateSize(const Slice& start_ikey,
|
||||
entry_count = n;
|
||||
}
|
||||
uint64_t data_size = data_size_.load(std::memory_order_relaxed);
|
||||
return entry_count * (data_size / n);
|
||||
return {entry_count * (data_size / n), entry_count};
|
||||
}
|
||||
|
||||
void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
@@ -560,7 +560,7 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
ValueType type;
|
||||
UnPackSequenceAndType(tag, &s->seq, &type);
|
||||
|
||||
if ((type == kTypeValue || type == kTypeDeletion) &&
|
||||
if ((type == kTypeValue || type == kTypeMerge) &&
|
||||
range_del_agg->ShouldDelete(Slice(key_ptr, key_length))) {
|
||||
type = kTypeRangeDeletion;
|
||||
}
|
||||
|
||||
+7
-1
@@ -326,7 +326,13 @@ class MemTable {
|
||||
return table_->IsSnapshotSupported() && !moptions_.inplace_update_support;
|
||||
}
|
||||
|
||||
uint64_t ApproximateSize(const Slice& start_ikey, const Slice& end_ikey);
|
||||
struct MemTableStats {
|
||||
uint64_t size;
|
||||
uint64_t count;
|
||||
};
|
||||
|
||||
MemTableStats ApproximateStats(const Slice& start_ikey,
|
||||
const Slice& end_ikey);
|
||||
|
||||
// Get the lock associated for the key
|
||||
port::RWMutex* GetLock(const Slice& key);
|
||||
|
||||
+8
-6
@@ -16,7 +16,7 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -190,13 +190,15 @@ uint64_t MemTableListVersion::GetTotalNumEntries() const {
|
||||
return total_num;
|
||||
}
|
||||
|
||||
uint64_t MemTableListVersion::ApproximateSize(const Slice& start_ikey,
|
||||
const Slice& end_ikey) {
|
||||
uint64_t total_size = 0;
|
||||
MemTable::MemTableStats MemTableListVersion::ApproximateStats(
|
||||
const Slice& start_ikey, const Slice& end_ikey) {
|
||||
MemTable::MemTableStats total_stats = {0, 0};
|
||||
for (auto& m : memlist_) {
|
||||
total_size += m->ApproximateSize(start_ikey, end_ikey);
|
||||
auto mStats = m->ApproximateStats(start_ikey, end_ikey);
|
||||
total_stats.size += mStats.size;
|
||||
total_stats.count += mStats.count;
|
||||
}
|
||||
return total_size;
|
||||
return total_stats;
|
||||
}
|
||||
|
||||
uint64_t MemTableListVersion::GetTotalNumDeletes() const {
|
||||
|
||||
+4
-1
@@ -95,7 +95,8 @@ class MemTableListVersion {
|
||||
|
||||
uint64_t GetTotalNumDeletes() const;
|
||||
|
||||
uint64_t ApproximateSize(const Slice& start_ikey, const Slice& end_ikey);
|
||||
MemTable::MemTableStats ApproximateStats(const Slice& start_ikey,
|
||||
const Slice& end_ikey);
|
||||
|
||||
// Returns the value of MemTable::GetEarliestSequenceNumber() on the most
|
||||
// recent MemTable in this list or kMaxSequenceNumber if the list is empty.
|
||||
@@ -221,6 +222,8 @@ class MemTableList {
|
||||
// PickMemtablesToFlush() is called.
|
||||
void FlushRequested() { flush_requested_ = true; }
|
||||
|
||||
bool HasFlushRequested() { return flush_requested_; }
|
||||
|
||||
// Copying allowed
|
||||
// MemTableList(const MemTableList&);
|
||||
// void operator=(const MemTableList&);
|
||||
|
||||
@@ -426,7 +426,7 @@ class Benchmark {
|
||||
virtual ~Benchmark() {}
|
||||
virtual void Run() {
|
||||
std::cout << "Number of threads: " << num_threads_ << std::endl;
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
uint64_t bytes_written = 0;
|
||||
uint64_t bytes_read = 0;
|
||||
uint64_t read_hits = 0;
|
||||
@@ -457,7 +457,7 @@ class Benchmark {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void RunThreads(std::vector<std::thread>* threads,
|
||||
virtual void RunThreads(std::vector<port::Thread>* threads,
|
||||
uint64_t* bytes_written, uint64_t* bytes_read,
|
||||
bool write, uint64_t* read_hits) = 0;
|
||||
|
||||
@@ -478,7 +478,7 @@ class FillBenchmark : public Benchmark {
|
||||
num_write_ops_per_thread_ = FLAGS_num_operations;
|
||||
}
|
||||
|
||||
void RunThreads(std::vector<std::thread>* threads, uint64_t* bytes_written,
|
||||
void RunThreads(std::vector<port::Thread>* threads, uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, bool write,
|
||||
uint64_t* read_hits) override {
|
||||
FillBenchmarkThread(table_, key_gen_, bytes_written, bytes_read, sequence_,
|
||||
@@ -494,7 +494,7 @@ class ReadBenchmark : public Benchmark {
|
||||
num_read_ops_per_thread_ = FLAGS_num_operations / FLAGS_num_threads;
|
||||
}
|
||||
|
||||
void RunThreads(std::vector<std::thread>* threads, uint64_t* bytes_written,
|
||||
void RunThreads(std::vector<port::Thread>* threads, uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, bool write,
|
||||
uint64_t* read_hits) override {
|
||||
for (int i = 0; i < FLAGS_num_threads; ++i) {
|
||||
@@ -518,7 +518,7 @@ class SeqReadBenchmark : public Benchmark {
|
||||
num_read_ops_per_thread_ = FLAGS_num_scans;
|
||||
}
|
||||
|
||||
void RunThreads(std::vector<std::thread>* threads, uint64_t* bytes_written,
|
||||
void RunThreads(std::vector<port::Thread>* threads, uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, bool write,
|
||||
uint64_t* read_hits) override {
|
||||
for (int i = 0; i < FLAGS_num_threads; ++i) {
|
||||
@@ -545,7 +545,7 @@ class ReadWriteBenchmark : public Benchmark {
|
||||
num_write_ops_per_thread_ = FLAGS_num_operations;
|
||||
}
|
||||
|
||||
void RunThreads(std::vector<std::thread>* threads, uint64_t* bytes_written,
|
||||
void RunThreads(std::vector<port::Thread>* threads, uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, bool write,
|
||||
uint64_t* read_hits) override {
|
||||
std::atomic_int threads_done;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "port/port.h"
|
||||
#include "util/histogram.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/stop_watch.h"
|
||||
@@ -561,7 +562,7 @@ TEST_F(PerfContextTest, DBMutexLockCounter) {
|
||||
for (int c = 0; c < 2; ++c) {
|
||||
InstrumentedMutex mutex(nullptr, Env::Default(), stats_code[c]);
|
||||
mutex.Lock();
|
||||
std::thread child_thread([&] {
|
||||
rocksdb::port::Thread child_thread([&] {
|
||||
SetPerfLevel(perf_level);
|
||||
perf_context.Reset();
|
||||
ASSERT_EQ(perf_context.db_mutex_lock_nanos, 0);
|
||||
|
||||
@@ -420,9 +420,10 @@ void RangeDelAggregator::AddToBuilder(
|
||||
RangeTombstone tombstone;
|
||||
if (collapse_deletions_) {
|
||||
auto next_tombstone_map_iter = std::next(tombstone_map_iter);
|
||||
if (next_tombstone_map_iter == stripe_map_iter->second.raw_map.end()) {
|
||||
// it's the sentinel tombstone
|
||||
break;
|
||||
if (next_tombstone_map_iter == stripe_map_iter->second.raw_map.end() ||
|
||||
tombstone_map_iter->second.seq_ == 0) {
|
||||
// it's a sentinel tombstone
|
||||
continue;
|
||||
}
|
||||
tombstone.start_key_ = tombstone_map_iter->first;
|
||||
tombstone.end_key_ = next_tombstone_map_iter->first;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_set.h"
|
||||
#include "port/port.h"
|
||||
#include "table/table_reader.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -359,7 +360,7 @@ class VersionBuilder::Rep {
|
||||
if (max_threads <= 1) {
|
||||
load_handlers_func();
|
||||
} else {
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
for (int i = 0; i < max_threads; i++) {
|
||||
threads.emplace_back(load_handlers_func);
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@
|
||||
#include "table/format.h"
|
||||
#include "table/get_context.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "table/plain_table_factory.h"
|
||||
#include "table/table_reader.h"
|
||||
|
||||
@@ -520,6 +520,8 @@ class Version {
|
||||
return next_;
|
||||
}
|
||||
|
||||
int TEST_refs() const { return refs_; }
|
||||
|
||||
VersionStorageInfo* storage_info() { return &storage_info_; }
|
||||
|
||||
VersionSet* version_set() { return vset_; }
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "db/write_callback.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "port/port.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
@@ -241,7 +242,7 @@ TEST_F(WriteCallbackTest, WriteWithCallbackTest) {
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// do all the writes
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
for (uint32_t i = 0; i < write_group.size(); i++) {
|
||||
threads.emplace_back(write_with_callback_func);
|
||||
}
|
||||
|
||||
+1
-1
@@ -317,7 +317,7 @@ void WriteThread::LaunchParallelFollowers(ParallelGroup* pg,
|
||||
|
||||
while (w != pg->last_writer) {
|
||||
// Writers that won't write don't get sequence allotment
|
||||
if (!w->CallbackFailed()) {
|
||||
if (!w->CallbackFailed() && w->ShouldWriteToMemtable()) {
|
||||
sequence += WriteBatchInternal::Count(w->batch);
|
||||
}
|
||||
w = w->link_newer;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: RocksDB 5.1.2 Released!
|
||||
layout: post
|
||||
author: maysamyabandeh
|
||||
category: blog
|
||||
---
|
||||
|
||||
### Public API Change
|
||||
* Support dynamically change `delete_obsolete_files_period_micros` option via SetDBOptions().
|
||||
* Added EventListener::OnExternalFileIngested which will be called when IngestExternalFile() add a file successfully.
|
||||
* BackupEngine::Open and BackupEngineReadOnly::Open now always return error statuses matching those of the backup Env.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix the bug that if 2PC is enabled, checkpoints may loss some recent transactions.
|
||||
* When file copying is needed when creating checkpoints or bulk loading files, fsync the file after the file copying.
|
||||
@@ -289,6 +289,10 @@ extern ROCKSDB_LIBRARY_API void rocksdb_release_snapshot(
|
||||
Else returns a pointer to a malloc()-ed null-terminated value. */
|
||||
extern ROCKSDB_LIBRARY_API char* rocksdb_property_value(rocksdb_t* db,
|
||||
const char* propname);
|
||||
/* returns 0 on success, -1 otherwise */
|
||||
int rocksdb_property_int(
|
||||
rocksdb_t* db,
|
||||
const char* propname, uint64_t *out_val);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API char* rocksdb_property_value_cf(
|
||||
rocksdb_t* db, rocksdb_column_family_handle_t* column_family,
|
||||
@@ -448,6 +452,10 @@ extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_iterate(
|
||||
void (*deleted)(void*, const char* k, size_t klen));
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_writebatch_data(
|
||||
rocksdb_writebatch_t*, size_t* size);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_set_save_point(
|
||||
rocksdb_writebatch_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_rollback_to_save_point(
|
||||
rocksdb_writebatch_t*, char** errptr);
|
||||
|
||||
/* Block based table options */
|
||||
|
||||
@@ -521,6 +529,8 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_cuckoo_table_factory(
|
||||
rocksdb_options_t* opt, rocksdb_cuckoo_table_options_t* table_options);
|
||||
|
||||
/* Options */
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_set_options(
|
||||
rocksdb_t* db, int count, const char* const keys[], const char* const values[], char** errptr);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API rocksdb_options_t* rocksdb_options_create();
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_destroy(rocksdb_options_t*);
|
||||
@@ -630,6 +640,10 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_soft_rate_limit(
|
||||
rocksdb_options_t*, double);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_hard_rate_limit(
|
||||
rocksdb_options_t*, double);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_soft_pending_compaction_bytes_limit(
|
||||
rocksdb_options_t* opt, size_t v);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_hard_pending_compaction_bytes_limit(
|
||||
rocksdb_options_t* opt, size_t v);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_rate_limit_delay_max_milliseconds(rocksdb_options_t*,
|
||||
unsigned int);
|
||||
@@ -695,6 +709,8 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_disable_data_sync(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_disable_auto_compactions(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_optimize_filters_for_hits(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_delete_obsolete_files_period_micros(rocksdb_options_t*,
|
||||
uint64_t);
|
||||
|
||||
@@ -39,8 +39,10 @@ class Cache;
|
||||
// is set, insert to the cache will fail when cache is full. User can also
|
||||
// set percentage of the cache reserves for high priority entries via
|
||||
// high_pri_pool_pct.
|
||||
// num_shard_bits = -1 means it is automatically determined: every shard
|
||||
// will be at least 512KB and number of shard bits will not exceed 6.
|
||||
extern std::shared_ptr<Cache> NewLRUCache(size_t capacity,
|
||||
int num_shard_bits = 6,
|
||||
int num_shard_bits = -1,
|
||||
bool strict_capacity_limit = false,
|
||||
double high_pri_pool_ratio = 0.0);
|
||||
|
||||
@@ -50,7 +52,7 @@ extern std::shared_ptr<Cache> NewLRUCache(size_t capacity,
|
||||
//
|
||||
// Return nullptr if it is not supported.
|
||||
extern std::shared_ptr<Cache> NewClockCache(size_t capacity,
|
||||
int num_shard_bits = 6,
|
||||
int num_shard_bits = -1,
|
||||
bool strict_capacity_limit = false);
|
||||
|
||||
class Cache {
|
||||
|
||||
+51
-7
@@ -464,7 +464,7 @@ class DB {
|
||||
static const std::string kNumDeletesImmMemTables;
|
||||
|
||||
// "rocksdb.estimate-num-keys" - returns estimated number of total keys in
|
||||
// the active and unflushed immutable memtables.
|
||||
// the active and unflushed immutable memtables and storage.
|
||||
static const std::string kEstimateNumKeys;
|
||||
|
||||
// "rocksdb.estimate-table-readers-mem" - returns estimated memory used for
|
||||
@@ -586,6 +586,14 @@ class DB {
|
||||
virtual bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* value) = 0;
|
||||
|
||||
// Flags for DB::GetSizeApproximation that specify whether memtable
|
||||
// stats should be included, or file stats approximation or both
|
||||
enum SizeApproximationFlags : uint8_t {
|
||||
NONE = 0,
|
||||
INCLUDE_MEMTABLES = 1,
|
||||
INCLUDE_FILES = 1 << 1
|
||||
};
|
||||
|
||||
// For each i in [0,n-1], store in "sizes[i]", the approximate
|
||||
// file system space used by keys in "[range[i].start .. range[i].limit)".
|
||||
//
|
||||
@@ -593,16 +601,52 @@ class DB {
|
||||
// if the user data compresses by a factor of ten, the returned
|
||||
// sizes will be one-tenth the size of the corresponding user data size.
|
||||
//
|
||||
// If include_memtable is set to true, then the result will also
|
||||
// include those recently written data in the mem-tables if
|
||||
// the mem-table type supports it.
|
||||
// If include_flags defines whether the returned size should include
|
||||
// the recently written data in the mem-tables (if
|
||||
// the mem-table type supports it), data serialized to disk, or both.
|
||||
// include_flags should be of type DB::SizeApproximationFlags
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable = false) = 0;
|
||||
uint8_t include_flags
|
||||
= INCLUDE_FILES) = 0;
|
||||
virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable = false) {
|
||||
uint8_t include_flags
|
||||
= INCLUDE_FILES) {
|
||||
GetApproximateSizes(DefaultColumnFamily(), range, n, sizes,
|
||||
include_memtable);
|
||||
include_flags);
|
||||
}
|
||||
|
||||
// The method is similar to GetApproximateSizes, except it
|
||||
// returns approximate number of records in memtables.
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) = 0;
|
||||
virtual void GetApproximateMemTableStats(const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) {
|
||||
GetApproximateMemTableStats(DefaultColumnFamily(), range, count, size);
|
||||
}
|
||||
|
||||
// Deprecated versions of GetApproximateSizes
|
||||
ROCKSDB_DEPRECATED_FUNC virtual void GetApproximateSizes(
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable) {
|
||||
uint8_t include_flags = SizeApproximationFlags::INCLUDE_FILES;
|
||||
if (include_memtable) {
|
||||
include_flags |= SizeApproximationFlags::INCLUDE_MEMTABLES;
|
||||
}
|
||||
GetApproximateSizes(DefaultColumnFamily(), range, n, sizes, include_flags);
|
||||
}
|
||||
ROCKSDB_DEPRECATED_FUNC virtual void GetApproximateSizes(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable) {
|
||||
uint8_t include_flags = SizeApproximationFlags::INCLUDE_FILES;
|
||||
if (include_memtable) {
|
||||
include_flags |= SizeApproximationFlags::INCLUDE_MEMTABLES;
|
||||
}
|
||||
GetApproximateSizes(column_family, range, n, sizes, include_flags);
|
||||
}
|
||||
|
||||
// Compact the underlying storage for the key range [*begin,*end].
|
||||
|
||||
+126
-115
@@ -17,7 +17,10 @@
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/universal_compaction.h"
|
||||
#include "rocksdb/version.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
@@ -230,7 +233,7 @@ struct ColumnFamilyOptions {
|
||||
// REQUIRES: The client must ensure that the comparator supplied
|
||||
// here has the same name and orders keys *exactly* the same as the
|
||||
// comparator provided to previous open calls on the same DB.
|
||||
const Comparator* comparator;
|
||||
const Comparator* comparator = BytewiseComparator();
|
||||
|
||||
// REQUIRES: The client must provide a merge operator if Merge operation
|
||||
// needs to be accessed. Calling Merge on a DB without a merge operator
|
||||
@@ -242,7 +245,7 @@ struct ColumnFamilyOptions {
|
||||
// for the first time. It's necessary to specify a merge operator when
|
||||
// openning the DB in this case.
|
||||
// Default: nullptr
|
||||
std::shared_ptr<MergeOperator> merge_operator;
|
||||
std::shared_ptr<MergeOperator> merge_operator = nullptr;
|
||||
|
||||
// A single CompactionFilter instance to call into during compaction.
|
||||
// Allows an application to modify/delete a key-value during background
|
||||
@@ -259,7 +262,7 @@ struct ColumnFamilyOptions {
|
||||
// thread-safe.
|
||||
//
|
||||
// Default: nullptr
|
||||
const CompactionFilter* compaction_filter;
|
||||
const CompactionFilter* compaction_filter = nullptr;
|
||||
|
||||
// This is a factory that provides compaction filter objects which allow
|
||||
// an application to modify/delete a key-value during background compaction.
|
||||
@@ -269,7 +272,7 @@ struct ColumnFamilyOptions {
|
||||
// from a single thread and so does not need to be thread-safe.
|
||||
//
|
||||
// Default: nullptr
|
||||
std::shared_ptr<CompactionFilterFactory> compaction_filter_factory;
|
||||
std::shared_ptr<CompactionFilterFactory> compaction_filter_factory = nullptr;
|
||||
|
||||
// -------------------
|
||||
// Parameters that affect performance
|
||||
@@ -290,7 +293,7 @@ struct ColumnFamilyOptions {
|
||||
// Default: 64MB
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t write_buffer_size;
|
||||
size_t write_buffer_size = 64 << 20;
|
||||
|
||||
// The maximum number of write buffers that are built up in memory.
|
||||
// The default and the minimum number is 2, so that when 1 write buffer
|
||||
@@ -303,7 +306,7 @@ struct ColumnFamilyOptions {
|
||||
// Default: 2
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int max_write_buffer_number;
|
||||
int max_write_buffer_number = 2;
|
||||
|
||||
// The minimum number of write buffers that will be merged together
|
||||
// before writing to storage. If set to 1, then
|
||||
@@ -312,7 +315,7 @@ struct ColumnFamilyOptions {
|
||||
// files. Also, an in-memory merge may result in writing lesser
|
||||
// data to storage if there are duplicate records in each of these
|
||||
// individual write buffers. Default: 1
|
||||
int min_write_buffer_number_to_merge;
|
||||
int min_write_buffer_number_to_merge = 1;
|
||||
|
||||
// The total maximum number of write buffers to maintain in memory including
|
||||
// copies of buffers that have already been flushed. Unlike
|
||||
@@ -338,7 +341,7 @@ struct ColumnFamilyOptions {
|
||||
// If using a TransactionDB/OptimisticTransactionDB, the default value will
|
||||
// be set to the value of 'max_write_buffer_number' if it is not explicitly
|
||||
// set by the user. Otherwise, the default is 0.
|
||||
int max_write_buffer_number_to_maintain;
|
||||
int max_write_buffer_number_to_maintain = 0;
|
||||
|
||||
// Compress blocks using the specified compression algorithm. This
|
||||
// parameter can be changed dynamically.
|
||||
@@ -384,7 +387,7 @@ struct ColumnFamilyOptions {
|
||||
// levels after base level.
|
||||
//
|
||||
// Default: kDisableCompressionOption (Disabled)
|
||||
CompressionType bottommost_compression;
|
||||
CompressionType bottommost_compression = kDisableCompressionOption;
|
||||
|
||||
// different options for compression algorithms
|
||||
CompressionOptions compression_opts;
|
||||
@@ -403,10 +406,10 @@ struct ColumnFamilyOptions {
|
||||
// 4) prefix(prefix(key)) == prefix(key)
|
||||
//
|
||||
// Default: nullptr
|
||||
std::shared_ptr<const SliceTransform> prefix_extractor;
|
||||
std::shared_ptr<const SliceTransform> prefix_extractor = nullptr;
|
||||
|
||||
// Number of levels for this database
|
||||
int num_levels;
|
||||
int num_levels = 7;
|
||||
|
||||
// Number of files to trigger level-0 compaction. A value <0 means that
|
||||
// level-0 compaction will not be triggered by number of files at all.
|
||||
@@ -414,19 +417,23 @@ struct ColumnFamilyOptions {
|
||||
// Default: 4
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int level0_file_num_compaction_trigger;
|
||||
int level0_file_num_compaction_trigger = 4;
|
||||
|
||||
// Soft limit on number of level-0 files. We start slowing down writes at this
|
||||
// point. A value <0 means that no writing slow down will be triggered by
|
||||
// number of files in level-0.
|
||||
//
|
||||
// Default: 20
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int level0_slowdown_writes_trigger;
|
||||
int level0_slowdown_writes_trigger = 20;
|
||||
|
||||
// Maximum number of level-0 files. We stop writes at this point.
|
||||
//
|
||||
// Default: 36
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int level0_stop_writes_trigger;
|
||||
int level0_stop_writes_trigger = 36;
|
||||
|
||||
// This does not do anything anymore. Deprecated.
|
||||
int max_mem_compaction_level;
|
||||
@@ -443,13 +450,13 @@ struct ColumnFamilyOptions {
|
||||
// Default: 64MB.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
uint64_t target_file_size_base;
|
||||
uint64_t target_file_size_base = 64 * 1048576;
|
||||
|
||||
// By default target_file_size_multiplier is 1, which means
|
||||
// by default files in different levels will have similar size.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
int target_file_size_multiplier;
|
||||
int target_file_size_multiplier = 1;
|
||||
|
||||
// Control maximum total data size for a level.
|
||||
// max_bytes_for_level_base is the max total for level-1.
|
||||
@@ -463,7 +470,7 @@ struct ColumnFamilyOptions {
|
||||
// Default: 256MB.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
uint64_t max_bytes_for_level_base;
|
||||
uint64_t max_bytes_for_level_base = 256 * 1048576;
|
||||
|
||||
// If true, RocksDB will pick target size of each level dynamically.
|
||||
// We will pick a base level b >= 1. L0 will be directly merged into level b,
|
||||
@@ -522,12 +529,12 @@ struct ColumnFamilyOptions {
|
||||
// NOTE: this option is experimental
|
||||
//
|
||||
// Default: false
|
||||
bool level_compaction_dynamic_level_bytes;
|
||||
bool level_compaction_dynamic_level_bytes = false;
|
||||
|
||||
// Default: 10.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
double max_bytes_for_level_multiplier;
|
||||
double max_bytes_for_level_multiplier = 10;
|
||||
|
||||
// Different max-size multipliers for different levels.
|
||||
// These are multiplied by max_bytes_for_level_multiplier to arrive
|
||||
@@ -536,14 +543,15 @@ struct ColumnFamilyOptions {
|
||||
// Default: 1
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
std::vector<int> max_bytes_for_level_multiplier_additional;
|
||||
std::vector<int> max_bytes_for_level_multiplier_additional =
|
||||
std::vector<int>(num_levels, 1);
|
||||
|
||||
// We try to limit number of bytes in one compaction to be lower than this
|
||||
// threshold. But it's not guaranteed.
|
||||
// Value 0 will be sanitized.
|
||||
//
|
||||
// Default: result.target_file_size_base * 25
|
||||
uint64_t max_compaction_bytes;
|
||||
uint64_t max_compaction_bytes = 0;
|
||||
|
||||
// DEPRECATED -- this options is no longer used
|
||||
// Puts are delayed to options.delayed_write_rate when any level has a
|
||||
@@ -552,25 +560,25 @@ struct ColumnFamilyOptions {
|
||||
// Default: 0 (disabled)
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
double soft_rate_limit;
|
||||
double soft_rate_limit = 0.0;
|
||||
|
||||
// DEPRECATED -- this options is no longer used
|
||||
double hard_rate_limit;
|
||||
double hard_rate_limit = 0.0;
|
||||
|
||||
// All writes will be slowed down to at least delayed_write_rate if estimated
|
||||
// bytes needed to be compaction exceed this threshold.
|
||||
//
|
||||
// Default: 64GB
|
||||
uint64_t soft_pending_compaction_bytes_limit;
|
||||
uint64_t soft_pending_compaction_bytes_limit = 64 * 1073741824ull;
|
||||
|
||||
// All writes are stopped if estimated bytes needed to be compaction exceed
|
||||
// this threshold.
|
||||
//
|
||||
// Default: 256GB
|
||||
uint64_t hard_pending_compaction_bytes_limit;
|
||||
uint64_t hard_pending_compaction_bytes_limit = 256 * 1073741824ull;
|
||||
|
||||
// DEPRECATED -- this options is no longer used
|
||||
unsigned int rate_limit_delay_max_milliseconds;
|
||||
unsigned int rate_limit_delay_max_milliseconds = 100;
|
||||
|
||||
// size of one block in arena memory allocation.
|
||||
// If <= 0, a proper value is automatically calculated (usually 1/8 of
|
||||
@@ -587,25 +595,25 @@ struct ColumnFamilyOptions {
|
||||
// Default: 0
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t arena_block_size;
|
||||
size_t arena_block_size = 0;
|
||||
|
||||
// Disable automatic compactions. Manual compactions can still
|
||||
// be issued on this column family
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
bool disable_auto_compactions;
|
||||
bool disable_auto_compactions = false;
|
||||
|
||||
// DEPREACTED
|
||||
// Does not have any effect.
|
||||
bool purge_redundant_kvs_while_flush;
|
||||
bool purge_redundant_kvs_while_flush = true;
|
||||
|
||||
// The compaction style. Default: kCompactionStyleLevel
|
||||
CompactionStyle compaction_style;
|
||||
CompactionStyle compaction_style = kCompactionStyleLevel;
|
||||
|
||||
// If level compaction_style = kCompactionStyleLevel, for each level,
|
||||
// which files are prioritized to be picked to compact.
|
||||
// Default: kByCompensatedSize
|
||||
CompactionPri compaction_pri;
|
||||
CompactionPri compaction_pri = kByCompensatedSize;
|
||||
|
||||
// If true, compaction will verify checksum on every read that happens
|
||||
// as part of compaction
|
||||
@@ -613,7 +621,7 @@ struct ColumnFamilyOptions {
|
||||
// Default: true
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
bool verify_checksums_in_compaction;
|
||||
bool verify_checksums_in_compaction = true;
|
||||
|
||||
// The options needed to support Universal Style compactions
|
||||
CompactionOptionsUniversal compaction_options_universal;
|
||||
@@ -629,12 +637,13 @@ struct ColumnFamilyOptions {
|
||||
// Default: 8
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
uint64_t max_sequential_skip_in_iterations;
|
||||
uint64_t max_sequential_skip_in_iterations = 8;
|
||||
|
||||
// This is a factory that provides MemTableRep objects.
|
||||
// Default: a factory that provides a skip-list-based implementation of
|
||||
// MemTableRep.
|
||||
std::shared_ptr<MemTableRepFactory> memtable_factory;
|
||||
std::shared_ptr<MemTableRepFactory> memtable_factory =
|
||||
std::shared_ptr<SkipListFactory>(new SkipListFactory);
|
||||
|
||||
// This is a factory that provides TableFactory objects.
|
||||
// Default: a block-based table factory that provides a default
|
||||
@@ -674,13 +683,13 @@ struct ColumnFamilyOptions {
|
||||
// * existing_value for that key is a put i.e. kTypeValue
|
||||
// If inplace_callback function is set, check doc for inplace_callback.
|
||||
// Default: false.
|
||||
bool inplace_update_support;
|
||||
bool inplace_update_support = false;
|
||||
|
||||
// Number of locks used for inplace update
|
||||
// Default: 10000, if inplace_update_support = true, else 0.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t inplace_update_num_locks;
|
||||
size_t inplace_update_num_locks = 10000;
|
||||
|
||||
// existing_value - pointer to previous value (from both memtable and sst).
|
||||
// nullptr if key doesn't exist
|
||||
@@ -722,7 +731,7 @@ struct ColumnFamilyOptions {
|
||||
UpdateStatus (*inplace_callback)(char* existing_value,
|
||||
uint32_t* existing_value_size,
|
||||
Slice delta_value,
|
||||
std::string* merged_value);
|
||||
std::string* merged_value) = nullptr;
|
||||
|
||||
// if prefix_extractor is set and memtable_prefix_bloom_size_ratio is not 0,
|
||||
// create prefix bloom for memtable with the size of
|
||||
@@ -732,7 +741,7 @@ struct ColumnFamilyOptions {
|
||||
// Default: 0 (disable)
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
double memtable_prefix_bloom_size_ratio;
|
||||
double memtable_prefix_bloom_size_ratio = 0.0;
|
||||
|
||||
// Page size for huge page for the arena used by the memtable. If <=0, it
|
||||
// won't allocate from huge page but from malloc.
|
||||
@@ -744,7 +753,7 @@ struct ColumnFamilyOptions {
|
||||
// malloc.
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t memtable_huge_page_size;
|
||||
size_t memtable_huge_page_size = 0;
|
||||
|
||||
// If non-nullptr, memtable will use the specified function to extract
|
||||
// prefixes for keys, and for each prefix maintain a hint of insert location
|
||||
@@ -766,7 +775,7 @@ struct ColumnFamilyOptions {
|
||||
//
|
||||
// Default: nullptr (disable)
|
||||
std::shared_ptr<const SliceTransform>
|
||||
memtable_insert_with_hint_prefix_extractor;
|
||||
memtable_insert_with_hint_prefix_extractor = nullptr;
|
||||
|
||||
// Control locality of bloom filter probes to improve cache miss rate.
|
||||
// This option only applies to memtable prefix bloom and plaintable
|
||||
@@ -774,7 +783,7 @@ struct ColumnFamilyOptions {
|
||||
// This optimization is turned off when set to 0, and positive number to turn
|
||||
// it on.
|
||||
// Default: 0
|
||||
uint32_t bloom_locality;
|
||||
uint32_t bloom_locality = 0;
|
||||
|
||||
// Maximum number of successive merge operations on a key in the memtable.
|
||||
//
|
||||
@@ -787,7 +796,7 @@ struct ColumnFamilyOptions {
|
||||
// Default: 0 (disabled)
|
||||
//
|
||||
// Dynamically changeable through SetOptions() API
|
||||
size_t max_successive_merges;
|
||||
size_t max_successive_merges = 0;
|
||||
|
||||
// The number of partial merge operands to accumulate before partial
|
||||
// merge will be performed. Partial merge will not be called
|
||||
@@ -796,7 +805,7 @@ struct ColumnFamilyOptions {
|
||||
// If min_partial_merge_operands < 2, then it will be treated as 2.
|
||||
//
|
||||
// Default: 2
|
||||
uint32_t min_partial_merge_operands;
|
||||
uint32_t min_partial_merge_operands = 2;
|
||||
|
||||
// This flag specifies that the implementation should optimize the filters
|
||||
// mainly for cases where keys are found rather than also optimize for keys
|
||||
@@ -812,21 +821,21 @@ struct ColumnFamilyOptions {
|
||||
// to the higher level.
|
||||
//
|
||||
// Default: false
|
||||
bool optimize_filters_for_hits;
|
||||
bool optimize_filters_for_hits = false;
|
||||
|
||||
// After writing every SST file, reopen it and read all the keys.
|
||||
// Default: false
|
||||
bool paranoid_file_checks;
|
||||
bool paranoid_file_checks = false;
|
||||
|
||||
// In debug mode, RocksDB run consistency checks on the LSM everytime the LSM
|
||||
// change (Flush, Compaction, AddFile). These checks are disabled in release
|
||||
// mode, use this option to enable them in release mode as well.
|
||||
// Default: false
|
||||
bool force_consistency_checks;
|
||||
bool force_consistency_checks = false;
|
||||
|
||||
// Measure IO stats in compactions and flushes, if true.
|
||||
// Default: false
|
||||
bool report_bg_io_stats;
|
||||
bool report_bg_io_stats = false;
|
||||
|
||||
// Create ColumnFamilyOptions with default values for all fields
|
||||
ColumnFamilyOptions();
|
||||
@@ -858,15 +867,15 @@ struct DBOptions {
|
||||
|
||||
// If true, the database will be created if it is missing.
|
||||
// Default: false
|
||||
bool create_if_missing;
|
||||
bool create_if_missing = false;
|
||||
|
||||
// If true, missing column families will be automatically created.
|
||||
// Default: false
|
||||
bool create_missing_column_families;
|
||||
bool create_missing_column_families = false;
|
||||
|
||||
// If true, an error is raised if the database already exists.
|
||||
// Default: false
|
||||
bool error_if_exists;
|
||||
bool error_if_exists = false;
|
||||
|
||||
// If true, RocksDB will aggressively check consistency of the data.
|
||||
// Also, if any of the writes to the database fails (Put, Delete, Merge,
|
||||
@@ -874,18 +883,18 @@ struct DBOptions {
|
||||
// Write operations.
|
||||
// In most cases you want this to be set to true.
|
||||
// Default: true
|
||||
bool paranoid_checks;
|
||||
bool paranoid_checks = true;
|
||||
|
||||
// Use the specified object to interact with the environment,
|
||||
// e.g. to read/write files, schedule background work, etc.
|
||||
// Default: Env::Default()
|
||||
Env* env;
|
||||
Env* env = Env::Default();
|
||||
|
||||
// Use to control write rate of flush and compaction. Flush has higher
|
||||
// priority than compaction. Rate limiting is disabled if nullptr.
|
||||
// If rate limiter is enabled, bytes_per_sync is set to 1MB by default.
|
||||
// Default: nullptr
|
||||
std::shared_ptr<RateLimiter> rate_limiter;
|
||||
std::shared_ptr<RateLimiter> rate_limiter = nullptr;
|
||||
|
||||
// Use to track SST files and control their file deletion rate.
|
||||
//
|
||||
@@ -901,15 +910,19 @@ struct DBOptions {
|
||||
// first db_path (db_name if db_paths is empty).
|
||||
//
|
||||
// Default: nullptr
|
||||
std::shared_ptr<SstFileManager> sst_file_manager;
|
||||
std::shared_ptr<SstFileManager> sst_file_manager = nullptr;
|
||||
|
||||
// Any internal progress/error information generated by the db will
|
||||
// be written to info_log if it is non-nullptr, or to a file stored
|
||||
// in the same directory as the DB contents if info_log is nullptr.
|
||||
// Default: nullptr
|
||||
std::shared_ptr<Logger> info_log;
|
||||
std::shared_ptr<Logger> info_log = nullptr;
|
||||
|
||||
InfoLogLevel info_log_level;
|
||||
#ifdef NDEBUG
|
||||
InfoLogLevel info_log_level = INFO_LEVEL;
|
||||
#else
|
||||
InfoLogLevel info_log_level = DEBUG_LEVEL;
|
||||
#endif // NDEBUG
|
||||
|
||||
// Number of open files that can be used by the DB. You may need to
|
||||
// increase this if your database has a large working set. Value -1 means
|
||||
@@ -917,12 +930,12 @@ struct DBOptions {
|
||||
// on target_file_size_base and target_file_size_multiplier for level-based
|
||||
// compaction. For universal-style compaction, you can usually set it to -1.
|
||||
// Default: -1
|
||||
int max_open_files;
|
||||
int max_open_files = -1;
|
||||
|
||||
// If max_open_files is -1, DB will open all files on DB::Open(). You can
|
||||
// use this option to increase the number of threads used to open the files.
|
||||
// Default: 16
|
||||
int max_file_opening_threads;
|
||||
int max_file_opening_threads = 16;
|
||||
|
||||
// Once write-ahead logs exceed this size, we will start forcing the flush of
|
||||
// column families whose memtables are backed by the oldest live WAL file
|
||||
@@ -930,12 +943,10 @@ struct DBOptions {
|
||||
// (default), we will dynamically choose the WAL size limit to be
|
||||
// [sum of all write_buffer_size * max_write_buffer_number] * 4
|
||||
// Default: 0
|
||||
uint64_t max_total_wal_size;
|
||||
uint64_t max_total_wal_size = 0;
|
||||
|
||||
// If non-null, then we should collect metrics about database operations
|
||||
// Statistics objects should not be shared between DB instances as
|
||||
// it does not use any locks to prevent concurrent updates.
|
||||
std::shared_ptr<Statistics> statistics;
|
||||
std::shared_ptr<Statistics> statistics = nullptr;
|
||||
|
||||
// If true, then the contents of manifest and data files are not synced
|
||||
// to stable storage. Their contents remain in the OS buffers till the
|
||||
@@ -943,14 +954,14 @@ struct DBOptions {
|
||||
// of data. Once the bulk-loading is complete, please issue a
|
||||
// sync to the OS to flush all dirty buffers to stable storage.
|
||||
// Default: false
|
||||
bool disableDataSync;
|
||||
bool disableDataSync = false;
|
||||
|
||||
// If true, then every store to stable storage will issue a fsync.
|
||||
// If false, then every store to stable storage will issue a fdatasync.
|
||||
// This parameter should be set to true while storing data to
|
||||
// filesystem like ext3 that can lose files after a reboot.
|
||||
// Default: false
|
||||
bool use_fsync;
|
||||
bool use_fsync = false;
|
||||
|
||||
// A list of paths where SST files can be put into, with its target size.
|
||||
// Newer data is placed into paths specified earlier in the vector while
|
||||
@@ -983,7 +994,7 @@ struct DBOptions {
|
||||
// If it is non empty, the log files will be in the specified dir,
|
||||
// and the db data dir's absolute path will be used as the log file
|
||||
// name's prefix.
|
||||
std::string db_log_dir;
|
||||
std::string db_log_dir = "";
|
||||
|
||||
// This specifies the absolute dir path for write-ahead logs (WAL).
|
||||
// If it is empty, the log files will be in the same dir as data,
|
||||
@@ -991,19 +1002,19 @@ struct DBOptions {
|
||||
// If it is non empty, the log files will be in kept the specified dir.
|
||||
// When destroying the db,
|
||||
// all log files in wal_dir and the dir itself is deleted
|
||||
std::string wal_dir;
|
||||
std::string wal_dir = "";
|
||||
|
||||
// The periodicity when obsolete files get deleted. The default
|
||||
// value is 6 hours. The files that get out of scope by compaction
|
||||
// process will still get automatically delete on every compaction,
|
||||
// regardless of this setting
|
||||
uint64_t delete_obsolete_files_period_micros;
|
||||
uint64_t delete_obsolete_files_period_micros = 6ULL * 60 * 60 * 1000000;
|
||||
|
||||
// Suggested number of concurrent background compaction jobs, submitted to
|
||||
// the default LOW priority thread pool.
|
||||
//
|
||||
// Default: 1
|
||||
int base_background_compactions;
|
||||
int base_background_compactions = 1;
|
||||
|
||||
// Maximum number of concurrent background compaction jobs, submitted to
|
||||
// the default LOW priority thread pool.
|
||||
@@ -1016,13 +1027,13 @@ struct DBOptions {
|
||||
// LOW priority thread pool. For more information, see
|
||||
// Env::SetBackgroundThreads
|
||||
// Default: 1
|
||||
int max_background_compactions;
|
||||
int max_background_compactions = 1;
|
||||
|
||||
// This value represents the maximum number of threads that will
|
||||
// concurrently perform a compaction job by breaking it into multiple,
|
||||
// smaller ones that are run simultaneously.
|
||||
// Default: 1 (i.e. no subcompactions)
|
||||
uint32_t max_subcompactions;
|
||||
uint32_t max_subcompactions = 1;
|
||||
|
||||
// Maximum number of concurrent background memtable flush jobs, submitted to
|
||||
// the HIGH priority thread pool.
|
||||
@@ -1039,24 +1050,24 @@ struct DBOptions {
|
||||
// HIGH priority thread pool. For more information, see
|
||||
// Env::SetBackgroundThreads
|
||||
// Default: 1
|
||||
int max_background_flushes;
|
||||
int max_background_flushes = 1;
|
||||
|
||||
// Specify the maximal size of the info log file. If the log file
|
||||
// is larger than `max_log_file_size`, a new info log file will
|
||||
// be created.
|
||||
// If max_log_file_size == 0, all logs will be written to one
|
||||
// log file.
|
||||
size_t max_log_file_size;
|
||||
size_t max_log_file_size = 0;
|
||||
|
||||
// Time for the info log file to roll (in seconds).
|
||||
// If specified with non-zero value, log file will be rolled
|
||||
// if it has been active longer than `log_file_time_to_roll`.
|
||||
// Default: 0 (disabled)
|
||||
size_t log_file_time_to_roll;
|
||||
size_t log_file_time_to_roll = 0;
|
||||
|
||||
// Maximal info log files to be kept.
|
||||
// Default: 1000
|
||||
size_t keep_log_file_num;
|
||||
size_t keep_log_file_num = 1000;
|
||||
|
||||
// Recycle log files.
|
||||
// If non-zero, we will reuse previously written log files for new
|
||||
@@ -1066,15 +1077,15 @@ struct DBOptions {
|
||||
// allocated and fdatasync does not need to update the inode after
|
||||
// each write.
|
||||
// Default: 0
|
||||
size_t recycle_log_file_num;
|
||||
size_t recycle_log_file_num = 0;
|
||||
|
||||
// manifest file is rolled over on reaching this limit.
|
||||
// The older manifest file be deleted.
|
||||
// The default value is MAX_INT so that roll-over does not take place.
|
||||
uint64_t max_manifest_file_size;
|
||||
uint64_t max_manifest_file_size = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
// Number of shards used for table cache.
|
||||
int table_cache_numshardbits;
|
||||
int table_cache_numshardbits = 6;
|
||||
|
||||
// DEPRECATED
|
||||
// int table_cache_remove_scan_count_limit;
|
||||
@@ -1091,22 +1102,22 @@ struct DBOptions {
|
||||
// are older than WAL_ttl_seconds will be deleted.
|
||||
// 4. If both are not 0, WAL files will be checked every 10 min and both
|
||||
// checks will be performed with ttl being first.
|
||||
uint64_t WAL_ttl_seconds;
|
||||
uint64_t WAL_size_limit_MB;
|
||||
uint64_t WAL_ttl_seconds = 0;
|
||||
uint64_t WAL_size_limit_MB = 0;
|
||||
|
||||
// Number of bytes to preallocate (via fallocate) the manifest
|
||||
// files. Default is 4mb, which is reasonable to reduce random IO
|
||||
// as well as prevent overallocation for mounts that preallocate
|
||||
// large amounts of data (such as xfs's allocsize option).
|
||||
size_t manifest_preallocation_size;
|
||||
size_t manifest_preallocation_size = 4 * 1024 * 1024;
|
||||
|
||||
// Allow the OS to mmap file for reading sst tables. Default: false
|
||||
bool allow_mmap_reads;
|
||||
bool allow_mmap_reads = false;
|
||||
|
||||
// Allow the OS to mmap file for writing.
|
||||
// DB::SyncWAL() only works if this is set to false.
|
||||
// Default: false
|
||||
bool allow_mmap_writes;
|
||||
bool allow_mmap_writes = false;
|
||||
|
||||
// Enable direct I/O mode for read/write
|
||||
// they may or may not improve performance depending on the use case
|
||||
@@ -1118,29 +1129,29 @@ struct DBOptions {
|
||||
|
||||
// Use O_DIRECT for reading file
|
||||
// Default: false
|
||||
bool use_direct_reads;
|
||||
bool use_direct_reads = false;
|
||||
|
||||
// Use O_DIRECT for writing file
|
||||
// Default: false
|
||||
bool use_direct_writes;
|
||||
bool use_direct_writes = false;
|
||||
|
||||
// If false, fallocate() calls are bypassed
|
||||
bool allow_fallocate;
|
||||
bool allow_fallocate = true;
|
||||
|
||||
// Disable child process inherit open files. Default: true
|
||||
bool is_fd_close_on_exec;
|
||||
bool is_fd_close_on_exec = true;
|
||||
|
||||
// DEPRECATED -- this options is no longer used
|
||||
bool skip_log_error_on_recovery;
|
||||
bool skip_log_error_on_recovery = false;
|
||||
|
||||
// if not zero, dump rocksdb.stats to LOG every stats_dump_period_sec
|
||||
// Default: 600 (10 min)
|
||||
unsigned int stats_dump_period_sec;
|
||||
unsigned int stats_dump_period_sec = 600;
|
||||
|
||||
// If set true, will hint the underlying file system that the file
|
||||
// access pattern is random, when a sst file is opened.
|
||||
// Default: true
|
||||
bool advise_random_on_open;
|
||||
bool advise_random_on_open = true;
|
||||
|
||||
// Amount of data to build up in memtables across all column
|
||||
// families before writing to disk.
|
||||
@@ -1152,7 +1163,7 @@ struct DBOptions {
|
||||
// to enable it.
|
||||
//
|
||||
// Default: 0 (disabled)
|
||||
size_t db_write_buffer_size;
|
||||
size_t db_write_buffer_size = 0;
|
||||
|
||||
// The memory usage of memtable will report to this object. The same object
|
||||
// can be passed into multiple DBs and it will track the sum of size of all
|
||||
@@ -1168,7 +1179,7 @@ struct DBOptions {
|
||||
// to enable it.
|
||||
//
|
||||
// Default: null
|
||||
std::shared_ptr<WriteBufferManager> write_buffer_manager;
|
||||
std::shared_ptr<WriteBufferManager> write_buffer_manager = nullptr;
|
||||
|
||||
// Specify the file access pattern once a compaction is started.
|
||||
// It will be applied to all input files of a compaction.
|
||||
@@ -1179,7 +1190,7 @@ struct DBOptions {
|
||||
SEQUENTIAL,
|
||||
WILLNEED
|
||||
};
|
||||
AccessHint access_hint_on_compaction_start;
|
||||
AccessHint access_hint_on_compaction_start = NORMAL;
|
||||
|
||||
// If true, always create a new file descriptor and new table reader
|
||||
// for compaction inputs. Turn this parameter on may introduce extra
|
||||
@@ -1191,7 +1202,7 @@ struct DBOptions {
|
||||
// for this mode if using block-based table.
|
||||
//
|
||||
// Default: false
|
||||
bool new_table_reader_for_compaction_inputs;
|
||||
bool new_table_reader_for_compaction_inputs = false;
|
||||
|
||||
// If non-zero, we perform bigger reads when doing compaction. If you're
|
||||
// running RocksDB on spinning disks, you should set this to at least 2MB.
|
||||
@@ -1201,7 +1212,7 @@ struct DBOptions {
|
||||
// true.
|
||||
//
|
||||
// Default: 0
|
||||
size_t compaction_readahead_size;
|
||||
size_t compaction_readahead_size = 0;
|
||||
|
||||
// This is a maximum buffer size that is used by WinMmapReadableFile in
|
||||
// unbuffered disk I/O mode. We need to maintain an aligned buffer for
|
||||
@@ -1218,14 +1229,14 @@ struct DBOptions {
|
||||
//
|
||||
// Special value: 0 - means do not maintain per instance buffer. Allocate
|
||||
// per request buffer and avoid locking.
|
||||
size_t random_access_max_buffer_size;
|
||||
size_t random_access_max_buffer_size = 1024 * 1024;
|
||||
|
||||
// This is the maximum buffer size that is used by WritableFileWriter.
|
||||
// On Windows, we need to maintain an aligned buffer for writes.
|
||||
// We allow the buffer to grow until it's size hits the limit.
|
||||
//
|
||||
// Default: 1024 * 1024 (1 MB)
|
||||
size_t writable_file_max_buffer_size;
|
||||
size_t writable_file_max_buffer_size = 1024 * 1024;
|
||||
|
||||
|
||||
// Use adaptive mutex, which spins in the user space before resorting
|
||||
@@ -1233,7 +1244,7 @@ struct DBOptions {
|
||||
// heavily contended. However, if the mutex is hot, we could end up
|
||||
// wasting spin time.
|
||||
// Default: false
|
||||
bool use_adaptive_mutex;
|
||||
bool use_adaptive_mutex = false;
|
||||
|
||||
// Create DBOptions with default values for all fields
|
||||
DBOptions();
|
||||
@@ -1254,11 +1265,11 @@ struct DBOptions {
|
||||
// to 1MB.
|
||||
//
|
||||
// This option applies to table files
|
||||
uint64_t bytes_per_sync;
|
||||
uint64_t bytes_per_sync = 0;
|
||||
|
||||
// Same as bytes_per_sync, but applies to WAL files
|
||||
// Default: 0, turned off
|
||||
uint64_t wal_bytes_per_sync;
|
||||
uint64_t wal_bytes_per_sync = 0;
|
||||
|
||||
// A vector of EventListeners which call-back functions will be called
|
||||
// when specific RocksDB event happens.
|
||||
@@ -1268,7 +1279,7 @@ struct DBOptions {
|
||||
// be tracked and available via GetThreadList() API.
|
||||
//
|
||||
// Default: false
|
||||
bool enable_thread_tracking;
|
||||
bool enable_thread_tracking = false;
|
||||
|
||||
// The limited write rate to DB if soft_pending_compaction_bytes_limit or
|
||||
// level0_slowdown_writes_trigger is triggered, or we are writing to the
|
||||
@@ -1278,8 +1289,8 @@ struct DBOptions {
|
||||
// gets behind further.
|
||||
// Unit: byte per second.
|
||||
//
|
||||
// Default: 2MB/s
|
||||
uint64_t delayed_write_rate;
|
||||
// Default: 16MB/s
|
||||
uint64_t delayed_write_rate = 16 * 1024U * 1024U;
|
||||
|
||||
// If true, allow multi-writers to update mem tables in parallel.
|
||||
// Only some memtable_factory-s support concurrent writes; currently it
|
||||
@@ -1289,7 +1300,7 @@ struct DBOptions {
|
||||
// if you are going to use this feature.
|
||||
//
|
||||
// Default: true
|
||||
bool allow_concurrent_memtable_write;
|
||||
bool allow_concurrent_memtable_write = true;
|
||||
|
||||
// If true, threads synchronizing with the write batch group leader will
|
||||
// wait for up to write_thread_max_yield_usec before blocking on a mutex.
|
||||
@@ -1297,7 +1308,7 @@ struct DBOptions {
|
||||
// regardless of whether allow_concurrent_memtable_write is enabled.
|
||||
//
|
||||
// Default: true
|
||||
bool enable_write_thread_adaptive_yield;
|
||||
bool enable_write_thread_adaptive_yield = true;
|
||||
|
||||
// The maximum number of microseconds that a write operation will use
|
||||
// a yielding spin loop to coordinate with other write threads before
|
||||
@@ -1306,7 +1317,7 @@ struct DBOptions {
|
||||
// throughput at the expense of increased CPU usage.
|
||||
//
|
||||
// Default: 100
|
||||
uint64_t write_thread_max_yield_usec;
|
||||
uint64_t write_thread_max_yield_usec = 100;
|
||||
|
||||
// The latency in microseconds after which a std::this_thread::yield
|
||||
// call (sched_yield on Linux) is considered to be a signal that
|
||||
@@ -1316,7 +1327,7 @@ struct DBOptions {
|
||||
// involuntary context switches.
|
||||
//
|
||||
// Default: 3
|
||||
uint64_t write_thread_slow_yield_usec;
|
||||
uint64_t write_thread_slow_yield_usec = 3;
|
||||
|
||||
// If true, then DB::Open() will not update the statistics used to optimize
|
||||
// compaction decision by loading table properties from many files.
|
||||
@@ -1324,11 +1335,11 @@ struct DBOptions {
|
||||
// disk environment.
|
||||
//
|
||||
// Default: false
|
||||
bool skip_stats_update_on_db_open;
|
||||
bool skip_stats_update_on_db_open = false;
|
||||
|
||||
// Recovery mode to control the consistency while replaying WAL
|
||||
// Default: kPointInTimeRecovery
|
||||
WALRecoveryMode wal_recovery_mode;
|
||||
WALRecoveryMode wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
|
||||
// if set to false then recovery will fail when a prepared
|
||||
// transaction is encountered in the WAL
|
||||
@@ -1337,7 +1348,7 @@ struct DBOptions {
|
||||
// A global cache for table-level rows.
|
||||
// Default: nullptr (disabled)
|
||||
// Not supported in ROCKSDB_LITE mode!
|
||||
std::shared_ptr<Cache> row_cache;
|
||||
std::shared_ptr<Cache> row_cache = nullptr;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// A filter object supplied to be invoked while processing write-ahead-logs
|
||||
@@ -1345,7 +1356,7 @@ struct DBOptions {
|
||||
// records, ignoring a particular record or skipping replay.
|
||||
// The filter is invoked at startup and is invoked from a single-thread
|
||||
// currently.
|
||||
WalFilter* wal_filter;
|
||||
WalFilter* wal_filter = nullptr;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// If true, then DB::Open / CreateColumnFamily / DropColumnFamily
|
||||
@@ -1353,12 +1364,12 @@ struct DBOptions {
|
||||
// persisted.
|
||||
//
|
||||
// DEFAULT: false
|
||||
bool fail_if_options_file_error;
|
||||
bool fail_if_options_file_error = false;
|
||||
|
||||
// If true, then print malloc stats together with rocksdb.stats
|
||||
// when printing to LOG.
|
||||
// DEFAULT: false
|
||||
bool dump_malloc_stats;
|
||||
bool dump_malloc_stats = false;
|
||||
|
||||
// By default RocksDB replay WAL logs and flush them on DB open, which may
|
||||
// create very small SST files. If this option is enabled, RocksDB will try
|
||||
@@ -1367,7 +1378,7 @@ struct DBOptions {
|
||||
// have logs to recover from.
|
||||
//
|
||||
// DEFAULT: false
|
||||
bool avoid_flush_during_recovery;
|
||||
bool avoid_flush_during_recovery = false;
|
||||
|
||||
// By default RocksDB will flush all memtables on DB close if there are
|
||||
// unpersisted data (i.e. with WAL disabled) The flush can be skip to speedup
|
||||
@@ -1376,7 +1387,7 @@ struct DBOptions {
|
||||
// DEFAULT: false
|
||||
//
|
||||
// Dynamically changeable through SetDBOptions() API.
|
||||
bool avoid_flush_during_shutdown;
|
||||
bool avoid_flush_during_shutdown = false;
|
||||
};
|
||||
|
||||
// Options to control the behavior of a database (passed to DB::Open)
|
||||
@@ -1508,7 +1519,7 @@ struct ReadOptions {
|
||||
// used in the table. Some table format (e.g. plain table) may not support
|
||||
// this option.
|
||||
// If true when calling Get(), we also skip prefix bloom when reading from
|
||||
// block based table. It provides a way to read exisiting data after
|
||||
// block based table. It provides a way to read existing data after
|
||||
// changing implementation of prefix extractor.
|
||||
bool total_order_seek;
|
||||
|
||||
|
||||
@@ -68,13 +68,13 @@ class SstFileManager {
|
||||
// this value is set to 1024 (1 Kb / sec) and we deleted a file of size 4 Kb
|
||||
// in 1 second, we will wait for another 3 seconds before we delete other
|
||||
// files, Set to 0 to disable deletion rate limiting.
|
||||
// @param delete_exisitng_trash: If set to true, the newly created
|
||||
// @param delete_existing_trash: If set to true, the newly created
|
||||
// SstFileManager will delete files that already exist in trash_dir.
|
||||
// @param status: If not nullptr, status will contain any errors that happened
|
||||
// during creating the missing trash_dir or deleting existing files in trash.
|
||||
extern SstFileManager* NewSstFileManager(
|
||||
Env* env, std::shared_ptr<Logger> info_log = nullptr,
|
||||
std::string trash_dir = "", int64_t rate_bytes_per_sec = 0,
|
||||
bool delete_exisitng_trash = true, Status* status = nullptr);
|
||||
bool delete_existing_trash = true, Status* status = nullptr);
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -86,6 +86,12 @@ struct BlockBasedTableOptions {
|
||||
// The hash index, if enabled, will do the hash lookup when
|
||||
// `Options.prefix_extractor` is provided.
|
||||
kHashSearch,
|
||||
|
||||
// TODO(myabandeh): this feature is in experimental phase and shall not be
|
||||
// used in production; either remove the feature or remove this comment if
|
||||
// it is ready to be used in production.
|
||||
// A two-level index implementation. Both levels are binary search indexes.
|
||||
kTwoLevelIndexSearch,
|
||||
};
|
||||
|
||||
IndexType index_type = kBinarySearch;
|
||||
@@ -138,6 +144,10 @@ struct BlockBasedTableOptions {
|
||||
// Same as block_restart_interval but used for the index block.
|
||||
int index_block_restart_interval = 1;
|
||||
|
||||
// Number of index keys per partition of indexes in a multi-level index
|
||||
// i.e., the number of data blocks covered by each index partition
|
||||
uint64_t index_per_partition = 1024;
|
||||
|
||||
// Use delta encoding to compress keys in blocks.
|
||||
// ReadOptions::pin_data requires this option to be disabled.
|
||||
//
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
/*
|
||||
@@ -20,6 +22,8 @@ class ThreadPool {
|
||||
virtual ~ThreadPool() {}
|
||||
|
||||
// Wait for all threads to finish.
|
||||
// Discard those threads that did not start
|
||||
// executing
|
||||
virtual void JoinAllThreads() = 0;
|
||||
|
||||
// Set the number of background threads that will be executing the
|
||||
@@ -28,6 +32,21 @@ class ThreadPool {
|
||||
|
||||
// Get the number of jobs scheduled in the ThreadPool queue.
|
||||
virtual unsigned int GetQueueLen() const = 0;
|
||||
|
||||
// Waits for all jobs to complete those
|
||||
// that already started running and those that did not
|
||||
// start yet. This ensures that everything that was thrown
|
||||
// on the TP runs even though
|
||||
// we may not have specified enough threads for the amount
|
||||
// of jobs
|
||||
virtual void WaitForJobsAndJoinAllThreads() = 0;
|
||||
|
||||
// Submit a fire and forget jobs
|
||||
// This allows to submit the same job multiple times
|
||||
virtual void SubmitJob(const std::function<void()>&) = 0;
|
||||
// This moves the function in for efficiency
|
||||
virtual void SubmitJob(std::function<void()>&&) = 0;
|
||||
|
||||
};
|
||||
|
||||
// NewThreadPool() is a function that could be used to create a ThreadPool
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Returns a new Env when called with a URI string. Populates the unique_ptr
|
||||
// argument if granting ownership to caller.
|
||||
typedef std::function<Env*(const std::string&, std::unique_ptr<Env>*)>
|
||||
EnvFactoryFunc;
|
||||
|
||||
// Creates a new Env using the registered factory function corresponding to a
|
||||
// prefix of uri.
|
||||
//
|
||||
// If no prefixes match, returns nullptr. If multiple prefixes match, the
|
||||
// factory function used is unspecified.
|
||||
//
|
||||
// Populates env_guard with result pointer if caller is granted ownership.
|
||||
Env* NewEnvFromUri(const std::string& uri, std::unique_ptr<Env>* env_guard);
|
||||
|
||||
// To register an Env factory function, initialize an EnvRegistrar object with
|
||||
// static storage duration. For example:
|
||||
//
|
||||
// static EnvRegistrar hdfs_reg("hdfs://", &CreateHdfsEnv);
|
||||
//
|
||||
// Then, calling NewEnvFromUri("hdfs://some_path", ...) will use CreateHdfsEnv
|
||||
// to make a new Env.
|
||||
class EnvRegistrar {
|
||||
public:
|
||||
explicit EnvRegistrar(std::string uri_prefix, EnvFactoryFunc env_factory);
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Creates a new T using the factory function that was registered with a pattern
|
||||
// that matches the provided "target" string according to std::regex_match.
|
||||
//
|
||||
// If no registered functions match, returns nullptr. If multiple functions
|
||||
// match, the factory function used is unspecified.
|
||||
//
|
||||
// Populates res_guard with result pointer if caller is granted ownership.
|
||||
template <typename T>
|
||||
T* NewCustomObject(const std::string& target, std::unique_ptr<T>* res_guard);
|
||||
|
||||
// Returns a new T when called with a string. Populates the unique_ptr argument
|
||||
// if granting ownership to caller.
|
||||
template <typename T>
|
||||
using FactoryFunc = std::function<T*(const std::string&, std::unique_ptr<T>*)>;
|
||||
|
||||
// To register a factory function for a type T, initialize a Registrar<T> object
|
||||
// with static storage duration. For example:
|
||||
//
|
||||
// static Registrar<Env> hdfs_reg("hdfs://.*", &CreateHdfsEnv);
|
||||
//
|
||||
// Then, calling NewCustomObject<Env>("hdfs://some_path", ...) will match the
|
||||
// regex provided above, so it returns the result of invoking CreateHdfsEnv.
|
||||
template <typename T>
|
||||
class Registrar {
|
||||
public:
|
||||
explicit Registrar(std::string pattern, FactoryFunc<T> factory);
|
||||
};
|
||||
|
||||
// Implementation details follow.
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
struct RegistryEntry {
|
||||
std::regex pattern;
|
||||
FactoryFunc<T> factory;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Registry {
|
||||
static Registry* Get() {
|
||||
static Registry<T> instance;
|
||||
return &instance;
|
||||
}
|
||||
std::vector<RegistryEntry<T>> entries;
|
||||
|
||||
private:
|
||||
Registry() = default;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
T* NewCustomObject(const std::string& target, std::unique_ptr<T>* res_guard) {
|
||||
res_guard->reset();
|
||||
for (const auto& entry : internal::Registry<T>::Get()->entries) {
|
||||
if (std::regex_match(target, entry.pattern)) {
|
||||
return entry.factory(target, res_guard);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Registrar<T>::Registrar(std::string pattern, FactoryFunc<T> factory) {
|
||||
internal::Registry<T>::Get()->entries.emplace_back(internal::RegistryEntry<T>{
|
||||
std::regex(std::move(pattern)), std::move(factory)});
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -161,9 +161,18 @@ class StackableDB : public DB {
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* r, int n, uint64_t* sizes,
|
||||
bool include_memtable = false) override {
|
||||
uint8_t include_flags
|
||||
= INCLUDE_FILES) override {
|
||||
return db_->GetApproximateSizes(column_family, r, n, sizes,
|
||||
include_memtable);
|
||||
include_flags);
|
||||
}
|
||||
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override {
|
||||
return db_->GetApproximateMemTableStats(column_family, range, count, size);
|
||||
}
|
||||
|
||||
using DB::CompactRange;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#define ROCKSDB_MAJOR 5
|
||||
#define ROCKSDB_MINOR 0
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_MINOR 2
|
||||
#define ROCKSDB_PATCH 1
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
@@ -14,71 +14,60 @@ public abstract class AbstractWriteBatch extends RocksObject
|
||||
|
||||
@Override
|
||||
public int count() {
|
||||
assert (isOwningHandle());
|
||||
return count0(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(byte[] key, byte[] value) {
|
||||
assert (isOwningHandle());
|
||||
put(nativeHandle_, key, key.length, value, value.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(ColumnFamilyHandle columnFamilyHandle, byte[] key,
|
||||
byte[] value) {
|
||||
assert (isOwningHandle());
|
||||
put(nativeHandle_, key, key.length, value, value.length,
|
||||
columnFamilyHandle.nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(byte[] key, byte[] value) {
|
||||
assert (isOwningHandle());
|
||||
merge(nativeHandle_, key, key.length, value, value.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(ColumnFamilyHandle columnFamilyHandle, byte[] key,
|
||||
byte[] value) {
|
||||
assert (isOwningHandle());
|
||||
merge(nativeHandle_, key, key.length, value, value.length,
|
||||
columnFamilyHandle.nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(byte[] key) {
|
||||
assert (isOwningHandle());
|
||||
remove(nativeHandle_, key, key.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(ColumnFamilyHandle columnFamilyHandle, byte[] key) {
|
||||
assert (isOwningHandle());
|
||||
remove(nativeHandle_, key, key.length, columnFamilyHandle.nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putLogData(byte[] blob) {
|
||||
assert (isOwningHandle());
|
||||
putLogData(nativeHandle_, blob, blob.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
assert (isOwningHandle());
|
||||
clear0(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSavePoint() {
|
||||
assert (isOwningHandle());
|
||||
setSavePoint0(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollbackToSavePoint() throws RocksDBException {
|
||||
assert (isOwningHandle());
|
||||
rollbackToSavePoint0(nativeHandle_);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,11 @@ public enum IndexType {
|
||||
* The hash index, if enabled, will do the hash lookup when
|
||||
* {@code Options.prefix_extractor} is provided.
|
||||
*/
|
||||
kHashSearch((byte) 1);
|
||||
kHashSearch((byte) 1),
|
||||
/**
|
||||
* A two-level index implementation. Both levels are binary search indexes.
|
||||
*/
|
||||
kTwoLevelIndexSearch((byte) 2);
|
||||
|
||||
/**
|
||||
* Returns the byte value of the enumerations value
|
||||
|
||||
@@ -76,7 +76,7 @@ public class TransactionLogIterator extends RocksObject {
|
||||
public BatchResult(final long sequenceNumber,
|
||||
final long nativeHandle) {
|
||||
sequenceNumber_ = sequenceNumber;
|
||||
writeBatch_ = new WriteBatch(nativeHandle);
|
||||
writeBatch_ = new WriteBatch(nativeHandle, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,8 +59,20 @@ public class WriteBatch extends AbstractWriteBatch {
|
||||
* @param nativeHandle address of native instance.
|
||||
*/
|
||||
WriteBatch(final long nativeHandle) {
|
||||
this(nativeHandle, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Private WriteBatch constructor which is used to construct
|
||||
* WriteBatch instances. </p>
|
||||
*
|
||||
* @param nativeHandle address of native instance.
|
||||
* @param owningNativeHandle whether to own this reference from the C++ side or not
|
||||
*/
|
||||
WriteBatch(final long nativeHandle, final boolean owningNativeHandle) {
|
||||
super(nativeHandle);
|
||||
disOwnNativeHandle();
|
||||
if(!owningNativeHandle)
|
||||
disOwnNativeHandle();
|
||||
}
|
||||
|
||||
@Override protected final native void disposeInternal(final long handle);
|
||||
|
||||
@@ -95,7 +95,7 @@ public class BlockBasedTableConfigTest {
|
||||
@Test
|
||||
public void indexType() {
|
||||
BlockBasedTableConfig blockBasedTableConfig = new BlockBasedTableConfig();
|
||||
assertThat(IndexType.values().length).isEqualTo(2);
|
||||
assertThat(IndexType.values().length).isEqualTo(3);
|
||||
blockBasedTableConfig.setIndexType(IndexType.kHashSearch);
|
||||
assertThat(blockBasedTableConfig.indexType().equals(
|
||||
IndexType.kHashSearch));
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
// size_t printf formatting named in the manner of C99 standard formatting
|
||||
// strings such as PRIu64
|
||||
// in fact, we could use that one
|
||||
@@ -156,6 +157,8 @@ class CondVar {
|
||||
Mutex* mu_;
|
||||
};
|
||||
|
||||
using Thread = std::thread;
|
||||
|
||||
static inline void AsmVolatilePause() {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
asm volatile("pause");
|
||||
|
||||
+10
-2
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "port/win/env_win.h"
|
||||
#include "port/win/win_thread.h"
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <thread>
|
||||
@@ -119,13 +120,20 @@ Status WinEnvIO::NewSequentialFile(const std::string& fname,
|
||||
// while they are still open with another handle. For that reason we
|
||||
// allow share_write and delete(allows rename).
|
||||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||||
|
||||
DWORD fileFlags = FILE_ATTRIBUTE_READONLY;
|
||||
|
||||
if (options.use_direct_reads && !options.use_mmap_reads) {
|
||||
fileFlags |= FILE_FLAG_NO_BUFFERING;
|
||||
}
|
||||
|
||||
{
|
||||
IOSTATS_TIMER_GUARD(open_nanos);
|
||||
hFile = CreateFileA(
|
||||
fname.c_str(), GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING, // Original fopen mode is "rb"
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
fileFlags, NULL);
|
||||
}
|
||||
|
||||
if (INVALID_HANDLE_VALUE == hFile) {
|
||||
@@ -830,7 +838,7 @@ void WinEnvThreads::StartThread(void(*function)(void* arg), void* arg) {
|
||||
state->arg = arg;
|
||||
try {
|
||||
|
||||
std::thread th(&StartThreadWrapper, state.get());
|
||||
rocksdb::port::WindowsThread th(&StartThreadWrapper, state.get());
|
||||
state.release();
|
||||
|
||||
std::lock_guard<std::mutex> lg(mu_);
|
||||
|
||||
+13
-3
@@ -16,11 +16,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "port/win/win_thread.h"
|
||||
#include <rocksdb/env.h>
|
||||
#include "util/threadpool_imp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
#undef GetCurrentTime
|
||||
#undef DeleteFile
|
||||
#undef GetTickCount
|
||||
|
||||
namespace rocksdb {
|
||||
namespace port {
|
||||
@@ -64,7 +74,7 @@ private:
|
||||
Env* hosted_env_;
|
||||
mutable std::mutex mu_;
|
||||
std::vector<ThreadPoolImpl> thread_pools_;
|
||||
std::vector<std::thread> threads_to_join_;
|
||||
std::vector<WindowsThread> threads_to_join_;
|
||||
|
||||
};
|
||||
|
||||
@@ -281,5 +291,5 @@ private:
|
||||
WinEnvThreads winenv_threads_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace port
|
||||
} // namespace rocksdb
|
||||
@@ -556,6 +556,7 @@ WinSequentialFile::~WinSequentialFile() {
|
||||
}
|
||||
|
||||
Status WinSequentialFile::Read(size_t n, Slice* result, char* scratch) {
|
||||
assert(result != nullptr && !WinFileData::use_direct_io());
|
||||
Status s;
|
||||
size_t r = 0;
|
||||
|
||||
@@ -580,6 +581,41 @@ Status WinSequentialFile::Read(size_t n, Slice* result, char* scratch) {
|
||||
return s;
|
||||
}
|
||||
|
||||
SSIZE_T WinSequentialFile::PositionedReadInternal(char* src, size_t numBytes,
|
||||
uint64_t offset) const {
|
||||
return pread(GetFileHandle(), src, numBytes, offset);
|
||||
}
|
||||
|
||||
Status WinSequentialFile::PositionedRead(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) {
|
||||
|
||||
Status s;
|
||||
|
||||
assert(WinFileData::use_direct_io());
|
||||
|
||||
// Windows ReadFile API accepts a DWORD.
|
||||
// While it is possible to read in a loop if n is > UINT_MAX
|
||||
// it is a highly unlikely case.
|
||||
if (n > UINT_MAX) {
|
||||
return IOErrorFromWindowsError(GetName(), ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
auto r = PositionedReadInternal(scratch, n, offset);
|
||||
|
||||
if (r < 0) {
|
||||
auto lastError = GetLastError();
|
||||
// Posix impl wants to treat reads from beyond
|
||||
// of the file as OK.
|
||||
if (lastError != ERROR_HANDLE_EOF) {
|
||||
s = IOErrorFromWindowsError(GetName(), lastError);
|
||||
}
|
||||
}
|
||||
|
||||
*result = Slice(scratch, (r < 0) ? 0 : size_t(r));
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
Status WinSequentialFile::Skip(uint64_t n) {
|
||||
// Can't handle more than signed max as SetFilePointerEx accepts a signed 64-bit
|
||||
// integer. As such it is a highly unlikley case to have n so large.
|
||||
@@ -855,6 +891,10 @@ size_t WinRandomAccessFile::GetUniqueId(char* id, size_t max_size) const {
|
||||
return GetUniqueIdFromFile(GetFileHandle(), id, max_size);
|
||||
}
|
||||
|
||||
size_t WinRandomAccessFile::GetRequiredBufferAlignment() const {
|
||||
return GetAlignment();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// WinWritableImpl
|
||||
//
|
||||
|
||||
+46
-30
@@ -101,6 +101,32 @@ class WinFileData {
|
||||
WinFileData& operator=(const WinFileData&) = delete;
|
||||
};
|
||||
|
||||
class WinSequentialFile : protected WinFileData, public SequentialFile {
|
||||
|
||||
// Override for behavior change when creating a custom env
|
||||
virtual SSIZE_T PositionedReadInternal(char* src, size_t numBytes,
|
||||
uint64_t offset) const;
|
||||
|
||||
public:
|
||||
WinSequentialFile(const std::string& fname, HANDLE f,
|
||||
const EnvOptions& options);
|
||||
|
||||
~WinSequentialFile();
|
||||
|
||||
WinSequentialFile(const WinSequentialFile&) = delete;
|
||||
WinSequentialFile& operator=(const WinSequentialFile&) = delete;
|
||||
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) override;
|
||||
virtual Status PositionedRead(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) override;
|
||||
|
||||
virtual Status Skip(uint64_t n) override;
|
||||
|
||||
virtual Status InvalidateCache(size_t offset, size_t length) override;
|
||||
|
||||
virtual bool use_direct_io() const override { return WinFileData::use_direct_io(); }
|
||||
};
|
||||
|
||||
// mmap() based random-access
|
||||
class WinMmapReadableFile : private WinFileData, public RandomAccessFile {
|
||||
HANDLE hMap_;
|
||||
@@ -208,23 +234,6 @@ class WinMmapFile : private WinFileData, public WritableFile {
|
||||
virtual size_t GetUniqueId(char* id, size_t max_size) const override;
|
||||
};
|
||||
|
||||
class WinSequentialFile : private WinFileData, public SequentialFile {
|
||||
public:
|
||||
WinSequentialFile(const std::string& fname, HANDLE f,
|
||||
const EnvOptions& options);
|
||||
|
||||
~WinSequentialFile();
|
||||
|
||||
WinSequentialFile(const WinSequentialFile&) = delete;
|
||||
WinSequentialFile& operator=(const WinSequentialFile&) = delete;
|
||||
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) override;
|
||||
|
||||
virtual Status Skip(uint64_t n) override;
|
||||
|
||||
virtual Status InvalidateCache(size_t offset, size_t length) override;
|
||||
};
|
||||
|
||||
class WinRandomAccessImpl {
|
||||
protected:
|
||||
WinFileData* file_base_;
|
||||
@@ -281,14 +290,17 @@ class WinRandomAccessImpl {
|
||||
|
||||
virtual ~WinRandomAccessImpl() {}
|
||||
|
||||
public:
|
||||
WinRandomAccessImpl(const WinRandomAccessImpl&) = delete;
|
||||
WinRandomAccessImpl& operator=(const WinRandomAccessImpl&) = delete;
|
||||
|
||||
Status ReadImpl(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const;
|
||||
|
||||
void HintImpl(RandomAccessFile::AccessPattern pattern);
|
||||
|
||||
size_t GetAlignment() const { return buffer_.Alignment(); }
|
||||
|
||||
public:
|
||||
|
||||
WinRandomAccessImpl(const WinRandomAccessImpl&) = delete;
|
||||
WinRandomAccessImpl& operator=(const WinRandomAccessImpl&) = delete;
|
||||
};
|
||||
|
||||
// pread() based random-access
|
||||
@@ -303,18 +315,22 @@ class WinRandomAccessFile
|
||||
|
||||
~WinRandomAccessFile();
|
||||
|
||||
virtual void EnableReadAhead() override;
|
||||
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override;
|
||||
|
||||
virtual bool ShouldForwardRawRequest() const override;
|
||||
|
||||
virtual void EnableReadAhead() override;
|
||||
|
||||
virtual size_t GetUniqueId(char* id, size_t max_size) const override;
|
||||
|
||||
virtual void Hint(AccessPattern pattern) override;
|
||||
|
||||
virtual bool use_direct_io() const override { return WinFileData::use_direct_io(); }
|
||||
|
||||
virtual Status InvalidateCache(size_t offset, size_t length) override;
|
||||
|
||||
virtual size_t GetUniqueId(char* id, size_t max_size) const override;
|
||||
virtual size_t GetRequiredBufferAlignment() const override;
|
||||
};
|
||||
|
||||
// This is a sequential write class. It has been mimicked (as others) after
|
||||
@@ -382,12 +398,6 @@ class WinWritableFile : private WinFileData,
|
||||
|
||||
~WinWritableFile();
|
||||
|
||||
// Indicates if the class makes use of direct I/O
|
||||
// Use PositionedAppend
|
||||
virtual bool use_direct_io() const override;
|
||||
|
||||
virtual size_t GetRequiredBufferAlignment() const override;
|
||||
|
||||
virtual Status Append(const Slice& data) override;
|
||||
|
||||
// Requires that the data is aligned as specified by
|
||||
@@ -408,6 +418,12 @@ class WinWritableFile : private WinFileData,
|
||||
|
||||
virtual Status Fsync() override;
|
||||
|
||||
// Indicates if the class makes use of direct I/O
|
||||
// Use PositionedAppend
|
||||
virtual bool use_direct_io() const override;
|
||||
|
||||
virtual size_t GetRequiredBufferAlignment() const override;
|
||||
|
||||
virtual uint64_t GetFileSize() override;
|
||||
|
||||
virtual Status Allocate(uint64_t offset, uint64_t len) override;
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "port/win/win_thread.h"
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
#undef min
|
||||
@@ -206,6 +208,9 @@ class CondVar {
|
||||
Mutex* mu_;
|
||||
};
|
||||
|
||||
// Wrapper around the platform efficient
|
||||
// or otherwise preferrable implementation
|
||||
using Thread = WindowsThread;
|
||||
|
||||
// OnceInit type helps emulate
|
||||
// Posix semantics with initialization
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// 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 "port/win/win_thread.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <process.h> // __beginthreadex
|
||||
#include <Windows.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
|
||||
namespace rocksdb {
|
||||
namespace port {
|
||||
|
||||
struct WindowsThread::Data {
|
||||
|
||||
std::function<void()> func_;
|
||||
uintptr_t handle_;
|
||||
|
||||
Data(std::function<void()>&& func) :
|
||||
func_(std::move(func)),
|
||||
handle_(0) {
|
||||
}
|
||||
|
||||
Data(const Data&) = delete;
|
||||
Data& operator=(const Data&) = delete;
|
||||
|
||||
static unsigned int __stdcall ThreadProc(void* arg);
|
||||
};
|
||||
|
||||
|
||||
void WindowsThread::Init(std::function<void()>&& func) {
|
||||
|
||||
data_.reset(new Data(std::move(func)));
|
||||
|
||||
data_->handle_ = _beginthreadex(NULL,
|
||||
0, // stack size
|
||||
&Data::ThreadProc,
|
||||
data_.get(),
|
||||
0, // init flag
|
||||
&th_id_);
|
||||
|
||||
if (data_->handle_ == 0) {
|
||||
throw std::system_error(std::make_error_code(
|
||||
std::errc::resource_unavailable_try_again),
|
||||
"Unable to create a thread");
|
||||
}
|
||||
}
|
||||
|
||||
WindowsThread::WindowsThread() :
|
||||
data_(nullptr),
|
||||
th_id_(0)
|
||||
{}
|
||||
|
||||
|
||||
WindowsThread::~WindowsThread() {
|
||||
// Must be joined or detached
|
||||
// before destruction.
|
||||
// This is the same as std::thread
|
||||
if (data_) {
|
||||
if (joinable()) {
|
||||
assert(false);
|
||||
std::terminate();
|
||||
}
|
||||
data_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
WindowsThread::WindowsThread(WindowsThread&& o) noexcept :
|
||||
WindowsThread() {
|
||||
*this = std::move(o);
|
||||
}
|
||||
|
||||
WindowsThread& WindowsThread::operator=(WindowsThread&& o) noexcept {
|
||||
|
||||
if (joinable()) {
|
||||
assert(false);
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
data_ = std::move(o.data_);
|
||||
|
||||
// Per spec both instances will have the same id
|
||||
th_id_ = o.th_id_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool WindowsThread::joinable() const {
|
||||
return (data_ && data_->handle_ != 0);
|
||||
}
|
||||
|
||||
WindowsThread::native_handle_type WindowsThread::native_handle() const {
|
||||
return reinterpret_cast<native_handle_type>(data_->handle_);
|
||||
}
|
||||
|
||||
unsigned WindowsThread::hardware_concurrency() {
|
||||
return std::thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
void WindowsThread::join() {
|
||||
|
||||
if (!joinable()) {
|
||||
assert(false);
|
||||
throw std::system_error(
|
||||
std::make_error_code(std::errc::invalid_argument),
|
||||
"Thread is no longer joinable");
|
||||
}
|
||||
|
||||
if (GetThreadId(GetCurrentThread()) == th_id_) {
|
||||
assert(false);
|
||||
throw std::system_error(
|
||||
std::make_error_code(std::errc::resource_deadlock_would_occur),
|
||||
"Can not join itself");
|
||||
}
|
||||
|
||||
auto ret = WaitForSingleObject(reinterpret_cast<HANDLE>(data_->handle_),
|
||||
INFINITE);
|
||||
if (ret != WAIT_OBJECT_0) {
|
||||
auto lastError = GetLastError();
|
||||
assert(false);
|
||||
throw std::system_error(static_cast<int>(lastError),
|
||||
std::system_category(),
|
||||
"WaitForSingleObjectFailed");
|
||||
}
|
||||
|
||||
CloseHandle(reinterpret_cast<HANDLE>(data_->handle_));
|
||||
data_->handle_ = 0;
|
||||
}
|
||||
|
||||
bool WindowsThread::detach() {
|
||||
|
||||
if (!joinable()) {
|
||||
assert(false);
|
||||
throw std::system_error(
|
||||
std::make_error_code(std::errc::invalid_argument),
|
||||
"Thread is no longer available");
|
||||
}
|
||||
|
||||
BOOL ret = CloseHandle(reinterpret_cast<HANDLE>(data_->handle_));
|
||||
data_->handle_ = 0;
|
||||
|
||||
return (ret == TRUE);
|
||||
}
|
||||
|
||||
void WindowsThread::swap(WindowsThread& o) {
|
||||
data_.swap(o.data_);
|
||||
std::swap(th_id_, o.th_id_);
|
||||
}
|
||||
|
||||
unsigned int __stdcall WindowsThread::Data::ThreadProc(void* arg) {
|
||||
auto data = reinterpret_cast<WindowsThread::Data*>(arg);
|
||||
data->func_();
|
||||
_endthreadex(0);
|
||||
return 0;
|
||||
}
|
||||
} // namespace port
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// 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 <memory>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rocksdb {
|
||||
namespace port {
|
||||
|
||||
// This class is a replacement for std::thread
|
||||
// 2 reasons we do not like std::thread:
|
||||
// -- is that it dynamically allocates its internals that are automatically
|
||||
// freed when the thread terminates and not on the destruction of the
|
||||
// object. This makes it difficult to control the source of memory
|
||||
// allocation
|
||||
// - This implements Pimpl so we can easily replace the guts of the
|
||||
// object in our private version if necessary.
|
||||
class WindowsThread {
|
||||
|
||||
struct Data;
|
||||
|
||||
std::unique_ptr<Data> data_;
|
||||
unsigned int th_id_;
|
||||
|
||||
void Init(std::function<void()>&&);
|
||||
|
||||
public:
|
||||
|
||||
typedef void* native_handle_type;
|
||||
|
||||
// Construct with no thread
|
||||
WindowsThread();
|
||||
|
||||
// Template constructor
|
||||
//
|
||||
// This templated constructor accomplishes several things
|
||||
//
|
||||
// - Allows the class as whole to be not a template
|
||||
//
|
||||
// - take "universal" references to support both _lvalues and _rvalues
|
||||
//
|
||||
// - because this constructor is a catchall case in many respects it
|
||||
// may prevent us from using both the default __ctor, the move __ctor.
|
||||
// Also it may circumvent copy __ctor deletion. To work around this
|
||||
// we make sure this one has at least one argument and eliminate
|
||||
// it from the overload selection when WindowsThread is the first
|
||||
// argument.
|
||||
//
|
||||
// - construct with Fx(Ax...) with a variable number of types/arguments.
|
||||
//
|
||||
// - Gathers together the callable object with its arguments and constructs
|
||||
// a single callable entity
|
||||
//
|
||||
// - Makes use of std::function to convert it to a specification-template
|
||||
// dependent type that both checks the signature conformance to ensure
|
||||
// that all of the necessary arguments are provided and allows pimpl
|
||||
// implementation.
|
||||
template<class Fn,
|
||||
class... Args,
|
||||
class = typename std::enable_if<
|
||||
!std::is_same<typename std::decay<Fn>::type,
|
||||
WindowsThread>::value>::type>
|
||||
explicit WindowsThread(Fn&& fx, Args&&... ax) :
|
||||
WindowsThread() {
|
||||
|
||||
// Use binder to create a single callable entity
|
||||
auto binder = std::bind(std::forward<Fn>(fx),
|
||||
std::forward<Args>(ax)...);
|
||||
// Use std::function to take advantage of the type erasure
|
||||
// so we can still hide implementation within pimpl
|
||||
// This also makes sure that the binder signature is compliant
|
||||
std::function<void()> target = binder;
|
||||
|
||||
Init(std::move(target));
|
||||
}
|
||||
|
||||
|
||||
~WindowsThread();
|
||||
|
||||
WindowsThread(const WindowsThread&) = delete;
|
||||
|
||||
WindowsThread& operator=(const WindowsThread&) = delete;
|
||||
|
||||
WindowsThread(WindowsThread&&) noexcept;
|
||||
|
||||
WindowsThread& operator=(WindowsThread&&) noexcept;
|
||||
|
||||
bool joinable() const;
|
||||
|
||||
unsigned int get_id() const { return th_id_; }
|
||||
|
||||
native_handle_type native_handle() const;
|
||||
|
||||
static unsigned hardware_concurrency();
|
||||
|
||||
void join();
|
||||
|
||||
bool detach();
|
||||
|
||||
void swap(WindowsThread&);
|
||||
};
|
||||
} // namespace port
|
||||
} // namespace rocksdb
|
||||
|
||||
namespace std {
|
||||
inline
|
||||
void swap(rocksdb::port::WindowsThread& th1,
|
||||
rocksdb::port::WindowsThread& th2) {
|
||||
th1.swap(th2);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
@@ -74,7 +74,7 @@ LIB_SOURCES = \
|
||||
table/full_filter_block.cc \
|
||||
table/get_context.cc \
|
||||
table/iterator.cc \
|
||||
table/merger.cc \
|
||||
table/merging_iterator.cc \
|
||||
table/meta_blocks.cc \
|
||||
table/sst_file_writer.cc \
|
||||
table/plain_table_builder.cc \
|
||||
@@ -153,7 +153,6 @@ LIB_SOURCES = \
|
||||
utilities/document/json_document_builder.cc \
|
||||
utilities/document/json_document.cc \
|
||||
utilities/env_mirror.cc \
|
||||
utilities/env_registry.cc \
|
||||
utilities/geodb/geodb_impl.cc \
|
||||
utilities/leveldb_options/leveldb_options.cc \
|
||||
utilities/lua/rocks_lua_compaction_filter.cc \
|
||||
@@ -299,7 +298,7 @@ MAIN_SOURCES = \
|
||||
utilities/checkpoint/checkpoint_test.cc \
|
||||
utilities/document/document_db_test.cc \
|
||||
utilities/document/json_document_test.cc \
|
||||
utilities/env_registry_test.cc \
|
||||
utilities/object_registry_test.cc \
|
||||
utilities/geodb/geodb_test.cc \
|
||||
utilities/memory/memory_test.cc \
|
||||
utilities/merge_operators/string_append/stringappend_test.cc \
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -53,6 +54,14 @@ extern const std::string kHashIndexPrefixesBlock;
|
||||
extern const std::string kHashIndexPrefixesMetadataBlock;
|
||||
|
||||
typedef BlockBasedTableOptions::IndexType IndexType;
|
||||
class IndexBuilder;
|
||||
|
||||
namespace {
|
||||
rocksdb::IndexBuilder* CreateIndexBuilder(
|
||||
IndexType index_type, const InternalKeyComparator* comparator,
|
||||
const SliceTransform* prefix_extractor, int index_block_restart_interval,
|
||||
uint64_t index_per_partition);
|
||||
}
|
||||
|
||||
// The interface for building index.
|
||||
// Instruction for adding a new concrete IndexBuilder:
|
||||
@@ -101,7 +110,26 @@ class IndexBuilder {
|
||||
// may therefore perform any operation required for block finalization.
|
||||
//
|
||||
// REQUIRES: Finish() has not yet been called.
|
||||
virtual Status Finish(IndexBlocks* index_blocks) = 0;
|
||||
inline Status Finish(IndexBlocks* index_blocks) {
|
||||
// Throw away the changes to last_partition_block_handle. It has no effect
|
||||
// on the first call to Finish anyway.
|
||||
BlockHandle last_partition_block_handle;
|
||||
return Finish(index_blocks, last_partition_block_handle);
|
||||
}
|
||||
|
||||
// This override of Finish can be utilized to build the 2nd level index in
|
||||
// PartitionIndexBuilder.
|
||||
//
|
||||
// index_blocks will be filled with the resulting index data. If the return
|
||||
// value is Status::InComplete() then it means that the index is partitioned
|
||||
// and the callee should keep calling Finish until Status::OK() is returned.
|
||||
// In that case, last_partition_block_handle is pointer to the block written
|
||||
// with the result of the last call to Finish. This can be utilized to build
|
||||
// the second level index pointing to each block of partitioned indexes. The
|
||||
// last call to Finish() that returns Status::OK() populates index_blocks with
|
||||
// the 2nd level index content.
|
||||
virtual Status Finish(IndexBlocks* index_blocks,
|
||||
const BlockHandle& last_partition_block_handle) = 0;
|
||||
|
||||
// Get the estimated size for index block.
|
||||
virtual size_t EstimatedSize() const = 0;
|
||||
@@ -141,7 +169,9 @@ class ShortenedIndexBuilder : public IndexBuilder {
|
||||
index_block_builder_.Add(*last_key_in_current_block, handle_encoding);
|
||||
}
|
||||
|
||||
virtual Status Finish(IndexBlocks* index_blocks) override {
|
||||
virtual Status Finish(
|
||||
IndexBlocks* index_blocks,
|
||||
const BlockHandle& last_partition_block_handle) override {
|
||||
index_blocks->index_block_contents = index_block_builder_.Finish();
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -154,6 +184,108 @@ class ShortenedIndexBuilder : public IndexBuilder {
|
||||
BlockBuilder index_block_builder_;
|
||||
};
|
||||
|
||||
/**
|
||||
* IndexBuilder for two-level indexing. Internally it creates a new index for
|
||||
* each partition and Finish then in order when Finish is called on it
|
||||
* continiously until Status::OK() is returned.
|
||||
*
|
||||
* The format on the disk would be I I I I I I IP where I is block containing a
|
||||
* partition of indexes built using ShortenedIndexBuilder and IP is a block
|
||||
* containing a secondary index on the partitions, built using
|
||||
* ShortenedIndexBuilder.
|
||||
*/
|
||||
class PartitionIndexBuilder : public IndexBuilder {
|
||||
public:
|
||||
explicit PartitionIndexBuilder(const InternalKeyComparator* comparator,
|
||||
const SliceTransform* prefix_extractor,
|
||||
const uint64_t index_per_partition,
|
||||
int index_block_restart_interval)
|
||||
: IndexBuilder(comparator),
|
||||
prefix_extractor_(prefix_extractor),
|
||||
index_block_builder_(index_block_restart_interval),
|
||||
index_per_partition_(index_per_partition),
|
||||
index_block_restart_interval_(index_block_restart_interval) {
|
||||
sub_index_builder_ =
|
||||
CreateIndexBuilder(sub_type_, comparator_, prefix_extractor_,
|
||||
index_block_restart_interval_, index_per_partition_);
|
||||
}
|
||||
|
||||
virtual ~PartitionIndexBuilder() { delete sub_index_builder_; }
|
||||
|
||||
virtual void AddIndexEntry(std::string* last_key_in_current_block,
|
||||
const Slice* first_key_in_next_block,
|
||||
const BlockHandle& block_handle) override {
|
||||
sub_index_builder_->AddIndexEntry(last_key_in_current_block,
|
||||
first_key_in_next_block, block_handle);
|
||||
num_indexes++;
|
||||
if (UNLIKELY(first_key_in_next_block == nullptr)) { // no more keys
|
||||
entries_.push_back({std::string(*last_key_in_current_block),
|
||||
std::unique_ptr<IndexBuilder>(sub_index_builder_)});
|
||||
sub_index_builder_ = nullptr;
|
||||
} else if (num_indexes % index_per_partition_ == 0) {
|
||||
entries_.push_back({std::string(*last_key_in_current_block),
|
||||
std::unique_ptr<IndexBuilder>(sub_index_builder_)});
|
||||
sub_index_builder_ = CreateIndexBuilder(
|
||||
sub_type_, comparator_, prefix_extractor_,
|
||||
index_block_restart_interval_, index_per_partition_);
|
||||
}
|
||||
}
|
||||
|
||||
virtual Status Finish(
|
||||
IndexBlocks* index_blocks,
|
||||
const BlockHandle& last_partition_block_handle) override {
|
||||
assert(!entries_.empty());
|
||||
// It must be set to null after last key is added
|
||||
assert(sub_index_builder_ == nullptr);
|
||||
if (finishing == true) {
|
||||
Entry& last_entry = entries_.front();
|
||||
std::string handle_encoding;
|
||||
last_partition_block_handle.EncodeTo(&handle_encoding);
|
||||
index_block_builder_.Add(last_entry.key, handle_encoding);
|
||||
entries_.pop_front();
|
||||
}
|
||||
// If there is no sub_index left, then return the 2nd level index.
|
||||
if (UNLIKELY(entries_.empty())) {
|
||||
index_blocks->index_block_contents = index_block_builder_.Finish();
|
||||
return Status::OK();
|
||||
} else {
|
||||
// Finish the next partition index in line and Incomplete() to indicate we
|
||||
// expect more calls to Finish
|
||||
Entry& entry = entries_.front();
|
||||
auto s = entry.value->Finish(index_blocks);
|
||||
finishing = true;
|
||||
return s.ok() ? Status::Incomplete() : s;
|
||||
}
|
||||
}
|
||||
|
||||
virtual size_t EstimatedSize() const override {
|
||||
size_t total = 0;
|
||||
for (auto it = entries_.begin(); it != entries_.end(); ++it) {
|
||||
total += it->value->EstimatedSize();
|
||||
}
|
||||
total += index_block_builder_.CurrentSizeEstimate();
|
||||
total +=
|
||||
sub_index_builder_ == nullptr ? 0 : sub_index_builder_->EstimatedSize();
|
||||
return total;
|
||||
}
|
||||
|
||||
private:
|
||||
static const IndexType sub_type_ = BlockBasedTableOptions::kBinarySearch;
|
||||
struct Entry {
|
||||
std::string key;
|
||||
std::unique_ptr<IndexBuilder> value;
|
||||
};
|
||||
std::list<Entry> entries_; // list of partitioned indexes and their keys
|
||||
const SliceTransform* prefix_extractor_;
|
||||
BlockBuilder index_block_builder_; // top-level index builder
|
||||
IndexBuilder* sub_index_builder_; // the active partition index builder
|
||||
uint64_t index_per_partition_;
|
||||
int index_block_restart_interval_;
|
||||
uint64_t num_indexes = 0;
|
||||
bool finishing =
|
||||
false; // true if Finish is called once but not complete yet.
|
||||
};
|
||||
|
||||
// HashIndexBuilder contains a binary-searchable primary index and the
|
||||
// metadata for secondary hash index construction.
|
||||
// The metadata for hash index consists two parts:
|
||||
@@ -222,9 +354,11 @@ class HashIndexBuilder : public IndexBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
virtual Status Finish(IndexBlocks* index_blocks) override {
|
||||
virtual Status Finish(
|
||||
IndexBlocks* index_blocks,
|
||||
const BlockHandle& last_partition_block_handle) override {
|
||||
FlushPendingPrefix();
|
||||
primary_index_builder_.Finish(index_blocks);
|
||||
primary_index_builder_.Finish(index_blocks, last_partition_block_handle);
|
||||
index_blocks->meta_blocks.insert(
|
||||
{kHashIndexPrefixesBlock.c_str(), prefix_block_});
|
||||
index_blocks->meta_blocks.insert(
|
||||
@@ -269,11 +403,12 @@ class HashIndexBuilder : public IndexBuilder {
|
||||
namespace {
|
||||
|
||||
// Create a index builder based on its type.
|
||||
IndexBuilder* CreateIndexBuilder(IndexType type,
|
||||
IndexBuilder* CreateIndexBuilder(IndexType index_type,
|
||||
const InternalKeyComparator* comparator,
|
||||
const SliceTransform* prefix_extractor,
|
||||
int index_block_restart_interval) {
|
||||
switch (type) {
|
||||
int index_block_restart_interval,
|
||||
uint64_t index_per_partition) {
|
||||
switch (index_type) {
|
||||
case BlockBasedTableOptions::kBinarySearch: {
|
||||
return new ShortenedIndexBuilder(comparator,
|
||||
index_block_restart_interval);
|
||||
@@ -282,6 +417,11 @@ IndexBuilder* CreateIndexBuilder(IndexType type,
|
||||
return new HashIndexBuilder(comparator, prefix_extractor,
|
||||
index_block_restart_interval);
|
||||
}
|
||||
case BlockBasedTableOptions::kTwoLevelIndexSearch: {
|
||||
return new PartitionIndexBuilder(comparator, prefix_extractor,
|
||||
index_per_partition,
|
||||
index_block_restart_interval);
|
||||
}
|
||||
default: {
|
||||
assert(!"Do not recognize the index type ");
|
||||
return nullptr;
|
||||
@@ -512,7 +652,8 @@ struct BlockBasedTableBuilder::Rep {
|
||||
index_builder(
|
||||
CreateIndexBuilder(table_options.index_type, &internal_comparator,
|
||||
&this->internal_prefix_transform,
|
||||
table_options.index_block_restart_interval)),
|
||||
table_options.index_block_restart_interval,
|
||||
table_options.index_per_partition)),
|
||||
compression_type(_compression_type),
|
||||
compression_opts(_compression_opts),
|
||||
compression_dict(_compression_dict),
|
||||
@@ -852,9 +993,14 @@ Status BlockBasedTableBuilder::Finish() {
|
||||
}
|
||||
|
||||
IndexBuilder::IndexBlocks index_blocks;
|
||||
auto s = r->index_builder->Finish(&index_blocks);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
auto index_builder_status = r->index_builder->Finish(&index_blocks);
|
||||
if (index_builder_status.IsIncomplete()) {
|
||||
// We we have more than one index partition then meta_blocks are not
|
||||
// supported for the index. Currently meta_blocks are used only by
|
||||
// HashIndexBuilder which is not multi-partition.
|
||||
assert(index_blocks.meta_blocks.empty());
|
||||
} else if (!index_builder_status.ok()) {
|
||||
return index_builder_status;
|
||||
}
|
||||
|
||||
// Write meta blocks and metaindex block with the following order.
|
||||
@@ -956,8 +1102,21 @@ Status BlockBasedTableBuilder::Finish() {
|
||||
// flush the meta index block
|
||||
WriteRawBlock(meta_index_builder.Finish(), kNoCompression,
|
||||
&metaindex_block_handle);
|
||||
|
||||
const bool is_data_block = true;
|
||||
WriteBlock(index_blocks.index_block_contents, &index_block_handle,
|
||||
false /* is_data_block */);
|
||||
!is_data_block);
|
||||
// If there are more index partitions, finish them and write them out
|
||||
Status& s = index_builder_status;
|
||||
while (s.IsIncomplete()) {
|
||||
s = r->index_builder->Finish(&index_blocks, index_block_handle);
|
||||
if (!s.ok() && !s.IsIncomplete()) {
|
||||
return s;
|
||||
}
|
||||
WriteBlock(index_blocks.index_block_contents, &index_block_handle,
|
||||
!is_data_block);
|
||||
// The last index_block_handle will be for the partition index block
|
||||
}
|
||||
}
|
||||
|
||||
// Write footer
|
||||
|
||||
@@ -153,8 +153,12 @@ class BlockBasedTable::IndexReader {
|
||||
virtual ~IndexReader() {}
|
||||
|
||||
// Create an iterator for index access.
|
||||
// An iter is passed in, if it is not null, update this one and return it
|
||||
// If it is null, create a new Iterator
|
||||
// If iter is null then a new object is created on heap and the callee will
|
||||
// have the ownership. If a non-null iter is passed in it will be used, and
|
||||
// the returned value is either the same as iter or a new on-heap object that
|
||||
// wrapps the passed iter. In the latter case the return value would point to
|
||||
// a different object then iter and the callee has the ownership of the
|
||||
// returned object.
|
||||
virtual InternalIterator* NewIterator(BlockIter* iter = nullptr,
|
||||
bool total_order_seek = true) = 0;
|
||||
|
||||
@@ -175,6 +179,63 @@ class BlockBasedTable::IndexReader {
|
||||
Statistics* statistics_;
|
||||
};
|
||||
|
||||
// Index that allows binary search lookup in a two-level index structure.
|
||||
class PartitionIndexReader : public IndexReader {
|
||||
public:
|
||||
// Read the partition index from the file and create an instance for
|
||||
// `PartitionIndexReader`.
|
||||
// On success, index_reader will be populated; otherwise it will remain
|
||||
// unmodified.
|
||||
static Status Create(BlockBasedTable* table, RandomAccessFileReader* file,
|
||||
const Footer& footer, const BlockHandle& index_handle,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const Comparator* comparator, IndexReader** index_reader,
|
||||
const PersistentCacheOptions& cache_options) {
|
||||
std::unique_ptr<Block> index_block;
|
||||
auto s = ReadBlockFromFile(
|
||||
file, footer, ReadOptions(), index_handle, &index_block, ioptions,
|
||||
true /* decompress */, Slice() /*compression dict*/, cache_options,
|
||||
kDisableGlobalSequenceNumber, 0 /* read_amp_bytes_per_bit */);
|
||||
|
||||
if (s.ok()) {
|
||||
*index_reader = new PartitionIndexReader(
|
||||
table, comparator, std::move(index_block), ioptions.statistics);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// return a two-level iterator: first level is on the partition index
|
||||
virtual InternalIterator* NewIterator(BlockIter* iter = nullptr,
|
||||
bool dont_care = true) override {
|
||||
return NewTwoLevelIterator(
|
||||
new BlockBasedTable::BlockEntryIteratorState(table_, ReadOptions(),
|
||||
false),
|
||||
index_block_->NewIterator(comparator_, iter, true));
|
||||
}
|
||||
|
||||
virtual size_t size() const override { return index_block_->size(); }
|
||||
virtual size_t usable_size() const override {
|
||||
return index_block_->usable_size();
|
||||
}
|
||||
|
||||
virtual size_t ApproximateMemoryUsage() const override {
|
||||
assert(index_block_);
|
||||
return index_block_->ApproximateMemoryUsage();
|
||||
}
|
||||
|
||||
private:
|
||||
PartitionIndexReader(BlockBasedTable* table, const Comparator* comparator,
|
||||
std::unique_ptr<Block>&& index_block, Statistics* stats)
|
||||
: IndexReader(comparator, stats),
|
||||
table_(table),
|
||||
index_block_(std::move(index_block)) {
|
||||
assert(index_block_ != nullptr);
|
||||
}
|
||||
BlockBasedTable* table_;
|
||||
std::unique_ptr<Block> index_block_;
|
||||
};
|
||||
|
||||
// Index that allows binary search lookup for the first key of each block.
|
||||
// This class can be viewed as a thin wrapper for `Block` class which already
|
||||
// supports binary search.
|
||||
@@ -1352,33 +1413,27 @@ Status BlockBasedTable::MaybeLoadDataBlockToCache(
|
||||
return s;
|
||||
}
|
||||
|
||||
class BlockBasedTable::BlockEntryIteratorState : public TwoLevelIteratorState {
|
||||
public:
|
||||
BlockEntryIteratorState(BlockBasedTable* table,
|
||||
const ReadOptions& read_options, bool skip_filters)
|
||||
: TwoLevelIteratorState(table->rep_->ioptions.prefix_extractor !=
|
||||
nullptr),
|
||||
table_(table),
|
||||
read_options_(read_options),
|
||||
skip_filters_(skip_filters) {}
|
||||
BlockBasedTable::BlockEntryIteratorState::BlockEntryIteratorState(
|
||||
BlockBasedTable* table, const ReadOptions& read_options, bool skip_filters)
|
||||
: TwoLevelIteratorState(table->rep_->ioptions.prefix_extractor != nullptr),
|
||||
table_(table),
|
||||
read_options_(read_options),
|
||||
skip_filters_(skip_filters) {}
|
||||
|
||||
InternalIterator* NewSecondaryIterator(const Slice& index_value) override {
|
||||
return NewDataBlockIterator(table_->rep_, read_options_, index_value);
|
||||
InternalIterator*
|
||||
BlockBasedTable::BlockEntryIteratorState::NewSecondaryIterator(
|
||||
const Slice& index_value) {
|
||||
// Return a block iterator on the index partition
|
||||
return NewDataBlockIterator(table_->rep_, read_options_, index_value);
|
||||
}
|
||||
|
||||
bool BlockBasedTable::BlockEntryIteratorState::PrefixMayMatch(
|
||||
const Slice& internal_key) {
|
||||
if (read_options_.total_order_seek || skip_filters_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrefixMayMatch(const Slice& internal_key) override {
|
||||
if (read_options_.total_order_seek || skip_filters_) {
|
||||
return true;
|
||||
}
|
||||
return table_->PrefixMayMatch(internal_key);
|
||||
}
|
||||
|
||||
private:
|
||||
// Don't own table_
|
||||
BlockBasedTable* table_;
|
||||
const ReadOptions read_options_;
|
||||
bool skip_filters_;
|
||||
};
|
||||
return table_->PrefixMayMatch(internal_key);
|
||||
}
|
||||
|
||||
// This will be broken if the user specifies an unusual implementation
|
||||
// of Options.comparator, or if the user specifies an unusual
|
||||
@@ -1489,13 +1544,30 @@ InternalIterator* BlockBasedTable::NewIterator(const ReadOptions& read_options,
|
||||
InternalIterator* BlockBasedTable::NewRangeTombstoneIterator(
|
||||
const ReadOptions& read_options) {
|
||||
if (rep_->range_del_handle.IsNull()) {
|
||||
// The block didn't exist, nullptr indicates no range tombstones.
|
||||
return nullptr;
|
||||
}
|
||||
if (rep_->range_del_entry.cache_handle != nullptr) {
|
||||
// We have a handle to an uncompressed block cache entry that's held for
|
||||
// this table's lifetime. Increment its refcount before returning an
|
||||
// iterator based on it since the returned iterator may outlive this table
|
||||
// reader.
|
||||
assert(rep_->range_del_entry.value != nullptr);
|
||||
Cache* block_cache = rep_->table_options.block_cache.get();
|
||||
assert(block_cache != nullptr);
|
||||
if (block_cache->Ref(rep_->range_del_entry.cache_handle)) {
|
||||
auto iter = rep_->range_del_entry.value->NewIterator(
|
||||
&rep_->internal_comparator, nullptr /* iter */,
|
||||
true /* total_order_seek */, rep_->ioptions.statistics);
|
||||
iter->RegisterCleanup(&ReleaseCachedEntry, block_cache,
|
||||
rep_->range_del_entry.cache_handle);
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
std::string str;
|
||||
rep_->range_del_handle.EncodeTo(&str);
|
||||
// Even though range_del_entry already references the meta-block when block
|
||||
// cache is enabled, we still call the below function to get another reference
|
||||
// since the caller may need the iterator beyond this table reader's lifetime.
|
||||
// The meta-block exists but isn't in uncompressed block cache (maybe because
|
||||
// it is disabled), so go through the full lookup process.
|
||||
return NewDataBlockIterator(rep_, read_options, Slice(str));
|
||||
}
|
||||
|
||||
@@ -1534,15 +1606,19 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
||||
if (!FullFilterKeyMayMatch(read_options, filter, key)) {
|
||||
RecordTick(rep_->ioptions.statistics, BLOOM_FILTER_USEFUL);
|
||||
} else {
|
||||
BlockIter iiter;
|
||||
NewIndexIterator(read_options, &iiter);
|
||||
BlockIter iiter_on_stack;
|
||||
auto iiter = NewIndexIterator(read_options, &iiter_on_stack);
|
||||
std::unique_ptr<InternalIterator> iiter_unique_ptr;
|
||||
if (iiter != &iiter_on_stack) {
|
||||
iiter_unique_ptr = std::unique_ptr<InternalIterator>(iiter);
|
||||
}
|
||||
|
||||
PinnedIteratorsManager* pinned_iters_mgr = get_context->pinned_iters_mgr();
|
||||
bool pin_blocks = pinned_iters_mgr && pinned_iters_mgr->PinningEnabled();
|
||||
|
||||
bool done = false;
|
||||
for (iiter.Seek(key); iiter.Valid() && !done; iiter.Next()) {
|
||||
Slice handle_value = iiter.value();
|
||||
for (iiter->Seek(key); iiter->Valid() && !done; iiter->Next()) {
|
||||
Slice handle_value = iiter->value();
|
||||
|
||||
BlockHandle handle;
|
||||
bool not_exist_in_filter =
|
||||
@@ -1558,7 +1634,7 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
||||
break;
|
||||
} else {
|
||||
BlockIter biter;
|
||||
NewDataBlockIterator(rep_, read_options, iiter.value(), &biter);
|
||||
NewDataBlockIterator(rep_, read_options, iiter->value(), &biter);
|
||||
|
||||
if (read_options.read_tier == kBlockCacheTier &&
|
||||
biter.status().IsIncomplete()) {
|
||||
@@ -1594,7 +1670,7 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = iiter.status();
|
||||
s = iiter->status();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1615,22 +1691,26 @@ Status BlockBasedTable::Prefetch(const Slice* const begin,
|
||||
return Status::InvalidArgument(*begin, *end);
|
||||
}
|
||||
|
||||
BlockIter iiter;
|
||||
NewIndexIterator(ReadOptions(), &iiter);
|
||||
BlockIter iiter_on_stack;
|
||||
auto iiter = NewIndexIterator(ReadOptions(), &iiter_on_stack);
|
||||
std::unique_ptr<InternalIterator> iiter_unique_ptr;
|
||||
if (iiter != &iiter_on_stack) {
|
||||
iiter_unique_ptr = std::unique_ptr<InternalIterator>(iiter);
|
||||
}
|
||||
|
||||
if (!iiter.status().ok()) {
|
||||
if (!iiter->status().ok()) {
|
||||
// error opening index iterator
|
||||
return iiter.status();
|
||||
return iiter->status();
|
||||
}
|
||||
|
||||
// indicates if we are on the last page that need to be pre-fetched
|
||||
bool prefetching_boundary_page = false;
|
||||
|
||||
for (begin ? iiter.Seek(*begin) : iiter.SeekToFirst(); iiter.Valid();
|
||||
iiter.Next()) {
|
||||
Slice block_handle = iiter.value();
|
||||
for (begin ? iiter->Seek(*begin) : iiter->SeekToFirst(); iiter->Valid();
|
||||
iiter->Next()) {
|
||||
Slice block_handle = iiter->value();
|
||||
|
||||
if (end && comparator.Compare(iiter.key(), *end) >= 0) {
|
||||
if (end && comparator.Compare(iiter->key(), *end) >= 0) {
|
||||
if (prefetching_boundary_page) {
|
||||
break;
|
||||
}
|
||||
@@ -1720,6 +1800,11 @@ Status BlockBasedTable::CreateIndexReader(
|
||||
}
|
||||
|
||||
switch (index_type_on_file) {
|
||||
case BlockBasedTableOptions::kTwoLevelIndexSearch: {
|
||||
return PartitionIndexReader::Create(
|
||||
this, file, footer, footer.index_handle(), rep_->ioptions, comparator,
|
||||
index_reader, rep_->persistent_cache_options);
|
||||
}
|
||||
case BlockBasedTableOptions::kBinarySearch: {
|
||||
return BinarySearchIndexReader::Create(
|
||||
file, footer, footer.index_handle(), rep_->ioptions, comparator,
|
||||
@@ -1946,6 +2031,21 @@ Status BlockBasedTable::DumpTable(WritableFile* out_file) {
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Output compression dictionary
|
||||
if (rep_->compression_dict_block != nullptr) {
|
||||
auto compression_dict = rep_->compression_dict_block->data;
|
||||
out_file->Append(
|
||||
"Compression Dictionary:\n"
|
||||
"--------------------------------------\n");
|
||||
out_file->Append(" size (bytes): ");
|
||||
out_file->Append(rocksdb::ToString(compression_dict.size()));
|
||||
out_file->Append("\n\n");
|
||||
out_file->Append(" HEX ");
|
||||
out_file->Append(compression_dict.ToString(true).c_str());
|
||||
out_file->Append("\n\n");
|
||||
}
|
||||
|
||||
// Output range deletions block
|
||||
auto* range_del_iter = NewRangeTombstoneIterator(ReadOptions());
|
||||
if (range_del_iter != nullptr) {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/table_properties_internal.h"
|
||||
#include "table/table_reader.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "util/cf_options.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
@@ -149,6 +150,8 @@ class BlockBasedTable : public TableReader {
|
||||
// The key retrieved are internal keys.
|
||||
Status GetKVPairsFromDataBlocks(std::vector<KVPairBlock>* kv_pair_blocks);
|
||||
|
||||
class BlockEntryIteratorState;
|
||||
|
||||
private:
|
||||
template <class TValue>
|
||||
struct CachableEntry;
|
||||
@@ -157,7 +160,6 @@ class BlockBasedTable : public TableReader {
|
||||
Rep* rep_;
|
||||
bool compaction_optimized_;
|
||||
|
||||
class BlockEntryIteratorState;
|
||||
// input_iter: if it is not null, update this one and return it as Iterator
|
||||
static InternalIterator* NewDataBlockIterator(
|
||||
Rep* rep, const ReadOptions& ro, const Slice& index_value,
|
||||
@@ -273,4 +275,19 @@ class BlockBasedTable : public TableReader {
|
||||
void operator=(const TableReader&) = delete;
|
||||
};
|
||||
|
||||
// Maitaning state of a two-level iteration on a partitioned index structure
|
||||
class BlockBasedTable::BlockEntryIteratorState : public TwoLevelIteratorState {
|
||||
public:
|
||||
BlockEntryIteratorState(BlockBasedTable* table,
|
||||
const ReadOptions& read_options, bool skip_filters);
|
||||
InternalIterator* NewSecondaryIterator(const Slice& index_value) override;
|
||||
bool PrefixMayMatch(const Slice& internal_key) override;
|
||||
|
||||
private:
|
||||
// Don't own table_
|
||||
BlockBasedTable* table_;
|
||||
const ReadOptions read_options_;
|
||||
bool skip_filters_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
File renamed without changes.
+54
-28
@@ -21,6 +21,7 @@
|
||||
#include "db/memtable.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "memtable/stl_wrappers.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -995,6 +996,7 @@ class TableTest : public testing::Test {
|
||||
}
|
||||
return *plain_internal_comparator;
|
||||
}
|
||||
void IndexTest(BlockBasedTableOptions table_options);
|
||||
|
||||
private:
|
||||
std::unique_ptr<InternalKeyComparator> plain_internal_comparator;
|
||||
@@ -1382,13 +1384,18 @@ TEST_F(BlockBasedTableTest, TotalOrderSeekOnHashIndex) {
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(4));
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
// Hash search index with filter policy
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(4));
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
// Binary search index
|
||||
table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
break;
|
||||
}
|
||||
|
||||
TableConstructor c(BytewiseComparator(),
|
||||
@@ -1527,7 +1534,7 @@ void AddInternalKey(TableConstructor* c, const std::string& prefix,
|
||||
c->Add(k.Encode().ToString(), "v");
|
||||
}
|
||||
|
||||
TEST_F(TableTest, HashIndexTest) {
|
||||
void TableTest::IndexTest(BlockBasedTableOptions table_options) {
|
||||
TableConstructor c(BytewiseComparator());
|
||||
|
||||
// keys with prefix length 3, make sure the key/value is big enough to fill
|
||||
@@ -1551,9 +1558,6 @@ TEST_F(TableTest, HashIndexTest) {
|
||||
stl_wrappers::KVMap kvmap;
|
||||
Options options;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
table_options.hash_index_allow_collision = true;
|
||||
table_options.block_size = 1700;
|
||||
table_options.block_cache = NewLRUCache(1024, 4);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
@@ -1567,7 +1571,7 @@ TEST_F(TableTest, HashIndexTest) {
|
||||
auto props = reader->GetTableProperties();
|
||||
ASSERT_EQ(5u, props->num_data_blocks);
|
||||
|
||||
std::unique_ptr<InternalIterator> hash_iter(
|
||||
std::unique_ptr<InternalIterator> index_iter(
|
||||
reader->NewIterator(ReadOptions()));
|
||||
|
||||
// -- Find keys do not exist, but have common prefix.
|
||||
@@ -1577,13 +1581,13 @@ TEST_F(TableTest, HashIndexTest) {
|
||||
|
||||
// find the lower bound of the prefix
|
||||
for (size_t i = 0; i < prefixes.size(); ++i) {
|
||||
hash_iter->Seek(InternalKey(prefixes[i], 0, kTypeValue).Encode());
|
||||
ASSERT_OK(hash_iter->status());
|
||||
ASSERT_TRUE(hash_iter->Valid());
|
||||
index_iter->Seek(InternalKey(prefixes[i], 0, kTypeValue).Encode());
|
||||
ASSERT_OK(index_iter->status());
|
||||
ASSERT_TRUE(index_iter->Valid());
|
||||
|
||||
// seek the first element in the block
|
||||
ASSERT_EQ(lower_bound[i], hash_iter->key().ToString());
|
||||
ASSERT_EQ("v", hash_iter->value().ToString());
|
||||
ASSERT_EQ(lower_bound[i], index_iter->key().ToString());
|
||||
ASSERT_EQ("v", index_iter->value().ToString());
|
||||
}
|
||||
|
||||
// find the upper bound of prefixes
|
||||
@@ -1592,46 +1596,46 @@ TEST_F(TableTest, HashIndexTest) {
|
||||
// find existing keys
|
||||
for (const auto& item : kvmap) {
|
||||
auto ukey = ExtractUserKey(item.first).ToString();
|
||||
hash_iter->Seek(ukey);
|
||||
index_iter->Seek(ukey);
|
||||
|
||||
// ASSERT_OK(regular_iter->status());
|
||||
ASSERT_OK(hash_iter->status());
|
||||
ASSERT_OK(index_iter->status());
|
||||
|
||||
// ASSERT_TRUE(regular_iter->Valid());
|
||||
ASSERT_TRUE(hash_iter->Valid());
|
||||
ASSERT_TRUE(index_iter->Valid());
|
||||
|
||||
ASSERT_EQ(item.first, hash_iter->key().ToString());
|
||||
ASSERT_EQ(item.second, hash_iter->value().ToString());
|
||||
ASSERT_EQ(item.first, index_iter->key().ToString());
|
||||
ASSERT_EQ(item.second, index_iter->value().ToString());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < prefixes.size(); ++i) {
|
||||
// the key is greater than any existing keys.
|
||||
auto key = prefixes[i] + "9";
|
||||
hash_iter->Seek(InternalKey(key, 0, kTypeValue).Encode());
|
||||
index_iter->Seek(InternalKey(key, 0, kTypeValue).Encode());
|
||||
|
||||
ASSERT_OK(hash_iter->status());
|
||||
ASSERT_OK(index_iter->status());
|
||||
if (i == prefixes.size() - 1) {
|
||||
// last key
|
||||
ASSERT_TRUE(!hash_iter->Valid());
|
||||
ASSERT_TRUE(!index_iter->Valid());
|
||||
} else {
|
||||
ASSERT_TRUE(hash_iter->Valid());
|
||||
ASSERT_TRUE(index_iter->Valid());
|
||||
// seek the first element in the block
|
||||
ASSERT_EQ(upper_bound[i], hash_iter->key().ToString());
|
||||
ASSERT_EQ("v", hash_iter->value().ToString());
|
||||
ASSERT_EQ(upper_bound[i], index_iter->key().ToString());
|
||||
ASSERT_EQ("v", index_iter->value().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// find keys with prefix that don't match any of the existing prefixes.
|
||||
std::vector<std::string> non_exist_prefixes = {"002", "004", "006", "008"};
|
||||
for (const auto& prefix : non_exist_prefixes) {
|
||||
hash_iter->Seek(InternalKey(prefix, 0, kTypeValue).Encode());
|
||||
index_iter->Seek(InternalKey(prefix, 0, kTypeValue).Encode());
|
||||
// regular_iter->Seek(prefix);
|
||||
|
||||
ASSERT_OK(hash_iter->status());
|
||||
ASSERT_OK(index_iter->status());
|
||||
// Seek to non-existing prefixes should yield either invalid, or a
|
||||
// key with prefix greater than the target.
|
||||
if (hash_iter->Valid()) {
|
||||
Slice ukey = ExtractUserKey(hash_iter->key());
|
||||
if (index_iter->Valid()) {
|
||||
Slice ukey = ExtractUserKey(index_iter->key());
|
||||
Slice ukey_prefix = options.prefix_extractor->Transform(ukey);
|
||||
ASSERT_TRUE(BytewiseComparator()->Compare(prefix, ukey_prefix) < 0);
|
||||
}
|
||||
@@ -1639,6 +1643,28 @@ TEST_F(TableTest, HashIndexTest) {
|
||||
c.ResetTableReader();
|
||||
}
|
||||
|
||||
TEST_F(TableTest, BinaryIndexTest) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type = BlockBasedTableOptions::kBinarySearch;
|
||||
IndexTest(table_options);
|
||||
}
|
||||
|
||||
TEST_F(TableTest, HashIndexTest) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type = BlockBasedTableOptions::kHashSearch;
|
||||
IndexTest(table_options);
|
||||
}
|
||||
|
||||
TEST_F(TableTest, PartitionIndexTest) {
|
||||
const int max_index_keys = 5;
|
||||
for (int i = 1; i <= max_index_keys + 1; i++) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
|
||||
table_options.index_per_partition = i;
|
||||
IndexTest(table_options);
|
||||
}
|
||||
}
|
||||
|
||||
// It's very hard to figure out the index block size of a block accurately.
|
||||
// To make sure we get the index size, we just make sure as key number
|
||||
// grows, the filter block size also grows.
|
||||
@@ -2204,8 +2230,8 @@ TEST_F(BlockBasedTableTest, NewIndexIteratorLeak) {
|
||||
std::unique_ptr<InternalIterator> iter(reader->NewIterator(ro));
|
||||
};
|
||||
|
||||
auto thread1 = std::thread(func1);
|
||||
auto thread2 = std::thread(func2);
|
||||
auto thread1 = port::Thread(func1);
|
||||
auto thread2 = port::Thread(func2);
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
+26
-9
@@ -47,7 +47,7 @@
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/utilities/env_registry.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||
#include "rocksdb/utilities/options_util.h"
|
||||
#include "rocksdb/utilities/sim_cache.h"
|
||||
@@ -72,7 +72,6 @@
|
||||
#include <io.h> // open/close
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
using GFLAGS::ParseCommandLineFlags;
|
||||
using GFLAGS::RegisterFlagValidator;
|
||||
using GFLAGS::SetUsageMessage;
|
||||
@@ -459,6 +458,7 @@ DEFINE_bool(verify_checksum, false, "Verify checksum for every block read"
|
||||
" from storage");
|
||||
|
||||
DEFINE_bool(statistics, false, "Database statistics");
|
||||
DEFINE_string(statistics_string, "", "Serialized statistics string");
|
||||
static class std::shared_ptr<rocksdb::Statistics> dbstats;
|
||||
|
||||
DEFINE_int64(writes, -1, "Number of write operations to do. If negative, do"
|
||||
@@ -603,7 +603,7 @@ DEFINE_bool(use_stderr_info_logger, false,
|
||||
|
||||
DEFINE_bool(use_blob_db, false, "Whether to use BlobDB. ");
|
||||
|
||||
enum rocksdb::CompressionType StringToCompressionType(const char* ctype) {
|
||||
static enum rocksdb::CompressionType StringToCompressionType(const char* ctype) {
|
||||
assert(ctype);
|
||||
|
||||
if (!strcasecmp(ctype, "none"))
|
||||
@@ -627,7 +627,7 @@ enum rocksdb::CompressionType StringToCompressionType(const char* ctype) {
|
||||
return rocksdb::kSnappyCompression; // default value
|
||||
}
|
||||
|
||||
std::string ColumnFamilyName(size_t i) {
|
||||
static std::string ColumnFamilyName(size_t i) {
|
||||
if (i == 0) {
|
||||
return rocksdb::kDefaultColumnFamilyName;
|
||||
} else {
|
||||
@@ -871,7 +871,7 @@ enum RepFactory {
|
||||
kCuckoo
|
||||
};
|
||||
|
||||
enum RepFactory StringToRepFactory(const char* ctype) {
|
||||
static enum RepFactory StringToRepFactory(const char* ctype) {
|
||||
assert(ctype);
|
||||
|
||||
if (!strcasecmp(ctype, "skip_list"))
|
||||
@@ -939,7 +939,6 @@ static const bool FLAGS_deletepercent_dummy __attribute__((unused)) =
|
||||
static const bool FLAGS_table_cache_numshardbits_dummy __attribute__((unused)) =
|
||||
RegisterFlagValidator(&FLAGS_table_cache_numshardbits,
|
||||
&ValidateTableCacheNumshardbits);
|
||||
} // namespace
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -1212,7 +1211,7 @@ class ReporterAgent {
|
||||
abort();
|
||||
}
|
||||
|
||||
reporting_thread_ = std::thread([&]() { SleepAndReport(); });
|
||||
reporting_thread_ = port::Thread([&]() { SleepAndReport(); });
|
||||
}
|
||||
|
||||
~ReporterAgent() {
|
||||
@@ -1272,7 +1271,7 @@ class ReporterAgent {
|
||||
std::atomic<int64_t> total_ops_done_;
|
||||
int64_t last_report_;
|
||||
const uint64_t report_interval_secs_;
|
||||
std::thread reporting_thread_;
|
||||
rocksdb::port::Thread reporting_thread_;
|
||||
std::mutex mutex_;
|
||||
// will notify on stop
|
||||
std::condition_variable stop_cv_;
|
||||
@@ -4878,6 +4877,24 @@ int db_bench_tool(int argc, char** argv) {
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
FLAGS_compaction_style_e = (rocksdb::CompactionStyle) FLAGS_compaction_style;
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (FLAGS_statistics && !FLAGS_statistics_string.empty()) {
|
||||
fprintf(stderr,
|
||||
"Cannot provide both --statistics and --statistics_string.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!FLAGS_statistics_string.empty()) {
|
||||
std::unique_ptr<Statistics> custom_stats_guard;
|
||||
dbstats.reset(NewCustomObject<Statistics>(FLAGS_statistics_string,
|
||||
&custom_stats_guard));
|
||||
custom_stats_guard.release();
|
||||
if (dbstats == nullptr) {
|
||||
fprintf(stderr, "No Statistics registered matching string: %s\n",
|
||||
FLAGS_statistics_string.c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
if (FLAGS_statistics) {
|
||||
dbstats = rocksdb::CreateDBStatistics();
|
||||
}
|
||||
@@ -4903,7 +4920,7 @@ int db_bench_tool(int argc, char** argv) {
|
||||
fprintf(stderr, "Cannot provide both --hdfs and --env_uri.\n");
|
||||
exit(1);
|
||||
} else if (!FLAGS_env_uri.empty()) {
|
||||
FLAGS_env = NewEnvFromUri(FLAGS_env_uri, &custom_env_guard);
|
||||
FLAGS_env = NewCustomObject<Env>(FLAGS_env_uri, &custom_env_guard);
|
||||
if (FLAGS_env == nullptr) {
|
||||
fprintf(stderr, "No Env registered for URI: %s\n", FLAGS_env_uri.c_str());
|
||||
exit(1);
|
||||
|
||||
+4
-4
@@ -21,7 +21,7 @@
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/utilities/backupable_db.h"
|
||||
#include "rocksdb/utilities/env_registry.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
@@ -1770,7 +1770,7 @@ void ChangeCompactionStyleCommand::DoCommand() {
|
||||
// verify compaction result
|
||||
files_per_level = "";
|
||||
int num_files = 0;
|
||||
for (int i = 0; i < db_->NumberLevels(); i++) {
|
||||
for (int i = 0; i < db_->NumberLevels(GetCfHandle()); i++) {
|
||||
db_->GetProperty(GetCfHandle(),
|
||||
"rocksdb.num-files-at-level" + NumberToString(i),
|
||||
&property);
|
||||
@@ -2634,7 +2634,7 @@ void BackupCommand::DoCommand() {
|
||||
}
|
||||
printf("open db OK\n");
|
||||
std::unique_ptr<Env> custom_env_guard;
|
||||
Env* custom_env = NewEnvFromUri(test_cluster_, &custom_env_guard);
|
||||
Env* custom_env = NewCustomObject<Env>(test_cluster_, &custom_env_guard);
|
||||
BackupableDBOptions backup_options =
|
||||
BackupableDBOptions(test_path_, custom_env);
|
||||
backup_options.max_background_operations = thread_num_;
|
||||
@@ -2696,7 +2696,7 @@ void RestoreCommand::Help(std::string& ret) {
|
||||
|
||||
void RestoreCommand::DoCommand() {
|
||||
std::unique_ptr<Env> custom_env_guard;
|
||||
Env* custom_env = NewEnvFromUri(backup_env_uri_, &custom_env_guard);
|
||||
Env* custom_env = NewCustomObject<Env>(backup_env_uri_, &custom_env_guard);
|
||||
std::unique_ptr<BackupEngineReadOnly> restore_engine;
|
||||
Status status;
|
||||
{
|
||||
|
||||
+4
-4
@@ -29,15 +29,15 @@ void LDBCommandRunner::PrintHelp(const char* exec_name) {
|
||||
" : Values are input/output as hex\n");
|
||||
ret.append(" --" + LDBCommand::ARG_HEX +
|
||||
" : Both keys and values are input/output as hex\n");
|
||||
ret.append(
|
||||
" --" + LDBCommand::ARG_CF_NAME +
|
||||
" : name of the column family to operate on. default: default column "
|
||||
"family\n");
|
||||
ret.append("\n");
|
||||
|
||||
ret.append(
|
||||
"The following optional parameters control the database "
|
||||
"internals:\n");
|
||||
ret.append(
|
||||
" --" + LDBCommand::ARG_CF_NAME +
|
||||
"=<string> : name of the column family to operate on. default: default "
|
||||
"column family\n");
|
||||
ret.append(" --" + LDBCommand::ARG_TTL +
|
||||
" with 'put','get','scan','dump','query','batchput'"
|
||||
" : DB supports ttl and value is internally timestamp-suffixed\n");
|
||||
|
||||
@@ -66,12 +66,13 @@ int main() {
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "db/filename.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
#include "db/filename.h"
|
||||
|
||||
using GFLAGS::ParseCommandLineFlags;
|
||||
using GFLAGS::RegisterFlagValidator;
|
||||
@@ -290,7 +291,7 @@ class WriteStress {
|
||||
// frequently than the first one.
|
||||
std::atomic<char> key_prefix_[kPrefixSize];
|
||||
std::atomic<bool> stop_;
|
||||
std::vector<std::thread> threads_;
|
||||
std::vector<port::Thread> threads_;
|
||||
std::unique_ptr<DB> db_;
|
||||
};
|
||||
|
||||
|
||||
Loaded 100 of 145 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user