mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01393464ea | ||
|
|
97a152b841 | ||
|
|
537ccd2fbe | ||
|
|
86e421fba9 | ||
|
|
c5201abc4a | ||
|
|
4eda1b79fa | ||
|
|
030f7c4a7b | ||
|
|
6c60c8f3f3 | ||
|
|
2660754dbc | ||
|
|
94a867c845 | ||
|
|
1b5474c8e1 | ||
|
|
b135fa7a8b |
No files matched your search
@@ -90,9 +90,6 @@ jobs:
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -107,47 +104,38 @@ jobs:
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 .. && make V=1 -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly:
|
||||
build-linux-gcc-7-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/build-folly"
|
||||
- run: USE_FOLLY=1 LIB_MODE=static V=1 make -j32 check
|
||||
- run: USE_FOLLY=1 LIB_MODE=static CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly-lite-no-test:
|
||||
build-linux-gcc-7-with-folly-lite-no-test:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: zjay437/rocksdb:0.6
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: USE_FOLLY_LITE=1 V=1 make -j32 all
|
||||
- run: USE_FOLLY_LITE=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 all
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-coroutines:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
|
||||
@@ -47,9 +47,6 @@ package/
|
||||
unity.a
|
||||
tags
|
||||
etags
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_test2
|
||||
|
||||
+5
-16
@@ -267,8 +267,8 @@ endif(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
CHECK_C_COMPILER_FLAG("-march=loongarch64" HAS_LOONGARCH64)
|
||||
if(HAS_LOONGARCH64)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=loongarch64 -mtune=loongarch64")
|
||||
endif(HAS_LOONGARCH64)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
|
||||
@@ -488,8 +488,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Cygwin")
|
||||
add_definitions(-fno-builtin-memcmp -DCYGWIN)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
add_definitions(-DOS_MACOSX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "iOS")
|
||||
add_definitions(-DOS_MACOSX -DIOS_CROSS_COMPILE)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_definitions(-DOS_LINUX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
|
||||
@@ -617,7 +615,7 @@ if(USE_FOLLY)
|
||||
FMT_INST_PATH)
|
||||
exec_program(ls ARGS -d ${FOLLY_INST_PATH}/../gflags* OUTPUT_VARIABLE
|
||||
GFLAGS_INST_PATH)
|
||||
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.83.0)
|
||||
set(Boost_DIR ${BOOST_INST_PATH}/lib/cmake/Boost-1.78.0)
|
||||
if(EXISTS ${FMT_INST_PATH}/lib64)
|
||||
set(fmt_DIR ${FMT_INST_PATH}/lib64/cmake/fmt)
|
||||
else()
|
||||
@@ -693,7 +691,6 @@ set(SOURCES
|
||||
db/db_impl/db_impl_write.cc
|
||||
db/db_impl/db_impl_compaction_flush.cc
|
||||
db/db_impl/db_impl_files.cc
|
||||
db/db_impl/db_impl_follower.cc
|
||||
db/db_impl/db_impl_open.cc
|
||||
db/db_impl/db_impl_debug.cc
|
||||
db/db_impl/db_impl_experimental.cc
|
||||
@@ -751,7 +748,6 @@ set(SOURCES
|
||||
env/env_encryption.cc
|
||||
env/file_system.cc
|
||||
env/file_system_tracer.cc
|
||||
env/fs_on_demand.cc
|
||||
env/fs_remap.cc
|
||||
env/mock_env.cc
|
||||
env/unique_id_gen.cc
|
||||
@@ -939,7 +935,6 @@ set(SOURCES
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/simulator_cache/cache_simulator.cc
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/table_properties_collectors/compact_for_tiering_collector.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
utilities/trace/replayer_impl.cc
|
||||
@@ -1042,8 +1037,10 @@ endif()
|
||||
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
db/db_impl/db_impl_follower.cc
|
||||
port/port_posix.cc
|
||||
env/env_posix.cc
|
||||
env/fs_on_demand.cc
|
||||
env/fs_posix.cc
|
||||
env/io_posix.cc)
|
||||
endif()
|
||||
@@ -1052,7 +1049,6 @@ if(USE_FOLLY_LITE)
|
||||
list(APPEND SOURCES
|
||||
third-party/folly/folly/container/detail/F14Table.cpp
|
||||
third-party/folly/folly/detail/Futex.cpp
|
||||
third-party/folly/folly/lang/Exception.cpp
|
||||
third-party/folly/folly/lang/SafeAssert.cpp
|
||||
third-party/folly/folly/lang/ToAscii.cpp
|
||||
third-party/folly/folly/ScopeGuard.cpp
|
||||
@@ -1060,12 +1056,6 @@ if(USE_FOLLY_LITE)
|
||||
third-party/folly/folly/synchronization/DistributedMutex.cpp
|
||||
third-party/folly/folly/synchronization/ParkingLot.cpp)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
exec_program(python3 ${PROJECT_SOURCE_DIR}/third-party/folly ARGS
|
||||
build/fbcode_builder/getdeps.py show-source-dir boost OUTPUT_VARIABLE
|
||||
BOOST_SOURCE_PATH)
|
||||
exec_program(ls ARGS -d ${BOOST_SOURCE_PATH}/boost* OUTPUT_VARIABLE
|
||||
BOOST_INCLUDE_DIR)
|
||||
include_directories(${BOOST_INCLUDE_DIR})
|
||||
add_definitions(-DUSE_FOLLY -DFOLLY_NO_CONFIG)
|
||||
list(APPEND THIRDPARTY_LIBS glog)
|
||||
endif()
|
||||
@@ -1489,7 +1479,6 @@ if(WITH_TESTS)
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/simulator_cache/cache_simulator_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_for_tiering_collector_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
|
||||
+4
-50
@@ -1,60 +1,14 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 9.5.2 (08/13/2024)
|
||||
### Bug Fixes
|
||||
* Fix a race condition in pessimistic transactions that could allow multiple transactions with the same name to be registered simultaneously, resulting in a crash or other unpredictable behavior.
|
||||
|
||||
### Public API Changes
|
||||
* Add ticker stats to count file read retries due to checksum mismatch
|
||||
|
||||
## 9.5.1 (08/02/2024)
|
||||
## 9.3.2 (08/02/2024)
|
||||
### Bug Fixes
|
||||
* *Make DestroyDB supports slow deletion when it's configured in `SstFileManager`. The slow deletion is subject to the configured `rate_bytes_per_sec`, but not subject to the `max_trash_db_ratio`.
|
||||
* Fix a race condition in pessimistic transactions that could allow multiple transactions with the same name to be registered simultaneously, resulting in a crash or other unpredictable behavior.
|
||||
|
||||
## 9.5.0 (07/19/2024)
|
||||
### Public API Changes
|
||||
* Introduced new C API function rocksdb_writebatch_iterate_cf for column family-aware iteration over the contents of a WriteBatch
|
||||
* Add support to ingest SST files generated by a DB instead of SstFileWriter. This can be enabled with experimental option `IngestExternalFileOptions::allow_db_generated_files`.
|
||||
|
||||
### Behavior Changes
|
||||
* When calculating total log size for the `log_size_for_flush` argument in `CreateCheckpoint` API, the size of the archived log will not be included to avoid unnecessary flush
|
||||
|
||||
## 9.3.1 (05/25/2024)
|
||||
### Bug Fixes
|
||||
* Fix a major bug in which an iterator using prefix filtering and SeekForPrev might miss data when the DB is using `whole_key_filtering=false` and `partition_filters=true`.
|
||||
* Fixed a bug where `OnErrorRecoveryBegin()` is not called before auto recovery starts.
|
||||
* Fixed a bug where event listener reads ErrorHandler's `bg_error_` member without holding db mutex(#12803).
|
||||
* Fixed a bug in handling MANIFEST write error that caused the latest valid MANIFEST file to get deleted, resulting in the DB being unopenable.
|
||||
* Fixed a race between error recovery due to manifest sync or write failure and external SST file ingestion. Both attempt to write a new manifest file, which causes an assertion failure.
|
||||
|
||||
### Performance Improvements
|
||||
* Fix an issue where compactions were opening table files and reading table properties while holding db mutex_.
|
||||
* Reduce unnecessary filesystem queries and DB mutex acquires in creating backups and checkpoints.
|
||||
|
||||
## 9.4.0 (06/23/2024)
|
||||
### New Features
|
||||
* Added a `CompactForTieringCollectorFactory` to auto trigger compaction for tiering use case.
|
||||
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `GetEntityForUpdate` API.
|
||||
* Added a new "count" command to the ldb repl shell. By default, it prints a count of keys in the database from start to end. The options --from=<key> and/or --to=<key> can be specified to limit the range.
|
||||
* Add `rocksdb_writebatch_update_timestamps`, `rocksdb_writebatch_wi_update_timestamps` in C API.
|
||||
* Add `rocksdb_iter_refresh` in C API.
|
||||
* Add `rocksdb_writebatch_create_with_params`, `rocksdb_writebatch_wi_create_with_params` to create WB and WBWI with all options in C API
|
||||
|
||||
### Public API Changes
|
||||
* Deprecated names `LogFile` and `VectorLogPtr` in favor of new names `WalFile` and `VectorWalPtr`.
|
||||
* Introduce a new universal compaction option CompactionOptionsUniversal::max_read_amp which allows user to define the limit on the number of sorted runs separately from the trigger for compaction (`level0_file_num_compaction_trigger`) #12477.
|
||||
|
||||
### Behavior Changes
|
||||
* Inactive WALs are immediately closed upon being fully sync-ed rather than in a background thread. This is to ensure LinkFile() is not called on files still open for write, which might not be supported by some FileSystem implementations. This should not be a performance issue, but an opt-out is available with with new DB option `background_close_inactive_wals`.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a rare case in which a hard-linked WAL in a Checkpoint is not fully synced (so might lose data on power loss).
|
||||
* Fixed the output of the `ldb dump_wal` command for `PutEntity` records so it prints the key and correctly resets the hexadecimal formatting flag after printing the wide-column entity.
|
||||
* Fixed an issue where `PutEntity` records were handled incorrectly while rebuilding transactions during recovery.
|
||||
* Various read operations could ignore various ReadOptions that might be relevant. Fixed many such cases, which can result in behavior change but a better reflection of specified options.
|
||||
|
||||
### Performance Improvements
|
||||
* Improved write throughput to memtable when there's a large number of concurrent writers and allow_concurrent_memtable_write=true(#12545)
|
||||
* [internal only] Build script improvement
|
||||
|
||||
## 9.3.0 (05/17/2024)
|
||||
### New Features
|
||||
|
||||
@@ -103,7 +103,6 @@ dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
export LIB_MODE="$(LIB_MODE)"; \
|
||||
export ROCKSDB_CXX_STANDARD="$(ROCKSDB_CXX_STANDARD)"; \
|
||||
export USE_FOLLY="$(USE_FOLLY)"; \
|
||||
export USE_FOLLY_LITE="$(USE_FOLLY_LITE)"; \
|
||||
"$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
@@ -501,17 +500,6 @@ endif
|
||||
ifeq ($(USE_FOLLY_LITE),1)
|
||||
# Path to the Folly source code and include files
|
||||
FOLLY_DIR = ./third-party/folly
|
||||
ifneq ($(strip $(BOOST_SOURCE_PATH)),)
|
||||
BOOST_INCLUDE = $(shell (ls -d $(BOOST_SOURCE_PATH)/boost*/))
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(BOOST_INCLUDE)
|
||||
PLATFORM_CXXFLAGS += -I$(BOOST_INCLUDE)
|
||||
else
|
||||
PLATFORM_CCFLAGS += -isystem $(BOOST_INCLUDE)
|
||||
PLATFORM_CXXFLAGS += -isystem $(BOOST_INCLUDE)
|
||||
endif
|
||||
endif # BOOST_SOURCE_PATH
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(FOLLY_DIR)
|
||||
@@ -648,37 +636,26 @@ TESTS = $(patsubst %.cc, %, $(notdir $(TEST_MAIN_SOURCES)))
|
||||
TESTS += $(patsubst %.c, %, $(notdir $(TEST_MAIN_SOURCES_C)))
|
||||
TESTS += $(PLUGIN_TESTS)
|
||||
|
||||
# `make check-headers` to verify that each header file includes its own deps
|
||||
# and that public headers do not depend on internal headers
|
||||
# `make check-headers` to very that each header file includes its own
|
||||
# dependencies
|
||||
ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
|
||||
# TODO: add/support JNI headers
|
||||
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
|
||||
# Some headers like in port/ are platform-specific
|
||||
DEV_HEADERS_TO_CHECK := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/')
|
||||
PUBLIC_HEADERS_TO_CHECK := $(shell $(FIND) include/ -type f -name '*.h' | grep -E -v 'lua/')
|
||||
DEV_HEADERS := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | grep -E -v 'port/|plugin/|lua/|range_tree/')
|
||||
else
|
||||
DEV_HEADERS_TO_CHECK :=
|
||||
PUBLIC_HEADERS_TO_CHECK :=
|
||||
DEV_HEADERS :=
|
||||
endif
|
||||
HEADER_OK_FILES = $(patsubst %.h, %.h.ok, $(DEV_HEADERS_TO_CHECK)) \
|
||||
$(patsubst %.h, %.h.pub, $(PUBLIC_HEADERS_TO_CHECK))
|
||||
HEADER_OK_FILES = $(patsubst %.h, %.h.ok, $(DEV_HEADERS))
|
||||
|
||||
AM_V_CCH = $(am__v_CCH_$(V))
|
||||
am__v_CCH_ = $(am__v_CCH_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_CCH_0 = @echo " CC.h " $<;
|
||||
am__v_CCH_1 =
|
||||
|
||||
# verify headers include their own dependencies, under dev build settings
|
||||
%.h.ok: %.h # .h.ok not actually created, so re-checked on each invocation
|
||||
# -DROCKSDB_NAMESPACE=42 ensures the namespace header is included
|
||||
$(AM_V_CCH) echo '#include "$<"' | $(CXX) $(CXXFLAGS) \
|
||||
-DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
|
||||
# verify public headers do not depend on internal headers, under typical
|
||||
# user build settings
|
||||
%.h.pub: %.h # .h.pub not actually created, so re-checked on each invocation
|
||||
$(AM_V_CCH) cd include/ && echo '#include "$(patsubst include/%,%,$<)"' | \
|
||||
$(CXX) -I. -DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
$(AM_V_CCH) echo '#include "$<"' | $(CXX) $(CXXFLAGS) -DROCKSDB_NAMESPACE=42 -x c++ -c - -o /dev/null
|
||||
|
||||
check-headers: $(HEADER_OK_FILES)
|
||||
|
||||
@@ -1654,9 +1631,6 @@ compaction_job_stats_test: $(OBJ_DIR)/db/compaction/compaction_job_stats_test.o
|
||||
compaction_service_test: $(OBJ_DIR)/db/compaction/compaction_service_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_for_tiering_collector_test: $(OBJ_DIR)/utilities/table_properties_collectors/compact_for_tiering_collector_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_on_deletion_collector_test: $(OBJ_DIR)/utilities/table_properties_collectors/compact_on_deletion_collector_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2139,8 +2113,8 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.8
|
||||
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
|
||||
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
|
||||
SNAPPY_VER ?= 1.2.1
|
||||
SNAPPY_SHA256 ?= 736aeb64d86566d2236ddffa2865ee5d7a82d26c9016b36218fcc27ea4f09f86
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.4
|
||||
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
|
||||
@@ -2237,7 +2211,7 @@ libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
mkdir snappy-$(SNAPPY_VER)/build
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DSNAPPY_BUILD_BENCHMARKS=OFF -DSNAPPY_BUILD_TESTS=OFF --compile-no-warning-as-error ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
|
||||
|
||||
lz4-$(LZ4_VER).tar.gz:
|
||||
@@ -2484,13 +2458,14 @@ checkout_folly:
|
||||
fi
|
||||
@# Pin to a particular version for public CI, so that PR authors don't
|
||||
@# need to worry about folly breaking our integration. Update periodically
|
||||
cd third-party/folly && git reset --hard c48fdd205c1c291651749d532b8055fe822bba25
|
||||
cd third-party/folly && git reset --hard beacd86d63cd71c904632262e6c36f60874d78ba
|
||||
@# A hack to remove boost dependency.
|
||||
@# NOTE: this hack is only needed if building using USE_FOLLY_LITE
|
||||
perl -pi -e 's/^(#include <boost)/\/\/$$1/' third-party/folly/folly/functional/Invoke.h
|
||||
@# NOTE: this hack is required for clang in some cases
|
||||
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
|
||||
@# NOTE: this hack is required for gcc in some cases
|
||||
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
|
||||
@# NOTE: boost source will be needed for any build including `USE_FOLLY_LITE` builds as those depend on boost headers
|
||||
cd third-party/folly && $(PYTHON) build/fbcode_builder/getdeps.py fetch boost
|
||||
|
||||
CXX_M_FLAGS = $(filter -m%, $(CXXFLAGS))
|
||||
|
||||
@@ -2502,6 +2477,8 @@ build_folly:
|
||||
echo "Please run checkout_folly first"; \
|
||||
false; \
|
||||
fi
|
||||
# Restore the original version of Invoke.h with boost dependency
|
||||
cd third-party/folly && ${GIT_COMMAND} checkout folly/functional/Invoke.h
|
||||
cd third-party/folly && \
|
||||
CXXFLAGS=" $(CXX_M_FLAGS) -DHAVE_CXX11_ATOMIC " $(PYTHON) build/fbcode_builder/getdeps.py build --no-tests
|
||||
|
||||
|
||||
@@ -317,7 +317,6 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/simulator_cache/cache_simulator.cc",
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_for_tiering_collector.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/trace/replayer_impl.cc",
|
||||
@@ -395,7 +394,6 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"db_stress_tool/cf_consistency_stress.cc",
|
||||
"db_stress_tool/db_stress_common.cc",
|
||||
"db_stress_tool/db_stress_driver.cc",
|
||||
"db_stress_tool/db_stress_filters.cc",
|
||||
"db_stress_tool/db_stress_gflags.cc",
|
||||
"db_stress_tool/db_stress_listener.cc",
|
||||
"db_stress_tool/db_stress_shared_state.cc",
|
||||
@@ -413,8 +411,6 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
], headers=None)
|
||||
|
||||
|
||||
cpp_binary_wrapper(name="ldb", srcs=["tools/ldb.cc"], deps=[":rocksdb_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="db_stress", srcs=["db_stress_tool/db_stress.cc"], deps=[":rocksdb_stress_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
|
||||
cpp_binary_wrapper(name="cache_bench", srcs=["cache/cache_bench.cc"], deps=[":rocksdb_cache_bench_tools_lib"], extra_preprocessor_flags=[], extra_bench_libs=False)
|
||||
@@ -4628,12 +4624,6 @@ cpp_unittest_wrapper(name="compact_files_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="compact_for_tiering_collector_test",
|
||||
srcs=["utilities/table_properties_collectors/compact_for_tiering_collector_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="compact_on_deletion_collector_test",
|
||||
srcs=["utilities/table_properties_collectors/compact_on_deletion_collector_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
|
||||
@@ -193,10 +193,6 @@ def generate_targets(repo_path, deps_map):
|
||||
+ src_mk.get("STRESS_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
)
|
||||
# ldb binary
|
||||
TARGETS.add_binary(
|
||||
"ldb", ["tools/ldb.cc"], [":rocksdb_tools_lib"]
|
||||
)
|
||||
# db_stress binary
|
||||
TARGETS.add_binary(
|
||||
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
|
||||
|
||||
@@ -585,7 +585,7 @@ EOF
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbenchmark"
|
||||
fi
|
||||
fi
|
||||
if test $USE_FOLLY || test $USE_FOLLY_LITE; then
|
||||
if test $USE_FOLLY; then
|
||||
# Test whether libfolly library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <folly/synchronization/DistributedMutex.h>
|
||||
@@ -751,11 +751,6 @@ if [ "$USE_FOLLY" ]; then
|
||||
FOLLY_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-inst-dir folly`
|
||||
fi
|
||||
fi
|
||||
if [ "$USE_FOLLY_LITE" ]; then
|
||||
if [ "$FOLLY_DIR" ]; then
|
||||
BOOST_SOURCE_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-source-dir boost`
|
||||
fi
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
@@ -797,7 +792,6 @@ echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
|
||||
echo "FIND=$FIND" >> "$OUTPUT"
|
||||
echo "WATCH=$WATCH" >> "$OUTPUT"
|
||||
echo "FOLLY_PATH=$FOLLY_PATH" >> "$OUTPUT"
|
||||
echo "BOOST_SOURCE_PATH=$BOOST_SOURCE_PATH" >> "$OUTPUT"
|
||||
|
||||
# This will enable some related identifiers for the preprocessor
|
||||
if test -n "$JEMALLOC"; then
|
||||
|
||||
Vendored
-58
@@ -18,7 +18,6 @@
|
||||
#include "cache/lru_cache.h"
|
||||
#include "cache/typed_cache.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "table/block_based/block_cache.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
@@ -1018,63 +1017,6 @@ INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest,
|
||||
testing::Values(secondary_cache_test_util::kLRU));
|
||||
|
||||
TEST(MiscBlockCacheTest, UncacheAggressivenessAdvisor) {
|
||||
// Aggressiveness to a sequence of Report() calls (as string of 0s and 1s)
|
||||
// exactly until the first ShouldContinue() == false.
|
||||
const std::vector<std::pair<uint32_t, Slice>> expectedTraces{
|
||||
// Aggressiveness 1 aborts on first unsuccessful erasure.
|
||||
{1, "0"},
|
||||
{1, "11111111111111111111110"},
|
||||
// For sufficient evidence, aggressiveness 2 requires a minimum of two
|
||||
// unsuccessful erasures.
|
||||
{2, "00"},
|
||||
{2, "0110"},
|
||||
{2, "1100"},
|
||||
{2, "011111111111111111111111111111111111111111111111111111111111111100"},
|
||||
{2, "0111111111111111111111111111111111110"},
|
||||
// For sufficient evidence, aggressiveness 3 and higher require a minimum
|
||||
// of three unsuccessful erasures.
|
||||
{3, "000"},
|
||||
{3, "01010"},
|
||||
{3, "111000"},
|
||||
{3, "00111111111111111111111111111111111100"},
|
||||
{3, "00111111111111111111110"},
|
||||
|
||||
{4, "000"},
|
||||
{4, "01010"},
|
||||
{4, "111000"},
|
||||
{4, "001111111111111111111100"},
|
||||
{4, "0011111111111110"},
|
||||
|
||||
{6, "000"},
|
||||
{6, "01010"},
|
||||
{6, "111000"},
|
||||
{6, "00111111111111100"},
|
||||
{6, "0011111110"},
|
||||
|
||||
// 69 -> 50% threshold, now up to minimum of 4
|
||||
{69, "0000"},
|
||||
{69, "010000"},
|
||||
{69, "01010000"},
|
||||
{69, "101010100010101000"},
|
||||
|
||||
// 230 -> 10% threshold, appropriately higher minimum
|
||||
{230, "000000000000"},
|
||||
{230, "0000000000010000000000"},
|
||||
{230, "00000000000100000000010000000000"}};
|
||||
for (const auto& [aggressiveness, t] : expectedTraces) {
|
||||
SCOPED_TRACE("aggressiveness=" + std::to_string(aggressiveness) + " with " +
|
||||
t.ToString());
|
||||
UncacheAggressivenessAdvisor uaa(aggressiveness);
|
||||
for (size_t i = 0; i < t.size(); ++i) {
|
||||
SCOPED_TRACE("i=" + std::to_string(i));
|
||||
ASSERT_TRUE(uaa.ShouldContinue());
|
||||
uaa.Report(t[i] & 1);
|
||||
}
|
||||
ASSERT_FALSE(uaa.ShouldContinue());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -138,10 +138,10 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
reinit_internal_iter();
|
||||
break;
|
||||
} else {
|
||||
*memtable_range_tombstone_iter_ =
|
||||
std::make_unique<TruncatedRangeDelIterator>(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
|
||||
&cfd->internal_comparator(), nullptr, nullptr);
|
||||
delete *memtable_range_tombstone_iter_;
|
||||
*memtable_range_tombstone_iter_ = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
|
||||
&cfd->internal_comparator(), nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
|
||||
@@ -55,8 +55,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
db_iter_->SetIter(iter);
|
||||
}
|
||||
|
||||
void SetMemtableRangetombstoneIter(
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* iter) {
|
||||
void SetMemtableRangetombstoneIter(TruncatedRangeDelIterator** iter) {
|
||||
memtable_range_tombstone_iter_ = iter;
|
||||
}
|
||||
|
||||
@@ -111,8 +110,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
bool allow_refresh_ = true;
|
||||
// If this is nullptr, it means the mutable memtable does not contain range
|
||||
// tombstone when added under this DBIter.
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* memtable_range_tombstone_iter_ =
|
||||
nullptr;
|
||||
TruncatedRangeDelIterator** memtable_range_tombstone_iter_ = nullptr;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ Status BuildTable(
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
std::vector<SequenceNumber> snapshots, SequenceNumber earliest_snapshot,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
@@ -195,7 +195,7 @@ Status BuildTable(
|
||||
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
CompactionIterator c_iter(
|
||||
iter, ucmp, &merge, kMaxSequenceNumber, &snapshots, earliest_snapshot,
|
||||
iter, ucmp, &merge, kMaxSequenceNumber, &snapshots,
|
||||
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
|
||||
ShouldReportDetailedTime(env, ioptions.stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ Status BuildTable(
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
std::vector<SequenceNumber> snapshots, SequenceNumber earliest_snapshot,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, SnapshotChecker* snapshot_checker,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
|
||||
@@ -231,7 +231,6 @@ struct rocksdb_livefiles_t {
|
||||
};
|
||||
struct rocksdb_column_family_handle_t {
|
||||
ColumnFamilyHandle* rep;
|
||||
bool immortal; /* only true for default cf */
|
||||
};
|
||||
struct rocksdb_column_family_metadata_t {
|
||||
ColumnFamilyMetaData rep;
|
||||
@@ -532,42 +531,6 @@ struct rocksdb_universal_compaction_options_t {
|
||||
ROCKSDB_NAMESPACE::CompactionOptionsUniversal* rep;
|
||||
};
|
||||
|
||||
struct rocksdb_callback_logger_t : public Logger {
|
||||
static const ssize_t STACK_BUFSZ = 512;
|
||||
rocksdb_callback_logger_t(InfoLogLevel log_level,
|
||||
void (*logv_cb)(void*, unsigned, char*, size_t),
|
||||
void* priv)
|
||||
: Logger(log_level), logv_cb_(logv_cb), priv_(priv) {}
|
||||
|
||||
using Logger::Logv;
|
||||
void Logv(const InfoLogLevel level, const char* fmt, va_list ap0) override {
|
||||
char stack_buf[STACK_BUFSZ];
|
||||
char* alloc_buf = nullptr;
|
||||
char* buf = stack_buf;
|
||||
int len = 0;
|
||||
va_list ap1;
|
||||
if (!logv_cb_) return;
|
||||
va_copy(ap1, ap0);
|
||||
len = vsnprintf(buf, STACK_BUFSZ, fmt, ap0);
|
||||
if (len <= 0)
|
||||
goto cleanup;
|
||||
else if (len >= STACK_BUFSZ) {
|
||||
buf = alloc_buf = reinterpret_cast<char*>(malloc(len + 1));
|
||||
if (!buf) goto cleanup;
|
||||
len = vsnprintf(buf, len + 1, fmt, ap1);
|
||||
if (len <= 0) goto cleanup;
|
||||
}
|
||||
logv_cb_(priv_, unsigned(level), buf, size_t(len));
|
||||
cleanup:
|
||||
va_end(ap1);
|
||||
free(alloc_buf);
|
||||
}
|
||||
|
||||
private:
|
||||
void (*logv_cb_)(void*, unsigned, char*, size_t) = nullptr;
|
||||
void* priv_ = nullptr;
|
||||
};
|
||||
|
||||
static bool SaveError(char** errptr, const Status& s) {
|
||||
assert(errptr != nullptr);
|
||||
if (s.ok()) {
|
||||
@@ -583,7 +546,6 @@ static bool SaveError(char** errptr, const Status& s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copies str to a new malloc()-ed buffer. The buffer is not NUL terminated.
|
||||
static char* CopyString(const std::string& str) {
|
||||
char* result = reinterpret_cast<char*>(malloc(sizeof(char) * str.size()));
|
||||
memcpy(result, str.data(), sizeof(char) * str.size());
|
||||
@@ -944,7 +906,6 @@ rocksdb_t* rocksdb_open_and_trim_history(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -975,7 +936,6 @@ rocksdb_t* rocksdb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1011,7 +971,6 @@ rocksdb_t* rocksdb_open_column_families_with_ttl(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1045,7 +1004,6 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1077,7 +1035,6 @@ rocksdb_t* rocksdb_open_as_secondary_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
@@ -1115,7 +1072,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family(
|
||||
SaveError(errptr, db->rep->CreateColumnFamily(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep)));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -1139,7 +1095,6 @@ rocksdb_column_family_handle_t** rocksdb_create_column_families(
|
||||
for (size_t i = 0; i != handles.size(); ++i) {
|
||||
c_handles[i] = new rocksdb_column_family_handle_t;
|
||||
c_handles[i]->rep = handles[i];
|
||||
c_handles[i]->immortal = false;
|
||||
}
|
||||
|
||||
return c_handles;
|
||||
@@ -1159,7 +1114,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family_with_ttl(
|
||||
SaveError(errptr, db_with_ttl->CreateColumnFamilyWithTtl(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep), ttl));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -1181,19 +1135,9 @@ char* rocksdb_column_family_handle_get_name(
|
||||
return CopyString(name);
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_get_default_column_family_handle(
|
||||
rocksdb_t* db) {
|
||||
rocksdb_column_family_handle_t* handle = new rocksdb_column_family_handle_t;
|
||||
handle->rep = db->rep->DefaultColumnFamily();
|
||||
handle->immortal = true;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void rocksdb_column_family_handle_destroy(
|
||||
rocksdb_column_family_handle_t* handle) {
|
||||
if (!handle->immortal) {
|
||||
delete handle->rep;
|
||||
}
|
||||
delete handle->rep;
|
||||
delete handle;
|
||||
}
|
||||
|
||||
@@ -1429,18 +1373,6 @@ char* rocksdb_get_cf_with_ts(rocksdb_t* db,
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_get_db_identity(rocksdb_t* db, size_t* id_len) {
|
||||
std::string identity_tmp;
|
||||
Status s = db->rep->GetDbIdentity(identity_tmp);
|
||||
if (!s.ok()) {
|
||||
*id_len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*id_len = identity_tmp.size();
|
||||
return CopyString(identity_tmp);
|
||||
}
|
||||
|
||||
void rocksdb_multi_get(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
size_t num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes, char** values_list,
|
||||
@@ -1838,26 +1770,6 @@ void rocksdb_approximate_sizes_cf(
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf_with_flags(
|
||||
rocksdb_t* db, rocksdb_column_family_handle_t* column_family,
|
||||
int num_ranges, const char* const* range_start_key,
|
||||
const size_t* range_start_key_len, const char* const* range_limit_key,
|
||||
const size_t* range_limit_key_len, uint8_t include_flags, uint64_t* sizes,
|
||||
char** errptr) {
|
||||
Range* ranges = new Range[num_ranges];
|
||||
for (int i = 0; i < num_ranges; i++) {
|
||||
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
|
||||
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
|
||||
}
|
||||
Status s = db->rep->GetApproximateSizes(
|
||||
column_family->rep, ranges, num_ranges, sizes,
|
||||
static_cast<DB::SizeApproximationFlags>(include_flags));
|
||||
if (!s.ok()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
void rocksdb_delete_file(rocksdb_t* db, const char* name) {
|
||||
db->rep->DeleteFile(name);
|
||||
}
|
||||
@@ -2035,10 +1947,6 @@ void rocksdb_iter_get_error(const rocksdb_iterator_t* iter, char** errptr) {
|
||||
SaveError(errptr, iter->rep->status());
|
||||
}
|
||||
|
||||
void rocksdb_iter_refresh(const rocksdb_iterator_t* iter, char** errptr) {
|
||||
SaveError(errptr, iter->rep->Refresh());
|
||||
}
|
||||
|
||||
rocksdb_writebatch_t* rocksdb_writebatch_create() {
|
||||
return new rocksdb_writebatch_t;
|
||||
}
|
||||
@@ -2050,15 +1958,6 @@ rocksdb_writebatch_t* rocksdb_writebatch_create_from(const char* rep,
|
||||
return b;
|
||||
}
|
||||
|
||||
rocksdb_writebatch_t* rocksdb_writebatch_create_with_params(
|
||||
size_t reserved_bytes, size_t max_bytes, size_t protection_bytes_per_key,
|
||||
size_t default_cf_ts_sz) {
|
||||
rocksdb_writebatch_t* b = new rocksdb_writebatch_t;
|
||||
b->rep = WriteBatch(reserved_bytes, max_bytes, protection_bytes_per_key,
|
||||
default_cf_ts_sz);
|
||||
return b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_destroy(rocksdb_writebatch_t* b) { delete b; }
|
||||
|
||||
void rocksdb_writebatch_clear(rocksdb_writebatch_t* b) { b->rep.Clear(); }
|
||||
@@ -2286,32 +2185,6 @@ class H : public WriteBatch::Handler {
|
||||
}
|
||||
};
|
||||
|
||||
class HCF : public WriteBatch::Handler {
|
||||
public:
|
||||
void* state_;
|
||||
void (*put_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen);
|
||||
void (*deleted_cf_)(void*, uint32_t cfid, const char* k, size_t klen);
|
||||
void (*merge_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen);
|
||||
Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
(*put_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
|
||||
value.size());
|
||||
return Status::OK();
|
||||
}
|
||||
Status DeleteCF(uint32_t column_family_id, const Slice& key) override {
|
||||
(*deleted_cf_)(state_, column_family_id, key.data(), key.size());
|
||||
return Status::OK();
|
||||
}
|
||||
Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
(*merge_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
|
||||
value.size());
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
|
||||
void (*put)(void*, const char* k, size_t klen,
|
||||
const char* v, size_t vlen),
|
||||
@@ -2324,21 +2197,6 @@ void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
|
||||
b->rep.Iterate(&handler);
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_iterate_cf(
|
||||
rocksdb_writebatch_t* b, void* state,
|
||||
void (*put_cf)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen),
|
||||
void (*deleted_cf)(void*, uint32_t cfid, const char* k, size_t klen),
|
||||
void (*merge_cf)(void*, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen)) {
|
||||
HCF handler;
|
||||
handler.state_ = state;
|
||||
handler.put_cf_ = put_cf;
|
||||
handler.deleted_cf_ = deleted_cf;
|
||||
handler.merge_cf_ = merge_cf;
|
||||
b->rep.Iterate(&handler);
|
||||
}
|
||||
|
||||
const char* rocksdb_writebatch_data(rocksdb_writebatch_t* b, size_t* size) {
|
||||
*size = b->rep.GetDataSize();
|
||||
return b->rep.Data().c_str();
|
||||
@@ -2365,35 +2223,6 @@ rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create(
|
||||
return b;
|
||||
}
|
||||
|
||||
rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create_with_params(
|
||||
rocksdb_comparator_t* backup_index_comparator, size_t reserved_bytes,
|
||||
unsigned char overwrite_key, size_t max_bytes,
|
||||
size_t protection_bytes_per_key) {
|
||||
rocksdb_writebatch_wi_t* b = new rocksdb_writebatch_wi_t;
|
||||
b->rep = new WriteBatchWithIndex(backup_index_comparator, reserved_bytes,
|
||||
overwrite_key, max_bytes,
|
||||
protection_bytes_per_key);
|
||||
return b;
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_update_timestamps(
|
||||
rocksdb_writebatch_t* wb, const char* ts, size_t tslen, void* state,
|
||||
size_t (*get_ts_size)(void*, uint32_t), char** errptr) {
|
||||
SaveError(errptr, wb->rep.UpdateTimestamps(
|
||||
Slice(ts, tslen), [&get_ts_size, &state](uint32_t cf) {
|
||||
return (*get_ts_size)(state, cf);
|
||||
}));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_update_timestamps(
|
||||
rocksdb_writebatch_wi_t* wb, const char* ts, size_t tslen, void* state,
|
||||
size_t (*get_ts_size)(void*, uint32_t), char** errptr) {
|
||||
SaveError(errptr, wb->rep->GetWriteBatch()->UpdateTimestamps(
|
||||
Slice(ts, tslen), [&get_ts_size, &state](uint32_t cf) {
|
||||
return (*get_ts_size)(state, cf);
|
||||
}));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_destroy(rocksdb_writebatch_wi_t* b) {
|
||||
if (b->rep) {
|
||||
delete b->rep;
|
||||
@@ -3134,15 +2963,6 @@ rocksdb_logger_t* rocksdb_logger_create_stderr_logger(int log_level,
|
||||
return logger;
|
||||
}
|
||||
|
||||
rocksdb_logger_t* rocksdb_logger_create_callback_logger(
|
||||
int log_level, void (*callback)(void*, unsigned, char*, size_t),
|
||||
void* priv) {
|
||||
rocksdb_logger_t* logger = new rocksdb_logger_t;
|
||||
logger->rep = std::make_shared<rocksdb_callback_logger_t>(
|
||||
static_cast<InfoLogLevel>(log_level), callback, priv);
|
||||
return logger;
|
||||
}
|
||||
|
||||
void rocksdb_logger_destroy(rocksdb_logger_t* logger) { delete logger; }
|
||||
|
||||
void rocksdb_options_set_env(rocksdb_options_t* opt, rocksdb_env_t* env) {
|
||||
@@ -4065,27 +3885,6 @@ void rocksdb_options_set_plain_table_factory(
|
||||
opt->rep.table_factory.reset(factory);
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_write_dbid_to_manifest(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.write_dbid_to_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_dbid_to_manifest(
|
||||
rocksdb_options_t* opt, unsigned char write_dbid_to_manifest) {
|
||||
opt->rep.write_dbid_to_manifest = write_dbid_to_manifest;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_track_and_verify_wals_in_manifest(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.track_and_verify_wals_in_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_track_and_verify_wals_in_manifest(
|
||||
rocksdb_options_t* opt, unsigned char track_and_verify_wals_in_manifest) {
|
||||
opt->rep.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_successive_merges(rocksdb_options_t* opt,
|
||||
size_t v) {
|
||||
opt->rep.max_successive_merges = v;
|
||||
@@ -4465,8 +4264,13 @@ void rocksdb_perfcontext_destroy(rocksdb_perfcontext_t* context) {
|
||||
|
||||
/*
|
||||
TODO:
|
||||
DB::OpenForReadOnly
|
||||
DB::KeyMayExist
|
||||
DB::GetOptions
|
||||
DB::GetSortedWalFiles
|
||||
DB::GetLatestSequenceNumber
|
||||
DB::GetUpdatesSince
|
||||
DB::GetDbIdentity
|
||||
DB::RunManualCompaction
|
||||
custom cache
|
||||
table_properties_collectors
|
||||
@@ -5943,7 +5747,6 @@ rocksdb_column_family_handle_t* rocksdb_transactiondb_create_column_family(
|
||||
SaveError(errptr, txn_db->rep->CreateColumnFamily(
|
||||
ColumnFamilyOptions(column_family_options->rep),
|
||||
std::string(column_family_name), &(handle->rep)));
|
||||
handle->immortal = false;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -5986,7 +5789,6 @@ rocksdb_transactiondb_t* rocksdb_transactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_transactiondb_t* result = new rocksdb_transactiondb_t;
|
||||
@@ -6781,7 +6583,6 @@ rocksdb_optimistictransactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
c_handle->immortal = false;
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_optimistictransactiondb_t* result =
|
||||
|
||||
+1
-189
@@ -199,79 +199,6 @@ static void CheckDel(void* ptr, const char* k, size_t klen) {
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckPutCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 0:
|
||||
CheckEqual("bar", k, klen);
|
||||
CheckEqual("b", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 1:
|
||||
CheckEqual("box", k, klen);
|
||||
CheckEqual("c", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 4:
|
||||
CheckEqual("foo", k, klen);
|
||||
CheckEqual("f", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
case 6:
|
||||
CheckEqual("baz", k, klen);
|
||||
CheckEqual("a", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckDelCF(void* ptr, uint32_t cfid, const char* k, size_t klen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 2:
|
||||
CheckEqual("bar", k, klen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 5:
|
||||
CheckEqual("foo", k, klen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
// Callback from rocksdb_writebatch_iterate_cf()
|
||||
static void CheckMergeCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
|
||||
const char* v, size_t vlen) {
|
||||
int* state = (int*)ptr;
|
||||
switch (*state) {
|
||||
case 3:
|
||||
CheckEqual("box", k, klen);
|
||||
CheckEqual("cc", v, vlen);
|
||||
CheckCondition(cfid == 1);
|
||||
break;
|
||||
case 7:
|
||||
CheckEqual("baz", k, klen);
|
||||
CheckEqual("aa", v, vlen);
|
||||
CheckCondition(cfid == 0);
|
||||
break;
|
||||
default:
|
||||
CheckCondition(false);
|
||||
break;
|
||||
}
|
||||
(*state)++;
|
||||
}
|
||||
|
||||
static void CmpDestroy(void* arg) { (void)arg; }
|
||||
|
||||
static int CmpCompare(void* arg, const char* a, size_t alen, const char* b,
|
||||
@@ -871,11 +798,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_delete(db, woptions, "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// get the identity before the backup
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
rocksdb_close(db);
|
||||
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
@@ -896,14 +818,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
// the db_identity after the backup is different
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) != 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
|
||||
rocksdb_backup_engine_close(be);
|
||||
}
|
||||
|
||||
@@ -919,14 +833,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
checkpoint = NULL;
|
||||
|
||||
// get the identity before the checkpoint
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
// start a new database from the checkpoint
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
@@ -935,13 +841,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
// the db_identity after the checkpoint is different
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) != 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbcheckpointname, &err);
|
||||
@@ -952,73 +852,6 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("checkpoint_db_id_in_manifest");
|
||||
{
|
||||
// create new DB with set_write_dbid_to_manifest=true
|
||||
// db_identity is now the same across checkpoints
|
||||
rocksdb_close(db);
|
||||
db = NULL;
|
||||
|
||||
rocksdb_options_t* options_dbid_in_manifest = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(options_dbid_in_manifest, 1);
|
||||
|
||||
unsigned char write_to_manifest =
|
||||
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
|
||||
CheckCondition(!write_to_manifest);
|
||||
rocksdb_options_set_write_dbid_to_manifest(options_dbid_in_manifest, true);
|
||||
CheckCondition(!write_to_manifest);
|
||||
write_to_manifest =
|
||||
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
|
||||
CheckCondition(write_to_manifest);
|
||||
|
||||
db = rocksdb_open(options_dbid_in_manifest, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_t* checkpoint =
|
||||
rocksdb_checkpoint_object_create(db, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_checkpoint_object_destroy(checkpoint);
|
||||
checkpoint = NULL;
|
||||
|
||||
// get the identity before the backup
|
||||
size_t before_db_id_len = 0;
|
||||
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
|
||||
CheckCondition(before_db_id_len == 36);
|
||||
|
||||
// open the checkpoint
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options_dbid_in_manifest, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_error_if_exists(options_dbid_in_manifest, 0);
|
||||
db = rocksdb_open(options_dbid_in_manifest, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// the db_identity after the checkpoint is the same
|
||||
size_t after_db_id_len = 0;
|
||||
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
|
||||
CheckCondition(after_db_id_len == 36);
|
||||
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) == 0);
|
||||
Free(&before_db_id);
|
||||
Free(&after_db_id);
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options_dbid_in_manifest, dbcheckpointname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_options_destroy(options_dbid_in_manifest);
|
||||
options_dbid_in_manifest = NULL;
|
||||
|
||||
// re-open the default database
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("compactall");
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
@@ -1761,21 +1594,6 @@ int main(int argc, char** argv) {
|
||||
CheckPinGetCF(db, roptions, handles[1], "bar", NULL);
|
||||
CheckPinGetCF(db, roptions, handles[1], "box", "c");
|
||||
CheckPinGetCF(db, roptions, handles[1], "buff", "rocksdb");
|
||||
rocksdb_writebatch_clear(wb);
|
||||
// Test WriteBatch iteration with Column Family
|
||||
int pos = 0;
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
|
||||
rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
|
||||
rocksdb_writebatch_merge_cf(wb, handles[1], "box", 3, "cc", 2);
|
||||
rocksdb_writebatch_put(wb, "foo", 3, "f", 1);
|
||||
rocksdb_writebatch_delete(wb, "foo", 3);
|
||||
rocksdb_writebatch_put(wb, "baz", 3, "a", 1);
|
||||
rocksdb_writebatch_merge(wb, "baz", 3, "aa", 2);
|
||||
rocksdb_writebatch_iterate_cf(wb, &pos, CheckPutCF, CheckDelCF,
|
||||
CheckMergeCF);
|
||||
CheckCondition(pos == 8);
|
||||
rocksdb_writebatch_clear(wb);
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
|
||||
rocksdb_flush_wal(db, 1, &err);
|
||||
@@ -2322,12 +2140,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(rocksdb_statistics_level_all ==
|
||||
rocksdb_options_get_statistics_level(o));
|
||||
|
||||
CheckCondition(0 ==
|
||||
rocksdb_options_get_track_and_verify_wals_in_manifest(o));
|
||||
rocksdb_options_set_track_and_verify_wals_in_manifest(o, 42);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_track_and_verify_wals_in_manifest(o));
|
||||
|
||||
/* Blob Options */
|
||||
rocksdb_options_set_enable_blob_files(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_blob_files(o));
|
||||
|
||||
@@ -538,7 +538,6 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
refs_(0),
|
||||
initialized_(false),
|
||||
dropped_(false),
|
||||
flush_skip_reschedule_(false),
|
||||
internal_comparator_(cf_options.comparator),
|
||||
initial_cf_options_(SanitizeOptions(db_options, cf_options)),
|
||||
ioptions_(db_options, initial_cf_options_),
|
||||
@@ -1529,20 +1528,6 @@ Status ColumnFamilyData::ValidateOptions(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cf_options.compaction_style == kCompactionStyleUniversal) {
|
||||
int max_read_amp = cf_options.compaction_options_universal.max_read_amp;
|
||||
if (max_read_amp < -1) {
|
||||
return Status::NotSupported(
|
||||
"CompactionOptionsUniversal::max_read_amp should be at least -1.");
|
||||
} else if (0 < max_read_amp &&
|
||||
max_read_amp < cf_options.level0_file_num_compaction_trigger) {
|
||||
return Status::NotSupported(
|
||||
"CompactionOptionsUniversal::max_read_amp limits the number of sorted"
|
||||
" runs but is smaller than the compaction trigger "
|
||||
"level0_file_num_compaction_trigger.");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1622,19 +1607,6 @@ FSDirectory* ColumnFamilyData::GetDataDir(size_t path_id) const {
|
||||
return data_dirs_[path_id].get();
|
||||
}
|
||||
|
||||
void ColumnFamilyData::SetFlushSkipReschedule() {
|
||||
const Comparator* ucmp = user_comparator();
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
if (ts_sz == 0 || ioptions_.persist_user_defined_timestamps) {
|
||||
return;
|
||||
}
|
||||
flush_skip_reschedule_.store(true);
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::GetAndClearFlushSkipReschedule() {
|
||||
return flush_skip_reschedule_.exchange(false);
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::ShouldPostponeFlushToRetainUDT(
|
||||
uint64_t max_memtable_id) {
|
||||
const Comparator* ucmp = user_comparator();
|
||||
|
||||
@@ -329,10 +329,6 @@ class ColumnFamilyData {
|
||||
void SetDropped();
|
||||
bool IsDropped() const { return dropped_.load(std::memory_order_relaxed); }
|
||||
|
||||
void SetFlushSkipReschedule();
|
||||
|
||||
bool GetAndClearFlushSkipReschedule();
|
||||
|
||||
// thread-safe
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
|
||||
@@ -572,10 +568,6 @@ class ColumnFamilyData {
|
||||
// of its files (if missing)
|
||||
void RecoverEpochNumbers();
|
||||
|
||||
int GetUnflushedMemTableCountForWriteStallCheck() const {
|
||||
return (mem_->IsEmpty() ? 0 : 1) + imm_.NumNotFlushed();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
@@ -600,15 +592,6 @@ class ColumnFamilyData {
|
||||
std::atomic<bool> initialized_;
|
||||
std::atomic<bool> dropped_; // true if client dropped it
|
||||
|
||||
// When user-defined timestamps in memtable only feature is enabled, this
|
||||
// flag indicates a successfully requested flush that should
|
||||
// skip being rescheduled and haven't undergone the rescheduling check yet.
|
||||
// This flag is cleared when a check skips rescheduling a FlushRequest.
|
||||
// With this flag, automatic flushes in regular cases can continue to
|
||||
// retain UDTs by getting rescheduled as usual while manual flushes and
|
||||
// error recovery flushes will proceed without getting rescheduled.
|
||||
std::atomic<bool> flush_skip_reschedule_;
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
InternalTblPropCollFactories internal_tbl_prop_coll_factories_;
|
||||
|
||||
|
||||
+60
-234
@@ -23,7 +23,6 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
@@ -35,13 +34,6 @@
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
std::string EncodeAsUint64(uint64_t number) {
|
||||
std::string result;
|
||||
PutFixed64(&result, number);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static const int kValueSize = 1000;
|
||||
|
||||
@@ -3178,8 +3170,6 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile1) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(2));
|
||||
Open();
|
||||
@@ -3232,8 +3222,6 @@ TEST_P(ColumnFamilyTest, IteratorCloseWALFile2) {
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(2));
|
||||
Open();
|
||||
@@ -3291,8 +3279,6 @@ TEST_P(ColumnFamilyTest, ForwardIteratorCloseWALFile) {
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
// When this option is removed, the test will need re-engineering
|
||||
db_options_.background_close_inactive_wals = true;
|
||||
column_family_options_.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(3));
|
||||
column_family_options_.level0_file_num_compaction_trigger = 2;
|
||||
@@ -3617,9 +3603,7 @@ TEST(ColumnFamilyTest, ValidateMemtableKVChecksumOption) {
|
||||
}
|
||||
|
||||
// Tests the flushing behavior of a column family to retain user-defined
|
||||
// timestamp when `persist_user_defined_timestamp` is false. The behavior of
|
||||
// auto flush is it makes some effort to retain user-defined timestamps while
|
||||
// the behavior of manual flush is that it skips retaining UDTs.
|
||||
// timestamp when `persist_user_defined_timestamp` is false.
|
||||
class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
|
||||
public:
|
||||
ColumnFamilyRetainUDTTest() : ColumnFamilyTestBase(kLatestFormatVersion) {}
|
||||
@@ -3637,27 +3621,6 @@ class ColumnFamilyRetainUDTTest : public ColumnFamilyTestBase {
|
||||
return db_->Put(WriteOptions(), handles_[cf], Slice(key), Slice(ts),
|
||||
Slice(value));
|
||||
}
|
||||
|
||||
std::string Get(int cf, const std::string& key, const std::string& read_ts) {
|
||||
ReadOptions ropts;
|
||||
Slice timestamp = read_ts;
|
||||
ropts.timestamp = ×tamp;
|
||||
std::string value;
|
||||
Status s = db_->Get(ropts, handles_[cf], Slice(key), &value);
|
||||
if (s.IsNotFound()) {
|
||||
return "NOT_FOUND";
|
||||
} else if (s.ok()) {
|
||||
return value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void CheckEffectiveCutoffTime(uint64_t expected_cutoff) {
|
||||
std::string effective_full_history_ts_low;
|
||||
EXPECT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
EXPECT_EQ(EncodeAsUint64(expected_cutoff), effective_full_history_ts_low);
|
||||
}
|
||||
};
|
||||
|
||||
class TestTsComparator : public Comparator {
|
||||
@@ -3701,9 +3664,7 @@ TEST_F(ColumnFamilyRetainUDTTest, SanityCheck) {
|
||||
Close();
|
||||
}
|
||||
|
||||
class AutoFlushRetainUDTTest : public ColumnFamilyRetainUDTTest {};
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3713,22 +3674,28 @@ TEST_F(AutoFlushRetainUDTTest, FullHistoryTsLowNotSet) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
// No `full_history_ts_low` explicitly set by user, auto flush is continued
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
// No `full_history_ts_low` explicitly set by user, flush is continued
|
||||
// without checking if its UDTs expired.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// After flush, `full_history_ts_low` should be automatically advanced to
|
||||
// the effective cutoff timestamp: write_ts + 1
|
||||
CheckEffectiveCutoffTime(2);
|
||||
std::string cutoff_ts;
|
||||
PutFixed64(&cutoff_ts, 2);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, AllKeysExpired) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, AllKeysExpired) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3738,22 +3705,27 @@ TEST_F(AutoFlushRetainUDTTest, AllKeysExpired) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(3)));
|
||||
// All keys expired w.r.t the configured `full_history_ts_low`, auto flush
|
||||
// continue without the need for a re-schedule.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
std::string cutoff_ts;
|
||||
PutFixed64(&cutoff_ts, 3);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
// All keys expired w.r.t the configured `full_history_ts_low`, flush continue
|
||||
// without the need for a re-schedule.
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// `full_history_ts_low` stays unchanged after flush.
|
||||
CheckEffectiveCutoffTime(3);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
TEST_F(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
@@ -3763,218 +3735,72 @@ TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushToAvoidWriteStall) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(1)));
|
||||
std::string cutoff_ts;
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
PutFixed64(&cutoff_ts, 1);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
ASSERT_OK(db_->SetOptions(handles_[0], {{"max_write_buffer_number", "1"}}));
|
||||
// Not all keys expired, but auto flush is continued without a re-schedule
|
||||
// because of risk of write stall.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Not all keys expired, but flush is continued without a re-schedule because
|
||||
// of risk of write stall.
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
// After flush, `full_history_ts_low` should be automatically advanced to
|
||||
// the effective cutoff timestamp: write_ts + 1
|
||||
CheckEffectiveCutoffTime(2);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
|
||||
cutoff_ts.clear();
|
||||
PutFixed64(&cutoff_ts, 2);
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(AutoFlushRetainUDTTest, NotAllKeysExpiredFlushRescheduled) {
|
||||
std::atomic<int> local_counter{1};
|
||||
TEST_F(ColumnFamilyRetainUDTTest, NotAllKeysExpiredFlushRescheduled) {
|
||||
std::string cutoff_ts;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AfterRetainUDTReschedule:cb", [&](void* /*arg*/) {
|
||||
// Increasing full_history_ts_low so all keys expired after the initial
|
||||
// FlushRequest is rescheduled
|
||||
ASSERT_OK(
|
||||
db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(3)));
|
||||
cutoff_ts.clear();
|
||||
PutFixed64(&cutoff_ts, 3);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(2, reschedule_count);
|
||||
local_counter.fetch_add(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Open();
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(1)));
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(Put(0, "foo", write_ts, "v1"));
|
||||
PutFixed64(&cutoff_ts, 1);
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], cutoff_ts));
|
||||
// Not all keys expired, and there is no risk of write stall. Flush is
|
||||
// rescheduled. The actual flush happens after `full_history_ts_low` is
|
||||
// increased to mark all keys expired.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Make sure callback is not skipped.
|
||||
ASSERT_EQ(2, local_counter);
|
||||
ASSERT_OK(Flush(0));
|
||||
|
||||
CheckEffectiveCutoffTime(3);
|
||||
std::string effective_full_history_ts_low;
|
||||
ASSERT_OK(
|
||||
db_->GetFullHistoryTsLow(handles_[0], &effective_full_history_ts_low));
|
||||
// `full_history_ts_low` stays unchanged.
|
||||
ASSERT_EQ(cutoff_ts, effective_full_history_ts_low);
|
||||
Close();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
class ManualFlushSkipRetainUDTTest : public ColumnFamilyRetainUDTTest {
|
||||
public:
|
||||
// Write an entry with timestamp that is not expired w.r.t cutoff timestamp,
|
||||
// and make sure automatic flush would be rescheduled to retain UDT.
|
||||
void CheckAutomaticFlushRetainUDT(uint64_t write_ts) {
|
||||
std::atomic<int> local_counter{1};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AfterRetainUDTReschedule:cb", [&](void* /*arg*/) {
|
||||
// Increasing full_history_ts_low so all keys expired after the
|
||||
// initial FlushRequest is rescheduled
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(
|
||||
handles_[0], EncodeAsUint64(write_ts + 1)));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundFlush:CheckFlushRequest:cb", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
auto reschedule_count = *static_cast<int*>(arg);
|
||||
ASSERT_EQ(2, reschedule_count);
|
||||
local_counter.fetch_add(1);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
EXPECT_OK(Put(0, "foo", EncodeAsUint64(write_ts),
|
||||
"foo" + std::to_string(write_ts)));
|
||||
EXPECT_OK(dbfull()->TEST_SwitchWAL());
|
||||
EXPECT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Make sure callback is not skipped.
|
||||
EXPECT_EQ(2, local_counter);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ManualFlush) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
// Manual flush proceeds without trying to retain UDT.
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(Flush(0));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ManualCompaction) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
// Manual compaction proceeds without trying to retain UDT.
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v2"));
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(CompactRangeOptions(), handles_[0], nullptr, nullptr));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, BulkLoading) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
|
||||
// Test flush behavior in bulk loading scenarios.
|
||||
Options options(db_options_, column_family_options_);
|
||||
std::string sst_files_dir = dbname_ + "/sst_files/";
|
||||
ASSERT_OK(DestroyDir(env_, sst_files_dir));
|
||||
ASSERT_OK(env_->CreateDir(sst_files_dir));
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
std::string file1 = sst_files_dir + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
ASSERT_OK(sst_file_writer.Put("foo", EncodeAsUint64(0), "v2"));
|
||||
ExternalSstFileInfo file1_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file1_info));
|
||||
|
||||
// Bulk loading in UDT mode doesn't support external file key range overlap
|
||||
// with DB key range.
|
||||
ASSERT_TRUE(db_->IngestExternalFile({file1}, IngestExternalFileOptions())
|
||||
.IsInvalidArgument());
|
||||
|
||||
std::string file2 = sst_files_dir + "file2.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
ASSERT_OK(sst_file_writer.Put("bar", EncodeAsUint64(0), "val"));
|
||||
ExternalSstFileInfo file2_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
||||
// A successful bulk loading, and it doesn't trigger any flush. As a result
|
||||
// the effective cutoff timestamp is also unchanged.
|
||||
ASSERT_OK(db_->IngestExternalFile({file2}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get(0, "foo", EncodeAsUint64(1)), "v1");
|
||||
ASSERT_EQ(Get(0, "bar", EncodeAsUint64(0)), "val");
|
||||
CheckEffectiveCutoffTime(0);
|
||||
CheckAutomaticFlushRetainUDT(1);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, AutomaticFlushQueued) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
ASSERT_OK(Put(0, "foo", EncodeAsUint64(1), "v1"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
CheckEffectiveCutoffTime(0);
|
||||
|
||||
// Default `max_write_buffer_number=2` used, writing another memtable can get
|
||||
// automatic flush to proceed because of memory pressure. Not doing that so
|
||||
// we can test automatic flush gets to proceed because of an ongoing manual
|
||||
// flush attempt.
|
||||
ASSERT_OK(Flush(0));
|
||||
CheckEffectiveCutoffTime(2);
|
||||
CheckAutomaticFlushRetainUDT(3);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ManualFlushSkipRetainUDTTest, ConcurrentManualFlushes) {
|
||||
Open();
|
||||
ASSERT_OK(db_->IncreaseFullHistoryTsLow(handles_[0], EncodeAsUint64(0)));
|
||||
|
||||
std::vector<ROCKSDB_NAMESPACE::port::Thread> manual_flush_tds;
|
||||
std::atomic<int> next_ts{0};
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
|
||||
auto manual_flush = [&](int write_ts) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock,
|
||||
[&write_ts, &next_ts] { return write_ts == next_ts.load(); });
|
||||
ASSERT_OK(Put(0, "foo" + std::to_string(write_ts),
|
||||
EncodeAsUint64(write_ts),
|
||||
"val_" + std::to_string(write_ts)));
|
||||
next_ts.fetch_add(1);
|
||||
cv.notify_all();
|
||||
}
|
||||
if (write_ts % 2 == 0) {
|
||||
ASSERT_OK(Flush(0));
|
||||
} else {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), handles_[0], nullptr,
|
||||
nullptr));
|
||||
}
|
||||
};
|
||||
|
||||
for (int write_ts = 0; write_ts < 10; write_ts++) {
|
||||
manual_flush_tds.emplace_back(manual_flush, write_ts);
|
||||
}
|
||||
|
||||
for (auto& td : manual_flush_tds) {
|
||||
td.join();
|
||||
}
|
||||
|
||||
CheckEffectiveCutoffTime(10);
|
||||
CheckAutomaticFlushRetainUDT(11);
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -63,9 +63,7 @@ uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
// TODO(hx235): consider making this function part of the construction so we
|
||||
// don't forget to call it
|
||||
void Compaction::FinalizeInputInfo(Version* _input_version) {
|
||||
void Compaction::SetInputVersion(Version* _input_version) {
|
||||
input_version_ = _input_version;
|
||||
cfd_ = input_version_->cfd();
|
||||
|
||||
@@ -866,11 +864,6 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cfd_->ioptions()->table_factory->Name() ==
|
||||
TableFactory::kPlainTableName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Round-Robin pri under leveled compaction allows subcompactions by default
|
||||
// and the number of subcompactions can be larger than max_subcompactions_
|
||||
if (cfd_->ioptions()->compaction_pri == kRoundRobin &&
|
||||
|
||||
@@ -289,7 +289,14 @@ class Compaction {
|
||||
// is the sum of all input file sizes.
|
||||
uint64_t OutputFilePreallocationSize() const;
|
||||
|
||||
void FinalizeInputInfo(Version* input_version);
|
||||
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
|
||||
// status and fail the compaction if needed
|
||||
// TODO(hx235): consider making this function part of the construction so we
|
||||
// don't forget to call it
|
||||
void FinalizeInputInfo(Version* input_version) {
|
||||
SetInputVersion(input_version);
|
||||
InitInputTableProperties().PermitUncheckedError();
|
||||
}
|
||||
|
||||
struct InputLevelSummaryBuffer {
|
||||
char buffer[128];
|
||||
@@ -326,16 +333,6 @@ class Compaction {
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs);
|
||||
|
||||
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
|
||||
// status and fail the compaction if needed
|
||||
//
|
||||
// May open and read table files for table property.
|
||||
// Should not be called while holding mutex_.
|
||||
const TablePropertiesCollection& GetOrInitInputTableProperties() {
|
||||
InitInputTableProperties().PermitUncheckedError();
|
||||
return input_table_properties_;
|
||||
}
|
||||
|
||||
const TablePropertiesCollection& GetInputTableProperties() const {
|
||||
return input_table_properties_;
|
||||
}
|
||||
@@ -436,6 +433,7 @@ class Compaction {
|
||||
const int output_level);
|
||||
|
||||
private:
|
||||
void SetInputVersion(Version* input_version);
|
||||
|
||||
Status InitInputTableProperties();
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace ROCKSDB_NAMESPACE {
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
@@ -41,7 +40,7 @@ CompactionIterator::CompactionIterator(
|
||||
const SequenceNumber preserve_time_min_seqno,
|
||||
const SequenceNumber preclude_last_level_min_seqno)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots, earliest_snapshot,
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, job_snapshot, snapshot_checker, env,
|
||||
report_detailed_time, expect_valid_internal_key, range_del_agg,
|
||||
blob_file_builder, allow_data_in_errors, enforce_single_del_contracts,
|
||||
@@ -55,7 +54,6 @@ CompactionIterator::CompactionIterator(
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber /*last_sequence*/, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
@@ -93,7 +91,9 @@ CompactionIterator::CompactionIterator(
|
||||
// snapshots_ cannot be nullptr, but we will assert later in the body of
|
||||
// the constructor.
|
||||
visible_at_tip_(snapshots_ ? snapshots_->empty() : false),
|
||||
earliest_snapshot_(earliest_snapshot),
|
||||
earliest_snapshot_(!snapshots_ || snapshots_->empty()
|
||||
? kMaxSequenceNumber
|
||||
: snapshots_->at(0)),
|
||||
info_log_(info_log),
|
||||
allow_data_in_errors_(allow_data_in_errors),
|
||||
enforce_single_del_contracts_(enforce_single_del_contracts),
|
||||
|
||||
@@ -203,7 +203,6 @@ class CompactionIterator {
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
@@ -223,7 +222,6 @@ class CompactionIterator {
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_snapshot,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SequenceNumber job_snapshot, const SnapshotChecker* snapshot_checker,
|
||||
Env* env, bool report_detailed_time, bool expect_valid_internal_key,
|
||||
|
||||
@@ -296,7 +296,6 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
snapshots_.empty() ? kMaxSequenceNumber : snapshots_.at(0),
|
||||
earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker_.get(), Env::Default(),
|
||||
false /* report_detailed_time */, false, range_del_agg_.get(),
|
||||
|
||||
@@ -174,9 +174,6 @@ CompactionJob::CompactionJob(
|
||||
db_mutex_(db_mutex),
|
||||
db_error_handler_(db_error_handler),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
earliest_snapshot_(existing_snapshots_.empty()
|
||||
? kMaxSequenceNumber
|
||||
: existing_snapshots_.at(0)),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
job_context_(job_context),
|
||||
@@ -285,7 +282,6 @@ void CompactionJob::Prepare() {
|
||||
|
||||
// collect all seqno->time information from the input files which will be used
|
||||
// to encode seqno->time to the output files.
|
||||
|
||||
uint64_t preserve_time_duration =
|
||||
std::max(c->immutable_options()->preserve_internal_time_seconds,
|
||||
c->immutable_options()->preclude_last_level_data_seconds);
|
||||
@@ -323,11 +319,28 @@ void CompactionJob::Prepare() {
|
||||
seqno_to_time_mapping_.Enforce();
|
||||
} else {
|
||||
seqno_to_time_mapping_.Enforce(_current_time);
|
||||
seqno_to_time_mapping_.GetCurrentTieringCutoffSeqnos(
|
||||
static_cast<uint64_t>(_current_time),
|
||||
c->immutable_options()->preserve_internal_time_seconds,
|
||||
c->immutable_options()->preclude_last_level_data_seconds,
|
||||
&preserve_time_min_seqno_, &preclude_last_level_min_seqno_);
|
||||
uint64_t preserve_time =
|
||||
static_cast<uint64_t>(_current_time) > preserve_time_duration
|
||||
? _current_time - preserve_time_duration
|
||||
: 0;
|
||||
// GetProximalSeqnoBeforeTime tells us the last seqno known to have been
|
||||
// written at or before the given time. + 1 to get the minimum we should
|
||||
// preserve without excluding anything that might have been written on or
|
||||
// after the given time.
|
||||
preserve_time_min_seqno_ =
|
||||
seqno_to_time_mapping_.GetProximalSeqnoBeforeTime(preserve_time) + 1;
|
||||
if (c->immutable_options()->preclude_last_level_data_seconds > 0) {
|
||||
uint64_t preclude_last_level_time =
|
||||
static_cast<uint64_t>(_current_time) >
|
||||
c->immutable_options()->preclude_last_level_data_seconds
|
||||
? _current_time -
|
||||
c->immutable_options()->preclude_last_level_data_seconds
|
||||
: 0;
|
||||
preclude_last_level_min_seqno_ =
|
||||
seqno_to_time_mapping_.GetProximalSeqnoBeforeTime(
|
||||
preclude_last_level_time) +
|
||||
1;
|
||||
}
|
||||
}
|
||||
// For accuracy of the GetProximalSeqnoBeforeTime queries above, we only
|
||||
// limit the capacity after them.
|
||||
@@ -468,11 +481,6 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
read_options.rate_limiter_priority = GetRateLimiterPriority();
|
||||
auto* c = compact_->compaction;
|
||||
if (c->immutable_options()->table_factory->Name() ==
|
||||
TableFactory::kPlainTableName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->max_subcompactions() <= 1 &&
|
||||
!(c->immutable_options()->compaction_pri == kRoundRobin &&
|
||||
c->immutable_options()->compaction_style == kCompactionStyleLevel)) {
|
||||
@@ -626,7 +634,6 @@ Status CompactionJob::Run() {
|
||||
const size_t num_threads = compact_->sub_compact_states.size();
|
||||
assert(num_threads > 0);
|
||||
const uint64_t start_micros = db_options_.clock->NowMicros();
|
||||
compact_->compaction->GetOrInitInputTableProperties();
|
||||
|
||||
// Launch a thread for each of subcompactions 1...num_threads-1
|
||||
std::vector<port::Thread> thread_pool;
|
||||
@@ -1288,9 +1295,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
auto c_iter = std::make_unique<CompactionIterator>(
|
||||
input, cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_snapshot_,
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq, snapshot_checker_,
|
||||
env_, ShouldReportDetailedTime(env_, stats_),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_, job_snapshot_seq,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_),
|
||||
/*expect_valid_internal_key=*/true, range_del_agg.get(),
|
||||
blob_file_builder.get(), db_options_.allow_data_in_errors,
|
||||
db_options_.enforce_single_del_contracts, manual_compaction_canceled_,
|
||||
@@ -1963,10 +1969,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
cfd->GetName(), sub_compact->compaction->output_level(),
|
||||
bottommost_level_, TableFileCreationReason::kCompaction,
|
||||
0 /* oldest_key_time */, current_time, db_id_, db_session_id_,
|
||||
sub_compact->compaction->max_output_file_size(), file_number,
|
||||
preclude_last_level_min_seqno_ == kMaxSequenceNumber
|
||||
? preclude_last_level_min_seqno_
|
||||
: std::min(earliest_snapshot_, preclude_last_level_min_seqno_));
|
||||
sub_compact->compaction->max_output_file_size(), file_number);
|
||||
|
||||
outputs.NewBuilder(tboptions);
|
||||
|
||||
|
||||
@@ -314,8 +314,6 @@ class CompactionJob {
|
||||
// deleted because that version is not visible in any snapshot.
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
|
||||
SequenceNumber earliest_snapshot_;
|
||||
|
||||
// This is the earliest snapshot that could be used for write-conflict
|
||||
// checking by a transaction. For any user-key newer than this snapshot, we
|
||||
// should make sure not to remove evidence that a write occurred.
|
||||
|
||||
@@ -4330,118 +4330,6 @@ TEST_F(CompactionPickerTest, IntraL0WhenL0IsSmall) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMaxReadAmpLargeDB) {
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
ioptions_.num_levels = 50;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
mutable_cf_options_.compaction_options_universal.size_ratio = 10;
|
||||
mutable_cf_options_.write_buffer_size = 256 << 20;
|
||||
// Avoid space amp compaction
|
||||
mutable_cf_options_.compaction_options_universal
|
||||
.max_size_amplification_percent = 200;
|
||||
const int kMaxRuns = 8;
|
||||
for (int max_read_amp : {kMaxRuns, 0, -1}) {
|
||||
SCOPED_TRACE("max_read_amp = " + std::to_string(max_read_amp));
|
||||
if (max_read_amp == -1) {
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = kMaxRuns;
|
||||
} else {
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
|
||||
}
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp =
|
||||
max_read_amp;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
uint64_t max_run_size = 20ull << 30;
|
||||
// When max_read_amp = 0, we estimate the number of levels needed based on
|
||||
// size_ratio and write_buffer_size. See more in
|
||||
// UniversalCompactionBuilder::PickCompaction().
|
||||
// With a 20GB last level, we estimate that 8 levels are needed:
|
||||
// L0 256MB
|
||||
// L1 256MB * 1.1 (size_ratio) = 282MB
|
||||
// L2 (256MB + 282MB) * 1.1 = 592MB
|
||||
// L3 1243MB
|
||||
// L4 2610MB
|
||||
// L5 5481MB
|
||||
// L6 11510MB
|
||||
// L7 24171MB > 20GB
|
||||
for (int i = 0; i <= kMaxRuns; ++i) {
|
||||
SCOPED_TRACE("i = " + std::to_string(i));
|
||||
NewVersionStorage(/*num_levels=*/50, kCompactionStyleUniversal);
|
||||
Add(/*level=*/49, /*file_number=*/10, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/max_run_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/0, /*largest_seq=*/0,
|
||||
/*compensated_file_size=*/max_run_size);
|
||||
// Besides the last sorted run, we add additional `i` sorted runs
|
||||
// without triggering space-amp or size-amp compactions.
|
||||
uint64_t file_size = 1 << 20;
|
||||
for (int j = 0; j < i; ++j) {
|
||||
Add(/*level=*/j, /*file_number=*/100 - j, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/file_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/100 - j, /*largest_seq=*/100 - j,
|
||||
/*compensated_file_size=*/file_size);
|
||||
// to avoid space-amp and size-amp compaction
|
||||
file_size *= 2;
|
||||
}
|
||||
UpdateVersionStorageInfo();
|
||||
// level0_file_num_compaction_trigger is still used as trigger to
|
||||
// check potential compactions
|
||||
ASSERT_EQ(
|
||||
universal_compaction_picker.NeedsCompaction(vstorage_.get()),
|
||||
i + 1 >= mutable_cf_options_.level0_file_num_compaction_trigger);
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_,
|
||||
vstorage_.get(), &log_buffer_));
|
||||
if (i == kMaxRuns) {
|
||||
// There are in total i + 1 > kMaxRuns sorted runs.
|
||||
// This triggers compaction ignoring size_ratio.
|
||||
ASSERT_NE(nullptr, compaction);
|
||||
ASSERT_EQ(CompactionReason::kUniversalSortedRunNum,
|
||||
compaction->compaction_reason());
|
||||
// First two runs are compacted
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(1, compaction->output_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
} else {
|
||||
ASSERT_EQ(nullptr, compaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMaxReadAmpSmallDB) {
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
ioptions_.num_levels = 50;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 1;
|
||||
mutable_cf_options_.compaction_options_universal.size_ratio = 10;
|
||||
mutable_cf_options_.write_buffer_size = 256 << 20;
|
||||
mutable_cf_options_.compaction_options_universal
|
||||
.max_size_amplification_percent = 200;
|
||||
const int kMaxRuns = 1;
|
||||
for (int max_read_amp : {-1, kMaxRuns, 0}) {
|
||||
SCOPED_TRACE("max_read_amp = " + std::to_string(max_read_amp));
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp =
|
||||
max_read_amp;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
NewVersionStorage(/*num_levels=*/50, kCompactionStyleUniversal);
|
||||
// max_run_size is much smaller than write_buffer_size,
|
||||
// only 1 level is needed.
|
||||
uint64_t max_run_size = 8 << 10;
|
||||
Add(/*level=*/49, /*file_number=*/10, /*smallest=*/"100",
|
||||
/*largest=*/"200", /*file_size=*/max_run_size, /*path_id=*/0,
|
||||
/*smallest_seq=*/0, /*largest_seq=*/0,
|
||||
/*compensated_file_size=*/max_run_size);
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_TRUE(universal_compaction_picker.NeedsCompaction(vstorage_.get()));
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_EQ(nullptr, compaction);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -227,7 +227,6 @@ class UniversalCompactionBuilder {
|
||||
const InternalKeyComparator* icmp_;
|
||||
double score_;
|
||||
std::vector<SortedRun> sorted_runs_;
|
||||
uint64_t max_run_size_;
|
||||
const std::string& cf_name_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const MutableDBOptions& mutable_db_options_;
|
||||
@@ -236,8 +235,7 @@ class UniversalCompactionBuilder {
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level,
|
||||
uint64_t* max_run_size);
|
||||
const VersionStorageInfo& vstorage, int last_level);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -442,15 +440,11 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level,
|
||||
uint64_t* max_run_size) {
|
||||
assert(max_run_size);
|
||||
*max_run_size = 0;
|
||||
const VersionStorageInfo& vstorage, int last_level) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
*max_run_size = std::max(*max_run_size, f->fd.GetFileSize());
|
||||
}
|
||||
for (int level = 1; level <= last_level; level++) {
|
||||
uint64_t total_compensated_size = 0U;
|
||||
@@ -472,7 +466,6 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
ret.emplace_back(level, nullptr, total_size, total_compensated_size,
|
||||
being_compacted);
|
||||
}
|
||||
*max_run_size = std::max(*max_run_size, total_size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -484,16 +477,13 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
max_run_size_ = 0;
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, max_output_level, &max_run_size_);
|
||||
int file_num_compaction_trigger =
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger;
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_, max_output_level);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
vstorage_->FilesMarkedForCompaction().empty() &&
|
||||
sorted_runs_.size() < (unsigned int)file_num_compaction_trigger)) {
|
||||
sorted_runs_.size() < (unsigned int)mutable_cf_options_
|
||||
.level0_file_num_compaction_trigger)) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: nothing to do\n",
|
||||
cf_name_.c_str());
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
@@ -515,9 +505,11 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
TEST_SYNC_POINT_CALLBACK("PostPickPeriodicCompaction", c);
|
||||
}
|
||||
|
||||
// Check for size amplification.
|
||||
if (c == nullptr &&
|
||||
sorted_runs_.size() >= static_cast<size_t>(file_num_compaction_trigger)) {
|
||||
// Check for size amplification.
|
||||
sorted_runs_.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger)) {
|
||||
if ((c = PickCompactionToReduceSizeAmp()) != nullptr) {
|
||||
TEST_SYNC_POINT("PickCompactionToReduceSizeAmpReturnNonnullptr");
|
||||
ROCKS_LOG_BUFFER(log_buffer_, "[%s] Universal: compacting for size amp\n",
|
||||
@@ -535,48 +527,13 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
cf_name_.c_str());
|
||||
} else {
|
||||
// Size amplification and file size ratios are within configured limits.
|
||||
// If max read amplification exceeds configured limits, then force
|
||||
// compaction to reduce the number sorted runs without looking at file
|
||||
// size ratios.
|
||||
|
||||
// If max read amplification is exceeding configured limits, then force
|
||||
// compaction without looking at filesize ratios and try to reduce
|
||||
// the number of files to fewer than level0_file_num_compaction_trigger.
|
||||
// This is guaranteed by NeedsCompaction()
|
||||
assert(sorted_runs_.size() >=
|
||||
static_cast<size_t>(file_num_compaction_trigger));
|
||||
int max_num_runs =
|
||||
mutable_cf_options_.compaction_options_universal.max_read_amp;
|
||||
if (max_num_runs < 0) {
|
||||
// any value < -1 is not valid
|
||||
assert(max_num_runs == -1);
|
||||
// By default, fall back to `level0_file_num_compaction_trigger`
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
} else if (max_num_runs == 0) {
|
||||
if (mutable_cf_options_.compaction_options_universal.stop_style ==
|
||||
kCompactionStopStyleTotalSize) {
|
||||
// 0 means auto-tuning by RocksDB. We estimate max num run based on
|
||||
// max_run_size, size_ratio and write buffer size:
|
||||
// Assume the size of the lowest level size is equal to
|
||||
// write_buffer_size. Each subsequent level is the max size without
|
||||
// triggering size_ratio compaction. `max_num_runs` is the minimum
|
||||
// number of levels required such that the target size of the
|
||||
// largest level is at least `max_run_size_`.
|
||||
max_num_runs = 1;
|
||||
double cur_level_max_size =
|
||||
static_cast<double>(mutable_cf_options_.write_buffer_size);
|
||||
double total_run_size = 0;
|
||||
while (cur_level_max_size < static_cast<double>(max_run_size_)) {
|
||||
// This loop should not take too many iterations since
|
||||
// cur_level_max_size at least doubles each iteration.
|
||||
total_run_size += cur_level_max_size;
|
||||
cur_level_max_size = (100.0 + ratio) / 100.0 * total_run_size;
|
||||
++max_num_runs;
|
||||
}
|
||||
} else {
|
||||
// TODO: implement the auto-tune logic for this stop style
|
||||
max_num_runs = file_num_compaction_trigger;
|
||||
}
|
||||
} else {
|
||||
// max_num_runs > 0, it's the limit on the number of sorted run
|
||||
}
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger));
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs_.size(); i++) {
|
||||
@@ -587,25 +544,17 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater
|
||||
// than the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted > max_num_runs) {
|
||||
unsigned int num_files = num_sr_not_compacted - max_num_runs + 1;
|
||||
if (num_sr_not_compacted >
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger) {
|
||||
unsigned int num_files =
|
||||
num_sr_not_compacted -
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionToReduceSortedRuns(UINT_MAX, num_files)) !=
|
||||
nullptr) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Universal: compacting for file num, to "
|
||||
"compact file num -- %u, max num runs allowed"
|
||||
"-- %d, max_run_size -- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_files, max_num_runs,
|
||||
max_run_size_);
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name_.c_str(), num_files);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] Universal: skipping compaction for file num, num runs not "
|
||||
"being compacted -- %u, max num runs allowed -- %d, max_run_size "
|
||||
"-- %" PRIu64 "\n",
|
||||
cf_name_.c_str(), num_sr_not_compacted, max_num_runs,
|
||||
max_run_size_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,8 +275,6 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
log_buffer_->FlushBufferToLog();
|
||||
LogCompaction();
|
||||
const uint64_t start_micros = db_options_.clock->NowMicros();
|
||||
c->GetOrInitInputTableProperties();
|
||||
|
||||
// Pick the only sub-compaction we should have
|
||||
assert(compact_->sub_compact_states.size() == 1);
|
||||
SubcompactionState* sub_compact = compact_->sub_compact_states.data();
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "test_util/mock_time_env.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
@@ -1735,82 +1734,6 @@ TEST_P(TimedPutPrecludeLastLevelTest, PreserveTimedPutOnPenultimateLevel) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(TimedPutPrecludeLastLevelTest, AutoTriggerCompaction) {
|
||||
const int kNumTrigger = 10;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 200;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.preclude_last_level_data_seconds = 60;
|
||||
options.preserve_internal_time_seconds = 0;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.num_levels = kNumLevels;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
ConfigOptions config_options;
|
||||
config_options.ignore_unsupported_options = false;
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> factory;
|
||||
std::string id = CompactForTieringCollectorFactory::kClassName();
|
||||
ASSERT_OK(TablePropertiesCollectorFactory::CreateFromString(
|
||||
config_options, "compaction_trigger_ratio=0.4; id=" + id, &factory));
|
||||
auto collector_factory =
|
||||
factory->CheckedCast<CompactForTieringCollectorFactory>();
|
||||
options.table_properties_collector_factories.push_back(factory);
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions wo;
|
||||
wo.protection_bytes_per_key = GetParam();
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(rnd.Uniform(10) + 1));
|
||||
});
|
||||
|
||||
for (int i = 0; i < kNumKeys / 4; i++) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100), wo));
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(rnd.Uniform(2)));
|
||||
});
|
||||
}
|
||||
// Create one file with regular Put.
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Create one file with TimedPut.
|
||||
// These data are eligible to be put on the last level once written to db
|
||||
// and compaction will fast track them to the last level.
|
||||
for (int i = kNumKeys / 4; i < kNumKeys / 2; i++) {
|
||||
ASSERT_OK(TimedPut(0, Key(i), rnd.RandomString(100), 50, wo));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// TimedPut file moved to the last level via auto triggered compaction.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("1,0,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
collector_factory->SetCompactionTriggerRatio(1.1);
|
||||
for (int i = kNumKeys / 2; i < kNumKeys * 3 / 4; i++) {
|
||||
ASSERT_OK(TimedPut(0, Key(i), rnd.RandomString(100), 50, wo));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("2,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
collector_factory->SetCompactionTriggerRatio(0);
|
||||
for (int i = kNumKeys * 3 / 4; i < kNumKeys; i++) {
|
||||
ASSERT_OK(TimedPut(0, Key(i), rnd.RandomString(100), 50, wo));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("3,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(TimedPutPrecludeLastLevelTest,
|
||||
TimedPutPrecludeLastLevelTest, ::testing::Values(0, 8));
|
||||
|
||||
|
||||
@@ -71,9 +71,6 @@ Status VerifySstFileChecksumInternal(const Options& options,
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(
|
||||
|
||||
+112
-269
@@ -26,7 +26,6 @@
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/hash.h"
|
||||
@@ -741,6 +740,118 @@ class LookupLiarCache : public CacheWrapper {
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, AddRedundantStats) {
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
int iterations_tested = 0;
|
||||
for (const std::shared_ptr<Cache>& base_cache :
|
||||
{NewLRUCache(capacity, num_shard_bits),
|
||||
// FixedHyperClockCache
|
||||
HyperClockCacheOptions(
|
||||
capacity,
|
||||
BlockBasedTableOptions().block_size /*estimated_value_size*/,
|
||||
num_shard_bits)
|
||||
.MakeSharedCache(),
|
||||
// AutoHyperClockCache
|
||||
HyperClockCacheOptions(capacity, 0 /*estimated_value_size*/,
|
||||
num_shard_bits)
|
||||
.MakeSharedCache()}) {
|
||||
if (!base_cache) {
|
||||
// Skip clock cache when not supported
|
||||
continue;
|
||||
}
|
||||
++iterations_tested;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<LookupLiarCache> cache =
|
||||
std::make_shared<LookupLiarCache>(base_cache);
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// Normal access filter+index+data.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Againt access filter+index+data, but force redundant load+insert on index
|
||||
cache->SetNthLookupNotFound(2);
|
||||
ASSERT_EQ("value", Get("bar"));
|
||||
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just filter (with high probability), and force redundant
|
||||
// load+insert
|
||||
cache->SetNthLookupNotFound(1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("this key was not added"));
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(5, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just data, forcing redundant load+insert
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(read_options)};
|
||||
cache->SetNthLookupNotFound(1);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), "bar");
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(6, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
}
|
||||
EXPECT_GE(iterations_tested, 1);
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -1236,274 +1347,6 @@ TEST_F(DBBlockCacheTest, HyperClockCacheReportProblems) {
|
||||
EXPECT_EQ(logger->PopCounts(), (std::array<int, 3>{{0, 1, 0}}));
|
||||
}
|
||||
|
||||
class DBBlockCacheTypeTest
|
||||
: public DBBlockCacheTest,
|
||||
public secondary_cache_test_util::WithCacheTypeParam {};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBlockCacheTypeTestInstance, DBBlockCacheTypeTest,
|
||||
secondary_cache_test_util::GetTestingCacheTypes());
|
||||
|
||||
TEST_P(DBBlockCacheTypeTest, AddRedundantStats) {
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
estimated_value_size_ = table_options.block_size;
|
||||
std::shared_ptr<Cache> base_cache =
|
||||
NewCache(capacity, num_shard_bits, /*strict_capacity_limit=*/false);
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
std::shared_ptr<LookupLiarCache> cache =
|
||||
std::make_shared<LookupLiarCache>(base_cache);
|
||||
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// Normal access filter+index+data.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Againt access filter+index+data, but force redundant load+insert on index
|
||||
cache->SetNthLookupNotFound(2);
|
||||
ASSERT_EQ("value", Get("bar"));
|
||||
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just filter (with high probability), and force redundant
|
||||
// load+insert
|
||||
cache->SetNthLookupNotFound(1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("this key was not added"));
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(5, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
|
||||
// Access just data, forcing redundant load+insert
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(read_options)};
|
||||
cache->SetNthLookupNotFound(1);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), "bar");
|
||||
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD));
|
||||
EXPECT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD));
|
||||
// --------
|
||||
EXPECT_EQ(6, TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_ADD_REDUNDANT));
|
||||
EXPECT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_ADD_REDUNDANT));
|
||||
// --------
|
||||
EXPECT_EQ(3, TestGetTickerCount(options, BLOCK_CACHE_ADD_REDUNDANT));
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string AltKey(int i) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "altkey%06d", i);
|
||||
return std::string(buf);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_P(DBBlockCacheTypeTest, Uncache) {
|
||||
for (bool partitioned : {false, true}) {
|
||||
SCOPED_TRACE("partitioned=" + std::to_string(partitioned));
|
||||
for (uint32_t ua : {0, 1, 2, 10000}) {
|
||||
SCOPED_TRACE("ua=" + std::to_string(ua));
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
options.uncache_aggressiveness = ua;
|
||||
options.create_if_missing = true;
|
||||
// Don't allow background operations to keep Versions referenced
|
||||
options.stats_dump_period_sec = 0;
|
||||
options.stats_persist_period_sec = 0;
|
||||
auto stats = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.statistics = stats;
|
||||
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
estimated_value_size_ = table_options.block_size;
|
||||
std::shared_ptr<Cache> cache =
|
||||
NewCache(capacity, num_shard_bits, /*strict_capacity_limit=*/false);
|
||||
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.partition_filters = partitioned;
|
||||
table_options.index_type =
|
||||
partitioned ? BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch
|
||||
: BlockBasedTableOptions::IndexType::kBinarySearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
size_t kBaselineCount = 1; // Because of entry stats collector
|
||||
|
||||
ASSERT_EQ(kBaselineCount, cache->GetOccupancyCount());
|
||||
ASSERT_EQ(0U, cache->GetUsage());
|
||||
|
||||
constexpr uint8_t kNumDataBlocks = 10;
|
||||
constexpr uint8_t kNumFiles = 3;
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
// Force some overlap with ordering
|
||||
ASSERT_OK(Put(Key((i * 7) % kNumDataBlocks),
|
||||
Random::GetTLSInstance()->RandomBinaryString(
|
||||
static_cast<int>(table_options.block_size))));
|
||||
if (i >= kNumDataBlocks - kNumFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(int{kNumFiles}, NumTableFilesAtLevel(0));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
size_t meta_blocks_per_file = /*index & filter*/ 2U * (1U + partitioned);
|
||||
ASSERT_EQ(
|
||||
cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks + meta_blocks_per_file * kNumFiles);
|
||||
ASSERT_GE(cache->GetUsage(), kNumDataBlocks * table_options.block_size);
|
||||
|
||||
// Combine into one file, making the originals obsolete
|
||||
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
if (ua == 0) {
|
||||
// Expect to see cache entries for new file and obsolete files
|
||||
EXPECT_EQ(cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks * 2U +
|
||||
meta_blocks_per_file * (kNumFiles + 1));
|
||||
EXPECT_GE(cache->GetUsage(),
|
||||
kNumDataBlocks * table_options.block_size * 2U);
|
||||
} else {
|
||||
// Expect only to see cache entries for new file
|
||||
EXPECT_EQ(cache->GetOccupancyCount(),
|
||||
kBaselineCount + kNumDataBlocks + meta_blocks_per_file);
|
||||
EXPECT_GE(cache->GetUsage(), kNumDataBlocks * table_options.block_size);
|
||||
EXPECT_LT(cache->GetUsage(),
|
||||
kNumDataBlocks * table_options.block_size * 2U);
|
||||
}
|
||||
|
||||
size_t alt_baseline_count = cache->GetOccupancyCount();
|
||||
size_t alt_baseline_usage = cache->GetUsage();
|
||||
ASSERT_OK(stats->Reset());
|
||||
// We aren't generally cleaning up cache entries on DB::Close, especially
|
||||
// because someone might just re-open the same DB.
|
||||
Reopen(options);
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(Key(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
EXPECT_EQ(cache->GetOccupancyCount(), alt_baseline_count);
|
||||
EXPECT_EQ(cache->GetUsage(), alt_baseline_usage);
|
||||
|
||||
// Check for unnecessary unncessary cache churn
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_ADD), 0U);
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_MISS), 0U);
|
||||
ASSERT_GT(stats->getTickerCount(BLOCK_CACHE_HIT), 0U);
|
||||
|
||||
// And now do a similar test as above except with trivial moves, making
|
||||
// sure that we aren't falsely uncaching in that case, which would cause
|
||||
// unnecessary cache misses. Using AltKey instead of Key to avoid
|
||||
// interference.
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
// No overlap
|
||||
ASSERT_OK(
|
||||
Put(AltKey(i), Random::GetTLSInstance()->RandomBinaryString(
|
||||
static_cast<int>(table_options.block_size))));
|
||||
if (i >= kNumDataBlocks - kNumFiles) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(int{kNumFiles}, NumTableFilesAtLevel(0));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(AltKey(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
ASSERT_EQ(cache->GetOccupancyCount(),
|
||||
alt_baseline_count + kNumDataBlocks +
|
||||
meta_blocks_per_file * kNumFiles);
|
||||
ASSERT_GE(cache->GetUsage(),
|
||||
alt_baseline_usage + kNumDataBlocks * table_options.block_size);
|
||||
|
||||
ASSERT_OK(stats->Reset());
|
||||
|
||||
// Make trivial move
|
||||
{
|
||||
auto a = AltKey(0);
|
||||
auto b = AltKey(kNumDataBlocks);
|
||||
Slice slice_a{a};
|
||||
Slice slice_b{b};
|
||||
ASSERT_OK(db_->CompactRange({}, &slice_a, &slice_b));
|
||||
}
|
||||
ASSERT_EQ(/*old*/ 1 + /*new*/ int{kNumFiles}, NumTableFilesAtLevel(1));
|
||||
|
||||
for (int i = 0; i < kNumDataBlocks; i++) {
|
||||
ASSERT_NE(Get(AltKey(i)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
// Should be the same if trivial move
|
||||
ASSERT_EQ(cache->GetOccupancyCount(),
|
||||
alt_baseline_count + kNumDataBlocks +
|
||||
meta_blocks_per_file * kNumFiles);
|
||||
|
||||
// Check for unnecessary unncessary cache churn
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_ADD), 0U);
|
||||
ASSERT_EQ(stats->getTickerCount(BLOCK_CACHE_MISS), 0U);
|
||||
ASSERT_GT(stats->getTickerCount(BLOCK_CACHE_HIT), 0U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DBBlockCacheKeyTest
|
||||
: public DBTestBase,
|
||||
|
||||
+43
-107
@@ -322,67 +322,6 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, FilterNumEntriesCoalesce) {
|
||||
for (bool partition_filters : {true, false}) {
|
||||
SCOPED_TRACE("partition_filters=" + std::to_string(partition_filters));
|
||||
for (bool prefix : {true, false}) {
|
||||
SCOPED_TRACE("prefix=" + std::to_string(prefix));
|
||||
for (bool whole : {true, false}) {
|
||||
SCOPED_TRACE("whole=" + std::to_string(whole));
|
||||
Options options = last_options_;
|
||||
options.prefix_extractor.reset();
|
||||
if (prefix) {
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
}
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
bbto.whole_key_filtering = whole;
|
||||
if (partition_filters) {
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Need a snapshot to allow keeping multiple entries for the same key
|
||||
std::vector<const Snapshot*> snapshots;
|
||||
for (int i = 1; i <= 3; ++i) {
|
||||
std::string val = "val" + std::to_string(i);
|
||||
ASSERT_OK(Put("foo1", val));
|
||||
ASSERT_OK(Put("foo2", val));
|
||||
ASSERT_OK(Put("bar1", val));
|
||||
ASSERT_OK(Put("bar2", val));
|
||||
ASSERT_OK(Put("bar3", val));
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
TablePropertiesCollection tpc;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&tpc));
|
||||
// sanity checks
|
||||
ASSERT_EQ(tpc.size(), 1U);
|
||||
auto& tp = *tpc.begin()->second;
|
||||
EXPECT_EQ(tp.num_entries, 3U * 5U);
|
||||
|
||||
// test checks
|
||||
unsigned ex_filter_entries = 0;
|
||||
if (whole) {
|
||||
ex_filter_entries += 5; // unique keys
|
||||
}
|
||||
if (prefix) {
|
||||
ex_filter_entries += 2; // unique prefixes
|
||||
}
|
||||
EXPECT_EQ(tp.num_filter_entries, ex_filter_entries);
|
||||
|
||||
for (auto* sn : snapshots) {
|
||||
db_->ReleaseSnapshot(sn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
for (bool partition_filters : {true, false}) {
|
||||
Options options = last_options_;
|
||||
@@ -3204,55 +3143,52 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterOptions) {
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, SeekForPrevWithPartitionedFilters) {
|
||||
for (bool wkf : {true, false}) {
|
||||
SCOPED_TRACE("whole_key_filtering=" + std::to_string(wkf));
|
||||
Options options = CurrentOptions();
|
||||
constexpr size_t kNumKeys = 10000;
|
||||
static_assert(kNumKeys <= 10000, "kNumKeys have to be <= 10000");
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeys + 10));
|
||||
options.create_if_missing = true;
|
||||
constexpr size_t kPrefixLength = 4;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(kPrefixLength));
|
||||
options.compression = kNoCompression;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
bbto.index_shortening =
|
||||
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
|
||||
bbto.block_size = 128;
|
||||
bbto.metadata_block_size = 128;
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
bbto.whole_key_filtering = wkf;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
Options options = CurrentOptions();
|
||||
constexpr size_t kNumKeys = 10000;
|
||||
static_assert(kNumKeys <= 10000, "kNumKeys have to be <= 10000");
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeys + 10));
|
||||
options.create_if_missing = true;
|
||||
constexpr size_t kPrefixLength = 4;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(kPrefixLength));
|
||||
options.compression = kNoCompression;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(50));
|
||||
bbto.index_shortening =
|
||||
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
|
||||
bbto.block_size = 128;
|
||||
bbto.metadata_block_size = 128;
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const std::string value(64, '\0');
|
||||
const std::string value(64, '\0');
|
||||
|
||||
WriteOptions write_opts;
|
||||
write_opts.disableWAL = true;
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(4) << std::fixed << i;
|
||||
ASSERT_OK(db_->Put(write_opts, oss.str(), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_opts;
|
||||
// Use legacy, implicit prefix seek
|
||||
read_opts.total_order_seek = false;
|
||||
read_opts.auto_prefix_mode = false;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
// Seek with a key after each one added but with same prefix. One will
|
||||
// surely cross a partition boundary.
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(4) << std::fixed << i << "a";
|
||||
it->SeekForPrev(oss.str());
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_TRUE(it->Valid());
|
||||
}
|
||||
WriteOptions write_opts;
|
||||
write_opts.disableWAL = true;
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(4) << std::fixed << i;
|
||||
ASSERT_OK(db_->Put(write_opts, oss.str(), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_opts;
|
||||
// Use legacy, implicit prefix seek
|
||||
read_opts.total_order_seek = false;
|
||||
read_opts.auto_prefix_mode = false;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
// Seek with a key after each one added but with same prefix. One will
|
||||
// surely cross a partition boundary.
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::setw(4) << std::fixed << i << "a";
|
||||
it->SeekForPrev(oss.str());
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_TRUE(it->Valid());
|
||||
}
|
||||
it.reset();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
+24
-93
@@ -21,7 +21,6 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/metadata.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/file_checksum_helper.h"
|
||||
@@ -92,12 +91,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
|
||||
return GetSortedWalFilesImpl(files,
|
||||
/*need_seqnos*/ true);
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// Record tracked WALs as a (minimum) cross-check for directory scan
|
||||
std::vector<uint64_t> required_by_manifest;
|
||||
|
||||
@@ -123,10 +117,7 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: need to include archived WALs because needed WALs might have been
|
||||
// archived since getting required_by_manifest set
|
||||
Status s = wal_manager_.GetSortedWalFiles(files, need_seqnos,
|
||||
/*include_archived*/ true);
|
||||
Status s = wal_manager_.GetSortedWalFiles(files);
|
||||
|
||||
// DisableFileDeletions / EnableFileDeletions not supported in read-only DB
|
||||
if (deletions_disabled.ok()) {
|
||||
@@ -161,11 +152,11 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
size_t wal_count = files.size();
|
||||
size_t wal_size = files.size();
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Number of WAL files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
|
||||
"Number of log files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
|
||||
" required by manifest)",
|
||||
wal_count, required_by_manifest.size());
|
||||
wal_size, required_by_manifest.size());
|
||||
#ifndef NDEBUG
|
||||
std::ostringstream wal_names;
|
||||
for (const auto& wal : files) {
|
||||
@@ -186,7 +177,7 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) {
|
||||
Status DBImpl::GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) {
|
||||
uint64_t current_logfile_number;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -207,20 +198,14 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
// NOTE: This implementation was largely migrated from Checkpoint.
|
||||
|
||||
Status s;
|
||||
VectorWalPtr live_wal_files;
|
||||
VectorLogPtr live_wal_files;
|
||||
bool flush_memtable = true;
|
||||
if (!immutable_db_options_.allow_2pc) {
|
||||
if (opts.wal_size_for_flush == std::numeric_limits<uint64_t>::max()) {
|
||||
flush_memtable = false;
|
||||
} else if (opts.wal_size_for_flush > 0) {
|
||||
// FIXME: avoid querying the filesystem for current WAL state
|
||||
// If the outstanding WAL files are small, we skip the flush.
|
||||
// Don't take archived log size into account when calculating wal
|
||||
// size for flush, and don't need to verify consistency with manifest
|
||||
// here & now.
|
||||
s = wal_manager_.GetSortedWalFiles(live_wal_files,
|
||||
/* need_seqnos */ false,
|
||||
/*include_archived*/ false);
|
||||
// If the outstanding log files are small, we skip the flush.
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -231,7 +216,6 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
// We may be able to cover 2PC case too.
|
||||
uint64_t total_wal_size = 0;
|
||||
for (auto& wal : live_wal_files) {
|
||||
assert(wal->Type() == kAliveLogFile);
|
||||
total_wal_size += wal->SizeFileBytes();
|
||||
}
|
||||
if (total_wal_size < opts.wal_size_for_flush) {
|
||||
@@ -332,8 +316,6 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
const uint64_t options_number = versions_->options_file_number();
|
||||
const uint64_t options_size = versions_->options_file_size_;
|
||||
const uint64_t min_log_num = MinLogNumberToKeep();
|
||||
// Ensure consistency with manifest for track_and_verify_wals_in_manifest
|
||||
const uint64_t max_log_num = logfile_number_;
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -397,13 +379,10 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCheckpoint:SavedLiveFiles2");
|
||||
|
||||
if (s.ok()) {
|
||||
// FlushWAL is required to ensure we can physically copy everything
|
||||
// logically written to the WAL. (Sync not strictly required for
|
||||
// active WAL to be copied rather than hard linked, even when
|
||||
// Checkpoint guarantees that the copied-to file is sync-ed. Plus we can't
|
||||
// help track_and_verify_wals_in_manifest after manifest_size is
|
||||
// already determined.)
|
||||
s = FlushWAL(/*sync=*/false);
|
||||
// To maximize the effectiveness of track_and_verify_wals_in_manifest,
|
||||
// sync WAL when it is enabled.
|
||||
s = FlushWAL(
|
||||
immutable_db_options_.track_and_verify_wals_in_manifest /* sync */);
|
||||
if (s.IsNotSupported()) { // read-only DB or similar
|
||||
s = Status::OK();
|
||||
}
|
||||
@@ -412,52 +391,21 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive1");
|
||||
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive2");
|
||||
|
||||
// Even after WAL flush, there could be multiple WALs that are not
|
||||
// fully synced. Although the output DB of a Checkpoint or Backup needs
|
||||
// to be fully synced on return, we don't strictly need to sync this
|
||||
// DB (the input DB). If we allow Checkpoint to hard link an inactive
|
||||
// WAL that isn't fully synced, that could result in an insufficiently
|
||||
// sync-ed Checkpoint. Here we get the set of WALs that are potentially
|
||||
// unsynced or still being written to, to prevent them from being hard
|
||||
// linked. Enforcing max_log_num from above ensures any new WALs after
|
||||
// GetOpenWalSizes() and before GetSortedWalFiles() are not included in
|
||||
// the results.
|
||||
// NOTE: we might still hard link a file that is open for writing, even
|
||||
// if we don't do any more writes to it.
|
||||
//
|
||||
// In a step toward reducing unnecessary file metadata queries, we also
|
||||
// get and use our known flushed sizes for those WALs.
|
||||
// FIXME: eventually we should not be using filesystem queries at all for
|
||||
// the required set of WAL files.
|
||||
//
|
||||
// However for recycled log files, we just copy the whole file,
|
||||
// for better or worse.
|
||||
//
|
||||
std::map<uint64_t, uint64_t> open_wal_number_to_size;
|
||||
bool recycling_log_files = immutable_db_options_.recycle_log_file_num > 0;
|
||||
if (s.ok() && !recycling_log_files) {
|
||||
s = GetOpenWalSizes(open_wal_number_to_size);
|
||||
}
|
||||
|
||||
// [old comment] If we have more than one column family, we also need to get
|
||||
// WAL files.
|
||||
// If we have more than one column family, we also need to get WAL files.
|
||||
if (s.ok()) {
|
||||
// FIXME: avoid querying the filesystem for current WAL state
|
||||
s = GetSortedWalFilesImpl(live_wal_files,
|
||||
/* need_seqnos */ false);
|
||||
s = GetSortedWalFiles(live_wal_files);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
size_t wal_count = live_wal_files.size();
|
||||
size_t wal_size = live_wal_files.size();
|
||||
// Link WAL files. Copy exact size of last one because it is the only one
|
||||
// that has changes after the last flush.
|
||||
auto wal_dir = immutable_db_options_.GetWalDir();
|
||||
for (size_t i = 0; s.ok() && i < wal_count; ++i) {
|
||||
for (size_t i = 0; s.ok() && i < wal_size; ++i) {
|
||||
if ((live_wal_files[i]->Type() == kAliveLogFile) &&
|
||||
(!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num) &&
|
||||
live_wal_files[i]->LogNumber() <= max_log_num) {
|
||||
(!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num)) {
|
||||
results.emplace_back();
|
||||
LiveFileStorageInfo& info = results.back();
|
||||
auto f = live_wal_files[i]->PathName();
|
||||
@@ -466,29 +414,12 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
info.directory = wal_dir;
|
||||
info.file_number = live_wal_files[i]->LogNumber();
|
||||
info.file_type = kWalFile;
|
||||
if (recycling_log_files) {
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
// Recyclable WAL files must be copied instead of hard linked
|
||||
info.trim_to_size = true;
|
||||
} else {
|
||||
auto it = open_wal_number_to_size.find(info.file_number);
|
||||
if (it == open_wal_number_to_size.end()) {
|
||||
// Known fully synced and no future writes (in part from
|
||||
// max_log_num check). Ok to hard link
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
assert(!info.trim_to_size);
|
||||
} else {
|
||||
// Marked as (possibly) still open -> use our known flushed size
|
||||
// and force file copy instead of hard link
|
||||
info.size = it->second;
|
||||
info.trim_to_size = true;
|
||||
// FIXME: this is needed as long as db_stress uses
|
||||
// SetReadUnsyncedData(false), because it will only be able to
|
||||
// copy the synced portion of the WAL, which under
|
||||
// SetReadUnsyncedData(false) is given by the reported file size.
|
||||
info.size = std::min(info.size, live_wal_files[i]->SizeFileBytes());
|
||||
}
|
||||
}
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
// Trim the log either if its the last one, or log file recycling is
|
||||
// enabled. In the latter case, a hard link doesn't prevent the file
|
||||
// from being renamed and recycled. So we need to copy it instead.
|
||||
info.trim_to_size = (i + 1 == wal_size) ||
|
||||
(immutable_db_options_.recycle_log_file_num > 0);
|
||||
if (opts.include_checksum_info) {
|
||||
info.file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
info.file_checksum = kUnknownFileChecksum;
|
||||
|
||||
+5
-5
@@ -86,8 +86,8 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedWals:Start"},
|
||||
{"DBImpl::SyncClosedWals:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{"DBImpl::SyncClosedLogs:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -111,8 +111,8 @@ TEST_F(DBFlushTest, SyncSkip) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncSkip:1", "DBImpl::SyncClosedWals:Skip"},
|
||||
{"DBImpl::SyncClosedWals:Skip", "DBFlushTest::SyncSkip:2"}});
|
||||
{{"DBFlushTest::SyncSkip:1", "DBImpl::SyncClosedLogs:Skip"},
|
||||
{"DBImpl::SyncClosedLogs:Skip", "DBFlushTest::SyncSkip:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
@@ -2381,7 +2381,7 @@ TEST_F(DBFlushTest, PickRightMemtables) {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::SyncClosedWals:BeforeReLock", [&](void* /*arg*/) {
|
||||
"DBImpl::SyncClosedLogs:BeforeReLock", [&](void* /*arg*/) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "what", "v"));
|
||||
auto* cfhi =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(handles_[1]);
|
||||
|
||||
+68
-157
@@ -392,8 +392,8 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
if (!s.ok()) {
|
||||
io_s = versions_->io_status();
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
s = error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -638,7 +638,7 @@ Status DBImpl::CloseHelper() {
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Unable to clear writer for WAL %s with error -- %s",
|
||||
"Unable to Sync WAL file %s with error -- %s",
|
||||
LogFileName(immutable_db_options_.GetWalDir(), log_number).c_str(),
|
||||
s.ToString().c_str());
|
||||
// Retain the first error
|
||||
@@ -917,8 +917,8 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
|
||||
read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (!s.ok() && versions_->io_status().IsIOError()) {
|
||||
error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
s = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1550,132 +1550,64 @@ bool DBImpl::WALBufferIsEmpty() {
|
||||
return res;
|
||||
}
|
||||
|
||||
Status DBImpl::GetOpenWalSizes(std::map<uint64_t, uint64_t>& number_to_size) {
|
||||
assert(number_to_size.empty());
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
for (auto& log : logs_) {
|
||||
auto* open_file = log.writer->file();
|
||||
if (open_file) {
|
||||
number_to_size[log.number] = open_file->GetFlushedSize();
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::SyncWAL() {
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:Begin");
|
||||
WriteOptions write_options;
|
||||
VersionEdit synced_wals;
|
||||
Status s = SyncWalImpl(/*include_current_wal=*/true, write_options,
|
||||
/*job_context=*/nullptr, &synced_wals,
|
||||
/*error_recovery_in_prog=*/false);
|
||||
|
||||
if (s.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
s = ApplyWALToManifest(read_options, write_options, &synced_wals);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
|
||||
const WriteOptions& write_options,
|
||||
JobContext* job_context, VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog) {
|
||||
autovector<log::Writer*, 1> wals_to_sync;
|
||||
bool need_wal_dir_sync;
|
||||
// Number of a WAL that was active at the start of call and maybe is by
|
||||
// the end of the call.
|
||||
uint64_t maybe_active_number;
|
||||
// Sync WALs up to this number
|
||||
uint64_t up_to_number;
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
bool need_log_dir_sync;
|
||||
uint64_t current_log_number;
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
assert(!logs_.empty());
|
||||
|
||||
maybe_active_number = logfile_number_;
|
||||
up_to_number =
|
||||
include_current_wal ? maybe_active_number : maybe_active_number - 1;
|
||||
// This SyncWAL() call only cares about logs up to this number.
|
||||
current_log_number = logfile_number_;
|
||||
|
||||
while (logs_.front().number <= up_to_number && logs_.front().IsSyncing()) {
|
||||
while (logs_.front().number <= current_log_number &&
|
||||
logs_.front().IsSyncing()) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
// First check that logs are safe to sync in background.
|
||||
if (include_current_wal &&
|
||||
!logs_.back().writer->file()->writable_file()->IsSyncThreadSafe()) {
|
||||
return IOStatus::NotSupported(
|
||||
"SyncWAL() is not supported for this implementation of WAL file",
|
||||
immutable_db_options_.allow_mmap_writes
|
||||
? "try setting Options::allow_mmap_writes to false"
|
||||
: Slice());
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number <= current_log_number; ++it) {
|
||||
if (!it->writer->file()->writable_file()->IsSyncThreadSafe()) {
|
||||
return Status::NotSupported(
|
||||
"SyncWAL() is not supported for this implementation of WAL file",
|
||||
immutable_db_options_.allow_mmap_writes
|
||||
? "try setting Options::allow_mmap_writes to false"
|
||||
: Slice());
|
||||
}
|
||||
}
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number <= up_to_number; ++it) {
|
||||
it != logs_.end() && it->number <= current_log_number; ++it) {
|
||||
auto& log = *it;
|
||||
// Ensure the head of logs_ is marked as getting_synced if any is.
|
||||
log.PrepareForSync();
|
||||
// If last sync failed on a later WAL, this could be a fully synced
|
||||
// and closed WAL that just needs to be recorded as synced in the
|
||||
// manifest.
|
||||
if (log.writer->file()) {
|
||||
wals_to_sync.push_back(log.writer);
|
||||
}
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
need_wal_dir_sync = !log_dir_synced_;
|
||||
need_log_dir_sync = !log_dir_synced_;
|
||||
}
|
||||
|
||||
if (include_current_wal) {
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
}
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
const WriteOptions write_options;
|
||||
IOOptions opts;
|
||||
IOStatus io_s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
std::list<log::Writer*> wals_internally_closed;
|
||||
io_s = WritableFileWriter::PrepareIOOptions(write_options, opts);
|
||||
if (!io_s.ok()) {
|
||||
status = io_s;
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
for (log::Writer* log : wals_to_sync) {
|
||||
if (job_context) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
log->get_log_number());
|
||||
}
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
if (log->get_log_number() >= maybe_active_number) {
|
||||
assert(log->get_log_number() == maybe_active_number);
|
||||
io_s = log->file()->SyncWithoutFlush(opts,
|
||||
immutable_db_options_.use_fsync);
|
||||
} else {
|
||||
io_s = log->file()->Sync(opts, immutable_db_options_.use_fsync);
|
||||
}
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
io_s =
|
||||
log->file()->SyncWithoutFlush(opts, immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
status = io_s;
|
||||
break;
|
||||
}
|
||||
// WALs can be closed when purging obsolete files, but if recycling is
|
||||
// enabled, the log file is closed here so that it can be reused. And
|
||||
// immediate closure here upon final sync makes it easier to guarantee
|
||||
// that Checkpoint doesn't LinkFile on a WAL still open for write, which
|
||||
// might be unsupported for some FileSystem implementations. Close here
|
||||
// should be inexpensive because flush and sync are done, so the kill
|
||||
// switch background_close_inactive_wals is expected to be removed in
|
||||
// the future.
|
||||
if (log->get_log_number() < maybe_active_number &&
|
||||
(immutable_db_options_.recycle_log_file_num > 0 ||
|
||||
!immutable_db_options_.background_close_inactive_wals)) {
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
io_s = log->file()->Close(opts);
|
||||
wals_internally_closed.push_back(log);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
@@ -1685,34 +1617,31 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
|
||||
// future writes
|
||||
IOStatusCheck(io_s);
|
||||
}
|
||||
if (io_s.ok() && need_wal_dir_sync) {
|
||||
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
status = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
IOOptions(), nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
if (include_current_wal) {
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:1");
|
||||
} else {
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::SyncClosedWals:BeforeReLock",
|
||||
/*arg=*/nullptr);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:1");
|
||||
VersionEdit synced_wals;
|
||||
{
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
for (auto* wal : wals_internally_closed) {
|
||||
// We can only modify the state of log::Writer under the mutex
|
||||
bool was_closed = wal->PublishIfClosed();
|
||||
assert(was_closed);
|
||||
(void)was_closed;
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
MarkLogsSynced(up_to_number, need_wal_dir_sync, synced_wals);
|
||||
if (status.ok()) {
|
||||
MarkLogsSynced(current_log_number, need_log_dir_sync, &synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(up_to_number);
|
||||
MarkLogsNotSynced(current_log_number);
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::ApplyWALToManifest(const ReadOptions& read_options,
|
||||
@@ -1725,8 +1654,8 @@ Status DBImpl::ApplyWALToManifest(const ReadOptions& read_options,
|
||||
read_options, write_options, synced_wals, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (!status.ok() && versions_->io_status().IsIOError()) {
|
||||
error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
status = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -1830,19 +1759,16 @@ void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
|
||||
wal.GetPreSyncSize() > 0) {
|
||||
synced_wals->AddWal(wal.number, WalMetadata(wal.GetPreSyncSize()));
|
||||
}
|
||||
// Reclaim closed WALs (wal.writer->file() == nullptr), and if we don't
|
||||
// need to close before that (background_close_inactive_wals) we can
|
||||
// opportunistically reclaim WALs that happen to be fully synced.
|
||||
// (Probably not worth extra code and mutex release to opportunistically
|
||||
// close WALs that became eligible since last holding the mutex.
|
||||
// FindObsoleteFiles can take care of it.)
|
||||
// Check if the file has been closed, i.e wal.writer->file() == nullptr
|
||||
// which can happen if log recycling is enabled, or if all the data in
|
||||
// the log has been synced
|
||||
if (wal.writer->file() == nullptr ||
|
||||
(immutable_db_options_.background_close_inactive_wals &&
|
||||
wal.GetPreSyncSize() == wal.writer->file()->GetFlushedSize())) {
|
||||
wal.GetPreSyncSize() == wal.writer->file()->GetFlushedSize()) {
|
||||
// Fully synced
|
||||
logs_to_free_.push_back(wal.ReleaseWriter());
|
||||
it = logs_.erase(it);
|
||||
} else {
|
||||
assert(wal.GetPreSyncSize() < wal.writer->file()->GetFlushedSize());
|
||||
wal.FinishSync();
|
||||
++it;
|
||||
}
|
||||
@@ -2065,19 +1991,19 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
read_options, super_version->GetSeqnoToTimeMapping(), arena);
|
||||
Status s;
|
||||
if (!read_options.ignore_range_deletions) {
|
||||
std::unique_ptr<TruncatedRangeDelIterator> mem_tombstone_iter;
|
||||
TruncatedRangeDelIterator* mem_tombstone_iter = nullptr;
|
||||
auto range_del_iter = super_version->mem->NewRangeTombstoneIterator(
|
||||
read_options, sequence, false /* immutable_memtable */);
|
||||
if (range_del_iter == nullptr || range_del_iter->empty()) {
|
||||
delete range_del_iter;
|
||||
} else {
|
||||
mem_tombstone_iter = std::make_unique<TruncatedRangeDelIterator>(
|
||||
mem_tombstone_iter = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(range_del_iter),
|
||||
&cfd->ioptions()->internal_comparator, nullptr /* smallest */,
|
||||
nullptr /* largest */);
|
||||
}
|
||||
merge_iter_builder.AddPointAndTombstoneIterator(
|
||||
mem_iter, std::move(mem_tombstone_iter));
|
||||
merge_iter_builder.AddPointAndTombstoneIterator(mem_iter,
|
||||
mem_tombstone_iter);
|
||||
} else {
|
||||
merge_iter_builder.AddIterator(mem_iter);
|
||||
}
|
||||
@@ -4544,11 +4470,8 @@ bool DBImpl::GetAggregatedIntProperty(const Slice& property,
|
||||
if (property_info == nullptr || property_info->handle_int == nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto aggregator = CreateIntPropertyAggregator(property);
|
||||
if (aggregator == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t sum = 0;
|
||||
bool ret = true;
|
||||
{
|
||||
// Needs mutex to protect the list of column families.
|
||||
@@ -4562,14 +4485,14 @@ bool DBImpl::GetAggregatedIntProperty(const Slice& property,
|
||||
// GetIntPropertyInternal may release db mutex and re-acquire it.
|
||||
mutex_.AssertHeld();
|
||||
if (ret) {
|
||||
aggregator->Add(cfd, value);
|
||||
sum += value;
|
||||
} else {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*aggregated_value = aggregator->Aggregate();
|
||||
*aggregated_value = sum;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -5828,18 +5751,6 @@ Status DBImpl::IngestExternalFiles(
|
||||
"timestamps enabled doesn't support ingest behind.");
|
||||
}
|
||||
}
|
||||
if (ingest_opts.allow_db_generated_files) {
|
||||
if (ingest_opts.write_global_seqno) {
|
||||
return Status::NotSupported(
|
||||
"write_global_seqno is deprecated and does not work with "
|
||||
"allow_db_generated_files.");
|
||||
}
|
||||
if (ingest_opts.move_files) {
|
||||
return Status::NotSupported(
|
||||
"Options move_files and allow_db_generated_files are not "
|
||||
"compatible.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (yanqin) maybe handle the case in which column_families have
|
||||
|
||||
+15
-56
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
@@ -58,7 +57,6 @@
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/trace_reader_writer.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/user_write_callback.h"
|
||||
#include "rocksdb/utilities/replayer.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/merging_iterator.h"
|
||||
@@ -233,10 +231,6 @@ class DBImpl : public DB {
|
||||
using DB::Write;
|
||||
Status Write(const WriteOptions& options, WriteBatch* updates) override;
|
||||
|
||||
using DB::WriteWithCallback;
|
||||
Status WriteWithCallback(const WriteOptions& options, WriteBatch* updates,
|
||||
UserWriteCallback* user_write_cb) override;
|
||||
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
@@ -507,16 +501,8 @@ class DBImpl : public DB {
|
||||
// All the returned filenames start with "/"
|
||||
Status GetLiveFiles(std::vector<std::string>&, uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true) override;
|
||||
Status GetSortedWalFiles(VectorWalPtr& files) override;
|
||||
Status GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos);
|
||||
|
||||
// Get the known flushed sizes of WALs that might still be written to
|
||||
// or have pending sync.
|
||||
// NOTE: unlike alive_log_files_, this function includes WALs that might
|
||||
// be obsolete (but not obsolete to a pending Checkpoint) and not yet fully
|
||||
// synced.
|
||||
Status GetOpenWalSizes(std::map<uint64_t, uint64_t>& number_to_size);
|
||||
Status GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) override;
|
||||
Status GetSortedWalFiles(VectorLogPtr& files) override;
|
||||
Status GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) override;
|
||||
Status GetCreationTimeOfOldestFile(uint64_t* creation_time) override;
|
||||
|
||||
Status GetUpdatesSince(
|
||||
@@ -702,8 +688,7 @@ class DBImpl : public DB {
|
||||
// thread to determine whether it is safe to perform the write.
|
||||
virtual Status WriteWithCallback(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch,
|
||||
WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb = nullptr);
|
||||
WriteCallback* callback);
|
||||
|
||||
// Returns the sequence number that is guaranteed to be smaller than or equal
|
||||
// to the sequence number of any key that could be inserted into the current
|
||||
@@ -1464,9 +1449,6 @@ class DBImpl : public DB {
|
||||
Status RenameTempFileToOptionsFile(const std::string& file_name);
|
||||
Status DeleteObsoleteOptionsFiles();
|
||||
|
||||
void NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
|
||||
FlushReason flush_reason);
|
||||
|
||||
void NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int job_id, FlushReason flush_reason);
|
||||
@@ -1517,7 +1499,6 @@ class DBImpl : public DB {
|
||||
// batch that does not have duplicate keys.
|
||||
Status WriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
UserWriteCallback* user_write_cb = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false, uint64_t* seq_used = nullptr,
|
||||
size_t batch_cnt = 0,
|
||||
@@ -1526,7 +1507,6 @@ class DBImpl : public DB {
|
||||
|
||||
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
UserWriteCallback* user_write_cb = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false,
|
||||
uint64_t* seq_used = nullptr);
|
||||
@@ -1553,8 +1533,7 @@ class DBImpl : public DB {
|
||||
// marks start of a new sub-batch.
|
||||
Status WriteImplWALOnly(
|
||||
WriteThread* write_thread, const WriteOptions& options,
|
||||
WriteBatch* updates, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
WriteBatch* updates, WriteCallback* callback, uint64_t* log_used,
|
||||
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
|
||||
const PublishLastSeq publish_last_seq, const bool disable_memtable);
|
||||
@@ -1726,11 +1705,8 @@ class DBImpl : public DB {
|
||||
return w;
|
||||
}
|
||||
Status ClearWriter() {
|
||||
Status s;
|
||||
if (writer->file()) {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
s = writer->WriteBuffer(WriteOptions());
|
||||
}
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
Status s = writer->WriteBuffer(WriteOptions());
|
||||
delete writer;
|
||||
writer = nullptr;
|
||||
return s;
|
||||
@@ -1745,16 +1721,10 @@ class DBImpl : public DB {
|
||||
|
||||
void PrepareForSync() {
|
||||
assert(!getting_synced);
|
||||
// Ensure the head of logs_ is marked as getting_synced if any is.
|
||||
// Size is expected to be monotonically increasing.
|
||||
assert(writer->file()->GetFlushedSize() >= pre_sync_size);
|
||||
getting_synced = true;
|
||||
// If last sync failed on a later WAL, this could be a fully synced
|
||||
// and closed WAL that just needs to be recorded as synced in the
|
||||
// manifest.
|
||||
if (writer->file()) {
|
||||
// Size is expected to be monotonically increasing.
|
||||
assert(writer->file()->GetFlushedSize() >= pre_sync_size);
|
||||
pre_sync_size = writer->file()->GetFlushedSize();
|
||||
}
|
||||
pre_sync_size = writer->file()->GetFlushedSize();
|
||||
}
|
||||
|
||||
void FinishSync() {
|
||||
@@ -1952,7 +1922,7 @@ class DBImpl : public DB {
|
||||
void ReleaseFileNumberFromPendingOutputs(
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& v);
|
||||
|
||||
IOStatus SyncClosedWals(const WriteOptions& write_options,
|
||||
IOStatus SyncClosedLogs(const WriteOptions& write_options,
|
||||
JobContext* job_context, VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog);
|
||||
|
||||
@@ -2211,8 +2181,7 @@ class DBImpl : public DB {
|
||||
void GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
FlushReason flush_reason, FlushRequest* req);
|
||||
|
||||
// Returns true if `req` is successfully enqueued.
|
||||
bool SchedulePendingFlush(const FlushRequest& req);
|
||||
void SchedulePendingFlush(const FlushRequest& req);
|
||||
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||
@@ -2288,11 +2257,6 @@ class DBImpl : public DB {
|
||||
ColumnFamilyData* PickCompactionFromQueue(
|
||||
std::unique_ptr<TaskLimiterToken>* token, LogBuffer* log_buffer);
|
||||
|
||||
IOStatus SyncWalImpl(bool include_current_wal,
|
||||
const WriteOptions& write_options,
|
||||
JobContext* job_context, VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog);
|
||||
|
||||
// helper function to call after some of the logs_ were synced
|
||||
void MarkLogsSynced(uint64_t up_to, bool synced_dir, VersionEdit* edit);
|
||||
Status ApplyWALToManifest(const ReadOptions& read_options,
|
||||
@@ -2350,9 +2314,6 @@ class DBImpl : public DB {
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
|
||||
void UpdateDeletionCompactionStats(const std::unique_ptr<Compaction>& c);
|
||||
|
||||
// May open and read table files for table property.
|
||||
// Should not be called while holding mutex_.
|
||||
void BuildCompactionJobInfo(const ColumnFamilyData* cfd, Compaction* c,
|
||||
const Status& st,
|
||||
const CompactionJobStats& compaction_job_stats,
|
||||
@@ -2568,10 +2529,8 @@ class DBImpl : public DB {
|
||||
|
||||
bool persistent_stats_cfd_exists_ = true;
|
||||
|
||||
// The current WAL file and those that have not been found obsolete from
|
||||
// memtable flushes. A WAL not on this list might still be pending writer
|
||||
// flush and/or sync and close and might still be in logs_. alive_log_files_
|
||||
// is protected by mutex_ and log_write_mutex_ with details as follows:
|
||||
// alive_log_files_ is protected by mutex_ and log_write_mutex_ with details
|
||||
// as follows:
|
||||
// 1. read by FindObsoleteFiles() which can be called in either application
|
||||
// thread or RocksDB bg threads, both mutex_ and log_write_mutex_ are
|
||||
// held.
|
||||
@@ -2622,7 +2581,7 @@ class DBImpl : public DB {
|
||||
// 8. read by MarkLogsNotSynced() and MarkLogsSynced() are protected by
|
||||
// log_write_mutex_.
|
||||
// 9. erase() by MarkLogsSynced() protected by log_write_mutex_.
|
||||
// 10. read by SyncClosedWals() protected by only log_write_mutex_. This can
|
||||
// 10. read by SyncClosedLogs() protected by only log_write_mutex_. This can
|
||||
// happen in bg flush threads after DB::Open() returns success to
|
||||
// applications.
|
||||
// 11. reads, e.g. front(), iteration, and back() called by PreprocessWrite()
|
||||
@@ -2635,7 +2594,7 @@ class DBImpl : public DB {
|
||||
// 13. emplace_back() by SwitchMemtable() hold both mutex_ and
|
||||
// log_write_mutex_. This happens in the write group leader. Can conflict
|
||||
// with bg threads calling FindObsoleteFiles(), MarkLogsSynced(),
|
||||
// SyncClosedWals(), etc. as well as application threads calling
|
||||
// SyncClosedLogs(), etc. as well as application threads calling
|
||||
// FlushWAL(), SyncWAL(), LockWAL(). This is fine because all parties
|
||||
// require at least log_write_mutex_.
|
||||
// 14. iteration called in WriteToWAL(write_group) protected by
|
||||
|
||||
@@ -87,9 +87,6 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
|
||||
mutex_.AssertHeld();
|
||||
assert(flush_req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
ColumnFamilyData* cfd = flush_req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||
if (cfd->GetAndClearFlushSkipReschedule()) {
|
||||
return false;
|
||||
}
|
||||
uint64_t max_memtable_id =
|
||||
flush_req.cfd_to_max_mem_id_to_persist.begin()->second;
|
||||
if (cfd->IsDropped() ||
|
||||
@@ -101,20 +98,15 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
|
||||
// alleviated if we continue with the flush instead of postponing it.
|
||||
const auto& mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
|
||||
// Use the same criteria as WaitUntilFlushWouldNotStallWrites does w.r.t
|
||||
// defining what a write stall is about to happen means. If this uses a
|
||||
// stricter criteria, for example, a write stall is about to happen if the
|
||||
// last memtable is 10% full, there is a possibility that manual flush could
|
||||
// be waiting in `WaitUntilFlushWouldNotStallWrites` with the incorrect
|
||||
// expectation that others will clear up the excessive memtables and
|
||||
// eventually let it proceed. The others in this case won't start clearing
|
||||
// until the last memtable is 10% full. To avoid that scenario, the criteria
|
||||
// this uses should be the same or less strict than
|
||||
// `WaitUntilFlushWouldNotStallWrites` does.
|
||||
// Taking the status of the active Memtable into consideration so that we are
|
||||
// not just checking if DB is currently already in write stall mode.
|
||||
int mem_to_flush = cfd->mem()->ApproximateMemoryUsageFast() >=
|
||||
cfd->mem()->write_buffer_size() / 2
|
||||
? 1
|
||||
: 0;
|
||||
WriteStallCondition write_stall =
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
|
||||
/*num_l0_files=*/0,
|
||||
cfd->imm()->NumNotFlushed() + mem_to_flush, /*num_l0_files=*/0,
|
||||
/*num_compaction_needed_bytes=*/0, mutable_cf_options,
|
||||
*cfd->ioptions())
|
||||
.first;
|
||||
@@ -124,19 +116,89 @@ bool DBImpl::ShouldRescheduleFlushRequestToRetainUDT(
|
||||
return true;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::SyncClosedWals(const WriteOptions& write_options,
|
||||
IOStatus DBImpl::SyncClosedLogs(const WriteOptions& write_options,
|
||||
JobContext* job_context,
|
||||
VersionEdit* synced_wals,
|
||||
bool error_recovery_in_prog) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedWals:Start");
|
||||
|
||||
IOStatus io_s = SyncWalImpl(/*include_current_wal*/ false, write_options,
|
||||
job_context, synced_wals, error_recovery_in_prog);
|
||||
if (!io_s.ok()) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedWals:Failed");
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedWals:end");
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Start");
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
autovector<log::Writer*, 1> logs_to_sync;
|
||||
uint64_t current_log_number = logfile_number_;
|
||||
while (logs_.front().number < current_log_number &&
|
||||
logs_.front().IsSyncing()) {
|
||||
log_sync_cv_.Wait();
|
||||
}
|
||||
for (auto it = logs_.begin();
|
||||
it != logs_.end() && it->number < current_log_number; ++it) {
|
||||
auto& log = *it;
|
||||
log.PrepareForSync();
|
||||
logs_to_sync.push_back(log.writer);
|
||||
}
|
||||
|
||||
IOStatus io_s;
|
||||
if (!logs_to_sync.empty()) {
|
||||
log_write_mutex_.Unlock();
|
||||
|
||||
assert(job_context);
|
||||
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
log->get_log_number());
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
|
||||
IOOptions io_options;
|
||||
io_s = WritableFileWriter::PrepareIOOptions(write_options, io_options);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
io_s = log->file()->Sync(io_options, immutable_db_options_.use_fsync);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (immutable_db_options_.recycle_log_file_num > 0) {
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
// Normally the log file is closed when purging obsolete file, but if
|
||||
// log recycling is enabled, the log file is closed here so that it
|
||||
// can be reused.
|
||||
io_s = log->Close(write_options);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
IOOptions io_options;
|
||||
io_s = WritableFileWriter::PrepareIOOptions(write_options, io_options);
|
||||
if (io_s.ok()) {
|
||||
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
io_options, nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::SyncClosedLogs:BeforeReLock",
|
||||
/*arg=*/nullptr);
|
||||
log_write_mutex_.Lock();
|
||||
|
||||
// "number <= current_log_number - 1" is equivalent to
|
||||
// "number < current_log_number".
|
||||
if (io_s.ok()) {
|
||||
MarkLogsSynced(current_log_number - 1, true, synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(current_log_number - 1);
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return io_s;
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:end");
|
||||
return io_s;
|
||||
}
|
||||
|
||||
@@ -175,12 +237,12 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// If needs_to_sync_closed_wals is true, we need to record the current
|
||||
// maximum memtable ID of this column family so that a later PickMemtables()
|
||||
// call will not pick memtables whose IDs are higher. This is due to the fact
|
||||
// that SyncClosedWals() may release the db mutex, and memtable switch can
|
||||
// that SyncClosedLogs() may release the db mutex, and memtable switch can
|
||||
// happen for this column family in the meantime. The newly created memtables
|
||||
// have their data backed by unsynced WALs, thus they cannot be included in
|
||||
// this flush job.
|
||||
// Another reason why we must record the current maximum memtable ID of this
|
||||
// column family: SyncClosedWals() may release db mutex, thus it's possible
|
||||
// column family: SyncClosedLogs() may release db mutex, thus it's possible
|
||||
// for application to continue to insert into memtables increasing db's
|
||||
// sequence number. The application may take a snapshot, but this snapshot is
|
||||
// not included in `snapshot_seqs` which will be passed to flush job because
|
||||
@@ -194,7 +256,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
|
||||
// If needs_to_sync_closed_wals is false, then the flush job will pick ALL
|
||||
// existing memtables of the column family when PickMemTable() is called
|
||||
// later. Although we won't call SyncClosedWals() in this case, we may still
|
||||
// later. Although we won't call SyncClosedLogs() in this case, we may still
|
||||
// call the callbacks of the listeners, i.e. NotifyOnFlushBegin() which also
|
||||
// releases and re-acquires the db mutex. In the meantime, the application
|
||||
// can still insert into the memtables and increase the db's sequence number.
|
||||
@@ -224,12 +286,12 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
bool need_cancel = false;
|
||||
IOStatus log_io_s = IOStatus::OK();
|
||||
if (needs_to_sync_closed_wals) {
|
||||
// SyncClosedWals() may unlock and re-lock the log_write_mutex multiple
|
||||
// SyncClosedLogs() may unlock and re-lock the log_write_mutex multiple
|
||||
// times.
|
||||
VersionEdit synced_wals;
|
||||
bool error_recovery_in_prog = error_handler_.IsRecoveryInProgress();
|
||||
mutex_.Unlock();
|
||||
log_io_s = SyncClosedWals(write_options, job_context, &synced_wals,
|
||||
log_io_s = SyncClosedLogs(write_options, job_context, &synced_wals,
|
||||
error_recovery_in_prog);
|
||||
mutex_.Lock();
|
||||
if (log_io_s.ok() && synced_wals.IsWalAddition()) {
|
||||
@@ -244,7 +306,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
error_handler_.SetBGError(log_io_s, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedWals:Skip");
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Skip");
|
||||
}
|
||||
s = log_io_s;
|
||||
|
||||
@@ -518,7 +580,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
VersionEdit synced_wals;
|
||||
bool error_recovery_in_prog = error_handler_.IsRecoveryInProgress();
|
||||
mutex_.Unlock();
|
||||
log_io_s = SyncClosedWals(write_options, job_context, &synced_wals,
|
||||
log_io_s = SyncClosedLogs(write_options, job_context, &synced_wals,
|
||||
error_recovery_in_prog);
|
||||
mutex_.Lock();
|
||||
if (log_io_s.ok() && synced_wals.IsWalAddition()) {
|
||||
@@ -1595,12 +1657,10 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
|
||||
mutex_.Unlock();
|
||||
if (compaction_job_info != nullptr) {
|
||||
BuildCompactionJobInfo(cfd, c.get(), s, compaction_job_stats,
|
||||
job_context->job_id, compaction_job_info);
|
||||
}
|
||||
mutex_.Lock();
|
||||
|
||||
if (status.ok()) {
|
||||
// Done
|
||||
@@ -2250,23 +2310,6 @@ void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
|
||||
FlushReason flush_reason) {
|
||||
if (immutable_db_options_.listeners.size() == 0U) {
|
||||
return;
|
||||
}
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
std::vector<ManualFlushInfo> info;
|
||||
for (ColumnFamilyData* cfd : cfds) {
|
||||
info.push_back({cfd->GetID(), cfd->GetName(), flush_reason});
|
||||
}
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnManualFlushScheduled(this, info);
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
FlushReason flush_reason,
|
||||
@@ -2371,14 +2414,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
for (const auto& req : flush_reqs) {
|
||||
assert(req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
ColumnFamilyData* loop_cfd =
|
||||
req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||
bool already_queued_for_flush = loop_cfd->queued_for_flush();
|
||||
bool flush_req_enqueued = SchedulePendingFlush(req);
|
||||
if (already_queued_for_flush || flush_req_enqueued) {
|
||||
loop_cfd->SetFlushSkipReschedule();
|
||||
}
|
||||
SchedulePendingFlush(req);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -2390,8 +2426,6 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotifyOnManualFlushScheduled({cfd}, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:AfterScheduleFlush");
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTable:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
@@ -2536,7 +2570,6 @@ Status DBImpl::AtomicFlushMemTables(
|
||||
}
|
||||
}
|
||||
}
|
||||
NotifyOnManualFlushScheduled(cfds, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
|
||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
|
||||
if (s.ok() && flush_options.wait) {
|
||||
@@ -2594,9 +2627,7 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
|
||||
flush_reason,
|
||||
{{cfd,
|
||||
std::numeric_limits<uint64_t>::max() /* max_mem_id_to_persist */}}};
|
||||
if (SchedulePendingFlush(flush_req)) {
|
||||
cfd->SetFlushSkipReschedule();
|
||||
};
|
||||
SchedulePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
@@ -2684,13 +2715,13 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
// mode due to pending compaction bytes, but that's less common
|
||||
// No extra immutable Memtable will be created if the current Memtable is
|
||||
// empty.
|
||||
write_stall_condition =
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
|
||||
*cfd->ioptions())
|
||||
.first;
|
||||
int mem_to_flush = cfd->mem()->IsEmpty() ? 0 : 1;
|
||||
write_stall_condition = ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->imm()->NumNotFlushed() + mem_to_flush,
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(),
|
||||
mutable_cf_options, *cfd->ioptions())
|
||||
.first;
|
||||
} while (write_stall_condition != WriteStallCondition::kNormal);
|
||||
}
|
||||
return Status::OK();
|
||||
@@ -3002,14 +3033,13 @@ ColumnFamilyData* DBImpl::PickCompactionFromQueue(
|
||||
return cfd;
|
||||
}
|
||||
|
||||
bool DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
|
||||
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
|
||||
mutex_.AssertHeld();
|
||||
bool enqueued = false;
|
||||
if (reject_new_background_jobs_) {
|
||||
return enqueued;
|
||||
return;
|
||||
}
|
||||
if (flush_req.cfd_to_max_mem_id_to_persist.empty()) {
|
||||
return enqueued;
|
||||
return;
|
||||
}
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
// For the non-atomic flush case, we never schedule multiple column
|
||||
@@ -3024,7 +3054,6 @@ bool DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
|
||||
cfd->set_queued_for_flush(true);
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
enqueued = true;
|
||||
}
|
||||
} else {
|
||||
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
@@ -3033,9 +3062,7 @@ bool DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
|
||||
}
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
enqueued = true;
|
||||
}
|
||||
return enqueued;
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingCompaction(ColumnFamilyData* cfd) {
|
||||
@@ -4163,7 +4190,7 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
compaction_job_info->base_input_level = c->start_level();
|
||||
compaction_job_info->output_level = c->output_level();
|
||||
compaction_job_info->stats = compaction_job_stats;
|
||||
const auto& input_table_properties = c->GetOrInitInputTableProperties();
|
||||
const auto& input_table_properties = c->GetInputTableProperties();
|
||||
const auto& output_table_properties = c->GetOutputTableProperties();
|
||||
compaction_job_info->table_properties.insert(input_table_properties.begin(),
|
||||
input_table_properties.end());
|
||||
|
||||
@@ -312,26 +312,6 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// logs_ could have changed while we were waiting.
|
||||
continue;
|
||||
}
|
||||
// This WAL file is not live, so it's OK if we never sync the rest of it.
|
||||
// If it's already closed, then it's been fully synced. If
|
||||
// !background_close_inactive_wals then we need to Close it before
|
||||
// removing from logs_ but not blocking while holding log_write_mutex_.
|
||||
if (!immutable_db_options_.background_close_inactive_wals &&
|
||||
log.writer->file()) {
|
||||
// We are taking ownership of and pinning the front entry, so we can
|
||||
// expect it to be the same after releasing and re-acquiring the lock
|
||||
log.PrepareForSync();
|
||||
log_write_mutex_.Unlock();
|
||||
// TODO: maybe check the return value of Close.
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
auto s = log.writer->file()->Close({});
|
||||
s.PermitUncheckedError();
|
||||
log_write_mutex_.Lock();
|
||||
log.writer->PublishIfClosed();
|
||||
assert(&log == &logs_.front());
|
||||
log.FinishSync();
|
||||
log_sync_cv_.SignalAll();
|
||||
}
|
||||
logs_to_free_.push_back(log.ReleaseWriter());
|
||||
logs_.pop_front();
|
||||
}
|
||||
@@ -430,24 +410,12 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
state.manifest_delete_files.size());
|
||||
// We may ignore the dbname when generating the file names.
|
||||
for (auto& file : state.sst_delete_files) {
|
||||
auto* handle = file.metadata->table_reader_handle;
|
||||
if (file.only_delete_metadata) {
|
||||
if (handle) {
|
||||
// Simply release handle of file that is not being deleted
|
||||
table_cache_->Release(handle);
|
||||
}
|
||||
} else {
|
||||
// File is being deleted (actually obsolete)
|
||||
auto number = file.metadata->fd.GetNumber();
|
||||
candidate_files.emplace_back(MakeTableFileName(number), file.path);
|
||||
if (handle == nullptr) {
|
||||
// For files not "pinned" in table cache
|
||||
handle = TableCache::Lookup(table_cache_.get(), number);
|
||||
}
|
||||
if (handle) {
|
||||
TableCache::ReleaseObsolete(table_cache_.get(), handle,
|
||||
file.uncache_aggressiveness);
|
||||
}
|
||||
if (!file.only_delete_metadata) {
|
||||
candidate_files.emplace_back(
|
||||
MakeTableFileName(file.metadata->fd.GetNumber()), file.path);
|
||||
}
|
||||
if (file.metadata->table_reader_handle) {
|
||||
table_cache_->Release(file.metadata->table_reader_handle);
|
||||
}
|
||||
file.DeleteMetadata();
|
||||
}
|
||||
@@ -523,7 +491,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
for (const auto w : state.logs_to_free) {
|
||||
// TODO: maybe check the return value of Close.
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
auto s = w->Close({});
|
||||
auto s = w->Close(WriteOptions());
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
@@ -609,6 +577,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
std::string fname;
|
||||
std::string dir_to_sync;
|
||||
if (type == kTableFile) {
|
||||
// evict from cache
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
fname = MakeTableFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else if (type == kBlobFile) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include "db/db_impl/db_impl_follower.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/arena_wrapped_db_iter.h"
|
||||
|
||||
+14
-27
@@ -301,10 +301,7 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
}
|
||||
if (immutable_db_options_.write_dbid_to_manifest) {
|
||||
std::string temp_db_id;
|
||||
s = GetDbIdentityFromIdentityFile(&temp_db_id);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
GetDbIdentityFromIdentityFile(&temp_db_id);
|
||||
new_db.SetDBId(temp_db_id);
|
||||
}
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -530,12 +527,6 @@ Status DBImpl::Recover(
|
||||
/*no_error_if_files_missing=*/false, is_retry,
|
||||
&desc_status);
|
||||
desc_status.PermitUncheckedError();
|
||||
if (is_retry) {
|
||||
RecordTick(stats_, FILE_READ_CORRUPTION_RETRY_COUNT);
|
||||
if (desc_status.ok()) {
|
||||
RecordTick(stats_, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
|
||||
}
|
||||
}
|
||||
if (can_retry) {
|
||||
// If we're opening for the first time and the failure is likely due to
|
||||
// a corrupt MANIFEST file (could result in either the log::Reader
|
||||
@@ -646,7 +637,7 @@ Status DBImpl::Recover(
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction,
|
||||
f->temperature, // this can be different from
|
||||
// `last_level_temperature`
|
||||
// `last_level_temperature`
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number,
|
||||
f->file_checksum, f->file_checksum_func_name,
|
||||
@@ -1679,8 +1670,6 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
SequenceNumber earliest_snapshot =
|
||||
(snapshot_seqs.empty() ? kMaxSequenceNumber : snapshot_seqs.at(0));
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
@@ -1700,7 +1689,6 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
IOStatus io_s;
|
||||
const ReadOptions read_option(Env::IOActivity::kDBOpen);
|
||||
const WriteOptions write_option(Env::IO_HIGH, Env::IOActivity::kDBOpen);
|
||||
|
||||
TableBuilderOptions tboptions(
|
||||
*cfd->ioptions(), mutable_cf_options, read_option, write_option,
|
||||
cfd->internal_comparator(), cfd->internal_tbl_prop_coll_factories(),
|
||||
@@ -1709,22 +1697,21 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
0 /* level */, false /* is_bottommost */,
|
||||
TableFileCreationReason::kRecovery, 0 /* oldest_key_time */,
|
||||
0 /* file_creation_time */, db_id_, db_session_id_,
|
||||
0 /* target_file_size */, meta.fd.GetNumber(), kMaxSequenceNumber);
|
||||
0 /* target_file_size */, meta.fd.GetNumber());
|
||||
Version* version = cfd->current();
|
||||
version->Ref();
|
||||
uint64_t num_input_entries = 0;
|
||||
s = BuildTable(dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(),
|
||||
iter.get(), std::move(range_del_iters), &meta,
|
||||
&blob_file_additions, snapshot_seqs, earliest_snapshot,
|
||||
earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks,
|
||||
cfd->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_,
|
||||
job_id, nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
&num_input_entries);
|
||||
s = BuildTable(
|
||||
dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::move(range_del_iters), &meta, &blob_file_additions,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks, cfd->internal_stats(), &io_s,
|
||||
io_tracer_, BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_, job_id,
|
||||
nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
&num_input_entries);
|
||||
version->Unref();
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
|
||||
+63
-107
@@ -155,36 +155,21 @@ Status DBImpl::Write(const WriteOptions& write_options, WriteBatch* my_batch) {
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = WriteImpl(write_options, my_batch, /*callback=*/nullptr,
|
||||
/*user_write_cb=*/nullptr,
|
||||
/*log_used=*/nullptr);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb) {
|
||||
Status s;
|
||||
if (write_options.protection_bytes_per_key > 0) {
|
||||
s = WriteBatchInternal::UpdateProtectionInfo(
|
||||
my_batch, write_options.protection_bytes_per_key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = WriteImpl(write_options, my_batch, callback, user_write_cb);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch,
|
||||
UserWriteCallback* user_write_cb) {
|
||||
WriteCallback* callback) {
|
||||
Status s;
|
||||
if (write_options.protection_bytes_per_key > 0) {
|
||||
s = WriteBatchInternal::UpdateProtectionInfo(
|
||||
my_batch, write_options.protection_bytes_per_key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = WriteImpl(write_options, my_batch, /*callback=*/nullptr, user_write_cb);
|
||||
s = WriteImpl(write_options, my_batch, callback, nullptr);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -194,9 +179,9 @@ Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
// published sequence.
|
||||
Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
uint64_t log_ref, bool disable_memtable,
|
||||
uint64_t* seq_used, size_t batch_cnt,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable, uint64_t* seq_used,
|
||||
size_t batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback,
|
||||
PostMemTableCallback* post_memtable_callback) {
|
||||
assert(!seq_per_batch_ || batch_cnt != 0);
|
||||
@@ -283,10 +268,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
return Status::NotSupported(
|
||||
"seq_per_batch currently does not honor post_memtable_callback");
|
||||
}
|
||||
if (my_batch->HasDeleteRange() && immutable_db_options_.row_cache) {
|
||||
return Status::NotSupported(
|
||||
"DeleteRange is not compatible with row cache.");
|
||||
}
|
||||
// Otherwise IsLatestPersistentState optimization does not make sense
|
||||
assert(!WriteBatchInternal::IsLatestPersistentState(my_batch) ||
|
||||
disable_memtable);
|
||||
@@ -303,10 +284,10 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
seq_per_batch_ ? kDoAssignOrder : kDontAssignOrder;
|
||||
// Otherwise it is WAL-only Prepare batches in WriteCommitted policy and
|
||||
// they don't consume sequence.
|
||||
return WriteImplWALOnly(
|
||||
&nonmem_write_thread_, write_options, my_batch, callback, user_write_cb,
|
||||
log_used, log_ref, seq_used, batch_cnt, pre_release_callback,
|
||||
assign_order, kDontPublishLastSeq, disable_memtable);
|
||||
return WriteImplWALOnly(&nonmem_write_thread_, write_options, my_batch,
|
||||
callback, log_used, log_ref, seq_used, batch_cnt,
|
||||
pre_release_callback, assign_order,
|
||||
kDontPublishLastSeq, disable_memtable);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.unordered_write) {
|
||||
@@ -318,9 +299,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// Use a write thread to i) optimize for WAL write, ii) publish last
|
||||
// sequence in in increasing order, iii) call pre_release_callback serially
|
||||
Status status = WriteImplWALOnly(
|
||||
&write_thread_, write_options, my_batch, callback, user_write_cb,
|
||||
log_used, log_ref, &seq, sub_batch_cnt, pre_release_callback,
|
||||
kDoAssignOrder, kDoPublishLastSeq, disable_memtable);
|
||||
&write_thread_, write_options, my_batch, callback, log_used, log_ref,
|
||||
&seq, sub_batch_cnt, pre_release_callback, kDoAssignOrder,
|
||||
kDoPublishLastSeq, disable_memtable);
|
||||
TEST_SYNC_POINT("DBImpl::WriteImpl:UnorderedWriteAfterWriteWAL");
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -337,20 +318,17 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
return PipelinedWriteImpl(write_options, my_batch, callback, user_write_cb,
|
||||
log_used, log_ref, disable_memtable, seq_used);
|
||||
return PipelinedWriteImpl(write_options, my_batch, callback, log_used,
|
||||
log_ref, disable_memtable, seq_used);
|
||||
}
|
||||
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
|
||||
log_ref, disable_memtable, batch_cnt,
|
||||
pre_release_callback, post_memtable_callback);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, batch_cnt, pre_release_callback,
|
||||
post_memtable_callback);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_CALLER) {
|
||||
write_thread_.SetMemWritersEachStride(&w);
|
||||
}
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
// we are a non-leader in a parallel group
|
||||
|
||||
@@ -556,35 +534,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += seq_inc;
|
||||
|
||||
if (log_context.need_log_sync) {
|
||||
VersionEdit synced_wals;
|
||||
log_write_mutex_.Lock();
|
||||
if (status.ok()) {
|
||||
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
|
||||
&synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(logfile_number_);
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
|
||||
}
|
||||
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hence provide a simple implementation that is not necessarily
|
||||
// efficient.
|
||||
if (status.ok() && two_write_queues_) {
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
} else {
|
||||
status = SyncWAL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PreReleaseCallback is called after WAL write and before memtable write
|
||||
if (status.ok()) {
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
@@ -668,6 +617,34 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
assert(pre_release_cb_status.ok());
|
||||
}
|
||||
|
||||
if (log_context.need_log_sync) {
|
||||
VersionEdit synced_wals;
|
||||
log_write_mutex_.Lock();
|
||||
if (status.ok()) {
|
||||
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
|
||||
&synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(logfile_number_);
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
const ReadOptions read_options;
|
||||
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
|
||||
}
|
||||
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hence provide a simple implementation that is not necessarily efficient.
|
||||
if (two_write_queues_) {
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
} else {
|
||||
status = SyncWAL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool should_exit_batch_group = true;
|
||||
if (in_parallel_group) {
|
||||
// CompleteParallelWorker returns true if this thread should
|
||||
@@ -702,7 +679,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable, uint64_t* seq_used) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
@@ -710,8 +686,8 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
WriteContext write_context;
|
||||
|
||||
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
|
||||
log_ref, disable_memtable, /*_batch_cnt=*/0,
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, /*_batch_cnt=*/0,
|
||||
/*_pre_release_callback=*/nullptr);
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
TEST_SYNC_POINT("DBImplWrite::PipelinedWriteImpl:AfterJoinBatchGroup");
|
||||
@@ -850,9 +826,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
// so we need to set its status to pass ASSERT_STATUS_CHECKED
|
||||
memtable_write_group.status.PermitUncheckedError();
|
||||
}
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_CALLER) {
|
||||
write_thread_.SetMemWritersEachStride(&w);
|
||||
}
|
||||
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_memtable_time);
|
||||
@@ -892,8 +866,7 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
WriteThread::Writer w(write_options, my_batch, callback,
|
||||
/*user_write_cb=*/nullptr, log_ref,
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
false /*disable_memtable*/);
|
||||
|
||||
if (w.CheckCallback(this) && w.ShouldWriteToMemtable()) {
|
||||
@@ -943,15 +916,13 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
// applicable in a two-queue setting.
|
||||
Status DBImpl::WriteImplWALOnly(
|
||||
WriteThread* write_thread, const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
WriteBatch* my_batch, WriteCallback* callback, uint64_t* log_used,
|
||||
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
|
||||
const PublishLastSeq publish_last_seq, const bool disable_memtable) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
|
||||
log_ref, disable_memtable, sub_batch_cnt,
|
||||
pre_release_callback);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, sub_batch_cnt, pre_release_callback);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
write_thread->JoinBatchGroup(&w);
|
||||
@@ -1205,7 +1176,8 @@ void DBImpl::MemTableInsertStatusCheck(const Status& status) {
|
||||
mutex_.Lock();
|
||||
assert(!error_handler_.IsBGWorkStopped());
|
||||
// Maybe change the return status to void?
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable)
|
||||
.PermitUncheckedError();
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -1517,11 +1489,6 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
RecordTick(stats_, WAL_FILE_BYTES, log_size);
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed()) {
|
||||
writer->CheckPostWalWriteCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -1586,11 +1553,6 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
stats->AddDBStats(InternalStats::kIntStatsWriteWithWal, write_with_wal,
|
||||
concurrent);
|
||||
RecordTick(stats_, WRITE_WITH_WAL, write_with_wal);
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed()) {
|
||||
writer->CheckPostWalWriteCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
return io_s;
|
||||
}
|
||||
@@ -2239,11 +2201,6 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
memtable_info.earliest_seqno = cfd->mem()->GetEarliestSequenceNumber();
|
||||
memtable_info.num_entries = cfd->mem()->num_entries();
|
||||
memtable_info.num_deletes = cfd->mem()->num_deletes();
|
||||
if (!cfd->ioptions()->persist_user_defined_timestamps &&
|
||||
cfd->user_comparator()->timestamp_size() > 0) {
|
||||
const Slice& newest_udt = cfd->mem()->GetNewestUDT();
|
||||
memtable_info.newest_udt.assign(newest_udt.data(), newest_udt.size());
|
||||
}
|
||||
// Log this later after lock release. It may be outdated, e.g., if background
|
||||
// flush happens before logging, but that should be ok.
|
||||
int num_imm_unflushed = cfd->imm()->NumNotFlushed();
|
||||
@@ -2263,15 +2220,14 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
SequenceNumber seq = versions_->LastSequence();
|
||||
new_mem = cfd->ConstructNewMemtable(mutable_cf_options, seq);
|
||||
context->superversion_context.NewSuperVersion();
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] New memtable created with log file: #%" PRIu64
|
||||
". Immutable memtables: %d.\n",
|
||||
cfd->GetName().c_str(), new_log_number, num_imm_unflushed);
|
||||
// There should be no concurrent write as the thread is at the front of
|
||||
// writer queue
|
||||
cfd->mem()->ConstructFragmentedRangeTombstones();
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] New memtable created with log file: #%" PRIu64
|
||||
". Immutable memtables: %d.\n",
|
||||
cfd->GetName().c_str(), new_log_number, num_imm_unflushed);
|
||||
// There should be no concurrent write as the thread is at the front of
|
||||
// writer queue
|
||||
cfd->mem()->ConstructFragmentedRangeTombstones();
|
||||
|
||||
mutex_.Lock();
|
||||
if (recycle_log_number != 0) {
|
||||
@@ -2363,8 +2319,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
read_options, write_options, &wal_deletion, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (!s.ok() && versions_->io_status().IsIOError()) {
|
||||
error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
s = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
|
||||
@@ -705,7 +705,6 @@ class DBIOCorruptionTest
|
||||
DBIOCorruptionTest() : DBIOFailureTest() {
|
||||
BlockBasedTableOptions bbto;
|
||||
options_ = CurrentOptions();
|
||||
options_.statistics = CreateDBStatistics();
|
||||
|
||||
base_env_ = env_;
|
||||
EXPECT_NE(base_env_, nullptr);
|
||||
@@ -728,8 +727,6 @@ class DBIOCorruptionTest
|
||||
|
||||
Status ReopenDB() { return TryReopen(options_); }
|
||||
|
||||
Statistics* stats() { return options_.statistics.get(); }
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Env> env_guard_;
|
||||
std::shared_ptr<CorruptionFS> fs_;
|
||||
@@ -752,12 +749,8 @@ TEST_P(DBIOCorruptionTest, GetReadCorruptionRetry) {
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(val, "val1");
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
} else {
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -780,12 +773,8 @@ TEST_P(DBIOCorruptionTest, IterReadCorruptionRetry) {
|
||||
}
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
} else {
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
delete iter;
|
||||
}
|
||||
@@ -810,13 +799,9 @@ TEST_P(DBIOCorruptionTest, MultiGetReadCorruptionRetry) {
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_EQ(values[0].ToString(), "val1");
|
||||
ASSERT_EQ(values[1].ToString(), "val2");
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
} else {
|
||||
ASSERT_TRUE(statuses[0].IsCorruption());
|
||||
ASSERT_TRUE(statuses[1].IsCorruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,9 +818,6 @@ TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
|
||||
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
@@ -844,7 +826,6 @@ TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
|
||||
ASSERT_EQ(val, "val1");
|
||||
} else {
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,9 +838,6 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
|
||||
Status s = Flush();
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
@@ -868,7 +846,6 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
|
||||
ASSERT_EQ(val, "val1");
|
||||
} else {
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,12 +862,8 @@ TEST_P(DBIOCorruptionTest, ManifestCorruptionRetry) {
|
||||
|
||||
if (std::get<2>(GetParam())) {
|
||||
ASSERT_OK(ReopenDB());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
|
||||
1);
|
||||
} else {
|
||||
ASSERT_EQ(ReopenDB(), Status::Corruption());
|
||||
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
@@ -192,49 +192,6 @@ TEST_F(DBPropertiesTest, GetAggregatedIntPropertyTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, AggregateBlockCacheProperty) {
|
||||
constexpr size_t kCapacity = 1000;
|
||||
LRUCacheOptions co;
|
||||
co.capacity = kCapacity;
|
||||
co.num_shard_bits = 0;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
auto block_cache = NewLRUCache(co);
|
||||
|
||||
// All columns families share the same block cache.
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_opt;
|
||||
table_opt.no_block_cache = false;
|
||||
table_opt.block_cache = block_cache;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_opt));
|
||||
|
||||
CreateAndReopenWithCF({"one", "two", "three", "four"}, options);
|
||||
|
||||
// Insert unpinned block to the cache
|
||||
constexpr size_t kSize1 = 100;
|
||||
ASSERT_OK(block_cache->Insert("block1", nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, kSize1));
|
||||
// Insert pinned block to the cache
|
||||
constexpr size_t kSize2 = 200;
|
||||
Cache::Handle* block2 = nullptr;
|
||||
ASSERT_OK(block_cache->Insert("block2", nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, kSize2, &block2));
|
||||
|
||||
uint64_t value;
|
||||
ASSERT_TRUE(db_->GetAggregatedIntProperty(DB::Properties::kBlockCacheCapacity,
|
||||
&value));
|
||||
ASSERT_EQ(value, kCapacity);
|
||||
|
||||
ASSERT_TRUE(
|
||||
db_->GetAggregatedIntProperty(DB::Properties::kBlockCacheUsage, &value));
|
||||
ASSERT_EQ(value, kSize1 + kSize2);
|
||||
|
||||
ASSERT_TRUE(db_->GetAggregatedIntProperty(
|
||||
DB::Properties::kBlockCachePinnedUsage, &value));
|
||||
ASSERT_EQ(value, kSize2);
|
||||
|
||||
block_cache->Release(block2);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
ASSERT_EQ(a == 0U, b == 0U);
|
||||
@@ -1126,6 +1083,7 @@ TEST_F(DBPropertiesTest, EstimateCompressionRatio) {
|
||||
ASSERT_GT(CompressionRatioAtLevel(1), 10.0);
|
||||
}
|
||||
|
||||
|
||||
class CountingUserTblPropCollector : public TablePropertiesCollector {
|
||||
public:
|
||||
const char* Name() const override { return "CountingUserTblPropCollector"; }
|
||||
@@ -2408,6 +2366,7 @@ TEST_F(DBPropertiesTest, TableMetaIndexKeys) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -3820,10 +3820,6 @@ TEST_F(DBRangeDelTest, RowCache) {
|
||||
ASSERT_OK(wb.DeleteRange(Key(1), Key(5)));
|
||||
ASSERT_TRUE(db_->Write(WriteOptions(), &wb).IsNotSupported());
|
||||
ASSERT_EQ(Get(Key(3)), "val");
|
||||
// By default, memtable insertion failure will turn the DB to read-only mode.
|
||||
// The check for delete range should happen before that to fail early
|
||||
// and should not turn db into read-only mdoe.
|
||||
ASSERT_OK(Put(Key(5), "foo"));
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -245,13 +245,7 @@ TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
|
||||
// In DirectIO, where we support tail prefetching, during table open, we only
|
||||
// do 1 read instead of 4 as described above. Actual checksum verification
|
||||
// reads stay the same.
|
||||
#ifdef OS_WIN
|
||||
// No file system prefetch implemented for OS Win. During table open,
|
||||
// we only do 1 read for BufferedIO.
|
||||
int num_read_per_file = 4;
|
||||
#else
|
||||
int num_read_per_file = (!use_direct_io_) ? 7 : 4;
|
||||
#endif
|
||||
int expected = kNumFiles * num_read_per_file;
|
||||
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
|
||||
+1
-1
@@ -921,7 +921,7 @@ TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
|
||||
// before restarting the DB.
|
||||
// We have to set this on the 2nd to last file for it to delay deletion
|
||||
// on the last file. (Quirk of DeleteScheduler::BackgroundEmptyTrash())
|
||||
options.sst_file_manager->SetDeleteRateBytesPerSecond(1);
|
||||
options.sst_file_manager->SetDeleteRateBytesPerSecond(1024 * 1024);
|
||||
}
|
||||
ASSERT_OK(Put("Key2", DummyString(1024, v)));
|
||||
ASSERT_OK(Put("Key3", DummyString(1024, v)));
|
||||
|
||||
+2
-10
@@ -5645,8 +5645,6 @@ TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
ASSERT_EQ(
|
||||
dbfull()->GetOptions().compaction_options_universal.allow_trivial_move,
|
||||
false);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_read_amp,
|
||||
-1);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_universal", "{size_ratio=7;}"}}));
|
||||
@@ -5668,11 +5666,9 @@ TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
ASSERT_EQ(
|
||||
dbfull()->GetOptions().compaction_options_universal.allow_trivial_move,
|
||||
false);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_read_amp,
|
||||
-1);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compaction_options_universal",
|
||||
"{min_merge_width=11;max_read_amp=0;}"}}));
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_universal", "{min_merge_width=11;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 7u);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.min_merge_width,
|
||||
11u);
|
||||
@@ -5691,8 +5687,6 @@ TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
ASSERT_EQ(
|
||||
dbfull()->GetOptions().compaction_options_universal.allow_trivial_move,
|
||||
false);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_read_amp,
|
||||
0);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, FileCreationRandomFailure) {
|
||||
@@ -6353,8 +6347,6 @@ TEST_F(DBTest, PromoteL0) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
// Exercise what was a use-after-free (ASAN failure) under ~VersionSet()
|
||||
options.uncache_aggressiveness = 300;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// non overlapping ranges
|
||||
|
||||
+1
-1
@@ -4154,7 +4154,7 @@ TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
TEST_SYNC_POINT("DBTest2::LiveFilesOmitObsoleteFiles:FlushTriggered");
|
||||
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
VectorWalPtr log_files;
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(db_->GetSortedWalFiles(log_files));
|
||||
TEST_SYNC_POINT("DBTest2::LiveFilesOmitObsoleteFiles:LiveFilesCaptured");
|
||||
for (const auto& log_file : log_files) {
|
||||
|
||||
@@ -565,11 +565,6 @@ Options DBTestBase::GetOptions(
|
||||
options.unordered_write = false;
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithBinarySearchWithFirstKeyIndex: {
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::kBinarySearchWithFirstKey;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
+6
-7
@@ -276,16 +276,16 @@ class SpecialEnv : public EnvWrapper {
|
||||
SpecialEnv* env_;
|
||||
std::unique_ptr<WritableFile> base_;
|
||||
};
|
||||
class SpecialWalFile : public WritableFile {
|
||||
class WalFile : public WritableFile {
|
||||
public:
|
||||
SpecialWalFile(SpecialEnv* env, std::unique_ptr<WritableFile>&& b)
|
||||
WalFile(SpecialEnv* env, std::unique_ptr<WritableFile>&& b)
|
||||
: env_(env), base_(std::move(b)) {
|
||||
env_->num_open_wal_file_.fetch_add(1);
|
||||
}
|
||||
virtual ~SpecialWalFile() { env_->num_open_wal_file_.fetch_add(-1); }
|
||||
virtual ~WalFile() { env_->num_open_wal_file_.fetch_add(-1); }
|
||||
Status Append(const Slice& data) override {
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_SYNC_POINT("SpecialEnv::SpecialWalFile::Append:1");
|
||||
TEST_SYNC_POINT("SpecialEnv::WalFile::Append:1");
|
||||
#endif
|
||||
Status s;
|
||||
if (env_->log_write_error_.load(std::memory_order_acquire)) {
|
||||
@@ -299,7 +299,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
s = base_->Append(data);
|
||||
}
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_SYNC_POINT("SpecialEnv::SpecialWalFile::Append:2");
|
||||
TEST_SYNC_POINT("SpecialEnv::WalFile::Append:2");
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
@@ -419,7 +419,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
} else if (strstr(f.c_str(), "MANIFEST") != nullptr) {
|
||||
r->reset(new ManifestFile(this, std::move(*r)));
|
||||
} else if (strstr(f.c_str(), "log") != nullptr) {
|
||||
r->reset(new SpecialWalFile(this, std::move(*r)));
|
||||
r->reset(new WalFile(this, std::move(*r)));
|
||||
} else {
|
||||
r->reset(new OtherFile(this, std::move(*r)));
|
||||
}
|
||||
@@ -1041,7 +1041,6 @@ class DBTestBase : public testing::Test {
|
||||
kPartitionedFilterWithNewTableReaderForCompactions,
|
||||
kUniversalSubcompactions,
|
||||
kUnorderedWrite,
|
||||
kBlockBasedTableWithBinarySearchWithFirstKeyIndex,
|
||||
// This must be the last line
|
||||
kEnd,
|
||||
};
|
||||
|
||||
+2
-92
@@ -14,7 +14,6 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/udt_util.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
@@ -271,10 +270,8 @@ TEST_F(DBWALTest, SyncWALNotWaitWrite) {
|
||||
ASSERT_OK(Put("foo3", "bar3"));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"SpecialEnv::SpecialWalFile::Append:1",
|
||||
"DBWALTest::SyncWALNotWaitWrite:1"},
|
||||
{"DBWALTest::SyncWALNotWaitWrite:2",
|
||||
"SpecialEnv::SpecialWalFile::Append:2"},
|
||||
{"SpecialEnv::WalFile::Append:1", "DBWALTest::SyncWALNotWaitWrite:1"},
|
||||
{"DBWALTest::SyncWALNotWaitWrite:2", "SpecialEnv::WalFile::Append:2"},
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -1472,93 +1469,6 @@ TEST_F(DBWALTest, SyncMultipleLogs) {
|
||||
ASSERT_OK(dbfull()->SyncWAL());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, SyncWalPartialFailure) {
|
||||
class MyTestFileSystem : public FileSystemWrapper {
|
||||
public:
|
||||
explicit MyTestFileSystem(std::shared_ptr<FileSystem> base)
|
||||
: FileSystemWrapper(std::move(base)) {}
|
||||
|
||||
static const char* kClassName() { return "MyTestFileSystem"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
IOStatus NewWritableFile(const std::string& fname,
|
||||
const FileOptions& file_opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
IOStatus s = target()->NewWritableFile(fname, file_opts, result, dbg);
|
||||
if (s.ok()) {
|
||||
*result =
|
||||
std::make_unique<MyTestWritableFile>(std::move(*result), *this);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
AcqRelAtomic<uint32_t> syncs_before_failure_{UINT32_MAX};
|
||||
|
||||
protected:
|
||||
class MyTestWritableFile : public FSWritableFileOwnerWrapper {
|
||||
public:
|
||||
MyTestWritableFile(std::unique_ptr<FSWritableFile>&& file,
|
||||
MyTestFileSystem& fs)
|
||||
: FSWritableFileOwnerWrapper(std::move(file)), fs_(fs) {}
|
||||
|
||||
IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override {
|
||||
int prev_val = fs_.syncs_before_failure_.FetchSub(1);
|
||||
if (prev_val == 0) {
|
||||
return IOStatus::IOError("fault");
|
||||
} else {
|
||||
return target()->Sync(options, dbg);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
MyTestFileSystem& fs_;
|
||||
};
|
||||
};
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.max_write_buffer_number = 4;
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
options.max_bgerror_resume_count = 0; // manual resume
|
||||
|
||||
auto custom_fs =
|
||||
std::make_shared<MyTestFileSystem>(options.env->GetFileSystem());
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(custom_fs));
|
||||
options.env = fault_fs_env.get();
|
||||
Reopen(options);
|
||||
Defer closer([this]() { Close(); });
|
||||
|
||||
// This is the simplest way to get
|
||||
// * one inactive WAL, synced
|
||||
// * one inactive WAL, not synced, and
|
||||
// * one active WAL, not synced
|
||||
// with a single thread, to exercise as much logic as we reasonably can.
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db_)->PauseBackgroundWork());
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db_)->TEST_SwitchMemtable());
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
ASSERT_OK(Put("key2", "val2"));
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db_)->TEST_SwitchMemtable());
|
||||
ASSERT_OK(Put("key3", "val3"));
|
||||
|
||||
// Allow 1 of the WALs to sync, but another won't
|
||||
custom_fs->syncs_before_failure_.Store(1);
|
||||
ASSERT_NOK(db_->SyncWAL());
|
||||
|
||||
// Stuck in this state. (This could previously cause a segfault.)
|
||||
ASSERT_NOK(db_->SyncWAL());
|
||||
|
||||
// Can't Resume because WAL write failure is considered non-recoverable,
|
||||
// regardless of the IOStatus itself. (Can/should be fixed?)
|
||||
ASSERT_NOK(db_->Resume());
|
||||
|
||||
// Verify no data loss after reopen.
|
||||
// Also Close() could previously crash in this state.
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val1", Get("key1"));
|
||||
ASSERT_EQ("val2", Get("key2"));
|
||||
ASSERT_EQ("val3", Get("key3"));
|
||||
}
|
||||
|
||||
// Github issue 1339. Prior the fix we read sequence id from the first log to
|
||||
// a local variable, then keep increase the variable as we replay logs,
|
||||
// ignoring actual sequence id of the records. This is incorrect if some writes
|
||||
|
||||
+39
-34
@@ -250,6 +250,8 @@ void ErrorHandler::CancelErrorRecovery() {
|
||||
EndAutoRecovery();
|
||||
}
|
||||
|
||||
STATIC_AVOID_DESTRUCTION(const Status, kOkStatus){Status::OK()};
|
||||
|
||||
// This is the main function for looking at an error during a background
|
||||
// operation and deciding the severity, and error recovery strategy. The high
|
||||
// level algorithm is as follows -
|
||||
@@ -268,11 +270,11 @@ void ErrorHandler::CancelErrorRecovery() {
|
||||
// This can also get called as part of a recovery operation. In that case, we
|
||||
// also track the error separately in recovery_error_ so we can tell in the
|
||||
// end whether recovery succeeded or not
|
||||
void ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason) {
|
||||
const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
if (bg_err.ok()) {
|
||||
return;
|
||||
return kOkStatus;
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
@@ -337,7 +339,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
} else {
|
||||
// This error is less severe than previously encountered error. Don't
|
||||
// take any further action
|
||||
return;
|
||||
return bg_error_;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +356,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
if (bg_error_.severity() >= Status::Severity::kHardError) {
|
||||
is_db_stopped_.store(true, std::memory_order_release);
|
||||
}
|
||||
return bg_error_;
|
||||
}
|
||||
|
||||
// This is the main function for looking at IO related error during the
|
||||
@@ -380,14 +383,14 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
// 3) for other cases, HandleKnownErrors(const Status& bg_err,
|
||||
// BackgroundErrorReason reason) will be called to handle other error cases
|
||||
// such as delegating to SstFileManager to handle no space error.
|
||||
void ErrorHandler::SetBGError(const Status& bg_status,
|
||||
BackgroundErrorReason reason) {
|
||||
const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
Status tmp_status = bg_status;
|
||||
IOStatus bg_io_err = status_to_io_status(std::move(tmp_status));
|
||||
|
||||
if (bg_io_err.ok()) {
|
||||
return;
|
||||
return kOkStatus;
|
||||
}
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "Background IO error %s",
|
||||
bg_io_err.ToString().c_str());
|
||||
@@ -410,11 +413,11 @@ void ErrorHandler::SetBGError(const Status& bg_status,
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&bg_err, db_mutex_, &auto_recovery);
|
||||
recover_context_ = context;
|
||||
return;
|
||||
}
|
||||
if (bg_io_err.subcode() != IOStatus::SubCode::kNoSpace &&
|
||||
(bg_io_err.GetScope() == IOStatus::IOErrorScope::kIOErrorScopeFile ||
|
||||
bg_io_err.GetRetryable())) {
|
||||
return bg_error_;
|
||||
} else if (bg_io_err.subcode() != IOStatus::SubCode::kNoSpace &&
|
||||
(bg_io_err.GetScope() ==
|
||||
IOStatus::IOErrorScope::kIOErrorScopeFile ||
|
||||
bg_io_err.GetRetryable())) {
|
||||
// Second, check if the error is a retryable IO error (file scope IO error
|
||||
// is also treated as retryable IO error in RocksDB write path). if it is
|
||||
// retryable error and its severity is higher than bg_error_, overwrite the
|
||||
@@ -423,6 +426,10 @@ void ErrorHandler::SetBGError(const Status& bg_status,
|
||||
// IO error as hard error. Note that, all the NoSpace error should be
|
||||
// handled by the SstFileManager::StartErrorRecovery(). Therefore, no matter
|
||||
// it is retryable or file scope, this logic will be bypassed.
|
||||
bool auto_recovery = false;
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&new_bg_io_err, db_mutex_,
|
||||
&auto_recovery);
|
||||
|
||||
RecordStats({ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT},
|
||||
{} /* int_histograms */);
|
||||
@@ -438,13 +445,9 @@ void ErrorHandler::SetBGError(const Status& bg_status,
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Compaction will schedule by itself to resume\n");
|
||||
bool auto_recovery = false;
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&new_bg_io_err, db_mutex_,
|
||||
&auto_recovery);
|
||||
// Not used in this code path.
|
||||
new_bg_io_err.PermitUncheckedError();
|
||||
return;
|
||||
return bg_error_;
|
||||
}
|
||||
|
||||
Status::Severity severity;
|
||||
@@ -466,14 +469,10 @@ void ErrorHandler::SetBGError(const Status& bg_status,
|
||||
Status bg_err(new_bg_io_err, severity);
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
recover_context_ = context;
|
||||
bool auto_recovery = db_options_.max_bgerror_resume_count > 0;
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&new_bg_io_err, db_mutex_,
|
||||
&auto_recovery);
|
||||
StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
return;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
} else {
|
||||
return HandleKnownErrors(new_bg_io_err, reason);
|
||||
}
|
||||
HandleKnownErrors(new_bg_io_err, reason);
|
||||
}
|
||||
|
||||
void ErrorHandler::AddFilesToQuarantine(
|
||||
@@ -621,23 +620,23 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
const IOStatus& io_error) {
|
||||
db_mutex_->AssertHeld();
|
||||
if (bg_error_.ok() || io_error.ok()) {
|
||||
return;
|
||||
}
|
||||
if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_) {
|
||||
// Auto resume BG error is not enabled
|
||||
return;
|
||||
}
|
||||
if (end_recovery_) {
|
||||
if (bg_error_.ok()) {
|
||||
return bg_error_;
|
||||
} else if (io_error.ok()) {
|
||||
return kOkStatus;
|
||||
} else if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_) {
|
||||
// Auto resume BG error is not enabled, directly return bg_error_.
|
||||
return bg_error_;
|
||||
} else if (end_recovery_) {
|
||||
// Can temporarily release db mutex
|
||||
EventHelpers::NotifyOnErrorRecoveryEnd(db_options_.listeners, bg_error_,
|
||||
Status::ShutdownInProgress(),
|
||||
db_mutex_);
|
||||
db_mutex_->AssertHeld();
|
||||
return;
|
||||
return bg_error_;
|
||||
}
|
||||
RecordStats({ERROR_HANDLER_AUTORESUME_COUNT}, {} /* int_histograms */);
|
||||
ROCKS_LOG_INFO(
|
||||
@@ -665,6 +664,12 @@ void ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
|
||||
recovery_thread_.reset(
|
||||
new port::Thread(&ErrorHandler::RecoverFromRetryableBGIOError, this));
|
||||
|
||||
if (recovery_error_.ok()) {
|
||||
return recovery_error_;
|
||||
} else {
|
||||
return bg_error_;
|
||||
}
|
||||
}
|
||||
|
||||
// Automatic recover from Retryable BG IO error. Must be called after db
|
||||
|
||||
+4
-3
@@ -56,7 +56,7 @@ class ErrorHandler {
|
||||
Status::Severity GetErrorSeverity(BackgroundErrorReason reason,
|
||||
Status::Code code, Status::SubCode subcode);
|
||||
|
||||
void SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
const Status& SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
|
||||
Status GetBGError() const { return bg_error_; }
|
||||
|
||||
@@ -135,10 +135,11 @@ class ErrorHandler {
|
||||
// unsorted.
|
||||
autovector<uint64_t> files_to_quarantine_;
|
||||
|
||||
void HandleKnownErrors(const Status& bg_err, BackgroundErrorReason reason);
|
||||
const Status& HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason);
|
||||
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
|
||||
void RecoverFromNoSpace();
|
||||
void StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
void RecoverFromRetryableBGIOError();
|
||||
// First, if it is in recovery and the recovery_error is ok. Set the
|
||||
// recovery_error_ to bg_err. Second, if the severity is higher than the
|
||||
|
||||
@@ -425,7 +425,7 @@ TEST_F(DBErrorHandlingFSTest, FlushWALWriteRetryableError) {
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::SyncClosedWals:Start",
|
||||
"DBImpl::SyncClosedLogs:Start",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -470,7 +470,7 @@ TEST_F(DBErrorHandlingFSTest, FlushWALAtomicWriteRetryableError) {
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::SyncClosedWals:Start",
|
||||
"DBImpl::SyncClosedLogs:Start",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -851,17 +851,13 @@ TEST_F(DBErrorHandlingFSTest, DoubleManifestWriteError) {
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_TRUE(s.IsNoSpace());
|
||||
ASSERT_EQ(dbfull()->TEST_GetBGError().severity(),
|
||||
ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_FALSE(dbfull()->TEST_GetFilesToQuarantine().empty());
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
|
||||
// This Resume() will attempt to create a new manifest file and fail again
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_TRUE(s.IsNoSpace());
|
||||
ASSERT_EQ(dbfull()->TEST_GetBGError().severity(),
|
||||
ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_FALSE(dbfull()->TEST_GetFilesToQuarantine().empty());
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
+3
-6
@@ -228,18 +228,15 @@ void EventHelpers::NotifyOnErrorRecoveryEnd(
|
||||
InstrumentedMutex* db_mutex) {
|
||||
if (!listeners.empty()) {
|
||||
db_mutex->AssertHeld();
|
||||
// Make copies before releasing mutex to avoid race.
|
||||
Status old_bg_error_cp = old_bg_error;
|
||||
Status new_bg_error_cp = new_bg_error;
|
||||
// release lock while notifying events
|
||||
db_mutex->Unlock();
|
||||
TEST_SYNC_POINT("NotifyOnErrorRecoveryEnd:MutexUnlocked:1");
|
||||
TEST_SYNC_POINT("NotifyOnErrorRecoveryEnd:MutexUnlocked:2");
|
||||
for (auto& listener : listeners) {
|
||||
BackgroundErrorRecoveryInfo info;
|
||||
info.old_bg_error = old_bg_error_cp;
|
||||
info.new_bg_error = new_bg_error_cp;
|
||||
listener->OnErrorRecoveryCompleted(old_bg_error_cp);
|
||||
info.old_bg_error = old_bg_error;
|
||||
info.new_bg_error = new_bg_error;
|
||||
listener->OnErrorRecoveryCompleted(old_bg_error);
|
||||
listener->OnErrorRecoveryEnd(info);
|
||||
info.old_bg_error.PermitUncheckedError();
|
||||
info.new_bg_error.PermitUncheckedError();
|
||||
|
||||
@@ -44,12 +44,9 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
return status;
|
||||
}
|
||||
|
||||
// Files generated in another DB or CF may have a different column family
|
||||
// ID, so we let it pass here.
|
||||
if (file_to_ingest.cf_id !=
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily &&
|
||||
file_to_ingest.cf_id != cfd_->GetID() &&
|
||||
!ingestion_options_.allow_db_generated_files) {
|
||||
file_to_ingest.cf_id != cfd_->GetID()) {
|
||||
return Status::InvalidArgument(
|
||||
"External file column family id don't match");
|
||||
}
|
||||
@@ -114,7 +111,6 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
const std::string path_inside_db = TableFileName(
|
||||
cfd_->ioptions()->cf_paths, f.fd.GetNumber(), f.fd.GetPathId());
|
||||
if (ingestion_options_.move_files) {
|
||||
assert(!ingestion_options_.allow_db_generated_files);
|
||||
status =
|
||||
fs_->LinkFile(path_outside_db, path_inside_db, IOOptions(), nullptr);
|
||||
if (status.ok()) {
|
||||
@@ -346,7 +342,8 @@ Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed,
|
||||
autovector<UserKeyRange> ranges;
|
||||
ranges.reserve(n);
|
||||
for (const IngestedFileInfo& file_to_ingest : files_to_ingest_) {
|
||||
ranges.emplace_back(file_to_ingest.start_ukey, file_to_ingest.limit_ukey);
|
||||
ranges.emplace_back(file_to_ingest.smallest_internal_key.user_key(),
|
||||
file_to_ingest.largest_internal_key.user_key());
|
||||
}
|
||||
Status status = cfd_->RangesOverlapWithMemtables(
|
||||
ranges, super_version, db_options_.allow_data_in_errors, flush_needed);
|
||||
@@ -708,16 +705,9 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
|
||||
// Get table version
|
||||
auto version_iter = uprops.find(ExternalSstFilePropertyNames::kVersion);
|
||||
if (version_iter == uprops.end()) {
|
||||
if (!ingestion_options_.allow_db_generated_files) {
|
||||
return Status::Corruption("External file version not found");
|
||||
} else {
|
||||
// 0 is special version for when a file from live DB does not have the
|
||||
// version table property
|
||||
file_to_ingest->version = 0;
|
||||
}
|
||||
} else {
|
||||
file_to_ingest->version = DecodeFixed32(version_iter->second.c_str());
|
||||
return Status::Corruption("External file version not found");
|
||||
}
|
||||
file_to_ingest->version = DecodeFixed32(version_iter->second.c_str());
|
||||
|
||||
auto seqno_iter = uprops.find(ExternalSstFilePropertyNames::kGlobalSeqno);
|
||||
if (file_to_ingest->version == 2) {
|
||||
@@ -744,15 +734,8 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
|
||||
return Status::InvalidArgument(
|
||||
"External SST file V1 does not support global seqno");
|
||||
}
|
||||
} else if (file_to_ingest->version == 0) {
|
||||
// allow_db_generated_files is true
|
||||
assert(seqno_iter == uprops.end());
|
||||
file_to_ingest->original_seqno = 0;
|
||||
file_to_ingest->global_seqno_offset = 0;
|
||||
} else {
|
||||
return Status::InvalidArgument("External file version " +
|
||||
std::to_string(file_to_ingest->version) +
|
||||
" is not supported");
|
||||
return Status::InvalidArgument("External file version is not supported");
|
||||
}
|
||||
|
||||
file_to_ingest->cf_id = static_cast<uint32_t>(props->column_family_id);
|
||||
@@ -914,25 +897,6 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
} else if (!iter->status().ok()) {
|
||||
return iter->status();
|
||||
}
|
||||
if (ingestion_options_.allow_db_generated_files) {
|
||||
// Verify that all keys have seqno zero.
|
||||
// TODO: store largest seqno in table property and validate it instead.
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
Status pik_status =
|
||||
ParseInternalKey(iter->key(), &key, allow_data_in_errors);
|
||||
if (!pik_status.ok()) {
|
||||
return Status::Corruption("Corrupted key in external file. ",
|
||||
pik_status.getState());
|
||||
}
|
||||
if (key.sequence != 0) {
|
||||
return Status::NotSupported(
|
||||
"External file has a key with non zero sequence number.");
|
||||
}
|
||||
}
|
||||
if (!iter->status().ok()) {
|
||||
return iter->status();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
table_reader->NewRangeTombstoneIterator(ro));
|
||||
@@ -948,11 +912,6 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
return Status::Corruption("Corrupted key in external file. ",
|
||||
pik_status.getState());
|
||||
}
|
||||
if (key.sequence != 0) {
|
||||
return Status::Corruption(
|
||||
"External file has a range deletion with non zero sequence "
|
||||
"number.");
|
||||
}
|
||||
RangeTombstone tombstone(key, range_del_iter->value());
|
||||
|
||||
InternalKey start_key = tombstone.SerializeKey();
|
||||
@@ -971,17 +930,6 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
}
|
||||
}
|
||||
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
Slice smallest = file_to_ingest->smallest_internal_key.user_key();
|
||||
Slice largest = file_to_ingest->largest_internal_key.user_key();
|
||||
if (ts_sz > 0) {
|
||||
AppendUserKeyWithMaxTimestamp(&file_to_ingest->start_ukey, smallest, ts_sz);
|
||||
AppendUserKeyWithMinTimestamp(&file_to_ingest->limit_ukey, largest, ts_sz);
|
||||
} else {
|
||||
file_to_ingest->start_ukey.assign(smallest.data(), smallest.size());
|
||||
file_to_ingest->limit_ukey.assign(largest.data(), largest.size());
|
||||
}
|
||||
|
||||
auto s =
|
||||
GetSstInternalUniqueId(file_to_ingest->table_properties.db_id,
|
||||
file_to_ingest->table_properties.db_session_id,
|
||||
@@ -1034,8 +982,9 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
if (lvl > 0 && lvl < vstorage->base_level()) {
|
||||
continue;
|
||||
}
|
||||
if (cfd_->RangeOverlapWithCompaction(file_to_ingest->start_ukey,
|
||||
file_to_ingest->limit_ukey, lvl)) {
|
||||
if (cfd_->RangeOverlapWithCompaction(
|
||||
file_to_ingest->smallest_internal_key.user_key(),
|
||||
file_to_ingest->largest_internal_key.user_key(), lvl)) {
|
||||
// We must use L0 or any level higher than `lvl` to be able to overwrite
|
||||
// the compaction output keys that we overlap with in this level, We also
|
||||
// need to assign this file a seqno to overwrite the compaction output
|
||||
@@ -1045,8 +994,9 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
} else if (vstorage->NumLevelFiles(lvl) > 0) {
|
||||
bool overlap_with_level = false;
|
||||
status = sv->current->OverlapWithLevelIterator(
|
||||
ro, env_options_, file_to_ingest->start_ukey,
|
||||
file_to_ingest->limit_ukey, lvl, &overlap_with_level);
|
||||
ro, env_options_, file_to_ingest->smallest_internal_key.user_key(),
|
||||
file_to_ingest->largest_internal_key.user_key(), lvl,
|
||||
&overlap_with_level);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
@@ -1087,18 +1037,11 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
"Column family enables user-defined timestamps, please make sure the "
|
||||
"key range (without timestamp) of external file does not overlap "
|
||||
"with key range (without timestamp) in the db");
|
||||
return status;
|
||||
}
|
||||
if (*assigned_seqno == 0) {
|
||||
*assigned_seqno = last_seqno + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ingestion_options_.allow_db_generated_files && *assigned_seqno != 0) {
|
||||
return Status::InvalidArgument(
|
||||
"An ingested file is assigned to a non-zero sequence number, which is "
|
||||
"incompatible with ingestion option allow_db_generated_files.");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1222,8 +1165,9 @@ bool ExternalSstFileIngestionJob::IngestedFileFitInLevel(
|
||||
}
|
||||
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
Slice file_smallest_user_key(file_to_ingest->start_ukey);
|
||||
Slice file_largest_user_key(file_to_ingest->limit_ukey);
|
||||
Slice file_smallest_user_key(
|
||||
file_to_ingest->smallest_internal_key.user_key());
|
||||
Slice file_largest_user_key(file_to_ingest->largest_internal_key.user_key());
|
||||
|
||||
if (vstorage->OverlapInLevel(level, &file_smallest_user_key,
|
||||
&file_largest_user_key)) {
|
||||
|
||||
@@ -32,17 +32,6 @@ struct IngestedFileInfo {
|
||||
InternalKey smallest_internal_key;
|
||||
// Largest internal key in external file
|
||||
InternalKey largest_internal_key;
|
||||
// NOTE: use below two fields for all `*Overlap*` types of checks instead of
|
||||
// smallest_internal_key.user_key() and largest_internal_key.user_key().
|
||||
// The smallest / largest user key contained in the file for key range checks.
|
||||
// These could be different from smallest_internal_key.user_key(), and
|
||||
// largest_internal_key.user_key() when user-defined timestamps are enabled,
|
||||
// because the check is about making sure the user key without timestamps part
|
||||
// does not overlap. To achieve that, the smallest user key will be updated
|
||||
// with the maximum timestamp while the largest user key will be updated with
|
||||
// the min timestamp. It's otherwise the same.
|
||||
std::string start_ukey;
|
||||
std::string limit_ukey;
|
||||
// Sequence number for keys in external file
|
||||
SequenceNumber original_seqno;
|
||||
// Offset of the global sequence number field in the file, will
|
||||
|
||||
@@ -3358,60 +3358,11 @@ TEST_F(ExternalSSTFileWithTimestampTest, Basic) {
|
||||
|
||||
ASSERT_OK(IngestExternalUDTFile({file4}));
|
||||
|
||||
for (int k = 200; k < 250; k++) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(k), Key(k) + "_val",
|
||||
EncodeAsUint64(k));
|
||||
}
|
||||
|
||||
// In UDT mode, any external file that can be successfully ingested also
|
||||
// should not overlap with the db. As a result, they can always get the
|
||||
// seq 0 assigned.
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), seq_num_before_ingestion);
|
||||
|
||||
// file5.sst (Key(200), ts = 199)
|
||||
// While DB has (Key(200), ts = 200) => user key without timestamp overlaps
|
||||
std::string file5 = sst_files_dir_ + "file5.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file5));
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(200), EncodeAsUint64(199), Key(200) + "_val"));
|
||||
|
||||
ExternalSstFileInfo file5_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file5_info));
|
||||
ASSERT_TRUE(IngestExternalUDTFile({file5}).IsInvalidArgument());
|
||||
|
||||
// file6.sst (Key(200), ts = 201)
|
||||
// While DB has (Key(200), ts = 200) => user key without timestamp overlaps
|
||||
std::string file6 = sst_files_dir_ + "file6.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file6));
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(200), EncodeAsUint64(201), Key(0) + "_val"));
|
||||
|
||||
ExternalSstFileInfo file6_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file6_info));
|
||||
ASSERT_TRUE(IngestExternalUDTFile({file6}).IsInvalidArgument());
|
||||
|
||||
// Check memtable overlap.
|
||||
ASSERT_OK(dbfull()->Put(WriteOptions(), Key(250), EncodeAsUint64(250),
|
||||
Key(250) + "_val"));
|
||||
|
||||
std::string file7 = sst_files_dir_ + "file7.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file7));
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(250), EncodeAsUint64(249), Key(250) + "_val2"));
|
||||
|
||||
ExternalSstFileInfo file7_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file7_info));
|
||||
ASSERT_TRUE(IngestExternalUDTFile({file7}).IsInvalidArgument());
|
||||
|
||||
std::string file8 = sst_files_dir_ + "file8.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file8));
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(250), EncodeAsUint64(251), Key(250) + "_val3"));
|
||||
|
||||
ExternalSstFileInfo file8_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file8_info));
|
||||
ASSERT_TRUE(IngestExternalUDTFile({file8}).IsInvalidArgument());
|
||||
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction |
|
||||
kRangeDelSkipConfigs));
|
||||
@@ -3719,7 +3670,10 @@ TEST_F(ExternalSSTFileWithTimestampTest, TimestampsNotPersistedBasic) {
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
|
||||
testing::Combine(testing::Bool(), testing::Bool()));
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
std::make_tuple(true, false),
|
||||
std::make_tuple(true, true)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternSSTFileLinkFailFallbackTest,
|
||||
ExternSSTFileLinkFailFallbackTest,
|
||||
@@ -3727,339 +3681,6 @@ INSTANTIATE_TEST_CASE_P(ExternSSTFileLinkFailFallbackTest,
|
||||
std::make_tuple(true, true),
|
||||
std::make_tuple(false, false)));
|
||||
|
||||
class IngestDBGeneratedFileTest : public ExternalSSTFileTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
IngestDBGeneratedFileTest() {
|
||||
ingest_opts.allow_db_generated_files = true;
|
||||
ingest_opts.move_files = false;
|
||||
ingest_opts.verify_checksums_before_ingest = GetParam();
|
||||
ingest_opts.snapshot_consistency = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
IngestExternalFileOptions ingest_opts;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(BasicMultiConfig, IngestDBGeneratedFileTest,
|
||||
testing::Bool());
|
||||
|
||||
TEST_P(IngestDBGeneratedFileTest, FailureCase) {
|
||||
// Ingesting overlapping data should always fail.
|
||||
do {
|
||||
SCOPED_TRACE("option_config_ = " + std::to_string(option_config_));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"toto"}, options);
|
||||
// Fill CFs with overlapping keys. Will try to ingest CF1 into default CF.
|
||||
for (int k = 0; k < 50; ++k) {
|
||||
ASSERT_OK(Put(Key(k), "default_cf_" + Key(k)));
|
||||
}
|
||||
for (int k = 49; k < 100; ++k) {
|
||||
ASSERT_OK(Put(1, Key(k), "cf1_" + Key(k)));
|
||||
}
|
||||
ASSERT_OK(Flush(/*cf=*/1));
|
||||
{
|
||||
// Verify that largest key of the file has non-zero seqno.
|
||||
std::vector<std::vector<FileMetaData>> metadata;
|
||||
dbfull()->TEST_GetFilesMetaData(handles_[1], &metadata, nullptr);
|
||||
const FileMetaData& file = metadata[0][0];
|
||||
ValueType vtype;
|
||||
SequenceNumber seq;
|
||||
UnPackSequenceAndType(ExtractInternalKeyFooter(file.largest.Encode()),
|
||||
&seq, &vtype);
|
||||
ASSERT_GE(seq, 0);
|
||||
}
|
||||
std::vector<LiveFileMetaData> live_meta;
|
||||
db_->GetLiveFilesMetaData(&live_meta);
|
||||
ASSERT_EQ(live_meta.size(), 1);
|
||||
std::vector<std::string> to_ingest_files;
|
||||
to_ingest_files.emplace_back(live_meta[0].directory + "/" +
|
||||
live_meta[0].relative_filename);
|
||||
// Ingesting a file whose boundary key has non-zero seqno.
|
||||
Status s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
|
||||
ASSERT_TRUE(
|
||||
s.ToString().find("External file has non zero sequence number") !=
|
||||
std::string::npos);
|
||||
ASSERT_NOK(s);
|
||||
|
||||
{
|
||||
// Only non-boundary key with non-zero seqno.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(Put(1, Key(70), "cf1_" + Key(70)));
|
||||
ASSERT_OK(Flush(1));
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
|
||||
|
||||
// Verify that only the non-boundary key of the file has non-zero seqno.
|
||||
std::vector<std::vector<FileMetaData>> metadata;
|
||||
// File may be at different level for different options.
|
||||
dbfull()->TEST_GetFilesMetaData(handles_[1], &metadata, nullptr);
|
||||
bool found_file = false;
|
||||
for (const auto& level : metadata) {
|
||||
if (level.empty()) {
|
||||
continue;
|
||||
}
|
||||
ASSERT_FALSE(found_file);
|
||||
found_file = true;
|
||||
ASSERT_EQ(1, level.size());
|
||||
const FileMetaData& file = level[0];
|
||||
ValueType vtype;
|
||||
SequenceNumber seq;
|
||||
UnPackSequenceAndType(ExtractInternalKeyFooter(file.largest.Encode()),
|
||||
&seq, &vtype);
|
||||
ASSERT_EQ(seq, 0);
|
||||
UnPackSequenceAndType(ExtractInternalKeyFooter(file.smallest.Encode()),
|
||||
&seq, &vtype);
|
||||
ASSERT_EQ(seq, 0);
|
||||
ASSERT_GT(file.fd.largest_seqno, 0);
|
||||
}
|
||||
ASSERT_TRUE(found_file);
|
||||
live_meta.clear();
|
||||
db_->GetLiveFilesMetaData(&live_meta);
|
||||
ASSERT_EQ(live_meta.size(), 1);
|
||||
to_ingest_files[0] =
|
||||
live_meta[0].directory + "/" + live_meta[0].relative_filename;
|
||||
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(
|
||||
s.ToString().find(
|
||||
"External file has a key with non zero sequence number") !=
|
||||
std::string::npos);
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
|
||||
live_meta.clear();
|
||||
db_->GetLiveFilesMetaData(&live_meta);
|
||||
ASSERT_EQ(live_meta.size(), 1);
|
||||
ASSERT_EQ(0, live_meta[0].largest_seqno);
|
||||
to_ingest_files[0] =
|
||||
live_meta[0].directory + "/" + live_meta[0].relative_filename;
|
||||
|
||||
ingest_opts.allow_db_generated_files = false;
|
||||
// Ingesting a DB genrate file with allow_db_generated_files = false;
|
||||
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
|
||||
ASSERT_TRUE(s.ToString().find("External file version not found") !=
|
||||
std::string::npos);
|
||||
ASSERT_NOK(s);
|
||||
|
||||
const std::string err =
|
||||
"An ingested file is assigned to a non-zero sequence number, which is "
|
||||
"incompatible with ingestion option allow_db_generated_files";
|
||||
ingest_opts.allow_db_generated_files = true;
|
||||
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
|
||||
ASSERT_TRUE(s.ToString().find(err) != std::string::npos);
|
||||
ASSERT_NOK(s);
|
||||
if (options.compaction_style != kCompactionStyleUniversal) {
|
||||
// FIXME: after fixing ingestion with universal compaction, currently
|
||||
// will always ingest into L0.
|
||||
ingest_opts.fail_if_not_bottommost_level = true;
|
||||
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.ToString().find("Files cannot be ingested to Lmax") !=
|
||||
std::string::npos);
|
||||
ingest_opts.fail_if_not_bottommost_level = false;
|
||||
}
|
||||
ingest_opts.write_global_seqno = true;
|
||||
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
|
||||
ASSERT_TRUE(s.ToString().find("write_global_seqno is deprecated and does "
|
||||
"not work with allow_db_generated_files") !=
|
||||
std::string::npos);
|
||||
ASSERT_NOK(s);
|
||||
ingest_opts.write_global_seqno = false;
|
||||
|
||||
// Delete the overlapping key.
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), handles_[1], Key(49)));
|
||||
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
|
||||
live_meta.clear();
|
||||
db_->GetLiveFilesMetaData(&live_meta);
|
||||
bool cf1_file_found = false;
|
||||
for (const auto& f : live_meta) {
|
||||
if (f.column_family_name == "toto") {
|
||||
ASSERT_FALSE(cf1_file_found);
|
||||
cf1_file_found = true;
|
||||
ASSERT_EQ(0, f.largest_seqno);
|
||||
to_ingest_files[0] = f.directory + "/" + f.relative_filename;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(cf1_file_found);
|
||||
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ingest_opts.snapshot_consistency = true;
|
||||
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
|
||||
// snapshot_consistency with snapshot will assign a newest sequence number.
|
||||
ASSERT_TRUE(s.ToString().find(err) != std::string::npos);
|
||||
ASSERT_NOK(s);
|
||||
|
||||
ingest_opts.move_files = true;
|
||||
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
|
||||
ingest_opts.move_files = false;
|
||||
ASSERT_TRUE(
|
||||
s.ToString().find("Options move_files and allow_db_generated_files are "
|
||||
"not compatible") != std::string::npos);
|
||||
ASSERT_NOK(s);
|
||||
|
||||
ingest_opts.snapshot_consistency = false;
|
||||
ASSERT_OK(db_->IngestExternalFile(to_ingest_files, ingest_opts));
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
// Verify default CF content.
|
||||
std::string val;
|
||||
for (int k = 0; k < 100; ++k) {
|
||||
ASSERT_OK(db_->Get(ReadOptions(), Key(k), &val));
|
||||
if (k < 50) {
|
||||
ASSERT_EQ(val, "default_cf_" + Key(k));
|
||||
} else {
|
||||
ASSERT_EQ(val, "cf1_" + Key(k));
|
||||
}
|
||||
}
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
class IngestDBGeneratedFileTest2
|
||||
: public ExternalSSTFileTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
|
||||
public:
|
||||
IngestDBGeneratedFileTest2() = default;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(VaryingOptions, IngestDBGeneratedFileTest2,
|
||||
testing::Combine(testing::Bool(), testing::Bool(),
|
||||
testing::Bool(), testing::Bool()));
|
||||
|
||||
TEST_P(IngestDBGeneratedFileTest2, NotOverlapWithDB) {
|
||||
// Use a separate column family to sort some data, generate multiple SST
|
||||
// files. Then ingest these files into another column family or DB. The data
|
||||
// to be ingested does not overlap with existing data.
|
||||
IngestExternalFileOptions ingest_opts;
|
||||
ingest_opts.allow_db_generated_files = true;
|
||||
ingest_opts.move_files = false;
|
||||
ingest_opts.snapshot_consistency = std::get<0>(GetParam());
|
||||
ingest_opts.allow_global_seqno = std::get<1>(GetParam());
|
||||
ingest_opts.allow_blocking_flush = std::get<2>(GetParam());
|
||||
ingest_opts.fail_if_not_bottommost_level = std::get<3>(GetParam());
|
||||
|
||||
do {
|
||||
SCOPED_TRACE("option_config_ = " + std::to_string(option_config_));
|
||||
Options options = CurrentOptions();
|
||||
// vector memtable for temp CF does not support concurrent write
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
CreateAndReopenWithCF({"toto"}, options);
|
||||
|
||||
// non-empty bottommost level
|
||||
WriteOptions wo;
|
||||
for (int k = 0; k < 50; ++k) {
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], Key(k), "base_val_" + Key(k)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
|
||||
// non-empty memtable
|
||||
for (int k = 50; k < 100; ++k) {
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], Key(k), "base_val_" + Key(k)));
|
||||
}
|
||||
|
||||
// load external data to sort, generate multiple files
|
||||
Options temp_cf_opts;
|
||||
ColumnFamilyHandle* temp_cfh;
|
||||
temp_cf_opts.target_file_size_base = 4 << 10;
|
||||
temp_cf_opts.memtable_factory.reset(new VectorRepFactory());
|
||||
temp_cf_opts.allow_concurrent_memtable_write = false;
|
||||
temp_cf_opts.compaction_style = kCompactionStyleUniversal;
|
||||
ASSERT_OK(db_->CreateColumnFamily(temp_cf_opts, "temp_cf", &temp_cfh));
|
||||
|
||||
Random rnd(301);
|
||||
std::vector<std::string> expected_value;
|
||||
expected_value.resize(100);
|
||||
// Out of order insertion of keys from 100 to 199.
|
||||
for (int k = 99; k >= 0; --k) {
|
||||
expected_value[k] = rnd.RandomString(200);
|
||||
ASSERT_OK(db_->Put(wo, temp_cfh, Key(k + 100), expected_value[k]));
|
||||
}
|
||||
ASSERT_OK(db_->CompactRange(cro, temp_cfh, nullptr, nullptr));
|
||||
std::vector<std::string> sst_file_paths;
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
db_->GetColumnFamilyMetaData(temp_cfh, &cf_meta);
|
||||
ASSERT_GT(cf_meta.file_count, 1);
|
||||
for (const auto& level_meta : cf_meta.levels) {
|
||||
if (level_meta.level + 1 < temp_cf_opts.num_levels) {
|
||||
ASSERT_EQ(0, level_meta.files.size());
|
||||
} else {
|
||||
ASSERT_GT(level_meta.files.size(), 1);
|
||||
for (const auto& meta : level_meta.files) {
|
||||
ASSERT_EQ(0, meta.largest_seqno);
|
||||
sst_file_paths.emplace_back(meta.directory + "/" +
|
||||
meta.relative_filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_OK(
|
||||
db_->IngestExternalFile(handles_[1], sst_file_paths, ingest_opts));
|
||||
// Verify state of the CF1
|
||||
ReadOptions ro;
|
||||
std::string val;
|
||||
for (int k = 0; k < 100; ++k) {
|
||||
ASSERT_OK(db_->Get(ro, handles_[1], Key(k), &val));
|
||||
ASSERT_EQ(val, "base_val_" + Key(k));
|
||||
ASSERT_OK(db_->Get(ro, handles_[1], Key(100 + k), &val));
|
||||
ASSERT_EQ(val, expected_value[k]);
|
||||
}
|
||||
|
||||
// Ingest into another DB.
|
||||
if (!encrypted_env_) {
|
||||
// Ingestion between encrypted env and non-encrypted env won't work.
|
||||
std::string db2_path = test::PerThreadDBPath("DB2");
|
||||
Options db2_options;
|
||||
db2_options.create_if_missing = true;
|
||||
DB* db2 = nullptr;
|
||||
ASSERT_OK(DB::Open(db2_options, db2_path, &db2));
|
||||
// Write some base data.
|
||||
expected_value.emplace_back(rnd.RandomString(100));
|
||||
ASSERT_OK(db2->Put(WriteOptions(), Key(200), expected_value.back()));
|
||||
ASSERT_OK(db2->CompactRange(cro, nullptr, nullptr));
|
||||
expected_value.emplace_back(rnd.RandomString(100));
|
||||
ASSERT_OK(db2->Put(WriteOptions(), Key(201), expected_value.back()));
|
||||
|
||||
ASSERT_OK(db2->IngestExternalFile({sst_file_paths}, ingest_opts));
|
||||
{
|
||||
std::unique_ptr<Iterator> iter{db2->NewIterator(ReadOptions())};
|
||||
iter->SeekToFirst();
|
||||
// The DB should have keys 100-199 from ingested files, and keys 200 and
|
||||
// 201 from itself.
|
||||
for (int k = 100; k <= 201; ++k, iter->Next()) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key(), Key(k));
|
||||
ASSERT_EQ(iter->value(), expected_value[k - 100]);
|
||||
}
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Dropping the original CF should not affect db2, reopening it should not
|
||||
// miss SST files.
|
||||
ASSERT_OK(db_->DropColumnFamily(temp_cfh));
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(temp_cfh));
|
||||
ASSERT_OK(db2->Close());
|
||||
delete db2;
|
||||
ASSERT_OK(DB::Open(db2_options, db2_path, &db2));
|
||||
ASSERT_OK(db2->Close());
|
||||
delete db2;
|
||||
ASSERT_OK(DestroyDB(db2_path, db2_options));
|
||||
} else {
|
||||
ASSERT_OK(db_->DropColumnFamily(temp_cfh));
|
||||
ASSERT_OK(db_->DestroyColumnFamilyHandle(temp_cfh));
|
||||
}
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+72
-88
@@ -12,7 +12,6 @@
|
||||
// file data (or entire files) not protected by a "sync".
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "env/mock_env.h"
|
||||
@@ -546,6 +545,78 @@ TEST_P(FaultInjectionTest, WriteBatchWalTerminationTest) {
|
||||
ASSERT_EQ(db_->Get(ro, "boys", &val), Status::NotFound());
|
||||
}
|
||||
|
||||
TEST_P(FaultInjectionTest, NoDuplicateTrailingEntries) {
|
||||
auto fault_fs = std::make_shared<FaultInjectionTestFS>(FileSystem::Default());
|
||||
fault_fs->EnableWriteErrorInjection();
|
||||
fault_fs->SetFilesystemDirectWritable(false);
|
||||
const std::string file_name = NormalizePath(dbname_ + "/test_file");
|
||||
std::unique_ptr<log::Writer> log_writer = nullptr;
|
||||
constexpr uint64_t log_number = 0;
|
||||
{
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
const Status s =
|
||||
fault_fs->NewWritableFile(file_name, FileOptions(), &file, nullptr);
|
||||
ASSERT_OK(s);
|
||||
std::unique_ptr<WritableFileWriter> fwriter(
|
||||
new WritableFileWriter(std::move(file), file_name, FileOptions()));
|
||||
log_writer.reset(new log::Writer(std::move(fwriter), log_number,
|
||||
/*recycle_log_files=*/false));
|
||||
}
|
||||
|
||||
fault_fs->SetRandomWriteError(
|
||||
0xdeadbeef, /*one_in=*/1, IOStatus::IOError("Injected IOError"),
|
||||
/*inject_for_all_file_types=*/true, /*types=*/{});
|
||||
|
||||
{
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(0);
|
||||
std::string buf;
|
||||
assert(edit.EncodeTo(&buf));
|
||||
const Status s = log_writer->AddRecord(WriteOptions(), buf);
|
||||
ASSERT_NOK(s);
|
||||
}
|
||||
|
||||
fault_fs->DisableWriteErrorInjection();
|
||||
|
||||
// Flush remaining data from its buffer to underlying file.
|
||||
ASSERT_OK(log_writer->file()->writable_file()->Sync(IOOptions(),
|
||||
nullptr /* dbg */));
|
||||
// Closing the log writer will cause WritableFileWriter::Close()
|
||||
log_writer.reset();
|
||||
|
||||
{
|
||||
std::unique_ptr<FSSequentialFile> file;
|
||||
Status s =
|
||||
fault_fs->NewSequentialFile(file_name, FileOptions(), &file, nullptr);
|
||||
ASSERT_OK(s);
|
||||
std::unique_ptr<SequentialFileReader> freader(
|
||||
new SequentialFileReader(std::move(file), file_name));
|
||||
Status log_read_s;
|
||||
class LogReporter : public log::Reader::Reporter {
|
||||
public:
|
||||
Status* status_;
|
||||
explicit LogReporter(Status* _s) : status_(_s) {}
|
||||
void Corruption(size_t /*bytes*/, const Status& _s) override {
|
||||
if (status_->ok()) {
|
||||
*status_ = _s;
|
||||
}
|
||||
}
|
||||
} reporter(&log_read_s);
|
||||
std::unique_ptr<log::Reader> log_reader(new log::Reader(
|
||||
nullptr, std::move(freader), &reporter, /*checksum=*/true, log_number));
|
||||
Slice record;
|
||||
std::string data;
|
||||
size_t count = 0;
|
||||
while (log_reader->ReadRecord(&record, &data) && log_read_s.ok()) {
|
||||
VersionEdit edit;
|
||||
ASSERT_OK(edit.DecodeFrom(data));
|
||||
++count;
|
||||
}
|
||||
// Verify that only one version edit exists in the file.
|
||||
ASSERT_EQ(1, count);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FaultTest, FaultInjectionTest,
|
||||
::testing::Values(std::make_tuple(false, kDefault, kEnd),
|
||||
@@ -558,93 +629,6 @@ INSTANTIATE_TEST_CASE_P(
|
||||
std::make_tuple(false, kSyncWal, kEnd),
|
||||
std::make_tuple(true, kSyncWal, kEnd)));
|
||||
|
||||
class FaultInjectionDBTest : public DBTestBase {
|
||||
public:
|
||||
FaultInjectionDBTest()
|
||||
: DBTestBase("fault_injection_fs_test", /*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
TEST(FaultInjectionFSTest, ReadUnsyncedData) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs =
|
||||
std::make_shared<FaultInjectionTestFS>(FileSystem::Default());
|
||||
fault_fs->SetInjectUnsyncedDataLoss(true);
|
||||
ASSERT_TRUE(fault_fs->ReadUnsyncedData());
|
||||
ASSERT_TRUE(fault_fs->InjectUnsyncedDataLoss());
|
||||
|
||||
// This is a randomized mini-stress test, to reduce the chances of bugs in
|
||||
// FaultInjectionTestFS being caught only in db_stress, where they are
|
||||
// difficult to debug. ~1000 iterations might be needed to debug relevant
|
||||
// code changes. Limiting to 10 for each regular unit test run.
|
||||
auto seed = Random::GetTLSInstance()->Next();
|
||||
for (int i = 0; i < 10; i++, seed++) {
|
||||
Random rnd(seed);
|
||||
uint32_t len = rnd.Uniform(10000) + 1;
|
||||
|
||||
std::string f =
|
||||
test::PerThreadDBPath("read_unsynced." + std::to_string(seed));
|
||||
std::string data = rnd.RandomString(len);
|
||||
|
||||
// Create partially synced file
|
||||
std::unique_ptr<FSWritableFile> w;
|
||||
ASSERT_OK(fault_fs->NewWritableFile(f, {}, &w, nullptr));
|
||||
uint32_t synced_len = rnd.Uniform(len + 1);
|
||||
ASSERT_OK(w->Append(Slice(data.data(), synced_len), {}, nullptr));
|
||||
if (synced_len > 0) {
|
||||
ASSERT_OK(w->Sync({}, nullptr));
|
||||
}
|
||||
ASSERT_OK(w->Append(Slice(data.data() + synced_len, len - synced_len), {},
|
||||
nullptr));
|
||||
|
||||
// Test file size includes unsynced data
|
||||
{
|
||||
uint64_t file_size;
|
||||
ASSERT_OK(fault_fs->GetFileSize(f, {}, &file_size, nullptr));
|
||||
ASSERT_EQ(len, file_size);
|
||||
}
|
||||
|
||||
// Test read file contents, with two reads that probably don't
|
||||
// align with the unsynced split. And maybe a sync or write between
|
||||
// the two reads.
|
||||
std::unique_ptr<FSSequentialFile> r;
|
||||
ASSERT_OK(fault_fs->NewSequentialFile(f, {}, &r, nullptr));
|
||||
uint32_t first_read_len = rnd.Uniform(len + 1);
|
||||
Slice sl;
|
||||
std::unique_ptr<char[]> scratch(new char[first_read_len]);
|
||||
ASSERT_OK(r->Read(first_read_len, {}, &sl, scratch.get(), nullptr));
|
||||
ASSERT_EQ(first_read_len, sl.size());
|
||||
ASSERT_EQ(0, sl.compare(Slice(data.data(), first_read_len)));
|
||||
|
||||
// Maybe a sync and/or write and/or close between the two reads.
|
||||
if (rnd.OneIn(2)) {
|
||||
ASSERT_OK(w->Sync({}, nullptr));
|
||||
}
|
||||
if (rnd.OneIn(2)) {
|
||||
uint32_t more_len = rnd.Uniform(1000) + 1;
|
||||
std::string more_data = rnd.RandomString(more_len);
|
||||
ASSERT_OK(w->Append(more_data, {}, nullptr));
|
||||
data += more_data;
|
||||
len += more_len;
|
||||
}
|
||||
if (rnd.OneIn(2)) {
|
||||
ASSERT_OK(w->Sync({}, nullptr));
|
||||
}
|
||||
if (rnd.OneIn(2)) {
|
||||
ASSERT_OK(w->Close({}, nullptr));
|
||||
w.reset();
|
||||
}
|
||||
|
||||
// Second read some of, all of, or more than rest of file
|
||||
uint32_t second_read_len = rnd.Uniform(len + 1);
|
||||
scratch.reset(new char[second_read_len]);
|
||||
ASSERT_OK(r->Read(second_read_len, {}, &sl, scratch.get(), nullptr));
|
||||
if (len - first_read_len < second_read_len) {
|
||||
ASSERT_EQ(len - first_read_len, sl.size());
|
||||
} else {
|
||||
ASSERT_EQ(second_read_len, sl.size());
|
||||
}
|
||||
ASSERT_EQ(0, sl.compare(Slice(data.data() + first_read_len, sl.size())));
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+3
-30
@@ -115,9 +115,6 @@ FlushJob::FlushJob(
|
||||
db_mutex_(db_mutex),
|
||||
shutting_down_(shutting_down),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
earliest_snapshot_(existing_snapshots_.empty()
|
||||
? kMaxSequenceNumber
|
||||
: existing_snapshots_.at(0)),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
job_context_(job_context),
|
||||
@@ -197,7 +194,6 @@ void FlushJob::PickMemTable() {
|
||||
// Track effective cutoff user-defined timestamp during flush if
|
||||
// user-defined timestamps can be stripped.
|
||||
GetEffectiveCutoffUDTForPickedMemTables();
|
||||
GetPrecludeLastLevelMinSeqno();
|
||||
|
||||
ReportFlushInputSize(mems_);
|
||||
|
||||
@@ -506,7 +502,7 @@ Status FlushJob::MemPurge() {
|
||||
const std::atomic<bool> kManualCompactionCanceledFalse{false};
|
||||
CompactionIterator c_iter(
|
||||
iter.get(), (cfd_->internal_comparator()).user_comparator(), &merge,
|
||||
kMaxSequenceNumber, &existing_snapshots_, earliest_snapshot_,
|
||||
kMaxSequenceNumber, &existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq, snapshot_checker_,
|
||||
env, ShouldReportDetailedTime(env, ioptions->stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
@@ -972,17 +968,14 @@ Status FlushJob::WriteLevel0Table() {
|
||||
cfd_->GetID(), cfd_->GetName(), 0 /* level */,
|
||||
false /* is_bottommost */, TableFileCreationReason::kFlush,
|
||||
oldest_key_time, current_time, db_id_, db_session_id_,
|
||||
0 /* target_file_size */, meta_.fd.GetNumber(),
|
||||
preclude_last_level_min_seqno_ == kMaxSequenceNumber
|
||||
? preclude_last_level_min_seqno_
|
||||
: std::min(earliest_snapshot_, preclude_last_level_min_seqno_));
|
||||
0 /* target_file_size */, meta_.fd.GetNumber());
|
||||
const SequenceNumber job_snapshot_seq =
|
||||
job_context_->GetJobSnapshotSequence();
|
||||
|
||||
s = BuildTable(
|
||||
dbname_, versions_, db_options_, tboptions, file_options_,
|
||||
cfd_->table_cache(), iter.get(), std::move(range_del_iters), &meta_,
|
||||
&blob_file_additions, existing_snapshots_, earliest_snapshot_,
|
||||
&blob_file_additions, existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq,
|
||||
snapshot_checker_, mutable_cf_options_.paranoid_file_checks,
|
||||
cfd_->internal_stats(), &io_s, io_tracer_,
|
||||
@@ -1161,26 +1154,6 @@ void FlushJob::GetEffectiveCutoffUDTForPickedMemTables() {
|
||||
}
|
||||
}
|
||||
|
||||
void FlushJob::GetPrecludeLastLevelMinSeqno() {
|
||||
if (cfd_->ioptions()->preclude_last_level_data_seconds == 0) {
|
||||
return;
|
||||
}
|
||||
int64_t current_time = 0;
|
||||
Status s = db_options_.clock->GetCurrentTime(¤t_time);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Failed to get current time in Flush: Status: %s",
|
||||
s.ToString().c_str());
|
||||
} else {
|
||||
SequenceNumber preserve_time_min_seqno;
|
||||
seqno_to_time_mapping_->GetCurrentTieringCutoffSeqnos(
|
||||
static_cast<uint64_t>(current_time),
|
||||
cfd_->ioptions()->preserve_internal_time_seconds,
|
||||
cfd_->ioptions()->preclude_last_level_data_seconds,
|
||||
&preserve_time_min_seqno, &preclude_last_level_min_seqno_);
|
||||
}
|
||||
}
|
||||
|
||||
Status FlushJob::MaybeIncreaseFullHistoryTsLowToAboveCutoffUDT() {
|
||||
db_mutex_->AssertHeld();
|
||||
const auto* ucmp = cfd_->user_comparator();
|
||||
|
||||
@@ -143,13 +143,6 @@ class FlushJob {
|
||||
// `MaybeIncreaseFullHistoryTsLowToAboveCutoffUDT` for details.
|
||||
void GetEffectiveCutoffUDTForPickedMemTables();
|
||||
|
||||
// If this column family enables tiering feature, it will find the current
|
||||
// `preclude_last_level_min_seqno_`, and the smaller one between this and
|
||||
// the `earliset_snapshot_` will later be announced to user property
|
||||
// collectors. It indicates to tiering use cases which data are old enough to
|
||||
// be placed on the last level.
|
||||
void GetPrecludeLastLevelMinSeqno();
|
||||
|
||||
Status MaybeIncreaseFullHistoryTsLowToAboveCutoffUDT();
|
||||
|
||||
const std::string& dbname_;
|
||||
@@ -168,7 +161,6 @@ class FlushJob {
|
||||
InstrumentedMutex* db_mutex_;
|
||||
std::atomic<bool>* shutting_down_;
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber earliest_write_conflict_snapshot_;
|
||||
SnapshotChecker* snapshot_checker_;
|
||||
JobContext* job_context_;
|
||||
@@ -229,12 +221,6 @@ class FlushJob {
|
||||
// Keeps track of the newest user-defined timestamp for this flush job if
|
||||
// `persist_user_defined_timestamps` flag is false.
|
||||
std::string cutoff_udt_;
|
||||
|
||||
// The current minimum seqno that compaction jobs will preclude the data from
|
||||
// the last level. Data with seqnos larger than this or larger than
|
||||
// `earliest_snapshot_` will be output to the penultimate level had it gone
|
||||
// through a compaction to the last level.
|
||||
SequenceNumber preclude_last_level_min_seqno_ = kMaxSequenceNumber;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+1
-60
@@ -16,7 +16,6 @@
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -34,6 +33,7 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
|
||||
const std::map<LevelStatType, LevelStat> InternalStats::compaction_level_stats =
|
||||
{
|
||||
{LevelStatType::NUM_FILES, LevelStat{"NumFiles", "Files"}},
|
||||
@@ -2135,64 +2135,5 @@ void InternalStats::DumpCFFileHistogram(std::string* value) {
|
||||
value->append(oss.str());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class SumPropertyAggregator : public IntPropertyAggregator {
|
||||
public:
|
||||
SumPropertyAggregator() : aggregated_value_(0) {}
|
||||
virtual ~SumPropertyAggregator() override = default;
|
||||
|
||||
void Add(ColumnFamilyData* cfd, uint64_t value) override {
|
||||
(void)cfd;
|
||||
aggregated_value_ += value;
|
||||
}
|
||||
|
||||
uint64_t Aggregate() const override { return aggregated_value_; }
|
||||
|
||||
private:
|
||||
uint64_t aggregated_value_;
|
||||
};
|
||||
|
||||
// A block cache may be shared by multiple column families.
|
||||
// BlockCachePropertyAggregator ensures that the same cache is only added once.
|
||||
class BlockCachePropertyAggregator : public IntPropertyAggregator {
|
||||
public:
|
||||
BlockCachePropertyAggregator() = default;
|
||||
virtual ~BlockCachePropertyAggregator() override = default;
|
||||
|
||||
void Add(ColumnFamilyData* cfd, uint64_t value) override {
|
||||
auto* table_factory = cfd->ioptions()->table_factory.get();
|
||||
assert(table_factory != nullptr);
|
||||
Cache* cache =
|
||||
table_factory->GetOptions<Cache>(TableFactory::kBlockCacheOpts());
|
||||
if (cache != nullptr) {
|
||||
block_cache_properties_.emplace(cache, value);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Aggregate() const override {
|
||||
uint64_t sum = 0;
|
||||
for (const auto& p : block_cache_properties_) {
|
||||
sum += p.second;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<Cache*, uint64_t> block_cache_properties_;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::unique_ptr<IntPropertyAggregator> CreateIntPropertyAggregator(
|
||||
const Slice& property) {
|
||||
if (property == DB::Properties::kBlockCacheCapacity ||
|
||||
property == DB::Properties::kBlockCacheUsage ||
|
||||
property == DB::Properties::kBlockCachePinnedUsage) {
|
||||
return std::make_unique<BlockCachePropertyAggregator>();
|
||||
} else {
|
||||
return std::make_unique<SumPropertyAggregator>();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+1
-20
@@ -432,7 +432,7 @@ class InternalStats {
|
||||
explicit CompactionStatsFull() : stats(), penultimate_level_stats() {}
|
||||
|
||||
explicit CompactionStatsFull(CompactionReason reason, int c)
|
||||
: stats(reason, c), penultimate_level_stats(reason, c) {}
|
||||
: stats(reason, c), penultimate_level_stats(reason, c){}
|
||||
|
||||
uint64_t TotalBytesWritten() const {
|
||||
uint64_t bytes_written = stats.bytes_written + stats.bytes_written_blob;
|
||||
@@ -873,24 +873,5 @@ class InternalStats {
|
||||
uint64_t started_at_;
|
||||
};
|
||||
|
||||
// IntPropertyAggregator aggregates an integer property across all column
|
||||
// families.
|
||||
class IntPropertyAggregator {
|
||||
public:
|
||||
IntPropertyAggregator() {}
|
||||
virtual ~IntPropertyAggregator() {}
|
||||
|
||||
IntPropertyAggregator(const IntPropertyAggregator&) = delete;
|
||||
void operator=(const IntPropertyAggregator&) = delete;
|
||||
|
||||
// Add a column family's property value to the aggregator.
|
||||
virtual void Add(ColumnFamilyData* cfd, uint64_t value) = 0;
|
||||
|
||||
// Get the aggregated value.
|
||||
virtual uint64_t Aggregate() const = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IntPropertyAggregator> CreateIntPropertyAggregator(
|
||||
const Slice& property);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -53,11 +53,6 @@ Writer::~Writer() {
|
||||
|
||||
IOStatus Writer::WriteBuffer(const WriteOptions& write_options) {
|
||||
if (dest_->seen_error()) {
|
||||
#ifndef NDEBUG
|
||||
if (dest_->seen_injected_error()) {
|
||||
return IOStatus::IOError("Seen injected error. Skip writing buffer.");
|
||||
}
|
||||
#endif // NDEBUG
|
||||
return IOStatus::IOError("Seen error. Skip writing buffer.");
|
||||
}
|
||||
IOOptions opts;
|
||||
@@ -79,23 +74,9 @@ IOStatus Writer::Close(const WriteOptions& write_options) {
|
||||
return s;
|
||||
}
|
||||
|
||||
bool Writer::PublishIfClosed() {
|
||||
if (dest_->IsClosed()) {
|
||||
dest_.reset();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus Writer::AddRecord(const WriteOptions& write_options,
|
||||
const Slice& slice) {
|
||||
if (dest_->seen_error()) {
|
||||
#ifndef NDEBUG
|
||||
if (dest_->seen_injected_error()) {
|
||||
return IOStatus::IOError("Seen injected error. Skip writing buffer.");
|
||||
}
|
||||
#endif // NDEBUG
|
||||
return IOStatus::IOError("Seen error. Skip writing buffer.");
|
||||
}
|
||||
const char* ptr = slice.data();
|
||||
@@ -203,11 +184,6 @@ IOStatus Writer::AddCompressionTypeRecord(const WriteOptions& write_options) {
|
||||
}
|
||||
|
||||
if (dest_->seen_error()) {
|
||||
#ifndef NDEBUG
|
||||
if (dest_->seen_injected_error()) {
|
||||
return IOStatus::IOError("Seen injected error. Skip writing buffer.");
|
||||
}
|
||||
#endif // NDEBUG
|
||||
return IOStatus::IOError("Seen error. Skip writing buffer.");
|
||||
}
|
||||
|
||||
|
||||
@@ -107,11 +107,6 @@ class Writer {
|
||||
|
||||
IOStatus Close(const WriteOptions& write_options);
|
||||
|
||||
// If closing the writer through file(), call this afterwards to modify
|
||||
// this object's state to reflect that. Returns true if the destination file
|
||||
// has been closed. If it hasn't been closed, returns false with no change.
|
||||
bool PublishIfClosed();
|
||||
|
||||
bool BufferIsEmpty();
|
||||
|
||||
size_t TEST_block_offset() const { return block_offset_; }
|
||||
|
||||
+4
-6
@@ -13,7 +13,6 @@
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/kv_checksum.h"
|
||||
@@ -618,9 +617,8 @@ FragmentedRangeTombstoneIterator* MemTable::NewRangeTombstoneIteratorInternal(
|
||||
}
|
||||
|
||||
void MemTable::ConstructFragmentedRangeTombstones() {
|
||||
// There should be no concurrent Construction.
|
||||
// We could also check fragmented_range_tombstone_list_ to avoid repeate
|
||||
// constructions. We just construct them here again to be safe.
|
||||
assert(!IsFragmentedRangeTombstonesConstructed(false));
|
||||
// There should be no concurrent Construction
|
||||
if (!is_range_del_table_empty_.load(std::memory_order_relaxed)) {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
auto* unfragmented_iter = new MemTableIterator(
|
||||
@@ -957,9 +955,9 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
s->key->user_key())) {
|
||||
// Correct user key
|
||||
TEST_SYNC_POINT_CALLBACK("Memtable::SaveValue:Found:entry", &entry);
|
||||
std::optional<ReadLock> read_lock;
|
||||
std::unique_ptr<ReadLock> read_lock;
|
||||
if (s->inplace_update_support) {
|
||||
read_lock.emplace(s->mem->GetLock(s->key->user_key()));
|
||||
read_lock.reset(new ReadLock(s->mem->GetLock(s->key->user_key())));
|
||||
}
|
||||
|
||||
if (s->protection_bytes_per_key > 0) {
|
||||
|
||||
+10
-10
@@ -534,21 +534,21 @@ class MemTable {
|
||||
// Returns a heuristic flush decision
|
||||
bool ShouldFlushNow();
|
||||
|
||||
// Updates `fragmented_range_tombstone_list_` that will be used to serve reads
|
||||
// when this memtable becomes an immutable memtable (in some
|
||||
// MemtableListVersion::memlist_). Should be called when this memtable is
|
||||
// about to become immutable. May be called multiple times since
|
||||
// SwitchMemtable() may fail.
|
||||
void ConstructFragmentedRangeTombstones();
|
||||
|
||||
// Returns whether a fragmented range tombstone list is already constructed
|
||||
// for this memtable. It should be constructed right before a memtable is
|
||||
// added to an immutable memtable list. Note that if a memtable does not have
|
||||
// any range tombstone, then no range tombstone list will ever be constructed
|
||||
// and true is returned in that case.
|
||||
bool IsFragmentedRangeTombstonesConstructed() const {
|
||||
return fragmented_range_tombstone_list_.get() != nullptr ||
|
||||
is_range_del_table_empty_;
|
||||
// any range tombstone, then no range tombstone list will ever be constructed.
|
||||
// @param allow_empty Specifies whether a memtable with no range tombstone is
|
||||
// considered to have its fragmented range tombstone list constructed.
|
||||
bool IsFragmentedRangeTombstonesConstructed(bool allow_empty = true) const {
|
||||
if (allow_empty) {
|
||||
return fragmented_range_tombstone_list_.get() != nullptr ||
|
||||
is_range_del_table_empty_;
|
||||
} else {
|
||||
return fragmented_range_tombstone_list_.get() != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the newest user-defined timestamp contained in this MemTable. Check
|
||||
|
||||
+4
-4
@@ -235,19 +235,19 @@ void MemTableListVersion::AddIterators(
|
||||
SequenceNumber read_seq = options.snapshot != nullptr
|
||||
? options.snapshot->GetSequenceNumber()
|
||||
: kMaxSequenceNumber;
|
||||
std::unique_ptr<TruncatedRangeDelIterator> mem_tombstone_iter;
|
||||
TruncatedRangeDelIterator* mem_tombstone_iter = nullptr;
|
||||
auto range_del_iter = m->NewRangeTombstoneIterator(
|
||||
options, read_seq, true /* immutale_memtable */);
|
||||
if (range_del_iter == nullptr || range_del_iter->empty()) {
|
||||
delete range_del_iter;
|
||||
} else {
|
||||
mem_tombstone_iter = std::make_unique<TruncatedRangeDelIterator>(
|
||||
mem_tombstone_iter = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(range_del_iter),
|
||||
&m->GetInternalKeyComparator(), nullptr /* smallest */,
|
||||
nullptr /* largest */);
|
||||
}
|
||||
merge_iter_builder->AddPointAndTombstoneIterator(
|
||||
mem_iter, std::move(mem_tombstone_iter));
|
||||
merge_iter_builder->AddPointAndTombstoneIterator(mem_iter,
|
||||
mem_tombstone_iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ class MultiCfIteratorImpl {
|
||||
void Next() {
|
||||
assert(Valid());
|
||||
auto& min_heap = GetHeap<MultiCfMinHeap>([this]() {
|
||||
std::string target(key().data(), key().size());
|
||||
Slice target = key();
|
||||
InitMinHeap();
|
||||
Seek(target);
|
||||
});
|
||||
@@ -94,7 +94,7 @@ class MultiCfIteratorImpl {
|
||||
void Prev() {
|
||||
assert(Valid());
|
||||
auto& max_heap = GetHeap<MultiCfMaxHeap>([this]() {
|
||||
std::string target(key().data(), key().size());
|
||||
Slice target = key();
|
||||
InitMaxHeap();
|
||||
SeekForPrev(target);
|
||||
});
|
||||
@@ -198,15 +198,8 @@ class MultiCfIteratorImpl {
|
||||
|
||||
template <typename BinaryHeap, typename AdvanceFuncType>
|
||||
void AdvanceIterator(BinaryHeap& heap, AdvanceFuncType advance_func) {
|
||||
assert(!heap.empty());
|
||||
reset_func_();
|
||||
// It is possible for one or more child iters are at invalid keys due to
|
||||
// manual prefix iteration. For such cases, we consider the result of the
|
||||
// multi-cf-iter is also undefined.
|
||||
// https://github.com/facebook/rocksdb/wiki/Prefix-Seek#manual-prefix-iterating
|
||||
// for details about manual prefix iteration
|
||||
if (heap.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Keep the top iterator (by popping it from the heap)
|
||||
// 2. Make sure all others have iterated past the top iterator key slice
|
||||
|
||||
@@ -17,6 +17,16 @@
|
||||
#include "table/internal_iterator.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
struct FragmentedRangeTombstoneList;
|
||||
|
||||
struct FragmentedRangeTombstoneListCache {
|
||||
// ensure only the first reader needs to initialize l
|
||||
std::mutex reader_mutex;
|
||||
std::unique_ptr<FragmentedRangeTombstoneList> tombstones = nullptr;
|
||||
// readers will first check this bool to avoid
|
||||
std::atomic<bool> initialized = false;
|
||||
};
|
||||
|
||||
struct FragmentedRangeTombstoneList {
|
||||
public:
|
||||
// A compact representation of a "stack" of range tombstone fragments, which
|
||||
@@ -114,14 +124,6 @@ struct FragmentedRangeTombstoneList {
|
||||
uint64_t total_tombstone_payload_bytes_;
|
||||
};
|
||||
|
||||
struct FragmentedRangeTombstoneListCache {
|
||||
// ensure only the first reader needs to initialize l
|
||||
std::mutex reader_mutex;
|
||||
std::unique_ptr<FragmentedRangeTombstoneList> tombstones = nullptr;
|
||||
// readers will first check this bool to avoid
|
||||
std::atomic<bool> initialized = false;
|
||||
};
|
||||
|
||||
// FragmentedRangeTombstoneIterator converts an InternalIterator of a range-del
|
||||
// meta block into an iterator over non-overlapping tombstone fragments. The
|
||||
// tombstone fragmentation process should be more efficient than the range
|
||||
|
||||
+3
-4
@@ -480,10 +480,9 @@ class Repairer {
|
||||
dbname_, /* versions */ nullptr, immutable_db_options_, tboptions,
|
||||
file_options_, table_cache_.get(), iter.get(),
|
||||
std::move(range_del_iters), &meta, nullptr /* blob_file_additions */,
|
||||
{}, kMaxSequenceNumber, kMaxSequenceNumber, kMaxSequenceNumber,
|
||||
snapshot_checker, false /* paranoid_file_checks*/,
|
||||
nullptr /* internal_stats */, &io_s, nullptr /*IOTracer*/,
|
||||
BlobFileCreationReason::kRecovery,
|
||||
{}, kMaxSequenceNumber, kMaxSequenceNumber, snapshot_checker,
|
||||
false /* paranoid_file_checks*/, nullptr /* internal_stats */, &io_s,
|
||||
nullptr /*IOTracer*/, BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, nullptr /* event_logger */,
|
||||
0 /* job_id */, nullptr /* table_properties */, write_hint);
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
|
||||
@@ -69,36 +69,6 @@ SequenceNumber SeqnoToTimeMapping::GetProximalSeqnoBeforeTime(
|
||||
return it->seqno;
|
||||
}
|
||||
|
||||
void SeqnoToTimeMapping::GetCurrentTieringCutoffSeqnos(
|
||||
uint64_t current_time, uint64_t preserve_internal_time_seconds,
|
||||
uint64_t preclude_last_level_data_seconds,
|
||||
SequenceNumber* preserve_time_min_seqno,
|
||||
SequenceNumber* preclude_last_level_min_seqno) const {
|
||||
uint64_t preserve_time_duration = std::max(preserve_internal_time_seconds,
|
||||
preclude_last_level_data_seconds);
|
||||
if (preserve_time_duration <= 0) {
|
||||
return;
|
||||
}
|
||||
uint64_t preserve_time = current_time > preserve_time_duration
|
||||
? current_time - preserve_time_duration
|
||||
: 0;
|
||||
// GetProximalSeqnoBeforeTime tells us the last seqno known to have been
|
||||
// written at or before the given time. + 1 to get the minimum we should
|
||||
// preserve without excluding anything that might have been written on or
|
||||
// after the given time.
|
||||
if (preserve_time_min_seqno) {
|
||||
*preserve_time_min_seqno = GetProximalSeqnoBeforeTime(preserve_time) + 1;
|
||||
}
|
||||
if (preclude_last_level_data_seconds > 0 && preclude_last_level_min_seqno) {
|
||||
uint64_t preclude_last_level_time =
|
||||
current_time > preclude_last_level_data_seconds
|
||||
? current_time - preclude_last_level_data_seconds
|
||||
: 0;
|
||||
*preclude_last_level_min_seqno =
|
||||
GetProximalSeqnoBeforeTime(preclude_last_level_time) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void SeqnoToTimeMapping::EnforceMaxTimeSpan(uint64_t now) {
|
||||
assert(enforced_); // at least sorted
|
||||
uint64_t cutoff_time;
|
||||
|
||||
@@ -213,15 +213,6 @@ class SeqnoToTimeMapping {
|
||||
// must be in enforced state as a precondition.
|
||||
SequenceNumber GetProximalSeqnoBeforeTime(uint64_t time) const;
|
||||
|
||||
// Given current time, the configured `preserve_internal_time_seconds`, and
|
||||
// `preclude_last_level_data_seconds`, find the relevant cutoff sequence
|
||||
// numbers for tiering.
|
||||
void GetCurrentTieringCutoffSeqnos(
|
||||
uint64_t current_time, uint64_t preserve_internal_time_seconds,
|
||||
uint64_t preclude_last_level_data_seconds,
|
||||
SequenceNumber* preserve_time_min_seqno,
|
||||
SequenceNumber* preclude_last_level_min_seqno) const;
|
||||
|
||||
// Encode to a binary string by appending to `dest`.
|
||||
// Because this is a const operation depending on sortedness, the structure
|
||||
// must be in enforced state as a precondition.
|
||||
|
||||
+2
-17
@@ -163,11 +163,6 @@ Status TableCache::GetTableReader(
|
||||
return s;
|
||||
}
|
||||
|
||||
Cache::Handle* TableCache::Lookup(Cache* cache, uint64_t file_number) {
|
||||
Slice key = GetSliceForFileNumber(&file_number);
|
||||
return cache->Lookup(key);
|
||||
}
|
||||
|
||||
Status TableCache::FindTable(
|
||||
const ReadOptions& ro, const FileOptions& file_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
@@ -230,7 +225,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
const InternalKey* smallest_compaction_key,
|
||||
const InternalKey* largest_compaction_key, bool allow_unprepared_value,
|
||||
uint8_t block_protection_bytes_per_key, const SequenceNumber* read_seqno,
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* range_del_iter) {
|
||||
TruncatedRangeDelIterator** range_del_iter) {
|
||||
PERF_TIMER_GUARD(new_table_iterator_nanos);
|
||||
|
||||
Status s;
|
||||
@@ -285,7 +280,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
delete new_range_del_iter;
|
||||
*range_del_iter = nullptr;
|
||||
} else {
|
||||
*range_del_iter = std::make_unique<TruncatedRangeDelIterator>(
|
||||
*range_del_iter = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(
|
||||
new_range_del_iter),
|
||||
&icomparator, &file_meta.smallest, &file_meta.largest);
|
||||
@@ -732,14 +727,4 @@ uint64_t TableCache::ApproximateSize(
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TableCache::ReleaseObsolete(Cache* cache, Cache::Handle* h,
|
||||
uint32_t uncache_aggressiveness) {
|
||||
CacheInterface typed_cache(cache);
|
||||
TypedHandle* table_handle = reinterpret_cast<TypedHandle*>(h);
|
||||
TableReader* table_reader = typed_cache.Value(table_handle);
|
||||
table_reader->MarkObsolete(uncache_aggressiveness);
|
||||
typed_cache.ReleaseAndEraseIfLastRef(table_handle);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+1
-9
@@ -100,7 +100,7 @@ class TableCache {
|
||||
const InternalKey* largest_compaction_key, bool allow_unprepared_value,
|
||||
uint8_t protection_bytes_per_key,
|
||||
const SequenceNumber* range_del_read_seqno = nullptr,
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* range_del_iter = nullptr);
|
||||
TruncatedRangeDelIterator** range_del_iter = nullptr);
|
||||
|
||||
// If a seek to internal key "k" in specified file finds an entry,
|
||||
// call get_context->SaveValue() repeatedly until
|
||||
@@ -165,14 +165,6 @@ class TableCache {
|
||||
// Evict any entry for the specified file number
|
||||
static void Evict(Cache* cache, uint64_t file_number);
|
||||
|
||||
// Handles releasing, erasing, etc. of what should be the last reference
|
||||
// to an obsolete file.
|
||||
static void ReleaseObsolete(Cache* cache, Cache::Handle* handle,
|
||||
uint32_t uncache_aggressiveness);
|
||||
|
||||
// Return handle to an existing cache entry if there is one
|
||||
static Cache::Handle* Lookup(Cache* cache, uint64_t file_number);
|
||||
|
||||
// Find table reader
|
||||
// @param skip_filters Disables loading/accessing the filter block
|
||||
// @param level == -1 means not specified
|
||||
|
||||
@@ -44,9 +44,7 @@ class InternalTblPropCollFactory {
|
||||
virtual ~InternalTblPropCollFactory() {}
|
||||
// has to be thread-safe
|
||||
virtual InternalTblPropColl* CreateInternalTblPropColl(
|
||||
uint32_t column_family_id, int level_at_creation, int num_levels,
|
||||
SequenceNumber last_level_inclusive_max_seqno_threshold =
|
||||
kMaxSequenceNumber) = 0;
|
||||
uint32_t column_family_id, int level_at_creation) = 0;
|
||||
|
||||
// The name of the properties collector can be used for debugging purpose.
|
||||
virtual const char* Name() const = 0;
|
||||
@@ -94,15 +92,10 @@ class UserKeyTablePropertiesCollectorFactory
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> user_collector_factory)
|
||||
: user_collector_factory_(user_collector_factory) {}
|
||||
InternalTblPropColl* CreateInternalTblPropColl(
|
||||
uint32_t column_family_id, int level_at_creation, int num_levels,
|
||||
SequenceNumber last_level_inclusive_max_seqno_threshold =
|
||||
kMaxSequenceNumber) override {
|
||||
uint32_t column_family_id, int level_at_creation) override {
|
||||
TablePropertiesCollectorFactory::Context context;
|
||||
context.column_family_id = column_family_id;
|
||||
context.level_at_creation = level_at_creation;
|
||||
context.num_levels = num_levels;
|
||||
context.last_level_inclusive_max_seqno_threshold =
|
||||
last_level_inclusive_max_seqno_threshold;
|
||||
TablePropertiesCollector* collector =
|
||||
user_collector_factory_->CreateTablePropertiesCollector(context);
|
||||
if (collector) {
|
||||
|
||||
@@ -209,9 +209,7 @@ class RegularKeysStartWithAFactory : public InternalTblPropCollFactory,
|
||||
}
|
||||
}
|
||||
InternalTblPropColl* CreateInternalTblPropColl(
|
||||
uint32_t /*column_family_id*/, int /* level_at_creation */,
|
||||
int /* num_levels */,
|
||||
SequenceNumber /* last_level_inclusive_max_seqno_threshold */) override {
|
||||
uint32_t /*column_family_id*/, int /* level_at_creation */) override {
|
||||
return new RegularKeysStartWithAInternal();
|
||||
}
|
||||
const char* Name() const override { return "RegularKeysStartWithA"; }
|
||||
|
||||
@@ -18,7 +18,7 @@ TransactionLogIteratorImpl::TransactionLogIteratorImpl(
|
||||
const std::string& dir, const ImmutableDBOptions* options,
|
||||
const TransactionLogIterator::ReadOptions& read_options,
|
||||
const EnvOptions& soptions, const SequenceNumber seq,
|
||||
std::unique_ptr<VectorWalPtr> files, VersionSet const* const versions,
|
||||
std::unique_ptr<VectorLogPtr> files, VersionSet const* const versions,
|
||||
const bool seq_per_batch, const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: dir_(dir),
|
||||
options_(options),
|
||||
@@ -44,7 +44,7 @@ TransactionLogIteratorImpl::TransactionLogIteratorImpl(
|
||||
}
|
||||
|
||||
Status TransactionLogIteratorImpl::OpenLogFile(
|
||||
const WalFile* log_file,
|
||||
const LogFile* log_file,
|
||||
std::unique_ptr<SequentialFileReader>* file_reader) {
|
||||
FileSystemPtr fs(options_->fs, io_tracer_);
|
||||
std::unique_ptr<FSSequentialFile> file;
|
||||
@@ -281,7 +281,7 @@ void TransactionLogIteratorImpl::UpdateCurrentWriteBatch(const Slice& record) {
|
||||
current_status_ = Status::OK();
|
||||
}
|
||||
|
||||
Status TransactionLogIteratorImpl::OpenLogReader(const WalFile* log_file) {
|
||||
Status TransactionLogIteratorImpl::OpenLogReader(const LogFile* log_file) {
|
||||
std::unique_ptr<SequentialFileReader> file;
|
||||
Status s = OpenLogFile(log_file, &file);
|
||||
if (!s.ok()) {
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class WalFileImpl : public WalFile {
|
||||
class LogFileImpl : public LogFile {
|
||||
public:
|
||||
WalFileImpl(uint64_t logNum, WalFileType logType, SequenceNumber startSeq,
|
||||
LogFileImpl(uint64_t logNum, WalFileType logType, SequenceNumber startSeq,
|
||||
uint64_t sizeBytes)
|
||||
: logNumber_(logNum),
|
||||
type_(logType),
|
||||
@@ -43,7 +43,7 @@ class WalFileImpl : public WalFile {
|
||||
|
||||
uint64_t SizeFileBytes() const override { return sizeFileBytes_; }
|
||||
|
||||
bool operator<(const WalFile& that) const {
|
||||
bool operator<(const LogFile& that) const {
|
||||
return LogNumber() < that.LogNumber();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
const std::string& dir, const ImmutableDBOptions* options,
|
||||
const TransactionLogIterator::ReadOptions& read_options,
|
||||
const EnvOptions& soptions, const SequenceNumber seqNum,
|
||||
std::unique_ptr<VectorWalPtr> files, VersionSet const* const versions,
|
||||
std::unique_ptr<VectorLogPtr> files, VersionSet const* const versions,
|
||||
const bool seq_per_batch, const std::shared_ptr<IOTracer>& io_tracer);
|
||||
|
||||
bool Valid() override;
|
||||
@@ -77,7 +77,7 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
const TransactionLogIterator::ReadOptions read_options_;
|
||||
const EnvOptions& soptions_;
|
||||
SequenceNumber starting_sequence_number_;
|
||||
std::unique_ptr<VectorWalPtr> files_;
|
||||
std::unique_ptr<VectorLogPtr> files_;
|
||||
// Used only to get latest seq. num
|
||||
// TODO(icanadi) can this be just a callback?
|
||||
VersionSet const* const versions_;
|
||||
@@ -92,7 +92,7 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
std::unique_ptr<WriteBatch> current_batch_;
|
||||
std::unique_ptr<log::Reader> current_log_reader_;
|
||||
std::string scratch_;
|
||||
Status OpenLogFile(const WalFile* log_file,
|
||||
Status OpenLogFile(const LogFile* log_file,
|
||||
std::unique_ptr<SequentialFileReader>* file);
|
||||
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
@@ -123,6 +123,6 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
bool IsBatchExpected(const WriteBatch* batch, SequenceNumber expected_seq);
|
||||
// Update current batch if a continuous batch is found.
|
||||
void UpdateCurrentWriteBatch(const Slice& record);
|
||||
Status OpenLogReader(const WalFile* file);
|
||||
Status OpenLogReader(const LogFile* file);
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -255,6 +255,7 @@ bool VersionEdit::EncodeTo(std::string* dst,
|
||||
char p = static_cast<char>(0);
|
||||
PutLengthPrefixedSlice(dst, Slice(&p, 1));
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",
|
||||
dst);
|
||||
|
||||
|
||||
@@ -364,15 +364,14 @@ class DumpManifestHandler : public VersionEditHandler {
|
||||
|
||||
Status ApplyVersionEdit(VersionEdit& edit, ColumnFamilyData** cfd) override {
|
||||
// Write out each individual edit
|
||||
if (json_) {
|
||||
if (verbose_ && !json_) {
|
||||
// Print out DebugStrings. Can include non-terminating null characters.
|
||||
std::string edit_dump_str = edit.DebugJSON(count_, hex_);
|
||||
fwrite(edit_dump_str.data(), sizeof(char), edit_dump_str.size(), stdout);
|
||||
fwrite("\n", sizeof(char), 1, stdout);
|
||||
} else if (verbose_) {
|
||||
fwrite(edit.DebugString(hex_).data(), sizeof(char),
|
||||
edit.DebugString(hex_).size(), stdout);
|
||||
} else if (json_) {
|
||||
// Print out DebugStrings. Can include non-terminating null characters.
|
||||
std::string edit_dump_str = edit.DebugString(hex_);
|
||||
fwrite(edit_dump_str.data(), sizeof(char), edit_dump_str.size(), stdout);
|
||||
fwrite(edit.DebugString(hex_).data(), sizeof(char),
|
||||
edit.DebugString(hex_).size(), stdout);
|
||||
}
|
||||
++count_;
|
||||
return VersionEditHandler::ApplyVersionEdit(edit, cfd);
|
||||
|
||||
+31
-54
@@ -857,14 +857,10 @@ Version::~Version() {
|
||||
f->refs--;
|
||||
if (f->refs <= 0) {
|
||||
assert(cfd_ != nullptr);
|
||||
// When not in the process of closing the DB, we'll have a superversion
|
||||
// to get current mutable options from
|
||||
auto* sv = cfd_->GetSuperVersion();
|
||||
uint32_t path_id = f->fd.GetPathId();
|
||||
assert(path_id < cfd_->ioptions()->cf_paths.size());
|
||||
vset_->obsolete_files_.emplace_back(
|
||||
f, cfd_->ioptions()->cf_paths[path_id].path,
|
||||
sv ? sv->mutable_cf_options.uncache_aggressiveness : 0,
|
||||
cfd_->GetFileMetadataCacheReservationManager());
|
||||
}
|
||||
}
|
||||
@@ -976,8 +972,7 @@ class LevelIterator final : public InternalIterator {
|
||||
const std::vector<AtomicCompactionUnitBoundary>* compaction_boundaries =
|
||||
nullptr,
|
||||
bool allow_unprepared_value = false,
|
||||
std::unique_ptr<TruncatedRangeDelIterator>*** range_tombstone_iter_ptr_ =
|
||||
nullptr)
|
||||
TruncatedRangeDelIterator**** range_tombstone_iter_ptr_ = nullptr)
|
||||
: table_cache_(table_cache),
|
||||
read_options_(read_options),
|
||||
file_options_(file_options),
|
||||
@@ -1117,8 +1112,9 @@ class LevelIterator final : public InternalIterator {
|
||||
}
|
||||
|
||||
void ClearRangeTombstoneIter() {
|
||||
if (range_tombstone_iter_) {
|
||||
range_tombstone_iter_->reset();
|
||||
if (range_tombstone_iter_ && *range_tombstone_iter_) {
|
||||
delete *range_tombstone_iter_;
|
||||
*range_tombstone_iter_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1201,7 +1197,7 @@ class LevelIterator final : public InternalIterator {
|
||||
// iterator end).
|
||||
//
|
||||
// *range_tombstone_iter_ points to range tombstones of the current SST file
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* range_tombstone_iter_;
|
||||
TruncatedRangeDelIterator** range_tombstone_iter_;
|
||||
|
||||
// The sentinel key to be returned
|
||||
Slice sentinel_;
|
||||
@@ -1280,9 +1276,11 @@ void LevelIterator::Seek(const Slice& target) {
|
||||
ts_sz);
|
||||
if (prefix_extractor_->InDomain(target_user_key_without_ts) &&
|
||||
(!prefix_extractor_->InDomain(next_file_first_user_key_without_ts) ||
|
||||
prefix_extractor_->Transform(target_user_key_without_ts)
|
||||
.compare(prefix_extractor_->Transform(
|
||||
next_file_first_user_key_without_ts)) != 0)) {
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
prefix_extractor_->Transform(target_user_key_without_ts), false,
|
||||
prefix_extractor_->Transform(
|
||||
next_file_first_user_key_without_ts),
|
||||
false) != 0)) {
|
||||
// SkipEmptyFileForward() will not advance to next file when this flag
|
||||
// is set for reason detailed below.
|
||||
//
|
||||
@@ -1927,7 +1925,7 @@ InternalIterator* Version::TEST_GetLevelIterator(
|
||||
int level, bool allow_unprepared_value) {
|
||||
auto* arena = merge_iter_builder->GetArena();
|
||||
auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
|
||||
std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr = nullptr;
|
||||
TruncatedRangeDelIterator*** tombstone_iter_ptr = nullptr;
|
||||
auto level_iter = new (mem) LevelIterator(
|
||||
cfd_->table_cache(), read_options, file_options_,
|
||||
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
|
||||
@@ -2027,7 +2025,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
auto* arena = merge_iter_builder->GetArena();
|
||||
if (level == 0) {
|
||||
// Merge all level zero files together since they may overlap
|
||||
std::unique_ptr<TruncatedRangeDelIterator> tombstone_iter = nullptr;
|
||||
TruncatedRangeDelIterator* tombstone_iter = nullptr;
|
||||
for (size_t i = 0; i < storage_info_.LevelFilesBrief(0).num_files; i++) {
|
||||
const auto& file = storage_info_.LevelFilesBrief(0).files[i];
|
||||
auto table_iter = cfd_->table_cache()->NewIterator(
|
||||
@@ -2044,8 +2042,8 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
if (read_options.ignore_range_deletions) {
|
||||
merge_iter_builder->AddIterator(table_iter);
|
||||
} else {
|
||||
merge_iter_builder->AddPointAndTombstoneIterator(
|
||||
table_iter, std::move(tombstone_iter));
|
||||
merge_iter_builder->AddPointAndTombstoneIterator(table_iter,
|
||||
tombstone_iter);
|
||||
}
|
||||
}
|
||||
if (should_sample) {
|
||||
@@ -2062,7 +2060,7 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
// walks through the non-overlapping files in the level, opening them
|
||||
// lazily.
|
||||
auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
|
||||
std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr = nullptr;
|
||||
TruncatedRangeDelIterator*** tombstone_iter_ptr = nullptr;
|
||||
auto level_iter = new (mem) LevelIterator(
|
||||
cfd_->table_cache(), read_options, soptions,
|
||||
cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
|
||||
@@ -3503,10 +3501,6 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
score = kScoreForNeedCompaction;
|
||||
}
|
||||
} else {
|
||||
// For universal compaction, if a user configures `max_read_amp`, then
|
||||
// the score may be a false positive signal.
|
||||
// `level0_file_num_compaction_trigger` is used as a trigger to check
|
||||
// if there is any compaction work to do.
|
||||
score = static_cast<double>(num_sorted_runs) /
|
||||
mutable_cf_options.level0_file_num_compaction_trigger;
|
||||
if (compaction_style_ == kCompactionStyleLevel && num_levels() > 1) {
|
||||
@@ -5194,21 +5188,13 @@ Status VersionSet::Close(FSDirectory* db_dir, InstrumentedMutex* mu) {
|
||||
}
|
||||
|
||||
VersionSet::~VersionSet() {
|
||||
// Must clean up column families to make all files "obsolete"
|
||||
// we need to delete column_family_set_ because its destructor depends on
|
||||
// VersionSet
|
||||
column_family_set_.reset();
|
||||
|
||||
for (auto& file : obsolete_files_) {
|
||||
if (file.metadata->table_reader_handle) {
|
||||
// NOTE: DB is shutting down, so file is probably not obsolete, just
|
||||
// no longer referenced by Versions in memory.
|
||||
// For more context, see comment on "table_cache_->EraseUnRefEntries()"
|
||||
// in DBImpl::CloseHelper().
|
||||
// Using uncache_aggressiveness=0 overrides any previous marking to
|
||||
// attempt to uncache the file's blocks (which after cleaning up
|
||||
// column families could cause use-after-free)
|
||||
TableCache::ReleaseObsolete(table_cache_,
|
||||
file.metadata->table_reader_handle,
|
||||
/*uncache_aggressiveness=*/0);
|
||||
table_cache_->Release(file.metadata->table_reader_handle);
|
||||
TableCache::Evict(table_cache_, file.metadata->fd.GetNumber());
|
||||
}
|
||||
file.DeleteMetadata();
|
||||
}
|
||||
@@ -5508,7 +5494,6 @@ Status VersionSet::ProcessManifestWrites(
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
IOStatus manifest_io_status;
|
||||
std::unique_ptr<log::Writer> new_desc_log_ptr;
|
||||
{
|
||||
FileOptions opt_file_opts = fs_->OptimizeForManifestWrite(file_options_);
|
||||
mu->Unlock();
|
||||
@@ -5537,7 +5522,6 @@ Status VersionSet::ProcessManifestWrites(
|
||||
}
|
||||
}
|
||||
|
||||
log::Writer* raw_desc_log_ptr = descriptor_log_.get();
|
||||
if (s.ok() && new_descriptor_log) {
|
||||
// This is fine because everything inside of this block is serialized --
|
||||
// only one thread can be here at the same time
|
||||
@@ -5559,14 +5543,12 @@ Status VersionSet::ProcessManifestWrites(
|
||||
db_options_->listeners, nullptr,
|
||||
tmp_set.Contains(FileType::kDescriptorFile),
|
||||
tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
new_desc_log_ptr.reset(
|
||||
descriptor_log_.reset(
|
||||
new log::Writer(std::move(file_writer), 0, false));
|
||||
raw_desc_log_ptr = new_desc_log_ptr.get();
|
||||
s = WriteCurrentStateToManifest(write_options, curr_state,
|
||||
wal_additions, raw_desc_log_ptr, io_s);
|
||||
assert(s == io_s);
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
wal_additions, descriptor_log_.get(),
|
||||
io_s);
|
||||
} else {
|
||||
manifest_io_status = io_s;
|
||||
s = io_s;
|
||||
}
|
||||
@@ -5609,7 +5591,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
}
|
||||
++idx;
|
||||
#endif /* !NDEBUG */
|
||||
io_s = raw_desc_log_ptr->AddRecord(write_options, record);
|
||||
io_s = descriptor_log_->AddRecord(write_options, record);
|
||||
if (!io_s.ok()) {
|
||||
s = io_s;
|
||||
manifest_io_status = io_s;
|
||||
@@ -5619,7 +5601,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
|
||||
if (s.ok()) {
|
||||
io_s =
|
||||
SyncManifest(db_options_, write_options, raw_desc_log_ptr->file());
|
||||
SyncManifest(db_options_, write_options, descriptor_log_->file());
|
||||
manifest_io_status = io_s;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"VersionSet::ProcessManifestWrites:AfterSyncManifest", &io_s);
|
||||
@@ -5652,7 +5634,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
|
||||
if (s.ok()) {
|
||||
// find offset in manifest file where this version is stored.
|
||||
new_manifest_file_size = raw_desc_log_ptr->file()->GetFileSize();
|
||||
new_manifest_file_size = descriptor_log_->file()->GetFileSize();
|
||||
}
|
||||
|
||||
if (first_writer.edit_list.front()->IsColumnFamilyDrop()) {
|
||||
@@ -5698,7 +5680,6 @@ Status VersionSet::ProcessManifestWrites(
|
||||
// Append the old manifest file to the obsolete_manifest_ list to be deleted
|
||||
// by PurgeObsoleteFiles later.
|
||||
if (s.ok() && new_descriptor_log) {
|
||||
descriptor_log_ = std::move(new_desc_log_ptr);
|
||||
obsolete_manifests_.emplace_back(
|
||||
DescriptorFileName("", manifest_file_number_));
|
||||
}
|
||||
@@ -5776,7 +5757,6 @@ Status VersionSet::ProcessManifestWrites(
|
||||
// corrupted. So we need to force the next version update to start a
|
||||
// new manifest file.
|
||||
descriptor_log_.reset();
|
||||
new_desc_log_ptr.reset();
|
||||
// If manifest operations failed, then we know the CURRENT file still
|
||||
// points to the original MANIFEST. Therefore, we can safely delete the
|
||||
// new MANIFEST.
|
||||
@@ -7082,8 +7062,8 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
// that will be initialized to where CompactionMergingIterator stores
|
||||
// pointer to its range tombstones. This is used by LevelIterator
|
||||
// to update pointer to range tombstones as it traverse different SST files.
|
||||
std::vector<std::pair<std::unique_ptr<TruncatedRangeDelIterator>,
|
||||
std::unique_ptr<TruncatedRangeDelIterator>**>>
|
||||
std::vector<
|
||||
std::pair<TruncatedRangeDelIterator*, TruncatedRangeDelIterator***>>
|
||||
range_tombstones;
|
||||
size_t num = 0;
|
||||
for (size_t which = 0; which < c->num_input_levels(); which++) {
|
||||
@@ -7105,8 +7085,7 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
*end, fmd.smallest.user_key()) < 0) {
|
||||
continue;
|
||||
}
|
||||
std::unique_ptr<TruncatedRangeDelIterator> range_tombstone_iter =
|
||||
nullptr;
|
||||
TruncatedRangeDelIterator* range_tombstone_iter = nullptr;
|
||||
list[num++] = cfd->table_cache()->NewIterator(
|
||||
read_options, file_options_compactions,
|
||||
cfd->internal_comparator(), fmd, range_del_agg,
|
||||
@@ -7123,13 +7102,11 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
c->mutable_cf_options()->block_protection_bytes_per_key,
|
||||
/*range_del_read_seqno=*/nullptr,
|
||||
/*range_del_iter=*/&range_tombstone_iter);
|
||||
range_tombstones.emplace_back(std::move(range_tombstone_iter),
|
||||
nullptr);
|
||||
range_tombstones.emplace_back(range_tombstone_iter, nullptr);
|
||||
}
|
||||
} else {
|
||||
// Create concatenating iterator for the files from this level
|
||||
std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr =
|
||||
nullptr;
|
||||
TruncatedRangeDelIterator*** tombstone_iter_ptr = nullptr;
|
||||
list[num++] = new LevelIterator(
|
||||
cfd->table_cache(), read_options, file_options_compactions,
|
||||
cfd->internal_comparator(), c->input_levels(which),
|
||||
|
||||
+7
-14
@@ -797,20 +797,16 @@ struct ObsoleteFileInfo {
|
||||
// the file, usually because the file is trivial moved so two FileMetadata
|
||||
// is managing the file.
|
||||
bool only_delete_metadata = false;
|
||||
// To apply to this file
|
||||
uint32_t uncache_aggressiveness = 0;
|
||||
|
||||
ObsoleteFileInfo() noexcept
|
||||
: metadata(nullptr), only_delete_metadata(false) {}
|
||||
ObsoleteFileInfo(FileMetaData* f, const std::string& file_path,
|
||||
uint32_t _uncache_aggressiveness,
|
||||
std::shared_ptr<CacheReservationManager>
|
||||
file_metadata_cache_res_mgr_arg = nullptr)
|
||||
: metadata(f),
|
||||
path(file_path),
|
||||
uncache_aggressiveness(_uncache_aggressiveness),
|
||||
file_metadata_cache_res_mgr(
|
||||
std::move(file_metadata_cache_res_mgr_arg)) {}
|
||||
only_delete_metadata(false),
|
||||
file_metadata_cache_res_mgr(file_metadata_cache_res_mgr_arg) {}
|
||||
|
||||
ObsoleteFileInfo(const ObsoleteFileInfo&) = delete;
|
||||
ObsoleteFileInfo& operator=(const ObsoleteFileInfo&) = delete;
|
||||
@@ -820,13 +816,9 @@ struct ObsoleteFileInfo {
|
||||
}
|
||||
|
||||
ObsoleteFileInfo& operator=(ObsoleteFileInfo&& rhs) noexcept {
|
||||
path = std::move(rhs.path);
|
||||
metadata = rhs.metadata;
|
||||
rhs.metadata = nullptr;
|
||||
path = std::move(rhs.path);
|
||||
only_delete_metadata = rhs.only_delete_metadata;
|
||||
rhs.only_delete_metadata = false;
|
||||
uncache_aggressiveness = rhs.uncache_aggressiveness;
|
||||
rhs.uncache_aggressiveness = 0;
|
||||
file_metadata_cache_res_mgr = rhs.file_metadata_cache_res_mgr;
|
||||
rhs.file_metadata_cache_res_mgr = nullptr;
|
||||
|
||||
@@ -1503,7 +1495,10 @@ class VersionSet {
|
||||
void GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata);
|
||||
|
||||
void AddObsoleteBlobFile(uint64_t blob_file_number, std::string path) {
|
||||
// TODO: Erase file from BlobFileCache?
|
||||
assert(table_cache_);
|
||||
|
||||
table_cache_->Erase(GetSliceForKey(&blob_file_number));
|
||||
|
||||
obsolete_blob_files_.emplace_back(blob_file_number, std::move(path));
|
||||
}
|
||||
|
||||
@@ -1681,8 +1676,6 @@ class VersionSet {
|
||||
// Current size of manifest file
|
||||
uint64_t manifest_file_size_;
|
||||
|
||||
// Obsolete files, or during DB shutdown any files not referenced by what's
|
||||
// left of the in-memory LSM state.
|
||||
std::vector<ObsoleteFileInfo> obsolete_files_;
|
||||
std::vector<ObsoleteBlobFileInfo> obsolete_blob_files_;
|
||||
std::vector<std::string> obsolete_manifests_;
|
||||
|
||||
+29
-37
@@ -43,17 +43,16 @@ Status WalManager::DeleteFile(const std::string& fname, uint64_t number) {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
Status WalManager::GetSortedWalFiles(VectorWalPtr& files, bool need_seqnos,
|
||||
bool include_archived) {
|
||||
|
||||
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
|
||||
// dir in between.
|
||||
Status s;
|
||||
// list wal files in main db dir.
|
||||
VectorWalPtr logs;
|
||||
s = GetSortedWalsOfType(wal_dir_, logs, kAliveLogFile, need_seqnos);
|
||||
|
||||
if (!include_archived || !s.ok()) {
|
||||
VectorLogPtr logs;
|
||||
s = GetSortedWalsOfType(wal_dir_, logs, kAliveLogFile);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -68,13 +67,13 @@ Status WalManager::GetSortedWalFiles(VectorWalPtr& files, bool need_seqnos,
|
||||
std::string archivedir = ArchivalDirectory(wal_dir_);
|
||||
Status exists = env_->FileExists(archivedir);
|
||||
if (exists.ok()) {
|
||||
s = GetSortedWalsOfType(archivedir, files, kArchivedLogFile, need_seqnos);
|
||||
s = GetSortedWalsOfType(archivedir, files, kArchivedLogFile);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else if (!exists.IsNotFound()) {
|
||||
assert(s.ok());
|
||||
return exists;
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
|
||||
uint64_t latest_archived_log_number = 0;
|
||||
@@ -114,7 +113,7 @@ Status WalManager::GetUpdatesSince(
|
||||
// Get all sorted Wal Files.
|
||||
// Do binary search and open files and find the seq number.
|
||||
|
||||
std::unique_ptr<VectorWalPtr> wal_files(new VectorWalPtr);
|
||||
std::unique_ptr<VectorLogPtr> wal_files(new VectorLogPtr);
|
||||
Status s = GetSortedWalFiles(*wal_files);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -174,6 +173,7 @@ void WalManager::PurgeObsoleteWALFiles() {
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log, "Can't get archive files: %s",
|
||||
s.ToString().c_str());
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,9 +249,8 @@ void WalManager::PurgeObsoleteWALFiles() {
|
||||
}
|
||||
|
||||
size_t files_del_num = log_files_num - files_keep_num;
|
||||
VectorWalPtr archived_logs;
|
||||
s = GetSortedWalsOfType(archival_dir, archived_logs, kArchivedLogFile,
|
||||
/*need_seqno=*/false);
|
||||
VectorLogPtr archived_logs;
|
||||
s = GetSortedWalsOfType(archival_dir, archived_logs, kArchivedLogFile);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Unable to get archived WALs from: %s: %s",
|
||||
@@ -286,17 +285,14 @@ void WalManager::ArchiveWALFile(const std::string& fname, uint64_t number) {
|
||||
Status s = env_->RenameFile(fname, archived_log_name);
|
||||
// The sync point below is used in (DBTest,TransactionLogIteratorRace)
|
||||
TEST_SYNC_POINT("WalManager::PurgeObsoleteFiles:2");
|
||||
// The sync point below is used in
|
||||
// (CheckPointTest, CheckpointWithArchievedLog)
|
||||
TEST_SYNC_POINT("WalManager::ArchiveWALFile");
|
||||
ROCKS_LOG_INFO(db_options_.info_log, "Move log file %s to %s -- %s\n",
|
||||
fname.c_str(), archived_log_name.c_str(),
|
||||
s.ToString().c_str());
|
||||
}
|
||||
|
||||
Status WalManager::GetSortedWalsOfType(const std::string& path,
|
||||
VectorWalPtr& log_files,
|
||||
WalFileType log_type, bool need_seqnos) {
|
||||
VectorLogPtr& log_files,
|
||||
WalFileType log_type) {
|
||||
std::vector<std::string> all_files;
|
||||
const Status status = env_->GetChildren(path, &all_files);
|
||||
if (!status.ok()) {
|
||||
@@ -308,17 +304,13 @@ Status WalManager::GetSortedWalsOfType(const std::string& path,
|
||||
FileType type;
|
||||
if (ParseFileName(f, &number, &type) && type == kWalFile) {
|
||||
SequenceNumber sequence;
|
||||
if (need_seqnos) {
|
||||
Status s = ReadFirstRecord(log_type, number, &sequence);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (sequence == 0) {
|
||||
// empty file
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
sequence = 0;
|
||||
Status s = ReadFirstRecord(log_type, number, &sequence);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (sequence == 0) {
|
||||
// empty file
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reproduce the race condition where a log file is moved
|
||||
@@ -328,7 +320,7 @@ Status WalManager::GetSortedWalsOfType(const std::string& path,
|
||||
TEST_SYNC_POINT("WalManager::GetSortedWalsOfType:2");
|
||||
|
||||
uint64_t size_bytes;
|
||||
Status s = env_->GetFileSize(LogFileName(path, number), &size_bytes);
|
||||
s = env_->GetFileSize(LogFileName(path, number), &size_bytes);
|
||||
// re-try in case the alive log file has been moved to archive.
|
||||
if (!s.ok() && log_type == kAliveLogFile) {
|
||||
std::string archived_file = ArchivedLogFileName(path, number);
|
||||
@@ -346,20 +338,20 @@ Status WalManager::GetSortedWalsOfType(const std::string& path,
|
||||
}
|
||||
|
||||
log_files.emplace_back(
|
||||
new WalFileImpl(number, log_type, sequence, size_bytes));
|
||||
new LogFileImpl(number, log_type, sequence, size_bytes));
|
||||
}
|
||||
}
|
||||
std::sort(
|
||||
log_files.begin(), log_files.end(),
|
||||
[](const std::unique_ptr<WalFile>& a, const std::unique_ptr<WalFile>& b) {
|
||||
WalFileImpl* a_impl = static_cast_with_check<WalFileImpl>(a.get());
|
||||
WalFileImpl* b_impl = static_cast_with_check<WalFileImpl>(b.get());
|
||||
[](const std::unique_ptr<LogFile>& a, const std::unique_ptr<LogFile>& b) {
|
||||
LogFileImpl* a_impl = static_cast_with_check<LogFileImpl>(a.get());
|
||||
LogFileImpl* b_impl = static_cast_with_check<LogFileImpl>(b.get());
|
||||
return *a_impl < *b_impl;
|
||||
});
|
||||
return status;
|
||||
}
|
||||
|
||||
Status WalManager::RetainProbableWalFiles(VectorWalPtr& all_logs,
|
||||
Status WalManager::RetainProbableWalFiles(VectorLogPtr& all_logs,
|
||||
const SequenceNumber target) {
|
||||
int64_t start = 0; // signed to avoid overflow when target is < first file.
|
||||
int64_t end = static_cast<int64_t>(all_logs.size()) - 1;
|
||||
@@ -432,7 +424,7 @@ Status WalManager::ReadFirstRecord(const WalFileType type,
|
||||
}
|
||||
|
||||
Status WalManager::GetLiveWalFile(uint64_t number,
|
||||
std::unique_ptr<WalFile>* log_file) {
|
||||
std::unique_ptr<LogFile>* log_file) {
|
||||
if (!log_file) {
|
||||
return Status::InvalidArgument("log_file not preallocated.");
|
||||
}
|
||||
@@ -450,7 +442,7 @@ Status WalManager::GetLiveWalFile(uint64_t number,
|
||||
return s;
|
||||
}
|
||||
|
||||
log_file->reset(new WalFileImpl(number, kAliveLogFile,
|
||||
log_file->reset(new LogFileImpl(number, kAliveLogFile,
|
||||
0, // SequenceNumber
|
||||
size_bytes));
|
||||
|
||||
|
||||
+5
-6
@@ -49,8 +49,7 @@ class WalManager {
|
||||
wal_in_db_path_(db_options_.IsWalDirSameAsDBPath()),
|
||||
io_tracer_(io_tracer) {}
|
||||
|
||||
Status GetSortedWalFiles(VectorWalPtr& files, bool need_seqnos = true,
|
||||
bool include_archived = true);
|
||||
Status GetSortedWalFiles(VectorLogPtr& files);
|
||||
|
||||
// Allow user to tail transaction log to find all recent changes to the
|
||||
// database that are newer than `seq_number`.
|
||||
@@ -65,7 +64,7 @@ class WalManager {
|
||||
|
||||
Status DeleteFile(const std::string& fname, uint64_t number);
|
||||
|
||||
Status GetLiveWalFile(uint64_t number, std::unique_ptr<WalFile>* log_file);
|
||||
Status GetLiveWalFile(uint64_t number, std::unique_ptr<LogFile>* log_file);
|
||||
|
||||
Status TEST_ReadFirstRecord(const WalFileType type, const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
@@ -78,12 +77,12 @@ class WalManager {
|
||||
}
|
||||
|
||||
private:
|
||||
Status GetSortedWalsOfType(const std::string& path, VectorWalPtr& log_files,
|
||||
WalFileType type, bool need_seqnos);
|
||||
Status GetSortedWalsOfType(const std::string& path, VectorLogPtr& log_files,
|
||||
WalFileType type);
|
||||
// Requires: all_logs should be sorted with earliest log file first
|
||||
// Retains all log files in all_logs which contain updates with seq no.
|
||||
// Greater Than or Equal to the requested SequenceNumber.
|
||||
Status RetainProbableWalFiles(VectorWalPtr& all_logs,
|
||||
Status RetainProbableWalFiles(VectorLogPtr& all_logs,
|
||||
const SequenceNumber target);
|
||||
|
||||
// ReadFirstRecord checks the read_first_record_cache_ to see if the entry
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "db/wide/wide_columns_helper.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ios>
|
||||
|
||||
#include "db/wide/wide_column_serialization.h"
|
||||
|
||||
@@ -16,9 +15,6 @@ void WideColumnsHelper::DumpWideColumns(const WideColumns& columns,
|
||||
if (columns.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::ios_base::fmtflags orig_flags = os.flags();
|
||||
|
||||
if (hex) {
|
||||
os << std::hex;
|
||||
}
|
||||
@@ -27,8 +23,6 @@ void WideColumnsHelper::DumpWideColumns(const WideColumns& columns,
|
||||
for (++it; it != columns.end(); ++it) {
|
||||
os << ' ' << *it;
|
||||
}
|
||||
|
||||
os.flags(orig_flags);
|
||||
}
|
||||
|
||||
Status WideColumnsHelper::DumpSliceAsWideColumns(const Slice& value,
|
||||
|
||||
+23
-54
@@ -65,7 +65,6 @@
|
||||
#include "port/lang.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "util/aligned_storage.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/coding.h"
|
||||
@@ -1901,7 +1900,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// Make creation optional but do not incur
|
||||
// std::unique_ptr additional allocation
|
||||
using MemPostInfoMap = std::map<MemTable*, MemTablePostProcessInfo>;
|
||||
using PostMapType = aligned_storage<MemPostInfoMap>::type;
|
||||
using PostMapType = std::aligned_storage<sizeof(MemPostInfoMap)>::type;
|
||||
PostMapType mem_post_info_map_;
|
||||
// current recovered transaction we are rebuilding (recovery)
|
||||
WriteBatch* rebuilding_trx_;
|
||||
@@ -1915,7 +1914,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
bool write_before_prepare_;
|
||||
// Whether this batch was unprepared or not
|
||||
bool unprepared_batch_;
|
||||
using DupDetector = aligned_storage<DuplicateDetector>::type;
|
||||
using DupDetector = std::aligned_storage<sizeof(DuplicateDetector)>::type;
|
||||
DupDetector duplicate_detector_;
|
||||
bool dup_dectector_on_;
|
||||
|
||||
@@ -1923,7 +1922,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
bool hint_created_;
|
||||
// Hints for this batch
|
||||
using HintMap = std::unordered_map<MemTable*, void*>;
|
||||
using HintMapType = aligned_storage<HintMap>::type;
|
||||
using HintMapType = std::aligned_storage<sizeof(HintMap)>::type;
|
||||
HintMapType hint_;
|
||||
|
||||
HintMap& GetHintMap() {
|
||||
@@ -2122,15 +2121,14 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename RebuildTxnOp>
|
||||
Status PutCFImpl(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value, ValueType value_type,
|
||||
RebuildTxnOp rebuild_txn_op,
|
||||
const ProtectionInfoKVOS64* kv_prot_info) {
|
||||
// optimize for non-recovery mode
|
||||
if (UNLIKELY(write_after_commit_ && rebuilding_trx_ != nullptr)) {
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOS64`.
|
||||
return rebuild_txn_op(rebuilding_trx_, column_family_id, key, value);
|
||||
return WriteBatchInternal::Put(rebuilding_trx_, column_family_id, key,
|
||||
value);
|
||||
// else insert the values to the memtable right away
|
||||
}
|
||||
|
||||
@@ -2141,8 +2139,8 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// The CF is probably flushed and hence no need for insert but we still
|
||||
// need to keep track of the keys for upcoming rollback/commit.
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOS64`.
|
||||
ret_status =
|
||||
rebuild_txn_op(rebuilding_trx_, column_family_id, key, value);
|
||||
ret_status = WriteBatchInternal::Put(rebuilding_trx_, column_family_id,
|
||||
key, value);
|
||||
if (ret_status.ok()) {
|
||||
MaybeAdvanceSeq(IsDuplicateKeySeq(column_family_id, key));
|
||||
}
|
||||
@@ -2266,8 +2264,8 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
if (UNLIKELY(ret_status.ok() && rebuilding_trx_ != nullptr)) {
|
||||
assert(!write_after_commit_);
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOS64`.
|
||||
ret_status =
|
||||
rebuild_txn_op(rebuilding_trx_, column_family_id, key, value);
|
||||
ret_status = WriteBatchInternal::Put(rebuilding_trx_, column_family_id,
|
||||
key, value);
|
||||
}
|
||||
return ret_status;
|
||||
}
|
||||
@@ -2276,21 +2274,15 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
const Slice& value) override {
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
Status ret_status;
|
||||
|
||||
auto rebuild_txn_op = [](WriteBatch* rebuilding_trx, uint32_t cf_id,
|
||||
const Slice& k, const Slice& v) -> Status {
|
||||
return WriteBatchInternal::Put(rebuilding_trx, cf_id, k, v);
|
||||
};
|
||||
|
||||
if (kv_prot_info != nullptr) {
|
||||
// Memtable needs seqno, doesn't need CF ID
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
ret_status = PutCFImpl(column_family_id, key, value, kTypeValue,
|
||||
rebuild_txn_op, &mem_kv_prot_info);
|
||||
&mem_kv_prot_info);
|
||||
} else {
|
||||
ret_status = PutCFImpl(column_family_id, key, value, kTypeValue,
|
||||
rebuild_txn_op, nullptr /* kv_prot_info */);
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
// TODO: this assumes that if TryAgain status is returned to the caller,
|
||||
// the operation is actually tried again. The proper way to do this is to
|
||||
@@ -2309,23 +2301,15 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
std::string value_buf;
|
||||
Slice packed_value =
|
||||
PackValueAndWriteTime(value, unix_write_time, &value_buf);
|
||||
|
||||
auto rebuild_txn_op = [](WriteBatch* /* rebuilding_trx */,
|
||||
uint32_t /* cf_id */, const Slice& /* k */,
|
||||
const Slice& /* v */) -> Status {
|
||||
return Status::NotSupported();
|
||||
};
|
||||
|
||||
if (kv_prot_info != nullptr) {
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
ret_status = PutCFImpl(column_family_id, key, packed_value,
|
||||
kTypeValuePreferredSeqno, rebuild_txn_op,
|
||||
&mem_kv_prot_info);
|
||||
kTypeValuePreferredSeqno, &mem_kv_prot_info);
|
||||
} else {
|
||||
ret_status = PutCFImpl(column_family_id, key, packed_value,
|
||||
kTypeValuePreferredSeqno, rebuild_txn_op,
|
||||
nullptr /* kv_prot_info */);
|
||||
ret_status =
|
||||
PutCFImpl(column_family_id, key, packed_value,
|
||||
kTypeValuePreferredSeqno, nullptr /* kv_prot_info */);
|
||||
}
|
||||
|
||||
// TODO: this assumes that if TryAgain status is returned to the caller,
|
||||
@@ -2343,27 +2327,14 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
|
||||
Status s;
|
||||
|
||||
auto rebuild_txn_op = [](WriteBatch* rebuilding_trx, uint32_t cf_id,
|
||||
const Slice& k, Slice entity) -> Status {
|
||||
WideColumns columns;
|
||||
const Status st = WideColumnSerialization::Deserialize(entity, columns);
|
||||
if (!st.ok()) {
|
||||
return st;
|
||||
}
|
||||
|
||||
return WriteBatchInternal::PutEntity(rebuilding_trx, cf_id, k, columns);
|
||||
};
|
||||
|
||||
if (kv_prot_info) {
|
||||
// Memtable needs seqno, doesn't need CF ID
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
s = PutCFImpl(column_family_id, key, value, kTypeWideColumnEntity,
|
||||
rebuild_txn_op, &mem_kv_prot_info);
|
||||
&mem_kv_prot_info);
|
||||
} else {
|
||||
s = PutCFImpl(column_family_id, key, value, kTypeWideColumnEntity,
|
||||
rebuild_txn_op,
|
||||
/* kv_prot_info */ nullptr);
|
||||
}
|
||||
|
||||
@@ -2550,6 +2521,11 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(ret_status.ok());
|
||||
|
||||
if (db_ != nullptr) {
|
||||
if (db_->immutable_db_options().row_cache) {
|
||||
ret_status.PermitUncheckedError();
|
||||
return Status::NotSupported(
|
||||
"DeleteRange is not compatible with row cache.");
|
||||
}
|
||||
auto cf_handle = cf_mems_->GetColumnFamilyHandle();
|
||||
if (cf_handle == nullptr) {
|
||||
cf_handle = db_->DefaultColumnFamily();
|
||||
@@ -2802,23 +2778,16 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
const Slice& value) override {
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
Status ret_status;
|
||||
|
||||
auto rebuild_txn_op = [](WriteBatch* /* rebuilding_trx */,
|
||||
uint32_t /* cf_id */, const Slice& /* k */,
|
||||
const Slice& /* v */) -> Status {
|
||||
return Status::NotSupported();
|
||||
};
|
||||
|
||||
if (kv_prot_info != nullptr) {
|
||||
// Memtable needs seqno, doesn't need CF ID
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
// Same as PutCF except for value type.
|
||||
ret_status = PutCFImpl(column_family_id, key, value, kTypeBlobIndex,
|
||||
rebuild_txn_op, &mem_kv_prot_info);
|
||||
&mem_kv_prot_info);
|
||||
} else {
|
||||
ret_status = PutCFImpl(column_family_id, key, value, kTypeBlobIndex,
|
||||
rebuild_txn_op, nullptr /* kv_prot_info */);
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
if (UNLIKELY(ret_status.IsTryAgain())) {
|
||||
DecrementProtectionInfoIdxForTryAgain();
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include "db/write_callback.h"
|
||||
|
||||
#include <atomic>
|
||||
@@ -13,7 +15,6 @@
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/user_write_callback.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
@@ -83,28 +84,6 @@ class MockWriteCallback : public WriteCallback {
|
||||
bool AllowWriteBatching() override { return allow_batching_; }
|
||||
};
|
||||
|
||||
class MockUserWriteCallback : public UserWriteCallback {
|
||||
public:
|
||||
std::atomic<bool> write_enqueued_{false};
|
||||
std::atomic<bool> wal_write_done_{false};
|
||||
|
||||
MockUserWriteCallback() = default;
|
||||
|
||||
MockUserWriteCallback(const MockUserWriteCallback& other) {
|
||||
write_enqueued_.store(other.write_enqueued_.load());
|
||||
wal_write_done_.store(other.wal_write_done_.load());
|
||||
}
|
||||
|
||||
void OnWriteEnqueued() override { write_enqueued_.store(true); }
|
||||
|
||||
void OnWalWriteFinish() override { wal_write_done_.store(true); }
|
||||
|
||||
void Reset() {
|
||||
write_enqueued_.store(false);
|
||||
wal_write_done_.store(false);
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
class WriteCallbackPTest
|
||||
: public WriteCallbackTest,
|
||||
@@ -140,11 +119,9 @@ TEST_P(WriteCallbackPTest, WriteWithCallbackTest) {
|
||||
kvs_.clear();
|
||||
write_batch_.Clear();
|
||||
callback_.was_called_.store(false);
|
||||
user_write_cb_.Reset();
|
||||
}
|
||||
|
||||
MockWriteCallback callback_;
|
||||
MockUserWriteCallback user_write_cb_;
|
||||
WriteBatch write_batch_;
|
||||
std::vector<std::pair<string, string>> kvs_;
|
||||
};
|
||||
@@ -350,26 +327,18 @@ TEST_P(WriteCallbackPTest, WriteWithCallbackTest) {
|
||||
ASSERT_OK(WriteBatchInternal::InsertNoop(&write_op.write_batch_));
|
||||
const size_t ONE_BATCH = 1;
|
||||
s = db_impl->WriteImpl(woptions, &write_op.write_batch_,
|
||||
&write_op.callback_, &write_op.user_write_cb_,
|
||||
nullptr, 0, false, nullptr, ONE_BATCH,
|
||||
&write_op.callback_, nullptr, 0, false, nullptr,
|
||||
ONE_BATCH,
|
||||
two_queues_ ? &publish_seq_callback : nullptr);
|
||||
} else {
|
||||
s = db_impl->WriteWithCallback(woptions, &write_op.write_batch_,
|
||||
&write_op.callback_,
|
||||
&write_op.user_write_cb_);
|
||||
&write_op.callback_);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(write_op.user_write_cb_.write_enqueued_.load());
|
||||
if (write_op.callback_.should_fail_) {
|
||||
ASSERT_TRUE(s.IsBusy());
|
||||
ASSERT_FALSE(write_op.user_write_cb_.wal_write_done_.load());
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
if (enable_WAL_) {
|
||||
ASSERT_TRUE(write_op.user_write_cb_.wal_write_done_.load());
|
||||
} else {
|
||||
ASSERT_FALSE(write_op.user_write_cb_.wal_write_done_.load());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -471,16 +440,6 @@ TEST_F(WriteCallbackTest, WriteCallBackTest) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("value.a2", value);
|
||||
|
||||
MockUserWriteCallback user_write_cb;
|
||||
WriteBatch wb4;
|
||||
ASSERT_OK(wb4.Put("a", "value.a4"));
|
||||
|
||||
ASSERT_OK(db->WriteWithCallback(write_options, &wb4, &user_write_cb));
|
||||
ASSERT_OK(db->Get(read_options, "a", &value));
|
||||
ASSERT_EQ(value, "value.a4");
|
||||
ASSERT_TRUE(user_write_cb.write_enqueued_.load());
|
||||
ASSERT_TRUE(user_write_cb.wal_write_done_.load());
|
||||
|
||||
delete db;
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
}
|
||||
|
||||
+5
-53
@@ -404,8 +404,6 @@ void WriteThread::JoinBatchGroup(Writer* w) {
|
||||
|
||||
bool linked_as_leader = LinkOne(w, &newest_writer_);
|
||||
|
||||
w->CheckWriteEnqueuedCallback();
|
||||
|
||||
if (linked_as_leader) {
|
||||
SetState(w, STATE_GROUP_LEADER);
|
||||
}
|
||||
@@ -430,7 +428,6 @@ void WriteThread::JoinBatchGroup(Writer* w) {
|
||||
TEST_SYNC_POINT_CALLBACK("WriteThread::JoinBatchGroup:BeganWaiting", w);
|
||||
AwaitState(w,
|
||||
STATE_GROUP_LEADER | STATE_MEMTABLE_WRITER_LEADER |
|
||||
STATE_PARALLEL_MEMTABLE_CALLER |
|
||||
STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED,
|
||||
&jbg_ctx);
|
||||
TEST_SYNC_POINT_CALLBACK("WriteThread::JoinBatchGroup:DoneWaiting", w);
|
||||
@@ -659,57 +656,12 @@ void WriteThread::ExitAsMemTableWriter(Writer* /*self*/,
|
||||
SetState(leader, STATE_COMPLETED);
|
||||
}
|
||||
|
||||
void WriteThread::SetMemWritersEachStride(Writer* w) {
|
||||
WriteGroup* write_group = w->write_group;
|
||||
Writer* last_writer = write_group->last_writer;
|
||||
|
||||
// The stride is the same for each writer in write_group, so w will
|
||||
// call the writers with the same number in write_group mod total size
|
||||
size_t stride = static_cast<size_t>(std::sqrt(write_group->size));
|
||||
size_t count = 0;
|
||||
while (w) {
|
||||
if (count++ % stride == 0) {
|
||||
SetState(w, STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
}
|
||||
w = (w == last_writer) ? nullptr : w->link_newer;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteThread::LaunchParallelMemTableWriters(WriteGroup* write_group) {
|
||||
assert(write_group != nullptr);
|
||||
size_t group_size = write_group->size;
|
||||
write_group->running.store(group_size);
|
||||
|
||||
// The minimum number to allow the group use parallel caller mode.
|
||||
// The number must no lower than 3;
|
||||
const size_t MinParallelSize = 20;
|
||||
|
||||
// The group_size is too small, and there is no need to have
|
||||
// the parallel partial callers.
|
||||
if (group_size < MinParallelSize) {
|
||||
for (auto w : *write_group) {
|
||||
SetState(w, STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
}
|
||||
return;
|
||||
write_group->running.store(write_group->size);
|
||||
for (auto w : *write_group) {
|
||||
SetState(w, STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
}
|
||||
|
||||
// The stride is equal to std::sqrt(group_size) which can minimize
|
||||
// the total number of leader SetSate.
|
||||
// Set the leader itself STATE_PARALLEL_MEMTABLE_WRITER, and set
|
||||
// (stride-1) writers to be STATE_PARALLEL_MEMTABLE_CALLER.
|
||||
size_t stride = static_cast<size_t>(std::sqrt(group_size));
|
||||
auto w = write_group->leader;
|
||||
SetState(w, STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
|
||||
for (size_t i = 1; i < stride; i++) {
|
||||
w = w->link_newer;
|
||||
SetState(w, STATE_PARALLEL_MEMTABLE_CALLER);
|
||||
}
|
||||
|
||||
// After setting all STATE_PARALLEL_MEMTABLE_CALLER, the leader also
|
||||
// does the job as STATE_PARALLEL_MEMTABLE_CALLER.
|
||||
w = w->link_newer;
|
||||
SetMemWritersEachStride(w);
|
||||
}
|
||||
|
||||
static WriteThread::AdaptationContext cpmtw_ctx(
|
||||
@@ -836,8 +788,8 @@ void WriteThread::ExitAsBatchGroupLeader(WriteGroup& write_group,
|
||||
}
|
||||
|
||||
AwaitState(leader,
|
||||
STATE_MEMTABLE_WRITER_LEADER | STATE_PARALLEL_MEMTABLE_CALLER |
|
||||
STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED,
|
||||
STATE_MEMTABLE_WRITER_LEADER | STATE_PARALLEL_MEMTABLE_WRITER |
|
||||
STATE_COMPLETED,
|
||||
&eabgl_ctx);
|
||||
} else {
|
||||
Writer* head = newest_writer_.load(std::memory_order_acquire);
|
||||
|
||||
+4
-36
@@ -22,9 +22,7 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/user_write_callback.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "util/aligned_storage.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -73,12 +71,6 @@ class WriteThread {
|
||||
// A state indicating that the thread may be waiting using StateMutex()
|
||||
// and StateCondVar()
|
||||
STATE_LOCKED_WAITING = 32,
|
||||
|
||||
// The state used to inform a waiting writer that it has become a
|
||||
// caller to call some other waiting writers to write to memtable
|
||||
// by calling SetMemWritersEachStride. After doing
|
||||
// this, it will also write to memtable.
|
||||
STATE_PARALLEL_MEMTABLE_CALLER = 64,
|
||||
};
|
||||
|
||||
struct Writer;
|
||||
@@ -135,7 +127,6 @@ class WriteThread {
|
||||
uint64_t log_used; // log number that this batch was inserted into
|
||||
uint64_t log_ref; // log number that memtable insert should reference
|
||||
WriteCallback* callback;
|
||||
UserWriteCallback* user_write_cb;
|
||||
bool made_waitable; // records lazy construction of mutex and cv
|
||||
std::atomic<uint8_t> state; // write under StateMutex() or pre-link
|
||||
WriteGroup* write_group;
|
||||
@@ -143,8 +134,8 @@ class WriteThread {
|
||||
Status status;
|
||||
Status callback_status; // status returned by callback->Callback()
|
||||
|
||||
aligned_storage<std::mutex>::type state_mutex_bytes;
|
||||
aligned_storage<std::condition_variable>::type state_cv_bytes;
|
||||
std::aligned_storage<sizeof(std::mutex)>::type state_mutex_bytes;
|
||||
std::aligned_storage<sizeof(std::condition_variable)>::type state_cv_bytes;
|
||||
Writer* link_older; // read/write only before linking, or as leader
|
||||
Writer* link_newer; // lazy, read/write only before linking, or as leader
|
||||
|
||||
@@ -162,7 +153,6 @@ class WriteThread {
|
||||
log_used(0),
|
||||
log_ref(0),
|
||||
callback(nullptr),
|
||||
user_write_cb(nullptr),
|
||||
made_waitable(false),
|
||||
state(STATE_INIT),
|
||||
write_group(nullptr),
|
||||
@@ -171,8 +161,8 @@ class WriteThread {
|
||||
link_newer(nullptr) {}
|
||||
|
||||
Writer(const WriteOptions& write_options, WriteBatch* _batch,
|
||||
WriteCallback* _callback, UserWriteCallback* _user_write_cb,
|
||||
uint64_t _log_ref, bool _disable_memtable, size_t _batch_cnt = 0,
|
||||
WriteCallback* _callback, uint64_t _log_ref, bool _disable_memtable,
|
||||
size_t _batch_cnt = 0,
|
||||
PreReleaseCallback* _pre_release_callback = nullptr,
|
||||
PostMemTableCallback* _post_memtable_callback = nullptr)
|
||||
: batch(_batch),
|
||||
@@ -190,7 +180,6 @@ class WriteThread {
|
||||
log_used(0),
|
||||
log_ref(_log_ref),
|
||||
callback(_callback),
|
||||
user_write_cb(_user_write_cb),
|
||||
made_waitable(false),
|
||||
state(STATE_INIT),
|
||||
write_group(nullptr),
|
||||
@@ -214,18 +203,6 @@ class WriteThread {
|
||||
return callback_status.ok();
|
||||
}
|
||||
|
||||
void CheckWriteEnqueuedCallback() {
|
||||
if (user_write_cb != nullptr) {
|
||||
user_write_cb->OnWriteEnqueued();
|
||||
}
|
||||
}
|
||||
|
||||
void CheckPostWalWriteCallback() {
|
||||
if (user_write_cb != nullptr) {
|
||||
user_write_cb->OnWalWriteFinish();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateMutex() {
|
||||
if (!made_waitable) {
|
||||
// Note that made_waitable is tracked separately from state
|
||||
@@ -346,19 +323,10 @@ class WriteThread {
|
||||
// Causes JoinBatchGroup to return STATE_PARALLEL_MEMTABLE_WRITER for all of
|
||||
// the non-leader members of this write batch group. Sets Writer::sequence
|
||||
// before waking them up.
|
||||
// If the size of write_group n is not small, the leader will call n^0.5
|
||||
// members to be PARALLEL_MEMTABLE_CALLER in the write_group to help to set
|
||||
// other's status parallel. This ensures that the cost to call SetState
|
||||
// sequentially does not exceed 2(n^0.5).
|
||||
//
|
||||
// WriteGroup* write_group: Extra state used to coordinate the parallel add
|
||||
void LaunchParallelMemTableWriters(WriteGroup* write_group);
|
||||
|
||||
// One of the every stride=N number writer in the WriteGroup are set to the
|
||||
// MemTableWriters, where N is equal to square of the total number of this
|
||||
// write_group, and all of these MemTableWriters will write to memtable.
|
||||
void SetMemWritersEachStride(Writer* w);
|
||||
|
||||
// Reports the completion of w's batch to the parallel group leader, and
|
||||
// waits for the rest of the parallel batch to complete. Returns true
|
||||
// if this thread is the last to complete, and hence should advance
|
||||
|
||||
@@ -4,7 +4,6 @@ add_executable(db_stress${ARTIFACT_SUFFIX}
|
||||
db_stress.cc
|
||||
db_stress_common.cc
|
||||
db_stress_driver.cc
|
||||
db_stress_filters.cc
|
||||
db_stress_gflags.cc
|
||||
db_stress_listener.cc
|
||||
db_stress_shared_state.cc
|
||||
|
||||
@@ -590,11 +590,6 @@ class BatchedOpsStressTest : public StressTest {
|
||||
// For half of the time, set the upper bound to the next prefix
|
||||
ub_slices[i] = upper_bounds[i];
|
||||
ro_copies[i].iterate_upper_bound = &(ub_slices[i]);
|
||||
if (FLAGS_use_sqfc_for_range_queries) {
|
||||
ro_copies[i].table_filter =
|
||||
sqfc_factory_->GetTableFilterForRangeQuery(prefix_slices[i],
|
||||
ub_slices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
iters[i].reset(db_->NewIterator(ro_copies[i], cfh));
|
||||
|
||||
@@ -73,13 +73,13 @@ class CfConsistencyStressTest : public StressTest {
|
||||
status = db_->Write(write_opts, &batch);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
auto num = static_cast<long>(rand_column_families.size());
|
||||
thread->stats.AddBytesForWrites(num, (sz + 1) * num);
|
||||
} else if (!IsErrorInjectedAndRetryable(status)) {
|
||||
if (!status.ok()) {
|
||||
fprintf(stderr, "multi put or merge error: %s\n",
|
||||
status.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
} else {
|
||||
auto num = static_cast<long>(rand_column_families.size());
|
||||
thread->stats.AddBytesForWrites(num, (sz + 1) * num);
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -96,11 +96,11 @@ class CfConsistencyStressTest : public StressTest {
|
||||
batch.Delete(cfh, key);
|
||||
}
|
||||
Status s = db_->Write(write_opts, &batch);
|
||||
if (s.ok()) {
|
||||
thread->stats.AddDeletes(static_cast<long>(rand_column_families.size()));
|
||||
} else if (!IsErrorInjectedAndRetryable(s)) {
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "multidel error: %s\n", s.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
} else {
|
||||
thread->stats.AddDeletes(static_cast<long>(rand_column_families.size()));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -125,12 +125,12 @@ class CfConsistencyStressTest : public StressTest {
|
||||
batch.DeleteRange(cfh, key, end_key);
|
||||
}
|
||||
Status s = db_->Write(write_opts, &batch);
|
||||
if (s.ok()) {
|
||||
thread->stats.AddRangeDeletions(
|
||||
static_cast<long>(rand_column_families.size()));
|
||||
} else if (!IsErrorInjectedAndRetryable(s)) {
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "multi del range error: %s\n", s.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
} else {
|
||||
thread->stats.AddRangeDeletions(
|
||||
static_cast<long>(rand_column_families.size()));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -170,15 +170,6 @@ class CfConsistencyStressTest : public StressTest {
|
||||
std::string value0;
|
||||
s = db_->Get(readoptionscopy, column_families_[rand_column_families[0]],
|
||||
key, &value0);
|
||||
|
||||
// Temporarily disable error injection for verification
|
||||
if (fault_fs_guard) {
|
||||
fault_fs_guard->DisableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kRead);
|
||||
fault_fs_guard->DisableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kMetadataRead);
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsNotFound()) {
|
||||
bool found = s.ok();
|
||||
for (size_t i = 1; i < rand_column_families.size(); i++) {
|
||||
@@ -223,13 +214,6 @@ class CfConsistencyStressTest : public StressTest {
|
||||
}
|
||||
}
|
||||
|
||||
// Enable back error injection disabled for verification
|
||||
if (fault_fs_guard) {
|
||||
fault_fs_guard->EnableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kRead);
|
||||
fault_fs_guard->EnableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kMetadataRead);
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
if (!is_consistent) {
|
||||
@@ -241,7 +225,7 @@ class CfConsistencyStressTest : public StressTest {
|
||||
thread->stats.AddGets(1, 1);
|
||||
} else if (s.IsNotFound()) {
|
||||
thread->stats.AddGets(1, 0);
|
||||
} else if (!IsErrorInjectedAndRetryable(s)) {
|
||||
} else {
|
||||
fprintf(stderr, "TestGet error: %s\n", s.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
}
|
||||
@@ -277,7 +261,7 @@ class CfConsistencyStressTest : public StressTest {
|
||||
} else if (s.IsNotFound()) {
|
||||
// not found case
|
||||
thread->stats.AddGets(1, 0);
|
||||
} else if (!IsErrorInjectedAndRetryable(s)) {
|
||||
} else {
|
||||
// errors case
|
||||
fprintf(stderr, "MultiGet error: %s\n", s.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
@@ -339,14 +323,6 @@ class CfConsistencyStressTest : public StressTest {
|
||||
column_families_[rand_column_families[0]], key,
|
||||
&cmp_result);
|
||||
|
||||
// Temporarily disable error injection for verification
|
||||
if (fault_fs_guard) {
|
||||
fault_fs_guard->DisableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kRead);
|
||||
fault_fs_guard->DisableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kMetadataRead);
|
||||
}
|
||||
|
||||
if (s.ok() || s.IsNotFound()) {
|
||||
const bool cmp_found = s.ok();
|
||||
|
||||
@@ -482,14 +458,6 @@ class CfConsistencyStressTest : public StressTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable back error injection disabled for verification
|
||||
if (fault_fs_guard) {
|
||||
fault_fs_guard->EnableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kRead);
|
||||
fault_fs_guard->EnableThreadLocalErrorInjection(
|
||||
FaultInjectionIOType::kMetadataRead);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_consistent) {
|
||||
@@ -501,7 +469,7 @@ class CfConsistencyStressTest : public StressTest {
|
||||
thread->stats.AddGets(1, 1);
|
||||
} else if (s.IsNotFound()) {
|
||||
thread->stats.AddGets(1, 0);
|
||||
} else if (!IsErrorInjectedAndRetryable(s)) {
|
||||
} else {
|
||||
fprintf(stderr, "TestGetEntity error: %s\n", s.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
}
|
||||
@@ -572,9 +540,7 @@ class CfConsistencyStressTest : public StressTest {
|
||||
for (size_t j = 0; j < num_cfs; ++j) {
|
||||
const Status& s = result[j].status();
|
||||
const WideColumns& columns = result[j].columns();
|
||||
if (!s.ok() && IsErrorInjectedAndRetryable(s)) {
|
||||
break;
|
||||
} else if (!s.ok() && !s.IsNotFound()) {
|
||||
if (!s.ok() && !s.IsNotFound()) {
|
||||
fprintf(stderr, "TestMultiGetEntity (AttributeGroup) error: %s\n",
|
||||
s.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
@@ -679,9 +645,7 @@ class CfConsistencyStressTest : public StressTest {
|
||||
const Status& s = statuses[j];
|
||||
const WideColumns& columns = results[j].columns();
|
||||
|
||||
if (!s.ok() && IsErrorInjectedAndRetryable(s)) {
|
||||
break;
|
||||
} else if (!s.ok() && !s.IsNotFound()) {
|
||||
if (!s.ok() && !s.IsNotFound()) {
|
||||
fprintf(stderr, "TestMultiGetEntity error: %s\n",
|
||||
s.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
@@ -782,10 +746,6 @@ class CfConsistencyStressTest : public StressTest {
|
||||
if (GetNextPrefix(prefix, &upper_bound) && thread->rand.OneIn(2)) {
|
||||
ub_slice = Slice(upper_bound);
|
||||
ro_copy.iterate_upper_bound = &ub_slice;
|
||||
if (FLAGS_use_sqfc_for_range_queries) {
|
||||
ro_copy.table_filter =
|
||||
sqfc_factory_->GetTableFilterForRangeQuery(prefix, ub_slice);
|
||||
}
|
||||
}
|
||||
|
||||
ColumnFamilyHandle* const cfh =
|
||||
@@ -816,7 +776,7 @@ class CfConsistencyStressTest : public StressTest {
|
||||
s = iter->status();
|
||||
}
|
||||
|
||||
if (!s.ok() && !IsErrorInjectedAndRetryable(s)) {
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "TestPrefixScan error: %s\n", s.ToString().c_str());
|
||||
thread->stats.AddErrors(1);
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
ROCKSDB_NAMESPACE::Env* db_stress_listener_env = nullptr;
|
||||
ROCKSDB_NAMESPACE::Env* db_stress_env = nullptr;
|
||||
// If non-null, injects read error at a rate specified by the
|
||||
// read_fault_one_in or write_fault_one_in flag
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::FaultInjectionTestFS> fault_fs_guard;
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::SecondaryCache> compressed_secondary_cache;
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::Cache> block_cache;
|
||||
@@ -198,7 +200,7 @@ void CompressedCacheSetCapacityThread(void* v) {
|
||||
// Lower by upto 50% of usable block cache capacity
|
||||
adjustment = (adjustment * thread->rand.Uniform(50)) / 100;
|
||||
block_cache->SetCapacity(capacity - adjustment);
|
||||
fprintf(stdout, "New cache capacity = %zu\n",
|
||||
fprintf(stdout, "New cache capacity = %lu\n",
|
||||
block_cache->GetCapacity());
|
||||
db_stress_env->SleepForMicroseconds(10 * 1000 * 1000);
|
||||
block_cache->SetCapacity(capacity);
|
||||
@@ -393,16 +395,6 @@ bool VerifyWideColumns(const WideColumns& columns) {
|
||||
return VerifyWideColumns(value_of_default, columns);
|
||||
}
|
||||
|
||||
bool VerifyIteratorAttributeGroups(
|
||||
const IteratorAttributeGroups& attribute_groups) {
|
||||
for (const auto& attribute_group : attribute_groups) {
|
||||
if (!VerifyWideColumns(attribute_group.columns())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetNowNanos() {
|
||||
uint64_t t = db_stress_env->NowNanos();
|
||||
std::string ret;
|
||||
|
||||
@@ -89,6 +89,7 @@ DECLARE_string(options_file);
|
||||
DECLARE_int64(active_width);
|
||||
DECLARE_bool(test_batches_snapshots);
|
||||
DECLARE_bool(atomic_flush);
|
||||
DECLARE_int32(manual_wal_flush_one_in);
|
||||
DECLARE_int32(lock_wal_one_in);
|
||||
DECLARE_bool(test_cf_consistency);
|
||||
DECLARE_bool(test_multi_ops_txns);
|
||||
@@ -135,7 +136,6 @@ DECLARE_int32(universal_size_ratio);
|
||||
DECLARE_int32(universal_min_merge_width);
|
||||
DECLARE_int32(universal_max_merge_width);
|
||||
DECLARE_int32(universal_max_size_amplification_percent);
|
||||
DECLARE_int32(universal_max_read_amp);
|
||||
DECLARE_int32(clear_column_family_one_in);
|
||||
DECLARE_int32(get_live_files_apis_one_in);
|
||||
DECLARE_int32(get_all_column_family_metadata_one_in);
|
||||
@@ -169,9 +169,6 @@ DECLARE_int32(bloom_before_level);
|
||||
DECLARE_bool(partition_filters);
|
||||
DECLARE_bool(optimize_filters_for_memory);
|
||||
DECLARE_bool(detect_filter_construct_corruption);
|
||||
DECLARE_string(sqfc_name);
|
||||
DECLARE_uint32(sqfc_version);
|
||||
DECLARE_bool(use_sqfc_for_range_queries);
|
||||
DECLARE_int32(index_type);
|
||||
DECLARE_int32(data_block_index_type);
|
||||
DECLARE_string(db);
|
||||
@@ -192,6 +189,7 @@ DECLARE_uint64(bytes_per_sync);
|
||||
DECLARE_uint64(wal_bytes_per_sync);
|
||||
DECLARE_int32(kill_random_test);
|
||||
DECLARE_string(kill_exclude_prefixes);
|
||||
DECLARE_bool(disable_wal);
|
||||
DECLARE_uint64(recycle_log_file_num);
|
||||
DECLARE_int64(target_file_size_base);
|
||||
DECLARE_int32(target_file_size_multiplier);
|
||||
@@ -256,7 +254,6 @@ DECLARE_int32(prefix_size);
|
||||
DECLARE_bool(use_merge);
|
||||
DECLARE_uint32(use_put_entity_one_in);
|
||||
DECLARE_bool(use_attribute_group);
|
||||
DECLARE_bool(use_multi_cf_iterator);
|
||||
DECLARE_bool(use_full_merge_v1);
|
||||
DECLARE_int32(sync_wal_one_in);
|
||||
DECLARE_bool(avoid_unnecessary_blocking_io);
|
||||
@@ -317,6 +314,8 @@ DECLARE_int32(blob_cache_numshardbits);
|
||||
DECLARE_int32(prepopulate_blob_cache);
|
||||
|
||||
DECLARE_int32(approximate_size_one_in);
|
||||
DECLARE_bool(sync_fault_injection);
|
||||
|
||||
DECLARE_bool(best_efforts_recovery);
|
||||
DECLARE_bool(skip_verifydb);
|
||||
DECLARE_bool(enable_compaction_filter);
|
||||
@@ -416,7 +415,6 @@ DECLARE_bool(enable_memtable_insert_with_hint_prefix_extractor);
|
||||
DECLARE_bool(check_multiget_consistency);
|
||||
DECLARE_bool(check_multiget_entity_consistency);
|
||||
DECLARE_bool(inplace_update_support);
|
||||
DECLARE_uint32(uncache_aggressiveness);
|
||||
|
||||
constexpr long KB = 1024;
|
||||
constexpr int kRandomValueMaxFactor = 3;
|
||||
@@ -763,8 +761,6 @@ WideColumns GenerateExpectedWideColumns(uint32_t value_base,
|
||||
const Slice& slice);
|
||||
bool VerifyWideColumns(const Slice& value, const WideColumns& columns);
|
||||
bool VerifyWideColumns(const WideColumns& columns);
|
||||
bool VerifyIteratorAttributeGroups(
|
||||
const IteratorAttributeGroups& attribute_groups);
|
||||
|
||||
AttributeGroups GenerateAttributeGroups(
|
||||
const std::vector<ColumnFamilyHandle*>& cfhs, uint32_t value_base,
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
//
|
||||
|
||||
#include "db_stress_tool/db_stress_shared_state.h"
|
||||
#ifdef GFLAGS
|
||||
#include "db_stress_tool/db_stress_common.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
@@ -82,6 +81,31 @@ bool RunStressTestImpl(SharedState* shared) {
|
||||
stress->InitDb(shared);
|
||||
stress->FinishInitDb(shared);
|
||||
|
||||
if (FLAGS_write_fault_one_in) {
|
||||
if (!FLAGS_sync_fault_injection) {
|
||||
// unsynced WAL loss is not supported without sync_fault_injection
|
||||
fault_fs_guard->SetDirectWritableTypes({kWalFile});
|
||||
}
|
||||
IOStatus error_msg;
|
||||
if (FLAGS_inject_error_severity <= 1 || FLAGS_inject_error_severity > 2) {
|
||||
error_msg = IOStatus::IOError("Retryable injected write error");
|
||||
error_msg.SetRetryable(true);
|
||||
} else if (FLAGS_inject_error_severity == 2) {
|
||||
error_msg = IOStatus::IOError("Fatal injected write error");
|
||||
error_msg.SetDataLoss(true);
|
||||
}
|
||||
// TODO: inject write error for other file types including
|
||||
// MANIFEST, CURRENT, and WAL files.
|
||||
fault_fs_guard->SetRandomWriteError(
|
||||
shared->GetSeed(), FLAGS_write_fault_one_in, error_msg,
|
||||
/*inject_for_all_file_types=*/false, {FileType::kTableFile});
|
||||
fault_fs_guard->SetFilesystemDirectWritable(false);
|
||||
fault_fs_guard->EnableWriteErrorInjection();
|
||||
}
|
||||
if (FLAGS_sync_fault_injection) {
|
||||
fault_fs_guard->SetFilesystemDirectWritable(false);
|
||||
}
|
||||
|
||||
uint32_t n = FLAGS_threads;
|
||||
uint64_t now = clock->NowMicros();
|
||||
fprintf(stdout, "%s Initializing worker threads\n",
|
||||
@@ -158,15 +182,6 @@ bool RunStressTestImpl(SharedState* shared) {
|
||||
if (!FLAGS_expected_values_dir.empty()) {
|
||||
stress->TrackExpectedState(shared);
|
||||
}
|
||||
|
||||
if (FLAGS_sync_fault_injection || FLAGS_write_fault_one_in > 0) {
|
||||
fault_fs_guard->SetFilesystemDirectWritable(false);
|
||||
fault_fs_guard->SetInjectUnsyncedDataLoss(FLAGS_sync_fault_injection);
|
||||
if (FLAGS_exclude_wal_from_write_fault_injection) {
|
||||
fault_fs_guard->SetFileTypesExcludedFromWriteFaultInjection(
|
||||
{FileType::kWalFile});
|
||||
}
|
||||
}
|
||||
now = clock->NowMicros();
|
||||
fprintf(stdout, "%s Starting database operations\n",
|
||||
clock->TimeToString(now / 1000000).c_str());
|
||||
|
||||
@@ -13,20 +13,6 @@
|
||||
#include "monitoring/thread_status_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
void CheckIOActivity(const IOOptions& options) {
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#else
|
||||
(void)options;
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
class DbStressRandomAccessFileWrapper : public FSRandomAccessFileOwnerWrapper {
|
||||
public:
|
||||
explicit DbStressRandomAccessFileWrapper(
|
||||
@@ -97,67 +83,144 @@ class DbStressWritableFileWrapper : public FSWritableFileOwnerWrapper {
|
||||
|
||||
IOStatus Append(const Slice& data, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->Append(data, options, dbg);
|
||||
}
|
||||
IOStatus Append(const Slice& data, const IOOptions& options,
|
||||
const DataVerificationInfo& verification_info,
|
||||
IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->Append(data, options, verification_info, dbg);
|
||||
}
|
||||
IOStatus PositionedAppend(const Slice& data, uint64_t offset,
|
||||
const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->PositionedAppend(data, offset, options, dbg);
|
||||
}
|
||||
IOStatus PositionedAppend(const Slice& data, uint64_t offset,
|
||||
const IOOptions& options,
|
||||
const DataVerificationInfo& verification_info,
|
||||
IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->PositionedAppend(data, offset, options, verification_info,
|
||||
dbg);
|
||||
}
|
||||
|
||||
IOStatus Truncate(uint64_t size, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->Truncate(size, options, dbg);
|
||||
}
|
||||
|
||||
IOStatus Close(const IOOptions& options, IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->Close(options, dbg);
|
||||
}
|
||||
|
||||
IOStatus Flush(const IOOptions& options, IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->Flush(options, dbg);
|
||||
}
|
||||
|
||||
IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->Sync(options, dbg);
|
||||
}
|
||||
|
||||
IOStatus Fsync(const IOOptions& options, IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->Fsync(options, dbg);
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_FALLOCATE_PRESENT
|
||||
IOStatus Allocate(uint64_t offset, uint64_t len, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->Allocate(offset, len, options, dbg);
|
||||
}
|
||||
#endif
|
||||
|
||||
IOStatus RangeSync(uint64_t offset, uint64_t nbytes, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
CheckIOActivity(options);
|
||||
#ifndef NDEBUG
|
||||
const ThreadStatus::OperationType thread_op =
|
||||
ThreadStatusUtil::GetThreadOperation();
|
||||
Env::IOActivity io_activity =
|
||||
ThreadStatusUtil::TEST_GetExpectedIOActivity(thread_op);
|
||||
assert(io_activity == Env::IOActivity::kUnknown ||
|
||||
io_activity == options.io_activity);
|
||||
#endif
|
||||
return target()->RangeSync(offset, nbytes, options, dbg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db_stress_tool/db_stress_filters.h"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
#ifdef GFLAGS
|
||||
|
||||
using experimental::KeySegmentsExtractor;
|
||||
using experimental::MakeSharedBytewiseMinMaxSQFC;
|
||||
using experimental::SelectKeySegment;
|
||||
using experimental::SstQueryFilterConfigs;
|
||||
using experimental::SstQueryFilterConfigsManager;
|
||||
|
||||
namespace {
|
||||
class VariableWidthExtractor : public KeySegmentsExtractor {
|
||||
public:
|
||||
const char* Name() const override { return "VariableWidthExtractor"; }
|
||||
|
||||
void Extract(const Slice& key_or_bound, KeyKind /*kind*/,
|
||||
Result* result) const override {
|
||||
uint32_t len = static_cast<uint32_t>(key_or_bound.size());
|
||||
// This uses as delimiter any zero byte that follows a non-zero byte.
|
||||
// And in accordance with best practice, the delimiter is part of the
|
||||
// segment leading up to it.
|
||||
bool prev_non_zero = false;
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
if ((prev_non_zero && key_or_bound[i] == 0) || i + 1 == len) {
|
||||
result->segment_ends.push_back(i + 1);
|
||||
}
|
||||
prev_non_zero = key_or_bound[i] != 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto kVariableWidthExtractor = std::make_shared<VariableWidthExtractor>();
|
||||
class FixedWidthExtractor : public KeySegmentsExtractor {
|
||||
public:
|
||||
const char* Name() const override { return "FixedWidthExtractor"; }
|
||||
|
||||
void Extract(const Slice& key_or_bound, KeyKind /*kind*/,
|
||||
Result* result) const override {
|
||||
uint32_t len = static_cast<uint32_t>(key_or_bound.size());
|
||||
// Fixed 8-byte segments, with any leftovers going into another
|
||||
// segment.
|
||||
uint32_t i = 0;
|
||||
while (i + 8 <= len) {
|
||||
i += 8;
|
||||
result->segment_ends.push_back(i);
|
||||
}
|
||||
if (i < len) {
|
||||
result->segment_ends.push_back(len);
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto kFixedWidthExtractor = std::make_shared<FixedWidthExtractor>();
|
||||
// NOTE: MinMax filter on segment 0 is not normally useful because of metadata
|
||||
// on smallest and largest key for each SST file. But it doesn't hurt to test.
|
||||
const auto kFilter0 = MakeSharedBytewiseMinMaxSQFC(SelectKeySegment(0));
|
||||
const auto kFilter1 = MakeSharedBytewiseMinMaxSQFC(SelectKeySegment(1));
|
||||
const auto kFilter2 = MakeSharedBytewiseMinMaxSQFC(SelectKeySegment(2));
|
||||
const auto kFilter3 = MakeSharedBytewiseMinMaxSQFC(SelectKeySegment(3));
|
||||
const SstQueryFilterConfigs fooConfigs1{{kFilter0, kFilter2},
|
||||
kVariableWidthExtractor};
|
||||
const SstQueryFilterConfigs fooConfigs2{{kFilter1, kFilter3},
|
||||
kVariableWidthExtractor};
|
||||
const SstQueryFilterConfigs barConfigs2{{kFilter1, kFilter2},
|
||||
kFixedWidthExtractor};
|
||||
const SstQueryFilterConfigsManager::Data data = {
|
||||
{1, {{"foo", fooConfigs1}}},
|
||||
{2, {{"foo", fooConfigs2}, {"bar", barConfigs2}}},
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SstQueryFilterConfigsManager& DbStressSqfcManager() {
|
||||
std::once_flag flag;
|
||||
static std::shared_ptr<SstQueryFilterConfigsManager> mgr;
|
||||
std::call_once(flag, []() {
|
||||
Status s = SstQueryFilterConfigsManager::MakeShared(data, &mgr);
|
||||
assert(s.ok());
|
||||
assert(mgr);
|
||||
});
|
||||
return *mgr;
|
||||
}
|
||||
|
||||
#endif // GFLAGS
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// 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).
|
||||
|
||||
#ifdef GFLAGS
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/experimental.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
experimental::SstQueryFilterConfigsManager& DbStressSqfcManager();
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // GFLAGS
|
||||
@@ -306,9 +306,6 @@ DEFINE_int32(universal_max_merge_width, 0,
|
||||
DEFINE_int32(universal_max_size_amplification_percent, 0,
|
||||
"The max size amplification for universal style compaction");
|
||||
|
||||
DEFINE_int32(universal_max_read_amp, -1,
|
||||
"The limit on the number of sorted runs");
|
||||
|
||||
DEFINE_int32(clear_column_family_one_in, 1000000,
|
||||
"With a chance of 1/N, delete a column family and then recreate "
|
||||
"it again. If N == 0, never drop/create column families. "
|
||||
@@ -582,16 +579,6 @@ DEFINE_bool(
|
||||
.detect_filter_construct_corruption,
|
||||
"Detect corruption during new Bloom Filter and Ribbon Filter construction");
|
||||
|
||||
DEFINE_string(sqfc_name, "foo",
|
||||
"Config name to select from SstQueryFilterConfigsManager.");
|
||||
|
||||
DEFINE_uint32(sqfc_version, 0,
|
||||
"User-defined filtering version to select from "
|
||||
"SstQueryFilterConfigsManager. 0 = disable writing filters");
|
||||
|
||||
DEFINE_bool(use_sqfc_for_range_queries, true,
|
||||
"Apply SstQueryFilters to range queries");
|
||||
|
||||
DEFINE_int32(
|
||||
index_type,
|
||||
static_cast<int32_t>(
|
||||
@@ -968,9 +955,6 @@ DEFINE_uint32(use_put_entity_one_in, 0,
|
||||
DEFINE_bool(use_attribute_group, false,
|
||||
"If set, use the attribute_group API to put/get entities");
|
||||
|
||||
DEFINE_bool(use_multi_cf_iterator, false,
|
||||
"If set, use the multi_cf_iterator for TestIterate");
|
||||
|
||||
DEFINE_bool(use_full_merge_v1, false,
|
||||
"On true, use a merge operator that implement the deprecated "
|
||||
"version of FullMerge");
|
||||
@@ -1028,14 +1012,6 @@ DEFINE_int32(approximate_size_one_in, 64,
|
||||
DEFINE_int32(read_fault_one_in, 1000,
|
||||
"On non-zero, enables fault injection on read");
|
||||
|
||||
DEFINE_bool(error_recovery_with_no_fault_injection, false,
|
||||
"If true, error recovery will be done without fault injection if "
|
||||
"fault injection is enabled");
|
||||
|
||||
DEFINE_int32(metadata_read_fault_one_in, 1000,
|
||||
"On non-zero, enables fault injection on metadata read (i.e, "
|
||||
"directory and file metadata read)");
|
||||
|
||||
DEFINE_int32(get_property_one_in, 1000,
|
||||
"If non-zero, then DB::GetProperty() will be called to get various"
|
||||
" properties for every N ops on average. 0 indicates that"
|
||||
@@ -1095,13 +1071,6 @@ DEFINE_int32(write_fault_one_in, 0,
|
||||
"On non-zero, enables fault injection on write. Currently only"
|
||||
"injects write error when writing to SST files.");
|
||||
|
||||
DEFINE_bool(exclude_wal_from_write_fault_injection, false,
|
||||
"If true, we won't inject write fault when writing to WAL file");
|
||||
|
||||
DEFINE_int32(metadata_write_fault_one_in, 1000,
|
||||
"On non-zero, enables fault injection on metadata write (i.e, "
|
||||
"directory and file metadata write)");
|
||||
|
||||
DEFINE_uint64(user_timestamp_size, 0,
|
||||
"Number of bytes for a user-defined timestamp. Currently, only "
|
||||
"8-byte is supported");
|
||||
@@ -1110,11 +1079,6 @@ DEFINE_bool(persist_user_defined_timestamps, true,
|
||||
"Flag to indicate whether user-defined timestamps will be persisted"
|
||||
" during Flush");
|
||||
|
||||
DEFINE_int32(open_metadata_read_fault_one_in, 0,
|
||||
"On non-zero, enables fault injection on file metadata read (i.e, "
|
||||
"directory and file metadata read)"
|
||||
"during DB reopen.");
|
||||
|
||||
DEFINE_int32(open_metadata_write_fault_one_in, 0,
|
||||
"On non-zero, enables fault injection on file metadata write "
|
||||
"during DB reopen.");
|
||||
@@ -1436,11 +1400,4 @@ DEFINE_bool(check_multiget_entity_consistency, true,
|
||||
DEFINE_bool(inplace_update_support,
|
||||
ROCKSDB_NAMESPACE::Options().inplace_update_support,
|
||||
"Options.inplace_update_support");
|
||||
|
||||
DEFINE_uint32(uncache_aggressiveness,
|
||||
ROCKSDB_NAMESPACE::ColumnFamilyOptions().uncache_aggressiveness,
|
||||
"Aggressiveness of erasing cache entries that are likely "
|
||||
"obsolete. 0 = disabled, 1 = minimum, 100 = moderate, 10000 = "
|
||||
"normal max");
|
||||
|
||||
#endif // GFLAGS
|
||||
Loaded 100 of 232 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user