Compare commits

..
8 Commits
Author SHA1 Message Date
Andrew Kryczka c5201abc4a update HISTORY.md and version.h for 9.3.1 2024-06-04 11:43:02 -07:00
Fan Zhang(DevX) 4eda1b79fa add export_file to rockdb TARGETS generator and re-gen
Summary:
we are converting the implicit loads to explicit loads, then remove the hidden loads in fbcode macroes.
details see https://fb.workplace.com/groups/devx.build.bffs/permalink/7481848805183560/

Reviewed By: JakobDegen

Differential Revision: D57800976

fbshipit-source-id: a893aa2aa9237704ba9eb998cba210222c95dd2f
2024-06-04 11:42:26 -07:00
Andrew Kryczka 030f7c4a7b include bug fixes in 9.3.0 from after the branch cut until now 2024-06-02 16:52:07 -07:00
anand76 6c60c8f3f3 Fix stale memory access with FSBuffer and tiered sec cache (#12712)
Summary:
A `BlockBasedTable` with `TieredSecondaryCache` containing a NVM cache inserts blocks  into the compressed cache and the corresponding compressed block into the NVM cache.  The `BlockFetcher` is used to get the uncompressed and compressed blocks by calling `ReadBlockContents()` and `GetUncompressedBlock()` respectively. If the file system supports FSBuffer (i.e returning a FS allocated buffer rather than caller provided), that buffer gets freed between the two calls. This PR fixes it by making the FSBuffer unique pointer a member rather than local variable.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12712

Test Plan:
1. Add a unit test
2. Release validation stress test

Reviewed By: jaykorean

Differential Revision: D57974026

Pulled By: anand1976

fbshipit-source-id: cfa895914e74b4f628413b40e6e39d8d8e5286bd
2024-06-02 16:50:41 -07:00
Andrew Kryczka 2660754dbc Fix recycled WAL detection when wal_compression is enabled (#12643)
Summary:
I think the point of the `if (end_of_buffer_offset_ - buffer_.size() == 0)` was to only set `recycled_` when the first record was read. However, the condition was false when reading the first record when the WAL began with a  `kSetCompressionType` record because we had already dropped the `kSetCompressionType` record from `buffer_`. To fix this, I used `first_record_read_` instead.

Also, it was pretty confusing to treat the WAL as non-recycled when a recyclable record first appeared in a non-first record. I changed it to return an error if that happens.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12643

Reviewed By: hx235

Differential Revision: D57238099

Pulled By: ajkr

fbshipit-source-id: e20a2a0c9cf0c9510a7b6af463650a05d559239e
2024-06-02 16:50:29 -07:00
Peter Dillinger 94a867c845 Disallow memtable flush and sst ingest while WAL is locked (#12652)
Summary:
We recently noticed that some memtable flushed and file
ingestions could proceed during LockWAL, in violation of its stated
contract. (Note: we aren't 100% sure its actually needed by MySQL, but
we want it to be in a clean state nonetheless.)

Despite earlier skepticism that this could be done safely (https://github.com/facebook/rocksdb/issues/12666), I
found a place to wait to wait for LockWAL to be cleared before allowing
these operations to proceed: WaitForPendingWrites()

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12652

Test Plan:
Added to unit tests. Extended how db_stress validates LockWAL
and re-enabled combination of ingestion and LockWAL in crash test, in
follow-up to https://github.com/facebook/rocksdb/issues/12642

Ran blackbox_crash_test for a long while with relevant features
amplified.

Suggested follow-up: fix FaultInjectionTestFS to report file sizes
consistent with what the user has requested to be flushed.

Reviewed By: jowlyzhang

Differential Revision: D57622142

Pulled By: pdillinger

fbshipit-source-id: aef265fce69465618974b4ec47f4636257c676ce
2024-06-02 16:50:15 -07:00
Hui Xiao 1b5474c8e1 Fix unreleased bug fix .md name (#12672)
Summary:
Context/Summary: as above

Pull Request resolved: https://github.com/facebook/rocksdb/pull/12672

Test Plan: no code change

Reviewed By: ajkr

Differential Revision: D57505136

Pulled By: hx235

fbshipit-source-id: 0e216dc5974e9be10027b444eb6b4034f679dfd8
2024-06-02 16:49:53 -07:00
Andrew Kryczka b135fa7a8b 9.3.0 release notes 2024-06-02 16:21:22 -07:00
208 changed files with 2540 additions and 8189 deletions

No files matched your search

+4 -16
View File
@@ -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' }}
-3
View File
@@ -47,9 +47,6 @@ package/
unity.a
tags
etags
GPATH
GRTAGS
GTAGS
rocksdb_dump
rocksdb_undump
db_test2
+5 -16
View File
@@ -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
+2 -27
View File
@@ -1,34 +1,9 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 9.4.1 (08/02/2024)
## 9.3.1 (05/25/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`.
## 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
+12 -37
View File
@@ -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)
@@ -2484,15 +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 17054c85c2dee0e84cbee7ef28d7bae7d0b1630e
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: this hack is only needed if building target build_folly in the future
perl -pi -e 's,https://github.com/jedisct1/libsodium/releases/download/1.0.17/libsodium-1.0.17.tar.gz,https://download.libsodium.org/libsodium/releases/old/unsupported/libsodium-1.0.17.tar.gz,' third-party/folly/build/fbcode_builder/manifests/libsodium
@# 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))
@@ -2504,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
-10
View File
@@ -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"],
-4
View File
@@ -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"]
+1 -7
View File
@@ -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
-58
View File
@@ -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) {
+2 -2
View File
@@ -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
View File
@@ -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,
+6 -99
View File
@@ -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;
@@ -547,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());
@@ -908,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;
@@ -939,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;
@@ -975,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;
@@ -1009,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;
@@ -1041,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;
@@ -1079,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;
}
@@ -1103,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;
@@ -1123,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;
}
@@ -1145,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;
}
@@ -1393,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,
@@ -1979,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;
}
@@ -1994,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(); }
@@ -2268,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;
@@ -3959,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;
@@ -4359,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
@@ -5837,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;
}
@@ -5880,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;
@@ -6675,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 -101
View File
@@ -798,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);
@@ -823,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);
}
@@ -846,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);
@@ -862,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);
@@ -879,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");
@@ -2234,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));
-28
View File
@@ -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();
-17
View File
@@ -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
View File
@@ -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 = &timestamp;
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) {
+4 -4
View File
@@ -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),
-2
View File
@@ -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(),
+25 -16
View File
@@ -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.
@@ -1282,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_,
@@ -1957,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);
-2
View File
@@ -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.
-112
View File
@@ -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) {
+21 -72
View File
@@ -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_);
}
}
}
-77
View File
@@ -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));
-3
View File
@@ -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
View File
@@ -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,
+21 -74
View File
@@ -91,7 +91,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
return Status::OK();
}
Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
// Record tracked WALs as a (minimum) cross-check for directory scan
std::vector<uint64_t> required_by_manifest;
@@ -152,11 +152,11 @@ Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
}
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) {
@@ -177,7 +177,7 @@ Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
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_);
@@ -198,14 +198,13 @@ 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.
// If the outstanding log files are small, we skip the flush.
s = GetSortedWalFiles(live_wal_files);
if (!s.ok()) {
@@ -317,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();
@@ -382,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();
}
@@ -397,51 +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 = 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();
@@ -450,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
View File
@@ -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]);
+79 -170
View File
@@ -17,7 +17,6 @@
#include <cstdio>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <stdexcept>
@@ -638,7 +637,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
@@ -1550,132 +1549,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 +1616,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,
@@ -1830,19 +1758,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;
}
@@ -4544,11 +4469,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 +4484,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;
}
@@ -5241,14 +5163,6 @@ Status DestroyDB(const std::string& dbname, const Options& options,
Env* env = soptions.env;
std::vector<std::string> filenames;
bool wal_in_db_path = soptions.IsWalDirSameAsDBPath();
auto sfm = static_cast_with_check<SstFileManagerImpl>(
options.sst_file_manager.get());
// Allocate a separate trash bucket to be used by all the to be deleted
// files, so we can later wait for this bucket to be empty before return.
std::optional<int32_t> bucket;
if (sfm) {
bucket = sfm->NewTrashBucket();
}
// Reset the logger because it holds a handle to the
// log file and prevents cleanup and directory removal
@@ -5260,7 +5174,6 @@ Status DestroyDB(const std::string& dbname, const Options& options,
/*IODebugContext*=*/nullptr)
.PermitUncheckedError();
std::set<std::string> paths_to_delete;
FileLock* lock;
const std::string lockname = LockFileName(dbname);
Status result = env->LockFile(lockname, &lock);
@@ -5277,9 +5190,10 @@ Status DestroyDB(const std::string& dbname, const Options& options,
del = DestroyDB(path_to_delete, options);
} else if (type == kTableFile || type == kWalFile ||
type == kBlobFile) {
del = DeleteUnaccountedDBFile(&soptions, path_to_delete, dbname,
/*force_bg=*/false,
/*force_fg=*/false, bucket);
del = DeleteDBFile(
&soptions, path_to_delete, dbname,
/*force_bg=*/false,
/*force_fg=*/(type == kWalFile) ? !wal_in_db_path : false);
} else {
del = env->DeleteFile(path_to_delete);
}
@@ -5288,7 +5202,6 @@ Status DestroyDB(const std::string& dbname, const Options& options,
}
}
}
paths_to_delete.insert(dbname);
std::set<std::string> paths;
for (const DbPath& db_path : options.db_paths) {
@@ -5310,19 +5223,18 @@ Status DestroyDB(const std::string& dbname, const Options& options,
(type == kTableFile ||
type == kBlobFile)) { // Lock file will be deleted at end
std::string file_path = path + "/" + fname;
Status del = DeleteUnaccountedDBFile(&soptions, file_path, dbname,
/*force_bg=*/false,
/*force_fg=*/false, bucket);
Status del = DeleteDBFile(&soptions, file_path, dbname,
/*force_bg=*/false, /*force_fg=*/false);
if (!del.ok() && result.ok()) {
result = del;
}
}
}
// TODO: Should we return an error if we cannot delete the directory?
env->DeleteDir(path).PermitUncheckedError();
}
}
paths_to_delete.merge(paths);
std::vector<std::string> walDirFiles;
std::string archivedir = ArchivalDirectory(dbname);
bool wal_dir_exists = false;
@@ -5346,49 +5258,46 @@ Status DestroyDB(const std::string& dbname, const Options& options,
// Delete archival files.
for (const auto& file : archiveFiles) {
if (ParseFileName(file, &number, &type) && type == kWalFile) {
Status del = DeleteUnaccountedDBFile(
&soptions, archivedir + "/" + file, archivedir,
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path, bucket);
Status del =
DeleteDBFile(&soptions, archivedir + "/" + file, archivedir,
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
if (!del.ok() && result.ok()) {
result = del;
}
}
}
paths_to_delete.insert(archivedir);
// Ignore error in case dir contains other files
env->DeleteDir(archivedir).PermitUncheckedError();
}
// Delete log files in the WAL dir
if (wal_dir_exists) {
for (const auto& file : walDirFiles) {
if (ParseFileName(file, &number, &type) && type == kWalFile) {
Status del = DeleteUnaccountedDBFile(
&soptions, LogFileName(soptions.wal_dir, number),
soptions.wal_dir, /*force_bg=*/false,
/*force_fg=*/!wal_in_db_path, bucket);
Status del =
DeleteDBFile(&soptions, LogFileName(soptions.wal_dir, number),
soptions.wal_dir, /*force_bg=*/false,
/*force_fg=*/!wal_in_db_path);
if (!del.ok() && result.ok()) {
result = del;
}
}
}
paths_to_delete.insert(soptions.wal_dir);
// Ignore error in case dir contains other files
env->DeleteDir(soptions.wal_dir).PermitUncheckedError();
}
// Ignore error since state is already gone
env->UnlockFile(lock).PermitUncheckedError();
env->DeleteFile(lockname).PermitUncheckedError();
// Make sure trash files are all cleared before return.
if (sfm && bucket.has_value()) {
sfm->WaitForEmptyTrashBucket(bucket.value());
}
// sst_file_manager holds a ref to the logger. Make sure the logger is
// gone before trying to remove the directory.
soptions.sst_file_manager.reset();
// Ignore error in case dir contains other files
for (const auto& path_to_delete : paths_to_delete) {
env->DeleteDir(path_to_delete).PermitUncheckedError();
}
env->DeleteDir(dbname).PermitUncheckedError();
;
}
return result;
}
+15 -53
View File
@@ -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,14 +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;
// 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(
@@ -700,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
@@ -1224,8 +1211,6 @@ class DBImpl : public DB {
return logs_.back().number;
}
void TEST_DeleteObsoleteFiles();
const std::unordered_set<uint64_t>& TEST_GetFilesGrabbedForPurge() const {
return files_grabbed_for_purge_;
}
@@ -1462,9 +1447,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);
@@ -1515,7 +1497,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,
@@ -1524,7 +1505,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);
@@ -1551,8 +1531,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);
@@ -1724,11 +1703,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;
@@ -1743,16 +1719,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() {
@@ -1950,7 +1920,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);
@@ -2209,8 +2179,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,
@@ -2286,11 +2255,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,
@@ -2563,10 +2527,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.
@@ -2617,7 +2579,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()
@@ -2630,7 +2592,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
+105 -76
View File
@@ -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()) {
@@ -2248,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,
@@ -2369,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();
}
@@ -2388,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) {
@@ -2534,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) {
@@ -2592,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();
@@ -2682,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();
@@ -3000,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
@@ -3022,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) {
@@ -3031,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) {
-5
View File
@@ -314,11 +314,6 @@ const autovector<uint64_t>& DBImpl::TEST_GetFilesToQuarantine() const {
return error_handler_.GetFilesToQuarantine();
}
void DBImpl::TEST_DeleteObsoleteFiles() {
InstrumentedMutexLock l(&mutex_);
DeleteObsoleteFiles();
}
size_t DBImpl::TEST_EstimateInMemoryStatsHistorySize() const {
InstrumentedMutexLock l(&const_cast<DBImpl*>(this)->stats_history_mutex_);
return EstimateInMemoryStatsHistorySize();
+9 -39
View File
@@ -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) {
-1
View File
@@ -5,7 +5,6 @@
#include "db/db_impl/db_impl_follower.h"
#include <algorithm>
#include <cinttypes>
#include "db/arena_wrapped_db_iter.h"
+13 -20
View File
@@ -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);
@@ -1673,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();
@@ -1694,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(),
@@ -1703,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,
+31 -75
View File
@@ -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
@@ -701,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);
@@ -709,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");
@@ -849,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);
@@ -891,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()) {
@@ -942,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);
@@ -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) {
+2 -43
View File
@@ -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) {
-4
View File
@@ -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
+1 -53
View File
@@ -507,23 +507,6 @@ TEST_F(DBSSTTest, DBWithSstFileManagerForBlobFiles) {
ASSERT_EQ(files_deleted, 0);
ASSERT_EQ(files_scheduled_to_delete, 0);
Close();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"SstFileManagerImpl::ScheduleUnaccountedFileDeletion", [&](void* arg) {
assert(arg);
const std::string* const file_path =
static_cast<const std::string*>(arg);
if (EndsWith(*file_path, ".blob")) {
++files_scheduled_to_delete;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::OnDeleteFile", [&](void* arg) {
const std::string* const file_path =
static_cast<const std::string*>(arg);
if (EndsWith(*file_path, ".blob")) {
files_deleted++;
}
});
ASSERT_OK(DestroyDB(dbname_, options));
ASSERT_EQ(files_deleted, blob_files.size());
ASSERT_EQ(files_scheduled_to_delete, blob_files.size());
@@ -666,23 +649,6 @@ TEST_F(DBSSTTest, DBWithSstFileManagerForBlobFilesWithGC) {
}
Close();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"SstFileManagerImpl::ScheduleUnaccountedFileDeletion", [&](void* arg) {
assert(arg);
const std::string* const file_path =
static_cast<const std::string*>(arg);
if (EndsWith(*file_path, ".blob")) {
++files_scheduled_to_delete;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::OnDeleteFile", [&](void* arg) {
const std::string* const file_path =
static_cast<const std::string*>(arg);
if (EndsWith(*file_path, ".blob")) {
files_deleted++;
}
});
ASSERT_OK(DestroyDB(dbname_, options));
sfm->WaitForEmptyTrash();
ASSERT_EQ(files_deleted, 5);
@@ -921,7 +887,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(1024 * 1024);
options.sst_file_manager->SetDeleteRateBytesPerSecond(1);
}
ASSERT_OK(Put("Key2", DummyString(1024, v)));
ASSERT_OK(Put("Key3", DummyString(1024, v)));
@@ -1936,24 +1902,6 @@ TEST_F(DBSSTTest, DBWithSFMForBlobFilesAtomicFlush) {
ASSERT_EQ(files_deleted, 1);
Close();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"SstFileManagerImpl::ScheduleUnaccountedFileDeletion", [&](void* arg) {
assert(arg);
const std::string* const file_path =
static_cast<const std::string*>(arg);
if (EndsWith(*file_path, ".blob")) {
++files_scheduled_to_delete;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::OnDeleteFile", [&](void* arg) {
const std::string* const file_path =
static_cast<const std::string*>(arg);
if (EndsWith(*file_path, ".blob")) {
files_deleted++;
}
});
ASSERT_OK(DestroyDB(dbname_, options));
ASSERT_EQ(files_scheduled_to_delete, 4);
+2 -10
View File
@@ -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
View File
@@ -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) {
+6 -6
View File
@@ -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)));
}
+2 -92
View File
@@ -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
+2 -2
View File
@@ -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();
+11 -18
View File
@@ -342,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);
@@ -929,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,
@@ -992,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
@@ -1003,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;
}
@@ -1173,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)) {
-11
View File
@@ -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
-49
View File
@@ -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));
+72 -54
View File
@@ -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,59 +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());
ASSERT_TRUE(fault_fs->ReadUnsyncedData());
uint32_t len = Random::GetTLSInstance()->Uniform(10000) + 1;
Random rnd(len);
// Create partially synced file
std::string f = test::PerThreadDBPath("read_unsynced.data");
std::string data = rnd.RandomString(len);
{
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));
ASSERT_OK(w->Sync({}, nullptr));
ASSERT_OK(w->Append(Slice(data.data() + synced_len, len - synced_len), {},
nullptr));
// no sync here
ASSERT_OK(w->Close({}, 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.
{
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)));
uint32_t second_read_len = len - first_read_len;
scratch.reset(new char[second_read_len]);
ASSERT_OK(r->Read(second_read_len, {}, &sl, scratch.get(), nullptr));
ASSERT_EQ(second_read_len, sl.size());
ASSERT_EQ(0,
sl.compare(Slice(data.data() + first_read_len, second_read_len)));
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+3 -30
View File
@@ -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(&current_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();
-14
View File
@@ -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
View File
@@ -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
View File
@@ -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
-24
View File
@@ -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.");
}
-5
View File
@@ -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
View File
@@ -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
View File
@@ -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
+3 -10
View File
@@ -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
+10 -8
View File
@@ -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
View File
@@ -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,
-30
View File
@@ -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;
-9
View File
@@ -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.
-15
View File
@@ -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,
@@ -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
-8
View File
@@ -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
+2 -9
View File
@@ -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) {
+1 -3
View File
@@ -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"; }
+3 -3
View File
@@ -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()) {
+7 -7
View File
@@ -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
+6 -7
View File
@@ -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);
+4 -20
View File
@@ -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());
}
}
@@ -3505,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) {
@@ -5196,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();
}
+7 -14
View File
@@ -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_;
+15 -14
View File
@@ -44,13 +44,13 @@ Status WalManager::DeleteFile(const std::string& fname, uint64_t number) {
return s;
}
Status WalManager::GetSortedWalFiles(VectorWalPtr& files) {
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;
VectorLogPtr logs;
s = GetSortedWalsOfType(wal_dir_, logs, kAliveLogFile);
if (!s.ok()) {
return s;
@@ -72,8 +72,8 @@ Status WalManager::GetSortedWalFiles(VectorWalPtr& files) {
return s;
}
} else if (!exists.IsNotFound()) {
assert(s.ok());
return exists;
assert(s.IsIOError());
return s;
}
uint64_t latest_archived_log_number = 0;
@@ -113,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;
@@ -173,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;
}
@@ -248,7 +249,7 @@ void WalManager::PurgeObsoleteWALFiles() {
}
size_t files_del_num = log_files_num - files_keep_num;
VectorWalPtr archived_logs;
VectorLogPtr archived_logs;
s = GetSortedWalsOfType(archival_dir, archived_logs, kArchivedLogFile);
if (!s.ok()) {
ROCKS_LOG_WARN(db_options_.info_log,
@@ -290,7 +291,7 @@ void WalManager::ArchiveWALFile(const std::string& fname, uint64_t number) {
}
Status WalManager::GetSortedWalsOfType(const std::string& path,
VectorWalPtr& log_files,
VectorLogPtr& log_files,
WalFileType log_type) {
std::vector<std::string> all_files;
const Status status = env_->GetChildren(path, &all_files);
@@ -337,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;
@@ -423,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.");
}
@@ -441,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));
+4 -4
View File
@@ -49,7 +49,7 @@ class WalManager {
wal_in_db_path_(db_options_.IsWalDirSameAsDBPath()),
io_tracer_(io_tracer) {}
Status GetSortedWalFiles(VectorWalPtr& files);
Status GetSortedWalFiles(VectorLogPtr& files);
// Allow user to tail transaction log to find all recent changes to the
// database that are newer than `seq_number`.
@@ -64,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) {
@@ -77,12 +77,12 @@ class WalManager {
}
private:
Status GetSortedWalsOfType(const std::string& path, VectorWalPtr& log_files,
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
View File
@@ -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
View File
@@ -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();
+5 -46
View File
@@ -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
View File
@@ -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
View File
@@ -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
-1
View File
@@ -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
-5
View File
@@ -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));
+7 -71
View File
@@ -73,10 +73,7 @@ class CfConsistencyStressTest : public StressTest {
status = db_->Write(write_opts, &batch);
}
if (IsRetryableInjectedError(status)) {
fprintf(stdout, "multi put or merge error: %s\n",
status.ToString().c_str());
} else if (!status.ok()) {
if (!status.ok()) {
fprintf(stderr, "multi put or merge error: %s\n",
status.ToString().c_str());
thread->stats.AddErrors(1);
@@ -99,9 +96,7 @@ class CfConsistencyStressTest : public StressTest {
batch.Delete(cfh, key);
}
Status s = db_->Write(write_opts, &batch);
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "multidel error: %s\n", s.ToString().c_str());
} else if (!s.ok()) {
if (!s.ok()) {
fprintf(stderr, "multidel error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
} else {
@@ -130,9 +125,7 @@ class CfConsistencyStressTest : public StressTest {
batch.DeleteRange(cfh, key, end_key);
}
Status s = db_->Write(write_opts, &batch);
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "multi del range error: %s\n", s.ToString().c_str());
} else if (!s.ok()) {
if (!s.ok()) {
fprintf(stderr, "multi del range error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
} else {
@@ -177,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++) {
@@ -230,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) {
@@ -248,8 +225,6 @@ class CfConsistencyStressTest : public StressTest {
thread->stats.AddGets(1, 1);
} else if (s.IsNotFound()) {
thread->stats.AddGets(1, 0);
} else if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestGet error: %s\n", s.ToString().c_str());
} else {
fprintf(stderr, "TestGet error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
@@ -286,8 +261,6 @@ class CfConsistencyStressTest : public StressTest {
} else if (s.IsNotFound()) {
// not found case
thread->stats.AddGets(1, 0);
} else if (IsRetryableInjectedError(s)) {
fprintf(stdout, "MultiGet error: %s\n", s.ToString().c_str());
} else {
// errors case
fprintf(stderr, "MultiGet error: %s\n", s.ToString().c_str());
@@ -324,12 +297,7 @@ class CfConsistencyStressTest : public StressTest {
PinnableWideColumns result;
s = db_->GetEntity(read_opts, cfh, key, &result);
if (IsRetryableInjectedError(s)) {
fprintf(stdout,
"GetEntity error: inconsistent columns for key %s, entity %s\n",
StringToHex(key).c_str(),
WideColumnsToHex(result.columns()).c_str());
} else if (s.ok()) {
if (s.ok()) {
if (!VerifyWideColumns(result.columns())) {
fprintf(
stderr,
@@ -355,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();
@@ -498,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) {
@@ -517,8 +469,6 @@ class CfConsistencyStressTest : public StressTest {
thread->stats.AddGets(1, 1);
} else if (s.IsNotFound()) {
thread->stats.AddGets(1, 0);
} else if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestGetEntity error: %s\n", s.ToString().c_str());
} else {
fprintf(stderr, "TestGetEntity error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
@@ -590,11 +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 (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestMultiGetEntity (AttributeGroup) error: %s\n",
s.ToString().c_str());
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);
@@ -699,11 +645,7 @@ class CfConsistencyStressTest : public StressTest {
const Status& s = statuses[j];
const WideColumns& columns = results[j].columns();
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestMultiGetEntity error: %s\n",
s.ToString().c_str());
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);
@@ -804,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 =
@@ -838,9 +776,7 @@ class CfConsistencyStressTest : public StressTest {
s = iter->status();
}
if (IsRetryableInjectedError(s)) {
fprintf(stdout, "TestPrefixScan error: %s\n", s.ToString().c_str());
} else if (!s.ok()) {
if (!s.ok()) {
fprintf(stderr, "TestPrefixScan error: %s\n", s.ToString().c_str());
thread->stats.AddErrors(1);
+1 -11
View File
@@ -200,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);
@@ -395,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;
+4 -8
View File
@@ -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,
+25 -10
View File
@@ -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);
}
// Since wrie fault and sync fault implementations are coupled with each
// other in `TestFSWritableFile()`, we can not enable or disable only one
// of the two.
// TODO(hx235): decouple implementations of write fault injection and sync
// fault injection.
if (FLAGS_sync_fault_injection || FLAGS_write_fault_one_in > 0) {
fault_fs_guard->SetFilesystemDirectWritable(false);
}
now = clock->NowMicros();
fprintf(stdout, "%s Starting database operations\n",
clock->TimeToString(now / 1000000).c_str());
-93
View File
@@ -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
-16
View File
@@ -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
-40
View File
@@ -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,10 +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_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");
@@ -1107,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.");
@@ -1433,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
+20 -107
View File
@@ -71,53 +71,22 @@ class DbStressListener : public EventListener {
VerifyFilePath(info.file_path);
// pretending doing some work here
RandomSleep();
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (FLAGS_read_fault_one_in) {
(void)fault_fs_guard->GetAndResetErrorCount();
fault_fs_guard->DisableErrorInjection();
}
}
void OnFlushBegin(DB* /*db*/,
const FlushJobInfo& /*flush_job_info*/) override {
RandomSleep();
if (fault_fs_guard) {
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kRead, static_cast<uint32_t>(FLAGS_seed),
FLAGS_read_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kWrite, static_cast<uint32_t>(FLAGS_seed),
FLAGS_write_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kMetadataRead,
static_cast<uint32_t>(FLAGS_seed), FLAGS_metadata_read_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kMetadataWrite,
static_cast<uint32_t>(FLAGS_seed), FLAGS_metadata_write_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (FLAGS_read_fault_one_in) {
// Hardcoded to inject retryable error as a non-retryable error would put
// the DB in read-only mode and then it would crash on the next write.
fault_fs_guard->SetThreadLocalReadErrorContext(
static_cast<uint32_t>(FLAGS_seed), FLAGS_read_fault_one_in,
true /* retryable */);
fault_fs_guard->EnableErrorInjection();
}
}
@@ -143,51 +112,20 @@ class DbStressListener : public EventListener {
}
void OnSubcompactionBegin(const SubcompactionJobInfo& /* si */) override {
if (fault_fs_guard) {
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kRead, static_cast<uint32_t>(FLAGS_seed),
FLAGS_read_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kWrite, static_cast<uint32_t>(FLAGS_seed),
FLAGS_write_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kMetadataRead,
static_cast<uint32_t>(FLAGS_seed), FLAGS_metadata_read_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->SetThreadLocalErrorContext(
FaultInjectionIOType::kMetadataWrite,
static_cast<uint32_t>(FLAGS_seed), FLAGS_metadata_write_fault_one_in,
FLAGS_inject_error_severity == 1 /* retryable */,
FLAGS_inject_error_severity == 2 /* has_data_loss*/);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (FLAGS_read_fault_one_in) {
// Hardcoded to inject retryable error as a non-retryable error would put
// the DB in read-only mode and then it would crash on the next write.
fault_fs_guard->SetThreadLocalReadErrorContext(
static_cast<uint32_t>(FLAGS_seed), FLAGS_read_fault_one_in,
true /* retryable */);
fault_fs_guard->EnableErrorInjection();
}
}
void OnSubcompactionCompleted(const SubcompactionJobInfo& /* si */) override {
if (fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
if (FLAGS_read_fault_one_in) {
(void)fault_fs_guard->GetAndResetErrorCount();
fault_fs_guard->DisableErrorInjection();
}
}
@@ -273,35 +211,10 @@ class DbStressListener : public EventListener {
Status /* bg_error */,
bool* /* auto_recovery */) override {
RandomSleep();
if (FLAGS_error_recovery_with_no_fault_injection && fault_fs_guard) {
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->DisableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
// TODO(hx235): only exempt the flush thread during error recovery instead
// of all the flush threads from error injection
fault_fs_guard->SetIOActivtiesExemptedFromFaultInjection(
{Env::IOActivity::kFlush});
}
}
void OnErrorRecoveryCompleted(Status /* old_bg_error */) override {
RandomSleep();
if (FLAGS_error_recovery_with_no_fault_injection && fault_fs_guard) {
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kWrite);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataRead);
fault_fs_guard->EnableThreadLocalErrorInjection(
FaultInjectionIOType::kMetadataWrite);
fault_fs_guard->SetIOActivtiesExemptedFromFaultInjection({});
}
}
protected:
-7
View File
@@ -29,15 +29,8 @@ DECLARE_int32(clear_column_family_one_in);
DECLARE_bool(test_batches_snapshots);
DECLARE_int32(compaction_thread_pool_adjust_interval);
DECLARE_int32(continuous_verification_interval);
DECLARE_bool(error_recovery_with_no_fault_injection);
DECLARE_bool(sync_fault_injection);
DECLARE_bool(disable_wal);
DECLARE_int32(manual_wal_flush_one_in);
DECLARE_int32(metadata_read_fault_one_in);
DECLARE_int32(metadata_write_fault_one_in);
DECLARE_int32(read_fault_one_in);
DECLARE_int32(write_fault_one_in);
DECLARE_int32(open_metadata_read_fault_one_in);
DECLARE_int32(open_metadata_write_fault_one_in);
DECLARE_int32(open_write_fault_one_in);
DECLARE_int32(open_read_fault_one_in);
File diff suppressed because it is too large. Load diff
+9 -52
View File
@@ -7,13 +7,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "rocksdb/io_status.h"
#ifdef GFLAGS
#pragma once
#include "db_stress_tool/db_stress_common.h"
#include "db_stress_tool/db_stress_shared_state.h"
#include "rocksdb/experimental.h"
namespace ROCKSDB_NAMESPACE {
class SystemClock;
@@ -21,7 +19,6 @@ class Transaction;
class TransactionDB;
class OptimisticTransactionDB;
struct TransactionDBOptions;
using experimental::SstQueryFilterConfigsManager;
class StressTest {
public:
@@ -44,24 +41,8 @@ class StressTest {
virtual void VerifyDb(ThreadState* thread) const = 0;
virtual void ContinuouslyVerifyDb(ThreadState* /*thread*/) const = 0;
void PrintStatistics();
bool MightHaveDataLoss() {
return FLAGS_sync_fault_injection || FLAGS_write_fault_one_in > 0 ||
FLAGS_metadata_write_fault_one_in > 0 || FLAGS_disable_wal ||
FLAGS_manual_wal_flush_one_in > 0;
}
protected:
static int GetMinInjectedErrorCount(int error_count_1, int error_count_2) {
if (error_count_1 > 0 && error_count_2 > 0) {
return std::min(error_count_1, error_count_2);
} else if (error_count_1 > 0) {
return error_count_1;
} else if (error_count_2 > 0) {
return error_count_2;
} else {
return 0;
}
}
Status AssertSame(DB* db, ColumnFamilyHandle* cf,
ThreadState::SnapshotState& snap_state);
@@ -188,20 +169,6 @@ class StressTest {
const std::vector<int>& rand_column_families,
const std::vector<int64_t>& rand_keys);
// Given a key K, this creates an attribute group iterator which scans to K
// and then does a random sequence of Next/Prev operations. Called only when
// use_attribute_group=1
virtual Status TestIterateAttributeGroups(
ThreadState* thread, const ReadOptions& read_opts,
const std::vector<int>& rand_column_families,
const std::vector<int64_t>& rand_keys);
template <typename IterType, typename NewIterFunc, typename VerifyFunc>
Status TestIterateImpl(ThreadState* thread, const ReadOptions& read_opts,
const std::vector<int>& rand_column_families,
const std::vector<int64_t>& rand_keys,
NewIterFunc new_iter_func, VerifyFunc verify_func);
virtual Status TestIterateAgainstExpected(
ThreadState* /* thread */, const ReadOptions& /* read_opts */,
const std::vector<int>& /* rand_column_families */,
@@ -225,12 +192,10 @@ class StressTest {
// diverged = true if the two iterator is already diverged.
// True if verification passed, false if not.
// op_logs is the information to print when validation fails.
template <typename IterType, typename VerifyFuncType>
void VerifyIterator(ThreadState* thread, ColumnFamilyHandle* cmp_cfh,
const ReadOptions& ro, IterType* iter, Iterator* cmp_iter,
const ReadOptions& ro, Iterator* iter, Iterator* cmp_iter,
LastIterateOp op, const Slice& seek_key,
const std::string& op_logs, VerifyFuncType verifyFunc,
bool* diverged);
const std::string& op_logs, bool* diverged);
virtual Status TestBackupRestore(ThreadState* thread,
const std::vector<int>& rand_column_families,
@@ -259,13 +224,13 @@ class StressTest {
Status MaybeReleaseSnapshots(ThreadState* thread, uint64_t i);
Status TestGetLiveFiles() const;
Status TestGetLiveFilesMetaData() const;
Status TestGetLiveFilesStorageInfo() const;
Status TestGetAllColumnFamilyMetaData() const;
Status VerifyGetLiveFiles() const;
Status VerifyGetLiveFilesMetaData() const;
Status VerifyGetLiveFilesStorageInfo() const;
Status VerifyGetAllColumnFamilyMetaData() const;
Status TestGetSortedWalFiles() const;
Status TestGetCurrentWalFile() const;
Status VerifyGetSortedWalFiles() const;
Status VerifyGetCurrentWalFile() const;
void TestGetProperty(ThreadState* thread) const;
Status TestGetPropertiesOfAllTables() const;
@@ -280,11 +245,6 @@ class StressTest {
return Status::NotSupported("TestCustomOperations() must be overridden");
}
bool IsRetryableInjectedError(const Status& s) const {
return s.getState() && std::strstr(s.getState(), "inject") &&
!status_to_io_status(Status(s)).GetDataLoss();
}
void ProcessStatus(SharedState* shared, std::string msg, const Status& s,
bool ignore_injected_error = true) const;
@@ -343,7 +303,6 @@ class StressTest {
std::unordered_map<std::string, std::vector<std::string>> options_table_;
std::vector<std::string> options_index_;
std::atomic<bool> db_preload_finished_;
std::shared_ptr<SstQueryFilterConfigsManager::Factory> sqfc_factory_;
// Fields used for continuous verification from another thread
DB* cmp_db_;
@@ -385,9 +344,7 @@ void InitializeOptionsFromFlags(
// from OPTIONS file.
void InitializeOptionsGeneral(
const std::shared_ptr<Cache>& cache,
const std::shared_ptr<const FilterPolicy>& filter_policy,
const std::shared_ptr<SstQueryFilterConfigsManager::Factory>& sqfc_factory,
Options& options);
const std::shared_ptr<const FilterPolicy>& filter_policy, Options& options);
// If no OPTIONS file is specified, set up `options` so that we can test
// user-defined timestamp which requires `-user_timestamp_size=8`.
+7 -9
View File
@@ -20,10 +20,10 @@
// NOTE that if FLAGS_test_batches_snapshots is set, the test will have
// different behavior. See comment of the flag for details.
#include "db_stress_tool/db_stress_shared_state.h"
#ifdef GFLAGS
#include "db_stress_tool/db_stress_common.h"
#include "db_stress_tool/db_stress_driver.h"
#include "db_stress_tool/db_stress_shared_state.h"
#include "rocksdb/convenience.h"
#include "utilities/fault_injection_fs.h"
@@ -82,11 +82,9 @@ int db_stress_tool(int argc, char** argv) {
dbsl_env_wrapper_guard = std::make_shared<CompositeEnvWrapper>(raw_env);
db_stress_listener_env = dbsl_env_wrapper_guard.get();
if (FLAGS_open_metadata_read_fault_one_in ||
FLAGS_open_metadata_write_fault_one_in || FLAGS_open_read_fault_one_in ||
FLAGS_open_write_fault_one_in || FLAGS_metadata_read_fault_one_in ||
FLAGS_metadata_write_fault_one_in || FLAGS_read_fault_one_in ||
FLAGS_write_fault_one_in || FLAGS_sync_fault_injection) {
if (FLAGS_read_fault_one_in || FLAGS_sync_fault_injection ||
FLAGS_write_fault_one_in || FLAGS_open_metadata_write_fault_one_in ||
FLAGS_open_write_fault_one_in || FLAGS_open_read_fault_one_in) {
FaultInjectionTestFS* fs =
new FaultInjectionTestFS(raw_env->GetFileSystem());
fault_fs_guard.reset(fs);
@@ -303,11 +301,11 @@ int db_stress_tool(int argc, char** argv) {
}
if (FLAGS_use_put_entity_one_in > 0 &&
(FLAGS_use_full_merge_v1 || FLAGS_test_multi_ops_txns ||
(FLAGS_use_full_merge_v1 || FLAGS_use_txn || FLAGS_test_multi_ops_txns ||
FLAGS_user_timestamp_size > 0)) {
fprintf(stderr,
"Wide columns are incompatible with V1 Merge, the multi-op "
"transaction test, and user-defined timestamps\n");
"Wide columns are incompatible with V1 Merge, transactions, and "
"user-defined timestamps\n");
exit(1);
}
-19
View File
@@ -432,13 +432,6 @@ Status MultiOpsTxnsStressTest::TestIterate(
return s;
}
Status MultiOpsTxnsStressTest::TestIterateAttributeGroups(
ThreadState* /*thread*/, const ReadOptions& /*read_opts*/,
const std::vector<int>& /*rand_column_families*/,
const std::vector<int64_t>& /*rand_keys*/) {
return Status::NotSupported();
}
// Not intended for use.
Status MultiOpsTxnsStressTest::TestPut(ThreadState* /*thread*/,
WriteOptions& /*write_opts*/,
@@ -756,10 +749,6 @@ Status MultiOpsTxnsStressTest::SecondaryKeyUpdateTxn(ThreadState* thread,
ropts.iterate_upper_bound = &iter_ub;
ropts.rate_limiter_priority =
FLAGS_rate_limit_user_ops ? Env::IO_USER : Env::IO_TOTAL;
if (FLAGS_use_sqfc_for_range_queries) {
ropts.table_filter =
sqfc_factory_->GetTableFilterForRangeQuery(old_sk_prefix, iter_ub);
}
it = txn->GetIterator(ropts);
assert(it);
@@ -1118,10 +1107,6 @@ void MultiOpsTxnsStressTest::VerifyDb(ThreadState* thread) const {
ropts.snapshot = snapshot;
ropts.total_order_seek = true;
ropts.iterate_upper_bound = &iter_ub;
if (FLAGS_use_sqfc_for_range_queries) {
ropts.table_filter =
sqfc_factory_->GetTableFilterForRangeQuery(start_key, iter_ub);
}
std::unique_ptr<Iterator> it(db_->NewIterator(ropts));
for (it->Seek(start_key); it->Valid(); it->Next()) {
@@ -1630,10 +1615,6 @@ void MultiOpsTxnsStressTest::ScanExistingDb(SharedState* shared, int threads) {
ropts.iterate_lower_bound = &pk_lb;
ropts.iterate_upper_bound = &pk_ub;
ropts.total_order_seek = true;
if (FLAGS_use_sqfc_for_range_queries) {
ropts.table_filter =
sqfc_factory_->GetTableFilterForRangeQuery(pk_lb, pk_ub);
}
std::unique_ptr<Iterator> it(db_->NewIterator(ropts));
for (it->SeekToFirst(); it->Valid(); it->Next()) {
-5
View File
@@ -228,11 +228,6 @@ class MultiOpsTxnsStressTest : public StressTest {
const std::vector<int>& rand_column_families,
const std::vector<int64_t>& rand_keys) override;
Status TestIterateAttributeGroups(
ThreadState* thread, const ReadOptions& read_opts,
const std::vector<int>& rand_column_families,
const std::vector<int64_t>& rand_keys) override;
Status TestPut(ThreadState* thread, WriteOptions& write_opts,
const ReadOptions& read_opts, const std::vector<int>& cf_ids,
const std::vector<int64_t>& keys, char (&value)[100]) override;
File diff suppressed because it is too large. Load diff
+1
View File
@@ -457,6 +457,7 @@ Status PosixHelper::GetLogicalBlockSizeOfDirectory(const std::string& directory,
size_t* size) {
int fd = open(directory.c_str(), O_DIRECTORY | O_RDONLY);
if (fd == -1) {
close(fd);
return Status::IOError("Cannot open directory " + directory);
}
*size = PosixHelper::GetLogicalBlockSizeOfFd(fd);
+23 -124
View File
@@ -31,7 +31,6 @@ DeleteScheduler::DeleteScheduler(SystemClock* clock, FileSystem* fs,
total_trash_size_(0),
rate_bytes_per_sec_(rate_bytes_per_sec),
pending_files_(0),
next_trash_bucket_(0),
bytes_max_delete_chunk_(bytes_max_delete_chunk),
closing_(false),
cv_(&mu_),
@@ -67,8 +66,10 @@ Status DeleteScheduler::DeleteFile(const std::string& file_path,
total_trash_size_.load() > total_size * max_trash_db_ratio_.load())) {
// Rate limiting is disabled or trash size makes up more than
// max_trash_db_ratio_ (default 25%) of the total DB size
Status s = DeleteFileImmediately(file_path, /*accounted=*/true);
TEST_SYNC_POINT("DeleteScheduler::DeleteFile");
Status s = fs_->DeleteFile(file_path, IOOptions(), nullptr);
if (s.ok()) {
s = sst_file_manager_->OnDeleteFile(file_path);
ROCKS_LOG_INFO(info_log_,
"Deleted file %s immediately, rate_bytes_per_sec %" PRIi64
", total_trash_size %" PRIu64 ", total_size %" PRIi64
@@ -76,57 +77,15 @@ Status DeleteScheduler::DeleteFile(const std::string& file_path,
file_path.c_str(), rate_bytes_per_sec_.load(),
total_trash_size_.load(), total_size,
max_trash_db_ratio_.load());
InstrumentedMutexLock l(&mu_);
RecordTick(stats_.get(), FILES_DELETED_IMMEDIATELY);
}
return s;
}
return AddFileToDeletionQueue(file_path, dir_to_sync, /*bucket=*/std::nullopt,
/*accounted=*/true);
}
Status DeleteScheduler::DeleteUnaccountedFile(const std::string& file_path,
const std::string& dir_to_sync,
const bool force_bg,
std::optional<int32_t> bucket) {
uint64_t num_hard_links = 1;
fs_->NumFileLinks(file_path, IOOptions(), &num_hard_links, nullptr)
.PermitUncheckedError();
// We can tolerate rare races where we might immediately delete both links
// to a file.
if (rate_bytes_per_sec_.load() <= 0 || (!force_bg && num_hard_links > 1)) {
Status s = DeleteFileImmediately(file_path, /*accounted=*/false);
if (s.ok()) {
ROCKS_LOG_INFO(info_log_,
"Deleted file %s immediately, rate_bytes_per_sec %" PRIi64,
file_path.c_str(), rate_bytes_per_sec_.load());
}
return s;
}
return AddFileToDeletionQueue(file_path, dir_to_sync, bucket,
/*accounted=*/false);
}
Status DeleteScheduler::DeleteFileImmediately(const std::string& file_path,
bool accounted) {
TEST_SYNC_POINT("DeleteScheduler::DeleteFile");
TEST_SYNC_POINT_CALLBACK("DeleteScheduler::DeleteFile::cb",
const_cast<std::string*>(&file_path));
Status s = fs_->DeleteFile(file_path, IOOptions(), nullptr);
if (s.ok()) {
s = OnDeleteFile(file_path, accounted);
InstrumentedMutexLock l(&mu_);
RecordTick(stats_.get(), FILES_DELETED_IMMEDIATELY);
}
return s;
}
Status DeleteScheduler::AddFileToDeletionQueue(const std::string& file_path,
const std::string& dir_to_sync,
std::optional<int32_t> bucket,
bool accounted) {
// Move file to trash
std::string trash_file;
Status s = MarkAsTrash(file_path, accounted, &trash_file);
Status s = MarkAsTrash(file_path, &trash_file);
ROCKS_LOG_INFO(info_log_, "Mark file: %s as trash -- %s", trash_file.c_str(),
s.ToString().c_str());
@@ -135,7 +94,7 @@ Status DeleteScheduler::AddFileToDeletionQueue(const std::string& file_path,
file_path.c_str(), s.ToString().c_str());
s = fs_->DeleteFile(file_path, IOOptions(), nullptr);
if (s.ok()) {
s = OnDeleteFile(file_path, accounted);
s = sst_file_manager_->OnDeleteFile(file_path);
ROCKS_LOG_INFO(info_log_, "Deleted file %s immediately",
trash_file.c_str());
InstrumentedMutexLock l(&mu_);
@@ -145,13 +104,11 @@ Status DeleteScheduler::AddFileToDeletionQueue(const std::string& file_path,
}
// Update the total trash size
if (accounted) {
uint64_t trash_file_size = 0;
IOStatus io_s =
fs_->GetFileSize(trash_file, IOOptions(), &trash_file_size, nullptr);
if (io_s.ok()) {
total_trash_size_.fetch_add(trash_file_size);
}
uint64_t trash_file_size = 0;
IOStatus io_s =
fs_->GetFileSize(trash_file, IOOptions(), &trash_file_size, nullptr);
if (io_s.ok()) {
total_trash_size_.fetch_add(trash_file_size);
}
//**TODO: What should we do if we failed to
// get the file size?
@@ -160,15 +117,8 @@ Status DeleteScheduler::AddFileToDeletionQueue(const std::string& file_path,
{
InstrumentedMutexLock l(&mu_);
RecordTick(stats_.get(), FILES_MARKED_TRASH);
queue_.emplace(trash_file, dir_to_sync, accounted, bucket);
queue_.emplace(trash_file, dir_to_sync);
pending_files_++;
if (bucket.has_value()) {
auto iter = pending_files_in_buckets_.find(bucket.value());
assert(iter != pending_files_in_buckets_.end());
if (iter != pending_files_in_buckets_.end()) {
iter->second++;
}
}
if (pending_files_ == 1) {
cv_.SignalAll();
}
@@ -227,7 +177,7 @@ Status DeleteScheduler::CleanupDirectory(Env* env, SstFileManagerImpl* sfm,
}
Status DeleteScheduler::MarkAsTrash(const std::string& file_path,
bool accounted, std::string* trash_file) {
std::string* trash_file) {
// Sanity check of the path
size_t idx = file_path.rfind('/');
if (idx == std::string::npos || idx == file_path.size() - 1) {
@@ -261,7 +211,7 @@ Status DeleteScheduler::MarkAsTrash(const std::string& file_path,
}
cnt++;
}
if (s.ok() && accounted) {
if (s.ok()) {
s = sst_file_manager_->OnMoveFile(file_path, *trash_file);
}
return s;
@@ -285,8 +235,6 @@ void DeleteScheduler::BackgroundEmptyTrash() {
uint64_t total_deleted_bytes = 0;
int64_t current_delete_rate = rate_bytes_per_sec_.load();
while (!queue_.empty() && !closing_) {
// Satisfy static analysis.
std::optional<int32_t> bucket = std::nullopt;
if (current_delete_rate != rate_bytes_per_sec_.load()) {
// User changed the delete rate
current_delete_rate = rate_bytes_per_sec_.load();
@@ -299,17 +247,14 @@ void DeleteScheduler::BackgroundEmptyTrash() {
// Get new file to delete
const FileAndDir& fad = queue_.front();
std::string path_in_trash = fad.fname;
std::string dir_to_sync = fad.dir;
bool accounted = fad.accounted;
bucket = fad.bucket;
// We don't need to hold the lock while deleting the file
mu_.Unlock();
uint64_t deleted_bytes = 0;
bool is_complete = true;
// Delete file from trash and update total_penlty value
Status s = DeleteTrashFile(path_in_trash, dir_to_sync, accounted,
&deleted_bytes, &is_complete);
Status s =
DeleteTrashFile(path_in_trash, fad.dir, &deleted_bytes, &is_complete);
total_deleted_bytes += deleted_bytes;
mu_.Lock();
if (is_complete) {
@@ -343,20 +288,12 @@ void DeleteScheduler::BackgroundEmptyTrash() {
TEST_SYNC_POINT_CALLBACK("DeleteScheduler::BackgroundEmptyTrash:Wait",
&total_penalty);
int32_t pending_files_in_bucket = std::numeric_limits<int32_t>::max();
if (is_complete) {
pending_files_--;
if (bucket.has_value()) {
auto iter = pending_files_in_buckets_.find(bucket.value());
assert(iter != pending_files_in_buckets_.end());
if (iter != pending_files_in_buckets_.end()) {
pending_files_in_bucket = iter->second--;
}
}
}
if (pending_files_ == 0 || pending_files_in_bucket == 0) {
// Unblock WaitForEmptyTrash or WaitForEmptyTrashBucket since there are
// no more files waiting to be deleted
if (pending_files_ == 0) {
// Unblock WaitForEmptyTrash since there are no more files waiting
// to be deleted
cv_.SignalAll();
}
}
@@ -365,14 +302,12 @@ void DeleteScheduler::BackgroundEmptyTrash() {
Status DeleteScheduler::DeleteTrashFile(const std::string& path_in_trash,
const std::string& dir_to_sync,
bool accounted, uint64_t* deleted_bytes,
uint64_t* deleted_bytes,
bool* is_complete) {
uint64_t file_size;
Status s = fs_->GetFileSize(path_in_trash, IOOptions(), &file_size, nullptr);
*is_complete = true;
TEST_SYNC_POINT("DeleteScheduler::DeleteTrashFile:DeleteFile");
TEST_SYNC_POINT_CALLBACK("DeleteScheduler::DeleteTrashFile::cb",
const_cast<std::string*>(&path_in_trash));
if (s.ok()) {
bool need_full_delete = true;
if (bytes_max_delete_chunk_ != 0 && file_size > bytes_max_delete_chunk_) {
@@ -439,7 +374,7 @@ Status DeleteScheduler::DeleteTrashFile(const std::string& path_in_trash,
}
if (s.ok()) {
*deleted_bytes = file_size;
s = OnDeleteFile(path_in_trash, accounted);
s = sst_file_manager_->OnDeleteFile(path_in_trash);
}
}
}
@@ -449,24 +384,12 @@ Status DeleteScheduler::DeleteTrashFile(const std::string& path_in_trash,
path_in_trash.c_str(), s.ToString().c_str());
*deleted_bytes = 0;
} else {
if (accounted) {
total_trash_size_.fetch_sub(*deleted_bytes);
}
total_trash_size_.fetch_sub(*deleted_bytes);
}
return s;
}
Status DeleteScheduler::OnDeleteFile(const std::string& file_path,
bool accounted) {
if (accounted) {
return sst_file_manager_->OnDeleteFile(file_path);
}
TEST_SYNC_POINT_CALLBACK("DeleteScheduler::OnDeleteFile",
const_cast<std::string*>(&file_path));
return Status::OK();
}
void DeleteScheduler::WaitForEmptyTrash() {
InstrumentedMutexLock l(&mu_);
while (pending_files_ > 0 && !closing_) {
@@ -474,30 +397,6 @@ void DeleteScheduler::WaitForEmptyTrash() {
}
}
std::optional<int32_t> DeleteScheduler::NewTrashBucket() {
if (rate_bytes_per_sec_.load() <= 0) {
return std::nullopt;
}
InstrumentedMutexLock l(&mu_);
int32_t bucket_number = next_trash_bucket_++;
pending_files_in_buckets_.emplace(bucket_number, 0);
return bucket_number;
}
void DeleteScheduler::WaitForEmptyTrashBucket(int32_t bucket) {
InstrumentedMutexLock l(&mu_);
if (bucket >= next_trash_bucket_) {
return;
}
auto iter = pending_files_in_buckets_.find(bucket);
while (iter != pending_files_in_buckets_.end() && iter->second > 0 &&
!closing_) {
cv_.Wait();
iter = pending_files_in_buckets_.find(bucket);
}
pending_files_in_buckets_.erase(bucket);
}
void DeleteScheduler::MaybeCreateBackgroundThread() {
if (bg_thread_ == nullptr && rate_bytes_per_sec_.load() > 0) {
bg_thread_.reset(
+8 -61
View File
@@ -7,7 +7,6 @@
#include <map>
#include <optional>
#include <queue>
#include <string>
#include <thread>
@@ -49,45 +48,16 @@ class DeleteScheduler {
MaybeCreateBackgroundThread();
}
// Delete an accounted file that is tracked by `SstFileManager` and should be
// tracked by this `DeleteScheduler` when it's deleted.
// The file is deleted immediately if slow deletion is disabled. If force_bg
// is not set and trash to db size ratio exceeded the configured threshold,
// it is immediately deleted too. In all other cases, the file will be moved
// to a trash directory and scheduled for deletion by a background thread.
// Mark file as trash directory and schedule its deletion. If force_bg is
// set, it forces the file to always be deleted in the background thread,
// except when rate limiting is disabled
Status DeleteFile(const std::string& fname, const std::string& dir_to_sync,
const bool force_bg = false);
// Delete an unaccounted file that is not tracked by `SstFileManager` and
// should not be tracked by this `DeleteScheduler` when it's deleted.
// The file is deleted immediately if slow deletion is disabled. If force_bg
// is not set and the file have more than 1 hard link, it is immediately
// deleted too. In all other cases, the file will be moved to a trash
// directory and scheduled for deletion by a background thread.
// This API also supports assign a file to a specified bucket created by
// `NewTrashBucket` when delete files in the background. So the caller can
// wait for a specific bucket to be empty by checking the
// `WaitForEmptyTrashBucket` API.
Status DeleteUnaccountedFile(const std::string& file_path,
const std::string& dir_to_sync,
const bool force_bg = false,
std::optional<int32_t> bucket = std::nullopt);
// Wait for all files being deleted in the background to finish or for
// Wait for all files being deleteing in the background to finish or for
// destructor to be called.
void WaitForEmptyTrash();
// Creates a new trash bucket. A bucket is only created and returned when slow
// deletion is enabled.
// For each bucket that is created, the user should also call
// `WaitForEmptyTrashBucket` after scheduling file deletions to make sure the
// trash files are all cleared.
std::optional<int32_t> NewTrashBucket();
// Wait for all the files in the specified bucket to be deleted in the
// background or for the destructor to be called.
void WaitForEmptyTrashBucket(int32_t bucket);
// Return a map containing errors that happened in BackgroundEmptyTrash
// file_path => error status
std::map<std::string, Status> GetBackgroundErrors();
@@ -117,21 +87,12 @@ class DeleteScheduler {
}
private:
Status DeleteFileImmediately(const std::string& file_path, bool accounted);
Status AddFileToDeletionQueue(const std::string& file_path,
const std::string& dir_to_sync,
std::optional<int32_t> bucket, bool accounted);
Status MarkAsTrash(const std::string& file_path, bool accounted,
std::string* path_in_trash);
Status MarkAsTrash(const std::string& file_path, std::string* path_in_trash);
Status DeleteTrashFile(const std::string& path_in_trash,
const std::string& dir_to_sync, bool accounted,
const std::string& dir_to_sync,
uint64_t* deleted_bytes, bool* is_complete);
Status OnDeleteFile(const std::string& file_path, bool accounted);
void BackgroundEmptyTrash();
void MaybeCreateBackgroundThread();
@@ -143,28 +104,19 @@ class DeleteScheduler {
std::atomic<uint64_t> total_trash_size_;
// Maximum number of bytes that should be deleted per second
std::atomic<int64_t> rate_bytes_per_sec_;
// Mutex to protect queue_, pending_files_, next_trash_bucket_,
// pending_files_in_buckets_, bg_errors_, closing_, stats_
// Mutex to protect queue_, pending_files_, bg_errors_, closing_, stats_
InstrumentedMutex mu_;
struct FileAndDir {
FileAndDir(const std::string& _fname, const std::string& _dir,
bool _accounted, std::optional<int32_t> _bucket)
: fname(_fname), dir(_dir), accounted(_accounted), bucket(_bucket) {}
FileAndDir(const std::string& f, const std::string& d) : fname(f), dir(d) {}
std::string fname;
std::string dir; // empty will be skipped.
bool accounted;
std::optional<int32_t> bucket;
};
// Queue of trash files that need to be deleted
std::queue<FileAndDir> queue_;
// Number of trash files that are waiting to be deleted
int32_t pending_files_;
// Next trash bucket that can be created
int32_t next_trash_bucket_;
// A mapping from trash bucket to number of pending files in the bucket
std::map<int32_t, int32_t> pending_files_in_buckets_;
uint64_t bytes_max_delete_chunk_;
// Errors that happened in BackgroundEmptyTrash (file_path => error)
std::map<std::string, Status> bg_errors_;
@@ -175,7 +127,6 @@ class DeleteScheduler {
// Condition variable signaled in these conditions
// - pending_files_ value change from 0 => 1
// - pending_files_ value change from 1 => 0
// - a value in pending_files_in_buckets change from 1 => 0
// - closing_ value is set to true
InstrumentedCondVar cv_;
// Background thread running BackgroundEmptyTrash
@@ -187,10 +138,6 @@ class DeleteScheduler {
// If the trash size constitutes for more than this fraction of the total DB
// size we will start deleting new files passed to DeleteScheduler
// immediately
// Unaccounted files passed for deletion will not cause change in
// total_trash_size_ or affect the DeleteScheduler::total_trash_size_ over
// SstFileManager::total_size_ ratio. Their slow deletion is not subject to
// this configured threshold either.
std::atomic<double> max_trash_db_ratio_;
static const uint64_t kMicrosInSecond = 1000 * 1000LL;
std::shared_ptr<Statistics> stats_;
+2 -141
View File
@@ -78,7 +78,7 @@ class DeleteSchedulerTest : public testing::Test {
}
std::string NewDummyFile(const std::string& file_name, uint64_t size = 1024,
size_t dummy_files_dirs_idx = 0, bool track = true) {
size_t dummy_files_dirs_idx = 0) {
std::string file_path =
dummy_files_dirs_[dummy_files_dirs_idx] + "/" + file_name;
std::unique_ptr<WritableFile> f;
@@ -86,9 +86,7 @@ class DeleteSchedulerTest : public testing::Test {
std::string data(size, 'A');
EXPECT_OK(f->Append(data));
EXPECT_OK(f->Close());
if (track) {
EXPECT_OK(sst_file_mgr_->OnAddFile(file_path));
}
EXPECT_OK(sst_file_mgr_->OnAddFile(file_path));
return file_path;
}
@@ -355,8 +353,6 @@ TEST_F(DeleteSchedulerTest, DisableRateLimiting) {
ASSERT_EQ(num_files,
stats_->getAndResetTickerCount(FILES_DELETED_IMMEDIATELY));
ASSERT_FALSE(delete_scheduler_->NewTrashBucket().has_value());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
@@ -722,141 +718,6 @@ TEST_F(DeleteSchedulerTest, IsTrashCheck) {
ASSERT_FALSE(DeleteScheduler::IsTrashFile("abc.trashx"));
}
TEST_F(DeleteSchedulerTest, DeleteAccountedAndUnaccountedFiles) {
rate_bytes_per_sec_ = 1024 * 1024; // 1 MB / s
NewDeleteScheduler();
// Create 100 files, every file is 1 KB
int num_files = 100; // 100 files
uint64_t file_size = 1024; // 1 KB as a file size
std::vector<std::string> generated_files;
for (int i = 0; i < num_files; i++) {
std::string file_name = "file" + std::to_string(i) + ".data";
generated_files.push_back(NewDummyFile(file_name, file_size,
/*dummy_files_dirs_idx*/ 0,
/*track=*/false));
}
for (int i = 0; i < num_files; i++) {
if (i % 2) {
ASSERT_OK(sst_file_mgr_->OnAddFile(generated_files[i], file_size));
ASSERT_OK(delete_scheduler_->DeleteFile(generated_files[i], ""));
} else {
ASSERT_OK(
delete_scheduler_->DeleteUnaccountedFile(generated_files[i], ""));
}
}
delete_scheduler_->WaitForEmptyTrash();
ASSERT_EQ(0, delete_scheduler_->GetTotalTrashSize());
ASSERT_EQ(0, sst_file_mgr_->GetTotalSize());
}
TEST_F(DeleteSchedulerTest, ConcurrentlyDeleteUnaccountedFilesInBuckets) {
int bg_delete_file = 0;
int fg_delete_file = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::DeleteTrashFile:DeleteFile",
[&](void* /*arg*/) { bg_delete_file++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::DeleteFile", [&](void* /*arg*/) { fg_delete_file++; });
rate_bytes_per_sec_ = 1024 * 1024; // 1 MB / s
NewDeleteScheduler();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
// Create 1000 files, every file is 1 KB
int num_files = 1000;
uint64_t file_size = 1024; // 1 KB as a file size
std::vector<std::string> generated_files;
for (int i = 0; i < num_files; i++) {
std::string file_name = "file" + std::to_string(i) + ".data";
generated_files.push_back(NewDummyFile(file_name, file_size,
/*dummy_files_dirs_idx*/ 0,
/*track=*/false));
}
// Concurrently delete files in different buckets and check all the buckets
// are empty.
int thread_cnt = 10;
int files_per_thread = 100;
std::atomic<int> thread_num(0);
std::vector<port::Thread> threads;
std::function<void()> delete_thread = [&]() {
std::optional<int32_t> bucket = delete_scheduler_->NewTrashBucket();
ASSERT_TRUE(bucket.has_value());
int idx = thread_num.fetch_add(1);
int range_start = idx * files_per_thread;
int range_end = range_start + files_per_thread;
for (int j = range_start; j < range_end; j++) {
ASSERT_OK(delete_scheduler_->DeleteUnaccountedFile(
generated_files[j], "", /*false_bg=*/false, bucket));
}
delete_scheduler_->WaitForEmptyTrashBucket(bucket.value());
};
for (int i = 0; i < thread_cnt; i++) {
threads.emplace_back(delete_thread);
}
for (size_t i = 0; i < threads.size(); i++) {
threads[i].join();
}
ASSERT_EQ(0, delete_scheduler_->GetTotalTrashSize());
ASSERT_EQ(0, stats_->getAndResetTickerCount(FILES_DELETED_IMMEDIATELY));
ASSERT_EQ(1000, stats_->getAndResetTickerCount(FILES_MARKED_TRASH));
ASSERT_EQ(0, fg_delete_file);
ASSERT_EQ(1000, bg_delete_file);
// OK to re check an already empty bucket
delete_scheduler_->WaitForEmptyTrashBucket(9);
// Invalid bucket return too.
delete_scheduler_->WaitForEmptyTrashBucket(100);
std::optional<int32_t> next_bucket = delete_scheduler_->NewTrashBucket();
ASSERT_TRUE(next_bucket.has_value());
ASSERT_EQ(10, next_bucket.value());
delete_scheduler_->WaitForEmptyTrashBucket(10);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DeleteSchedulerTest,
ImmediatelyDeleteUnaccountedFilesWithRemainingLinks) {
int bg_delete_file = 0;
int fg_delete_file = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::DeleteTrashFile:DeleteFile",
[&](void* /*arg*/) { bg_delete_file++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DeleteScheduler::DeleteFile", [&](void* /*arg*/) { fg_delete_file++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
rate_bytes_per_sec_ = 1024 * 1024; // 1 MB / sec
NewDeleteScheduler();
std::string file1 = NewDummyFile("data_1", 500 * 1024,
/*dummy_files_dirs_idx*/ 0, /*track=*/false);
std::string file2 = NewDummyFile("data_2", 100 * 1024,
/*dummy_files_dirs_idx*/ 0, /*track=*/false);
ASSERT_OK(env_->LinkFile(file1, dummy_files_dirs_[0] + "/data_1b"));
ASSERT_OK(env_->LinkFile(file2, dummy_files_dirs_[0] + "/data_2b"));
// Should delete in 4 batch if there is no hardlink
ASSERT_OK(
delete_scheduler_->DeleteUnaccountedFile(file1, "", /*force_bg=*/false));
ASSERT_OK(
delete_scheduler_->DeleteUnaccountedFile(file2, "", /*force_bg=*/false));
delete_scheduler_->WaitForEmptyTrash();
ASSERT_EQ(0, delete_scheduler_->GetTotalTrashSize());
ASSERT_EQ(0, bg_delete_file);
ASSERT_EQ(2, fg_delete_file);
ASSERT_EQ(0, stats_->getAndResetTickerCount(FILES_MARKED_TRASH));
ASSERT_EQ(2, stats_->getAndResetTickerCount(FILES_DELETED_IMMEDIATELY));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+4 -25
View File
@@ -59,10 +59,7 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
return io_s;
}
if (slice.size() == 0) {
return IOStatus::Corruption(
"File smaller than expected for copy: " + source + " expecting " +
std::to_string(size) + " more bytes after " +
std::to_string(dest_writer->GetFileSize()));
return IOStatus::Corruption("file too small");
}
io_s = dest_writer->Append(opts, slice);
@@ -125,8 +122,8 @@ IOStatus CreateFile(FileSystem* fs, const std::string& destination,
Status DeleteDBFile(const ImmutableDBOptions* db_options,
const std::string& fname, const std::string& dir_to_sync,
const bool force_bg, const bool force_fg) {
SstFileManagerImpl* sfm = static_cast_with_check<SstFileManagerImpl>(
db_options->sst_file_manager.get());
SstFileManagerImpl* sfm =
static_cast<SstFileManagerImpl*>(db_options->sst_file_manager.get());
if (sfm && !force_fg) {
return sfm->ScheduleFileDeletion(fname, dir_to_sync, force_bg);
} else {
@@ -134,21 +131,6 @@ Status DeleteDBFile(const ImmutableDBOptions* db_options,
}
}
Status DeleteUnaccountedDBFile(const ImmutableDBOptions* db_options,
const std::string& fname,
const std::string& dir_to_sync,
const bool force_bg, const bool force_fg,
std::optional<int32_t> bucket) {
SstFileManagerImpl* sfm = static_cast_with_check<SstFileManagerImpl>(
db_options->sst_file_manager.get());
if (sfm && !force_fg) {
return sfm->ScheduleUnaccountedFileDeletion(fname, dir_to_sync, force_bg,
bucket);
} else {
return db_options->env->DeleteFile(fname);
}
}
// requested_checksum_func_name brings the function name of the checksum
// generator in checksum_factory. Empty string is permitted, in which case the
// name of the generator created by the factory is unchecked. When
@@ -244,10 +226,7 @@ IOStatus GenerateOneFileChecksum(
io_s.ToString());
}
if (slice.size() == 0) {
return IOStatus::Corruption(
"File smaller than expected for checksum: " + file_path +
" expecting " + std::to_string(size) + " more bytes after " +
std::to_string(offset));
return IOStatus::Corruption("file too small");
}
checksum_generator->Update(slice.data(), slice.size());
size -= slice.size();
-10
View File
@@ -55,16 +55,6 @@ Status DeleteDBFile(const ImmutableDBOptions* db_options,
const std::string& fname, const std::string& path_to_sync,
const bool force_bg, const bool force_fg);
// Delete an unaccounted DB file that is not tracked by SstFileManager and will
// not be tracked by its DeleteScheduler when getting deleted.
// If a legitimate bucket is provided and this file is scheduled for slow
// deletion, it will be assigned to the specified trash bucket.
Status DeleteUnaccountedDBFile(const ImmutableDBOptions* db_options,
const std::string& fname,
const std::string& dir_to_sync,
const bool force_bg, const bool force_fg,
std::optional<int32_t> bucket);
// TODO(hx235): pass the whole DBOptions intead of its individual fields
IOStatus GenerateOneFileChecksum(
FileSystem* fs, const std::string& file_path,
Loaded 100 of 208 files, more files were not shown because too many files have changed in this diff. Show more