mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 22:55:23 +08:00
Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 849dca7221 | |||
| 1039133f2d | |||
| c7226428dd | |||
| d6fdd59c63 | |||
| 35d8e65a04 | |||
| d46e832e94 | |||
| edc258127e | |||
| ec0167eecb | |||
| 0ea7170d7d | |||
| 7e3d3326ce | |||
| 7291a3f813 | |||
| c615689bb5 | |||
| 6f5ba0bf5b | |||
| f2f034ef3b | |||
| 5568aec421 | |||
| 47ad6b81ff | |||
| f1cb83fcf4 | |||
| b9873162f0 | |||
| 4decff6fa8 | |||
| 46e599fc6b | |||
| 266d85fbec | |||
| dc360df81e | |||
| bf6f03f3cd | |||
| af92d4ad11 | |||
| d0f1b49ab6 | |||
| 68829ed89c | |||
| 43549c7d59 | |||
| ba295cda29 | |||
| e446d14093 | |||
| 6d7e3b9faf | |||
| 45828c7215 | |||
| 0a7ba0e548 | |||
| 9c2f64e148 | |||
| 204af1eccc | |||
| d4da02d147 | |||
| a53c571d2d | |||
| b010116d82 | |||
| 0c6e8be9e2 | |||
| 199405192d | |||
| bafec6bb30 | |||
| a478e85697 | |||
| 677f249d6d | |||
| 6aa95f4d0f | |||
| 0f0d2ab95a | |||
| 46ec52499e | |||
| 00b33c2474 | |||
| 24e2c1640d | |||
| 398d72fa61 | |||
| 30a017feca | |||
| 3e955fad09 | |||
| 84ddbd186a | |||
| 90c1d81975 | |||
| 1c9ada59cc | |||
| ea8ccd2267 | |||
| 00e5e1ef7f | |||
| ccc095a016 | |||
| b5c99cc908 | |||
| f00e176c5b | |||
| 02a2c11732 | |||
| 0ef3fdd732 | |||
| 58b841b356 | |||
| f54d7f5fea | |||
| e763e1b623 | |||
| 48cf8da2bb | |||
| 06149429d9 | |||
| 1563801bce | |||
| 2190e96727 | |||
| 0faa026db6 | |||
| 78c2eedb4f | |||
| aa6509d8e4 | |||
| a6d3c762df | |||
| fccc12f386 | |||
| 95583e1532 | |||
| 237b292515 | |||
| a79c7c05e8 | |||
| 5a7e08468a | |||
| b4d88d7128 | |||
| def6a00740 | |||
| 6b77c07379 | |||
| cd2e5cae7f | |||
| 546a63272f | |||
| 35dfbd58dd | |||
| 51c2ea0feb | |||
| 9089373a01 | |||
| 7393ef779c | |||
| 4bcb7fb148 | |||
| e3a06f12d2 | |||
| a9c8d4ef15 | |||
| bb5ed4b1d1 | |||
| 0d5692e02b | |||
| 2f1a3a4d74 |
+64
-32
@@ -14,7 +14,7 @@
|
||||
# cd build
|
||||
# 3. Run cmake to generate project files for Windows, add more options to enable required third-party libraries.
|
||||
# See thirdparty.inc for more information.
|
||||
# sample command: cmake -G "Visual Studio 14 Win64" -DGFLAGS=1 -DSNAPPY=1 -DJEMALLOC=1 -DJNI=1 ..
|
||||
# sample command: cmake -G "Visual Studio 15 Win64" -DWITH_GFLAGS=1 -DWITH_SNAPPY=1 -DWITH_JEMALLOC=1 -DWITH_JNI=1 ..
|
||||
# 4. Then build the project in debug mode (you may want to add /m[:<N>] flag to run msbuild in <N> parallel threads
|
||||
# or simply /m ot use all avail cores)
|
||||
# msbuild rocksdb.sln
|
||||
@@ -34,15 +34,25 @@
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(rocksdb)
|
||||
enable_language(CXX)
|
||||
enable_language(C)
|
||||
enable_language(ASM)
|
||||
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
|
||||
option(WITH_JEMALLOC "build with JeMalloc" OFF)
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
option(WITH_LZ4 "build with lz4" OFF)
|
||||
option(WITH_ZLIB "build with zlib" OFF)
|
||||
if(MSVC)
|
||||
# Defaults currently different for GFLAGS.
|
||||
# We will address find_package work a little later
|
||||
option(WITH_GFLAGS "build with GFlags" OFF)
|
||||
option(WITH_XPRESS "build with windows built in compression" OFF)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
@@ -56,8 +66,18 @@ else()
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# No config file for this
|
||||
option(WITH_GFLAGS "build with GFlags" ON)
|
||||
if(WITH_GFLAGS)
|
||||
find_package(gflags)
|
||||
if(gflags_FOUND)
|
||||
add_definitions(-DGFLAGS=1)
|
||||
include_directories(${gflags_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${gflags_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
add_definitions(-DSNAPPY)
|
||||
@@ -65,7 +85,6 @@ else()
|
||||
list(APPEND THIRDPARTY_LIBS ${SNAPPY_LIBRARIES})
|
||||
endif()
|
||||
|
||||
option(WITH_ZLIB "build with zlib" OFF)
|
||||
if(WITH_ZLIB)
|
||||
find_package(zlib REQUIRED)
|
||||
add_definitions(-DZLIB)
|
||||
@@ -81,7 +100,6 @@ else()
|
||||
list(APPEND THIRDPARTY_LIBS ${BZIP2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
option(WITH_LZ4 "build with lz4" OFF)
|
||||
if(WITH_LZ4)
|
||||
find_package(lz4 REQUIRED)
|
||||
add_definitions(-DLZ4)
|
||||
@@ -114,20 +132,19 @@ 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(SH_CMD "sh")
|
||||
execute_process(COMMAND
|
||||
${SH_CMD} -c "build_tools/version.sh full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION}" ROCKSDB_VERSION)
|
||||
execute_process(COMMAND
|
||||
${SH_CMD} -c "build_tools/version.sh major"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION_MAJOR
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
|
||||
option(WITH_MD_LIBRARY "build with MD" ON)
|
||||
if(WIN32 AND MSVC)
|
||||
@@ -163,6 +180,18 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
CHECK_C_COMPILER_FLAG("-maltivec" HAS_ALTIVEC)
|
||||
if(HAS_ALTIVEC)
|
||||
message(STATUS " HAS_ALTIVEC yes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maltivec")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maltivec")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=power8")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=power8")
|
||||
endif(HAS_ALTIVEC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
@@ -175,7 +204,9 @@ else()
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
if(NOT HAVE_POWER8)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -254,13 +285,6 @@ if(WITH_UBSAN)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(gflags)
|
||||
if(gflags_FOUND)
|
||||
add_definitions(-DGFLAGS=1)
|
||||
include_directories(${gflags_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${gflags_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_package(NUMA)
|
||||
if(NUMA_FOUND)
|
||||
add_definitions(-DNUMA)
|
||||
@@ -490,6 +514,7 @@ set(SOURCES
|
||||
table/block_based_table_factory.cc
|
||||
table/block_based_table_reader.cc
|
||||
table/block_builder.cc
|
||||
table/block_fetcher.cc
|
||||
table/block_prefix_index.cc
|
||||
table/bloom_block.cc
|
||||
table/cuckoo_table_builder.cc
|
||||
@@ -617,6 +642,12 @@ set_source_files_properties(
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_POWER8)
|
||||
list(APPEND SOURCES
|
||||
util/crc32c_ppc.c
|
||||
util/crc32c_ppc_asm.S)
|
||||
endif(HAVE_POWER8)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES
|
||||
port/win/io_win.cc
|
||||
@@ -711,7 +742,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
set(package_config_destination ${CMAKE_INSTALL_LIBDIR}/cmake/rocksdb)
|
||||
|
||||
configure_package_config_file(
|
||||
${CMAKE_SOURCE_DIR}/cmake/RocksDBConfig.cmake.in RocksDBConfig.cmake
|
||||
${CMAKE_CURRENT_LIST_DIR}/cmake/RocksDBConfig.cmake.in RocksDBConfig.cmake
|
||||
INSTALL_DESTINATION ${package_config_destination}
|
||||
)
|
||||
|
||||
@@ -941,17 +972,18 @@ if(WITH_TESTS)
|
||||
|
||||
foreach(sourcefile ${TEST_EXES})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
add_executable(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
$<TARGET_OBJECTS:testharness>)
|
||||
set_target_properties(${exename}${ARTIFACT_SUFFIX}
|
||||
set_target_properties(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX}
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
OUTPUT_NAME ${exename}${ARTIFACT_SUFFIX}
|
||||
)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} gtest ${LIBS})
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${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})
|
||||
add_dependencies(check ${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
endforeach(sourcefile ${TEST_EXES})
|
||||
|
||||
|
||||
+5
-10
@@ -1,14 +1,9 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.10.3 (02/21/2018)
|
||||
### Bug fixes
|
||||
* Fix build break regression using gcc-7
|
||||
* Direct I/O writable file should do fsync in Close()
|
||||
|
||||
### New Features
|
||||
* Add rocksdb.iterator.internal-key property
|
||||
|
||||
## 5.10.1 (01/18/2018)
|
||||
## Unreleased
|
||||
### Public API Change
|
||||
* Iterator::SeekForPrev is now a pure virtual method. This is to prevent user who implement the Iterator interface fail to implement SeekForPrev by mistake.
|
||||
### Bug Fixes
|
||||
* Fix `DisableFileDeletions()` followed by `GetSortedWalFiles()` to not return obsolete WAL files that `PurgeObsoleteFiles()` is going to delete.
|
||||
* Fix DB::Flush() keep waiting after flush finish under certain condition.
|
||||
|
||||
## 5.10.0 (12/11/2017)
|
||||
@@ -16,7 +11,6 @@
|
||||
* When running `make` with environment variable `USE_SSE` set and `PORTABLE` unset, will use all machine features available locally. Previously this combination only compiled SSE-related features.
|
||||
|
||||
### New Features
|
||||
* CRC32C is now using the 3-way pipelined SSE algorithm `crc32c_3way` on supported platforms to improve performance. The system will choose to use this algorithm on supported platforms automatically whenever possible. If PCLMULQDQ is not supported it will fall back to the old Fast_CRC32 algorithm.
|
||||
* Provide lifetime hints when writing files on Linux. This reduces hardware write-amp on storage devices supporting multiple streams.
|
||||
* Add a DB stat, `NUMBER_ITER_SKIP`, which returns how many internal keys were skipped during iterations (e.g., due to being tombstones or duplicate versions of a key).
|
||||
* Add PerfContext counters, `key_lock_wait_count` and `key_lock_wait_time`, which measure the number of times transactions wait on key locks and total amount of time waiting.
|
||||
@@ -38,6 +32,7 @@
|
||||
* Return an error on write if write_options.sync = true and write_options.disableWAL = true to warn user of inconsistent options. Previously we will not write to WAL and not respecting the sync options in this case.
|
||||
|
||||
### New Features
|
||||
* CRC32C is now using the 3-way pipelined SSE algorithm `crc32c_3way` on supported platforms to improve performance. The system will choose to use this algorithm on supported platforms automatically whenever possible. If PCLMULQDQ is not supported it will fall back to the old Fast_CRC32 algorithm.
|
||||
* `DBOptions::writable_file_max_buffer_size` can now be changed dynamically.
|
||||
* `DBOptions::bytes_per_sync`, `DBOptions::compaction_readahead_size`, and `DBOptions::wal_bytes_per_sync` can now be changed dynamically, `DBOptions::wal_bytes_per_sync` will flush all memtables and switch to a new WAL file.
|
||||
* Support dynamic adjustment of rate limit according to demand for background I/O. It can be enabled by passing `true` to the `auto_tuned` parameter in `NewGenericRateLimiter()`. The value passed as `rate_bytes_per_sec` will still be respected as an upper-bound.
|
||||
|
||||
@@ -509,6 +509,7 @@ PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_merge_operator_test \
|
||||
db_sst_test \
|
||||
db_test \
|
||||
db_universal_compaction_test \
|
||||
@@ -520,7 +521,7 @@ PARALLEL_TEST = \
|
||||
persistent_cache_test \
|
||||
table_test \
|
||||
transaction_test \
|
||||
write_prepared_transaction_test
|
||||
write_prepared_transaction_test \
|
||||
|
||||
SUBSET := $(TESTS)
|
||||
ifdef ROCKSDBTESTS_START
|
||||
@@ -1555,11 +1556,11 @@ BZIP2_DOWNLOAD_BASE ?= http://www.bzip.org
|
||||
SNAPPY_VER ?= 1.1.4
|
||||
SNAPPY_SHA256 ?= 134bfe122fd25599bb807bb8130e7ba6d9bdb851e0b16efcb83ac4f5d0b70057
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/releases/download
|
||||
LZ4_VER ?= 1.7.5
|
||||
LZ4_SHA256 ?= 0190cacd63022ccb86f44fa5041dc6c3804407ad61550ca21c382827319e7e7e
|
||||
LZ4_VER ?= 1.8.0
|
||||
LZ4_SHA256 ?= 2ca482ea7a9bb103603108b5a7510b7592b90158c151ff50a28f1ca8389fccf6
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.2.0
|
||||
ZSTD_SHA256 ?= 4a7e4593a3638276ca7f2a09dc4f38e674d8317bbea51626393ca73fc047cbfb
|
||||
ZSTD_VER ?= 1.3.3
|
||||
ZSTD_SHA256 ?= a77c47153ee7de02626c5b2a097005786b71688be61e9fb81806a011f90b297b
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](http://140.211.168.68:8080/job/Rocksdb)
|
||||
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
|
||||
@@ -157,6 +157,7 @@ cpp_library(
|
||||
"table/block_based_table_factory.cc",
|
||||
"table/block_based_table_reader.cc",
|
||||
"table/block_builder.cc",
|
||||
"table/block_fetcher.cc",
|
||||
"table/block_prefix_index.cc",
|
||||
"table/bloom_block.cc",
|
||||
"table/cuckoo_table_builder.cc",
|
||||
@@ -566,7 +567,7 @@ ROCKS_TESTS = [
|
||||
[
|
||||
"db_merge_operator_test",
|
||||
"db/db_merge_operator_test.cc",
|
||||
"serial",
|
||||
"parallel",
|
||||
],
|
||||
[
|
||||
"db_options_test",
|
||||
@@ -1016,7 +1017,7 @@ ROCKS_TESTS = [
|
||||
[
|
||||
"write_prepared_transaction_test",
|
||||
"utilities/transactions/write_prepared_transaction_test.cc",
|
||||
"serial",
|
||||
"parallel",
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ image: Visual Studio 2015
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 14 2015 Win64" -DOPTDBG=1 -DXPRESS=1 -DPORTABLE=1 ..
|
||||
- cmake -G "Visual Studio 14 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
|
||||
@@ -36,7 +36,7 @@ def parse_src_mk(repo_path):
|
||||
# get all .cc / .c files
|
||||
def get_cc_files(repo_path):
|
||||
cc_files = []
|
||||
for root, dirnames, filenames in os.walk(repo_path): # noqa: B007 T25377293 Grandfathered in
|
||||
for root, dirnames, filenames in os.walk(repo_path):
|
||||
root = root[(len(repo_path) + 1):]
|
||||
if "java" in root:
|
||||
# Skip java
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# Create a tmp directory for the test to use
|
||||
TEST_DIR=$(mktemp -d /dev/shm/fbcode_rocksdb_XXXXXXX)
|
||||
# shellcheck disable=SC2068
|
||||
TEST_TMPDIR="$TEST_DIR" $@ && rm -rf "$TEST_DIR"
|
||||
|
||||
@@ -13,12 +13,10 @@ error=0
|
||||
|
||||
function log {
|
||||
DATE=`date +%Y-%m-%d:%H:%M:%S`
|
||||
# shellcheck disable=SC2068
|
||||
echo $DATE $@
|
||||
}
|
||||
|
||||
function log_err {
|
||||
# shellcheck disable=SC2145
|
||||
log "ERROR: $@ Error code: $error."
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# shellcheck disable=SC2148
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/8219ec1bcedf8ad9da05e121e193364de2cc4f61/5.x/centos6-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/64d8d58e3d84f8bde7a029763d4f5baf39d0d5b9/stable/centos6-native/6aaf4de
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/ba9be983c81de7299b59fe71950c664a84dcb5f8/5.x/gcc-5-glibc-2.23/339d858
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# shellcheck disable=SC2148
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# shellcheck disable=SC1113
|
||||
#/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
@@ -29,14 +28,12 @@ function package() {
|
||||
if dpkg --get-selections | grep --quiet $1; then
|
||||
log "$1 is already installed. skipping."
|
||||
else
|
||||
# shellcheck disable=SC2068
|
||||
apt-get install $@ -y
|
||||
fi
|
||||
elif [[ $OS = "centos" ]]; then
|
||||
if rpm -qa | grep --quiet $1; then
|
||||
log "$1 is already installed. skipping."
|
||||
else
|
||||
# shellcheck disable=SC2068
|
||||
yum install $@ -y
|
||||
fi
|
||||
fi
|
||||
@@ -55,7 +52,6 @@ function gem_install() {
|
||||
if gem list | grep --quiet $1; then
|
||||
log "$1 is already installed. skipping."
|
||||
else
|
||||
# shellcheck disable=SC2068
|
||||
gem install $@
|
||||
fi
|
||||
}
|
||||
@@ -129,5 +125,4 @@ function main() {
|
||||
include $LIB_DIR
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
main $@
|
||||
|
||||
@@ -72,7 +72,7 @@ def display_file_coverage(per_file_coverage, total_coverage):
|
||||
header_template = \
|
||||
"%" + str(max_file_name_length) + "s\t%s\t%s"
|
||||
separator = "-" * (max_file_name_length + 10 + 20)
|
||||
print header_template % ("Filename", "Coverage", "Lines") # noqa: E999 T25377293 Grandfathered in
|
||||
print header_template % ("Filename", "Coverage", "Lines")
|
||||
print separator
|
||||
|
||||
# -- Print body
|
||||
|
||||
@@ -1999,6 +1999,11 @@ void rocksdb_options_optimize_universal_style_compaction(
|
||||
opt->rep.OptimizeUniversalStyleCompaction(memtable_memory_budget);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_allow_ingest_behind(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.allow_ingest_behind = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_compactionfilter_t* filter) {
|
||||
|
||||
+17
-21
@@ -344,13 +344,12 @@ void SuperVersionUnrefHandle(void* ptr) {
|
||||
// When latter happens, we are in ~ColumnFamilyData(), no get should happen as
|
||||
// well.
|
||||
SuperVersion* sv = static_cast<SuperVersion*>(ptr);
|
||||
bool was_last_ref __attribute__((__unused__));
|
||||
was_last_ref = sv->Unref();
|
||||
// Thread-local SuperVersions can't outlive ColumnFamilyData::super_version_.
|
||||
// This is important because we can't do SuperVersion cleanup here.
|
||||
// That would require locking DB mutex, which would deadlock because
|
||||
// SuperVersionUnrefHandle is called with locked ThreadLocalPtr mutex.
|
||||
assert(!was_last_ref);
|
||||
if (sv->Unref()) {
|
||||
sv->db_mutex->Lock();
|
||||
sv->Cleanup();
|
||||
sv->db_mutex->Unlock();
|
||||
delete sv;
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -967,12 +966,6 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
RecalculateWriteStallConditions(mutable_cf_options);
|
||||
|
||||
if (old_superversion != nullptr) {
|
||||
// Reset SuperVersions cached in thread local storage.
|
||||
// This should be done before old_superversion->Unref(). That's to ensure
|
||||
// that local_sv_ never holds the last reference to SuperVersion, since
|
||||
// it has no means to safely do SuperVersion cleanup.
|
||||
ResetThreadLocalSuperVersions();
|
||||
|
||||
if (old_superversion->mutable_cf_options.write_buffer_size !=
|
||||
mutable_cf_options.write_buffer_size) {
|
||||
mem_->UpdateWriteBufferSize(mutable_cf_options.write_buffer_size);
|
||||
@@ -988,6 +981,9 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
sv_context->superversions_to_free.push_back(old_superversion);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset SuperVersions cached in thread local storage
|
||||
ResetThreadLocalSuperVersions();
|
||||
}
|
||||
|
||||
void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
@@ -999,12 +995,10 @@ void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
continue;
|
||||
}
|
||||
auto sv = static_cast<SuperVersion*>(ptr);
|
||||
bool was_last_ref __attribute__((__unused__));
|
||||
was_last_ref = sv->Unref();
|
||||
// sv couldn't have been the last reference because
|
||||
// ResetThreadLocalSuperVersions() is called before
|
||||
// unref'ing super_version_.
|
||||
assert(!was_last_ref);
|
||||
if (sv->Unref()) {
|
||||
sv->Cleanup();
|
||||
delete sv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1066,10 +1060,12 @@ ColumnFamilySet::~ColumnFamilySet() {
|
||||
while (column_family_data_.size() > 0) {
|
||||
// cfd destructor will delete itself from column_family_data_
|
||||
auto cfd = column_family_data_.begin()->second;
|
||||
cfd->Unref();
|
||||
bool last_ref __attribute__((__unused__)) = cfd->Unref();
|
||||
assert(last_ref);
|
||||
delete cfd;
|
||||
}
|
||||
dummy_cfd_->Unref();
|
||||
bool dummy_last_ref __attribute__((__unused__)) = dummy_cfd_->Unref();
|
||||
assert(dummy_last_ref);
|
||||
delete dummy_cfd_;
|
||||
}
|
||||
|
||||
|
||||
@@ -1868,126 +1868,6 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks for automatic getting a conflict if there is a
|
||||
// manual which has not yet been scheduled.
|
||||
// The manual compaction waits in NotScheduled
|
||||
// We generate more files and then trigger an automatic compaction
|
||||
// This will wait because there is an unscheduled manual compaction.
|
||||
// Once the conflict is hit, the manual compaction starts and ends
|
||||
// Then another automatic will start and end.
|
||||
TEST_F(ColumnFamilyTest, SameCFManualAutomaticConflict) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ColumnFamilyOptions default_cf, one;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.max_compaction_bytes = default_cf.target_file_size_base * 1100;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
one.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
one.num_levels = 1;
|
||||
// trigger compaction if there are >= 4 files
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
Reopen({default_cf, one});
|
||||
// make sure all background compaction jobs can be scheduled
|
||||
auto stop_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
|
||||
// SETUP column family "one" -- universal style
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger - 2; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
bool cf_1_1 = true;
|
||||
bool cf_1_2 = true;
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction()::Conflict",
|
||||
"ColumnFamilyTest::ManualAutoCon:7"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:9",
|
||||
"ColumnFamilyTest::ManualAutoCon:8"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:2",
|
||||
"ColumnFamilyTest::ManualAutoCon:6"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:4",
|
||||
"ColumnFamilyTest::ManualAutoCon:5"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:1",
|
||||
"ColumnFamilyTest::ManualAutoCon:2"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:1",
|
||||
"ColumnFamilyTest::ManualAutoCon:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:3");
|
||||
} else if (cf_1_2) {
|
||||
cf_1_2 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:2");
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RunManualCompaction:NotScheduled", [&](void* arg) {
|
||||
InstrumentedMutex* mutex = static_cast<InstrumentedMutex*>(arg);
|
||||
mutex->Unlock();
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:9");
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:7");
|
||||
mutex->Lock();
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:6");
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:8");
|
||||
WaitForFlush(1);
|
||||
|
||||
// Add more L0 files and force automatic compaction
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(ToString(one.level0_file_num_compaction_trigger + i),
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:5");
|
||||
// Add more L0 files and force automatic compaction
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
}
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:1");
|
||||
|
||||
threads.join();
|
||||
WaitForCompaction();
|
||||
// VERIFY compaction "one"
|
||||
ASSERT_LE(NumTableFilesAtLevel(0, 1), 3);
|
||||
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
}
|
||||
|
||||
// In this test, we generate enough files to trigger automatic compactions.
|
||||
// The automatic compaction waits in NonTrivial:AfterRun
|
||||
// We generate more files and then trigger an automatic compaction
|
||||
|
||||
+13
-10
@@ -405,20 +405,23 @@ void Compaction::Summary(char* output, int len) {
|
||||
uint64_t Compaction::OutputFilePreallocationSize() const {
|
||||
uint64_t preallocation_size = 0;
|
||||
|
||||
if (max_output_file_size_ != port::kMaxUint64 &&
|
||||
(cfd_->ioptions()->compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0)) {
|
||||
preallocation_size = max_output_file_size_;
|
||||
} else {
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
preallocation_size += file->fd.GetFileSize();
|
||||
}
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
preallocation_size += file->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
|
||||
if (max_output_file_size_ != port::kMaxUint64 &&
|
||||
(immutable_cf_options_.compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0)) {
|
||||
preallocation_size = std::min(max_output_file_size_, preallocation_size);
|
||||
}
|
||||
|
||||
// Over-estimate slightly so we don't end up just barely crossing
|
||||
// the threshold
|
||||
return preallocation_size + (preallocation_size / 10);
|
||||
// No point to prellocate more than 1GB.
|
||||
return std::min(uint64_t{1073741824},
|
||||
preallocation_size + (preallocation_size / 10));
|
||||
}
|
||||
|
||||
std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
|
||||
|
||||
@@ -184,8 +184,11 @@ void CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
Slice* skip_until) {
|
||||
if (compaction_filter_ != nullptr &&
|
||||
(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex) &&
|
||||
(visible_at_tip_ || ikey_.sequence > latest_snapshot_ ||
|
||||
ignore_snapshots_)) {
|
||||
(visible_at_tip_ || ignore_snapshots_ ||
|
||||
ikey_.sequence > latest_snapshot_ ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
latest_snapshot_))))) {
|
||||
// If the user has specified a compaction filter and the sequence
|
||||
// number is greater than any external snapshot, then invoke the
|
||||
// filter. If the return value of the compaction filter is true,
|
||||
@@ -411,7 +414,10 @@ void CompactionIterator::NextFromInput() {
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key)) {
|
||||
// Check whether the next key belongs to the same snapshot as the
|
||||
// SingleDelete.
|
||||
if (prev_snapshot == 0 || next_ikey.sequence > prev_snapshot) {
|
||||
if (prev_snapshot == 0 || next_ikey.sequence > prev_snapshot ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(next_ikey.sequence,
|
||||
prev_snapshot)))) {
|
||||
if (next_ikey.type == kTypeSingleDeletion) {
|
||||
// We encountered two SingleDeletes in a row. This could be due to
|
||||
// unexpected user input.
|
||||
@@ -422,8 +428,12 @@ void CompactionIterator::NextFromInput() {
|
||||
// input_->Next().
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
} else if ((ikey_.sequence <= earliest_write_conflict_snapshot_) ||
|
||||
has_outputted_key_) {
|
||||
} else if (has_outputted_key_ ||
|
||||
(ikey_.sequence <= earliest_write_conflict_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(
|
||||
ikey_.sequence,
|
||||
earliest_write_conflict_snapshot_))))) {
|
||||
// Found a matching value, we can drop the single delete and the
|
||||
// value. It is safe to drop both records since we've already
|
||||
// outputted a key in this snapshot, or there is no earlier
|
||||
@@ -471,6 +481,9 @@ void CompactionIterator::NextFromInput() {
|
||||
// comparison, so the value of has_current_user_key does not matter.
|
||||
has_current_user_key_ = false;
|
||||
if (compaction_ != nullptr && ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
// Key doesn't exist outside of this range.
|
||||
@@ -504,6 +517,9 @@ void CompactionIterator::NextFromInput() {
|
||||
input_->Next();
|
||||
} else if (compaction_ != nullptr && ikey_.type == kTypeDeletion &&
|
||||
ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
@@ -538,6 +554,10 @@ void CompactionIterator::NextFromInput() {
|
||||
// have hit (A)
|
||||
// We encapsulate the merge related state machine in a different
|
||||
// object to minimize change to the existing flow.
|
||||
// In case snapshot_checker is present, we can probably merge further
|
||||
// beyond prev_snapshot, since there could be more keys with sequence
|
||||
// smaller than prev_snapshot, but reported by snapshot_checker as not
|
||||
// visible by prev_snapshot. But it will make the logic more complicated.
|
||||
Status s = merge_helper_->MergeUntil(input_, range_del_agg_,
|
||||
prev_snapshot, bottommost_level_);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
|
||||
@@ -175,6 +175,8 @@ TEST_F(CompactionPickerTest, Level1Trigger) {
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, Level1Trigger2) {
|
||||
mutable_cf_options_.target_file_size_base = 10000000000;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
Add(1, 66U, "150", "200", 1000000001U);
|
||||
Add(1, 88U, "201", "300", 1000000000U);
|
||||
@@ -191,13 +193,14 @@ TEST_F(CompactionPickerTest, Level1Trigger2) {
|
||||
ASSERT_EQ(66U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(6U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(7U, compaction->input(1, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(uint64_t{1073741824}, compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, LevelMaxScore) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
Add(0, 1U, "150", "200", 1000000U);
|
||||
// Level 1 score 1.2
|
||||
Add(1, 66U, "150", "200", 6000000U);
|
||||
@@ -218,6 +221,9 @@ TEST_F(CompactionPickerTest, LevelMaxScore) {
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(7U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(mutable_cf_options_.target_file_size_base +
|
||||
mutable_cf_options_.target_file_size_base / 10,
|
||||
compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NeedsCompactionLevel) {
|
||||
@@ -521,9 +527,10 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_base = 100000000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
|
||||
Add(2, 6U, "150", "179", 50000000U);
|
||||
Add(2, 7U, "180", "220", 50000000U);
|
||||
@@ -543,6 +550,8 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
// Pick file 8 because it overlaps with 0 files on level 3.
|
||||
ASSERT_EQ(8U, compaction->input(0, 0)->fd.GetNumber());
|
||||
// Compaction input size * 1.1
|
||||
ASSERT_GE(uint64_t{55000000}, compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
|
||||
+57
-6
@@ -570,19 +570,19 @@ TEST_F(DBBasicTest, CompactBetweenSnapshots) {
|
||||
|
||||
TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
Options options = CurrentOptions();
|
||||
std::string dbname = test::TmpDir(env_) + "/db_options_test";
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
Close();
|
||||
Destroy(options);
|
||||
|
||||
// Does not exist, and create_if_missing == false: error
|
||||
DB* db = nullptr;
|
||||
options.create_if_missing = false;
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
Status s = DB::Open(options, dbname_, &db);
|
||||
ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != nullptr);
|
||||
ASSERT_TRUE(db == nullptr);
|
||||
|
||||
// Does not exist, and create_if_missing == true: OK
|
||||
options.create_if_missing = true;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
@@ -592,14 +592,14 @@ TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
// Does exist, and error_if_exists == true: error
|
||||
options.create_if_missing = false;
|
||||
options.error_if_exists = true;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != nullptr);
|
||||
ASSERT_TRUE(db == nullptr);
|
||||
|
||||
// Does exist, and error_if_exists == false: OK
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
@@ -847,6 +847,57 @@ TEST_F(DBBasicTest, MmapAndBufferOptions) {
|
||||
}
|
||||
#endif
|
||||
|
||||
class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv(Env* base) : EnvWrapper(base) { };
|
||||
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char *format, va_list ap) override { };
|
||||
private:
|
||||
virtual Status CloseImpl() override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
};
|
||||
|
||||
virtual Status NewLogger(const std::string& fname,
|
||||
shared_ptr<Logger>* result) {
|
||||
result->reset(new TestLogger());
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTest, DBClose) {
|
||||
Options options = GetDefaultOptions();
|
||||
std::string dbname = test::TmpDir(env_) + "/db_close_test";
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
|
||||
DB* db = nullptr;
|
||||
options.create_if_missing = true;
|
||||
options.env = new TestEnv(Env::Default());
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
s = db->Close();
|
||||
ASSERT_EQ(s, Status::NotSupported());
|
||||
|
||||
delete db;
|
||||
|
||||
// Provide our own logger and ensure DB::Close() does not close it
|
||||
options.info_log.reset(new TestEnv::TestLogger());
|
||||
options.create_if_missing = false;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
s = db->Close();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
delete db;
|
||||
delete options.env;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -57,6 +57,7 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Enabled");
|
||||
should_purge_files = true;
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
bg_cv_.SignalAll();
|
||||
} else {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
@@ -141,6 +142,18 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
{
|
||||
// If caller disabled deletions, this function should return files that are
|
||||
// guaranteed not to be deleted until deletions are re-enabled. We need to
|
||||
// wait for pending purges to finish since WalManager doesn't know which
|
||||
// files are going to be purged. Additional purges won't be scheduled as
|
||||
// long as deletions are disabled (so the below loop must terminate).
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while (disable_delete_obsolete_files_ > 0 &&
|
||||
pending_purge_obsolete_files_ > 0) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
return wal_manager_.GetSortedWalFiles(files);
|
||||
}
|
||||
|
||||
|
||||
+36
-21
@@ -123,13 +123,13 @@ CompressionType GetCompressionFlush(
|
||||
namespace {
|
||||
void DumpSupportInfo(Logger* logger) {
|
||||
ROCKS_LOG_HEADER(logger, "Compression algorithms supported:");
|
||||
ROCKS_LOG_HEADER(logger, "\tSnappy supported: %d", Snappy_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tZlib supported: %d", Zlib_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tBzip supported: %d", BZip2_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tLZ4 supported: %d", LZ4_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tZSTDNotFinal supported: %d",
|
||||
ZSTDNotFinal_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tZSTD supported: %d", ZSTD_Supported());
|
||||
for (auto& compression : OptionsHelper::compression_type_string_map) {
|
||||
if (compression.second != kNoCompression &&
|
||||
compression.second != kDisableCompressionOption) {
|
||||
ROCKS_LOG_HEADER(logger, "\t%s supported: %d", compression.first.c_str(),
|
||||
CompressionTypeSupported(compression.second));
|
||||
}
|
||||
}
|
||||
ROCKS_LOG_HEADER(logger, "Fast CRC32 supported: %s",
|
||||
crc32c::IsFastCrc32Supported().c_str());
|
||||
}
|
||||
@@ -141,6 +141,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
const bool seq_per_batch)
|
||||
: env_(options.env),
|
||||
dbname_(dbname),
|
||||
own_info_log_(options.info_log == nullptr),
|
||||
initial_db_options_(SanitizeOptions(dbname, options)),
|
||||
immutable_db_options_(initial_db_options_),
|
||||
mutable_db_options_(initial_db_options_),
|
||||
@@ -177,6 +178,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
num_running_flushes_(0),
|
||||
bg_purge_scheduled_(0),
|
||||
disable_delete_obsolete_files_(0),
|
||||
pending_purge_obsolete_files_(0),
|
||||
delete_obsolete_files_last_run_(env_->NowMicros()),
|
||||
last_stats_dump_time_microsec_(0),
|
||||
next_job_id_(1),
|
||||
@@ -212,7 +214,8 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
// requires a custom gc for compaction, we use that to set use_custom_gc_
|
||||
// as well.
|
||||
use_custom_gc_(seq_per_batch),
|
||||
preserve_deletes_(options.preserve_deletes) {
|
||||
preserve_deletes_(options.preserve_deletes),
|
||||
closed_(false) {
|
||||
env_->GetAbsolutePath(dbname, &db_absolute_path_);
|
||||
|
||||
// Reserve ten files or so for other uses and give the rest to TableCache.
|
||||
@@ -275,7 +278,7 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
}
|
||||
}
|
||||
|
||||
DBImpl::~DBImpl() {
|
||||
Status DBImpl::CloseImpl() {
|
||||
// CancelAllBackgroundWork called with false means we just set the shutdown
|
||||
// marker. After this we do a variant of the waiting and unschedule work
|
||||
// (to consider: moving all the waiting into CancelAllBackgroundWork(true))
|
||||
@@ -291,7 +294,8 @@ DBImpl::~DBImpl() {
|
||||
|
||||
// Wait for background work to finish
|
||||
while (bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ || bg_purge_scheduled_) {
|
||||
bg_flush_scheduled_ || bg_purge_scheduled_ ||
|
||||
pending_purge_obsolete_files_) {
|
||||
TEST_SYNC_POINT("DBImpl::~DBImpl:WaitJob");
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
@@ -378,8 +382,16 @@ DBImpl::~DBImpl() {
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Shutdown complete");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
Status s = Status::OK();
|
||||
if (immutable_db_options_.info_log && own_info_log_) {
|
||||
s = immutable_db_options_.info_log->Close();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
DBImpl::~DBImpl() { Close(); }
|
||||
|
||||
void DBImpl::MaybeIgnoreError(Status* s) const {
|
||||
if (s->ok() || immutable_db_options_.paranoid_checks) {
|
||||
// No change needed
|
||||
@@ -1510,7 +1522,8 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
ReadCallback* read_callback,
|
||||
bool allow_blob) {
|
||||
bool allow_blob,
|
||||
bool allow_refresh) {
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
|
||||
// Try to generate a DB iterator tree in continuous memory area to be
|
||||
@@ -1559,7 +1572,8 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
env_, read_options, *cfd->ioptions(), snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback,
|
||||
((read_options.snapshot != nullptr) ? nullptr : this), cfd, allow_blob);
|
||||
((read_options.snapshot != nullptr) ? nullptr : this), cfd, allow_blob,
|
||||
allow_refresh);
|
||||
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
@@ -1682,12 +1696,6 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
delete casted_s;
|
||||
}
|
||||
|
||||
bool DBImpl::HasActiveSnapshotInRange(SequenceNumber lower_bound,
|
||||
SequenceNumber upper_bound) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return snapshots_.HasSnapshotInRange(lower_bound, upper_bound);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status DBImpl::GetPropertiesOfAllTables(ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) {
|
||||
@@ -2051,8 +2059,7 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
name.c_str());
|
||||
return Status::NotSupported("Delete only supported for archived logs");
|
||||
}
|
||||
status =
|
||||
env_->DeleteFile(immutable_db_options_.wal_dir + "/" + name.c_str());
|
||||
status = wal_manager_.DeleteFile(name, number);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"DeleteFile %s failed -- %s.\n", name.c_str(),
|
||||
@@ -2325,7 +2332,15 @@ Status DB::DestroyColumnFamilyHandle(ColumnFamilyHandle* column_family) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
DB::~DB() { }
|
||||
DB::~DB() {}
|
||||
|
||||
Status DBImpl::Close() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
return CloseImpl();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DB::ListColumnFamilies(const DBOptions& db_options,
|
||||
const std::string& name,
|
||||
|
||||
+21
-6
@@ -148,7 +148,8 @@ class DBImpl : public DB {
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
ReadCallback* read_callback,
|
||||
bool allow_blob = false);
|
||||
bool allow_blob = false,
|
||||
bool allow_refresh = true);
|
||||
|
||||
virtual const Snapshot* GetSnapshot() override;
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
@@ -229,10 +230,6 @@ class DBImpl : public DB {
|
||||
|
||||
virtual bool SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) override;
|
||||
|
||||
// Whether there is an active snapshot in range [lower_bound, upper_bound).
|
||||
bool HasActiveSnapshotInRange(SequenceNumber lower_bound,
|
||||
SequenceNumber upper_bound);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::ResetStats;
|
||||
virtual Status ResetStats() override;
|
||||
@@ -471,6 +468,8 @@ class DBImpl : public DB {
|
||||
// belong to live files are posibly removed. Also, removes all the
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
// If FindObsoleteFiles() was run, we need to also run
|
||||
// PurgeObsoleteFiles(), even if disable_delete_obsolete_files_ is true
|
||||
void PurgeObsoleteFiles(const JobContext& background_contet,
|
||||
bool schedule_only = false);
|
||||
|
||||
@@ -614,10 +613,14 @@ class DBImpl : public DB {
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
const bool seq_per_batch);
|
||||
|
||||
virtual Status Close() override;
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
const DBOptions initial_db_options_;
|
||||
const ImmutableDBOptions immutable_db_options_;
|
||||
MutableDBOptions mutable_db_options_;
|
||||
@@ -919,6 +922,9 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
// Actual implementation of Close()
|
||||
virtual Status CloseImpl();
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
@@ -947,6 +953,8 @@ class DBImpl : public DB {
|
||||
// (i.e. whenever a flush is done, even if it didn't make any progress)
|
||||
// * whenever there is an error in background purge, flush or compaction
|
||||
// * whenever num_running_ingest_file_ goes to 0.
|
||||
// * whenever pending_purge_obsolete_files_ goes to 0.
|
||||
// * whenever disable_delete_obsolete_files_ goes to 0.
|
||||
InstrumentedCondVar bg_cv_;
|
||||
// Writes are protected by locking both mutex_ and log_write_mutex_, and reads
|
||||
// must be under either mutex_ or log_write_mutex_. Since after ::Open,
|
||||
@@ -954,7 +962,7 @@ class DBImpl : public DB {
|
||||
// from the same write_thread_ without any locks.
|
||||
uint64_t logfile_number_;
|
||||
std::deque<uint64_t>
|
||||
log_recycle_files; // a list of log files that we can recycle
|
||||
log_recycle_files_; // a list of log files that we can recycle
|
||||
bool log_dir_synced_;
|
||||
// Without two_write_queues, read and writes to log_empty_ are protected by
|
||||
// mutex_. Since it is currently updated/read only in write_thread_, it can be
|
||||
@@ -1213,6 +1221,10 @@ class DBImpl : public DB {
|
||||
// without any synchronization
|
||||
int disable_delete_obsolete_files_;
|
||||
|
||||
// Number of times FindObsoleteFiles has found deletable files and the
|
||||
// corresponding call to PurgeObsoleteFiles has not yet finished.
|
||||
int pending_purge_obsolete_files_;
|
||||
|
||||
// last time when DeleteObsoleteFiles with full scan was executed. Originaly
|
||||
// initialized with startup time.
|
||||
uint64_t delete_obsolete_files_last_run_;
|
||||
@@ -1366,6 +1378,9 @@ class DBImpl : public DB {
|
||||
// is set to false.
|
||||
std::atomic<SequenceNumber> preserve_deletes_seqnum_;
|
||||
const bool preserve_deletes_;
|
||||
|
||||
// Flag to check whether Close() has been called on this DB
|
||||
bool closed_;
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
|
||||
@@ -442,7 +442,8 @@ Status DBImpl::CompactFiles(
|
||||
} // release the mutex
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
@@ -1071,6 +1072,7 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
if (HasExclusiveManualCompaction()) {
|
||||
// only manual compactions are allowed to run. don't schedule automatic
|
||||
// compactions
|
||||
TEST_SYNC_POINT("DBImpl::MaybeScheduleFlushOrCompaction:Conflict");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1315,8 +1317,10 @@ void DBImpl::BackgroundCallFlush() {
|
||||
// created. Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FilesFound");
|
||||
// Have to flush the info logs before bg_flush_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
@@ -1396,7 +1400,8 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
@@ -1515,7 +1520,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
: m->manual_end->DebugString().c_str()));
|
||||
}
|
||||
} else if (!is_prepicked && !compaction_queue_.empty()) {
|
||||
if (HaveManualCompaction(compaction_queue_.front())) {
|
||||
if (HasExclusiveManualCompaction()) {
|
||||
// Can't compact right now, but try again later
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction()::Conflict");
|
||||
|
||||
|
||||
+17
-10
@@ -238,11 +238,11 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
while (alive_log_files_.begin()->number < min_log_number) {
|
||||
auto& earliest = *alive_log_files_.begin();
|
||||
if (immutable_db_options_.recycle_log_file_num >
|
||||
log_recycle_files.size()) {
|
||||
log_recycle_files_.size()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"adding log %" PRIu64 " to recycle list\n",
|
||||
earliest.number);
|
||||
log_recycle_files.push_back(earliest.number);
|
||||
log_recycle_files_.push_back(earliest.number);
|
||||
} else {
|
||||
job_context->log_delete_files.push_back(earliest.number);
|
||||
}
|
||||
@@ -283,8 +283,11 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// We're just cleaning up for DB::Write().
|
||||
assert(job_context->logs_to_free.empty());
|
||||
job_context->logs_to_free = logs_to_free_;
|
||||
job_context->log_recycle_files.assign(log_recycle_files.begin(),
|
||||
log_recycle_files.end());
|
||||
job_context->log_recycle_files.assign(log_recycle_files_.begin(),
|
||||
log_recycle_files_.end());
|
||||
if (job_context->HaveSomethingToDelete()) {
|
||||
++pending_purge_obsolete_files_;
|
||||
}
|
||||
logs_to_free_.clear();
|
||||
}
|
||||
|
||||
@@ -342,15 +345,12 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
TEST_SYNC_POINT("DBImpl::PurgeObsoleteFiles:Begin");
|
||||
// we'd better have sth to delete
|
||||
assert(state.HaveSomethingToDelete());
|
||||
|
||||
// this checks if FindObsoleteFiles() was run before. If not, don't do
|
||||
// PurgeObsoleteFiles(). If FindObsoleteFiles() was run, we need to also
|
||||
// run PurgeObsoleteFiles(), even if disable_delete_obsolete_files_ is true
|
||||
if (state.manifest_file_number == 0) {
|
||||
return;
|
||||
}
|
||||
// FindObsoleteFiles() should've populated this so nonzero
|
||||
assert(state.manifest_file_number != 0);
|
||||
|
||||
// Now, convert live list to an unordered map, WITHOUT mutex held;
|
||||
// set is slow.
|
||||
@@ -533,6 +533,13 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
wal_manager_.PurgeObsoleteWALFiles();
|
||||
#endif // ROCKSDB_LITE
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
--pending_purge_obsolete_files_;
|
||||
assert(pending_purge_obsolete_files_ >= 0);
|
||||
if (pending_purge_obsolete_files_ == 0) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::PurgeObsoleteFiles:End");
|
||||
}
|
||||
|
||||
void DBImpl::DeleteObsoleteFiles() {
|
||||
|
||||
+12
-8
@@ -521,9 +521,6 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
true /* disable_memtable */, pre_release_callback);
|
||||
if (write_options.disableWAL) {
|
||||
return status;
|
||||
}
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
@@ -580,7 +577,13 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
// LastAllocatedSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
size_t seq_inc = seq_per_batch_ ? write_group.size : 0 /*total_count*/;
|
||||
status = ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
if (!write_options.disableWAL) {
|
||||
status =
|
||||
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
} else {
|
||||
// Otherwise we inc seq number to do solely the seq allocation
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
}
|
||||
auto curr_seq = last_sequence + 1;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CallbackFailed()) {
|
||||
@@ -593,6 +596,7 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
// else seq advances only by memtable writes
|
||||
}
|
||||
if (status.ok() && write_options.sync) {
|
||||
assert(!write_options.disableWAL);
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hance provide a simple implementation that is not necessarily efficient.
|
||||
if (manual_wal_flush_) {
|
||||
@@ -693,7 +697,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !bg_error_.ok())) {
|
||||
return bg_error_;
|
||||
status = bg_error_;
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !flush_scheduler_.Empty())) {
|
||||
@@ -1211,9 +1215,9 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
}
|
||||
uint64_t recycle_log_number = 0;
|
||||
if (creating_new_log && immutable_db_options_.recycle_log_file_num &&
|
||||
!log_recycle_files.empty()) {
|
||||
recycle_log_number = log_recycle_files.front();
|
||||
log_recycle_files.pop_front();
|
||||
!log_recycle_files_.empty()) {
|
||||
recycle_log_number = log_recycle_files_.front();
|
||||
log_recycle_files_.pop_front();
|
||||
}
|
||||
uint64_t new_log_number =
|
||||
creating_new_log ? versions_->NewFileNumber() : logfile_number_;
|
||||
|
||||
+19
-9
@@ -217,9 +217,6 @@ class DBIter final: public Iterator {
|
||||
*prop = "Iterator is not valid.";
|
||||
}
|
||||
return Status::OK();
|
||||
} else if (prop_name == "rocksdb.iterator.internal-key") {
|
||||
*prop = saved_key_.GetUserKey().ToString();
|
||||
return Status::OK();
|
||||
}
|
||||
return Status::InvalidArgument("Undentified property.");
|
||||
}
|
||||
@@ -975,6 +972,17 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
// assume there is at least one parseable key for this user key
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kForward);
|
||||
assert(iter_->Valid());
|
||||
assert(user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()));
|
||||
|
||||
// In case read_callback presents, the value we seek to may not be visible.
|
||||
// Seek for the next value that's visible.
|
||||
while (!IsVisible(ikey.sequence)) {
|
||||
iter_->Next();
|
||||
FindParseableKey(&ikey, kForward);
|
||||
assert(iter_->Valid());
|
||||
assert(user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()));
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
@@ -1381,17 +1389,19 @@ void ArenaWrappedDBIter::Init(Env* env, const ReadOptions& read_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number,
|
||||
ReadCallback* read_callback, bool allow_blob) {
|
||||
ReadCallback* read_callback, bool allow_blob,
|
||||
bool allow_refresh) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem)
|
||||
DBIter(env, read_options, cf_options, cf_options.user_comparator, nullptr,
|
||||
sequence, true, max_sequential_skip_in_iteration, read_callback,
|
||||
allow_blob);
|
||||
sv_number_ = version_number;
|
||||
allow_refresh_ = allow_refresh;
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh() {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr) {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr || !allow_refresh_) {
|
||||
return Status::NotSupported("Creating renew iterator is not allowed.");
|
||||
}
|
||||
assert(db_iter_ != nullptr);
|
||||
@@ -1409,7 +1419,7 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_->mutex());
|
||||
Init(env, read_options_, *(cfd_->ioptions()), latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, allow_blob_);
|
||||
cur_sv_number, read_callback_, allow_blob_, allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator());
|
||||
@@ -1426,12 +1436,12 @@ ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
const ImmutableCFOptions& cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob) {
|
||||
bool allow_blob, bool allow_refresh) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, cf_options, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
allow_blob);
|
||||
if (db_impl != nullptr && cfd != nullptr) {
|
||||
allow_blob, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(read_options, db_impl, cfd, read_callback,
|
||||
allow_blob);
|
||||
}
|
||||
|
||||
+4
-3
@@ -73,7 +73,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, bool allow_blob);
|
||||
ReadCallback* read_callback, bool allow_blob, bool allow_refresh);
|
||||
|
||||
void StoreRefreshInfo(const ReadOptions& read_options, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, ReadCallback* read_callback,
|
||||
@@ -94,6 +94,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
ReadOptions read_options_;
|
||||
ReadCallback* read_callback_;
|
||||
bool allow_blob_ = false;
|
||||
bool allow_refresh_ = true;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
@@ -104,6 +105,6 @@ extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
const ImmutableCFOptions& cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false);
|
||||
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false,
|
||||
bool allow_refresh = true);
|
||||
} // namespace rocksdb
|
||||
|
||||
+125
-38
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "db/db_iter.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -55,7 +56,6 @@ TEST_F(DBIteratorTest, IteratorProperty) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Put(1, "1", "2");
|
||||
Delete(1, "2");
|
||||
ReadOptions ropt;
|
||||
ropt.pin_data = false;
|
||||
{
|
||||
@@ -65,15 +65,9 @@ TEST_F(DBIteratorTest, IteratorProperty) {
|
||||
ASSERT_NOK(iter->GetProperty("non_existing.value", &prop_value));
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
|
||||
ASSERT_EQ("0", prop_value);
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
|
||||
ASSERT_EQ("1", prop_value);
|
||||
iter->Next();
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
|
||||
ASSERT_EQ("Iterator is not valid.", prop_value);
|
||||
|
||||
// Get internal key at which the iteration stopped (tombstone in this case).
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
|
||||
ASSERT_EQ("2", prop_value);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
@@ -2164,46 +2158,139 @@ TEST_F(DBIteratorTest, SkipStatistics) {
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, SeekAfterHittingManyInternalKeys) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ReadOptions ropts;
|
||||
ropts.max_skippable_internal_keys = 2;
|
||||
TEST_F(DBIteratorTest, ReadCallback) {
|
||||
class TestReadCallback : public ReadCallback {
|
||||
public:
|
||||
explicit TestReadCallback(SequenceNumber last_visible_seq)
|
||||
: last_visible_seq_(last_visible_seq) {}
|
||||
|
||||
Put("1", "val_1");
|
||||
// Add more tombstones than max_skippable_internal_keys so that Next() fails.
|
||||
Delete("2");
|
||||
Delete("3");
|
||||
Delete("4");
|
||||
Delete("5");
|
||||
Put("6", "val_6");
|
||||
bool IsCommitted(SequenceNumber seq) override {
|
||||
return seq <= last_visible_seq_;
|
||||
}
|
||||
|
||||
unique_ptr<Iterator> iter(db_->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
private:
|
||||
SequenceNumber last_visible_seq_;
|
||||
};
|
||||
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
ASSERT_OK(Put("a", "va"));
|
||||
ASSERT_OK(Put("z", "vz"));
|
||||
SequenceNumber seq1 = db_->GetLatestSequenceNumber();
|
||||
TestReadCallback callback1(seq1);
|
||||
ASSERT_OK(Put("foo", "v4"));
|
||||
ASSERT_OK(Put("foo", "v5"));
|
||||
ASSERT_OK(Put("bar", "v7"));
|
||||
|
||||
SequenceNumber seq2 = db_->GetLatestSequenceNumber();
|
||||
auto* cfd =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
->cfd();
|
||||
// The iterator are suppose to see data before seq1.
|
||||
Iterator* iter =
|
||||
dbfull()->NewIteratorImpl(ReadOptions(), cfd, seq2, &callback1);
|
||||
|
||||
// Seek
|
||||
// The latest value of "foo" before seq1 is "v3"
|
||||
iter->Seek("foo");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "1");
|
||||
ASSERT_EQ(iter->value().ToString(), "val_1");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
// "bar" is not visible to the iterator. It will move on to the next key
|
||||
// "foo".
|
||||
iter->Seek("bar");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
|
||||
// This should fail as incomplete due to too many non-visible internal keys on
|
||||
// the way to the next valid user key.
|
||||
// Next
|
||||
// Seek to "a"
|
||||
iter->Seek("a");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("va", iter->value());
|
||||
// "bar" is not visible to the iterator. It will move on to the next key
|
||||
// "foo".
|
||||
iter->Next();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsIncomplete());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
|
||||
// Get the internal key at which Next() failed.
|
||||
std::string prop_value;
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
|
||||
ASSERT_EQ("4", prop_value);
|
||||
// Prev
|
||||
// Seek to "z"
|
||||
iter->Seek("z");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("vz", iter->value());
|
||||
// The previous key is "foo", which is visible to the iterator.
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
// "bar" is not visible to the iterator. It will move on to the next key "a".
|
||||
iter->Prev(); // skipping "bar"
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("a", iter->key());
|
||||
ASSERT_EQ("va", iter->value());
|
||||
|
||||
// Create a new iterator to seek to the internal key.
|
||||
unique_ptr<Iterator> iter2(db_->NewIterator(ropts));
|
||||
iter2->Seek(prop_value);
|
||||
ASSERT_TRUE(iter2->Valid());
|
||||
ASSERT_OK(iter2->status());
|
||||
// SeekForPrev
|
||||
// The previous key is "foo", which is visible to the iterator.
|
||||
iter->SeekForPrev("y");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
// "bar" is not visible to the iterator. It will move on to the next key "a".
|
||||
iter->SeekForPrev("bar");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("a", iter->key());
|
||||
ASSERT_EQ("va", iter->value());
|
||||
|
||||
ASSERT_EQ(iter2->key().ToString(), "6");
|
||||
ASSERT_EQ(iter2->value().ToString(), "val_6");
|
||||
delete iter;
|
||||
|
||||
// Prev beyond max_sequential_skip_in_iterations
|
||||
uint64_t num_versions =
|
||||
CurrentOptions().max_sequential_skip_in_iterations + 10;
|
||||
for (uint64_t i = 0; i < num_versions; i++) {
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
}
|
||||
SequenceNumber seq3 = db_->GetLatestSequenceNumber();
|
||||
TestReadCallback callback2(seq3);
|
||||
ASSERT_OK(Put("bar", "v8"));
|
||||
SequenceNumber seq4 = db_->GetLatestSequenceNumber();
|
||||
|
||||
// The iterator is suppose to see data before seq3.
|
||||
iter = dbfull()->NewIteratorImpl(ReadOptions(), cfd, seq4, &callback2);
|
||||
// Seek to "z", which is visible.
|
||||
iter->Seek("z");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("vz", iter->value());
|
||||
// Previous key is "foo" and the last value "v5" is visible.
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v5", iter->value());
|
||||
// Since the number of values of "bar" is more than
|
||||
// max_sequential_skip_in_iterations, Prev() will ultimately fallback to
|
||||
// seek in forward direction. Here we test the fallback seek is correct.
|
||||
// The last visible value should be (num_versions - 1), as "v8" is not
|
||||
// visible.
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bar", iter->key());
|
||||
ASSERT_EQ(ToString(num_versions - 1), iter->value());
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -518,8 +518,13 @@ static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
JobContext job_context(0);
|
||||
dbfull->FindObsoleteFiles(&job_context, false);
|
||||
ASSERT_EQ(empty, job_context.full_scan_candidate_files.empty());
|
||||
job_context.Clean();
|
||||
dbfull->TEST_UnlockMutex();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
// fulfill the contract of FindObsoleteFiles by calling PurgeObsoleteFiles
|
||||
// afterwards; otherwise the test may hang on shutdown
|
||||
dbfull->PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
|
||||
@@ -376,6 +376,30 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, OpenDBWithExistingTrash) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
options.sst_file_manager.reset(
|
||||
NewSstFileManager(env_, nullptr, "", 1024 * 1024 /* 1 MB/sec */));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
|
||||
Destroy(last_options_);
|
||||
|
||||
// Add some trash files to the db directory so the DB can clean them up
|
||||
env_->CreateDirIfMissing(dbname_);
|
||||
ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "001.sst.trash"));
|
||||
ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "002.sst.trash"));
|
||||
ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "003.sst.trash"));
|
||||
|
||||
// Reopen the DB and verify that it deletes existing trash files
|
||||
ASSERT_OK(TryReopen(options));
|
||||
sfm->WaitForEmptyTrash();
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "001.sst.trash"));
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "002.sst.trash"));
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "003.sst.trash"));
|
||||
}
|
||||
|
||||
|
||||
// Create a DB with 2 db_paths, and generate multiple files in the 2
|
||||
// db_paths using CompactRangeOptions, make sure that files that were
|
||||
// deleted from first db_path were deleted using DeleteScheduler and
|
||||
|
||||
+73
-76
@@ -503,7 +503,9 @@ TEST_F(DBTest, DISABLED_VeryLargeValue) {
|
||||
ASSERT_OK(Put(key2, raw));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::string value;
|
||||
Status s = db_->Get(ReadOptions(), key1, &value);
|
||||
@@ -2191,6 +2193,8 @@ class ModelDB : public DB {
|
||||
batch.Put(cf, k, v);
|
||||
return Write(o, &batch);
|
||||
}
|
||||
using DB::Close;
|
||||
virtual Status Close() override { return Status::OK(); }
|
||||
using DB::Delete;
|
||||
virtual Status Delete(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& key) override {
|
||||
@@ -3488,12 +3492,16 @@ TEST_F(DBTest, GetThreadStatus) {
|
||||
const int kTestCount = 3;
|
||||
const unsigned int kHighPriCounts[kTestCount] = {3, 2, 5};
|
||||
const unsigned int kLowPriCounts[kTestCount] = {10, 15, 3};
|
||||
const unsigned int kBottomPriCounts[kTestCount] = {2, 1, 4};
|
||||
for (int test = 0; test < kTestCount; ++test) {
|
||||
// Change the number of threads in high / low priority pool.
|
||||
env_->SetBackgroundThreads(kHighPriCounts[test], Env::HIGH);
|
||||
env_->SetBackgroundThreads(kLowPriCounts[test], Env::LOW);
|
||||
env_->SetBackgroundThreads(kBottomPriCounts[test], Env::BOTTOM);
|
||||
// Wait to ensure the all threads has been registered
|
||||
unsigned int thread_type_counts[ThreadStatus::NUM_THREAD_TYPES];
|
||||
// TODO(ajkr): it'd be better if SetBackgroundThreads returned only after
|
||||
// all threads have been registered.
|
||||
// Try up to 60 seconds.
|
||||
for (int num_try = 0; num_try < 60000; num_try++) {
|
||||
env_->SleepForMicroseconds(1000);
|
||||
@@ -3508,20 +3516,21 @@ TEST_F(DBTest, GetThreadStatus) {
|
||||
if (thread_type_counts[ThreadStatus::HIGH_PRIORITY] ==
|
||||
kHighPriCounts[test] &&
|
||||
thread_type_counts[ThreadStatus::LOW_PRIORITY] ==
|
||||
kLowPriCounts[test]) {
|
||||
kLowPriCounts[test] &&
|
||||
thread_type_counts[ThreadStatus::BOTTOM_PRIORITY] ==
|
||||
kBottomPriCounts[test]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Verify the total number of threades
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::HIGH_PRIORITY] +
|
||||
thread_type_counts[ThreadStatus::LOW_PRIORITY],
|
||||
kHighPriCounts[test] + kLowPriCounts[test]);
|
||||
// Verify the number of high-priority threads
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::HIGH_PRIORITY],
|
||||
kHighPriCounts[test]);
|
||||
// Verify the number of low-priority threads
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::LOW_PRIORITY],
|
||||
kLowPriCounts[test]);
|
||||
// Verify the number of bottom-priority threads
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::BOTTOM_PRIORITY],
|
||||
kBottomPriCounts[test]);
|
||||
}
|
||||
if (i == 0) {
|
||||
// repeat the test with multiple column families
|
||||
@@ -4375,7 +4384,6 @@ TEST_F(DBTest, DynamicFIFOCompactionOptions) {
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
Options options;
|
||||
@@ -4444,6 +4452,7 @@ TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
dbfull()->GetOptions().compaction_options_universal.allow_trivial_move,
|
||||
false);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, FileCreationRandomFailure) {
|
||||
Options options;
|
||||
@@ -5049,55 +5058,84 @@ TEST_F(DBTest, PromoteL0Failure) {
|
||||
status = experimental::PromoteL0(db_, db_->DefaultColumnFamily());
|
||||
ASSERT_TRUE(status.IsInvalidArgument());
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Github issue #596
|
||||
TEST_F(DBTest, HugeNumberOfLevels) {
|
||||
TEST_F(DBTest, CompactRangeWithEmptyBottomLevel) {
|
||||
const int kNumLevels = 2;
|
||||
const int kNumL0Files = 2;
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 2 * 1024 * 1024; // 2MB
|
||||
options.max_bytes_for_level_base = 2 * 1024 * 1024; // 2MB
|
||||
options.num_levels = 12;
|
||||
options.max_background_compactions = 10;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
ASSERT_OK(Put(Key(0), RandomString(&rnd, 1024)));
|
||||
Flush();
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), kNumL0Files);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), kNumL0Files);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, AutomaticConflictsWithManualCompaction) {
|
||||
const int kNumL0Files = 50;
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 2 * 1024 * 1024; // 2MB
|
||||
options.max_bytes_for_level_base = 2 * 1024 * 1024; // 2MB
|
||||
options.num_levels = 12;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
// never slowdown / stop
|
||||
options.level0_slowdown_writes_trigger = 999999;
|
||||
options.level0_stop_writes_trigger = 999999;
|
||||
options.max_background_compactions = 10;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
|
||||
// schedule automatic compactions after the manual one starts, but before it
|
||||
// finishes to ensure conflict.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction:Start",
|
||||
"DBTest::AutomaticConflictsWithManualCompaction:PrePuts"},
|
||||
{"DBTest::AutomaticConflictsWithManualCompaction:PostPuts",
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun"}});
|
||||
std::atomic<int> callback_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction()::Conflict",
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:Conflict",
|
||||
[&](void* arg) { callback_count.fetch_add(1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
CompactRangeOptions croptions;
|
||||
croptions.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(db_->CompactRange(croptions, nullptr, nullptr));
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// put two keys to ensure no trivial move
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
std::thread manual_compaction_thread([this]() {
|
||||
CompactRangeOptions croptions;
|
||||
croptions.exclusive_manual_compaction = true;
|
||||
ASSERT_OK(db_->CompactRange(croptions, nullptr, nullptr));
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("DBTest::AutomaticConflictsWithManualCompaction:PrePuts");
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
// put two keys to ensure no trivial move
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
TEST_SYNC_POINT("DBTest::AutomaticConflictsWithManualCompaction:PostPuts");
|
||||
|
||||
ASSERT_GE(callback_count.load(), 1);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(i)));
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
manual_compaction_thread.join();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
// Github issue #595
|
||||
@@ -5317,12 +5355,15 @@ class WriteStallListener : public EventListener {
|
||||
public:
|
||||
WriteStallListener() : condition_(WriteStallCondition::kNormal) {}
|
||||
void OnStallConditionsChanged(const WriteStallInfo& info) override {
|
||||
MutexLock l(&mutex_);
|
||||
condition_ = info.condition.cur;
|
||||
}
|
||||
bool CheckCondition(WriteStallCondition expected) {
|
||||
MutexLock l(&mutex_);
|
||||
return expected == condition_;
|
||||
}
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
WriteStallCondition condition_;
|
||||
};
|
||||
|
||||
@@ -5651,50 +5692,6 @@ TEST_F(DBTest, PauseBackgroundWorkTest) {
|
||||
// now it's done
|
||||
ASSERT_TRUE(done.load());
|
||||
}
|
||||
|
||||
// Keep spawning short-living threads that create an iterator and quit.
|
||||
// Meanwhile in another thread keep flushing memtables.
|
||||
// This used to cause a deadlock.
|
||||
TEST_F(DBTest, ThreadLocalPtrDeadlock) {
|
||||
std::atomic<int> flushes_done{0};
|
||||
std::atomic<int> threads_destroyed{0};
|
||||
auto done = [&] {
|
||||
return flushes_done.load() > 10;
|
||||
};
|
||||
|
||||
std::thread flushing_thread([&] {
|
||||
for (int i = 0; !done(); ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Slice("hi"),
|
||||
Slice(std::to_string(i).c_str())));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
int cnt = ++flushes_done;
|
||||
fprintf(stderr, "Flushed %d times\n", cnt);
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<std::thread> thread_spawning_threads(10);
|
||||
for (auto& t: thread_spawning_threads) {
|
||||
t = std::thread([&] {
|
||||
while (!done()) {
|
||||
{
|
||||
std::thread tmp_thread([&] {
|
||||
auto it = db_->NewIterator(ReadOptions());
|
||||
delete it;
|
||||
});
|
||||
tmp_thread.join();
|
||||
}
|
||||
++threads_destroyed;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& t: thread_spawning_threads) {
|
||||
t.join();
|
||||
}
|
||||
flushing_thread.join();
|
||||
fprintf(stderr, "Done. Flushed %d times, destroyed %d threads\n",
|
||||
flushes_done.load(), threads_destroyed.load());
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -2430,6 +2430,60 @@ TEST_F(DBTest2, ReadCallbackTest) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
// Regression test for race condition where an obsolete file is returned to
|
||||
// user as a "live file" but then deleted, all while file deletions are
|
||||
// disabled.
|
||||
//
|
||||
// It happened like this:
|
||||
//
|
||||
// 1. [flush thread] Log file "x.log" found by FindObsoleteFiles
|
||||
// 2. [user thread] DisableFileDeletions, GetSortedWalFiles are called and the
|
||||
// latter returned "x.log"
|
||||
// 3. [flush thread] PurgeObsoleteFiles deleted "x.log"
|
||||
// 4. [user thread] Reading "x.log" failed
|
||||
//
|
||||
// Unfortunately the only regression test I can come up with involves sleep.
|
||||
// We cannot set SyncPoints to repro since, once the fix is applied, the
|
||||
// SyncPoints would cause a deadlock as the repro's sequence of events is now
|
||||
// prohibited.
|
||||
//
|
||||
// Instead, if we sleep for a second between Find and Purge, and ensure the
|
||||
// read attempt happens after purge, then the sequence of events will almost
|
||||
// certainly happen on the old code.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::BackgroundCallFlush:FilesFound",
|
||||
"DBTest2::LiveFilesOmitObsoleteFiles:FlushTriggered"},
|
||||
{"DBImpl::PurgeObsoleteFiles:End",
|
||||
"DBTest2::LiveFilesOmitObsoleteFiles:LiveFilesCaptured"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::PurgeObsoleteFiles:Begin",
|
||||
[&](void* arg) { env_->SleepForMicroseconds(1000000); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put("key", "val");
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
db_->Flush(flush_opts);
|
||||
TEST_SYNC_POINT("DBTest2::LiveFilesOmitObsoleteFiles:FlushTriggered");
|
||||
|
||||
db_->DisableFileDeletions();
|
||||
VectorLogPtr log_files;
|
||||
db_->GetSortedWalFiles(log_files);
|
||||
TEST_SYNC_POINT("DBTest2::LiveFilesOmitObsoleteFiles:LiveFilesCaptured");
|
||||
for (const auto& log_file : log_files) {
|
||||
ASSERT_OK(env_->FileExists(LogFileName(dbname_, log_file->LogNumber())));
|
||||
}
|
||||
|
||||
db_->EnableFileDeletions();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+6
-2
@@ -867,7 +867,6 @@ size_t DBTestBase::CountLiveFiles() {
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
return metadata.size();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
int DBTestBase::NumTableFilesAtLevel(int level, int cf) {
|
||||
std::string property;
|
||||
@@ -928,6 +927,7 @@ std::string DBTestBase::FilesPerLevel(int cf) {
|
||||
result.resize(last_non_zero_offset);
|
||||
return result;
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
size_t DBTestBase::CountFiles() {
|
||||
std::vector<std::string> files;
|
||||
@@ -997,6 +997,7 @@ void DBTestBase::MoveFilesToLevel(int level, int cf) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DBTestBase::DumpFileCounts(const char* label) {
|
||||
fprintf(stderr, "---\n%s:\n", label);
|
||||
fprintf(stderr, "maxoverlap: %" PRIu64 "\n",
|
||||
@@ -1008,6 +1009,7 @@ void DBTestBase::DumpFileCounts(const char* label) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::string DBTestBase::DumpSSTableList() {
|
||||
std::string property;
|
||||
@@ -1157,11 +1159,13 @@ UpdateStatus DBTestBase::updateInPlaceNoAction(char* prevValue,
|
||||
|
||||
// Utility method to test InplaceUpdate
|
||||
void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
ScopedArenaIterator iter;
|
||||
Arena arena;
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
// This should be defined after range_del_agg so that it destructs the
|
||||
// assigned iterator before it range_del_agg is already destructed.
|
||||
ScopedArenaIterator iter;
|
||||
if (cf != 0) {
|
||||
iter.set(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[cf]));
|
||||
|
||||
+3
-1
@@ -863,13 +863,13 @@ class DBTestBase : public testing::Test {
|
||||
size_t TotalLiveFiles(int cf = 0);
|
||||
|
||||
size_t CountLiveFiles();
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
int NumTableFilesAtLevel(int level, int cf = 0);
|
||||
|
||||
double CompressionRatioAtLevel(int level, int cf = 0);
|
||||
|
||||
int TotalTableFiles(int cf = 0, int levels = -1);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Return spread of files per level
|
||||
std::string FilesPerLevel(int cf = 0);
|
||||
@@ -897,7 +897,9 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
void MoveFilesToLevel(int level, int cf = 0);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DumpFileCounts(const char* label);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
std::string DumpSSTableList();
|
||||
|
||||
|
||||
@@ -39,52 +39,6 @@ TEST_P(DBWriteTest, SyncAndDisableWAL) {
|
||||
ASSERT_TRUE(dbfull()->Write(write_options, &batch).IsInvalidArgument());
|
||||
}
|
||||
|
||||
// Sequence number should be return through input write batch.
|
||||
TEST_P(DBWriteTest, ReturnSeuqneceNumber) {
|
||||
Random rnd(4422);
|
||||
Open();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
WriteBatch batch;
|
||||
batch.Put("key" + ToString(i), test::RandomHumanReadableString(&rnd, 10));
|
||||
ASSERT_OK(dbfull()->Write(WriteOptions(), &batch));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(),
|
||||
WriteBatchInternal::Sequence(&batch));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, ReturnSeuqneceNumberMultiThreaded) {
|
||||
constexpr size_t kThreads = 16;
|
||||
constexpr size_t kNumKeys = 1000;
|
||||
Open();
|
||||
ASSERT_EQ(0, dbfull()->GetLatestSequenceNumber());
|
||||
// Check each sequence is used once and only once.
|
||||
std::vector<std::atomic_flag> flags(kNumKeys * kThreads + 1);
|
||||
for (size_t i = 0; i < flags.size(); i++) {
|
||||
flags[i].clear();
|
||||
}
|
||||
auto writer = [&](size_t id) {
|
||||
Random rnd(4422 + static_cast<uint32_t>(id));
|
||||
for (size_t k = 0; k < kNumKeys; k++) {
|
||||
WriteBatch batch;
|
||||
batch.Put("key" + ToString(id) + "-" + ToString(k),
|
||||
test::RandomHumanReadableString(&rnd, 10));
|
||||
ASSERT_OK(dbfull()->Write(WriteOptions(), &batch));
|
||||
SequenceNumber sequence = WriteBatchInternal::Sequence(&batch);
|
||||
ASSERT_GT(sequence, 0);
|
||||
ASSERT_LE(sequence, kNumKeys * kThreads);
|
||||
// The sequence isn't consumed by someone else.
|
||||
ASSERT_FALSE(flags[sequence].test_and_set());
|
||||
}
|
||||
};
|
||||
std::vector<port::Thread> threads;
|
||||
for (size_t i = 0; i < kThreads; i++) {
|
||||
threads.emplace_back(writer, i);
|
||||
}
|
||||
for (size_t i = 0; i < kThreads; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, IOErrorOnWALWritePropagateToWriteThreadFollower) {
|
||||
constexpr int kNumThreads = 5;
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
|
||||
@@ -1940,6 +1940,55 @@ TEST_F(ExternalSSTFileTest, IngestBehind) {
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, SkipBloomFilter) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
|
||||
// Create external SST file and include bloom filters
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
{
|
||||
std::string file_path = sst_files_dir_ + "sst_with_bloom.sst";
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
ASSERT_OK(sst_file_writer.Put("Key1", "Value1"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(
|
||||
db_->IngestExternalFile({file_path}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get("Key1"), "Value1");
|
||||
ASSERT_GE(
|
||||
options.statistics->getTickerCount(Tickers::BLOCK_CACHE_FILTER_ADD), 1);
|
||||
}
|
||||
|
||||
// Create external SST file but skip bloom filters
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
{
|
||||
std::string file_path = sst_files_dir_ + "sst_with_no_bloom.sst";
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, nullptr, true,
|
||||
Env::IOPriority::IO_TOTAL,
|
||||
true /* skip_filters */);
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
ASSERT_OK(sst_file_writer.Put("Key1", "Value1"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(
|
||||
db_->IngestExternalFile({file_path}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get("Key1"), "Value1");
|
||||
ASSERT_EQ(
|
||||
options.statistics->getTickerCount(Tickers::BLOCK_CACHE_FILTER_ADD), 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -364,6 +364,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
InternalStats::CompactionStats stats(1);
|
||||
stats.micros = db_options_.env->NowMicros() - start_micros;
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
MeasureTime(stats_, FLUSH_TIME, stats.micros);
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, stats);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
meta_.fd.GetFileSize());
|
||||
|
||||
+16
-6
@@ -40,6 +40,7 @@ class FlushJobTest : public testing::Test {
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
db_options_.statistics = rocksdb::CreateDBStatistics();
|
||||
// TODO(icanadi) Remove this once we mock out VersionSet
|
||||
NewDB();
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
@@ -138,16 +139,22 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), env_options_, versions_.get(), &mutex_,
|
||||
&shutting_down_, {}, kMaxSequenceNumber, snapshot_checker, &job_context,
|
||||
nullptr, nullptr, nullptr, kNoCompression, nullptr, &event_logger, true);
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
{}, kMaxSequenceNumber, snapshot_checker, &job_context,
|
||||
nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true);
|
||||
|
||||
HistogramData hist;
|
||||
FileMetaData fd;
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run(&fd));
|
||||
mutex_.Unlock();
|
||||
db_options_.statistics->histogramData(FLUSH_TIME, &hist);
|
||||
ASSERT_GT(hist.average, 0.0);
|
||||
|
||||
ASSERT_EQ(ToString(0), fd.smallest.user_key().ToString());
|
||||
ASSERT_EQ("9999a",
|
||||
fd.largest.user_key().ToString()); // range tombstone end key
|
||||
@@ -210,12 +217,15 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
snapshots, kMaxSequenceNumber, snapshot_checker,
|
||||
&job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
nullptr, &event_logger, true);
|
||||
db_options_.statistics.get(), &event_logger, true);
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
mutex_.Unlock();
|
||||
mock_table_factory_->AssertSingleFile(inserted_keys);
|
||||
HistogramData hist;
|
||||
db_options_.statistics->histogramData(FLUSH_TIME, &hist);
|
||||
ASSERT_GT(hist.average, 0.0);
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
|
||||
@@ -566,7 +566,7 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
sv_ = cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
}
|
||||
RangeDelAggregator range_del_agg(
|
||||
InternalKeyComparator(cfd_->internal_comparator()), {} /* snapshots */);
|
||||
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) {
|
||||
@@ -621,7 +621,7 @@ 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 */);
|
||||
cfd_->internal_comparator(), {} /* snapshots */);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
svnew->mem->NewRangeTombstoneIterator(read_options_));
|
||||
|
||||
+5
-2
@@ -81,8 +81,11 @@ struct SuperVersionContext {
|
||||
struct JobContext {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return full_scan_candidate_files.size() || sst_delete_files.size() ||
|
||||
log_delete_files.size() || manifest_delete_files.size() ||
|
||||
memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
|
||||
log_delete_files.size() || manifest_delete_files.size();
|
||||
}
|
||||
|
||||
inline bool HaveSomethingToClean() const {
|
||||
return memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
|
||||
superversion_context.HaveSomethingToDelete();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ class Reader {
|
||||
// The Reader will start reading at the first record located at physical
|
||||
// position >= initial_offset within the file.
|
||||
Reader(std::shared_ptr<Logger> info_log,
|
||||
// @lint-ignore TXT2 T25377293 Grandfathered in
|
||||
unique_ptr<SequentialFileReader>&& file,
|
||||
Reporter* reporter, bool checksum, uint64_t initial_offset,
|
||||
uint64_t log_num);
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
namespace rocksdb {
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
#ifdef __FreeBSD__
|
||||
#include <malloc_np.h>
|
||||
#define je_malloc_stats_print malloc_stats_print
|
||||
#else
|
||||
#include "jemalloc/jemalloc.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char* cur;
|
||||
|
||||
@@ -236,6 +236,14 @@ int MemTable::KeyComparator::operator()(const char* prefix_len_key,
|
||||
return comparator.Compare(a, key);
|
||||
}
|
||||
|
||||
void MemTableRep::InsertConcurrently(KeyHandle handle) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
throw std::runtime_error("concurrent insert not supported");
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
Slice MemTableRep::UserKey(const char* key) const {
|
||||
Slice slice = GetLengthPrefixedSlice(key);
|
||||
return Slice(slice.data(), slice.size() - 8);
|
||||
|
||||
@@ -108,22 +108,6 @@ class SnapshotList {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Whether there is an active snapshot in range [lower_bound, upper_bound).
|
||||
bool HasSnapshotInRange(SequenceNumber lower_bound,
|
||||
SequenceNumber upper_bound) {
|
||||
if (empty()) {
|
||||
return false;
|
||||
}
|
||||
const SnapshotImpl* s = &list_;
|
||||
while (s->next_ != &list_) {
|
||||
if (s->next_->number_ >= lower_bound) {
|
||||
return s->next_->number_ < upper_bound;
|
||||
}
|
||||
s = s->next_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the sequence number of the most recent snapshot
|
||||
SequenceNumber GetNewest() {
|
||||
if (empty()) {
|
||||
|
||||
@@ -39,6 +39,15 @@ namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status WalManager::DeleteFile(const std::string& fname, uint64_t number) {
|
||||
auto s = env_->DeleteFile(db_options_.wal_dir + "/" + fname);
|
||||
if (s.ok()) {
|
||||
MutexLock l(&read_first_record_cache_mutex_);
|
||||
read_first_record_cache_.erase(number);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status WalManager::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// First get sorted files in db dir, then get sorted files from archived
|
||||
// dir, to avoid a race condition where a log file is moved to archived
|
||||
|
||||
@@ -49,6 +49,8 @@ class WalManager {
|
||||
|
||||
void ArchiveWALFile(const std::string& fname, uint64_t number);
|
||||
|
||||
Status DeleteFile(const std::string& fname, uint64_t number);
|
||||
|
||||
Status TEST_ReadFirstRecord(const WalFileType type, const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
return ReadFirstRecord(type, number, sequence);
|
||||
|
||||
+10
-2
@@ -15,10 +15,12 @@
|
||||
// kTypeValue varstring varstring
|
||||
// kTypeDeletion varstring
|
||||
// kTypeSingleDeletion varstring
|
||||
// kTypeRangeDeletion varstring varstring
|
||||
// kTypeMerge varstring varstring
|
||||
// kTypeColumnFamilyValue varint32 varstring varstring
|
||||
// kTypeColumnFamilyDeletion varint32 varstring varstring
|
||||
// kTypeColumnFamilySingleDeletion varint32 varstring varstring
|
||||
// kTypeColumnFamilyDeletion varint32 varstring
|
||||
// kTypeColumnFamilySingleDeletion varint32 varstring
|
||||
// kTypeColumnFamilyRangeDeletion varint32 varstring varstring
|
||||
// kTypeColumnFamilyMerge varint32 varstring varstring
|
||||
// kTypeBeginPrepareXID varstring
|
||||
// kTypeEndPrepareXID
|
||||
@@ -144,6 +146,12 @@ WriteBatch::WriteBatch(const std::string& rep)
|
||||
max_bytes_(0),
|
||||
rep_(rep) {}
|
||||
|
||||
WriteBatch::WriteBatch(std::string&& rep)
|
||||
: save_points_(nullptr),
|
||||
content_flags_(ContentFlags::DEFERRED),
|
||||
max_bytes_(0),
|
||||
rep_(std::move(rep)) {}
|
||||
|
||||
WriteBatch::WriteBatch(const WriteBatch& src)
|
||||
: save_points_(src.save_points_),
|
||||
wal_term_point_(src.wal_term_point_),
|
||||
|
||||
+3
-3
@@ -105,13 +105,13 @@ GEM
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9.7)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.1.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minima (2.0.0)
|
||||
minitest (5.9.1)
|
||||
multipart-post (2.0.0)
|
||||
net-dns (0.8.0)
|
||||
nokogiri (1.6.8.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
nokogiri (1.8.1)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.4.1)
|
||||
sawyer (~> 0.7.0, >= 0.5.3)
|
||||
pathutil (0.14.0)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Auto-tuned Rate Limiter
|
||||
layout: post
|
||||
author: ajkr
|
||||
category: blog
|
||||
---
|
||||
|
||||
### Introduction
|
||||
|
||||
Our rate limiter has been hard to configure since users need to pick a value that is low enough to prevent background I/O spikes, which can impact user-visible read/write latencies. Meanwhile, picking too low a value can cause memtables and L0 files to pile up, eventually leading to writes stalling. Tuning the rate limiter has been especially difficult for users whose DB instances have different workloads, or have workloads that vary over time, or commonly both.
|
||||
|
||||
To address this, in RocksDB 5.9 we released a dynamic rate limiter that adjusts itself over time according to demand for background I/O. It can be enabled simply by passing `auto_tuned=true` in the `NewGenericRateLimiter()` call. In this case `rate_bytes_per_sec` will indicate the upper-bound of the window within which a rate limit will be picked dynamically. The chosen rate limit will be much lower unless absolutely necessary, so setting this to the device's maximum throughput is a reasonable choice on dedicated hosts.
|
||||
|
||||
### Algorithm
|
||||
|
||||
We use a simple multiplicative-increase, multiplicative-decrease algorithm. We measure demand for background I/O as the ratio of intervals where the rate limiter is drained. There are low and high watermarks for this ratio, which will trigger a change in rate limit when breached. The rate limit can move within a window bounded by the user-specified upper-bound, and a lower-bound that we derive internally. Users can expect this lower bound to be 1-2 orders of magnitude less than the provided upper-bound (so don't provide INT64_MAX as your upper-bound), although it's subject to change.
|
||||
|
||||
### Benchmark Results
|
||||
|
||||
Data is ingested at 10MB/s and the rate limiter was created with 1000MB/s as its upper bound. The dynamically chosen rate limit hovers around 125MB/s. The other clustering of points at 50MB/s is due to number of compaction threads being reduced to one when there's no compaction pressure.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
The following graph summarizes the above two time series graphs in CDF form. In particular, notice the p90 - p100 for background write rate are significantly lower with auto-tuned rate limiter enabled.
|
||||
|
||||

|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: WritePrepared Transactions
|
||||
layout: post
|
||||
author: maysamyabandeh
|
||||
category: blog
|
||||
---
|
||||
|
||||
RocksDB supports both optimistic and pessimistic concurrency controls. The pessimistic transactions make use of locks to provide isolation between the transactions. The default write policy in pessimistic transactions is _WriteCommitted_, which means that the data is written to the DB, i.e., the memtable, only after the transaction is committed. This policy simplified the implementation but came with some limitations in throughput, transaction size, and variety in supported isolation levels. In the below, we explain these in detail and present the other write policies, _WritePrepared_ and _WriteUnprepared_. We then dive into the design of _WritePrepared_ transactions.
|
||||
|
||||
> _WritePrepared_ are to be announced as production-ready soon.
|
||||
|
||||
### WriteCommitted, Pros and Cons
|
||||
|
||||
With _WriteCommitted_ write policy, the data is written to the memtable only after the transaction commits. This greatly simplifies the read path as any data that is read by other transactions can be assumed to be committed. This write policy, however, implies that the writes are buffered in memory in the meanwhile. This makes memory a bottleneck for large transactions. The delay of the commit phase in 2PC (two-phase commit) also becomes noticeable since most of the work, i.e., writing to memtable, is done at the commit phase. When the commit of multiple transactions are done in a serial fashion, such as in 2PC implementation of MySQL, the lengthy commit latency becomes a major contributor to lower throughput. Moreover this write policy cannot provide weaker isolation levels, such as READ UNCOMMITTED, that could potentially provide higher throughput for some applications.
|
||||
|
||||
### Alternatives: _WritePrepared_ and _WriteUnprepared_
|
||||
|
||||
To tackle the lengthy commit issue, we should do memtable writes at earlier phases of 2PC so that the commit phase become lightweight and fast. 2PC is composed of Write stage, where the transaction `::Put` is invoked, the prepare phase, where `::Prepare` is invoked (upon which the DB promises to commit the transaction if later is requested), and commit phase, where `::Commit` is invoked and the transaction writes become visible to all readers. To make the commit phase lightweight, the memtable write could be done at either `::Prepare` or `::Put` stages, resulting into _WritePrepared_ and _WriteUnprepared_ write policies respectively. The downside is that when another transaction is reading data, it would need a way to tell apart which data is committed, and if they are, whether they are committed before the transaction's start, i.e., in the read snapshot of the transaction. _WritePrepared_ would still have the issue of buffering the data, which makes the memory the bottleneck for large transactions. It however provides a good milestone for transitioning from _WriteCommitted_ to _WriteUnprepared_ write policy. Here we explain the design of _WritePrepared_ policy. We will cover the changes that make the design to also supported _WriteUnprepared_ in an upcoming post.
|
||||
|
||||
### _WritePrepared_ in a nutshell
|
||||
|
||||
These are the primary design questions that needs to be addressed:
|
||||
1) How do we identify the key/values in the DB with transactions that wrote them?
|
||||
2) How do we figure if a key/value written by transaction Txn_w is in the read snapshot of the reading transaction Txn_r?
|
||||
3) How do we rollback the data written by aborted transactions?
|
||||
|
||||
With _WritePrepared_, a transaction still buffers the writes in a write batch object in memory. When 2PC `::Prepare` is called, it writes the in-memory write batch to the WAL (write-ahead log) as well as to the memtable(s) (one memtable per column family); We reuse the existing notion of sequence numbers in RocksDB to tag all the key/values in the same write batch with the same sequence number, `prepare_seq`, which is also used as the identifier for the transaction. At commit time, it writes a commit marker to the WAL, whose sequence number, `commit_seq`, will be used as the commit timestamp of the transaction. Before releasing the commit sequence number to the readers, it stores a mapping from `prepare_seq` to `commit_seq` in an in-memory data structure that we call _CommitCache_. When a transaction reading values from the DB (tagged with `prepare_seq`) it makes use of the _CommitCache_ to figure if `commit_seq` of the value is in its read snapshot. To rollback an aborted transaction, we apply the status before the transaction by making another write that cancels out the writes of the aborted transaction.
|
||||
|
||||
The _CommitCache_ is a lock-free data structure that caches the recent commit entries. Looking up the entries in the cache must be enough for almost all th transactions that commit in a timely manner. When evicting the older entries from the cache, it still maintains some other data structures to cover the corner cases for transactions that takes abnormally too long to finish. We will cover them in the design details below.
|
||||
|
||||
### Preliminary Results
|
||||
The full experimental results are to be reported soon. Here we present the improvement in tps observed in some preliminary experiments with MyRocks:
|
||||
* sysbench update-noindex: 25%
|
||||
* sysbench read-write: 7.6%
|
||||
* linkbench: 3.7%
|
||||
|
||||
Learn more [here](https://github.com/facebook/rocksdb/wiki/WritePrepared-Transactions).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 303 KiB |
Vendored
+10
-1
@@ -73,9 +73,18 @@ RandomAccessFile::~RandomAccessFile() {
|
||||
WritableFile::~WritableFile() {
|
||||
}
|
||||
|
||||
Logger::~Logger() {
|
||||
Logger::~Logger() { Close(); }
|
||||
|
||||
Status Logger::Close() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
return CloseImpl();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Logger::CloseImpl() { return Status::OK(); }
|
||||
|
||||
FileLock::~FileLock() {
|
||||
}
|
||||
|
||||
|
||||
Vendored
+10
-6
@@ -277,6 +277,16 @@ class HdfsLogger : public Logger {
|
||||
HdfsWritableFile* file_;
|
||||
uint64_t (*gettid_)(); // Return the thread id for the current thread
|
||||
|
||||
virtual Status CloseImpl() {
|
||||
ROCKS_LOG_DEBUG(mylog, "[hdfs] HdfsLogger closed %s\n",
|
||||
file_->getName().c_str());
|
||||
Status s = file_->Close();
|
||||
if (mylog != nullptr && mylog == this) {
|
||||
mylog = nullptr;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public:
|
||||
HdfsLogger(HdfsWritableFile* f, uint64_t (*gettid)())
|
||||
: file_(f), gettid_(gettid) {
|
||||
@@ -285,12 +295,6 @@ class HdfsLogger : public Logger {
|
||||
}
|
||||
|
||||
virtual ~HdfsLogger() {
|
||||
ROCKS_LOG_DEBUG(mylog, "[hdfs] HdfsLogger closed %s\n",
|
||||
file_->getName().c_str());
|
||||
delete file_;
|
||||
if (mylog != nullptr && mylog == this) {
|
||||
mylog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Logv(const char* format, va_list ap) {
|
||||
|
||||
Vendored
+11
-3
@@ -24,6 +24,7 @@
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include "env/io_posix.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -32,6 +33,15 @@ namespace rocksdb {
|
||||
|
||||
class PosixLogger : public Logger {
|
||||
private:
|
||||
virtual Status CloseImpl() override {
|
||||
int ret;
|
||||
|
||||
ret = fclose(file_);
|
||||
if (ret) {
|
||||
return IOError("Unable to close log file", "", ret);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
FILE* file_;
|
||||
uint64_t (*gettid_)(); // Return the thread id for the current thread
|
||||
std::atomic_size_t log_size_;
|
||||
@@ -51,9 +61,7 @@ class PosixLogger : public Logger {
|
||||
last_flush_micros_(0),
|
||||
env_(env),
|
||||
flush_pending_(false) {}
|
||||
virtual ~PosixLogger() {
|
||||
fclose(file_);
|
||||
}
|
||||
virtual ~PosixLogger() { Close(); }
|
||||
virtual void Flush() override {
|
||||
TEST_SYNC_POINT("PosixLogger::Flush:Begin1");
|
||||
TEST_SYNC_POINT("PosixLogger::Flush:Begin2");
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# shellcheck disable=SC2148
|
||||
export USE_HDFS=1
|
||||
export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64/server:$JAVA_HOME/jre/lib/amd64:/usr/lib/hadoop/lib/native
|
||||
|
||||
|
||||
@@ -710,6 +710,9 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_optimize_level_style_compaction(
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_optimize_universal_style_compaction(
|
||||
rocksdb_options_t* opt, uint64_t memtable_memory_budget);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_allow_ingest_behind(rocksdb_options_t*,
|
||||
unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t*, rocksdb_compactionfilter_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compaction_filter_factory(
|
||||
|
||||
@@ -94,10 +94,6 @@ class CompactionFilter {
|
||||
// be used by a single thread that is doing the compaction run, and this
|
||||
// call does not need to be thread-safe. However, multiple filters may be
|
||||
// in existence and operating concurrently.
|
||||
//
|
||||
// The last paragraph is not true if you set max_subcompactions to more than
|
||||
// 1. In that case, subcompaction from multiple threads may call a single
|
||||
// CompactionFilter concurrently.
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& existing_value,
|
||||
std::string* new_value, bool* value_changed) const {
|
||||
return false;
|
||||
|
||||
@@ -163,6 +163,12 @@ class DB {
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr);
|
||||
|
||||
// Close the DB by releasing resources, closing files etc. This should be
|
||||
// called before calling the desctructor so that the caller can get back a
|
||||
// status in case there are any errors. Regardless of the return status, the
|
||||
// DB must be freed
|
||||
virtual Status Close() { return Status::OK(); }
|
||||
|
||||
// ListColumnFamilies will open the DB specified by argument name
|
||||
// and return the list of all column families in that DB
|
||||
// through column_families argument. The ordering of
|
||||
@@ -973,7 +979,7 @@ class DB {
|
||||
// the file can fit in, and ingest the file into this level (2). A file that
|
||||
// have a key range that overlap with the memtable key range will require us
|
||||
// to Flush the memtable first before ingesting the file.
|
||||
// In the second mode we will always ingest in the bottom mode level (see
|
||||
// In the second mode we will always ingest in the bottom most level (see
|
||||
// docs to IngestExternalFileOptions::ingest_behind).
|
||||
//
|
||||
// (1) External SST files can be created using SstFileWriter
|
||||
|
||||
@@ -819,9 +819,12 @@ class Logger {
|
||||
size_t kDoNotSupportGetLogFileSize = (std::numeric_limits<size_t>::max)();
|
||||
|
||||
explicit Logger(const InfoLogLevel log_level = InfoLogLevel::INFO_LEVEL)
|
||||
: log_level_(log_level) {}
|
||||
: closed_(false), log_level_(log_level) {}
|
||||
virtual ~Logger();
|
||||
|
||||
// Close the log file. Must be called before destructor
|
||||
virtual Status Close();
|
||||
|
||||
// Write a header to the log file with the specified format
|
||||
// It is recommended that you log all header information at the start of the
|
||||
// application. But it is not enforced.
|
||||
@@ -852,6 +855,8 @@ class Logger {
|
||||
// No copying allowed
|
||||
Logger(const Logger&);
|
||||
void operator=(const Logger&);
|
||||
virtual Status CloseImpl();
|
||||
bool closed_;
|
||||
InfoLogLevel log_level_;
|
||||
};
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class Iterator : public Cleanable {
|
||||
// Position at the last key in the source that at or before target
|
||||
// The iterator is Valid() after this call iff the source contains
|
||||
// an entry that comes at or before target.
|
||||
virtual void SeekForPrev(const Slice& target) {}
|
||||
virtual void SeekForPrev(const Slice& target) = 0;
|
||||
|
||||
// Moves to the next entry in the source. After this call, Valid() is
|
||||
// true iff the iterator was not positioned at the last entry in the source.
|
||||
@@ -97,9 +97,6 @@ class Iterator : public Cleanable {
|
||||
// Property "rocksdb.iterator.super-version-number":
|
||||
// LSM version used by the iterator. The same format as DB Property
|
||||
// kCurrentSuperVersionNumber. See its comment for more information.
|
||||
// Property "rocksdb.iterator.internal-key":
|
||||
// Get the user-key portion of the internal key at which the iteration
|
||||
// stopped.
|
||||
virtual Status GetProperty(std::string prop_name, std::string* prop);
|
||||
|
||||
private:
|
||||
|
||||
@@ -96,13 +96,7 @@ class MemTableRep {
|
||||
|
||||
// Like Insert(handle), but may be called concurrent with other calls
|
||||
// to InsertConcurrently for other handles
|
||||
virtual void InsertConcurrently(KeyHandle handle) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
throw std::runtime_error("concurrent insert not supported");
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
virtual void InsertConcurrently(KeyHandle handle);
|
||||
|
||||
// Returns true iff an entry that compares equal to key is in the collection.
|
||||
virtual bool Contains(const char* key) const = 0;
|
||||
|
||||
@@ -72,16 +72,18 @@ class SstFileWriter {
|
||||
SstFileWriter(const EnvOptions& env_options, const Options& options,
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool invalidate_page_cache = true,
|
||||
Env::IOPriority io_priority = Env::IOPriority::IO_TOTAL)
|
||||
Env::IOPriority io_priority = Env::IOPriority::IO_TOTAL,
|
||||
bool skip_filters = false)
|
||||
: SstFileWriter(env_options, options, options.comparator, column_family,
|
||||
invalidate_page_cache, io_priority) {}
|
||||
invalidate_page_cache, io_priority, skip_filters) {}
|
||||
|
||||
// Deprecated API
|
||||
SstFileWriter(const EnvOptions& env_options, const Options& options,
|
||||
const Comparator* user_comparator,
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool invalidate_page_cache = true,
|
||||
Env::IOPriority io_priority = Env::IOPriority::IO_TOTAL);
|
||||
Env::IOPriority io_priority = Env::IOPriority::IO_TOTAL,
|
||||
bool skip_filters = false);
|
||||
|
||||
~SstFileWriter();
|
||||
|
||||
|
||||
@@ -510,6 +510,8 @@ enum Histograms : uint32_t {
|
||||
BLOB_DB_COMPRESSION_MICROS,
|
||||
// BlobDB decompression time.
|
||||
BLOB_DB_DECOMPRESSION_MICROS,
|
||||
// Time spent flushing memtable to disk
|
||||
FLUSH_TIME,
|
||||
|
||||
HISTOGRAM_ENUM_MAX, // TODO(ldemailly): enforce HistogramsNameMap match
|
||||
};
|
||||
@@ -560,6 +562,7 @@ const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
|
||||
{BLOB_DB_GC_MICROS, "rocksdb.blobdb.gc.micros"},
|
||||
{BLOB_DB_COMPRESSION_MICROS, "rocksdb.blobdb.compression.micros"},
|
||||
{BLOB_DB_DECOMPRESSION_MICROS, "rocksdb.blobdb.decompression.micros"},
|
||||
{FLUSH_TIME, "rocksdb.db.flush.micros"},
|
||||
};
|
||||
|
||||
struct HistogramData {
|
||||
|
||||
@@ -217,6 +217,11 @@ struct BlockBasedTableOptions {
|
||||
// This option only affects newly written tables. When reading exising tables,
|
||||
// the information about version is read from the footer.
|
||||
uint32_t format_version = 2;
|
||||
|
||||
// Store index blocks on disk in compressed format. Changing this option to
|
||||
// false will avoid the overhead of decompression if index blocks are evicted
|
||||
// and read back
|
||||
bool enable_index_compression = true;
|
||||
};
|
||||
|
||||
// Table Properties that are specific to block-based table properties.
|
||||
|
||||
@@ -45,6 +45,7 @@ struct ThreadStatus {
|
||||
HIGH_PRIORITY = 0, // RocksDB BG thread in high-pri thread pool
|
||||
LOW_PRIORITY, // RocksDB BG thread in low-pri thread pool
|
||||
USER, // User thread (Non-RocksDB BG thread)
|
||||
BOTTOM_PRIORITY, // RocksDB BG thread in bottom-pri thread pool
|
||||
NUM_THREAD_TYPES
|
||||
};
|
||||
|
||||
@@ -163,7 +164,7 @@ struct ThreadStatus {
|
||||
// The followings are a set of utility functions for interpreting
|
||||
// the information of ThreadStatus
|
||||
|
||||
static const std::string& GetThreadTypeName(ThreadType thread_type);
|
||||
static std::string GetThreadTypeName(ThreadType thread_type);
|
||||
|
||||
// Obtain the name of an operation given its type.
|
||||
static const std::string& GetOperationName(OperationType op_type);
|
||||
|
||||
@@ -25,7 +25,7 @@ enum EntryType {
|
||||
kEntryOther,
|
||||
};
|
||||
|
||||
// <user key, seqeence number and entry type> tuple.
|
||||
// <user key, sequence number, and entry type> tuple.
|
||||
struct FullKey {
|
||||
Slice user_key;
|
||||
SequenceNumber sequence;
|
||||
|
||||
@@ -60,6 +60,10 @@ class DBWithTTL : public StackableDB {
|
||||
DBWithTTL** dbptr, std::vector<int32_t> ttls,
|
||||
bool read_only = false);
|
||||
|
||||
virtual void SetTtl(int32_t ttl) = 0;
|
||||
|
||||
virtual void SetTtl(ColumnFamilyHandle *h, int32_t ttl) = 0;
|
||||
|
||||
protected:
|
||||
explicit DBWithTTL(DB* db) : StackableDB(db) {}
|
||||
};
|
||||
|
||||
@@ -23,18 +23,19 @@ class StackableDB : public DB {
|
||||
explicit StackableDB(DB* db) : db_(db) {}
|
||||
|
||||
// StackableDB take shared ownership of the underlying db.
|
||||
explicit StackableDB(std::shared_ptr<DB> db)
|
||||
: db_(db.get()), shared_db_ptr_(db) {}
|
||||
explicit StackableDB(std::shared_ptr<DB>& db)
|
||||
: db_(db.get()), shared_db_(db) {}
|
||||
|
||||
~StackableDB() {
|
||||
if (shared_db_ptr_ == nullptr) {
|
||||
if (shared_db_ == nullptr) {
|
||||
delete db_;
|
||||
} else {
|
||||
assert(shared_db_ptr_.get() == db_);
|
||||
assert(shared_db_.get() == db_);
|
||||
}
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
virtual Status Close() override { return db_->Close(); }
|
||||
|
||||
virtual DB* GetBaseDB() {
|
||||
return db_;
|
||||
}
|
||||
@@ -383,7 +384,7 @@ class StackableDB : public DB {
|
||||
|
||||
protected:
|
||||
DB* db_;
|
||||
std::shared_ptr<DB> shared_db_ptr_;
|
||||
std::shared_ptr<DB> shared_db_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#define ROCKSDB_MAJOR 5
|
||||
#define ROCKSDB_MINOR 10
|
||||
#define ROCKSDB_PATCH 3
|
||||
#define ROCKSDB_PATCH 0
|
||||
|
||||
// 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
|
||||
|
||||
@@ -315,6 +315,7 @@ class WriteBatch : public WriteBatchBase {
|
||||
|
||||
// Constructor with a serialized string object
|
||||
explicit WriteBatch(const std::string& rep);
|
||||
explicit WriteBatch(std::string&& rep);
|
||||
|
||||
WriteBatch(const WriteBatch& src);
|
||||
WriteBatch(WriteBatch&& src) noexcept;
|
||||
|
||||
@@ -7,6 +7,8 @@ set(JNI_NATIVE_SOURCES
|
||||
rocksjni/clock_cache.cc
|
||||
rocksjni/columnfamilyhandle.cc
|
||||
rocksjni/compaction_filter.cc
|
||||
rocksjni/compaction_filter_factory.cc
|
||||
rocksjni/compaction_filter_factory_jnicallback.cc
|
||||
rocksjni/compaction_options_fifo.cc
|
||||
rocksjni/compaction_options_universal.cc
|
||||
rocksjni/comparator.cc
|
||||
@@ -17,6 +19,7 @@ set(JNI_NATIVE_SOURCES
|
||||
rocksjni/filter.cc
|
||||
rocksjni/ingest_external_file_options.cc
|
||||
rocksjni/iterator.cc
|
||||
rocksjni/jnicallback.cc
|
||||
rocksjni/loggerjnicallback.cc
|
||||
rocksjni/lru_cache.cc
|
||||
rocksjni/memtablejni.cc
|
||||
@@ -25,6 +28,7 @@ set(JNI_NATIVE_SOURCES
|
||||
rocksjni/options_util.cc
|
||||
rocksjni/ratelimiterjni.cc
|
||||
rocksjni/remove_emptyvalue_compactionfilterjni.cc
|
||||
rocksjni/rocks_callback_object.cc
|
||||
rocksjni/cassandra_compactionfilterjni.cc
|
||||
rocksjni/cassandra_value_operator.cc
|
||||
rocksjni/restorejni.cc
|
||||
@@ -204,6 +208,7 @@ add_jar(
|
||||
src/main/java/org/rocksdb/OptionsUtil.java
|
||||
src/main/java/org/rocksdb/PlainTableConfig.java
|
||||
src/main/java/org/rocksdb/RateLimiter.java
|
||||
src/main/java/org/rocksdb/RateLimiterMode.java
|
||||
src/main/java/org/rocksdb/ReadOptions.java
|
||||
src/main/java/org/rocksdb/ReadTier.java
|
||||
src/main/java/org/rocksdb/RemoveEmptyValueCompactionFilter.java
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# shellcheck disable=SC2148
|
||||
PLATFORM=64
|
||||
if [ `getconf LONG_BIT` != "64" ]
|
||||
then
|
||||
@@ -8,5 +7,4 @@ fi
|
||||
ROCKS_JAR=`find target -name rocksdbjni*.jar`
|
||||
|
||||
echo "Running benchmark in $PLATFORM-Bit mode."
|
||||
# shellcheck disable=SC2068
|
||||
java -server -d$PLATFORM -XX:NewSize=4m -XX:+AggressiveOpts -Djava.library.path=target -cp "${ROCKS_JAR}:benchmark/target/classes" org.rocksdb.benchmark.DbBenchmark $@
|
||||
|
||||
@@ -34,5 +34,4 @@ void Java_org_rocksdb_AbstractCompactionFilterFactory_disposeInternal(
|
||||
auto* ptr_sptr_cff =
|
||||
reinterpret_cast<std::shared_ptr<rocksdb::CompactionFilterFactoryJniCallback> *>(jhandle);
|
||||
delete ptr_sptr_cff;
|
||||
// @lint-ignore TXT4 T25377293 Grandfathered in
|
||||
}
|
||||
@@ -146,5 +146,4 @@ void Java_org_rocksdb_IngestExternalFileOptions_disposeInternal(
|
||||
auto* options =
|
||||
reinterpret_cast<rocksdb::IngestExternalFileOptions*>(jhandle);
|
||||
delete options;
|
||||
// @lint-ignore TXT4 T25377293 Grandfathered in
|
||||
}
|
||||
@@ -49,5 +49,4 @@ JniCallback::~JniCallback() {
|
||||
|
||||
releaseJniEnv(attached_thread);
|
||||
}
|
||||
// @lint-ignore TXT4 T25377293 Grandfathered in
|
||||
} // namespace rocksdb
|
||||
@@ -25,5 +25,4 @@ namespace rocksdb {
|
||||
};
|
||||
}
|
||||
|
||||
// @lint-ignore TXT4 T25377293 Grandfathered in
|
||||
#endif // JAVA_ROCKSJNI_JNICALLBACK_H_
|
||||
+45
-1
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/utilities/backupable_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
@@ -2806,8 +2807,10 @@ class HistogramTypeJni {
|
||||
return 0x1D;
|
||||
case rocksdb::Histograms::READ_NUM_MERGE_OPERANDS:
|
||||
return 0x1E;
|
||||
case rocksdb::Histograms::HISTOGRAM_ENUM_MAX:
|
||||
case rocksdb::Histograms::FLUSH_TIME:
|
||||
return 0x1F;
|
||||
case rocksdb::Histograms::HISTOGRAM_ENUM_MAX:
|
||||
return 0x20;
|
||||
|
||||
default:
|
||||
// undefined/default
|
||||
@@ -2882,6 +2885,8 @@ class HistogramTypeJni {
|
||||
case 0x1E:
|
||||
return rocksdb::Histograms::READ_NUM_MERGE_OPERANDS;
|
||||
case 0x1F:
|
||||
return rocksdb::Histograms::FLUSH_TIME;
|
||||
case 0x20:
|
||||
return rocksdb::Histograms::HISTOGRAM_ENUM_MAX;
|
||||
|
||||
default:
|
||||
@@ -2930,6 +2935,45 @@ class StatsLevelJni {
|
||||
}
|
||||
};
|
||||
|
||||
// The portal class for org.rocksdb.RateLimiterMode
|
||||
class RateLimiterModeJni {
|
||||
public:
|
||||
// Returns the equivalent org.rocksdb.RateLimiterMode for the provided
|
||||
// C++ rocksdb::RateLimiter::Mode enum
|
||||
static jbyte toJavaRateLimiterMode(
|
||||
const rocksdb::RateLimiter::Mode& rate_limiter_mode) {
|
||||
switch(rate_limiter_mode) {
|
||||
case rocksdb::RateLimiter::Mode::kReadsOnly:
|
||||
return 0x0;
|
||||
case rocksdb::RateLimiter::Mode::kWritesOnly:
|
||||
return 0x1;
|
||||
case rocksdb::RateLimiter::Mode::kAllIo:
|
||||
return 0x2;
|
||||
|
||||
default:
|
||||
// undefined/default
|
||||
return 0x1;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the equivalent C++ rocksdb::RateLimiter::Mode enum for the
|
||||
// provided Java org.rocksdb.RateLimiterMode
|
||||
static rocksdb::RateLimiter::Mode toCppRateLimiterMode(jbyte jrate_limiter_mode) {
|
||||
switch(jrate_limiter_mode) {
|
||||
case 0x0:
|
||||
return rocksdb::RateLimiter::Mode::kReadsOnly;
|
||||
case 0x1:
|
||||
return rocksdb::RateLimiter::Mode::kWritesOnly;
|
||||
case 0x2:
|
||||
return rocksdb::RateLimiter::Mode::kAllIo;
|
||||
|
||||
default:
|
||||
// undefined/default
|
||||
return rocksdb::RateLimiter::Mode::kWritesOnly;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// various utility functions for working with RocksDB and JNI
|
||||
class JniUtil {
|
||||
public:
|
||||
|
||||
@@ -12,16 +12,21 @@
|
||||
/*
|
||||
* Class: org_rocksdb_RateLimiter
|
||||
* Method: newRateLimiterHandle
|
||||
* Signature: (JJI)J
|
||||
* Signature: (JJIBZ)J
|
||||
*/
|
||||
jlong Java_org_rocksdb_RateLimiter_newRateLimiterHandle(
|
||||
JNIEnv* env, jclass jclazz, jlong jrate_bytes_per_second,
|
||||
jlong jrefill_period_micros, jint jfairness) {
|
||||
jlong jrefill_period_micros, jint jfairness, jbyte jrate_limiter_mode,
|
||||
jboolean jauto_tune) {
|
||||
auto rate_limiter_mode = rocksdb::RateLimiterModeJni::toCppRateLimiterMode(
|
||||
jrate_limiter_mode);
|
||||
auto * sptr_rate_limiter =
|
||||
new std::shared_ptr<rocksdb::RateLimiter>(rocksdb::NewGenericRateLimiter(
|
||||
static_cast<int64_t>(jrate_bytes_per_second),
|
||||
static_cast<int64_t>(jrefill_period_micros),
|
||||
static_cast<int32_t>(jfairness)));
|
||||
static_cast<int32_t>(jfairness),
|
||||
rate_limiter_mode,
|
||||
jauto_tune));
|
||||
|
||||
return reinterpret_cast<jlong>(sptr_rate_limiter);
|
||||
}
|
||||
@@ -50,6 +55,17 @@ void Java_org_rocksdb_RateLimiter_setBytesPerSecond(
|
||||
SetBytesPerSecond(jbytes_per_second);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_RateLimiter
|
||||
* Method: getBytesPerSecond
|
||||
* Signature: (J)J
|
||||
*/
|
||||
jlong Java_org_rocksdb_RateLimiter_getBytesPerSecond(
|
||||
JNIEnv* env, jobject jobj, jlong handle) {
|
||||
return reinterpret_cast<std::shared_ptr<rocksdb::RateLimiter> *>(handle)->get()->
|
||||
GetBytesPerSecond();
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_RateLimiter
|
||||
* Method: request
|
||||
|
||||
@@ -24,5 +24,4 @@ void Java_org_rocksdb_RocksCallbackObject_disposeInternal(
|
||||
// 2) Comparator -> BaseComparatorJniCallback + JniCallback -> ComparatorJniCallback
|
||||
// I think this is okay, as Comparator and JniCallback both have virtual destructors...
|
||||
delete reinterpret_cast<rocksdb::JniCallback*>(handle);
|
||||
// @lint-ignore TXT4 T25377293 Grandfathered in
|
||||
}
|
||||
@@ -30,5 +30,4 @@ namespace rocksdb {
|
||||
|
||||
return true;
|
||||
}
|
||||
// @lint-ignore TXT4 T25377293 Grandfathered in
|
||||
};
|
||||
@@ -30,5 +30,4 @@ namespace rocksdb {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
// @lint-ignore TXT4 T25377293 Grandfathered in
|
||||
#endif // JAVA_ROCKSJNI_STATISTICSJNI_H_
|
||||
@@ -12,8 +12,11 @@ package org.rocksdb;
|
||||
* @since 3.10.0
|
||||
*/
|
||||
public class RateLimiter extends RocksObject {
|
||||
private static final long DEFAULT_REFILL_PERIOD_MICROS = (100 * 1000);
|
||||
private static final int DEFAULT_FAIRNESS = 10;
|
||||
public static final long DEFAULT_REFILL_PERIOD_MICROS = 100 * 1000;
|
||||
public static final int DEFAULT_FAIRNESS = 10;
|
||||
public static final RateLimiterMode DEFAULT_MODE =
|
||||
RateLimiterMode.WRITES_ONLY;
|
||||
public static final boolean DEFAULT_AUTOTUNE = false;
|
||||
|
||||
/**
|
||||
* RateLimiter constructor
|
||||
@@ -21,24 +24,12 @@ public class RateLimiter extends RocksObject {
|
||||
* @param rateBytesPerSecond this is the only parameter you want to set
|
||||
* most of the time. It controls the total write rate of compaction
|
||||
* and flush in bytes per second. Currently, RocksDB does not enforce
|
||||
* rate limit for anything other than flush and compaction, e.g. write to WAL.
|
||||
* @param refillPeriodMicros this controls how often tokens are refilled. For example,
|
||||
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
|
||||
* 100ms, then 1MB is refilled every 100ms internally. Larger value can lead to
|
||||
* burstier writes while smaller value introduces more CPU overhead.
|
||||
* The default should work for most cases.
|
||||
* @param fairness RateLimiter accepts high-pri requests and low-pri requests.
|
||||
* A low-pri request is usually blocked in favor of hi-pri request. Currently,
|
||||
* RocksDB assigns low-pri to request from compaction and high-pri to request
|
||||
* from flush. Low-pri requests can get blocked if flush requests come in
|
||||
* continuously. This fairness parameter grants low-pri requests permission by
|
||||
* fairness chance even though high-pri requests exist to avoid starvation.
|
||||
* You should be good by leaving it at default 10.
|
||||
* rate limit for anything other than flush and compaction, e.g. write to
|
||||
* WAL.
|
||||
*/
|
||||
public RateLimiter(final long rateBytesPerSecond,
|
||||
final long refillPeriodMicros, final int fairness) {
|
||||
super(newRateLimiterHandle(rateBytesPerSecond,
|
||||
refillPeriodMicros, fairness));
|
||||
public RateLimiter(final long rateBytesPerSecond) {
|
||||
this(rateBytesPerSecond, DEFAULT_REFILL_PERIOD_MICROS, DEFAULT_FAIRNESS,
|
||||
DEFAULT_MODE, DEFAULT_AUTOTUNE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,10 +38,115 @@ public class RateLimiter extends RocksObject {
|
||||
* @param rateBytesPerSecond this is the only parameter you want to set
|
||||
* most of the time. It controls the total write rate of compaction
|
||||
* and flush in bytes per second. Currently, RocksDB does not enforce
|
||||
* rate limit for anything other than flush and compaction, e.g. write to WAL.
|
||||
* rate limit for anything other than flush and compaction, e.g. write to
|
||||
* WAL.
|
||||
* @param refillPeriodMicros this controls how often tokens are refilled. For
|
||||
* example,
|
||||
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
|
||||
* 100ms, then 1MB is refilled every 100ms internally. Larger value can
|
||||
* lead to burstier writes while smaller value introduces more CPU
|
||||
* overhead. The default of 100,000ms should work for most cases.
|
||||
*/
|
||||
public RateLimiter(final long rateBytesPerSecond) {
|
||||
this(rateBytesPerSecond, DEFAULT_REFILL_PERIOD_MICROS, DEFAULT_FAIRNESS);
|
||||
public RateLimiter(final long rateBytesPerSecond,
|
||||
final long refillPeriodMicros) {
|
||||
this(rateBytesPerSecond, refillPeriodMicros, DEFAULT_FAIRNESS, DEFAULT_MODE,
|
||||
DEFAULT_AUTOTUNE);
|
||||
}
|
||||
|
||||
/**
|
||||
* RateLimiter constructor
|
||||
*
|
||||
* @param rateBytesPerSecond this is the only parameter you want to set
|
||||
* most of the time. It controls the total write rate of compaction
|
||||
* and flush in bytes per second. Currently, RocksDB does not enforce
|
||||
* rate limit for anything other than flush and compaction, e.g. write to
|
||||
* WAL.
|
||||
* @param refillPeriodMicros this controls how often tokens are refilled. For
|
||||
* example,
|
||||
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
|
||||
* 100ms, then 1MB is refilled every 100ms internally. Larger value can
|
||||
* lead to burstier writes while smaller value introduces more CPU
|
||||
* overhead. The default of 100,000ms should work for most cases.
|
||||
* @param fairness RateLimiter accepts high-pri requests and low-pri requests.
|
||||
* A low-pri request is usually blocked in favor of hi-pri request.
|
||||
* Currently, RocksDB assigns low-pri to request from compaction and
|
||||
* high-pri to request from flush. Low-pri requests can get blocked if
|
||||
* flush requests come in continuously. This fairness parameter grants
|
||||
* low-pri requests permission by fairness chance even though high-pri
|
||||
* requests exist to avoid starvation.
|
||||
* You should be good by leaving it at default 10.
|
||||
*/
|
||||
public RateLimiter(final long rateBytesPerSecond,
|
||||
final long refillPeriodMicros, final int fairness) {
|
||||
this(rateBytesPerSecond, refillPeriodMicros, fairness, DEFAULT_MODE,
|
||||
DEFAULT_AUTOTUNE);
|
||||
}
|
||||
|
||||
/**
|
||||
* RateLimiter constructor
|
||||
*
|
||||
* @param rateBytesPerSecond this is the only parameter you want to set
|
||||
* most of the time. It controls the total write rate of compaction
|
||||
* and flush in bytes per second. Currently, RocksDB does not enforce
|
||||
* rate limit for anything other than flush and compaction, e.g. write to
|
||||
* WAL.
|
||||
* @param refillPeriodMicros this controls how often tokens are refilled. For
|
||||
* example,
|
||||
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
|
||||
* 100ms, then 1MB is refilled every 100ms internally. Larger value can
|
||||
* lead to burstier writes while smaller value introduces more CPU
|
||||
* overhead. The default of 100,000ms should work for most cases.
|
||||
* @param fairness RateLimiter accepts high-pri requests and low-pri requests.
|
||||
* A low-pri request is usually blocked in favor of hi-pri request.
|
||||
* Currently, RocksDB assigns low-pri to request from compaction and
|
||||
* high-pri to request from flush. Low-pri requests can get blocked if
|
||||
* flush requests come in continuously. This fairness parameter grants
|
||||
* low-pri requests permission by fairness chance even though high-pri
|
||||
* requests exist to avoid starvation.
|
||||
* You should be good by leaving it at default 10.
|
||||
* @param rateLimiterMode indicates which types of operations count against
|
||||
* the limit.
|
||||
*/
|
||||
public RateLimiter(final long rateBytesPerSecond,
|
||||
final long refillPeriodMicros, final int fairness,
|
||||
final RateLimiterMode rateLimiterMode) {
|
||||
this(rateBytesPerSecond, refillPeriodMicros, fairness, rateLimiterMode,
|
||||
DEFAULT_AUTOTUNE);
|
||||
}
|
||||
|
||||
/**
|
||||
* RateLimiter constructor
|
||||
*
|
||||
* @param rateBytesPerSecond this is the only parameter you want to set
|
||||
* most of the time. It controls the total write rate of compaction
|
||||
* and flush in bytes per second. Currently, RocksDB does not enforce
|
||||
* rate limit for anything other than flush and compaction, e.g. write to
|
||||
* WAL.
|
||||
* @param refillPeriodMicros this controls how often tokens are refilled. For
|
||||
* example,
|
||||
* when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
|
||||
* 100ms, then 1MB is refilled every 100ms internally. Larger value can
|
||||
* lead to burstier writes while smaller value introduces more CPU
|
||||
* overhead. The default of 100,000ms should work for most cases.
|
||||
* @param fairness RateLimiter accepts high-pri requests and low-pri requests.
|
||||
* A low-pri request is usually blocked in favor of hi-pri request.
|
||||
* Currently, RocksDB assigns low-pri to request from compaction and
|
||||
* high-pri to request from flush. Low-pri requests can get blocked if
|
||||
* flush requests come in continuously. This fairness parameter grants
|
||||
* low-pri requests permission by fairness chance even though high-pri
|
||||
* requests exist to avoid starvation.
|
||||
* You should be good by leaving it at default 10.
|
||||
* @param rateLimiterMode indicates which types of operations count against
|
||||
* the limit.
|
||||
* @param autoTune Enables dynamic adjustment of rate limit within the range
|
||||
* {@code [rate_bytes_per_sec / 20, rate_bytes_per_sec]}, according to
|
||||
* the recent demand for background I/O.
|
||||
*/
|
||||
public RateLimiter(final long rateBytesPerSecond,
|
||||
final long refillPeriodMicros, final int fairness,
|
||||
final RateLimiterMode rateLimiterMode, final boolean autoTune) {
|
||||
super(newRateLimiterHandle(rateBytesPerSecond,
|
||||
refillPeriodMicros, fairness, rateLimiterMode.getValue(), autoTune));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,6 +160,16 @@ public class RateLimiter extends RocksObject {
|
||||
setBytesPerSecond(nativeHandle_, bytesPerSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes per second.
|
||||
*
|
||||
* @return bytes per second.
|
||||
*/
|
||||
public long getBytesPerSecond() {
|
||||
assert(isOwningHandle());
|
||||
return getBytesPerSecond(nativeHandle_);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Request for token to write bytes. If this request can not be satisfied,
|
||||
* the call is blocked. Caller is responsible to make sure
|
||||
@@ -107,11 +213,13 @@ public class RateLimiter extends RocksObject {
|
||||
}
|
||||
|
||||
private static native long newRateLimiterHandle(final long rateBytesPerSecond,
|
||||
final long refillPeriodMicros, final int fairness);
|
||||
final long refillPeriodMicros, final int fairness,
|
||||
final byte rateLimiterMode, final boolean autoTune);
|
||||
@Override protected final native void disposeInternal(final long handle);
|
||||
|
||||
private native void setBytesPerSecond(final long handle,
|
||||
final long bytesPerSecond);
|
||||
private native long getBytesPerSecond(final long handle);
|
||||
private native void request(final long handle, final long bytes);
|
||||
private native long getSingleBurstBytes(final long handle);
|
||||
private native long getTotalBytesThrough(final long handle);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* Mode for {@link RateLimiter#RateLimiter(long, long, int, RateLimiterMode)}.
|
||||
*/
|
||||
public enum RateLimiterMode {
|
||||
READS_ONLY((byte)0x0),
|
||||
WRITES_ONLY((byte)0x1),
|
||||
ALL_IO((byte)0x2);
|
||||
|
||||
private final byte value;
|
||||
|
||||
RateLimiterMode(final byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the byte value of the enumerations value.</p>
|
||||
*
|
||||
* @return byte representation
|
||||
*/
|
||||
public byte getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Get the RateLimiterMode enumeration value by
|
||||
* passing the byte identifier to this method.</p>
|
||||
*
|
||||
* @param byteIdentifier of RateLimiterMode.
|
||||
*
|
||||
* @return AccessHint instance.
|
||||
*
|
||||
* @throws IllegalArgumentException if the access hint for the byteIdentifier
|
||||
* cannot be found
|
||||
*/
|
||||
public static RateLimiterMode getRateLimiterMode(final byte byteIdentifier) {
|
||||
for (final RateLimiterMode rateLimiterMode : RateLimiterMode.values()) {
|
||||
if (rateLimiterMode.getValue() == byteIdentifier) {
|
||||
return rateLimiterMode;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"Illegal value provided for RateLimiterMode.");
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.rocksdb.RateLimiter.*;
|
||||
|
||||
public class RateLimiterTest {
|
||||
|
||||
@@ -16,17 +17,21 @@ public class RateLimiterTest {
|
||||
new RocksMemoryResource();
|
||||
|
||||
@Test
|
||||
public void setBytesPerSecond() {
|
||||
public void bytesPerSecond() {
|
||||
try(final RateLimiter rateLimiter =
|
||||
new RateLimiter(1000, 100 * 1000, 1)) {
|
||||
new RateLimiter(1000, DEFAULT_REFILL_PERIOD_MICROS,
|
||||
DEFAULT_FAIRNESS, DEFAULT_MODE, DEFAULT_AUTOTUNE)) {
|
||||
assertThat(rateLimiter.getBytesPerSecond()).isGreaterThan(0);
|
||||
rateLimiter.setBytesPerSecond(2000);
|
||||
assertThat(rateLimiter.getBytesPerSecond()).isGreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSingleBurstBytes() {
|
||||
try(final RateLimiter rateLimiter =
|
||||
new RateLimiter(1000, 100 * 1000, 1)) {
|
||||
new RateLimiter(1000, DEFAULT_REFILL_PERIOD_MICROS,
|
||||
DEFAULT_FAIRNESS, DEFAULT_MODE, DEFAULT_AUTOTUNE)) {
|
||||
assertThat(rateLimiter.getSingleBurstBytes()).isEqualTo(100);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +39,8 @@ public class RateLimiterTest {
|
||||
@Test
|
||||
public void getTotalBytesThrough() {
|
||||
try(final RateLimiter rateLimiter =
|
||||
new RateLimiter(1000, 100 * 1000, 1)) {
|
||||
new RateLimiter(1000, DEFAULT_REFILL_PERIOD_MICROS,
|
||||
DEFAULT_FAIRNESS, DEFAULT_MODE, DEFAULT_AUTOTUNE)) {
|
||||
assertThat(rateLimiter.getTotalBytesThrough()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
@@ -42,8 +48,18 @@ public class RateLimiterTest {
|
||||
@Test
|
||||
public void getTotalRequests() {
|
||||
try(final RateLimiter rateLimiter =
|
||||
new RateLimiter(1000, 100 * 1000, 1)) {
|
||||
new RateLimiter(1000, DEFAULT_REFILL_PERIOD_MICROS,
|
||||
DEFAULT_FAIRNESS, DEFAULT_MODE, DEFAULT_AUTOTUNE)) {
|
||||
assertThat(rateLimiter.getTotalRequests()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoTune() {
|
||||
try(final RateLimiter rateLimiter =
|
||||
new RateLimiter(1000, DEFAULT_REFILL_PERIOD_MICROS,
|
||||
DEFAULT_FAIRNESS, DEFAULT_MODE, true)) {
|
||||
assertThat(rateLimiter.getBytesPerSecond()).isGreaterThan(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,21 @@
|
||||
namespace rocksdb {
|
||||
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
const std::string& ThreadStatus::GetThreadTypeName(
|
||||
std::string ThreadStatus::GetThreadTypeName(
|
||||
ThreadStatus::ThreadType thread_type) {
|
||||
static std::string thread_type_names[NUM_THREAD_TYPES + 1] = {
|
||||
"High Pri", "Low Pri", "User", "Unknown"};
|
||||
if (thread_type < 0 || thread_type >= NUM_THREAD_TYPES) {
|
||||
return thread_type_names[NUM_THREAD_TYPES]; // "Unknown"
|
||||
switch (thread_type) {
|
||||
case ThreadStatus::ThreadType::HIGH_PRIORITY:
|
||||
return "High Pri";
|
||||
case ThreadStatus::ThreadType::LOW_PRIORITY:
|
||||
return "Low Pri";
|
||||
case ThreadStatus::ThreadType::USER:
|
||||
return "User";
|
||||
case ThreadStatus::ThreadType::BOTTOM_PRIORITY:
|
||||
return "Bottom Pri";
|
||||
case ThreadStatus::ThreadType::NUM_THREAD_TYPES:
|
||||
assert(false);
|
||||
}
|
||||
return thread_type_names[thread_type];
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const std::string& ThreadStatus::GetOperationName(
|
||||
@@ -120,7 +127,7 @@ std::map<std::string, uint64_t>
|
||||
|
||||
#else
|
||||
|
||||
const std::string& ThreadStatus::GetThreadTypeName(
|
||||
std::string ThreadStatus::GetThreadTypeName(
|
||||
ThreadStatus::ThreadType thread_type) {
|
||||
static std::string dummy_str = "";
|
||||
return dummy_str;
|
||||
|
||||
+12
-15
@@ -208,6 +208,18 @@ std::unordered_map<std::string, ChecksumType>
|
||||
{"kCRC32c", kCRC32c},
|
||||
{"kxxHash", kxxHash}};
|
||||
|
||||
std::unordered_map<std::string, CompressionType>
|
||||
OptionsHelper::compression_type_string_map = {
|
||||
{"kNoCompression", kNoCompression},
|
||||
{"kSnappyCompression", kSnappyCompression},
|
||||
{"kZlibCompression", kZlibCompression},
|
||||
{"kBZip2Compression", kBZip2Compression},
|
||||
{"kLZ4Compression", kLZ4Compression},
|
||||
{"kLZ4HCCompression", kLZ4HCCompression},
|
||||
{"kXpressCompression", kXpressCompression},
|
||||
{"kZSTD", kZSTD},
|
||||
{"kZSTDNotFinalCompression", kZSTDNotFinalCompression},
|
||||
{"kDisableCompressionOption", kDisableCompressionOption}};
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
template <typename T>
|
||||
@@ -1476,19 +1488,6 @@ std::unordered_map<std::string, OptionTypeInfo>
|
||||
{0, OptionType::kBoolean, OptionVerificationType::kDeprecated, false,
|
||||
0}}};
|
||||
|
||||
std::unordered_map<std::string, CompressionType>
|
||||
OptionsHelper::compression_type_string_map = {
|
||||
{"kNoCompression", kNoCompression},
|
||||
{"kSnappyCompression", kSnappyCompression},
|
||||
{"kZlibCompression", kZlibCompression},
|
||||
{"kBZip2Compression", kBZip2Compression},
|
||||
{"kLZ4Compression", kLZ4Compression},
|
||||
{"kLZ4HCCompression", kLZ4HCCompression},
|
||||
{"kXpressCompression", kXpressCompression},
|
||||
{"kZSTD", kZSTD},
|
||||
{"kZSTDNotFinalCompression", kZSTDNotFinalCompression},
|
||||
{"kDisableCompressionOption", kDisableCompressionOption}};
|
||||
|
||||
std::unordered_map<std::string, BlockBasedTableOptions::IndexType>
|
||||
OptionsHelper::block_base_table_index_type_string_map = {
|
||||
{"kBinarySearch", BlockBasedTableOptions::IndexType::kBinarySearch},
|
||||
@@ -1582,8 +1581,6 @@ int offset_of(T1 CompactionOptionsUniversal::*member) {
|
||||
std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionsHelper::cf_options_type_info = {
|
||||
/* not yet supported
|
||||
CompactionOptionsFIFO compaction_options_fifo;
|
||||
CompactionOptionsUniversal compaction_options_universal;
|
||||
CompressionOptions compression_opts;
|
||||
TablePropertiesCollectorFactories table_properties_collector_factories;
|
||||
typedef std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
|
||||
|
||||
@@ -144,6 +144,8 @@ struct OptionsHelper {
|
||||
static std::map<CompactionStopStyle, std::string>
|
||||
compaction_stop_style_to_string;
|
||||
static std::unordered_map<std::string, ChecksumType> checksum_type_string_map;
|
||||
static std::unordered_map<std::string, CompressionType>
|
||||
compression_type_string_map;
|
||||
#ifndef ROCKSDB_LITE
|
||||
static std::unordered_map<std::string, OptionTypeInfo> cf_options_type_info;
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
@@ -155,8 +157,6 @@ struct OptionsHelper {
|
||||
static std::unordered_map<std::string, OptionTypeInfo> db_options_type_info;
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
lru_cache_options_type_info;
|
||||
static std::unordered_map<std::string, CompressionType>
|
||||
compression_type_string_map;
|
||||
static std::unordered_map<std::string, BlockBasedTableOptions::IndexType>
|
||||
block_base_table_index_type_string_map;
|
||||
static std::unordered_map<std::string, EncodingType> encoding_type_string_map;
|
||||
|
||||
@@ -199,7 +199,7 @@ Status RocksDBOptionsParser::ParseStatement(std::string* name,
|
||||
namespace {
|
||||
bool ReadOneLine(std::istringstream* iss, SequentialFile* seq_file,
|
||||
std::string* output, bool* has_data, Status* result) {
|
||||
const int kBufferSize = 4096;
|
||||
const int kBufferSize = 8192;
|
||||
char buffer[kBufferSize + 1];
|
||||
Slice input_slice;
|
||||
|
||||
|
||||
@@ -150,7 +150,8 @@ TEST_F(OptionsSettableTest, BlockBasedTableOptionsAllFieldsSettable) {
|
||||
"filter_policy=bloomfilter:4:true;whole_key_filtering=1;"
|
||||
"format_version=1;"
|
||||
"hash_index_allow_collision=false;"
|
||||
"verify_compression=true;read_amp_bytes_per_bit=0",
|
||||
"verify_compression=true;read_amp_bytes_per_bit=0;"
|
||||
"enable_index_compression=false",
|
||||
new_bbto));
|
||||
|
||||
ASSERT_EQ(unset_bytes_base,
|
||||
|
||||
+1
-1
@@ -253,7 +253,7 @@ Status WinEnvIO::OpenWritableFile(const std::string& fname,
|
||||
DWORD fileFlags = FILE_ATTRIBUTE_NORMAL;
|
||||
|
||||
if (local_options.use_direct_writes && !local_options.use_mmap_writes) {
|
||||
fileFlags = FILE_FLAG_NO_BUFFERING;
|
||||
fileFlags = FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
|
||||
}
|
||||
|
||||
// Desired access. We are want to write only here but if we want to memory
|
||||
|
||||
+3
-1
@@ -880,7 +880,7 @@ inline
|
||||
Status WinWritableImpl::SyncImpl() {
|
||||
Status s;
|
||||
// Calls flush buffers
|
||||
if (fsync(file_data_->GetFileHandle()) < 0) {
|
||||
if (!file_data_->use_direct_io() && fsync(file_data_->GetFileHandle()) < 0) {
|
||||
auto lastError = GetLastError();
|
||||
s = IOErrorFromWindowsError(
|
||||
"fsync failed at Sync() for: " + file_data_->GetName(), lastError);
|
||||
@@ -963,6 +963,8 @@ Status WinWritableFile::Sync() {
|
||||
|
||||
Status WinWritableFile::Fsync() { return SyncImpl(); }
|
||||
|
||||
bool WinWritableFile::IsSyncThreadSafe() const { return true; }
|
||||
|
||||
uint64_t WinWritableFile::GetFileSize() {
|
||||
return GetFileNextWriteOffset();
|
||||
}
|
||||
|
||||
@@ -368,6 +368,8 @@ class WinWritableFile : private WinFileData,
|
||||
|
||||
virtual Status Fsync() override;
|
||||
|
||||
virtual bool IsSyncThreadSafe() const override;
|
||||
|
||||
// Indicates if the class makes use of direct I/O
|
||||
// Use PositionedAppend
|
||||
virtual bool use_direct_io() const override;
|
||||
|
||||
@@ -96,6 +96,7 @@ LIB_SOURCES = \
|
||||
table/block_based_table_factory.cc \
|
||||
table/block_based_table_reader.cc \
|
||||
table/block_builder.cc \
|
||||
table/block_fetcher.cc \
|
||||
table/block_prefix_index.cc \
|
||||
table/bloom_block.cc \
|
||||
table/cuckoo_table_builder.cc \
|
||||
|
||||
+16
-24
@@ -421,36 +421,28 @@ Block::Block(BlockContents&& contents, SequenceNumber _global_seqno,
|
||||
}
|
||||
}
|
||||
|
||||
InternalIterator* Block::NewIterator(const Comparator* cmp, BlockIter* iter,
|
||||
bool total_order_seek, Statistics* stats) {
|
||||
BlockIter* Block::NewIterator(const Comparator* cmp, BlockIter* iter,
|
||||
bool total_order_seek, Statistics* stats) {
|
||||
BlockIter* ret_iter;
|
||||
if (iter != nullptr) {
|
||||
ret_iter = iter;
|
||||
} else {
|
||||
ret_iter = new BlockIter;
|
||||
}
|
||||
if (size_ < 2*sizeof(uint32_t)) {
|
||||
if (iter != nullptr) {
|
||||
iter->SetStatus(Status::Corruption("bad block contents"));
|
||||
return iter;
|
||||
} else {
|
||||
return NewErrorInternalIterator(Status::Corruption("bad block contents"));
|
||||
}
|
||||
ret_iter->SetStatus(Status::Corruption("bad block contents"));
|
||||
return ret_iter;
|
||||
}
|
||||
const uint32_t num_restarts = NumRestarts();
|
||||
if (num_restarts == 0) {
|
||||
if (iter != nullptr) {
|
||||
iter->SetStatus(Status::OK());
|
||||
return iter;
|
||||
} else {
|
||||
return NewEmptyInternalIterator();
|
||||
}
|
||||
ret_iter->SetStatus(Status::OK());
|
||||
return ret_iter;
|
||||
} else {
|
||||
BlockPrefixIndex* prefix_index_ptr =
|
||||
total_order_seek ? nullptr : prefix_index_.get();
|
||||
|
||||
if (iter != nullptr) {
|
||||
iter->Initialize(cmp, data_, restart_offset_, num_restarts,
|
||||
prefix_index_ptr, global_seqno_, read_amp_bitmap_.get());
|
||||
} else {
|
||||
iter = new BlockIter(cmp, data_, restart_offset_, num_restarts,
|
||||
prefix_index_ptr, global_seqno_,
|
||||
read_amp_bitmap_.get());
|
||||
}
|
||||
ret_iter->Initialize(cmp, data_, restart_offset_, num_restarts,
|
||||
prefix_index_ptr, global_seqno_,
|
||||
read_amp_bitmap_.get());
|
||||
|
||||
if (read_amp_bitmap_) {
|
||||
if (read_amp_bitmap_->GetStatistics() != stats) {
|
||||
@@ -460,7 +452,7 @@ InternalIterator* Block::NewIterator(const Comparator* cmp, BlockIter* iter,
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
return ret_iter;
|
||||
}
|
||||
|
||||
void Block::SetBlockPrefixIndex(BlockPrefixIndex* prefix_index) {
|
||||
|
||||
+9
-6
@@ -168,10 +168,10 @@ class Block {
|
||||
// If total_order_seek is true, hash_index_ and prefix_index_ are ignored.
|
||||
// This option only applies for index block. For data block, hash_index_
|
||||
// and prefix_index_ are null, so this option does not matter.
|
||||
InternalIterator* NewIterator(const Comparator* comparator,
|
||||
BlockIter* iter = nullptr,
|
||||
bool total_order_seek = true,
|
||||
Statistics* stats = nullptr);
|
||||
BlockIter* NewIterator(const Comparator* comparator,
|
||||
BlockIter* iter = nullptr,
|
||||
bool total_order_seek = true,
|
||||
Statistics* stats = nullptr);
|
||||
void SetBlockPrefixIndex(BlockPrefixIndex* prefix_index);
|
||||
|
||||
// Report an approximation of how much memory has been used.
|
||||
@@ -191,12 +191,15 @@ class Block {
|
||||
const SequenceNumber global_seqno_;
|
||||
|
||||
// No copying allowed
|
||||
Block(const Block&);
|
||||
void operator=(const Block&);
|
||||
Block(const Block&) = delete;
|
||||
void operator=(const Block&) = delete;
|
||||
};
|
||||
|
||||
class BlockIter : public InternalIterator {
|
||||
public:
|
||||
// Object created using this constructor will behave like an iterator
|
||||
// against an empty block. The state after the creation: Valid()=false
|
||||
// and status() is OK.
|
||||
BlockIter()
|
||||
: comparator_(nullptr),
|
||||
data_(nullptr),
|
||||
|
||||
@@ -783,9 +783,12 @@ Status BlockBasedTableBuilder::Finish() {
|
||||
WriteRawBlock(meta_index_builder.Finish(), kNoCompression,
|
||||
&metaindex_block_handle);
|
||||
|
||||
const bool is_data_block = true;
|
||||
WriteBlock(index_blocks.index_block_contents, &index_block_handle,
|
||||
!is_data_block);
|
||||
if (r->table_options.enable_index_compression) {
|
||||
WriteBlock(index_blocks.index_block_contents, &index_block_handle, false);
|
||||
} else {
|
||||
WriteRawBlock(index_blocks.index_block_contents, kNoCompression,
|
||||
&index_block_handle);
|
||||
}
|
||||
// If there are more index partitions, finish them and write them out
|
||||
Status& s = index_builder_status;
|
||||
while (s.IsIncomplete()) {
|
||||
@@ -793,8 +796,13 @@ Status BlockBasedTableBuilder::Finish() {
|
||||
if (!s.ok() && !s.IsIncomplete()) {
|
||||
return s;
|
||||
}
|
||||
WriteBlock(index_blocks.index_block_contents, &index_block_handle,
|
||||
!is_data_block);
|
||||
if (r->table_options.enable_index_compression) {
|
||||
WriteBlock(index_blocks.index_block_contents, &index_block_handle,
|
||||
false);
|
||||
} else {
|
||||
WriteRawBlock(index_blocks.index_block_contents, kNoCompression,
|
||||
&index_block_handle);
|
||||
}
|
||||
// The last index_block_handle will be for the partition index block
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,6 +223,9 @@ std::string BlockBasedTableFactory::GetPrintableTableOptions() const {
|
||||
snprintf(buffer, kBufferSize, " format_version: %d\n",
|
||||
table_options_.format_version);
|
||||
ret.append(buffer);
|
||||
snprintf(buffer, kBufferSize, " enable_index_compression: %d\n",
|
||||
table_options_.enable_index_compression);
|
||||
ret.append(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user