Compare commits

..
8 Commits
Author SHA1 Message Date
anand76 41214f0b4e Update HISTORY.md and version.h for 9.2.2 patch version 2024-05-31 10:26:59 -07:00
anand76 94ec0b4e6d 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-05-31 10:23:30 -07:00
Yu Zhang 08f93221f5 Fix issue that cause false alarm corruption report (#12626)
Summary:
The state of `saved_seq_for_penul_check_` is not correctly maintained with the current flow. It's supposed to store the original sequence number for a `kTypeValuePreferredSeqno` entry for use in the `DecideOutputLevel` function. However, it's not always properly cleared.

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

Test Plan:
Added unit test that would fail before the fix
./tiered_compaction_test --gtest_filter="*InterleavedTimedPutAndPut*"

Reviewed By: pdillinger

Differential Revision: D57123469

Pulled By: jowlyzhang

fbshipit-source-id: 8d73214b3b6dc152daf19b6bd6ee9063581dc277
2024-05-08 16:03:15 -07:00
anand76 a8e954024f Cherry pick fixes and an API change, and bump up patch version 2024-05-03 16:19:23 -07:00
anand76 bcc51fcc2e Fix build error due to virtual Iterator destructor (#12612)
Summary:
Fix build error due to virtual Iterator destructor not marked as override.

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

Reviewed By: jaykorean

Differential Revision: D56939155

Pulled By: anand1976

fbshipit-source-id: 2921d6facc296c69215de45151b08e279a1a98a2
2024-05-03 16:14:45 -07:00
Jaepil Jeong 78a1fcba4c Fix compile error in Clang (#12588)
Summary:
This PR fixes the following compile errors with Clang:

```
.../rocksdb/env/fs_on_demand.cc:184:5: error: no member named 'for_each' in namespace 'std'; did you mean 'std::ranges::for_each'?
  184 |     std::for_each(rchildren.begin(), rchildren.end(), [&](std::string& name) {
      |     ^~~~~~~~~~~~~
      |     std::ranges::for_each
/opt/homebrew/opt/llvm@17/bin/../include/c++/v1/__algorithm/ranges_for_each.h:68:23: note: 'std::ranges::for_each' declared here
   68 | inline constexpr auto for_each = __for_each::__fn{};
      |                       ^
.../rocksdb/env/fs_on_demand.cc:188:10: error: no member named 'sort' in namespace 'std'
  188 |     std::sort(result->begin(), result->end());
      |     ~~~~~^
.../rocksdb/env/fs_on_demand.cc:189:10: error: no member named 'sort' in namespace 'std'
  189 |     std::sort(rchildren.begin(), rchildren.end());
      |     ~~~~~^
.../rocksdb/env/fs_on_demand.cc:193:10: error: no member named 'set_union' in namespace 'std'
  193 |     std::set_union(result->begin(), result->end(), rchildren.begin(),
      |     ~~~~~^
.../rocksdb/env/fs_on_demand.cc:221:5: error: no member named 'for_each' in namespace 'std'; did you mean 'std::ranges::for_each'?
  221 |     std::for_each(
      |     ^~~~~~~~~~~~~
      |     std::ranges::for_each
/opt/homebrew/opt/llvm@17/bin/../include/c++/v1/__algorithm/ranges_for_each.h:68:23: note: 'std::ranges::for_each' declared here
   68 | inline constexpr auto for_each = __for_each::__fn{};
      |                       ^
.../rocksdb/env/fs_on_demand.cc:226:10: error: no member named 'sort' in namespace 'std'
  226 |     std::sort(result->begin(), result->end(), file_attr_sorter);
      |     ~~~~~^
.../rocksdb/env/fs_on_demand.cc:227:10: error: no member named 'sort' in namespace 'std'
  227 |     std::sort(rchildren.begin(), rchildren.end(), file_attr_sorter);
      |     ~~~~~^
.../rocksdb/env/fs_on_demand.cc:231:10: error: no member named 'set_union' in namespace 'std'
  231 |     std::set_union(rchildren.begin(), rchildren.end(), result->begin(),
      |     ~~~~~^
8 errors generated.
```

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

Reviewed By: jaykorean

Differential Revision: D56656222

Pulled By: ajkr

fbshipit-source-id: 7e94b6250fc9edfe597a61b7622f09d6b6cd9cbd
2024-05-03 16:13:57 -07:00
anand76 c79e69809e Implement secondary cache admission policy to allow all evicted blocks (#12599)
Summary:
Add a secondary cache admission policy to admit all blocks evicted from the block cache.

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

Reviewed By: pdillinger

Differential Revision: D56891760

Pulled By: anand1976

fbshipit-source-id: 193c98c055aa3477f4e3a78e5d3daef27a5eacf4
2024-05-03 16:13:36 -07:00
anand76 4226a32413 Update HISTORY.md for 9.2.0 2024-05-01 13:33:21 -07:00
271 changed files with 3593 additions and 13881 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
+5 -77
View File
@@ -1,86 +1,14 @@
# Rocksdb Change Log
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
## 9.5.2 (08/13/2024)
## 9.2.2 (05/31/2024)
### Bug Fixes
* Fix a race condition in pessimistic transactions that could allow multiple transactions with the same name to be registered simultaneously, resulting in a crash or other unpredictable behavior.
### Public API Changes
* Add ticker stats to count file read retries due to checksum mismatch
## 9.5.1 (08/02/2024)
### 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.5.0 (07/19/2024)
### Public API Changes
* Introduced new C API function rocksdb_writebatch_iterate_cf for column family-aware iteration over the contents of a WriteBatch
* Add support to ingest SST files generated by a DB instead of SstFileWriter. This can be enabled with experimental option `IngestExternalFileOptions::allow_db_generated_files`.
### Behavior Changes
* When calculating total log size for the `log_size_for_flush` argument in `CreateCheckpoint` API, the size of the archived log will not be included to avoid unnecessary flush
### Bug Fixes
* Fix a major bug in which an iterator using prefix filtering and SeekForPrev might miss data when the DB is using `whole_key_filtering=false` and `partition_filters=true`.
* Fixed a bug where `OnErrorRecoveryBegin()` is not called before auto recovery starts.
* Fixed a bug where event listener reads ErrorHandler's `bg_error_` member without holding db mutex(#12803).
* Fixed a bug in handling MANIFEST write error that caused the latest valid MANIFEST file to get deleted, resulting in the DB being unopenable.
* Fixed a race between error recovery due to manifest sync or write failure and external SST file ingestion. Both attempt to write a new manifest file, which causes an assertion failure.
### Performance Improvements
* Fix an issue where compactions were opening table files and reading table properties while holding db mutex_.
* Reduce unnecessary filesystem queries and DB mutex acquires in creating backups and checkpoints.
## 9.4.0 (06/23/2024)
### New Features
* Added a `CompactForTieringCollectorFactory` to auto trigger compaction for tiering use case.
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `GetEntityForUpdate` API.
* Added a new "count" command to the ldb repl shell. By default, it prints a count of keys in the database from start to end. The options --from=<key> and/or --to=<key> can be specified to limit the range.
* Add `rocksdb_writebatch_update_timestamps`, `rocksdb_writebatch_wi_update_timestamps` in C API.
* Add `rocksdb_iter_refresh` in C API.
* Add `rocksdb_writebatch_create_with_params`, `rocksdb_writebatch_wi_create_with_params` to create WB and WBWI with all options in C API
### Public API Changes
* Deprecated names `LogFile` and `VectorLogPtr` in favor of new names `WalFile` and `VectorWalPtr`.
* Introduce a new universal compaction option CompactionOptionsUniversal::max_read_amp which allows user to define the limit on the number of sorted runs separately from the trigger for compaction (`level0_file_num_compaction_trigger`) #12477.
### Behavior Changes
* Inactive WALs are immediately closed upon being fully sync-ed rather than in a background thread. This is to ensure LinkFile() is not called on files still open for write, which might not be supported by some FileSystem implementations. This should not be a performance issue, but an opt-out is available with with new DB option `background_close_inactive_wals`.
### Bug Fixes
* Fix a rare case in which a hard-linked WAL in a Checkpoint is not fully synced (so might lose data on power loss).
* Fixed the output of the `ldb dump_wal` command for `PutEntity` records so it prints the key and correctly resets the hexadecimal formatting flag after printing the wide-column entity.
* Fixed an issue where `PutEntity` records were handled incorrectly while rebuilding transactions during recovery.
* Various read operations could ignore various ReadOptions that might be relevant. Fixed many such cases, which can result in behavior change but a better reflection of specified options.
### Performance Improvements
* Improved write throughput to memtable when there's a large number of concurrent writers and allow_concurrent_memtable_write=true(#12545)
## 9.3.0 (05/17/2024)
### New Features
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `GetEntity` API.
* Added new `Iterator` property, "rocksdb.iterator.is-value-pinned", for checking whether the `Slice` returned by `Iterator::value()` can be used until the `Iterator` is destroyed.
* Optimistic transactions and WriteCommitted pessimistic transactions now support the `MultiGetEntity` API.
* Optimistic transactions and pessimistic transactions with the WriteCommitted policy now support the `PutEntity` API. Support for read APIs and other write policies (WritePrepared, WriteUnprepared) will be added later.
### Public API Changes
* Exposed block based metadata cache options via C API
* Exposed compaction pri via c api.
* Add a kAdmPolicyAllowAll option to TieredAdmissionPolicy that admits all blocks evicted from the primary block cache into the compressed secondary cache.
### Behavior Changes
* CompactRange() with change_level=true on a CF with FIFO compaction will return Status::NotSupported().
* External file ingestion with FIFO compaction will always ingest to L0.
### Bug Fixes
* Fixed a bug for databases using `DBOptions::allow_2pc == true` (all `TransactionDB`s except `OptimisticTransactionDB`) that have exactly one column family. Due to a missing WAL sync, attempting to open the DB could have returned a `Status::Corruption` with a message like "SST file is ahead of WALs".
* Fix a bug in CreateColumnFamilyWithImport() where if multiple CFs are imported, we were not resetting files' epoch number and L0 files can have overlapping key range but the same epoch number.
* Fixed race conditions when `ColumnFamilyOptions::inplace_update_support == true` between user overwrites and reads on the same key.
* Fix a bug where `CompactFiles()` can compact files of range conflict with other ongoing compactions' when `preclude_last_level_data_seconds > 0` is used
* Fixed a false positive `Status::Corruption` reported when reopening a DB that used `DBOptions::recycle_log_file_num > 0` and `DBOptions::wal_compression != kNoCompression`.
* While WAL is locked with LockWAL(), some operations like Flush() and IngestExternalFile() are now blocked as they should have been.
* Fixed a bug causing stale memory access when using the TieredSecondaryCache with an NVM secondary cache, and a file system that supports return an FS allocated buffer for MultiRead (FSSupportedOps::kFSBuffer is set).
## 9.2.1 (05/03/2024)
### Public API Changes
* Add a kAdmPolicyAllowAll option to TieredAdmissionPolicy that admits all blocks evicted from the primary block cache into the compressed secondary cache.
## 9.2.0 (05/01/2024)
### New Features
* Added two options `deadline` and `max_size_bytes` for CacheDumper to exit early
+15 -38
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)
@@ -2139,8 +2113,8 @@ ZLIB_DOWNLOAD_BASE ?= http://zlib.net
BZIP2_VER ?= 1.0.8
BZIP2_SHA256 ?= ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
BZIP2_DOWNLOAD_BASE ?= http://sourceware.org/pub/bzip2
SNAPPY_VER ?= 1.2.1
SNAPPY_SHA256 ?= 736aeb64d86566d2236ddffa2865ee5d7a82d26c9016b36218fcc27ea4f09f86
SNAPPY_VER ?= 1.1.8
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
LZ4_VER ?= 1.9.4
LZ4_SHA256 ?= 0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b
@@ -2237,7 +2211,7 @@ libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
-rm -rf snappy-$(SNAPPY_VER)
tar xvzf snappy-$(SNAPPY_VER).tar.gz
mkdir snappy-$(SNAPPY_VER)/build
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DSNAPPY_BUILD_BENCHMARKS=OFF -DSNAPPY_BUILD_TESTS=OFF --compile-no-warning-as-error ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
cd snappy-$(SNAPPY_VER)/build && CFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='$(ARCHFLAG) ${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
lz4-$(LZ4_VER).tar.gz:
@@ -2484,13 +2458,14 @@ checkout_folly:
fi
@# Pin to a particular version for public CI, so that PR authors don't
@# need to worry about folly breaking our integration. Update periodically
cd third-party/folly && git reset --hard c48fdd205c1c291651749d532b8055fe822bba25
cd third-party/folly && git reset --hard beacd86d63cd71c904632262e6c36f60874d78ba
@# A hack to remove boost dependency.
@# NOTE: this hack is only needed if building using USE_FOLLY_LITE
perl -pi -e 's/^(#include <boost)/\/\/$$1/' third-party/folly/folly/functional/Invoke.h
@# NOTE: this hack is required for clang in some cases
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
@# NOTE: this hack is required for gcc in some cases
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
@# NOTE: boost source will be needed for any build including `USE_FOLLY_LITE` builds as those depend on boost headers
cd third-party/folly && $(PYTHON) build/fbcode_builder/getdeps.py fetch boost
CXX_M_FLAGS = $(filter -m%, $(CXXFLAGS))
@@ -2502,6 +2477,8 @@ build_folly:
echo "Please run checkout_folly first"; \
false; \
fi
# Restore the original version of Invoke.h with boost dependency
cd third-party/folly && ${GIT_COMMAND} checkout folly/functional/Invoke.h
cd third-party/folly && \
CXXFLAGS=" $(CXX_M_FLAGS) -DHAVE_CXX11_ATOMIC " $(PYTHON) build/fbcode_builder/getdeps.py build --no-tests
-11
View File
@@ -4,7 +4,6 @@
# This file is a Facebook-specific integration for buck builds, so can
# only be validated by Facebook employees.
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
cpp_library_wrapper(name="rocksdb_lib", srcs=[
@@ -317,7 +316,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 +393,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 +410,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 +4623,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
View File
@@ -7,7 +7,6 @@ rocksdb_target_header_template = """# This file \100generated by:
# This file is a Facebook-specific integration for buck builds, so can
# only be validated by Facebook employees.
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
"""
+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) {
+4 -4
View File
@@ -138,10 +138,10 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
reinit_internal_iter();
break;
} else {
*memtable_range_tombstone_iter_ =
std::make_unique<TruncatedRangeDelIterator>(
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
&cfd->internal_comparator(), nullptr, nullptr);
delete *memtable_range_tombstone_iter_;
*memtable_range_tombstone_iter_ = new TruncatedRangeDelIterator(
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
&cfd->internal_comparator(), nullptr, nullptr);
}
}
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
+2 -4
View File
@@ -55,8 +55,7 @@ class ArenaWrappedDBIter : public Iterator {
db_iter_->SetIter(iter);
}
void SetMemtableRangetombstoneIter(
std::unique_ptr<TruncatedRangeDelIterator>* iter) {
void SetMemtableRangetombstoneIter(TruncatedRangeDelIterator** iter) {
memtable_range_tombstone_iter_ = iter;
}
@@ -111,8 +110,7 @@ class ArenaWrappedDBIter : public Iterator {
bool allow_refresh_ = true;
// If this is nullptr, it means the mutable memtable does not contain range
// tombstone when added under this DBIter.
std::unique_ptr<TruncatedRangeDelIterator>* memtable_range_tombstone_iter_ =
nullptr;
TruncatedRangeDelIterator** memtable_range_tombstone_iter_ = nullptr;
};
// Generate the arena wrapped iterator class.
+2 -2
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 -231
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;
@@ -532,42 +531,6 @@ struct rocksdb_universal_compaction_options_t {
ROCKSDB_NAMESPACE::CompactionOptionsUniversal* rep;
};
struct rocksdb_callback_logger_t : public Logger {
static const ssize_t STACK_BUFSZ = 512;
rocksdb_callback_logger_t(InfoLogLevel log_level,
void (*logv_cb)(void*, unsigned, char*, size_t),
void* priv)
: Logger(log_level), logv_cb_(logv_cb), priv_(priv) {}
using Logger::Logv;
void Logv(const InfoLogLevel level, const char* fmt, va_list ap0) override {
char stack_buf[STACK_BUFSZ];
char* alloc_buf = nullptr;
char* buf = stack_buf;
int len = 0;
va_list ap1;
if (!logv_cb_) return;
va_copy(ap1, ap0);
len = vsnprintf(buf, STACK_BUFSZ, fmt, ap0);
if (len <= 0)
goto cleanup;
else if (len >= STACK_BUFSZ) {
buf = alloc_buf = reinterpret_cast<char*>(malloc(len + 1));
if (!buf) goto cleanup;
len = vsnprintf(buf, len + 1, fmt, ap1);
if (len <= 0) goto cleanup;
}
logv_cb_(priv_, unsigned(level), buf, size_t(len));
cleanup:
va_end(ap1);
free(alloc_buf);
}
private:
void (*logv_cb_)(void*, unsigned, char*, size_t) = nullptr;
void* priv_ = nullptr;
};
static bool SaveError(char** errptr, const Status& s) {
assert(errptr != nullptr);
if (s.ok()) {
@@ -583,7 +546,6 @@ static bool SaveError(char** errptr, const Status& s) {
return true;
}
// Copies str to a new malloc()-ed buffer. The buffer is not NUL terminated.
static char* CopyString(const std::string& str) {
char* result = reinterpret_cast<char*>(malloc(sizeof(char) * str.size()));
memcpy(result, str.data(), sizeof(char) * str.size());
@@ -944,7 +906,6 @@ rocksdb_t* rocksdb_open_and_trim_history(
rocksdb_column_family_handle_t* c_handle =
new rocksdb_column_family_handle_t;
c_handle->rep = handles[i];
c_handle->immortal = false;
column_family_handles[i] = c_handle;
}
rocksdb_t* result = new rocksdb_t;
@@ -975,7 +936,6 @@ rocksdb_t* rocksdb_open_column_families(
rocksdb_column_family_handle_t* c_handle =
new rocksdb_column_family_handle_t;
c_handle->rep = handles[i];
c_handle->immortal = false;
column_family_handles[i] = c_handle;
}
rocksdb_t* result = new rocksdb_t;
@@ -1011,7 +971,6 @@ rocksdb_t* rocksdb_open_column_families_with_ttl(
rocksdb_column_family_handle_t* c_handle =
new rocksdb_column_family_handle_t;
c_handle->rep = handles[i];
c_handle->immortal = false;
column_family_handles[i] = c_handle;
}
rocksdb_t* result = new rocksdb_t;
@@ -1045,7 +1004,6 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
rocksdb_column_family_handle_t* c_handle =
new rocksdb_column_family_handle_t;
c_handle->rep = handles[i];
c_handle->immortal = false;
column_family_handles[i] = c_handle;
}
rocksdb_t* result = new rocksdb_t;
@@ -1077,7 +1035,6 @@ rocksdb_t* rocksdb_open_as_secondary_column_families(
rocksdb_column_family_handle_t* c_handle =
new rocksdb_column_family_handle_t;
c_handle->rep = handles[i];
c_handle->immortal = false;
column_family_handles[i] = c_handle;
}
rocksdb_t* result = new rocksdb_t;
@@ -1115,7 +1072,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family(
SaveError(errptr, db->rep->CreateColumnFamily(
ColumnFamilyOptions(column_family_options->rep),
std::string(column_family_name), &(handle->rep)));
handle->immortal = false;
return handle;
}
@@ -1139,7 +1095,6 @@ rocksdb_column_family_handle_t** rocksdb_create_column_families(
for (size_t i = 0; i != handles.size(); ++i) {
c_handles[i] = new rocksdb_column_family_handle_t;
c_handles[i]->rep = handles[i];
c_handles[i]->immortal = false;
}
return c_handles;
@@ -1159,7 +1114,6 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family_with_ttl(
SaveError(errptr, db_with_ttl->CreateColumnFamilyWithTtl(
ColumnFamilyOptions(column_family_options->rep),
std::string(column_family_name), &(handle->rep), ttl));
handle->immortal = false;
return handle;
}
@@ -1181,19 +1135,9 @@ char* rocksdb_column_family_handle_get_name(
return CopyString(name);
}
rocksdb_column_family_handle_t* rocksdb_get_default_column_family_handle(
rocksdb_t* db) {
rocksdb_column_family_handle_t* handle = new rocksdb_column_family_handle_t;
handle->rep = db->rep->DefaultColumnFamily();
handle->immortal = true;
return handle;
}
void rocksdb_column_family_handle_destroy(
rocksdb_column_family_handle_t* handle) {
if (!handle->immortal) {
delete handle->rep;
}
delete handle->rep;
delete handle;
}
@@ -1429,18 +1373,6 @@ char* rocksdb_get_cf_with_ts(rocksdb_t* db,
return result;
}
char* rocksdb_get_db_identity(rocksdb_t* db, size_t* id_len) {
std::string identity_tmp;
Status s = db->rep->GetDbIdentity(identity_tmp);
if (!s.ok()) {
*id_len = 0;
return nullptr;
}
*id_len = identity_tmp.size();
return CopyString(identity_tmp);
}
void rocksdb_multi_get(rocksdb_t* db, const rocksdb_readoptions_t* options,
size_t num_keys, const char* const* keys_list,
const size_t* keys_list_sizes, char** values_list,
@@ -1838,26 +1770,6 @@ void rocksdb_approximate_sizes_cf(
delete[] ranges;
}
extern ROCKSDB_LIBRARY_API void rocksdb_approximate_sizes_cf_with_flags(
rocksdb_t* db, rocksdb_column_family_handle_t* column_family,
int num_ranges, const char* const* range_start_key,
const size_t* range_start_key_len, const char* const* range_limit_key,
const size_t* range_limit_key_len, uint8_t include_flags, uint64_t* sizes,
char** errptr) {
Range* ranges = new Range[num_ranges];
for (int i = 0; i < num_ranges; i++) {
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
}
Status s = db->rep->GetApproximateSizes(
column_family->rep, ranges, num_ranges, sizes,
static_cast<DB::SizeApproximationFlags>(include_flags));
if (!s.ok()) {
SaveError(errptr, s);
}
delete[] ranges;
}
void rocksdb_delete_file(rocksdb_t* db, const char* name) {
db->rep->DeleteFile(name);
}
@@ -2035,10 +1947,6 @@ void rocksdb_iter_get_error(const rocksdb_iterator_t* iter, char** errptr) {
SaveError(errptr, iter->rep->status());
}
void rocksdb_iter_refresh(const rocksdb_iterator_t* iter, char** errptr) {
SaveError(errptr, iter->rep->Refresh());
}
rocksdb_writebatch_t* rocksdb_writebatch_create() {
return new rocksdb_writebatch_t;
}
@@ -2050,15 +1958,6 @@ rocksdb_writebatch_t* rocksdb_writebatch_create_from(const char* rep,
return b;
}
rocksdb_writebatch_t* rocksdb_writebatch_create_with_params(
size_t reserved_bytes, size_t max_bytes, size_t protection_bytes_per_key,
size_t default_cf_ts_sz) {
rocksdb_writebatch_t* b = new rocksdb_writebatch_t;
b->rep = WriteBatch(reserved_bytes, max_bytes, protection_bytes_per_key,
default_cf_ts_sz);
return b;
}
void rocksdb_writebatch_destroy(rocksdb_writebatch_t* b) { delete b; }
void rocksdb_writebatch_clear(rocksdb_writebatch_t* b) { b->rep.Clear(); }
@@ -2286,32 +2185,6 @@ class H : public WriteBatch::Handler {
}
};
class HCF : public WriteBatch::Handler {
public:
void* state_;
void (*put_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen);
void (*deleted_cf_)(void*, uint32_t cfid, const char* k, size_t klen);
void (*merge_cf_)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen);
Status PutCF(uint32_t column_family_id, const Slice& key,
const Slice& value) override {
(*put_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
value.size());
return Status::OK();
}
Status DeleteCF(uint32_t column_family_id, const Slice& key) override {
(*deleted_cf_)(state_, column_family_id, key.data(), key.size());
return Status::OK();
}
Status MergeCF(uint32_t column_family_id, const Slice& key,
const Slice& value) override {
(*merge_cf_)(state_, column_family_id, key.data(), key.size(), value.data(),
value.size());
return Status::OK();
}
};
void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
void (*put)(void*, const char* k, size_t klen,
const char* v, size_t vlen),
@@ -2324,21 +2197,6 @@ void rocksdb_writebatch_iterate(rocksdb_writebatch_t* b, void* state,
b->rep.Iterate(&handler);
}
void rocksdb_writebatch_iterate_cf(
rocksdb_writebatch_t* b, void* state,
void (*put_cf)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen),
void (*deleted_cf)(void*, uint32_t cfid, const char* k, size_t klen),
void (*merge_cf)(void*, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen)) {
HCF handler;
handler.state_ = state;
handler.put_cf_ = put_cf;
handler.deleted_cf_ = deleted_cf;
handler.merge_cf_ = merge_cf;
b->rep.Iterate(&handler);
}
const char* rocksdb_writebatch_data(rocksdb_writebatch_t* b, size_t* size) {
*size = b->rep.GetDataSize();
return b->rep.Data().c_str();
@@ -2365,35 +2223,6 @@ rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create(
return b;
}
rocksdb_writebatch_wi_t* rocksdb_writebatch_wi_create_with_params(
rocksdb_comparator_t* backup_index_comparator, size_t reserved_bytes,
unsigned char overwrite_key, size_t max_bytes,
size_t protection_bytes_per_key) {
rocksdb_writebatch_wi_t* b = new rocksdb_writebatch_wi_t;
b->rep = new WriteBatchWithIndex(backup_index_comparator, reserved_bytes,
overwrite_key, max_bytes,
protection_bytes_per_key);
return b;
}
void rocksdb_writebatch_update_timestamps(
rocksdb_writebatch_t* wb, const char* ts, size_t tslen, void* state,
size_t (*get_ts_size)(void*, uint32_t), char** errptr) {
SaveError(errptr, wb->rep.UpdateTimestamps(
Slice(ts, tslen), [&get_ts_size, &state](uint32_t cf) {
return (*get_ts_size)(state, cf);
}));
}
void rocksdb_writebatch_wi_update_timestamps(
rocksdb_writebatch_wi_t* wb, const char* ts, size_t tslen, void* state,
size_t (*get_ts_size)(void*, uint32_t), char** errptr) {
SaveError(errptr, wb->rep->GetWriteBatch()->UpdateTimestamps(
Slice(ts, tslen), [&get_ts_size, &state](uint32_t cf) {
return (*get_ts_size)(state, cf);
}));
}
void rocksdb_writebatch_wi_destroy(rocksdb_writebatch_wi_t* b) {
if (b->rep) {
delete b->rep;
@@ -2916,24 +2745,6 @@ void rocksdb_options_set_block_based_table_factory(
}
}
void rocksdb_block_based_options_set_top_level_index_pinning_tier(
rocksdb_block_based_table_options_t* options, int v) {
options->rep.metadata_cache_options.top_level_index_pinning =
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
}
void rocksdb_block_based_options_set_partition_pinning_tier(
rocksdb_block_based_table_options_t* options, int v) {
options->rep.metadata_cache_options.partition_pinning =
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
}
void rocksdb_block_based_options_set_unpartitioned_pinning_tier(
rocksdb_block_based_table_options_t* options, int v) {
options->rep.metadata_cache_options.unpartitioned_pinning =
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
}
rocksdb_cuckoo_table_options_t* rocksdb_cuckoo_options_create() {
return new rocksdb_cuckoo_table_options_t;
}
@@ -3134,15 +2945,6 @@ rocksdb_logger_t* rocksdb_logger_create_stderr_logger(int log_level,
return logger;
}
rocksdb_logger_t* rocksdb_logger_create_callback_logger(
int log_level, void (*callback)(void*, unsigned, char*, size_t),
void* priv) {
rocksdb_logger_t* logger = new rocksdb_logger_t;
logger->rep = std::make_shared<rocksdb_callback_logger_t>(
static_cast<InfoLogLevel>(log_level), callback, priv);
return logger;
}
void rocksdb_logger_destroy(rocksdb_logger_t* logger) { delete logger; }
void rocksdb_options_set_env(rocksdb_options_t* opt, rocksdb_env_t* env) {
@@ -4065,27 +3867,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;
@@ -4149,14 +3930,6 @@ void rocksdb_options_set_fifo_compaction_options(
opt->rep.compaction_options_fifo = fifo->rep;
}
void rocksdb_options_set_compaction_pri(rocksdb_options_t* opt, int pri) {
opt->rep.compaction_pri = static_cast<ROCKSDB_NAMESPACE::CompactionPri>(pri);
}
int rocksdb_options_get_compaction_pri(rocksdb_options_t* opt) {
return opt->rep.compaction_pri;
}
char* rocksdb_options_statistics_get_string(rocksdb_options_t* opt) {
ROCKSDB_NAMESPACE::Statistics* statistics = opt->rep.statistics.get();
if (statistics) {
@@ -4465,8 +4238,13 @@ void rocksdb_perfcontext_destroy(rocksdb_perfcontext_t* context) {
/*
TODO:
DB::OpenForReadOnly
DB::KeyMayExist
DB::GetOptions
DB::GetSortedWalFiles
DB::GetLatestSequenceNumber
DB::GetUpdatesSince
DB::GetDbIdentity
DB::RunManualCompaction
custom cache
table_properties_collectors
@@ -5943,7 +5721,6 @@ rocksdb_column_family_handle_t* rocksdb_transactiondb_create_column_family(
SaveError(errptr, txn_db->rep->CreateColumnFamily(
ColumnFamilyOptions(column_family_options->rep),
std::string(column_family_name), &(handle->rep)));
handle->immortal = false;
return handle;
}
@@ -5986,7 +5763,6 @@ rocksdb_transactiondb_t* rocksdb_transactiondb_open_column_families(
rocksdb_column_family_handle_t* c_handle =
new rocksdb_column_family_handle_t;
c_handle->rep = handles[i];
c_handle->immortal = false;
column_family_handles[i] = c_handle;
}
rocksdb_transactiondb_t* result = new rocksdb_transactiondb_t;
@@ -6781,7 +6557,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 =
+2 -200
View File
@@ -199,79 +199,6 @@ static void CheckDel(void* ptr, const char* k, size_t klen) {
(*state)++;
}
// Callback from rocksdb_writebatch_iterate_cf()
static void CheckPutCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen) {
int* state = (int*)ptr;
switch (*state) {
case 0:
CheckEqual("bar", k, klen);
CheckEqual("b", v, vlen);
CheckCondition(cfid == 1);
break;
case 1:
CheckEqual("box", k, klen);
CheckEqual("c", v, vlen);
CheckCondition(cfid == 1);
break;
case 4:
CheckEqual("foo", k, klen);
CheckEqual("f", v, vlen);
CheckCondition(cfid == 0);
break;
case 6:
CheckEqual("baz", k, klen);
CheckEqual("a", v, vlen);
CheckCondition(cfid == 0);
break;
default:
CheckCondition(false);
break;
}
(*state)++;
}
// Callback from rocksdb_writebatch_iterate_cf()
static void CheckDelCF(void* ptr, uint32_t cfid, const char* k, size_t klen) {
int* state = (int*)ptr;
switch (*state) {
case 2:
CheckEqual("bar", k, klen);
CheckCondition(cfid == 1);
break;
case 5:
CheckEqual("foo", k, klen);
CheckCondition(cfid == 0);
break;
default:
CheckCondition(false);
break;
}
(*state)++;
}
// Callback from rocksdb_writebatch_iterate_cf()
static void CheckMergeCF(void* ptr, uint32_t cfid, const char* k, size_t klen,
const char* v, size_t vlen) {
int* state = (int*)ptr;
switch (*state) {
case 3:
CheckEqual("box", k, klen);
CheckEqual("cc", v, vlen);
CheckCondition(cfid == 1);
break;
case 7:
CheckEqual("baz", k, klen);
CheckEqual("aa", v, vlen);
CheckCondition(cfid == 0);
break;
default:
CheckCondition(false);
break;
}
(*state)++;
}
static void CmpDestroy(void* arg) { (void)arg; }
static int CmpCompare(void* arg, const char* a, size_t alen, const char* b,
@@ -777,10 +704,6 @@ int main(int argc, char** argv) {
rocksdb_block_based_options_set_block_cache(table_options, cache);
rocksdb_block_based_options_set_data_block_index_type(table_options, 1);
rocksdb_block_based_options_set_data_block_hash_ratio(table_options, 0.75);
rocksdb_block_based_options_set_top_level_index_pinning_tier(table_options,
1);
rocksdb_block_based_options_set_partition_pinning_tier(table_options, 2);
rocksdb_block_based_options_set_unpartitioned_pinning_tier(table_options, 3);
rocksdb_options_set_block_based_table_factory(options, table_options);
rocksdb_options_set_compression(options, rocksdb_no_compression);
@@ -871,11 +794,6 @@ int main(int argc, char** argv) {
rocksdb_delete(db, woptions, "foo", 3, &err);
CheckNoError(err);
// get the identity before the backup
size_t before_db_id_len = 0;
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
CheckCondition(before_db_id_len == 36);
rocksdb_close(db);
rocksdb_destroy_db(options, dbname, &err);
@@ -896,14 +814,6 @@ int main(int argc, char** argv) {
CheckGet(db, roptions, "foo", "hello");
// the db_identity after the backup is different
size_t after_db_id_len = 0;
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
CheckCondition(after_db_id_len == 36);
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) != 0);
Free(&before_db_id);
Free(&after_db_id);
rocksdb_backup_engine_close(be);
}
@@ -919,14 +829,6 @@ int main(int argc, char** argv) {
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
CheckNoError(err);
rocksdb_checkpoint_object_destroy(checkpoint);
checkpoint = NULL;
// get the identity before the checkpoint
size_t before_db_id_len = 0;
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
CheckCondition(before_db_id_len == 36);
// start a new database from the checkpoint
rocksdb_close(db);
rocksdb_options_set_error_if_exists(options, 0);
@@ -935,13 +837,7 @@ int main(int argc, char** argv) {
CheckGet(db, roptions, "foo", "hello");
// the db_identity after the checkpoint is different
size_t after_db_id_len = 0;
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
CheckCondition(after_db_id_len == 36);
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) != 0);
Free(&before_db_id);
Free(&after_db_id);
rocksdb_checkpoint_object_destroy(checkpoint);
rocksdb_close(db);
rocksdb_destroy_db(options, dbcheckpointname, &err);
@@ -952,73 +848,6 @@ int main(int argc, char** argv) {
rocksdb_options_set_error_if_exists(options, 1);
}
StartPhase("checkpoint_db_id_in_manifest");
{
// create new DB with set_write_dbid_to_manifest=true
// db_identity is now the same across checkpoints
rocksdb_close(db);
db = NULL;
rocksdb_options_t* options_dbid_in_manifest = rocksdb_options_create();
rocksdb_options_set_create_if_missing(options_dbid_in_manifest, 1);
unsigned char write_to_manifest =
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
CheckCondition(!write_to_manifest);
rocksdb_options_set_write_dbid_to_manifest(options_dbid_in_manifest, true);
CheckCondition(!write_to_manifest);
write_to_manifest =
rocksdb_options_get_write_dbid_to_manifest(options_dbid_in_manifest);
CheckCondition(write_to_manifest);
db = rocksdb_open(options_dbid_in_manifest, dbbackupname, &err);
CheckNoError(err);
rocksdb_checkpoint_t* checkpoint =
rocksdb_checkpoint_object_create(db, &err);
CheckNoError(err);
rocksdb_checkpoint_create(checkpoint, dbcheckpointname, 0, &err);
CheckNoError(err);
rocksdb_checkpoint_object_destroy(checkpoint);
checkpoint = NULL;
// get the identity before the backup
size_t before_db_id_len = 0;
char* before_db_id = rocksdb_get_db_identity(db, &before_db_id_len);
CheckCondition(before_db_id_len == 36);
// open the checkpoint
rocksdb_close(db);
rocksdb_destroy_db(options_dbid_in_manifest, dbbackupname, &err);
CheckNoError(err);
rocksdb_options_set_error_if_exists(options_dbid_in_manifest, 0);
db = rocksdb_open(options_dbid_in_manifest, dbcheckpointname, &err);
CheckNoError(err);
// the db_identity after the checkpoint is the same
size_t after_db_id_len = 0;
char* after_db_id = rocksdb_get_db_identity(db, &after_db_id_len);
CheckCondition(after_db_id_len == 36);
CheckCondition(memcmp(after_db_id, before_db_id, after_db_id_len) == 0);
Free(&before_db_id);
Free(&after_db_id);
rocksdb_close(db);
rocksdb_destroy_db(options_dbid_in_manifest, dbcheckpointname, &err);
CheckNoError(err);
rocksdb_options_destroy(options_dbid_in_manifest);
options_dbid_in_manifest = NULL;
// re-open the default database
rocksdb_options_set_error_if_exists(options, 0);
db = rocksdb_open(options, dbname, &err);
CheckNoError(err);
rocksdb_options_set_error_if_exists(options, 1);
}
StartPhase("compactall");
rocksdb_compact_range(db, NULL, 0, NULL, 0);
CheckGet(db, roptions, "foo", "hello");
@@ -1761,21 +1590,6 @@ int main(int argc, char** argv) {
CheckPinGetCF(db, roptions, handles[1], "bar", NULL);
CheckPinGetCF(db, roptions, handles[1], "box", "c");
CheckPinGetCF(db, roptions, handles[1], "buff", "rocksdb");
rocksdb_writebatch_clear(wb);
// Test WriteBatch iteration with Column Family
int pos = 0;
rocksdb_writebatch_put_cf(wb, handles[1], "bar", 3, "b", 1);
rocksdb_writebatch_put_cf(wb, handles[1], "box", 3, "c", 1);
rocksdb_writebatch_delete_cf(wb, handles[1], "bar", 3);
rocksdb_writebatch_merge_cf(wb, handles[1], "box", 3, "cc", 2);
rocksdb_writebatch_put(wb, "foo", 3, "f", 1);
rocksdb_writebatch_delete(wb, "foo", 3);
rocksdb_writebatch_put(wb, "baz", 3, "a", 1);
rocksdb_writebatch_merge(wb, "baz", 3, "aa", 2);
rocksdb_writebatch_iterate_cf(wb, &pos, CheckPutCF, CheckDelCF,
CheckMergeCF);
CheckCondition(pos == 8);
rocksdb_writebatch_clear(wb);
rocksdb_writebatch_destroy(wb);
rocksdb_flush_wal(db, 1, &err);
@@ -2227,6 +2041,7 @@ int main(int argc, char** argv) {
rocksdb_options_set_advise_random_on_open(o, 1);
CheckCondition(1 == rocksdb_options_get_advise_random_on_open(o));
rocksdb_options_set_use_adaptive_mutex(o, 1);
CheckCondition(1 == rocksdb_options_get_use_adaptive_mutex(o));
@@ -2297,9 +2112,6 @@ int main(int argc, char** argv) {
rocksdb_options_set_compaction_style(o, 2);
CheckCondition(2 == rocksdb_options_get_compaction_style(o));
rocksdb_options_set_compaction_pri(o, 4);
CheckCondition(4 == rocksdb_options_get_compaction_pri(o));
rocksdb_options_set_atomic_flush(o, 1);
CheckCondition(1 == rocksdb_options_get_atomic_flush(o));
@@ -2322,12 +2134,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));
@@ -2810,10 +2616,6 @@ int main(int argc, char** argv) {
CheckCondition(1 == rocksdb_options_get_compaction_style(copy));
CheckCondition(2 == rocksdb_options_get_compaction_style(o));
rocksdb_options_set_compaction_pri(copy, 1);
CheckCondition(1 == rocksdb_options_get_compaction_pri(copy));
CheckCondition(4 == rocksdb_options_get_compaction_pri(o));
rocksdb_options_set_atomic_flush(copy, 0);
CheckCondition(0 == rocksdb_options_get_atomic_flush(copy));
CheckCondition(1 == rocksdb_options_get_atomic_flush(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) {
+1 -8
View File
@@ -63,9 +63,7 @@ uint64_t TotalFileSize(const std::vector<FileMetaData*>& files) {
return sum;
}
// TODO(hx235): consider making this function part of the construction so we
// don't forget to call it
void Compaction::FinalizeInputInfo(Version* _input_version) {
void Compaction::SetInputVersion(Version* _input_version) {
input_version_ = _input_version;
cfd_ = input_version_->cfd();
@@ -866,11 +864,6 @@ bool Compaction::ShouldFormSubcompactions() const {
return false;
}
if (cfd_->ioptions()->table_factory->Name() ==
TableFactory::kPlainTableName()) {
return false;
}
// Round-Robin pri under leveled compaction allows subcompactions by default
// and the number of subcompactions can be larger than max_subcompactions_
if (cfd_->ioptions()->compaction_pri == kRoundRobin &&
+9 -11
View File
@@ -289,7 +289,14 @@ class Compaction {
// is the sum of all input file sizes.
uint64_t OutputFilePreallocationSize() const;
void FinalizeInputInfo(Version* input_version);
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
// status and fail the compaction if needed
// TODO(hx235): consider making this function part of the construction so we
// don't forget to call it
void FinalizeInputInfo(Version* input_version) {
SetInputVersion(input_version);
InitInputTableProperties().PermitUncheckedError();
}
struct InputLevelSummaryBuffer {
char buffer[128];
@@ -326,16 +333,6 @@ class Compaction {
int output_level, VersionStorageInfo* vstorage,
const std::vector<CompactionInputFiles>& inputs);
// TODO(hx235): eventually we should consider `InitInputTableProperties()`'s
// status and fail the compaction if needed
//
// May open and read table files for table property.
// Should not be called while holding mutex_.
const TablePropertiesCollection& GetOrInitInputTableProperties() {
InitInputTableProperties().PermitUncheckedError();
return input_table_properties_;
}
const TablePropertiesCollection& GetInputTableProperties() const {
return input_table_properties_;
}
@@ -436,6 +433,7 @@ class Compaction {
const int output_level);
private:
void SetInputVersion(Version* input_version);
Status InitInputTableProperties();
+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 -22
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.
@@ -468,11 +481,6 @@ void CompactionJob::GenSubcompactionBoundaries() {
ReadOptions read_options(Env::IOActivity::kCompaction);
read_options.rate_limiter_priority = GetRateLimiterPriority();
auto* c = compact_->compaction;
if (c->immutable_options()->table_factory->Name() ==
TableFactory::kPlainTableName()) {
return;
}
if (c->max_subcompactions() <= 1 &&
!(c->immutable_options()->compaction_pri == kRoundRobin &&
c->immutable_options()->compaction_style == kCompactionStyleLevel)) {
@@ -626,7 +634,6 @@ Status CompactionJob::Run() {
const size_t num_threads = compact_->sub_compact_states.size();
assert(num_threads > 0);
const uint64_t start_micros = db_options_.clock->NowMicros();
compact_->compaction->GetOrInitInputTableProperties();
// Launch a thread for each of subcompactions 1...num_threads-1
std::vector<port::Thread> thread_pool;
@@ -1288,9 +1295,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
auto c_iter = std::make_unique<CompactionIterator>(
input, cfd->user_comparator(), &merge, versions_->LastSequence(),
&existing_snapshots_, earliest_snapshot_,
earliest_write_conflict_snapshot_, job_snapshot_seq, snapshot_checker_,
env_, ShouldReportDetailedTime(env_, stats_),
&existing_snapshots_, earliest_write_conflict_snapshot_, job_snapshot_seq,
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_),
/*expect_valid_internal_key=*/true, range_del_agg.get(),
blob_file_builder.get(), db_options_.allow_data_in_errors,
db_options_.enforce_single_del_contracts, manual_compaction_canceled_,
@@ -1963,10 +1969,7 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
cfd->GetName(), sub_compact->compaction->output_level(),
bottommost_level_, TableFileCreationReason::kCompaction,
0 /* oldest_key_time */, current_time, db_id_, db_session_id_,
sub_compact->compaction->max_output_file_size(), file_number,
preclude_last_level_min_seqno_ == kMaxSequenceNumber
? preclude_last_level_min_seqno_
: std::min(earliest_snapshot_, preclude_last_level_min_seqno_));
sub_compact->compaction->max_output_file_size(), file_number);
outputs.NewBuilder(tboptions);
-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.
+10 -24
View File
@@ -340,8 +340,8 @@ Compaction* CompactionPicker::CompactFiles(
#ifndef NDEBUG
assert(input_files.size());
// This compaction output should not overlap with a running compaction as
// `SanitizeAndConvertCompactionInputFiles` should've checked earlier and db
// mutex shouldn't have been released since.
// `SanitizeCompactionInputFiles` should've checked earlier and db mutex
// shouldn't have been released since.
int start_level = Compaction::kInvalidLevel;
for (const auto& in : input_files) {
// input_files should already be sorted by level
@@ -1040,18 +1040,19 @@ Status CompactionPicker::SanitizeCompactionInputFilesForAllLevels(
}
}
}
if (RangeOverlapWithCompaction(smallestkey, largestkey, output_level)) {
return Status::Aborted(
"A running compaction is writing to the same output level in an "
"overlapping key range");
}
return Status::OK();
}
Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
Status CompactionPicker::SanitizeCompactionInputFiles(
std::unordered_set<uint64_t>* input_files,
const ColumnFamilyMetaData& cf_meta, const int output_level,
const VersionStorageInfo* vstorage,
std::vector<CompactionInputFiles>* converted_input_files) const {
const ColumnFamilyMetaData& cf_meta, const int output_level) const {
assert(static_cast<int>(cf_meta.levels.size()) - 1 ==
cf_meta.levels[cf_meta.levels.size() - 1].level);
assert(converted_input_files);
if (output_level >= static_cast<int>(cf_meta.levels.size())) {
return Status::InvalidArgument(
"Output level for column family " + cf_meta.name +
@@ -1077,6 +1078,7 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
Status s = SanitizeCompactionInputFilesForAllLevels(input_files, cf_meta,
output_level);
if (!s.ok()) {
return s;
}
@@ -1117,22 +1119,6 @@ Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
}
}
s = GetCompactionInputsFromFileNumbers(converted_input_files, input_files,
vstorage, CompactionOptions());
if (!s.ok()) {
return s;
}
assert(converted_input_files->size() > 0);
if (output_level != 0 &&
FilesRangeOverlapWithCompaction(
*converted_input_files, output_level,
Compaction::EvaluatePenultimateLevel(
vstorage, ioptions_, (*converted_input_files)[0].level,
output_level))) {
return Status::Aborted(
"A running compaction is writing to the same output level(s) in an "
"overlapping key range");
}
return Status::OK();
}
+14 -19
View File
@@ -86,20 +86,16 @@ class CompactionPicker {
virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const = 0;
// Sanitize the input set of compaction input files and convert it to
// `std::vector<CompactionInputFiles>` in the output parameter
// `converted_input_files`.
// When the input parameters do not describe a valid
// compaction, the function will try to fix the input_files by adding
// necessary files. If it's not possible to convert an invalid input_files
// into a valid one by adding more files, the function will return a
// non-ok status with specific reason.
//
Status SanitizeAndConvertCompactionInputFiles(
std::unordered_set<uint64_t>* input_files,
const ColumnFamilyMetaData& cf_meta, const int output_level,
const VersionStorageInfo* vstorage,
std::vector<CompactionInputFiles>* converted_input_files) const;
// Sanitize the input set of compaction input files.
// When the input parameters do not describe a valid compaction, the
// function will try to fix the input_files by adding necessary
// files. If it's not possible to conver an invalid input_files
// into a valid one by adding more files, the function will return a
// non-ok status with specific reason.
//
Status SanitizeCompactionInputFiles(std::unordered_set<uint64_t>* input_files,
const ColumnFamilyMetaData& cf_meta,
const int output_level) const;
// Free up the files that participated in a compaction
//
@@ -113,8 +109,8 @@ class CompactionPicker {
// object.
//
// Caller must provide a set of input files that has been passed through
// `SanitizeAndConvertCompactionInputFiles` earlier. The lock should not be
// released between that call and this one.
// `SanitizeCompactionInputFiles` earlier. The lock should not be released
// between that call and this one.
Compaction* CompactFiles(const CompactionOptions& compact_options,
const std::vector<CompactionInputFiles>& input_files,
int output_level, VersionStorageInfo* vstorage,
@@ -124,7 +120,6 @@ class CompactionPicker {
// Converts a set of compaction input file numbers into
// a list of CompactionInputFiles.
// TODO(hx235): remove the unused paramter `compact_options`
Status GetCompactionInputsFromFileNumbers(
std::vector<CompactionInputFiles>* input_files,
std::unordered_set<uint64_t>* input_set,
@@ -230,8 +225,8 @@ class CompactionPicker {
protected:
const ImmutableOptions& ioptions_;
// A helper function to SanitizeAndConvertCompactionInputFiles() that
// sanitizes "input_files" by adding necessary files.
// A helper function to SanitizeCompactionInputFiles() that
// sanitizes "input_files" by adding necessary files.
virtual Status SanitizeCompactionInputFilesForAllLevels(
std::unordered_set<uint64_t>* input_files,
const ColumnFamilyMetaData& cf_meta, const int output_level) const;
-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_);
}
}
}
-2
View File
@@ -275,8 +275,6 @@ Status CompactionServiceCompactionJob::Run() {
log_buffer_->FlushBufferToLog();
LogCompaction();
const uint64_t start_micros = db_options_.clock->NowMicros();
c->GetOrInitInputTableProperties();
// Pick the only sub-compaction we should have
assert(compact_->sub_compact_states.size() == 1);
SubcompactionState* sub_compact = compact_->sub_compact_states.data();
-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,
+43 -107
View File
@@ -322,67 +322,6 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
}
}
TEST_F(DBBloomFilterTest, FilterNumEntriesCoalesce) {
for (bool partition_filters : {true, false}) {
SCOPED_TRACE("partition_filters=" + std::to_string(partition_filters));
for (bool prefix : {true, false}) {
SCOPED_TRACE("prefix=" + std::to_string(prefix));
for (bool whole : {true, false}) {
SCOPED_TRACE("whole=" + std::to_string(whole));
Options options = last_options_;
options.prefix_extractor.reset();
if (prefix) {
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
}
BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(10));
bbto.whole_key_filtering = whole;
if (partition_filters) {
bbto.partition_filters = true;
bbto.index_type =
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
}
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
DestroyAndReopen(options);
// Need a snapshot to allow keeping multiple entries for the same key
std::vector<const Snapshot*> snapshots;
for (int i = 1; i <= 3; ++i) {
std::string val = "val" + std::to_string(i);
ASSERT_OK(Put("foo1", val));
ASSERT_OK(Put("foo2", val));
ASSERT_OK(Put("bar1", val));
ASSERT_OK(Put("bar2", val));
ASSERT_OK(Put("bar3", val));
snapshots.push_back(db_->GetSnapshot());
}
ASSERT_OK(Flush());
TablePropertiesCollection tpc;
ASSERT_OK(db_->GetPropertiesOfAllTables(&tpc));
// sanity checks
ASSERT_EQ(tpc.size(), 1U);
auto& tp = *tpc.begin()->second;
EXPECT_EQ(tp.num_entries, 3U * 5U);
// test checks
unsigned ex_filter_entries = 0;
if (whole) {
ex_filter_entries += 5; // unique keys
}
if (prefix) {
ex_filter_entries += 2; // unique prefixes
}
EXPECT_EQ(tp.num_filter_entries, ex_filter_entries);
for (auto* sn : snapshots) {
db_->ReleaseSnapshot(sn);
}
}
}
}
}
TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
for (bool partition_filters : {true, false}) {
Options options = last_options_;
@@ -3204,55 +3143,52 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterOptions) {
}
TEST_F(DBBloomFilterTest, SeekForPrevWithPartitionedFilters) {
for (bool wkf : {true, false}) {
SCOPED_TRACE("whole_key_filtering=" + std::to_string(wkf));
Options options = CurrentOptions();
constexpr size_t kNumKeys = 10000;
static_assert(kNumKeys <= 10000, "kNumKeys have to be <= 10000");
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kNumKeys + 10));
options.create_if_missing = true;
constexpr size_t kPrefixLength = 4;
options.prefix_extractor.reset(NewFixedPrefixTransform(kPrefixLength));
options.compression = kNoCompression;
BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(50));
bbto.index_shortening =
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
bbto.block_size = 128;
bbto.metadata_block_size = 128;
bbto.partition_filters = true;
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
bbto.whole_key_filtering = wkf;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
DestroyAndReopen(options);
Options options = CurrentOptions();
constexpr size_t kNumKeys = 10000;
static_assert(kNumKeys <= 10000, "kNumKeys have to be <= 10000");
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kNumKeys + 10));
options.create_if_missing = true;
constexpr size_t kPrefixLength = 4;
options.prefix_extractor.reset(NewFixedPrefixTransform(kPrefixLength));
options.compression = kNoCompression;
BlockBasedTableOptions bbto;
bbto.filter_policy.reset(NewBloomFilterPolicy(50));
bbto.index_shortening =
BlockBasedTableOptions::IndexShorteningMode::kNoShortening;
bbto.block_size = 128;
bbto.metadata_block_size = 128;
bbto.partition_filters = true;
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
DestroyAndReopen(options);
const std::string value(64, '\0');
const std::string value(64, '\0');
WriteOptions write_opts;
write_opts.disableWAL = true;
for (size_t i = 0; i < kNumKeys; ++i) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(4) << std::fixed << i;
ASSERT_OK(db_->Put(write_opts, oss.str(), value));
}
ASSERT_OK(Flush());
ReadOptions read_opts;
// Use legacy, implicit prefix seek
read_opts.total_order_seek = false;
read_opts.auto_prefix_mode = false;
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
for (size_t i = 0; i < kNumKeys; ++i) {
// Seek with a key after each one added but with same prefix. One will
// surely cross a partition boundary.
std::ostringstream oss;
oss << std::setfill('0') << std::setw(4) << std::fixed << i << "a";
it->SeekForPrev(oss.str());
ASSERT_OK(it->status());
ASSERT_TRUE(it->Valid());
}
WriteOptions write_opts;
write_opts.disableWAL = true;
for (size_t i = 0; i < kNumKeys; ++i) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(4) << std::fixed << i;
ASSERT_OK(db_->Put(write_opts, oss.str(), value));
}
ASSERT_OK(Flush());
ReadOptions read_opts;
// Use legacy, implicit prefix seek
read_opts.total_order_seek = false;
read_opts.auto_prefix_mode = false;
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
for (size_t i = 0; i < kNumKeys; ++i) {
// Seek with a key after each one added but with same prefix. One will
// surely cross a partition boundary.
std::ostringstream oss;
oss << std::setfill('0') << std::setw(4) << std::fixed << i << "a";
it->SeekForPrev(oss.str());
ASSERT_OK(it->status());
ASSERT_TRUE(it->Valid());
}
it.reset();
}
namespace {
+22 -75
View File
@@ -16,7 +16,6 @@
#include "env/mock_env.h"
#include "port/port.h"
#include "port/stack_trace.h"
#include "rocksdb/advanced_options.h"
#include "rocksdb/concurrent_task_limiter.h"
#include "rocksdb/experimental.h"
#include "rocksdb/sst_file_writer.h"
@@ -6205,14 +6204,29 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
NumTableFilesAtLevel(0, 0));
}
// Wait until all CFs are pending compaction. WaitForFlushMemtable() can
// return before the next compaction is scheduled, so we need to do some
// waiting here.
// All CFs are pending compaction
unsigned int tp_len = env_->GetThreadPoolQueueLen(Env::LOW);
for (int i = 0; i < 10000 && tp_len < cf_count; i++) {
env_->SleepForMicroseconds(1000);
tp_len = env_->GetThreadPoolQueueLen(Env::LOW);
if (cf_count != tp_len) {
// The test is flaky and fails the assertion below.
// Print some debug information.
uint64_t num_running_flushes = 0;
if (db_->GetIntProperty(DB::Properties::kNumRunningFlushes,
&num_running_flushes)) {
fprintf(stdout, "Running flushes: %" PRIu64 "\n", num_running_flushes);
}
fprintf(stdout,
"%zu CF in compaction queue: ", pending_compaction_cfs.size());
for (const auto& cf_name : pending_compaction_cfs) {
fprintf(stdout, "%s, ", cf_name.c_str());
}
fprintf(stdout, "\n");
// print lsm
for (unsigned int cf = 0; cf < cf_count; cf++) {
fprintf(stdout, "%s: %s\n", cf_names[cf], FilesPerLevel(cf).c_str());
}
}
ASSERT_EQ(cf_count, tp_len);
// Unblock all compaction threads
@@ -7121,8 +7135,7 @@ class DBCompactionTestWithOngoingFileIngestionParam
SyncPoint::GetInstance()->SetCallBack(
"ExternalSstFileIngestionJob::Run", [&](void*) {
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::CompactFilesImpl::"
"PostSanitizeAndConvertCompactionInputFiles",
{{"DBImpl::CompactFilesImpl::PostSanitizeCompactionInputFiles",
"VersionSet::LogAndApply:WriteManifest"}});
});
} else {
@@ -7604,72 +7617,6 @@ class DBCompactionTestL0FilesMisorderCorruption : public DBCompactionTest {
std::shared_ptr<test::SleepingBackgroundTask> sleeping_task_;
};
TEST_F(DBCompactionTest, CompactFilesSupportKeyPlacementRangeConflict) {
Options options;
options.create_if_missing = true;
options.compaction_style = kCompactionStyleUniversal;
options.num_levels = 3;
DestroyAndReopen(options);
// To create LSM of below shape:
// L0: [k2]
// L1: [k3],[k4]
// L2: [k1, k5]
ASSERT_OK(Put("k1", "v"));
ASSERT_OK(Put("k5", "v"));
ASSERT_OK(Flush());
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ("0,0,1", FilesPerLevel());
ASSERT_OK(Put("k3", "v"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k4", "v"));
ASSERT_OK(Flush());
ASSERT_OK(experimental::PromoteL0(db_, db_->DefaultColumnFamily(), 1));
ASSERT_EQ("0,2,1", FilesPerLevel());
ASSERT_OK(Put("k2", "v"));
ASSERT_OK(Flush());
ASSERT_EQ("1,2,1", FilesPerLevel());
Close();
// To force below two CompactFiles() in order to coerce range conflict on L1
// upon (2)
// (1): Compact [k2] at L0 and [k3] at L1 with output to L1
// (2): Compact [k4] at L1 and [k1, k5] at L2 and output to L1 and L2
options.preclude_last_level_data_seconds = 1;
Reopen(options);
ColumnFamilyMetaData cf_meta_data;
db_->GetColumnFamilyMetaData(&cf_meta_data);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactFilesImpl:0", [&](void* /*arg*/) {
std::vector<std::string> c2_input_files;
c2_input_files.push_back(cf_meta_data.levels[1].files[1].name);
c2_input_files.push_back(cf_meta_data.levels[2].files[0].name);
// To verify CompactFiles() is aborted upon range conflict instead
// of crashing upon internal assertion
Status s = db_->CompactFiles(CompactionOptions(), c2_input_files,
2 /* output_level */);
ASSERT_TRUE(s.IsAborted());
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
std::vector<std::string> c1_input_files;
c1_input_files.push_back(cf_meta_data.levels[0].files[0].name);
c1_input_files.push_back(cf_meta_data.levels[1].files[0].name);
Status s = db_->CompactFiles(CompactionOptions(), c1_input_files,
1 /* output_level */);
ASSERT_OK(s);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBCompactionTestL0FilesMisorderCorruption,
FlushAfterIntraL0LevelCompactionWithIngestedFile) {
SetupOptions(CompactionStyle::kCompactionStyleLevel, "");
+30 -105
View File
@@ -21,7 +21,6 @@
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/metadata.h"
#include "rocksdb/transaction_log.h"
#include "rocksdb/types.h"
#include "test_util/sync_point.h"
#include "util/file_checksum_helper.h"
@@ -92,12 +91,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
return Status::OK();
}
Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
return GetSortedWalFilesImpl(files,
/*need_seqnos*/ true);
}
Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
// Record tracked WALs as a (minimum) cross-check for directory scan
std::vector<uint64_t> required_by_manifest;
@@ -123,10 +117,7 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
}
}
// NOTE: need to include archived WALs because needed WALs might have been
// archived since getting required_by_manifest set
Status s = wal_manager_.GetSortedWalFiles(files, need_seqnos,
/*include_archived*/ true);
Status s = wal_manager_.GetSortedWalFiles(files);
// DisableFileDeletions / EnableFileDeletions not supported in read-only DB
if (deletions_disabled.ok()) {
@@ -161,11 +152,11 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
}
if (s.ok()) {
size_t wal_count = files.size();
size_t wal_size = files.size();
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"Number of WAL files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
"Number of log files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
" required by manifest)",
wal_count, required_by_manifest.size());
wal_size, required_by_manifest.size());
#ifndef NDEBUG
std::ostringstream wal_names;
for (const auto& wal : files) {
@@ -186,7 +177,7 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
return s;
}
Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) {
Status DBImpl::GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) {
uint64_t current_logfile_number;
{
InstrumentedMutexLock l(&mutex_);
@@ -207,20 +198,14 @@ Status DBImpl::GetLiveFilesStorageInfo(
// NOTE: This implementation was largely migrated from Checkpoint.
Status s;
VectorWalPtr live_wal_files;
VectorLogPtr live_wal_files;
bool flush_memtable = true;
if (!immutable_db_options_.allow_2pc) {
if (opts.wal_size_for_flush == std::numeric_limits<uint64_t>::max()) {
flush_memtable = false;
} else if (opts.wal_size_for_flush > 0) {
// FIXME: avoid querying the filesystem for current WAL state
// If the outstanding WAL files are small, we skip the flush.
// Don't take archived log size into account when calculating wal
// size for flush, and don't need to verify consistency with manifest
// here & now.
s = wal_manager_.GetSortedWalFiles(live_wal_files,
/* need_seqnos */ false,
/*include_archived*/ false);
// If the outstanding log files are small, we skip the flush.
s = GetSortedWalFiles(live_wal_files);
if (!s.ok()) {
return s;
@@ -231,7 +216,6 @@ Status DBImpl::GetLiveFilesStorageInfo(
// We may be able to cover 2PC case too.
uint64_t total_wal_size = 0;
for (auto& wal : live_wal_files) {
assert(wal->Type() == kAliveLogFile);
total_wal_size += wal->SizeFileBytes();
}
if (total_wal_size < opts.wal_size_for_flush) {
@@ -245,18 +229,12 @@ Status DBImpl::GetLiveFilesStorageInfo(
// metadata.
mutex_.Lock();
if (flush_memtable) {
bool wal_locked = lock_wal_count_ > 0;
if (wal_locked) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"Can't FlushForGetLiveFiles while WAL is locked");
} else {
Status status = FlushForGetLiveFiles();
if (!status.ok()) {
mutex_.Unlock();
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"Cannot Flush data %s\n", status.ToString().c_str());
return status;
}
Status status = FlushForGetLiveFiles();
if (!status.ok()) {
mutex_.Unlock();
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "Cannot Flush data %s\n",
status.ToString().c_str());
return status;
}
}
@@ -332,8 +310,6 @@ Status DBImpl::GetLiveFilesStorageInfo(
const uint64_t options_number = versions_->options_file_number();
const uint64_t options_size = versions_->options_file_size_;
const uint64_t min_log_num = MinLogNumberToKeep();
// Ensure consistency with manifest for track_and_verify_wals_in_manifest
const uint64_t max_log_num = logfile_number_;
mutex_.Unlock();
@@ -397,13 +373,10 @@ Status DBImpl::GetLiveFilesStorageInfo(
TEST_SYNC_POINT("CheckpointImpl::CreateCheckpoint:SavedLiveFiles2");
if (s.ok()) {
// FlushWAL is required to ensure we can physically copy everything
// logically written to the WAL. (Sync not strictly required for
// active WAL to be copied rather than hard linked, even when
// Checkpoint guarantees that the copied-to file is sync-ed. Plus we can't
// help track_and_verify_wals_in_manifest after manifest_size is
// already determined.)
s = FlushWAL(/*sync=*/false);
// To maximize the effectiveness of track_and_verify_wals_in_manifest,
// sync WAL when it is enabled.
s = FlushWAL(
immutable_db_options_.track_and_verify_wals_in_manifest /* sync */);
if (s.IsNotSupported()) { // read-only DB or similar
s = Status::OK();
}
@@ -412,52 +385,21 @@ Status DBImpl::GetLiveFilesStorageInfo(
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive1");
TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive2");
// Even after WAL flush, there could be multiple WALs that are not
// fully synced. Although the output DB of a Checkpoint or Backup needs
// to be fully synced on return, we don't strictly need to sync this
// DB (the input DB). If we allow Checkpoint to hard link an inactive
// WAL that isn't fully synced, that could result in an insufficiently
// sync-ed Checkpoint. Here we get the set of WALs that are potentially
// unsynced or still being written to, to prevent them from being hard
// linked. Enforcing max_log_num from above ensures any new WALs after
// GetOpenWalSizes() and before GetSortedWalFiles() are not included in
// the results.
// NOTE: we might still hard link a file that is open for writing, even
// if we don't do any more writes to it.
//
// In a step toward reducing unnecessary file metadata queries, we also
// get and use our known flushed sizes for those WALs.
// FIXME: eventually we should not be using filesystem queries at all for
// the required set of WAL files.
//
// However for recycled log files, we just copy the whole file,
// for better or worse.
//
std::map<uint64_t, uint64_t> open_wal_number_to_size;
bool recycling_log_files = immutable_db_options_.recycle_log_file_num > 0;
if (s.ok() && !recycling_log_files) {
s = GetOpenWalSizes(open_wal_number_to_size);
}
// [old comment] If we have more than one column family, we also need to get
// WAL files.
// If we have more than one column family, we also need to get WAL files.
if (s.ok()) {
// FIXME: avoid querying the filesystem for current WAL state
s = GetSortedWalFilesImpl(live_wal_files,
/* need_seqnos */ false);
s = GetSortedWalFiles(live_wal_files);
}
if (!s.ok()) {
return s;
}
size_t wal_count = live_wal_files.size();
size_t wal_size = live_wal_files.size();
// Link WAL files. Copy exact size of last one because it is the only one
// that has changes after the last flush.
auto wal_dir = immutable_db_options_.GetWalDir();
for (size_t i = 0; s.ok() && i < wal_count; ++i) {
for (size_t i = 0; s.ok() && i < wal_size; ++i) {
if ((live_wal_files[i]->Type() == kAliveLogFile) &&
(!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num) &&
live_wal_files[i]->LogNumber() <= max_log_num) {
(!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num)) {
results.emplace_back();
LiveFileStorageInfo& info = results.back();
auto f = live_wal_files[i]->PathName();
@@ -466,29 +408,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]);
+2 -466
View File
@@ -17,7 +17,6 @@ class DBFollowerTest : public DBTestBase {
// Create the leader DB object
DBFollowerTest() : DBTestBase("/db_follower_test", /*env_do_fsync*/ false) {
follower_name_ = dbname_ + "/follower";
db_parent_ = dbname_;
Close();
Destroy(CurrentOptions());
EXPECT_EQ(env_->CreateDirIfMissing(dbname_), Status::OK());
@@ -28,215 +27,17 @@ class DBFollowerTest : public DBTestBase {
~DBFollowerTest() {
follower_.reset();
EXPECT_EQ(DestroyDB(follower_name_, CurrentOptions()), Status::OK());
Destroy(CurrentOptions());
dbname_ = db_parent_;
}
protected:
class DBFollowerTestFS : public FileSystemWrapper {
public:
explicit DBFollowerTestFS(const std::shared_ptr<FileSystem>& target)
: FileSystemWrapper(target),
cv_(&mutex_),
barrier_(false),
count_(0),
reinit_count_(0) {}
const char* Name() const override { return "DBFollowerTestFS"; }
IOStatus NewSequentialFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSSequentialFile>* result,
IODebugContext* dbg = nullptr) override {
class DBFollowerTestSeqFile : public FSSequentialFileWrapper {
public:
DBFollowerTestSeqFile(DBFollowerTestFS* fs,
std::unique_ptr<FSSequentialFile>&& file,
uint64_t /*size*/)
: FSSequentialFileWrapper(file.get()),
fs_(fs),
file_(std::move(file)) {}
IOStatus Read(size_t n, const IOOptions& options, Slice* result,
char* scratch, IODebugContext* dbg) override {
fs_->BarrierWait();
return target()->Read(n, options, result, scratch, dbg);
}
private:
DBFollowerTestFS* fs_;
std::unique_ptr<FSSequentialFile> file_;
};
std::unique_ptr<FSSequentialFile> file;
IOStatus s = target()->NewSequentialFile(fname, file_opts, &file, dbg);
if (s.ok() && test::GetFileType(fname) == kDescriptorFile) {
uint64_t size = 0;
EXPECT_EQ(target()->GetFileSize(fname, IOOptions(), &size, nullptr),
IOStatus::OK());
result->reset(new DBFollowerTestSeqFile(this, std::move(file), size));
} else {
*result = std::move(file);
}
return s;
}
void BarrierInit(int count) {
MutexLock l(&mutex_);
barrier_ = true;
count_ = count;
}
void BarrierWait() {
MutexLock l(&mutex_);
if (!barrier_) {
return;
}
if (--count_ == 0) {
if (reinit_count_ > 0) {
count_ = reinit_count_;
reinit_count_ = 0;
} else {
barrier_ = false;
}
cv_.SignalAll();
} else {
cv_.Wait();
}
}
void BarrierWaitAndReinit(int count) {
MutexLock l(&mutex_);
if (!barrier_) {
return;
}
reinit_count_ = count;
if (--count_ == 0) {
if (reinit_count_ > 0) {
count_ = reinit_count_;
reinit_count_ = 0;
} else {
barrier_ = false;
}
cv_.SignalAll();
} else {
cv_.Wait();
}
}
private:
port::Mutex mutex_;
port::CondVar cv_;
bool barrier_;
int count_;
int reinit_count_;
};
class DBFollowerTestSstPartitioner : public SstPartitioner {
public:
explicit DBFollowerTestSstPartitioner(uint64_t max_keys)
: max_keys_(max_keys), num_keys_(0) {}
const char* Name() const override { return "DBFollowerTestSstPartitioner"; }
PartitionerResult ShouldPartition(
const PartitionerRequest& /*request*/) override {
if (++num_keys_ > max_keys_) {
num_keys_ = 0;
return PartitionerResult::kRequired;
} else {
return PartitionerResult::kNotRequired;
}
}
bool CanDoTrivialMove(const Slice& /*smallest_user_key*/,
const Slice& /*largest_user_key*/) override {
return true;
}
private:
uint64_t max_keys_;
uint64_t num_keys_;
};
class DBFollowerTestSstPartitionerFactory : public SstPartitionerFactory {
public:
explicit DBFollowerTestSstPartitionerFactory(uint64_t max_keys)
: max_keys_(max_keys) {}
std::unique_ptr<SstPartitioner> CreatePartitioner(
const SstPartitioner::Context& /*context*/) const override {
std::unique_ptr<SstPartitioner> partitioner;
partitioner.reset(new DBFollowerTestSstPartitioner(max_keys_));
return partitioner;
}
const char* Name() const override {
return "DBFollowerTestSstPartitionerFactory";
}
private:
uint64_t max_keys_;
};
Status OpenAsFollower() {
Options opts = CurrentOptions();
if (!follower_env_) {
follower_env_ = NewCompositeEnv(
std::make_shared<DBFollowerTestFS>(env_->GetFileSystem()));
}
opts.env = follower_env_.get();
opts.follower_refresh_catchup_period_ms = 100;
return DB::OpenAsFollower(opts, follower_name_, dbname_, &follower_);
return DB::OpenAsFollower(CurrentOptions(), follower_name_, dbname_,
&follower_);
}
std::string FollowerGet(const std::string& k) {
ReadOptions options;
options.verify_checksums = true;
std::string result;
Status s = follower()->Get(options, k, &result);
if (s.IsNotFound()) {
result = "NOT_FOUND";
} else if (!s.ok()) {
result = s.ToString();
}
return result;
}
DB* follower() { return follower_.get(); }
DBFollowerTestFS* follower_fs() {
return static_cast<DBFollowerTestFS*>(follower_env_->GetFileSystem().get());
}
void CheckDirs() {
std::vector<std::string> db_children;
std::vector<std::string> follower_children;
EXPECT_OK(env_->GetChildren(dbname_, &db_children));
EXPECT_OK(env_->GetChildren(follower_name_, &follower_children));
std::set<uint64_t> db_filenums;
std::set<uint64_t> follower_filenums;
for (auto& name : db_children) {
if (test::GetFileType(name) != kTableFile) {
continue;
}
db_filenums.insert(test::GetFileNumber(name));
}
for (auto& name : follower_children) {
if (test::GetFileType(name) != kTableFile) {
continue;
}
follower_filenums.insert(test::GetFileNumber(name));
}
db_filenums.merge(follower_filenums);
EXPECT_EQ(follower_filenums.size(), db_filenums.size());
}
private:
std::string follower_name_;
std::string db_parent_;
std::unique_ptr<Env> follower_env_;
std::unique_ptr<DB> follower_;
};
@@ -250,273 +51,8 @@ TEST_F(DBFollowerTest, Basic) {
std::string val;
ASSERT_OK(follower()->Get(ReadOptions(), "k1", &val));
ASSERT_EQ(val, "v1");
CheckDirs();
}
TEST_F(DBFollowerTest, Flush) {
SyncPoint::GetInstance()->LoadDependency({
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
{"Leader::Done", "DBImplFollower::TryCatchupWithLeader:Begin2"},
{"DBImplFollower::TryCatchupWithLeader:End", "Follower::WaitForCatchup"},
});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(OpenAsFollower());
TEST_SYNC_POINT("Leader::Start");
ASSERT_OK(Put("k1", "v1"));
ASSERT_OK(Flush());
TEST_SYNC_POINT("Leader::Done");
TEST_SYNC_POINT("Follower::WaitForCatchup");
std::string val;
ASSERT_OK(follower()->Get(ReadOptions(), "k1", &val));
ASSERT_EQ(val, "v1");
CheckDirs();
SyncPoint::GetInstance()->DisableProcessing();
}
// This test creates 4 L0 files, immediately followed by a compaction to L1.
// The follower replays the 4 flush records from the MANIFEST unsuccessfully,
// and then successfully recovers a Version from the compaction record
TEST_F(DBFollowerTest, RetryCatchup) {
Options opts = CurrentOptions();
opts.disable_auto_compactions = true;
Reopen(opts);
ASSERT_OK(OpenAsFollower());
SyncPoint::GetInstance()->LoadDependency({
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
{"DBImpl::BackgroundCompaction:Start",
"DBImplFollower::TryCatchupWithLeader:Begin2"},
{"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin1",
"DBImpl::BackgroundCompaction:BeforeCompaction"},
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin2"},
{"DBImplFollower::TryCatchupWithLeader:End", "Follower::WaitForCatchup"},
});
SyncPoint::GetInstance()->EnableProcessing();
TEST_SYNC_POINT("Leader::Start");
ASSERT_OK(Put("k1", "v1"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k1", "v2"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k1", "v3"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k1", "v4"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
TEST_SYNC_POINT("Follower::WaitForCatchup");
ASSERT_EQ(FollowerGet("k1"), "v4");
CheckDirs();
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->DisableProcessing();
}
// This test validates the same as the previous test, except there is a
// MANIFEST rollover between the flushes and compaction. The follower
// does not switch to a new MANIFEST in ReadAndApply. So it would require
// another round of refresh before catching up.
TEST_F(DBFollowerTest, RetryCatchupManifestRollover) {
Options opts = CurrentOptions();
opts.disable_auto_compactions = true;
Reopen(opts);
ASSERT_OK(Put("k1", "v1"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k1", "v2"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k1", "v3"));
ASSERT_OK(Flush());
ASSERT_OK(OpenAsFollower());
SyncPoint::GetInstance()->LoadDependency({
{"DBImplFollower::TryCatchupWithLeader:Begin1", "Leader::Start"},
{"Leader::Flushed", "DBImplFollower::TryCatchupWithLeader:Begin2"},
{"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin1",
"Leader::Done"},
{"DBImpl::BackgroundCallCompaction:PurgedObsoleteFiles",
"VersionEditHandlerPointInTime::MaybeCreateVersion:Begin2"},
{"DBImplFollower::TryCatchupWithLeader:End",
"Follower::WaitForCatchup:1"},
});
SyncPoint::GetInstance()->EnableProcessing();
TEST_SYNC_POINT("Leader::Start");
ASSERT_OK(Put("k1", "v4"));
ASSERT_OK(Flush());
TEST_SYNC_POINT("Leader::Flushed");
TEST_SYNC_POINT("Leader::Done");
Reopen(opts);
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
SyncPoint::GetInstance()->LoadDependency({
{"DBImplFollower::TryCatchupWithLeader:End",
"Follower::WaitForCatchup:2"},
});
TEST_SYNC_POINT("Follower::WaitForCatchup:2");
ASSERT_EQ(FollowerGet("k1"), "v4");
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->DisableProcessing();
}
// This test creates 4 L0 files and compacts them. The follower, during catchup,
// successfully instantiates 4 Versions corresponding to the 4 files (but
// donesn't install them yet), followed by deleting those 4 and adding a new
// file from compaction. The test verifies that the 4 L0 files are deleted
// correctly by the follower.
// We use teh Barrier* functions to ensure that the follower first sees the 4
// L0 files and is able to link them, and then sees the compaction that
// obsoletes those L0 files (so those L0 files are intermediates that it has
// to explicitly delete). Suppose we don't have any barriers, its possible
// the follower reads the L0 records and compaction records from the MANIFEST
// in one read, which means those L0 files would have already been deleted
// by the leader and the follower cannot link to them.
TEST_F(DBFollowerTest, IntermediateObsoleteFiles) {
Options opts = CurrentOptions();
opts.disable_auto_compactions = true;
Reopen(opts);
ASSERT_OK(OpenAsFollower());
follower_fs()->BarrierInit(2);
ASSERT_OK(Put("k1", "v1"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k1", "v2"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k1", "v3"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k1", "v4"));
ASSERT_OK(Flush());
follower_fs()->BarrierWaitAndReinit(2);
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, true));
follower_fs()->BarrierWait();
SyncPoint::GetInstance()->LoadDependency({
{"DBImplFollower::TryCatchupWithLeader:End",
"Follower::WaitForCatchup:1"},
});
SyncPoint::GetInstance()->EnableProcessing();
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
CheckDirs();
ASSERT_EQ(FollowerGet("k1"), "v4");
}
// This test verifies a scenario where the follower can recover a Version
// partially (i.e some of the additions cannot be found), and the files
// that are found are obsoleted by a subsequent VersionEdit.
TEST_F(DBFollowerTest, PartialVersionRecovery) {
Options opts = CurrentOptions();
opts.disable_auto_compactions = true;
opts.sst_partitioner_factory =
std::make_shared<DBFollowerTestSstPartitionerFactory>(1);
Reopen(opts);
ASSERT_OK(Put("k1", "v1"));
ASSERT_OK(Put("k2", "v1"));
ASSERT_OK(Put("k3", "v1"));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_OK(Put("k1", "v2"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k3", "v2"));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_OK(OpenAsFollower());
ASSERT_OK(dbfull()->SetOptions(dbfull()->DefaultColumnFamily(),
{{"max_compaction_bytes", "1"}}));
follower_fs()->BarrierInit(2);
Slice key("k1");
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
follower_fs()->BarrierWaitAndReinit(2);
// The second compaction input overlaps the previous compaction outputs
// by one file. This file is never added to VersionStorageInfo since it
// was added and deleted before the catch up completes. We later verify that
// the follower correctly deleted this file.
key = Slice("k3");
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
follower_fs()->BarrierWait();
SyncPoint::GetInstance()->LoadDependency({
{"DBImplFollower::TryCatchupWithLeader:End",
"Follower::WaitForCatchup:1"},
});
SyncPoint::GetInstance()->EnableProcessing();
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
CheckDirs();
ASSERT_EQ(FollowerGet("k1"), "v2");
ASSERT_EQ(FollowerGet("k2"), "v1");
ASSERT_EQ(FollowerGet("k3"), "v2");
SyncPoint::GetInstance()->DisableProcessing();
}
// This test verifies a scenario similar to the PartialVersionRecovery, except
// with a MANIFEST rollover in between. When there is a rollover, the
// follower's attempt ends without installing a new Version. The next catch up
// attempt will recover a full Version.
TEST_F(DBFollowerTest, PartialVersionRecoveryWithRollover) {
Options opts = CurrentOptions();
opts.disable_auto_compactions = true;
opts.sst_partitioner_factory =
std::make_shared<DBFollowerTestSstPartitionerFactory>(1);
Reopen(opts);
ASSERT_OK(Put("k1", "v1"));
ASSERT_OK(Put("k2", "v1"));
ASSERT_OK(Put("k3", "v1"));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_OK(Put("k1", "v2"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k3", "v2"));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
opts.max_compaction_bytes = 1;
Reopen(opts);
ASSERT_OK(OpenAsFollower());
follower_fs()->BarrierInit(2);
Slice key("k1");
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
follower_fs()->BarrierWaitAndReinit(2);
Reopen(opts);
key = Slice("k3");
ASSERT_OK(dbfull()->TEST_CompactRange(1, &key, &key, nullptr, true));
follower_fs()->BarrierWait();
SyncPoint::GetInstance()->LoadDependency({
{"DBImplFollower::TryCatchupWithLeader:Begin1",
"Follower::WaitForCatchup:1"},
{"Follower::WaitForCatchup:2",
"DBImplFollower::TryCatchupWithLeader:Begin2"},
});
SyncPoint::GetInstance()->EnableProcessing();
TEST_SYNC_POINT("Follower::WaitForCatchup:1");
TEST_SYNC_POINT("Follower::WaitForCatchup:2");
SyncPoint::GetInstance()->LoadDependency({
{"DBImplFollower::TryCatchupWithLeader:End",
"Follower::WaitForCatchup:3"},
});
TEST_SYNC_POINT("Follower::WaitForCatchup:3");
CheckDirs();
ASSERT_EQ(FollowerGet("k1"), "v2");
ASSERT_EQ(FollowerGet("k2"), "v1");
ASSERT_EQ(FollowerGet("k3"), "v2");
SyncPoint::GetInstance()->DisableProcessing();
}
#endif
} // namespace ROCKSDB_NAMESPACE
+103 -297
View File
@@ -17,7 +17,6 @@
#include <cstdio>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <stdexcept>
@@ -392,8 +391,8 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
if (!s.ok()) {
io_s = versions_->io_status();
if (!io_s.ok()) {
error_handler_.SetBGError(io_s,
BackgroundErrorReason::kManifestWrite);
s = error_handler_.SetBGError(io_s,
BackgroundErrorReason::kManifestWrite);
}
}
}
@@ -638,7 +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
@@ -917,8 +916,8 @@ Status DBImpl::RegisterRecordSeqnoTimeWorker(const ReadOptions& read_options,
read_options, write_options, &edit, &mutex_,
directories_.GetDbDir());
if (!s.ok() && versions_->io_status().IsIOError()) {
error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
s = error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
}
}
@@ -1550,132 +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,
@@ -1725,8 +1653,8 @@ Status DBImpl::ApplyWALToManifest(const ReadOptions& read_options,
read_options, write_options, synced_wals, &mutex_,
directories_.GetDbDir());
if (!status.ok() && versions_->io_status().IsIOError()) {
error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
status = error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
}
return status;
}
@@ -1830,19 +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;
}
@@ -2065,19 +1990,19 @@ InternalIterator* DBImpl::NewInternalIterator(
read_options, super_version->GetSeqnoToTimeMapping(), arena);
Status s;
if (!read_options.ignore_range_deletions) {
std::unique_ptr<TruncatedRangeDelIterator> mem_tombstone_iter;
TruncatedRangeDelIterator* mem_tombstone_iter = nullptr;
auto range_del_iter = super_version->mem->NewRangeTombstoneIterator(
read_options, sequence, false /* immutable_memtable */);
if (range_del_iter == nullptr || range_del_iter->empty()) {
delete range_del_iter;
} else {
mem_tombstone_iter = std::make_unique<TruncatedRangeDelIterator>(
mem_tombstone_iter = new TruncatedRangeDelIterator(
std::unique_ptr<FragmentedRangeTombstoneIterator>(range_del_iter),
&cfd->ioptions()->internal_comparator, nullptr /* smallest */,
nullptr /* largest */);
}
merge_iter_builder.AddPointAndTombstoneIterator(
mem_iter, std::move(mem_tombstone_iter));
merge_iter_builder.AddPointAndTombstoneIterator(mem_iter,
mem_tombstone_iter);
} else {
merge_iter_builder.AddIterator(mem_iter);
}
@@ -2174,7 +2099,7 @@ Status DBImpl::GetEntity(const ReadOptions& _read_options,
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
_read_options.io_activity != Env::IOActivity::kGetEntity) {
return Status::InvalidArgument(
"Can only call GetEntity with `ReadOptions::io_activity` set to "
"Cannot call GetEntity with `ReadOptions::io_activity` != "
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kGetEntity`");
}
ReadOptions read_options(_read_options);
@@ -2201,7 +2126,7 @@ Status DBImpl::GetEntity(const ReadOptions& _read_options, const Slice& key,
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
_read_options.io_activity != Env::IOActivity::kGetEntity) {
s = Status::InvalidArgument(
"Can only call GetEntity with `ReadOptions::io_activity` set to "
"Cannot call GetEntity with `ReadOptions::io_activity` != "
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kGetEntity`");
for (size_t i = 0; i < num_column_families; ++i) {
(*result)[i].SetStatus(s);
@@ -3260,55 +3185,22 @@ void DBImpl::MultiGetEntity(const ReadOptions& _read_options, size_t num_keys,
ColumnFamilyHandle** column_families,
const Slice* keys, PinnableWideColumns* results,
Status* statuses, bool sorted_input) {
assert(statuses);
if (!column_families) {
const Status s = Status::InvalidArgument(
"Cannot call MultiGetEntity without column families");
for (size_t i = 0; i < num_keys; ++i) {
statuses[i] = s;
}
return;
}
if (!keys) {
const Status s =
Status::InvalidArgument("Cannot call MultiGetEntity without keys");
for (size_t i = 0; i < num_keys; ++i) {
statuses[i] = s;
}
return;
}
if (!results) {
const Status s = Status::InvalidArgument(
"Cannot call MultiGetEntity without PinnableWideColumns objects");
for (size_t i = 0; i < num_keys; ++i) {
statuses[i] = s;
}
return;
}
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
_read_options.io_activity != Env::IOActivity::kMultiGetEntity) {
const Status s = Status::InvalidArgument(
"Can only call MultiGetEntity with `ReadOptions::io_activity` set to "
Status s = Status::InvalidArgument(
"Can only call MultiGetEntity with `ReadOptions::io_activity` is "
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kMultiGetEntity`");
for (size_t i = 0; i < num_keys; ++i) {
statuses[i] = s;
if (statuses[i].ok()) {
statuses[i] = s;
}
}
return;
}
ReadOptions read_options(_read_options);
if (read_options.io_activity == Env::IOActivity::kUnknown) {
read_options.io_activity = Env::IOActivity::kMultiGetEntity;
}
MultiGetCommon(read_options, num_keys, column_families, keys,
/* values */ nullptr, results, /* timestamps */ nullptr,
statuses, sorted_input);
@@ -3318,54 +3210,22 @@ void DBImpl::MultiGetEntity(const ReadOptions& _read_options,
ColumnFamilyHandle* column_family, size_t num_keys,
const Slice* keys, PinnableWideColumns* results,
Status* statuses, bool sorted_input) {
assert(statuses);
if (!column_family) {
const Status s = Status::InvalidArgument(
"Cannot call MultiGetEntity without a column family handle");
for (size_t i = 0; i < num_keys; ++i) {
statuses[i] = s;
}
return;
}
if (!keys) {
const Status s =
Status::InvalidArgument("Cannot call MultiGetEntity without keys");
for (size_t i = 0; i < num_keys; ++i) {
statuses[i] = s;
}
return;
}
if (!results) {
const Status s = Status::InvalidArgument(
"Cannot call MultiGetEntity without PinnableWideColumns objects");
for (size_t i = 0; i < num_keys; ++i) {
statuses[i] = s;
}
return;
}
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
_read_options.io_activity != Env::IOActivity::kMultiGetEntity) {
const Status s = Status::InvalidArgument(
"Can only call MultiGetEntity with `ReadOptions::io_activity` set to "
Status s = Status::InvalidArgument(
"Can only call MultiGetEntity with `ReadOptions::io_activity` is "
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kMultiGetEntity`");
for (size_t i = 0; i < num_keys; ++i) {
statuses[i] = s;
if (statuses[i].ok()) {
statuses[i] = s;
}
}
return;
}
ReadOptions read_options(_read_options);
if (read_options.io_activity == Env::IOActivity::kUnknown) {
read_options.io_activity = Env::IOActivity::kMultiGetEntity;
}
MultiGetCommon(read_options, column_family, num_keys, keys,
/* values */ nullptr, results, /* timestamps */ nullptr,
statuses, sorted_input);
@@ -3374,34 +3234,18 @@ void DBImpl::MultiGetEntity(const ReadOptions& _read_options,
void DBImpl::MultiGetEntity(const ReadOptions& _read_options, size_t num_keys,
const Slice* keys,
PinnableAttributeGroups* results) {
assert(results);
if (!keys) {
const Status s =
Status::InvalidArgument("Cannot call MultiGetEntity without keys");
for (size_t i = 0; i < num_keys; ++i) {
for (size_t j = 0; j < results[i].size(); ++j) {
results[i][j].SetStatus(s);
}
}
return;
}
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
_read_options.io_activity != Env::IOActivity::kMultiGetEntity) {
const Status s = Status::InvalidArgument(
"Can only call MultiGetEntity with `ReadOptions::io_activity` set to "
Status s = Status::InvalidArgument(
"Can only call MultiGetEntity with ReadOptions::io_activity` is "
"`Env::IOActivity::kUnknown` or `Env::IOActivity::kMultiGetEntity`");
for (size_t i = 0; i < num_keys; ++i) {
for (size_t j = 0; j < results[i].size(); ++j) {
results[i][j].SetStatus(s);
}
}
return;
}
ReadOptions read_options(_read_options);
if (read_options.io_activity == Env::IOActivity::kUnknown) {
read_options.io_activity = Env::IOActivity::kMultiGetEntity;
@@ -3419,7 +3263,6 @@ void DBImpl::MultiGetEntity(const ReadOptions& _read_options, size_t num_keys,
++total_count;
}
}
std::vector<Status> statuses(total_count);
std::vector<PinnableWideColumns> columns(total_count);
MultiGetCommon(read_options, total_count, column_families.data(),
@@ -3440,15 +3283,6 @@ void DBImpl::MultiGetEntity(const ReadOptions& _read_options, size_t num_keys,
}
}
void DBImpl::MultiGetEntityWithCallback(
const ReadOptions& read_options, ColumnFamilyHandle* column_family,
ReadCallback* callback,
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys) {
assert(read_options.io_activity == Env::IOActivity::kMultiGetEntity);
MultiGetWithCallbackImpl(read_options, column_family, callback, sorted_keys);
}
Status DBImpl::WrapUpCreateColumnFamilies(
const ReadOptions& read_options, const WriteOptions& write_options,
const std::vector<const ColumnFamilyOptions*>& cf_options) {
@@ -4544,11 +4378,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 +4393,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 +5072,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 +5083,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 +5099,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 +5111,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 +5132,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 +5167,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;
}
@@ -5828,18 +5646,6 @@ Status DBImpl::IngestExternalFiles(
"timestamps enabled doesn't support ingest behind.");
}
}
if (ingest_opts.allow_db_generated_files) {
if (ingest_opts.write_global_seqno) {
return Status::NotSupported(
"write_global_seqno is deprecated and does not work with "
"allow_db_generated_files.");
}
if (ingest_opts.move_files) {
return Status::NotSupported(
"Options move_files and allow_db_generated_files are not "
"compatible.");
}
}
}
// TODO (yanqin) maybe handle the case in which column_families have
+26 -80
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,
@@ -297,11 +291,6 @@ class DBImpl : public DB {
const Slice* keys,
PinnableAttributeGroups* results) override;
void MultiGetEntityWithCallback(
const ReadOptions& read_options, ColumnFamilyHandle* column_family,
ReadCallback* callback,
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys);
Status CreateColumnFamily(const ColumnFamilyOptions& cf_options,
const std::string& column_family,
ColumnFamilyHandle** handle) override {
@@ -507,16 +496,8 @@ class DBImpl : public DB {
// All the returned filenames start with "/"
Status GetLiveFiles(std::vector<std::string>&, uint64_t* manifest_file_size,
bool flush_memtable = true) override;
Status GetSortedWalFiles(VectorWalPtr& files) override;
Status GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos);
// Get the known flushed sizes of WALs that might still be written to
// or have pending sync.
// NOTE: unlike alive_log_files_, this function includes WALs that might
// be obsolete (but not obsolete to a pending Checkpoint) and not yet fully
// synced.
Status GetOpenWalSizes(std::map<uint64_t, uint64_t>& number_to_size);
Status GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) override;
Status GetSortedWalFiles(VectorLogPtr& files) override;
Status GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) override;
Status GetCreationTimeOfOldestFile(uint64_t* creation_time) override;
Status GetUpdatesSince(
@@ -702,8 +683,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
@@ -1226,8 +1206,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_;
}
@@ -1464,9 +1442,6 @@ class DBImpl : public DB {
Status RenameTempFileToOptionsFile(const std::string& file_name);
Status DeleteObsoleteOptionsFiles();
void NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
FlushReason flush_reason);
void NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
const MutableCFOptions& mutable_cf_options,
int job_id, FlushReason flush_reason);
@@ -1517,7 +1492,6 @@ class DBImpl : public DB {
// batch that does not have duplicate keys.
Status WriteImpl(const WriteOptions& options, WriteBatch* updates,
WriteCallback* callback = nullptr,
UserWriteCallback* user_write_cb = nullptr,
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
bool disable_memtable = false, uint64_t* seq_used = nullptr,
size_t batch_cnt = 0,
@@ -1526,7 +1500,6 @@ class DBImpl : public DB {
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
WriteCallback* callback = nullptr,
UserWriteCallback* user_write_cb = nullptr,
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
bool disable_memtable = false,
uint64_t* seq_used = nullptr);
@@ -1553,8 +1526,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);
@@ -1671,7 +1643,6 @@ class DBImpl : public DB {
friend class ForwardIterator;
friend struct SuperVersion;
friend class CompactedDBImpl;
friend class DBImplFollower;
#ifndef NDEBUG
friend class DBTest_ConcurrentFlushWAL_Test;
friend class DBTest_MixedSlowdownOptionsStop_Test;
@@ -1726,11 +1697,8 @@ class DBImpl : public DB {
return w;
}
Status ClearWriter() {
Status s;
if (writer->file()) {
// TODO: plumb Env::IOActivity, Env::IOPriority
s = writer->WriteBuffer(WriteOptions());
}
// TODO: plumb Env::IOActivity, Env::IOPriority
Status s = writer->WriteBuffer(WriteOptions());
delete writer;
writer = nullptr;
return s;
@@ -1745,16 +1713,10 @@ class DBImpl : public DB {
void PrepareForSync() {
assert(!getting_synced);
// Ensure the head of logs_ is marked as getting_synced if any is.
// Size is expected to be monotonically increasing.
assert(writer->file()->GetFlushedSize() >= pre_sync_size);
getting_synced = true;
// If last sync failed on a later WAL, this could be a fully synced
// and closed WAL that just needs to be recorded as synced in the
// manifest.
if (writer->file()) {
// Size is expected to be monotonically increasing.
assert(writer->file()->GetFlushedSize() >= pre_sync_size);
pre_sync_size = writer->file()->GetFlushedSize();
}
pre_sync_size = writer->file()->GetFlushedSize();
}
void FinishSync() {
@@ -1952,7 +1914,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);
@@ -2086,22 +2048,17 @@ class DBImpl : public DB {
mutex_.Lock();
}
if (immutable_db_options_.unordered_write) {
// Wait for the ones who already wrote to the WAL to finish their
// memtable write.
if (pending_memtable_writes_.load() != 0) {
// XXX: suspicious wait while holding DB mutex?
std::unique_lock<std::mutex> guard(switch_mutex_);
switch_cv_.wait(guard,
[&] { return pending_memtable_writes_.load() == 0; });
}
} else {
// (Writes are finished before the next write group starts.)
if (!immutable_db_options_.unordered_write) {
// Then the writes are finished before the next write group starts
return;
}
// Wait for any LockWAL to clear
while (lock_wal_count_ > 0) {
bg_cv_.Wait();
// Wait for the ones who already wrote to the WAL to finish their
// memtable write.
if (pending_memtable_writes_.load() != 0) {
std::unique_lock<std::mutex> guard(switch_mutex_);
switch_cv_.wait(guard,
[&] { return pending_memtable_writes_.load() == 0; });
}
}
@@ -2211,8 +2168,7 @@ class DBImpl : public DB {
void GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
FlushReason flush_reason, FlushRequest* req);
// Returns true if `req` is successfully enqueued.
bool SchedulePendingFlush(const FlushRequest& req);
void SchedulePendingFlush(const FlushRequest& req);
void SchedulePendingCompaction(ColumnFamilyData* cfd);
void SchedulePendingPurge(std::string fname, std::string dir_to_sync,
@@ -2288,11 +2244,6 @@ class DBImpl : public DB {
ColumnFamilyData* PickCompactionFromQueue(
std::unique_ptr<TaskLimiterToken>* token, LogBuffer* log_buffer);
IOStatus SyncWalImpl(bool include_current_wal,
const WriteOptions& write_options,
JobContext* job_context, VersionEdit* synced_wals,
bool error_recovery_in_prog);
// helper function to call after some of the logs_ were synced
void MarkLogsSynced(uint64_t up_to, bool synced_dir, VersionEdit* edit);
Status ApplyWALToManifest(const ReadOptions& read_options,
@@ -2350,9 +2301,6 @@ class DBImpl : public DB {
bool HaveManualCompaction(ColumnFamilyData* cfd);
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
void UpdateDeletionCompactionStats(const std::unique_ptr<Compaction>& c);
// May open and read table files for table property.
// Should not be called while holding mutex_.
void BuildCompactionJobInfo(const ColumnFamilyData* cfd, Compaction* c,
const Status& st,
const CompactionJobStats& compaction_job_stats,
@@ -2568,10 +2516,8 @@ class DBImpl : public DB {
bool persistent_stats_cfd_exists_ = true;
// The current WAL file and those that have not been found obsolete from
// memtable flushes. A WAL not on this list might still be pending writer
// flush and/or sync and close and might still be in logs_. alive_log_files_
// is protected by mutex_ and log_write_mutex_ with details as follows:
// alive_log_files_ is protected by mutex_ and log_write_mutex_ with details
// as follows:
// 1. read by FindObsoleteFiles() which can be called in either application
// thread or RocksDB bg threads, both mutex_ and log_write_mutex_ are
// held.
@@ -2622,7 +2568,7 @@ class DBImpl : public DB {
// 8. read by MarkLogsNotSynced() and MarkLogsSynced() are protected by
// log_write_mutex_.
// 9. erase() by MarkLogsSynced() protected by log_write_mutex_.
// 10. read by SyncClosedWals() protected by only log_write_mutex_. This can
// 10. read by SyncClosedLogs() protected by only log_write_mutex_. This can
// happen in bg flush threads after DB::Open() returns success to
// applications.
// 11. reads, e.g. front(), iteration, and back() called by PreprocessWrite()
@@ -2635,7 +2581,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
@@ -3041,9 +2987,9 @@ inline Status DBImpl::FailIfReadCollapsedHistory(const ColumnFamilyData* cfd,
if (!full_history_ts_low.empty() &&
ucmp->CompareTimestamp(ts, full_history_ts_low) < 0) {
std::stringstream oss;
oss << "Read timestamp: " << ucmp->TimestampToString(ts)
oss << "Read timestamp: " << ts.ToString(true)
<< " is smaller than full_history_ts_low: "
<< ucmp->TimestampToString(full_history_ts_low) << std::endl;
<< Slice(full_history_ts_low).ToString(true) << std::endl;
return Status::InvalidArgument(oss.str());
}
return Status::OK();
+120 -113
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;
}
@@ -162,25 +224,19 @@ Status DBImpl::FlushMemTableToOutputFile(
// the host crashes after flushing and before WAL is persistent, the
// flushed SST may contain data from write batches whose updates to
// other (unflushed) column families are missing.
//
// When 2PC is enabled, non-recent WAL(s) may be needed for crash-recovery,
// even when there is only one CF in the DB, for prepared transactions that
// had not been committed yet. Make sure we sync them to keep the persisted
// WAL state at least as new as the persisted SST state.
const bool needs_to_sync_closed_wals =
logfile_number_ > 0 &&
(versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1 ||
allow_2pc());
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1;
// 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 +250,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 +280,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 +300,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 +574,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()) {
@@ -1032,12 +1088,7 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
assert(ucmp->timestamp_size() == ts_low.size() && !ts_low.empty());
if (!current_ts_low.empty() &&
ucmp->CompareTimestamp(ts_low, current_ts_low) < 0) {
std::stringstream oss;
oss << "Current full_history_ts_low: "
<< ucmp->TimestampToString(current_ts_low)
<< " is higher than provided ts: " << ucmp->TimestampToString(ts_low)
<< std::endl;
return Status::InvalidArgument(oss.str());
return Status::InvalidArgument("Cannot decrease full_history_ts_low");
}
Status s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
@@ -1069,11 +1120,6 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
if (options.target_path_id >= cfd->ioptions()->cf_paths.size()) {
return Status::InvalidArgument("Invalid target path ID");
}
if (options.change_level &&
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
return Status::NotSupported(
"FIFO compaction does not support change_level.");
}
bool flush_needed = true;
@@ -1134,16 +1180,6 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
final_output_level, options, begin, end, exclusive,
false /* disable_trivial_move */,
std::numeric_limits<uint64_t>::max(), trim_ts);
} else if (cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
// FIFOCompactionPicker::CompactRange() will ignore the input key range
// [begin, end] and just try to pick compaction based on the configured
// option `compaction_options_fifo`. So we skip checking if [begin, end]
// overlaps with the DB here.
final_output_level = 0;
s = RunManualCompaction(cfd, /*input_level=*/0, final_output_level, options,
begin, end, exclusive,
false /* disable_trivial_move */,
std::numeric_limits<uint64_t>::max(), trim_ts);
} else {
int first_overlapped_level = kInvalidLevel;
{
@@ -1228,7 +1264,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
CleanupSuperVersion(super_version);
}
if (s.ok() && first_overlapped_level != kInvalidLevel) {
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal) {
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
assert(first_overlapped_level == 0);
s = RunManualCompaction(
cfd, first_overlapped_level, first_overlapped_level, options, begin,
@@ -1480,11 +1517,16 @@ Status DBImpl::CompactFilesImpl(
std::to_string(cfd->ioptions()->num_levels - 1));
}
Status s = cfd->compaction_picker()->SanitizeCompactionInputFiles(
&input_set, cf_meta, output_level);
TEST_SYNC_POINT("DBImpl::CompactFilesImpl::PostSanitizeCompactionInputFiles");
if (!s.ok()) {
return s;
}
std::vector<CompactionInputFiles> input_files;
Status s = cfd->compaction_picker()->SanitizeAndConvertCompactionInputFiles(
&input_set, cf_meta, output_level, version->storage_info(), &input_files);
TEST_SYNC_POINT(
"DBImpl::CompactFilesImpl::PostSanitizeAndConvertCompactionInputFiles");
s = cfd->compaction_picker()->GetCompactionInputsFromFileNumbers(
&input_files, &input_set, version->storage_info(), compact_options);
if (!s.ok()) {
return s;
}
@@ -1595,12 +1637,10 @@ Status DBImpl::CompactFilesImpl(
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
mutex_.Unlock();
if (compaction_job_info != nullptr) {
BuildCompactionJobInfo(cfd, c.get(), s, compaction_job_stats,
job_context->job_id, compaction_job_info);
}
mutex_.Lock();
if (status.ok()) {
// Done
@@ -2250,23 +2290,6 @@ void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
}
}
void DBImpl::NotifyOnManualFlushScheduled(autovector<ColumnFamilyData*> cfds,
FlushReason flush_reason) {
if (immutable_db_options_.listeners.size() == 0U) {
return;
}
if (shutting_down_.load(std::memory_order_acquire)) {
return;
}
std::vector<ManualFlushInfo> info;
for (ColumnFamilyData* cfd : cfds) {
info.push_back({cfd->GetID(), cfd->GetName(), flush_reason});
}
for (const auto& listener : immutable_db_options_.listeners) {
listener->OnManualFlushScheduled(this, info);
}
}
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
const FlushOptions& flush_options,
FlushReason flush_reason,
@@ -2371,14 +2394,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
}
}
for (const auto& req : flush_reqs) {
assert(req.cfd_to_max_mem_id_to_persist.size() == 1);
ColumnFamilyData* loop_cfd =
req.cfd_to_max_mem_id_to_persist.begin()->first;
bool already_queued_for_flush = loop_cfd->queued_for_flush();
bool flush_req_enqueued = SchedulePendingFlush(req);
if (already_queued_for_flush || flush_req_enqueued) {
loop_cfd->SetFlushSkipReschedule();
}
SchedulePendingFlush(req);
}
MaybeScheduleFlushOrCompaction();
}
@@ -2390,8 +2406,6 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
}
}
}
NotifyOnManualFlushScheduled({cfd}, flush_reason);
TEST_SYNC_POINT("DBImpl::FlushMemTable:AfterScheduleFlush");
TEST_SYNC_POINT("DBImpl::FlushMemTable:BeforeWaitForBgFlush");
if (s.ok() && flush_options.wait) {
@@ -2536,7 +2550,6 @@ Status DBImpl::AtomicFlushMemTables(
}
}
}
NotifyOnManualFlushScheduled(cfds, flush_reason);
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:AfterScheduleFlush");
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
if (s.ok() && flush_options.wait) {
@@ -2594,9 +2607,7 @@ Status DBImpl::RetryFlushesForErrorRecovery(FlushReason flush_reason,
flush_reason,
{{cfd,
std::numeric_limits<uint64_t>::max() /* max_mem_id_to_persist */}}};
if (SchedulePendingFlush(flush_req)) {
cfd->SetFlushSkipReschedule();
};
SchedulePendingFlush(flush_req);
}
}
MaybeScheduleFlushOrCompaction();
@@ -2684,13 +2695,13 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
// mode due to pending compaction bytes, but that's less common
// No extra immutable Memtable will be created if the current Memtable is
// empty.
write_stall_condition =
ColumnFamilyData::GetWriteStallConditionAndCause(
cfd->GetUnflushedMemTableCountForWriteStallCheck(),
vstorage->l0_delay_trigger_count() + 1,
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
*cfd->ioptions())
.first;
int mem_to_flush = cfd->mem()->IsEmpty() ? 0 : 1;
write_stall_condition = ColumnFamilyData::GetWriteStallConditionAndCause(
cfd->imm()->NumNotFlushed() + mem_to_flush,
vstorage->l0_delay_trigger_count() + 1,
vstorage->estimated_compaction_needed_bytes(),
mutable_cf_options, *cfd->ioptions())
.first;
} while (write_stall_condition != WriteStallCondition::kNormal);
}
return Status::OK();
@@ -3002,14 +3013,13 @@ ColumnFamilyData* DBImpl::PickCompactionFromQueue(
return cfd;
}
bool DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
mutex_.AssertHeld();
bool enqueued = false;
if (reject_new_background_jobs_) {
return enqueued;
return;
}
if (flush_req.cfd_to_max_mem_id_to_persist.empty()) {
return enqueued;
return;
}
if (!immutable_db_options_.atomic_flush) {
// For the non-atomic flush case, we never schedule multiple column
@@ -3024,7 +3034,6 @@ bool DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
cfd->set_queued_for_flush(true);
++unscheduled_flushes_;
flush_queue_.push_back(flush_req);
enqueued = true;
}
} else {
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
@@ -3033,9 +3042,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) {
@@ -3333,7 +3340,7 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
mutex_.Unlock();
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
"[JOB %d] Waiting after background flush error: %s, "
"[JOB %d] Waiting after background flush error: %s"
"Accumulated background error counts: %" PRIu64,
job_context.job_id, s.ToString().c_str(), error_cnt);
log_buffer.FlushBufferToLog();
@@ -4163,7 +4170,7 @@ void DBImpl::BuildCompactionJobInfo(
compaction_job_info->base_input_level = c->start_level();
compaction_job_info->output_level = c->output_level();
compaction_job_info->stats = compaction_job_stats;
const auto& input_table_properties = c->GetOrInitInputTableProperties();
const auto& input_table_properties = c->GetInputTableProperties();
const auto& output_table_properties = c->GetOutputTableProperties();
compaction_job_info->table_properties.insert(input_table_properties.begin(),
input_table_properties.end());
-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 -39
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"
@@ -96,28 +95,17 @@ Status DBImplFollower::TryCatchUpWithLeader() {
assert(versions_.get() != nullptr);
assert(manifest_reader_.get() != nullptr);
Status s;
TEST_SYNC_POINT("DBImplFollower::TryCatchupWithLeader:Begin1");
TEST_SYNC_POINT("DBImplFollower::TryCatchupWithLeader:Begin2");
// read the manifest and apply new changes to the follower instance
std::unordered_set<ColumnFamilyData*> cfds_changed;
JobContext job_context(0, true /*create_superversion*/);
{
InstrumentedMutexLock lock_guard(&mutex_);
std::vector<std::string> files_to_delete;
s = static_cast_with_check<ReactiveVersionSet>(versions_.get())
->ReadAndApply(&mutex_, &manifest_reader_,
manifest_reader_status_.get(), &cfds_changed,
&files_to_delete);
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem_);
pending_outputs_inserted_elem_.reset(new std::list<uint64_t>::iterator(
CaptureCurrentFileNumberInPendingOutputs()));
manifest_reader_status_.get(), &cfds_changed);
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Last sequence is %" PRIu64,
static_cast<uint64_t>(versions_->LastSequence()));
ROCKS_LOG_INFO(
immutable_db_options_.info_log, "Next file number is %" PRIu64,
static_cast<uint64_t>(versions_->current_next_file_number()));
for (ColumnFamilyData* cfd : cfds_changed) {
if (cfd->IsDropped()) {
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] is dropped\n",
@@ -159,33 +147,9 @@ Status DBImplFollower::TryCatchUpWithLeader() {
sv_context.NewSuperVersion();
}
}
for (auto& file : files_to_delete) {
IOStatus io_s = fs_->DeleteFile(file, IOOptions(), nullptr);
if (!io_s.ok()) {
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"Cannot delete file %s: %s", file.c_str(),
io_s.ToString().c_str());
}
}
}
job_context.Clean();
// Cleanup unused, obsolete files.
JobContext purge_files_job_context(0);
{
InstrumentedMutexLock lock_guard(&mutex_);
// Currently, follower instance does not create any database files, thus
// is unnecessary for the follower to force full scan.
FindObsoleteFiles(&purge_files_job_context, /*force=*/false);
}
if (purge_files_job_context.HaveSomethingToDelete()) {
PurgeObsoleteFiles(purge_files_job_context);
}
purge_files_job_context.Clean();
TEST_SYNC_POINT("DBImplFollower::TryCatchupWithLeader:End");
return s;
}
@@ -235,8 +199,6 @@ Status DBImplFollower::Close() {
catch_up_thread_.reset();
}
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem_);
return DBImpl::Close();
}
+1 -2
View File
@@ -27,7 +27,7 @@ class DBImplFollower : public DBImplSecondary {
bool OwnTablesAndLogs() const override {
// TODO: Change this to true once we've properly implemented file
// deletion for the read scaling case
return true;
return false;
}
Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families,
@@ -49,6 +49,5 @@ class DBImplFollower : public DBImplSecondary {
std::string src_path_;
port::Mutex mu_;
port::CondVar cv_;
std::unique_ptr<std::list<uint64_t>::iterator> pending_outputs_inserted_elem_;
};
} // namespace ROCKSDB_NAMESPACE
+14 -27
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);
@@ -530,12 +527,6 @@ Status DBImpl::Recover(
/*no_error_if_files_missing=*/false, is_retry,
&desc_status);
desc_status.PermitUncheckedError();
if (is_retry) {
RecordTick(stats_, FILE_READ_CORRUPTION_RETRY_COUNT);
if (desc_status.ok()) {
RecordTick(stats_, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
}
}
if (can_retry) {
// If we're opening for the first time and the failure is likely due to
// a corrupt MANIFEST file (could result in either the log::Reader
@@ -646,7 +637,7 @@ Status DBImpl::Recover(
f->fd.smallest_seqno, f->fd.largest_seqno,
f->marked_for_compaction,
f->temperature, // this can be different from
// `last_level_temperature`
// `last_level_temperature`
f->oldest_blob_file_number, f->oldest_ancester_time,
f->file_creation_time, f->epoch_number,
f->file_checksum, f->file_checksum_func_name,
@@ -1679,8 +1670,6 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
SequenceNumber earliest_write_conflict_snapshot;
std::vector<SequenceNumber> snapshot_seqs =
snapshots_.GetAll(&earliest_write_conflict_snapshot);
SequenceNumber earliest_snapshot =
(snapshot_seqs.empty() ? kMaxSequenceNumber : snapshot_seqs.at(0));
auto snapshot_checker = snapshot_checker_.get();
if (use_custom_gc_ && snapshot_checker == nullptr) {
snapshot_checker = DisableGCSnapshotChecker::Instance();
@@ -1700,7 +1689,6 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
IOStatus io_s;
const ReadOptions read_option(Env::IOActivity::kDBOpen);
const WriteOptions write_option(Env::IO_HIGH, Env::IOActivity::kDBOpen);
TableBuilderOptions tboptions(
*cfd->ioptions(), mutable_cf_options, read_option, write_option,
cfd->internal_comparator(), cfd->internal_tbl_prop_coll_factories(),
@@ -1709,22 +1697,21 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
0 /* level */, false /* is_bottommost */,
TableFileCreationReason::kRecovery, 0 /* oldest_key_time */,
0 /* file_creation_time */, db_id_, db_session_id_,
0 /* target_file_size */, meta.fd.GetNumber(), kMaxSequenceNumber);
0 /* target_file_size */, meta.fd.GetNumber());
Version* version = cfd->current();
version->Ref();
uint64_t num_input_entries = 0;
s = BuildTable(dbname_, versions_.get(), immutable_db_options_, tboptions,
file_options_for_compaction_, cfd->table_cache(),
iter.get(), std::move(range_del_iters), &meta,
&blob_file_additions, snapshot_seqs, earliest_snapshot,
earliest_write_conflict_snapshot, kMaxSequenceNumber,
snapshot_checker, paranoid_file_checks,
cfd->internal_stats(), &io_s, io_tracer_,
BlobFileCreationReason::kRecovery,
nullptr /* seqno_to_time_mapping */, &event_logger_,
job_id, nullptr /* table_properties */, write_hint,
nullptr /*full_history_ts_low*/, &blob_callback_, version,
&num_input_entries);
s = BuildTable(
dbname_, versions_.get(), immutable_db_options_, tboptions,
file_options_for_compaction_, cfd->table_cache(), iter.get(),
std::move(range_del_iters), &meta, &blob_file_additions,
snapshot_seqs, earliest_write_conflict_snapshot, kMaxSequenceNumber,
snapshot_checker, paranoid_file_checks, cfd->internal_stats(), &io_s,
io_tracer_, BlobFileCreationReason::kRecovery,
nullptr /* seqno_to_time_mapping */, &event_logger_, job_id,
nullptr /* table_properties */, write_hint,
nullptr /*full_history_ts_low*/, &blob_callback_, version,
&num_input_entries);
version->Unref();
LogFlush(immutable_db_options_.info_log);
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
+1 -2
View File
@@ -680,8 +680,7 @@ Status DBImplSecondary::TryCatchUpWithPrimary() {
InstrumentedMutexLock lock_guard(&mutex_);
s = static_cast_with_check<ReactiveVersionSet>(versions_.get())
->ReadAndApply(&mutex_, &manifest_reader_,
manifest_reader_status_.get(), &cfds_changed,
/*files_to_delete=*/nullptr);
manifest_reader_status_.get(), &cfds_changed);
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Last sequence is %" PRIu64,
static_cast<uint64_t>(versions_->LastSequence()));
+64 -114
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);
@@ -236,11 +221,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
return Status::InvalidArgument(
"`WriteOptions::protection_bytes_per_key` must be zero or eight");
} else if (write_options.disableWAL &&
immutable_db_options_.recycle_log_file_num > 0 &&
!(two_write_queues_ && disable_memtable)) {
// Corruption detection in recycled WALs relies on sequential sequence
// numbers, but WritePreparedTxnDB uses disableWAL internally for split
// writes
immutable_db_options_.recycle_log_file_num > 0) {
return Status::InvalidArgument(
"WriteOptions::disableWAL option is not supported if "
"DBOptions::recycle_log_file_num > 0");
@@ -283,10 +264,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 +280,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 +295,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 +314,17 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
}
if (immutable_db_options_.enable_pipelined_write) {
return PipelinedWriteImpl(write_options, my_batch, callback, user_write_cb,
log_used, log_ref, disable_memtable, seq_used);
return PipelinedWriteImpl(write_options, my_batch, callback, log_used,
log_ref, disable_memtable, seq_used);
}
PERF_TIMER_GUARD(write_pre_and_post_process_time);
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
log_ref, disable_memtable, batch_cnt,
pre_release_callback, post_memtable_callback);
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
disable_memtable, batch_cnt, pre_release_callback,
post_memtable_callback);
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
write_thread_.JoinBatchGroup(&w);
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_CALLER) {
write_thread_.SetMemWritersEachStride(&w);
}
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
// we are a non-leader in a parallel group
@@ -556,35 +530,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
const SequenceNumber current_sequence = last_sequence + 1;
last_sequence += seq_inc;
if (log_context.need_log_sync) {
VersionEdit synced_wals;
log_write_mutex_.Lock();
if (status.ok()) {
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
&synced_wals);
} else {
MarkLogsNotSynced(logfile_number_);
}
log_write_mutex_.Unlock();
if (status.ok() && synced_wals.IsWalAddition()) {
InstrumentedMutexLock l(&mutex_);
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
}
// Requesting sync with two_write_queues_ is expected to be very rare. We
// hence provide a simple implementation that is not necessarily
// efficient.
if (status.ok() && two_write_queues_) {
if (manual_wal_flush_) {
status = FlushWAL(true);
} else {
status = SyncWAL();
}
}
}
// PreReleaseCallback is called after WAL write and before memtable write
if (status.ok()) {
SequenceNumber next_sequence = current_sequence;
@@ -668,6 +613,34 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
assert(pre_release_cb_status.ok());
}
if (log_context.need_log_sync) {
VersionEdit synced_wals;
log_write_mutex_.Lock();
if (status.ok()) {
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
&synced_wals);
} else {
MarkLogsNotSynced(logfile_number_);
}
log_write_mutex_.Unlock();
if (status.ok() && synced_wals.IsWalAddition()) {
InstrumentedMutexLock l(&mutex_);
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
status = ApplyWALToManifest(read_options, write_options, &synced_wals);
}
// Requesting sync with two_write_queues_ is expected to be very rare. We
// hence provide a simple implementation that is not necessarily efficient.
if (two_write_queues_) {
if (manual_wal_flush_) {
status = FlushWAL(true);
} else {
status = SyncWAL();
}
}
}
bool should_exit_batch_group = true;
if (in_parallel_group) {
// CompleteParallelWorker returns true if this thread should
@@ -702,7 +675,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
WriteBatch* my_batch, WriteCallback* callback,
UserWriteCallback* user_write_cb,
uint64_t* log_used, uint64_t log_ref,
bool disable_memtable, uint64_t* seq_used) {
PERF_TIMER_GUARD(write_pre_and_post_process_time);
@@ -710,8 +682,8 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
WriteContext write_context;
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
log_ref, disable_memtable, /*_batch_cnt=*/0,
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
disable_memtable, /*_batch_cnt=*/0,
/*_pre_release_callback=*/nullptr);
write_thread_.JoinBatchGroup(&w);
TEST_SYNC_POINT("DBImplWrite::PipelinedWriteImpl:AfterJoinBatchGroup");
@@ -850,9 +822,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
// so we need to set its status to pass ASSERT_STATUS_CHECKED
memtable_write_group.status.PermitUncheckedError();
}
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_CALLER) {
write_thread_.SetMemWritersEachStride(&w);
}
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
PERF_TIMER_STOP(write_pre_and_post_process_time);
PERF_TIMER_FOR_WAIT_GUARD(write_memtable_time);
@@ -892,8 +862,7 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
PERF_TIMER_GUARD(write_pre_and_post_process_time);
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
WriteThread::Writer w(write_options, my_batch, callback,
/*user_write_cb=*/nullptr, log_ref,
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
false /*disable_memtable*/);
if (w.CheckCallback(this) && w.ShouldWriteToMemtable()) {
@@ -943,15 +912,13 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
// applicable in a two-queue setting.
Status DBImpl::WriteImplWALOnly(
WriteThread* write_thread, const WriteOptions& write_options,
WriteBatch* my_batch, WriteCallback* callback,
UserWriteCallback* user_write_cb, uint64_t* log_used,
WriteBatch* my_batch, WriteCallback* callback, uint64_t* log_used,
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
const PublishLastSeq publish_last_seq, const bool disable_memtable) {
PERF_TIMER_GUARD(write_pre_and_post_process_time);
WriteThread::Writer w(write_options, my_batch, callback, user_write_cb,
log_ref, disable_memtable, sub_batch_cnt,
pre_release_callback);
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
disable_memtable, sub_batch_cnt, pre_release_callback);
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
write_thread->JoinBatchGroup(&w);
@@ -1205,7 +1172,8 @@ void DBImpl::MemTableInsertStatusCheck(const Status& status) {
mutex_.Lock();
assert(!error_handler_.IsBGWorkStopped());
// Maybe change the return status to void?
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable)
.PermitUncheckedError();
mutex_.Unlock();
}
}
@@ -1517,11 +1485,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 +1549,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;
}
@@ -2194,8 +2152,6 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* /*cfd*/,
// two_write_queues_ is true (This is to simplify the reasoning.)
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
mutex_.AssertHeld();
assert(lock_wal_count_ == 0);
// TODO: plumb Env::IOActivity, Env::IOPriority
const ReadOptions read_options;
const WriteOptions write_options;
@@ -2239,11 +2195,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 +2214,14 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
SequenceNumber seq = versions_->LastSequence();
new_mem = cfd->ConstructNewMemtable(mutable_cf_options, seq);
context->superversion_context.NewSuperVersion();
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[%s] New memtable created with log file: #%" PRIu64
". Immutable memtables: %d.\n",
cfd->GetName().c_str(), new_log_number, num_imm_unflushed);
// There should be no concurrent write as the thread is at the front of
// writer queue
cfd->mem()->ConstructFragmentedRangeTombstones();
}
ROCKS_LOG_INFO(immutable_db_options_.info_log,
"[%s] New memtable created with log file: #%" PRIu64
". Immutable memtables: %d.\n",
cfd->GetName().c_str(), new_log_number, num_imm_unflushed);
// There should be no concurrent write as the thread is at the front of
// writer queue
cfd->mem()->ConstructFragmentedRangeTombstones();
mutex_.Lock();
if (recycle_log_number != 0) {
@@ -2363,8 +2313,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
read_options, write_options, &wal_deletion, &mutex_,
directories_.GetDbDir());
if (!s.ok() && versions_->io_status().IsIOError()) {
error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
s = error_handler_.SetBGError(versions_->io_status(),
BackgroundErrorReason::kManifestWrite);
}
if (!s.ok()) {
return s;
-27
View File
@@ -705,7 +705,6 @@ class DBIOCorruptionTest
DBIOCorruptionTest() : DBIOFailureTest() {
BlockBasedTableOptions bbto;
options_ = CurrentOptions();
options_.statistics = CreateDBStatistics();
base_env_ = env_;
EXPECT_NE(base_env_, nullptr);
@@ -728,8 +727,6 @@ class DBIOCorruptionTest
Status ReopenDB() { return TryReopen(options_); }
Statistics* stats() { return options_.statistics.get(); }
protected:
std::unique_ptr<Env> env_guard_;
std::shared_ptr<CorruptionFS> fs_;
@@ -752,12 +749,8 @@ TEST_P(DBIOCorruptionTest, GetReadCorruptionRetry) {
if (std::get<2>(GetParam())) {
ASSERT_OK(s);
ASSERT_EQ(val, "val1");
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_TRUE(s.IsCorruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
}
@@ -780,12 +773,8 @@ TEST_P(DBIOCorruptionTest, IterReadCorruptionRetry) {
}
if (std::get<2>(GetParam())) {
ASSERT_OK(iter->status());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_TRUE(iter->status().IsCorruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
delete iter;
}
@@ -810,13 +799,9 @@ TEST_P(DBIOCorruptionTest, MultiGetReadCorruptionRetry) {
if (std::get<2>(GetParam())) {
ASSERT_EQ(values[0].ToString(), "val1");
ASSERT_EQ(values[1].ToString(), "val2");
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_TRUE(statuses[0].IsCorruption());
ASSERT_TRUE(statuses[1].IsCorruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
}
@@ -833,9 +818,6 @@ TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
if (std::get<2>(GetParam())) {
ASSERT_OK(s);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
std::string val;
ReadOptions ro;
@@ -844,7 +826,6 @@ TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
ASSERT_EQ(val, "val1");
} else {
ASSERT_TRUE(s.IsCorruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
}
@@ -857,9 +838,6 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
Status s = Flush();
if (std::get<2>(GetParam())) {
ASSERT_OK(s);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
std::string val;
ReadOptions ro;
@@ -868,7 +846,6 @@ TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
ASSERT_EQ(val, "val1");
} else {
ASSERT_NOK(s);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
}
@@ -885,12 +862,8 @@ TEST_P(DBIOCorruptionTest, ManifestCorruptionRetry) {
if (std::get<2>(GetParam())) {
ASSERT_OK(ReopenDB());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 1);
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT),
1);
} else {
ASSERT_EQ(ReopenDB(), Status::Corruption());
ASSERT_EQ(stats()->getTickerCount(FILE_READ_CORRUPTION_RETRY_COUNT), 0);
}
SyncPoint::GetInstance()->DisableProcessing();
}
-10
View File
@@ -109,16 +109,6 @@ Status DBIter::GetProperty(std::string prop_name, std::string* prop) {
*prop = "Iterator is not valid.";
}
return Status::OK();
} else if (prop_name == "rocksdb.iterator.is-value-pinned") {
if (valid_) {
*prop = (pin_thru_lifetime_ && iter_.Valid() &&
iter_.value().data() == value_.data())
? "1"
: "0";
} else {
*prop = "Iterator is not valid.";
}
return Status::OK();
} else if (prop_name == "rocksdb.iterator.internal-key") {
*prop = saved_key_.GetUserKey().ToString();
return Status::OK();
+4 -19
View File
@@ -133,17 +133,11 @@ TEST_P(DBIteratorTest, IteratorProperty) {
ASSERT_NOK(iter->GetProperty("non_existing.value", &prop_value));
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
ASSERT_EQ("0", prop_value);
ASSERT_OK(
iter->GetProperty("rocksdb.iterator.is-value-pinned", &prop_value));
ASSERT_EQ("0", prop_value);
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
ASSERT_EQ("1", prop_value);
iter->Next();
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
ASSERT_EQ("Iterator is not valid.", prop_value);
ASSERT_OK(
iter->GetProperty("rocksdb.iterator.is-value-pinned", &prop_value));
ASSERT_EQ("Iterator is not valid.", prop_value);
// Get internal key at which the iteration stopped (tombstone in this case).
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
@@ -1686,15 +1680,12 @@ TEST_P(DBIteratorTest, PinnedDataIteratorMultipleFiles) {
ro.pin_data = true;
auto iter = NewIterator(ro);
std::vector<std::pair<Slice, Slice>> results;
std::vector<std::pair<Slice, std::string>> results;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
std::string prop_value;
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
ASSERT_EQ("1", prop_value);
ASSERT_OK(
iter->GetProperty("rocksdb.iterator.is-value-pinned", &prop_value));
ASSERT_EQ("1", prop_value);
results.emplace_back(iter->key(), iter->value());
results.emplace_back(iter->key(), iter->value().ToString());
}
ASSERT_EQ(results.size(), true_data.size());
@@ -1748,9 +1739,6 @@ TEST_P(DBIteratorTest, PinnedDataIteratorMergeOperator) {
std::string prop_value;
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
ASSERT_EQ("1", prop_value);
ASSERT_OK(
iter->GetProperty("rocksdb.iterator.is-value-pinned", &prop_value));
ASSERT_EQ("0", prop_value);
results.emplace_back(iter->key(), iter->value().ToString());
}
ASSERT_OK(iter->status());
@@ -1804,15 +1792,12 @@ TEST_P(DBIteratorTest, PinnedDataIteratorReadAfterUpdate) {
}
}
std::vector<std::pair<Slice, Slice>> results;
std::vector<std::pair<Slice, std::string>> results;
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
std::string prop_value;
ASSERT_OK(iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
ASSERT_EQ("1", prop_value);
ASSERT_OK(
iter->GetProperty("rocksdb.iterator.is-value-pinned", &prop_value));
ASSERT_EQ("1", prop_value);
results.emplace_back(iter->key(), iter->value());
results.emplace_back(iter->key(), iter->value().ToString());
}
ASSERT_OK(iter->status());
+2 -2
View File
@@ -732,7 +732,7 @@ TEST_P(DbMemtableKVChecksumTest, GetWithCorruptAfterMemtableInsert) {
});
SyncPoint::GetInstance()->SetCallBack(
"Memtable::SaveValue:Found:entry", [&](void* entry) {
"Memtable::SaveValue:Begin:entry", [&](void* entry) {
char* buf = *static_cast<char**>(entry);
buf[corrupt_byte_offset_] += corrupt_byte_addend_;
++corrupt_byte_offset_;
@@ -769,7 +769,7 @@ TEST_P(DbMemtableKVChecksumTest,
});
SyncPoint::GetInstance()->SetCallBack(
"Memtable::SaveValue:Found:entry", [&](void* entry) {
"Memtable::SaveValue:Begin:entry", [&](void* entry) {
char* buf = *static_cast<char**>(entry);
buf[corrupt_byte_offset_] += corrupt_byte_addend_;
++corrupt_byte_offset_;
+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
-6
View File
@@ -245,13 +245,7 @@ TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
// In DirectIO, where we support tail prefetching, during table open, we only
// do 1 read instead of 4 as described above. Actual checksum verification
// reads stay the same.
#ifdef OS_WIN
// No file system prefetch implemented for OS Win. During table open,
// we only do 1 read for BufferedIO.
int num_read_per_file = 4;
#else
int num_read_per_file = (!use_direct_io_) ? 7 : 4;
#endif
int expected = kNumFiles * num_read_per_file;
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
-52
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);
@@ -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);
+19 -35
View File
@@ -3844,9 +3844,6 @@ TEST_P(DBTestWithParam, FIFOCompactionTest) {
} else {
CompactRangeOptions cro;
cro.exclusive_manual_compaction = exclusive_manual_compaction_;
cro.change_level = true;
ASSERT_TRUE(db_->CompactRange(cro, nullptr, nullptr).IsNotSupported());
cro.change_level = false;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
}
// only 5 files should survive
@@ -4677,27 +4674,24 @@ TEST_F(DBTest, DynamicMemtableOptions) {
#ifdef ROCKSDB_USING_THREAD_STATUS
namespace {
bool VerifyOperationCount(Env* env, ThreadStatus::OperationType op_type,
void VerifyOperationCount(Env* env, ThreadStatus::OperationType op_type,
int expected_count) {
int op_count = 0;
std::vector<ThreadStatus> thread_list;
EXPECT_OK(env->GetThreadList(&thread_list));
ASSERT_OK(env->GetThreadList(&thread_list));
for (const auto& thread : thread_list) {
if (thread.operation_type == op_type) {
op_count++;
}
}
if (op_count != expected_count) {
fprintf(stderr, "op_count: %d, expected_count %d\n", op_count,
expected_count);
for (const auto& thread : thread_list) {
fprintf(stderr, "thread id: %" PRIu64 ", thread status: %s, cf_name %s\n",
fprintf(stderr, "thread id: %" PRIu64 ", thread status: %s\n",
thread.thread_id,
thread.GetOperationName(thread.operation_type).c_str(),
thread.cf_name.c_str());
thread.GetOperationName(thread.operation_type).c_str());
}
}
return op_count == expected_count;
ASSERT_EQ(op_count, expected_count);
}
} // anonymous namespace
@@ -4797,11 +4791,11 @@ TEST_F(DBTest, ThreadStatusFlush) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
CreateAndReopenWithCF({"pikachu"}, options);
ASSERT_TRUE(VerifyOperationCount(env_, ThreadStatus::OP_FLUSH, 0));
VerifyOperationCount(env_, ThreadStatus::OP_FLUSH, 0);
ASSERT_OK(Put(1, "foo", "v1"));
ASSERT_EQ("v1", Get(1, "foo"));
ASSERT_TRUE(VerifyOperationCount(env_, ThreadStatus::OP_FLUSH, 0));
VerifyOperationCount(env_, ThreadStatus::OP_FLUSH, 0);
uint64_t num_running_flushes = 0;
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningFlushes,
@@ -4814,7 +4808,7 @@ TEST_F(DBTest, ThreadStatusFlush) {
// The first sync point is to make sure there's one flush job
// running when we perform VerifyOperationCount().
TEST_SYNC_POINT("DBTest::ThreadStatusFlush:1");
ASSERT_TRUE(VerifyOperationCount(env_, ThreadStatus::OP_FLUSH, 1));
VerifyOperationCount(env_, ThreadStatus::OP_FLUSH, 1);
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningFlushes,
&num_running_flushes));
ASSERT_EQ(num_running_flushes, 1);
@@ -4825,11 +4819,17 @@ TEST_F(DBTest, ThreadStatusFlush) {
}
TEST_P(DBTestWithParam, ThreadStatusSingleCompaction) {
const int kTestKeySize = 16;
const int kTestValueSize = 984;
const int kEntrySize = kTestKeySize + kTestValueSize;
const int kEntriesPerBuffer = 100;
Options options;
options.create_if_missing = true;
options.write_buffer_size = kEntrySize * kEntriesPerBuffer;
options.compaction_style = kCompactionStyleLevel;
options.target_file_size_base = options.write_buffer_size;
options.max_bytes_for_level_base = options.target_file_size_base * 2;
options.max_bytes_for_level_multiplier = 2;
options.compression = kNoCompression;
options = CurrentOptions(options);
options.env = env_;
@@ -4864,7 +4864,7 @@ TEST_P(DBTestWithParam, ThreadStatusSingleCompaction) {
&num_running_compactions));
ASSERT_EQ(num_running_compactions, 0);
TEST_SYNC_POINT("DBTest::ThreadStatusSingleCompaction:0");
ASSERT_EQ(NumTableFilesAtLevel(0),
ASSERT_GE(NumTableFilesAtLevel(0),
options.level0_file_num_compaction_trigger);
// This makes sure at least one compaction is running.
@@ -4872,18 +4872,10 @@ TEST_P(DBTestWithParam, ThreadStatusSingleCompaction) {
if (options.enable_thread_tracking) {
// expecting one single L0 to L1 compaction
// This test is flaky and fails here.
bool match = VerifyOperationCount(env_, ThreadStatus::OP_COMPACTION, 1);
if (!match) {
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningCompactions,
&num_running_compactions));
fprintf(stderr, "running compaction: %" PRIu64 " lsm state: %s\n",
num_running_compactions, FilesPerLevel().c_str());
}
ASSERT_TRUE(match);
VerifyOperationCount(env_, ThreadStatus::OP_COMPACTION, 1);
} else {
// If thread tracking is not enabled, compaction count should be 0.
ASSERT_TRUE(VerifyOperationCount(env_, ThreadStatus::OP_COMPACTION, 0));
VerifyOperationCount(env_, ThreadStatus::OP_COMPACTION, 0);
}
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningCompactions,
&num_running_compactions));
@@ -5645,8 +5637,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 +5658,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 +5679,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 +6339,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) {
-5
View File
@@ -565,11 +565,6 @@ Options DBTestBase::GetOptions(
options.unordered_write = false;
break;
}
case kBlockBasedTableWithBinarySearchWithFirstKeyIndex: {
table_options.index_type =
BlockBasedTableOptions::kBinarySearchWithFirstKey;
break;
}
default:
break;
+6 -7
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)));
}
@@ -1041,7 +1041,6 @@ class DBTestBase : public testing::Test {
kPartitionedFilterWithNewTableReaderForCompactions,
kUniversalSubcompactions,
kUnorderedWrite,
kBlockBasedTableWithBinarySearchWithFirstKeyIndex,
// This must be the last line
kEnd,
};
+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
+22 -83
View File
@@ -666,25 +666,11 @@ TEST_P(DBWriteTest, LockWALInEffect) {
// try the 1st WAL created during open
ASSERT_OK(Put("key0", "value"));
ASSERT_NE(options.manual_wal_flush, dbfull()->WALBufferIsEmpty());
ASSERT_OK(db_->LockWAL());
ASSERT_TRUE(dbfull()->WALBufferIsEmpty());
uint64_t wal_num = dbfull()->TEST_GetCurrentLogNumber();
// Manual flush with wait=false should abruptly fail with TryAgain
FlushOptions flush_opts;
flush_opts.wait = false;
for (bool allow_write_stall : {true, false}) {
flush_opts.allow_write_stall = allow_write_stall;
ASSERT_TRUE(db_->Flush(flush_opts).IsTryAgain());
}
ASSERT_EQ(wal_num, dbfull()->TEST_GetCurrentLogNumber());
ASSERT_OK(db_->UnlockWAL());
// try the 2nd wal created during SwitchWAL (not locked this time)
// try the 2nd wal created during SwitchWAL
ASSERT_OK(dbfull()->TEST_SwitchWAL());
ASSERT_NE(wal_num, dbfull()->TEST_GetCurrentLogNumber());
ASSERT_OK(Put("key1", "value"));
ASSERT_NE(options.manual_wal_flush, dbfull()->WALBufferIsEmpty());
ASSERT_OK(db_->LockWAL());
@@ -723,57 +709,21 @@ TEST_P(DBWriteTest, LockWALInEffect) {
}
TEST_P(DBWriteTest, LockWALConcurrentRecursive) {
// This is a micro-stress test of LockWAL and concurrency handling.
// It is considered the most convenient way to balance functional
// coverage and reproducibility (vs. the two extremes of (a) unit tests
// tailored to specific interleavings and (b) db_stress)
Options options = GetOptions();
Reopen(options);
ASSERT_OK(Put("k1", "k1_orig"));
ASSERT_OK(Put("k1", "val"));
ASSERT_OK(db_->LockWAL()); // 0 -> 1
auto frozen_seqno = db_->GetLatestSequenceNumber();
std::string ingest_file = dbname_ + "/external.sst";
{
SstFileWriter sst_file_writer(EnvOptions(), options);
ASSERT_OK(sst_file_writer.Open(ingest_file));
ASSERT_OK(sst_file_writer.Put("k2", "k2_val"));
ExternalSstFileInfo external_info;
ASSERT_OK(sst_file_writer.Finish(&external_info));
}
AcqRelAtomic<bool> parallel_ingest_completed{false};
port::Thread parallel_ingest{[&]() {
IngestExternalFileOptions ingest_opts;
ingest_opts.move_files = true; // faster than copy
// Shouldn't finish until WAL unlocked
ASSERT_OK(db_->IngestExternalFile({ingest_file}, ingest_opts));
parallel_ingest_completed.Store(true);
}};
AcqRelAtomic<bool> flush_completed{false};
port::Thread parallel_flush{[&]() {
FlushOptions flush_opts;
// NB: Flush with wait=false case is tested above in LockWALInEffect
flush_opts.wait = true;
// allow_write_stall = true blocks in fewer cases
flush_opts.allow_write_stall = true;
// Shouldn't finish until WAL unlocked
ASSERT_OK(db_->Flush(flush_opts));
flush_completed.Store(true);
}};
AcqRelAtomic<bool> parallel_put_completed{false};
port::Thread parallel_put{[&]() {
// This can make certain failure scenarios more likely:
// sleep(1);
// Shouldn't finish until WAL unlocked
ASSERT_OK(Put("k1", "k1_mod"));
parallel_put_completed.Store(true);
std::atomic<bool> t1_completed{false};
port::Thread t1{[&]() {
// Won't finish until WAL unlocked
ASSERT_OK(Put("k1", "val2"));
t1_completed = true;
}};
ASSERT_OK(db_->LockWAL()); // 1 -> 2
// Read-only ops are OK
ASSERT_EQ(Get("k1"), "k1_orig");
ASSERT_EQ(Get("k1"), "val");
{
std::vector<LiveFileStorageInfo> files;
LiveFilesStorageInfoOptions lf_opts;
@@ -782,35 +732,29 @@ TEST_P(DBWriteTest, LockWALConcurrentRecursive) {
ASSERT_OK(db_->GetLiveFilesStorageInfo({lf_opts}, &files));
}
port::Thread parallel_lock_wal{[&]() {
port::Thread t2{[&]() {
ASSERT_OK(db_->LockWAL()); // 2 -> 3 or 1 -> 2
}};
ASSERT_OK(db_->UnlockWAL()); // 2 -> 1 or 3 -> 2
// Give parallel_put an extra chance to jump in case of bug
// Give t1 an extra chance to jump in case of bug
std::this_thread::yield();
parallel_lock_wal.join();
ASSERT_FALSE(parallel_put_completed.Load());
ASSERT_FALSE(parallel_ingest_completed.Load());
ASSERT_FALSE(flush_completed.Load());
t2.join();
ASSERT_FALSE(t1_completed.load());
// Should now have 2 outstanding LockWAL
ASSERT_EQ(Get("k1"), "k1_orig");
ASSERT_EQ(Get("k1"), "val");
ASSERT_OK(db_->UnlockWAL()); // 2 -> 1
ASSERT_FALSE(parallel_put_completed.Load());
ASSERT_FALSE(parallel_ingest_completed.Load());
ASSERT_FALSE(flush_completed.Load());
ASSERT_EQ(Get("k1"), "k1_orig");
ASSERT_EQ(Get("k2"), "NOT_FOUND");
ASSERT_FALSE(t1_completed.load());
ASSERT_EQ(Get("k1"), "val");
ASSERT_EQ(frozen_seqno, db_->GetLatestSequenceNumber());
// Ensure final Unlock is concurrency safe and extra Unlock is safe but
// non-OK
std::atomic<int> unlock_ok{0};
port::Thread parallel_stuff{[&]() {
port::Thread t3{[&]() {
if (db_->UnlockWAL().ok()) {
unlock_ok++;
}
@@ -823,23 +767,18 @@ TEST_P(DBWriteTest, LockWALConcurrentRecursive) {
if (db_->UnlockWAL().ok()) {
unlock_ok++;
}
parallel_stuff.join();
t3.join();
// There was one extra unlock, so just one non-ok
ASSERT_EQ(unlock_ok.load(), 2);
// Write can proceed
parallel_put.join();
ASSERT_TRUE(parallel_put_completed.Load());
ASSERT_EQ(Get("k1"), "k1_mod");
parallel_ingest.join();
ASSERT_TRUE(parallel_ingest_completed.Load());
ASSERT_EQ(Get("k2"), "k2_val");
parallel_flush.join();
ASSERT_TRUE(flush_completed.Load());
t1.join();
ASSERT_TRUE(t1_completed.load());
ASSERT_EQ(Get("k1"), "val2");
// And new writes
ASSERT_OK(Put("k3", "val"));
ASSERT_EQ(Get("k3"), "val");
ASSERT_OK(Put("k2", "val"));
ASSERT_EQ(Get("k2"), "val");
}
TEST_P(DBWriteTest, ConcurrentlyDisabledWAL) {
+4 -19
View File
@@ -47,8 +47,6 @@ EntryType GetEntryType(ValueType value_type) {
return kEntryBlobIndex;
case kTypeWideColumnEntity:
return kEntryWideColumnEntity;
case kTypeValuePreferredSeqno:
return kEntryTimedPut;
default:
return kEntryOther;
}
@@ -157,23 +155,10 @@ void ReplaceInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
result->append(key.data() + key_sz - kNumInternalBytes, kNumInternalBytes);
}
std::string ParsedInternalKey::DebugString(bool log_err_key, bool hex,
const Comparator* ucmp) const {
std::string ParsedInternalKey::DebugString(bool log_err_key, bool hex) const {
std::string result = "'";
size_t ts_sz_for_debug = ucmp == nullptr ? 0 : ucmp->timestamp_size();
if (log_err_key) {
if (ts_sz_for_debug == 0) {
result += user_key.ToString(hex);
} else {
assert(user_key.size() >= ts_sz_for_debug);
Slice user_key_without_ts = user_key;
user_key_without_ts.remove_suffix(ts_sz_for_debug);
result += user_key_without_ts.ToString(hex);
Slice ts = Slice(user_key.data() + user_key.size() - ts_sz_for_debug,
ts_sz_for_debug);
result += "|timestamp:";
result += ucmp->TimestampToString(ts);
}
result += user_key.ToString(hex);
} else {
result += "<redacted>";
}
@@ -186,11 +171,11 @@ std::string ParsedInternalKey::DebugString(bool log_err_key, bool hex,
return result;
}
std::string InternalKey::DebugString(bool hex, const Comparator* ucmp) const {
std::string InternalKey::DebugString(bool hex) const {
std::string result;
ParsedInternalKey parsed;
if (ParseInternalKey(rep_, &parsed, false /* log_err_key */).ok()) {
result = parsed.DebugString(true /* log_err_key */, hex, ucmp); // TODO
result = parsed.DebugString(true /* log_err_key */, hex); // TODO
} else {
result = "(bad)";
result.append(EscapeString(rep_));
+2 -3
View File
@@ -148,8 +148,7 @@ struct ParsedInternalKey {
// u contains timestamp if user timestamp feature is enabled.
ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t)
: user_key(u), sequence(seq), type(t) {}
std::string DebugString(bool log_err_key, bool hex,
const Comparator* ucmp = nullptr) const;
std::string DebugString(bool log_err_key, bool hex) const;
void clear() {
user_key.clear();
@@ -504,7 +503,7 @@ class InternalKey {
AppendInternalKeyFooter(&rep_, s, t);
}
std::string DebugString(bool hex, const Comparator* ucmp = nullptr) const;
std::string DebugString(bool hex) const;
};
inline int InternalKeyComparator::Compare(const InternalKey& a,
+39 -34
View File
@@ -250,6 +250,8 @@ void ErrorHandler::CancelErrorRecovery() {
EndAutoRecovery();
}
STATIC_AVOID_DESTRUCTION(const Status, kOkStatus){Status::OK()};
// This is the main function for looking at an error during a background
// operation and deciding the severity, and error recovery strategy. The high
// level algorithm is as follows -
@@ -268,11 +270,11 @@ void ErrorHandler::CancelErrorRecovery() {
// This can also get called as part of a recovery operation. In that case, we
// also track the error separately in recovery_error_ so we can tell in the
// end whether recovery succeeded or not
void ErrorHandler::HandleKnownErrors(const Status& bg_err,
BackgroundErrorReason reason) {
const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
BackgroundErrorReason reason) {
db_mutex_->AssertHeld();
if (bg_err.ok()) {
return;
return kOkStatus;
}
ROCKS_LOG_INFO(db_options_.info_log,
@@ -337,7 +339,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
} else {
// This error is less severe than previously encountered error. Don't
// take any further action
return;
return bg_error_;
}
}
@@ -354,6 +356,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
if (bg_error_.severity() >= Status::Severity::kHardError) {
is_db_stopped_.store(true, std::memory_order_release);
}
return bg_error_;
}
// This is the main function for looking at IO related error during the
@@ -380,14 +383,14 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
// 3) for other cases, HandleKnownErrors(const Status& bg_err,
// BackgroundErrorReason reason) will be called to handle other error cases
// such as delegating to SstFileManager to handle no space error.
void ErrorHandler::SetBGError(const Status& bg_status,
BackgroundErrorReason reason) {
const Status& ErrorHandler::SetBGError(const Status& bg_status,
BackgroundErrorReason reason) {
db_mutex_->AssertHeld();
Status tmp_status = bg_status;
IOStatus bg_io_err = status_to_io_status(std::move(tmp_status));
if (bg_io_err.ok()) {
return;
return kOkStatus;
}
ROCKS_LOG_WARN(db_options_.info_log, "Background IO error %s",
bg_io_err.ToString().c_str());
@@ -410,11 +413,11 @@ void ErrorHandler::SetBGError(const Status& bg_status,
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&bg_err, db_mutex_, &auto_recovery);
recover_context_ = context;
return;
}
if (bg_io_err.subcode() != IOStatus::SubCode::kNoSpace &&
(bg_io_err.GetScope() == IOStatus::IOErrorScope::kIOErrorScopeFile ||
bg_io_err.GetRetryable())) {
return bg_error_;
} else if (bg_io_err.subcode() != IOStatus::SubCode::kNoSpace &&
(bg_io_err.GetScope() ==
IOStatus::IOErrorScope::kIOErrorScopeFile ||
bg_io_err.GetRetryable())) {
// Second, check if the error is a retryable IO error (file scope IO error
// is also treated as retryable IO error in RocksDB write path). if it is
// retryable error and its severity is higher than bg_error_, overwrite the
@@ -423,6 +426,10 @@ void ErrorHandler::SetBGError(const Status& bg_status,
// IO error as hard error. Note that, all the NoSpace error should be
// handled by the SstFileManager::StartErrorRecovery(). Therefore, no matter
// it is retryable or file scope, this logic will be bypassed.
bool auto_recovery = false;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&new_bg_io_err, db_mutex_,
&auto_recovery);
RecordStats({ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT},
{} /* int_histograms */);
@@ -438,13 +445,9 @@ void ErrorHandler::SetBGError(const Status& bg_status,
ROCKS_LOG_INFO(
db_options_.info_log,
"ErrorHandler: Compaction will schedule by itself to resume\n");
bool auto_recovery = false;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&new_bg_io_err, db_mutex_,
&auto_recovery);
// Not used in this code path.
new_bg_io_err.PermitUncheckedError();
return;
return bg_error_;
}
Status::Severity severity;
@@ -466,14 +469,10 @@ void ErrorHandler::SetBGError(const Status& bg_status,
Status bg_err(new_bg_io_err, severity);
CheckAndSetRecoveryAndBGError(bg_err);
recover_context_ = context;
bool auto_recovery = db_options_.max_bgerror_resume_count > 0;
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
&new_bg_io_err, db_mutex_,
&auto_recovery);
StartRecoverFromRetryableBGIOError(bg_io_err);
return;
return StartRecoverFromRetryableBGIOError(bg_io_err);
} else {
return HandleKnownErrors(new_bg_io_err, reason);
}
HandleKnownErrors(new_bg_io_err, reason);
}
void ErrorHandler::AddFilesToQuarantine(
@@ -621,23 +620,23 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
return s;
}
void ErrorHandler::StartRecoverFromRetryableBGIOError(
const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
const IOStatus& io_error) {
db_mutex_->AssertHeld();
if (bg_error_.ok() || io_error.ok()) {
return;
}
if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_) {
// Auto resume BG error is not enabled
return;
}
if (end_recovery_) {
if (bg_error_.ok()) {
return bg_error_;
} else if (io_error.ok()) {
return kOkStatus;
} else if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_) {
// Auto resume BG error is not enabled, directly return bg_error_.
return bg_error_;
} else if (end_recovery_) {
// Can temporarily release db mutex
EventHelpers::NotifyOnErrorRecoveryEnd(db_options_.listeners, bg_error_,
Status::ShutdownInProgress(),
db_mutex_);
db_mutex_->AssertHeld();
return;
return bg_error_;
}
RecordStats({ERROR_HANDLER_AUTORESUME_COUNT}, {} /* int_histograms */);
ROCKS_LOG_INFO(
@@ -665,6 +664,12 @@ void ErrorHandler::StartRecoverFromRetryableBGIOError(
recovery_thread_.reset(
new port::Thread(&ErrorHandler::RecoverFromRetryableBGIOError, this));
if (recovery_error_.ok()) {
return recovery_error_;
} else {
return bg_error_;
}
}
// Automatic recover from Retryable BG IO error. Must be called after db
+4 -3
View File
@@ -56,7 +56,7 @@ class ErrorHandler {
Status::Severity GetErrorSeverity(BackgroundErrorReason reason,
Status::Code code, Status::SubCode subcode);
void SetBGError(const Status& bg_err, BackgroundErrorReason reason);
const Status& SetBGError(const Status& bg_err, BackgroundErrorReason reason);
Status GetBGError() const { return bg_error_; }
@@ -135,10 +135,11 @@ class ErrorHandler {
// unsorted.
autovector<uint64_t> files_to_quarantine_;
void HandleKnownErrors(const Status& bg_err, BackgroundErrorReason reason);
const Status& HandleKnownErrors(const Status& bg_err,
BackgroundErrorReason reason);
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
void RecoverFromNoSpace();
void StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
void RecoverFromRetryableBGIOError();
// First, if it is in recovery and the recovery_error is ok. Set the
// recovery_error_ to bg_err. Second, if the severity is higher than the
+4 -16
View File
@@ -30,14 +30,6 @@ class DBErrorHandlingFSTest : public DBTestBase {
fault_env_.reset(new CompositeEnvWrapper(env_, fault_fs_));
}
~DBErrorHandlingFSTest() {
// Before destroying fault_env_
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->LoadDependency({});
SyncPoint::GetInstance()->ClearAllCallBacks();
Close();
}
std::string GetManifestNameFromLiveFiles() {
std::vector<std::string> live_files;
uint64_t manifest_size;
@@ -425,7 +417,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 +462,7 @@ TEST_F(DBErrorHandlingFSTest, FlushWALAtomicWriteRetryableError) {
listener->EnableAutoRecovery(false);
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::SyncClosedWals:Start",
"DBImpl::SyncClosedLogs:Start",
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
SyncPoint::GetInstance()->EnableProcessing();
@@ -851,17 +843,13 @@ TEST_F(DBErrorHandlingFSTest, DoubleManifestWriteError) {
});
SyncPoint::GetInstance()->EnableProcessing();
s = Flush();
ASSERT_TRUE(s.IsNoSpace());
ASSERT_EQ(dbfull()->TEST_GetBGError().severity(),
ROCKSDB_NAMESPACE::Status::Severity::kHardError);
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
ASSERT_FALSE(dbfull()->TEST_GetFilesToQuarantine().empty());
fault_fs_->SetFilesystemActive(true);
// This Resume() will attempt to create a new manifest file and fail again
s = dbfull()->Resume();
ASSERT_TRUE(s.IsNoSpace());
ASSERT_EQ(dbfull()->TEST_GetBGError().severity(),
ROCKSDB_NAMESPACE::Status::Severity::kHardError);
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
ASSERT_FALSE(dbfull()->TEST_GetFilesToQuarantine().empty());
fault_fs_->SetFilesystemActive(true);
SyncPoint::GetInstance()->ClearAllCallBacks();
+3 -6
View File
@@ -228,18 +228,15 @@ void EventHelpers::NotifyOnErrorRecoveryEnd(
InstrumentedMutex* db_mutex) {
if (!listeners.empty()) {
db_mutex->AssertHeld();
// Make copies before releasing mutex to avoid race.
Status old_bg_error_cp = old_bg_error;
Status new_bg_error_cp = new_bg_error;
// release lock while notifying events
db_mutex->Unlock();
TEST_SYNC_POINT("NotifyOnErrorRecoveryEnd:MutexUnlocked:1");
TEST_SYNC_POINT("NotifyOnErrorRecoveryEnd:MutexUnlocked:2");
for (auto& listener : listeners) {
BackgroundErrorRecoveryInfo info;
info.old_bg_error = old_bg_error_cp;
info.new_bg_error = new_bg_error_cp;
listener->OnErrorRecoveryCompleted(old_bg_error_cp);
info.old_bg_error = old_bg_error;
info.new_bg_error = new_bg_error;
listener->OnErrorRecoveryCompleted(old_bg_error);
listener->OnErrorRecoveryEnd(info);
info.old_bg_error.PermitUncheckedError();
info.new_bg_error.PermitUncheckedError();
+18 -76
View File
@@ -44,12 +44,9 @@ Status ExternalSstFileIngestionJob::Prepare(
return status;
}
// Files generated in another DB or CF may have a different column family
// ID, so we let it pass here.
if (file_to_ingest.cf_id !=
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily &&
file_to_ingest.cf_id != cfd_->GetID() &&
!ingestion_options_.allow_db_generated_files) {
file_to_ingest.cf_id != cfd_->GetID()) {
return Status::InvalidArgument(
"External file column family id don't match");
}
@@ -114,7 +111,6 @@ Status ExternalSstFileIngestionJob::Prepare(
const std::string path_inside_db = TableFileName(
cfd_->ioptions()->cf_paths, f.fd.GetNumber(), f.fd.GetPathId());
if (ingestion_options_.move_files) {
assert(!ingestion_options_.allow_db_generated_files);
status =
fs_->LinkFile(path_outside_db, path_inside_db, IOOptions(), nullptr);
if (status.ok()) {
@@ -346,7 +342,8 @@ Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed,
autovector<UserKeyRange> ranges;
ranges.reserve(n);
for (const IngestedFileInfo& file_to_ingest : files_to_ingest_) {
ranges.emplace_back(file_to_ingest.start_ukey, file_to_ingest.limit_ukey);
ranges.emplace_back(file_to_ingest.smallest_internal_key.user_key(),
file_to_ingest.largest_internal_key.user_key());
}
Status status = cfd_->RangesOverlapWithMemtables(
ranges, super_version, db_options_.allow_data_in_errors, flush_needed);
@@ -708,16 +705,9 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
// Get table version
auto version_iter = uprops.find(ExternalSstFilePropertyNames::kVersion);
if (version_iter == uprops.end()) {
if (!ingestion_options_.allow_db_generated_files) {
return Status::Corruption("External file version not found");
} else {
// 0 is special version for when a file from live DB does not have the
// version table property
file_to_ingest->version = 0;
}
} else {
file_to_ingest->version = DecodeFixed32(version_iter->second.c_str());
return Status::Corruption("External file version not found");
}
file_to_ingest->version = DecodeFixed32(version_iter->second.c_str());
auto seqno_iter = uprops.find(ExternalSstFilePropertyNames::kGlobalSeqno);
if (file_to_ingest->version == 2) {
@@ -744,15 +734,8 @@ Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
return Status::InvalidArgument(
"External SST file V1 does not support global seqno");
}
} else if (file_to_ingest->version == 0) {
// allow_db_generated_files is true
assert(seqno_iter == uprops.end());
file_to_ingest->original_seqno = 0;
file_to_ingest->global_seqno_offset = 0;
} else {
return Status::InvalidArgument("External file version " +
std::to_string(file_to_ingest->version) +
" is not supported");
return Status::InvalidArgument("External file version is not supported");
}
file_to_ingest->cf_id = static_cast<uint32_t>(props->column_family_id);
@@ -914,25 +897,6 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
} else if (!iter->status().ok()) {
return iter->status();
}
if (ingestion_options_.allow_db_generated_files) {
// Verify that all keys have seqno zero.
// TODO: store largest seqno in table property and validate it instead.
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
Status pik_status =
ParseInternalKey(iter->key(), &key, allow_data_in_errors);
if (!pik_status.ok()) {
return Status::Corruption("Corrupted key in external file. ",
pik_status.getState());
}
if (key.sequence != 0) {
return Status::NotSupported(
"External file has a key with non zero sequence number.");
}
}
if (!iter->status().ok()) {
return iter->status();
}
}
std::unique_ptr<InternalIterator> range_del_iter(
table_reader->NewRangeTombstoneIterator(ro));
@@ -948,11 +912,6 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
return Status::Corruption("Corrupted key in external file. ",
pik_status.getState());
}
if (key.sequence != 0) {
return Status::Corruption(
"External file has a range deletion with non zero sequence "
"number.");
}
RangeTombstone tombstone(key, range_del_iter->value());
InternalKey start_key = tombstone.SerializeKey();
@@ -971,17 +930,6 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
}
}
const size_t ts_sz = ucmp->timestamp_size();
Slice smallest = file_to_ingest->smallest_internal_key.user_key();
Slice largest = file_to_ingest->largest_internal_key.user_key();
if (ts_sz > 0) {
AppendUserKeyWithMaxTimestamp(&file_to_ingest->start_ukey, smallest, ts_sz);
AppendUserKeyWithMinTimestamp(&file_to_ingest->limit_ukey, largest, ts_sz);
} else {
file_to_ingest->start_ukey.assign(smallest.data(), smallest.size());
file_to_ingest->limit_ukey.assign(largest.data(), largest.size());
}
auto s =
GetSstInternalUniqueId(file_to_ingest->table_properties.db_id,
file_to_ingest->table_properties.db_session_id,
@@ -1005,15 +953,13 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
*assigned_seqno = 0;
auto ucmp = cfd_->user_comparator();
const size_t ts_sz = ucmp->timestamp_size();
if (force_global_seqno || files_overlap_ ||
compaction_style == kCompactionStyleFIFO) {
if (force_global_seqno || files_overlap_) {
*assigned_seqno = last_seqno + 1;
// If files overlap, we have to ingest them at level 0.
if (files_overlap_ || compaction_style == kCompactionStyleFIFO) {
if (files_overlap_) {
assert(ts_sz == 0);
file_to_ingest->picked_level = 0;
if (ingestion_options_.fail_if_not_bottommost_level &&
cfd_->NumberLevels() > 1) {
if (ingestion_options_.fail_if_not_bottommost_level) {
status = Status::TryAgain(
"Files cannot be ingested to Lmax. Please make sure key range of "
"Lmax does not overlap with files to ingest.");
@@ -1034,8 +980,9 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
if (lvl > 0 && lvl < vstorage->base_level()) {
continue;
}
if (cfd_->RangeOverlapWithCompaction(file_to_ingest->start_ukey,
file_to_ingest->limit_ukey, lvl)) {
if (cfd_->RangeOverlapWithCompaction(
file_to_ingest->smallest_internal_key.user_key(),
file_to_ingest->largest_internal_key.user_key(), lvl)) {
// We must use L0 or any level higher than `lvl` to be able to overwrite
// the compaction output keys that we overlap with in this level, We also
// need to assign this file a seqno to overwrite the compaction output
@@ -1045,8 +992,9 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
} else if (vstorage->NumLevelFiles(lvl) > 0) {
bool overlap_with_level = false;
status = sv->current->OverlapWithLevelIterator(
ro, env_options_, file_to_ingest->start_ukey,
file_to_ingest->limit_ukey, lvl, &overlap_with_level);
ro, env_options_, file_to_ingest->smallest_internal_key.user_key(),
file_to_ingest->largest_internal_key.user_key(), lvl,
&overlap_with_level);
if (!status.ok()) {
return status;
}
@@ -1087,18 +1035,11 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
"Column family enables user-defined timestamps, please make sure the "
"key range (without timestamp) of external file does not overlap "
"with key range (without timestamp) in the db");
return status;
}
if (*assigned_seqno == 0) {
*assigned_seqno = last_seqno + 1;
}
}
if (ingestion_options_.allow_db_generated_files && *assigned_seqno != 0) {
return Status::InvalidArgument(
"An ingested file is assigned to a non-zero sequence number, which is "
"incompatible with ingestion option allow_db_generated_files.");
}
return status;
}
@@ -1222,8 +1163,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
+4 -445
View File
@@ -3082,68 +3082,6 @@ TEST_P(ExternalSSTFileTest,
delete iter;
}
TEST_F(ExternalSSTFileTest, FIFOCompaction) {
// FIFO always ingests SST files to L0 and assign latest sequence number.
Options options = CurrentOptions();
options.num_levels = 1;
options.compaction_style = kCompactionStyleFIFO;
options.max_open_files = -1;
DestroyAndReopen(options);
std::map<std::string, std::string> true_data;
for (int i = 0; i < 100; ++i) {
ASSERT_OK(Put(Key(i), Key(i) + "_val"));
true_data[Key(i)] = Key(i) + "_val";
}
ASSERT_OK(Flush());
ASSERT_EQ("1", FilesPerLevel());
std::vector<std::pair<std::string, std::string>> file_data;
for (int i = 0; i <= 20; i++) {
file_data.emplace_back(Key(i), Key(i) + "_ingest");
}
// Overlaps with memtable, will trigger flush
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1,
/*allow_global_seqno=*/true, true, false,
false, false, &true_data));
ASSERT_EQ("2", FilesPerLevel());
file_data.clear();
for (int i = 100; i <= 120; i++) {
file_data.emplace_back(Key(i), Key(i) + "_ingest");
}
// global sequence number is always assigned, so this will fail
ASSERT_NOK(GenerateAndAddExternalFile(options, file_data, -1,
/*allow_global_seqno=*/false, true,
false, false, false, &true_data));
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1,
/*allow_global_seqno=*/true, true, false,
false, false, &true_data));
// Compact to data to lower level to test multi-level FIFO later
options.num_levels = 7;
options.compaction_style = kCompactionStyleUniversal;
ASSERT_OK(TryReopen(options));
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
options.num_levels = 7;
options.compaction_style = kCompactionStyleFIFO;
ASSERT_OK(TryReopen(options));
file_data.clear();
for (int i = 200; i <= 220; i++) {
file_data.emplace_back(Key(i), Key(i) + "_ingest");
}
// Files are ingested into L0 for multi-level FIFO
ASSERT_OK(GenerateAndAddExternalFile(options, file_data, -1,
/*allow_global_seqno=*/true, true, false,
false, false, &true_data));
ASSERT_EQ("1,0,0,0,0,0,1", FilesPerLevel());
VerifyDBFromMap(true_data);
}
class ExternalSSTFileWithTimestampTest : public ExternalSSTFileTest {
public:
ExternalSSTFileWithTimestampTest() = default;
@@ -3358,60 +3296,11 @@ TEST_F(ExternalSSTFileWithTimestampTest, Basic) {
ASSERT_OK(IngestExternalUDTFile({file4}));
for (int k = 200; k < 250; k++) {
VerifyValueAndTs(Key(k), EncodeAsUint64(k), Key(k) + "_val",
EncodeAsUint64(k));
}
// In UDT mode, any external file that can be successfully ingested also
// should not overlap with the db. As a result, they can always get the
// seq 0 assigned.
ASSERT_EQ(db_->GetLatestSequenceNumber(), seq_num_before_ingestion);
// file5.sst (Key(200), ts = 199)
// While DB has (Key(200), ts = 200) => user key without timestamp overlaps
std::string file5 = sst_files_dir_ + "file5.sst";
ASSERT_OK(sst_file_writer.Open(file5));
ASSERT_OK(
sst_file_writer.Put(Key(200), EncodeAsUint64(199), Key(200) + "_val"));
ExternalSstFileInfo file5_info;
ASSERT_OK(sst_file_writer.Finish(&file5_info));
ASSERT_TRUE(IngestExternalUDTFile({file5}).IsInvalidArgument());
// file6.sst (Key(200), ts = 201)
// While DB has (Key(200), ts = 200) => user key without timestamp overlaps
std::string file6 = sst_files_dir_ + "file6.sst";
ASSERT_OK(sst_file_writer.Open(file6));
ASSERT_OK(
sst_file_writer.Put(Key(200), EncodeAsUint64(201), Key(0) + "_val"));
ExternalSstFileInfo file6_info;
ASSERT_OK(sst_file_writer.Finish(&file6_info));
ASSERT_TRUE(IngestExternalUDTFile({file6}).IsInvalidArgument());
// Check memtable overlap.
ASSERT_OK(dbfull()->Put(WriteOptions(), Key(250), EncodeAsUint64(250),
Key(250) + "_val"));
std::string file7 = sst_files_dir_ + "file7.sst";
ASSERT_OK(sst_file_writer.Open(file7));
ASSERT_OK(
sst_file_writer.Put(Key(250), EncodeAsUint64(249), Key(250) + "_val2"));
ExternalSstFileInfo file7_info;
ASSERT_OK(sst_file_writer.Finish(&file7_info));
ASSERT_TRUE(IngestExternalUDTFile({file7}).IsInvalidArgument());
std::string file8 = sst_files_dir_ + "file8.sst";
ASSERT_OK(sst_file_writer.Open(file8));
ASSERT_OK(
sst_file_writer.Put(Key(250), EncodeAsUint64(251), Key(250) + "_val3"));
ExternalSstFileInfo file8_info;
ASSERT_OK(sst_file_writer.Finish(&file8_info));
ASSERT_TRUE(IngestExternalUDTFile({file8}).IsInvalidArgument());
DestroyAndRecreateExternalSSTFilesDir();
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction |
kRangeDelSkipConfigs));
@@ -3719,7 +3608,10 @@ TEST_F(ExternalSSTFileWithTimestampTest, TimestampsNotPersistedBasic) {
}
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
testing::Combine(testing::Bool(), testing::Bool()));
testing::Values(std::make_tuple(false, false),
std::make_tuple(false, true),
std::make_tuple(true, false),
std::make_tuple(true, true)));
INSTANTIATE_TEST_CASE_P(ExternSSTFileLinkFailFallbackTest,
ExternSSTFileLinkFailFallbackTest,
@@ -3727,339 +3619,6 @@ INSTANTIATE_TEST_CASE_P(ExternSSTFileLinkFailFallbackTest,
std::make_tuple(true, true),
std::make_tuple(false, false)));
class IngestDBGeneratedFileTest : public ExternalSSTFileTestBase,
public ::testing::WithParamInterface<bool> {
public:
IngestDBGeneratedFileTest() {
ingest_opts.allow_db_generated_files = true;
ingest_opts.move_files = false;
ingest_opts.verify_checksums_before_ingest = GetParam();
ingest_opts.snapshot_consistency = false;
}
protected:
IngestExternalFileOptions ingest_opts;
};
INSTANTIATE_TEST_CASE_P(BasicMultiConfig, IngestDBGeneratedFileTest,
testing::Bool());
TEST_P(IngestDBGeneratedFileTest, FailureCase) {
// Ingesting overlapping data should always fail.
do {
SCOPED_TRACE("option_config_ = " + std::to_string(option_config_));
Options options = CurrentOptions();
CreateAndReopenWithCF({"toto"}, options);
// Fill CFs with overlapping keys. Will try to ingest CF1 into default CF.
for (int k = 0; k < 50; ++k) {
ASSERT_OK(Put(Key(k), "default_cf_" + Key(k)));
}
for (int k = 49; k < 100; ++k) {
ASSERT_OK(Put(1, Key(k), "cf1_" + Key(k)));
}
ASSERT_OK(Flush(/*cf=*/1));
{
// Verify that largest key of the file has non-zero seqno.
std::vector<std::vector<FileMetaData>> metadata;
dbfull()->TEST_GetFilesMetaData(handles_[1], &metadata, nullptr);
const FileMetaData& file = metadata[0][0];
ValueType vtype;
SequenceNumber seq;
UnPackSequenceAndType(ExtractInternalKeyFooter(file.largest.Encode()),
&seq, &vtype);
ASSERT_GE(seq, 0);
}
std::vector<LiveFileMetaData> live_meta;
db_->GetLiveFilesMetaData(&live_meta);
ASSERT_EQ(live_meta.size(), 1);
std::vector<std::string> to_ingest_files;
to_ingest_files.emplace_back(live_meta[0].directory + "/" +
live_meta[0].relative_filename);
// Ingesting a file whose boundary key has non-zero seqno.
Status s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_TRUE(
s.ToString().find("External file has non zero sequence number") !=
std::string::npos);
ASSERT_NOK(s);
{
// Only non-boundary key with non-zero seqno.
const Snapshot* snapshot = db_->GetSnapshot();
ASSERT_OK(Put(1, Key(70), "cf1_" + Key(70)));
ASSERT_OK(Flush(1));
CompactRangeOptions cro;
cro.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
// Verify that only the non-boundary key of the file has non-zero seqno.
std::vector<std::vector<FileMetaData>> metadata;
// File may be at different level for different options.
dbfull()->TEST_GetFilesMetaData(handles_[1], &metadata, nullptr);
bool found_file = false;
for (const auto& level : metadata) {
if (level.empty()) {
continue;
}
ASSERT_FALSE(found_file);
found_file = true;
ASSERT_EQ(1, level.size());
const FileMetaData& file = level[0];
ValueType vtype;
SequenceNumber seq;
UnPackSequenceAndType(ExtractInternalKeyFooter(file.largest.Encode()),
&seq, &vtype);
ASSERT_EQ(seq, 0);
UnPackSequenceAndType(ExtractInternalKeyFooter(file.smallest.Encode()),
&seq, &vtype);
ASSERT_EQ(seq, 0);
ASSERT_GT(file.fd.largest_seqno, 0);
}
ASSERT_TRUE(found_file);
live_meta.clear();
db_->GetLiveFilesMetaData(&live_meta);
ASSERT_EQ(live_meta.size(), 1);
to_ingest_files[0] =
live_meta[0].directory + "/" + live_meta[0].relative_filename;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_NOK(s);
ASSERT_TRUE(
s.ToString().find(
"External file has a key with non zero sequence number") !=
std::string::npos);
db_->ReleaseSnapshot(snapshot);
}
CompactRangeOptions cro;
cro.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
live_meta.clear();
db_->GetLiveFilesMetaData(&live_meta);
ASSERT_EQ(live_meta.size(), 1);
ASSERT_EQ(0, live_meta[0].largest_seqno);
to_ingest_files[0] =
live_meta[0].directory + "/" + live_meta[0].relative_filename;
ingest_opts.allow_db_generated_files = false;
// Ingesting a DB genrate file with allow_db_generated_files = false;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_TRUE(s.ToString().find("External file version not found") !=
std::string::npos);
ASSERT_NOK(s);
const std::string err =
"An ingested file is assigned to a non-zero sequence number, which is "
"incompatible with ingestion option allow_db_generated_files";
ingest_opts.allow_db_generated_files = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_TRUE(s.ToString().find(err) != std::string::npos);
ASSERT_NOK(s);
if (options.compaction_style != kCompactionStyleUniversal) {
// FIXME: after fixing ingestion with universal compaction, currently
// will always ingest into L0.
ingest_opts.fail_if_not_bottommost_level = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_NOK(s);
ASSERT_TRUE(s.ToString().find("Files cannot be ingested to Lmax") !=
std::string::npos);
ingest_opts.fail_if_not_bottommost_level = false;
}
ingest_opts.write_global_seqno = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ASSERT_TRUE(s.ToString().find("write_global_seqno is deprecated and does "
"not work with allow_db_generated_files") !=
std::string::npos);
ASSERT_NOK(s);
ingest_opts.write_global_seqno = false;
// Delete the overlapping key.
ASSERT_OK(db_->Delete(WriteOptions(), handles_[1], Key(49)));
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
live_meta.clear();
db_->GetLiveFilesMetaData(&live_meta);
bool cf1_file_found = false;
for (const auto& f : live_meta) {
if (f.column_family_name == "toto") {
ASSERT_FALSE(cf1_file_found);
cf1_file_found = true;
ASSERT_EQ(0, f.largest_seqno);
to_ingest_files[0] = f.directory + "/" + f.relative_filename;
}
}
ASSERT_TRUE(cf1_file_found);
const Snapshot* snapshot = db_->GetSnapshot();
ingest_opts.snapshot_consistency = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
// snapshot_consistency with snapshot will assign a newest sequence number.
ASSERT_TRUE(s.ToString().find(err) != std::string::npos);
ASSERT_NOK(s);
ingest_opts.move_files = true;
s = db_->IngestExternalFile(to_ingest_files, ingest_opts);
ingest_opts.move_files = false;
ASSERT_TRUE(
s.ToString().find("Options move_files and allow_db_generated_files are "
"not compatible") != std::string::npos);
ASSERT_NOK(s);
ingest_opts.snapshot_consistency = false;
ASSERT_OK(db_->IngestExternalFile(to_ingest_files, ingest_opts));
db_->ReleaseSnapshot(snapshot);
// Verify default CF content.
std::string val;
for (int k = 0; k < 100; ++k) {
ASSERT_OK(db_->Get(ReadOptions(), Key(k), &val));
if (k < 50) {
ASSERT_EQ(val, "default_cf_" + Key(k));
} else {
ASSERT_EQ(val, "cf1_" + Key(k));
}
}
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
}
class IngestDBGeneratedFileTest2
: public ExternalSSTFileTestBase,
public ::testing::WithParamInterface<std::tuple<bool, bool, bool, bool>> {
public:
IngestDBGeneratedFileTest2() = default;
};
INSTANTIATE_TEST_CASE_P(VaryingOptions, IngestDBGeneratedFileTest2,
testing::Combine(testing::Bool(), testing::Bool(),
testing::Bool(), testing::Bool()));
TEST_P(IngestDBGeneratedFileTest2, NotOverlapWithDB) {
// Use a separate column family to sort some data, generate multiple SST
// files. Then ingest these files into another column family or DB. The data
// to be ingested does not overlap with existing data.
IngestExternalFileOptions ingest_opts;
ingest_opts.allow_db_generated_files = true;
ingest_opts.move_files = false;
ingest_opts.snapshot_consistency = std::get<0>(GetParam());
ingest_opts.allow_global_seqno = std::get<1>(GetParam());
ingest_opts.allow_blocking_flush = std::get<2>(GetParam());
ingest_opts.fail_if_not_bottommost_level = std::get<3>(GetParam());
do {
SCOPED_TRACE("option_config_ = " + std::to_string(option_config_));
Options options = CurrentOptions();
// vector memtable for temp CF does not support concurrent write
options.allow_concurrent_memtable_write = false;
CreateAndReopenWithCF({"toto"}, options);
// non-empty bottommost level
WriteOptions wo;
for (int k = 0; k < 50; ++k) {
ASSERT_OK(db_->Put(wo, handles_[1], Key(k), "base_val_" + Key(k)));
}
ASSERT_OK(Flush());
CompactRangeOptions cro;
cro.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
// non-empty memtable
for (int k = 50; k < 100; ++k) {
ASSERT_OK(db_->Put(wo, handles_[1], Key(k), "base_val_" + Key(k)));
}
// load external data to sort, generate multiple files
Options temp_cf_opts;
ColumnFamilyHandle* temp_cfh;
temp_cf_opts.target_file_size_base = 4 << 10;
temp_cf_opts.memtable_factory.reset(new VectorRepFactory());
temp_cf_opts.allow_concurrent_memtable_write = false;
temp_cf_opts.compaction_style = kCompactionStyleUniversal;
ASSERT_OK(db_->CreateColumnFamily(temp_cf_opts, "temp_cf", &temp_cfh));
Random rnd(301);
std::vector<std::string> expected_value;
expected_value.resize(100);
// Out of order insertion of keys from 100 to 199.
for (int k = 99; k >= 0; --k) {
expected_value[k] = rnd.RandomString(200);
ASSERT_OK(db_->Put(wo, temp_cfh, Key(k + 100), expected_value[k]));
}
ASSERT_OK(db_->CompactRange(cro, temp_cfh, nullptr, nullptr));
std::vector<std::string> sst_file_paths;
ColumnFamilyMetaData cf_meta;
db_->GetColumnFamilyMetaData(temp_cfh, &cf_meta);
ASSERT_GT(cf_meta.file_count, 1);
for (const auto& level_meta : cf_meta.levels) {
if (level_meta.level + 1 < temp_cf_opts.num_levels) {
ASSERT_EQ(0, level_meta.files.size());
} else {
ASSERT_GT(level_meta.files.size(), 1);
for (const auto& meta : level_meta.files) {
ASSERT_EQ(0, meta.largest_seqno);
sst_file_paths.emplace_back(meta.directory + "/" +
meta.relative_filename);
}
}
}
ASSERT_OK(
db_->IngestExternalFile(handles_[1], sst_file_paths, ingest_opts));
// Verify state of the CF1
ReadOptions ro;
std::string val;
for (int k = 0; k < 100; ++k) {
ASSERT_OK(db_->Get(ro, handles_[1], Key(k), &val));
ASSERT_EQ(val, "base_val_" + Key(k));
ASSERT_OK(db_->Get(ro, handles_[1], Key(100 + k), &val));
ASSERT_EQ(val, expected_value[k]);
}
// Ingest into another DB.
if (!encrypted_env_) {
// Ingestion between encrypted env and non-encrypted env won't work.
std::string db2_path = test::PerThreadDBPath("DB2");
Options db2_options;
db2_options.create_if_missing = true;
DB* db2 = nullptr;
ASSERT_OK(DB::Open(db2_options, db2_path, &db2));
// Write some base data.
expected_value.emplace_back(rnd.RandomString(100));
ASSERT_OK(db2->Put(WriteOptions(), Key(200), expected_value.back()));
ASSERT_OK(db2->CompactRange(cro, nullptr, nullptr));
expected_value.emplace_back(rnd.RandomString(100));
ASSERT_OK(db2->Put(WriteOptions(), Key(201), expected_value.back()));
ASSERT_OK(db2->IngestExternalFile({sst_file_paths}, ingest_opts));
{
std::unique_ptr<Iterator> iter{db2->NewIterator(ReadOptions())};
iter->SeekToFirst();
// The DB should have keys 100-199 from ingested files, and keys 200 and
// 201 from itself.
for (int k = 100; k <= 201; ++k, iter->Next()) {
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key(), Key(k));
ASSERT_EQ(iter->value(), expected_value[k - 100]);
}
ASSERT_FALSE(iter->Valid());
ASSERT_OK(iter->status());
}
// Dropping the original CF should not affect db2, reopening it should not
// miss SST files.
ASSERT_OK(db_->DropColumnFamily(temp_cfh));
ASSERT_OK(db_->DestroyColumnFamilyHandle(temp_cfh));
ASSERT_OK(db2->Close());
delete db2;
ASSERT_OK(DB::Open(db2_options, db2_path, &db2));
ASSERT_OK(db2->Close());
delete db2;
ASSERT_OK(DestroyDB(db2_path, db2_options));
} else {
ASSERT_OK(db_->DropColumnFamily(temp_cfh));
ASSERT_OK(db_->DestroyColumnFamilyHandle(temp_cfh));
}
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction));
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+72 -88
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,93 +629,6 @@ INSTANTIATE_TEST_CASE_P(
std::make_tuple(false, kSyncWal, kEnd),
std::make_tuple(true, kSyncWal, kEnd)));
class FaultInjectionDBTest : public DBTestBase {
public:
FaultInjectionDBTest()
: DBTestBase("fault_injection_fs_test", /*env_do_fsync=*/false) {}
};
TEST(FaultInjectionFSTest, ReadUnsyncedData) {
std::shared_ptr<FaultInjectionTestFS> fault_fs =
std::make_shared<FaultInjectionTestFS>(FileSystem::Default());
fault_fs->SetInjectUnsyncedDataLoss(true);
ASSERT_TRUE(fault_fs->ReadUnsyncedData());
ASSERT_TRUE(fault_fs->InjectUnsyncedDataLoss());
// This is a randomized mini-stress test, to reduce the chances of bugs in
// FaultInjectionTestFS being caught only in db_stress, where they are
// difficult to debug. ~1000 iterations might be needed to debug relevant
// code changes. Limiting to 10 for each regular unit test run.
auto seed = Random::GetTLSInstance()->Next();
for (int i = 0; i < 10; i++, seed++) {
Random rnd(seed);
uint32_t len = rnd.Uniform(10000) + 1;
std::string f =
test::PerThreadDBPath("read_unsynced." + std::to_string(seed));
std::string data = rnd.RandomString(len);
// Create partially synced file
std::unique_ptr<FSWritableFile> w;
ASSERT_OK(fault_fs->NewWritableFile(f, {}, &w, nullptr));
uint32_t synced_len = rnd.Uniform(len + 1);
ASSERT_OK(w->Append(Slice(data.data(), synced_len), {}, nullptr));
if (synced_len > 0) {
ASSERT_OK(w->Sync({}, nullptr));
}
ASSERT_OK(w->Append(Slice(data.data() + synced_len, len - synced_len), {},
nullptr));
// Test file size includes unsynced data
{
uint64_t file_size;
ASSERT_OK(fault_fs->GetFileSize(f, {}, &file_size, nullptr));
ASSERT_EQ(len, file_size);
}
// Test read file contents, with two reads that probably don't
// align with the unsynced split. And maybe a sync or write between
// the two reads.
std::unique_ptr<FSSequentialFile> r;
ASSERT_OK(fault_fs->NewSequentialFile(f, {}, &r, nullptr));
uint32_t first_read_len = rnd.Uniform(len + 1);
Slice sl;
std::unique_ptr<char[]> scratch(new char[first_read_len]);
ASSERT_OK(r->Read(first_read_len, {}, &sl, scratch.get(), nullptr));
ASSERT_EQ(first_read_len, sl.size());
ASSERT_EQ(0, sl.compare(Slice(data.data(), first_read_len)));
// Maybe a sync and/or write and/or close between the two reads.
if (rnd.OneIn(2)) {
ASSERT_OK(w->Sync({}, nullptr));
}
if (rnd.OneIn(2)) {
uint32_t more_len = rnd.Uniform(1000) + 1;
std::string more_data = rnd.RandomString(more_len);
ASSERT_OK(w->Append(more_data, {}, nullptr));
data += more_data;
len += more_len;
}
if (rnd.OneIn(2)) {
ASSERT_OK(w->Sync({}, nullptr));
}
if (rnd.OneIn(2)) {
ASSERT_OK(w->Close({}, nullptr));
w.reset();
}
// Second read some of, all of, or more than rest of file
uint32_t second_read_len = rnd.Uniform(len + 1);
scratch.reset(new char[second_read_len]);
ASSERT_OK(r->Read(second_read_len, {}, &sl, scratch.get(), nullptr));
if (len - first_read_len < second_read_len) {
ASSERT_EQ(len - first_read_len, sl.size());
} else {
ASSERT_EQ(second_read_len, sl.size());
}
ASSERT_EQ(0, sl.compare(Slice(data.data() + first_read_len, sl.size())));
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+3 -30
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
+42 -46
View File
@@ -175,29 +175,22 @@ Status ImportColumnFamilyJob::Run() {
static_cast<uint64_t>(temp_current_time);
}
// Recover files' epoch number using dummy VersionStorageInfo
VersionBuilder dummy_version_builder(
cfd_->current()->version_set()->file_options(), cfd_->ioptions(),
cfd_->table_cache(), cfd_->current()->storage_info(),
cfd_->current()->version_set(),
cfd_->GetFileMetadataCacheReservationManager());
VersionStorageInfo dummy_vstorage(
&cfd_->internal_comparator(), cfd_->user_comparator(),
cfd_->NumberLevels(), cfd_->ioptions()->compaction_style,
nullptr /* src_vstorage */, cfd_->ioptions()->force_consistency_checks,
EpochNumberRequirement::kMightMissing, cfd_->ioptions()->clock,
cfd_->GetLatestMutableCFOptions()->bottommost_file_compaction_delay,
cfd_->current()->version_set()->offpeak_time_option());
Status s;
// When importing multiple CFs, we should not reuse epoch number from ingested
// files. Since these epoch numbers were assigned by different CFs, there may
// be different files from different CFs with the same epoch number. With a
// subsequent intra-L0 compaction we may end up with files with overlapping
// key range but the same epoch number. Here we will create a dummy
// VersionStorageInfo per CF being imported. Each CF's files will be assigned
// increasing epoch numbers to avoid duplicated epoch number. This is done by
// only resetting epoch number of the new CF in the first call to
// RecoverEpochNumbers() below.
for (size_t i = 0; s.ok() && i < files_to_import_.size(); ++i) {
VersionBuilder dummy_version_builder(
cfd_->current()->version_set()->file_options(), cfd_->ioptions(),
cfd_->table_cache(), cfd_->current()->storage_info(),
cfd_->current()->version_set(),
cfd_->GetFileMetadataCacheReservationManager());
VersionStorageInfo dummy_vstorage(
&cfd_->internal_comparator(), cfd_->user_comparator(),
cfd_->NumberLevels(), cfd_->ioptions()->compaction_style,
nullptr /* src_vstorage */, cfd_->ioptions()->force_consistency_checks,
EpochNumberRequirement::kMightMissing, cfd_->ioptions()->clock,
cfd_->GetLatestMutableCFOptions()->bottommost_file_compaction_delay,
cfd_->current()->version_set()->offpeak_time_option());
for (size_t j = 0; s.ok() && j < files_to_import_[i].size(); ++j) {
const auto& f = files_to_import_[i][j];
const auto& file_metadata = *metadatas_[i][j];
@@ -225,39 +218,42 @@ Status ImportColumnFamilyJob::Run() {
f.table_properties.user_defined_timestamps_persisted));
s = dummy_version_builder.Apply(&dummy_version_edit);
}
if (s.ok()) {
s = dummy_version_builder.SaveTo(&dummy_vstorage);
}
if (s.ok()) {
// force resetting epoch number for each file
dummy_vstorage.RecoverEpochNumbers(cfd_, /*restart_epoch=*/i == 0,
/*force=*/true);
edit_.SetColumnFamily(cfd_->GetID());
}
if (s.ok()) {
s = dummy_version_builder.SaveTo(&dummy_vstorage);
}
if (s.ok()) {
dummy_vstorage.RecoverEpochNumbers(cfd_);
}
// Record changes from this CF import in VersionEdit, including files with
// recovered epoch numbers
if (s.ok()) {
edit_.SetColumnFamily(cfd_->GetID());
for (int level = 0; level < dummy_vstorage.num_levels(); level++) {
for (FileMetaData* file_meta : dummy_vstorage.LevelFiles(level)) {
edit_.AddFile(level, *file_meta);
// If incoming sequence number is higher, update local sequence
// number.
if (file_meta->fd.largest_seqno > versions_->LastSequence()) {
versions_->SetLastAllocatedSequence(file_meta->fd.largest_seqno);
versions_->SetLastPublishedSequence(file_meta->fd.largest_seqno);
versions_->SetLastSequence(file_meta->fd.largest_seqno);
}
}
}
}
// Release resources occupied by the dummy VersionStorageInfo
for (int level = 0; level < dummy_vstorage.num_levels(); level++) {
for (FileMetaData* file_meta : dummy_vstorage.LevelFiles(level)) {
file_meta->refs--;
if (file_meta->refs <= 0) {
delete file_meta;
edit_.AddFile(level, *file_meta);
// If incoming sequence number is higher, update local sequence number.
if (file_meta->fd.largest_seqno > versions_->LastSequence()) {
versions_->SetLastAllocatedSequence(file_meta->fd.largest_seqno);
versions_->SetLastPublishedSequence(file_meta->fd.largest_seqno);
versions_->SetLastSequence(file_meta->fd.largest_seqno);
}
}
}
}
// Release resources occupied by the dummy VersionStorageInfo
for (int level = 0; level < dummy_vstorage.num_levels(); level++) {
for (FileMetaData* file_meta : dummy_vstorage.LevelFiles(level)) {
file_meta->refs--;
if (file_meta->refs <= 0) {
delete file_meta;
}
}
}
return s;
}
-102
View File
@@ -946,108 +946,6 @@ TEST_F(ImportColumnFamilyTest, ImportMultiColumnFamilySeveralFilesWithOverlap) {
ASSERT_OK(db_->DestroyColumnFamilyHandle(second_cfh));
}
TEST_F(ImportColumnFamilyTest, AssignEpochNumberToMultipleCF) {
// Test ingesting CFs where L0 files could have the same epoch number.
Options options = CurrentOptions();
options.level_compaction_dynamic_level_bytes = true;
options.max_background_jobs = 8;
env_->SetBackgroundThreads(2, Env::LOW);
env_->SetBackgroundThreads(0, Env::BOTTOM);
CreateAndReopenWithCF({"CF1", "CF2"}, options);
// CF1:
// L6: [0, 99], [100, 199]
// CF2:
// L6: [1000, 1099], [1100, 1199]
for (int i = 100; i < 200; ++i) {
ASSERT_OK(Put(1, Key(i), Key(i) + "_val"));
ASSERT_OK(Put(2, Key(1000 + i), Key(1000 + i) + "_val"));
}
ASSERT_OK(Flush(1));
ASSERT_OK(Flush(2));
for (int i = 0; i < 100; ++i) {
ASSERT_OK(Put(1, Key(i), Key(i) + "_val"));
ASSERT_OK(Put(2, Key(1000 + i), Key(1000 + i) + "_val"));
}
ASSERT_OK(Flush(1));
ASSERT_OK(Flush(2));
MoveFilesToLevel(6, 1);
MoveFilesToLevel(6, 2);
// CF1:
// level 0 epoch: 5 file num 30 smallest key000010 - key000019
// level 0 epoch: 4 file num 27 smallest key000000 - key000009
// level 0 epoch: 3 file num 23 smallest key000100 - key000199
// level 6 epoch: 2 file num 20 smallest key000000 - key000099
// level 6 epoch: 1 file num 17 smallest key000100 - key000199
// CF2:
// level 0 epoch: 5 file num 31 smallest key001010 - key001019
// level 0 epoch: 4 file num 28 smallest key001000 - key001009
// level 0 epoch: 3 file num 25 smallest key001020 - key001029
// level 6 epoch: 2 file num 21 smallest key001000 - key001099
// level 6 epoch: 1 file num 18 smallest key001100 - key001199
for (int i = 100; i < 200; ++i) {
ASSERT_OK(Put(1, Key(i), Key(i) + "_val"));
}
ASSERT_OK(Flush(1));
for (int i = 20; i < 30; ++i) {
ASSERT_OK(Put(2, Key(i + 1000), Key(i + 1000) + "_val"));
}
ASSERT_OK(Flush(2));
for (int i = 0; i < 20; ++i) {
ASSERT_OK(Put(1, Key(i), Key(i) + "_val"));
ASSERT_OK(Put(2, Key(i + 1000), Key(i + 1000) + "_val"));
if (i % 10 == 9) {
ASSERT_OK(Flush(1));
ASSERT_OK(Flush(2));
}
}
ASSERT_OK(Flush(1));
ASSERT_OK(Flush(2));
// Create a CF by importing these two CF1 and CF2.
// Then two compactions will be triggerred, one to compact from L0
// to L6 (files #23 and #17), and another to do intra-L0 compaction
// for the rest of the L0 files. Before a bug fix, we used to
// directly use the epoch numbers from the ingested files in the new CF.
// This means different files from different CFs can have the same epoch
// number. If the intra-L0 compaction finishes first, it can cause a
// corruption where two L0 files can have the same epoch number but
// with overlapping key range.
Checkpoint* checkpoint1;
ASSERT_OK(Checkpoint::Create(db_, &checkpoint1));
ASSERT_OK(checkpoint1->ExportColumnFamily(handles_[1], export_files_dir_,
&metadata_ptr_));
ASSERT_OK(checkpoint1->ExportColumnFamily(handles_[2], export_files_dir2_,
&metadata_ptr2_));
ASSERT_NE(metadata_ptr_, nullptr);
ASSERT_NE(metadata_ptr2_, nullptr);
std::atomic_int compaction_counter = 0;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun",
[&compaction_counter](void*) {
compaction_counter++;
if (compaction_counter == 1) {
// Wait for the next compaction to finish
TEST_SYNC_POINT("WaitForSecondCompaction");
}
});
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BackgroundCompaction:AfterCompaction",
"WaitForSecondCompaction"}});
SyncPoint::GetInstance()->EnableProcessing();
ImportColumnFamilyOptions import_options;
import_options.move_files = false;
std::vector<const ExportImportFilesMetaData*> metadatas = {metadata_ptr_,
metadata_ptr2_};
ASSERT_OK(db_->CreateColumnFamilyWithImport(options, "CF3", import_options,
metadatas, &import_cfh_));
WaitForCompactOptions o;
ASSERT_OK(db_->WaitForCompact(o));
delete checkpoint1;
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
+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
+4 -9
View File
@@ -487,11 +487,9 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size,
type == kRecyclableUserDefinedTimestampSizeType);
if (is_recyclable_type) {
header_size = kRecyclableHeaderSize;
if (first_record_read_ && !recycled_) {
// A recycled log should have started with a recycled record
return kBadRecord;
if (end_of_buffer_offset_ - buffer_.size() == 0) {
recycled_ = true;
}
recycled_ = true;
// We need enough for the larger header
if (buffer_.size() < static_cast<size_t>(kRecyclableHeaderSize)) {
int r = kEof;
@@ -869,12 +867,9 @@ bool FragmentBufferedReader::TryReadFragment(
int header_size = kHeaderSize;
if ((type >= kRecyclableFullType && type <= kRecyclableLastType) ||
type == kRecyclableUserDefinedTimestampSizeType) {
if (first_record_read_ && !recycled_) {
// A recycled log should have started with a recycled record
*fragment_type_or_err = kBadRecord;
return true;
if (end_of_buffer_offset_ - buffer_.size() == 0) {
recycled_ = true;
}
recycled_ = true;
header_size = kRecyclableHeaderSize;
while (buffer_.size() < static_cast<size_t>(kRecyclableHeaderSize)) {
size_t old_size = buffer_.size();
-62
View File
@@ -774,32 +774,6 @@ TEST_P(LogTest, RecycleWithTimestampSize) {
ASSERT_EQ("EOF", Read());
}
// Validates that `MaybeAddUserDefinedTimestampSizeRecord`` adds padding to the
// tail of a block and switches to a new block, if there's not enough space for
// the record.
TEST_P(LogTest, TimestampSizeRecordPadding) {
bool recyclable_log = (std::get<0>(GetParam()) != 0);
const size_t header_size =
recyclable_log ? kRecyclableHeaderSize : kHeaderSize;
const size_t data_len = kBlockSize - 2 * header_size;
const auto first_str = BigString("foo", data_len);
Write(first_str);
UnorderedMap<uint32_t, size_t> ts_sz = {
{2, sizeof(uint64_t)},
};
ASSERT_OK(
writer_->MaybeAddUserDefinedTimestampSizeRecord(WriteOptions(), ts_sz));
ASSERT_LT(writer_->TEST_block_offset(), kBlockSize);
const auto second_str = BigString("bar", 1000);
Write(second_str);
ASSERT_EQ(first_str, Read());
CheckRecordAndTimestampSize(second_str, ts_sz);
}
// Do NOT enable compression for this instantiation.
INSTANTIATE_TEST_CASE_P(
Log, LogTest,
@@ -1157,42 +1131,6 @@ TEST_P(CompressionLogTest, AlignedFragmentation) {
ASSERT_EQ("EOF", Read());
}
TEST_P(CompressionLogTest, ChecksumMismatch) {
const CompressionType kCompressionType = std::get<2>(GetParam());
const bool kCompressionEnabled = kCompressionType != kNoCompression;
const bool kRecyclableLog = (std::get<0>(GetParam()) != 0);
if (!StreamingCompressionTypeSupported(kCompressionType)) {
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
return;
}
ASSERT_OK(SetupTestEnv());
Write("foooooo");
int header_len;
if (kRecyclableLog) {
header_len = kRecyclableHeaderSize;
} else {
header_len = kHeaderSize;
}
int compression_record_len;
if (kCompressionEnabled) {
compression_record_len = header_len + 4;
} else {
compression_record_len = 0;
}
IncrementByte(compression_record_len + header_len /* offset */,
14 /* delta */);
ASSERT_EQ("EOF", Read());
if (!kRecyclableLog) {
ASSERT_GT(DroppedBytes(), 0U);
ASSERT_EQ("OK", MatchError("checksum mismatch"));
} else {
ASSERT_EQ(0U, DroppedBytes());
ASSERT_EQ("", ReportMessage());
}
}
INSTANTIATE_TEST_CASE_P(
Compression, CompressionLogTest,
::testing::Combine(::testing::Values(0, 1), ::testing::Bool(),
+10 -50
View File
@@ -27,8 +27,6 @@ Writer::Writer(std::unique_ptr<WritableFileWriter>&& dest, uint64_t log_number,
block_offset_(0),
log_number_(log_number),
recycle_log_files_(recycle_log_files),
// Header size varies depending on whether we are recycling or not.
header_size_(recycle_log_files ? kRecyclableHeaderSize : kHeaderSize),
manual_flush_(manual_flush),
compression_type_(compression_type),
compress_(nullptr) {
@@ -53,11 +51,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,28 +72,18 @@ 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();
size_t left = slice.size();
// Header size varies depending on whether we are recycling or not.
const int header_size =
recycle_log_files_ ? kRecyclableHeaderSize : kHeaderSize;
// Fragment the record if necessary and emit it. Note that if slice
// is empty, we still want to iterate once to emit a single
// zero-length record
@@ -119,12 +102,12 @@ IOStatus Writer::AddRecord(const WriteOptions& write_options,
do {
const int64_t leftover = kBlockSize - block_offset_;
assert(leftover >= 0);
if (leftover < header_size_) {
if (leftover < header_size) {
// Switch to a new block
if (leftover > 0) {
// Fill the trailer (literal below relies on kHeaderSize and
// kRecyclableHeaderSize being <= 11)
assert(header_size_ <= 11);
assert(header_size <= 11);
s = dest_->Append(opts,
Slice("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
static_cast<size_t>(leftover)),
@@ -137,9 +120,9 @@ IOStatus Writer::AddRecord(const WriteOptions& write_options,
}
// Invariant: we never leave < header_size bytes in a block.
assert(static_cast<int64_t>(kBlockSize - block_offset_) >= header_size_);
assert(static_cast<int64_t>(kBlockSize - block_offset_) >= header_size);
const size_t avail = kBlockSize - block_offset_ - header_size_;
const size_t avail = kBlockSize - block_offset_ - header_size;
// Compress the record if compression is enabled.
// Compress() is called at least once (compress_start=true) and after the
@@ -203,11 +186,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.");
}
@@ -225,7 +203,8 @@ IOStatus Writer::AddCompressionTypeRecord(const WriteOptions& write_options) {
}
}
// Initialize fields required for compression
const size_t max_output_buffer_len = kBlockSize - header_size_;
const size_t max_output_buffer_len =
kBlockSize - (recycle_log_files_ ? kRecyclableHeaderSize : kHeaderSize);
CompressionOptions opts;
constexpr uint32_t compression_format_version = 2;
compress_ = StreamingCompress::Create(compression_type_, opts,
@@ -265,25 +244,6 @@ IOStatus Writer::MaybeAddUserDefinedTimestampSizeRecord(
record.EncodeTo(&encoded);
RecordType type = recycle_log_files_ ? kRecyclableUserDefinedTimestampSizeType
: kUserDefinedTimestampSizeType;
// If there's not enough space for this record, switch to a new block.
const int64_t leftover = kBlockSize - block_offset_;
if (leftover < header_size_ + (int)encoded.size()) {
IOOptions opts;
IOStatus s = WritableFileWriter::PrepareIOOptions(write_options, opts);
if (!s.ok()) {
return s;
}
std::vector<char> trailer(leftover, '\x00');
s = dest_->Append(opts, Slice(trailer.data(), trailer.size()));
if (!s.ok()) {
return s;
}
block_offset_ = 0;
}
return EmitPhysicalRecord(write_options, type, encoded.data(),
encoded.size());
}
-8
View File
@@ -107,21 +107,13 @@ 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_; }
private:
std::unique_ptr<WritableFileWriter> dest_;
size_t block_offset_; // Current offset in block
uint64_t log_number_;
bool recycle_log_files_;
int header_size_;
// crc32c values for all supported record types. These are
// pre-computed to reduce the overhead of computing the crc of the
+38 -21
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(
@@ -930,10 +928,21 @@ struct Saver {
} // anonymous namespace
static bool SaveValue(void* arg, const char* entry) {
TEST_SYNC_POINT_CALLBACK("Memtable::SaveValue:Begin:entry", &entry);
Saver* s = static_cast<Saver*>(arg);
assert(s != nullptr);
assert(!s->value || !s->columns);
if (s->protection_bytes_per_key > 0) {
*(s->status) = MemTable::VerifyEntryChecksum(
entry, s->protection_bytes_per_key, s->allow_data_in_errors);
if (!s->status->ok()) {
ROCKS_LOG_ERROR(s->logger, "In SaveValue: %s", s->status->getState());
// Memtable entry corrupted
return false;
}
}
MergeContext* merge_context = s->merge_context;
SequenceNumber max_covering_tombstone_seq = s->max_covering_tombstone_seq;
const MergeOperator* merge_operator = s->merge_operator;
@@ -956,22 +965,6 @@ static bool SaveValue(void* arg, const char* entry) {
if (user_comparator->EqualWithoutTimestamp(user_key_slice,
s->key->user_key())) {
// Correct user key
TEST_SYNC_POINT_CALLBACK("Memtable::SaveValue:Found:entry", &entry);
std::optional<ReadLock> read_lock;
if (s->inplace_update_support) {
read_lock.emplace(s->mem->GetLock(s->key->user_key()));
}
if (s->protection_bytes_per_key > 0) {
*(s->status) = MemTable::VerifyEntryChecksum(
entry, s->protection_bytes_per_key, s->allow_data_in_errors);
if (!s->status->ok()) {
ROCKS_LOG_ERROR(s->logger, "In SaveValue: %s", s->status->getState());
// Memtable entry corrupted
return false;
}
}
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
ValueType type;
SequenceNumber seq;
@@ -1042,6 +1035,10 @@ static bool SaveValue(void* arg, const char* entry) {
return false;
}
if (s->inplace_update_support) {
s->mem->GetLock(s->key->user_key())->ReadLock();
}
Slice v = GetLengthPrefixedSlice(key_ptr + key_length);
*(s->status) = Status::OK();
@@ -1052,6 +1049,10 @@ static bool SaveValue(void* arg, const char* entry) {
s->columns->SetPlainValue(v);
}
if (s->inplace_update_support) {
s->mem->GetLock(s->key->user_key())->ReadUnlock();
}
*(s->found_final_value) = true;
*(s->is_blob_index) = true;
@@ -1059,6 +1060,10 @@ static bool SaveValue(void* arg, const char* entry) {
}
case kTypeValue:
case kTypeValuePreferredSeqno: {
if (s->inplace_update_support) {
s->mem->GetLock(s->key->user_key())->ReadLock();
}
Slice v = GetLengthPrefixedSlice(key_ptr + key_length);
if (type == kTypeValuePreferredSeqno) {
@@ -1095,6 +1100,10 @@ static bool SaveValue(void* arg, const char* entry) {
s->columns->SetPlainValue(v);
}
if (s->inplace_update_support) {
s->mem->GetLock(s->key->user_key())->ReadUnlock();
}
*(s->found_final_value) = true;
if (s->is_blob_index != nullptr) {
@@ -1104,6 +1113,10 @@ static bool SaveValue(void* arg, const char* entry) {
return false;
}
case kTypeWideColumnEntity: {
if (s->inplace_update_support) {
s->mem->GetLock(s->key->user_key())->ReadLock();
}
Slice v = GetLengthPrefixedSlice(key_ptr + key_length);
*(s->status) = Status::OK();
@@ -1145,6 +1158,10 @@ static bool SaveValue(void* arg, const char* entry) {
*(s->status) = s->columns->SetWideColumnValue(v);
}
if (s->inplace_update_support) {
s->mem->GetLock(s->key->user_key())->ReadUnlock();
}
*(s->found_final_value) = true;
if (s->is_blob_index != nullptr) {
@@ -1482,9 +1499,9 @@ Status MemTable::Update(SequenceNumber seq, ValueType value_type,
// Update value, if new value size <= previous value size
if (new_size <= prev_size) {
WriteLock wl(GetLock(lkey.user_key()));
char* p =
EncodeVarint32(const_cast<char*>(key_ptr) + key_length, new_size);
WriteLock wl(GetLock(lkey.user_key()));
memcpy(p, value.data(), value.size());
assert((unsigned)((p + value.size()) - entry) ==
(unsigned)(VarintLength(key_length) + key_length +
+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
+4 -4
View File
@@ -235,19 +235,19 @@ void MemTableListVersion::AddIterators(
SequenceNumber read_seq = options.snapshot != nullptr
? options.snapshot->GetSequenceNumber()
: kMaxSequenceNumber;
std::unique_ptr<TruncatedRangeDelIterator> mem_tombstone_iter;
TruncatedRangeDelIterator* mem_tombstone_iter = nullptr;
auto range_del_iter = m->NewRangeTombstoneIterator(
options, read_seq, true /* immutale_memtable */);
if (range_del_iter == nullptr || range_del_iter->empty()) {
delete range_del_iter;
} else {
mem_tombstone_iter = std::make_unique<TruncatedRangeDelIterator>(
mem_tombstone_iter = new TruncatedRangeDelIterator(
std::unique_ptr<FragmentedRangeTombstoneIterator>(range_del_iter),
&m->GetInternalKeyComparator(), nullptr /* smallest */,
nullptr /* largest */);
}
merge_iter_builder->AddPointAndTombstoneIterator(
mem_iter, std::move(mem_tombstone_iter));
merge_iter_builder->AddPointAndTombstoneIterator(mem_iter,
mem_tombstone_iter);
}
}
}
+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.
+2 -17
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,
@@ -230,7 +225,7 @@ InternalIterator* TableCache::NewIterator(
const InternalKey* smallest_compaction_key,
const InternalKey* largest_compaction_key, bool allow_unprepared_value,
uint8_t block_protection_bytes_per_key, const SequenceNumber* read_seqno,
std::unique_ptr<TruncatedRangeDelIterator>* range_del_iter) {
TruncatedRangeDelIterator** range_del_iter) {
PERF_TIMER_GUARD(new_table_iterator_nanos);
Status s;
@@ -285,7 +280,7 @@ InternalIterator* TableCache::NewIterator(
delete new_range_del_iter;
*range_del_iter = nullptr;
} else {
*range_del_iter = std::make_unique<TruncatedRangeDelIterator>(
*range_del_iter = new TruncatedRangeDelIterator(
std::unique_ptr<FragmentedRangeTombstoneIterator>(
new_range_del_iter),
&icomparator, &file_meta.smallest, &file_meta.largest);
@@ -732,14 +727,4 @@ uint64_t TableCache::ApproximateSize(
return result;
}
void TableCache::ReleaseObsolete(Cache* cache, Cache::Handle* h,
uint32_t uncache_aggressiveness) {
CacheInterface typed_cache(cache);
TypedHandle* table_handle = reinterpret_cast<TypedHandle*>(h);
TableReader* table_reader = typed_cache.Value(table_handle);
table_reader->MarkObsolete(uncache_aggressiveness);
typed_cache.ReleaseAndEraseIfLastRef(table_handle);
}
} // namespace ROCKSDB_NAMESPACE
+1 -9
View File
@@ -100,7 +100,7 @@ class TableCache {
const InternalKey* largest_compaction_key, bool allow_unprepared_value,
uint8_t protection_bytes_per_key,
const SequenceNumber* range_del_read_seqno = nullptr,
std::unique_ptr<TruncatedRangeDelIterator>* range_del_iter = nullptr);
TruncatedRangeDelIterator** range_del_iter = nullptr);
// If a seek to internal key "k" in specified file finds an entry,
// call get_context->SaveValue() repeatedly until
@@ -165,14 +165,6 @@ class TableCache {
// Evict any entry for the specified file number
static void Evict(Cache* cache, uint64_t file_number);
// Handles releasing, erasing, etc. of what should be the last reference
// to an obsolete file.
static void ReleaseObsolete(Cache* cache, Cache::Handle* handle,
uint32_t uncache_aggressiveness);
// Return handle to an existing cache entry if there is one
static Cache::Handle* Lookup(Cache* cache, uint64_t file_number);
// Find table reader
// @param skip_filters Disables loading/accessing the filter block
// @param level == -1 means not specified
+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
+1
View File
@@ -255,6 +255,7 @@ bool VersionEdit::EncodeTo(std::string* dst,
char p = static_cast<char>(0);
PutLengthPrefixedSlice(dst, Slice(&p, 1));
}
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",
dst);
+5 -39
View File
@@ -152,7 +152,7 @@ Status FileChecksumRetriever::ApplyVersionEdit(VersionEdit& edit,
VersionEditHandler::VersionEditHandler(
bool read_only, std::vector<ColumnFamilyDescriptor> column_families,
VersionSet* version_set, bool track_found_and_missing_files,
VersionSet* version_set, bool track_missing_files,
bool no_error_if_files_missing, const std::shared_ptr<IOTracer>& io_tracer,
const ReadOptions& read_options, bool skip_load_table_files,
EpochNumberRequirement epoch_number_requirement)
@@ -160,7 +160,7 @@ VersionEditHandler::VersionEditHandler(
read_only_(read_only),
column_families_(std::move(column_families)),
version_set_(version_set),
track_found_and_missing_files_(track_found_and_missing_files),
track_missing_files_(track_missing_files),
no_error_if_files_missing_(no_error_if_files_missing),
io_tracer_(io_tracer),
skip_load_table_files_(skip_load_table_files),
@@ -500,8 +500,7 @@ ColumnFamilyData* VersionEditHandler::CreateCfAndInit(
assert(builders_.find(cf_id) == builders_.end());
builders_.emplace(cf_id,
VersionBuilderUPtr(new BaseReferencedVersionBuilder(cfd)));
if (track_found_and_missing_files_) {
cf_to_found_files_.emplace(cf_id, std::unordered_set<uint64_t>());
if (track_missing_files_) {
cf_to_missing_files_.emplace(cf_id, std::unordered_set<uint64_t>());
cf_to_missing_blob_files_high_.emplace(cf_id, kInvalidBlobFileNumber);
}
@@ -514,11 +513,7 @@ ColumnFamilyData* VersionEditHandler::DestroyCfAndCleanup(
auto builder_iter = builders_.find(cf_id);
assert(builder_iter != builders_.end());
builders_.erase(builder_iter);
if (track_found_and_missing_files_) {
auto found_files_iter = cf_to_found_files_.find(cf_id);
assert(found_files_iter != cf_to_found_files_.end());
cf_to_found_files_.erase(found_files_iter);
if (track_missing_files_) {
auto missing_files_iter = cf_to_missing_files_.find(cf_id);
assert(missing_files_iter != cf_to_missing_files_.end());
cf_to_missing_files_.erase(missing_files_iter);
@@ -734,7 +729,7 @@ VersionEditHandlerPointInTime::VersionEditHandlerPointInTime(
const ReadOptions& read_options,
EpochNumberRequirement epoch_number_requirement)
: VersionEditHandler(read_only, column_families, version_set,
/*track_found_and_missing_files=*/true,
/*track_missing_files=*/true,
/*no_error_if_files_missing=*/true, io_tracer,
read_options, epoch_number_requirement) {}
@@ -829,12 +824,6 @@ void VersionEditHandlerPointInTime::CheckIterationResult(
version_set_->AppendVersion(cfd, v_iter->second);
versions_.erase(v_iter);
// Let's clear found_files, since any files in that are part of the
// installed Version. Any files that got obsoleted would have already
// been moved to intermediate_files_
auto found_files_iter = cf_to_found_files_.find(cfd->GetID());
assert(found_files_iter != cf_to_found_files_.end());
found_files_iter->second.clear();
}
}
} else {
@@ -865,16 +854,10 @@ ColumnFamilyData* VersionEditHandlerPointInTime::DestroyCfAndCleanup(
Status VersionEditHandlerPointInTime::MaybeCreateVersion(
const VersionEdit& edit, ColumnFamilyData* cfd, bool force_create_version) {
TEST_SYNC_POINT("VersionEditHandlerPointInTime::MaybeCreateVersion:Begin1");
TEST_SYNC_POINT("VersionEditHandlerPointInTime::MaybeCreateVersion:Begin2");
assert(cfd != nullptr);
if (!force_create_version) {
assert(edit.GetColumnFamily() == cfd->GetID());
}
auto found_files_iter = cf_to_found_files_.find(cfd->GetID());
assert(found_files_iter != cf_to_found_files_.end());
std::unordered_set<uint64_t>& found_files = found_files_iter->second;
auto missing_files_iter = cf_to_missing_files_.find(cfd->GetID());
assert(missing_files_iter != cf_to_missing_files_.end());
std::unordered_set<uint64_t>& missing_files = missing_files_iter->second;
@@ -906,18 +889,6 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
auto fiter = missing_files.find(file_num);
if (fiter != missing_files.end()) {
missing_files.erase(fiter);
} else {
fiter = found_files.find(file_num);
// Only mark new files added during this catchup attempt for deletion.
// These files were never installed in VersionStorageInfo.
// Already referenced files that are deleted by a VersionEdit will
// be added to the VersionStorageInfo's obsolete files when the old
// version is dereferenced.
if (fiter != found_files.end()) {
intermediate_files_.emplace_back(
MakeTableFileName(cfd->ioptions()->cf_paths[0].path, file_num));
found_files.erase(fiter);
}
}
}
@@ -933,14 +904,9 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
s = VerifyFile(cfd, fpath, level, meta);
if (s.IsPathNotFound() || s.IsNotFound() || s.IsCorruption()) {
missing_files.insert(file_num);
if (s.IsCorruption()) {
found_files.insert(file_num);
}
s = Status::OK();
} else if (!s.ok()) {
break;
} else {
found_files.insert(file_num);
}
}
+15 -24
View File
@@ -104,7 +104,7 @@ using VersionBuilderUPtr = std::unique_ptr<BaseReferencedVersionBuilder>;
// To use this class and its subclasses,
// 1. Create an object of VersionEditHandler or its subclasses.
// VersionEditHandler handler(read_only, column_families, version_set,
// track_found_and_missing_files,
// track_missing_files,
// no_error_if_files_missing);
// 2. Status s = handler.Iterate(reader, &db_id);
// 3. Check s and handle possible errors.
@@ -116,17 +116,16 @@ class VersionEditHandler : public VersionEditHandlerBase {
explicit VersionEditHandler(
bool read_only,
const std::vector<ColumnFamilyDescriptor>& column_families,
VersionSet* version_set, bool track_found_and_missing_files,
VersionSet* version_set, bool track_missing_files,
bool no_error_if_files_missing,
const std::shared_ptr<IOTracer>& io_tracer,
const ReadOptions& read_options,
EpochNumberRequirement epoch_number_requirement =
EpochNumberRequirement::kMustPresent)
: VersionEditHandler(read_only, column_families, version_set,
track_found_and_missing_files,
no_error_if_files_missing, io_tracer, read_options,
/*skip_load_table_files=*/false,
epoch_number_requirement) {}
: VersionEditHandler(
read_only, column_families, version_set, track_missing_files,
no_error_if_files_missing, io_tracer, read_options,
/*skip_load_table_files=*/false, epoch_number_requirement) {}
~VersionEditHandler() override {}
@@ -145,7 +144,7 @@ class VersionEditHandler : public VersionEditHandlerBase {
protected:
explicit VersionEditHandler(
bool read_only, std::vector<ColumnFamilyDescriptor> column_families,
VersionSet* version_set, bool track_found_and_missing_files,
VersionSet* version_set, bool track_missing_files,
bool no_error_if_files_missing,
const std::shared_ptr<IOTracer>& io_tracer,
const ReadOptions& read_options, bool skip_load_table_files,
@@ -196,8 +195,7 @@ class VersionEditHandler : public VersionEditHandlerBase {
// by subsequent manifest records, Recover() will return failure status.
std::unordered_map<uint32_t, std::string> column_families_not_found_;
VersionEditParams version_edit_params_;
const bool track_found_and_missing_files_;
std::unordered_map<uint32_t, std::unordered_set<uint64_t>> cf_to_found_files_;
const bool track_missing_files_;
std::unordered_map<uint32_t, std::unordered_set<uint64_t>>
cf_to_missing_files_;
std::unordered_map<uint32_t, uint64_t> cf_to_missing_blob_files_high_;
@@ -275,8 +273,6 @@ class VersionEditHandlerPointInTime : public VersionEditHandler {
bool in_atomic_group_ = false;
std::vector<std::string> intermediate_files_;
private:
bool AtomicUpdateVersionsCompleted();
bool AtomicUpdateVersionsContains(uint32_t cfid);
@@ -314,10 +310,6 @@ class ManifestTailer : public VersionEditHandlerPointInTime {
return cfds_changed_;
}
std::vector<std::string>& GetIntermediateFiles() {
return intermediate_files_;
}
protected:
Status Initialize() override;
@@ -350,7 +342,7 @@ class DumpManifestHandler : public VersionEditHandler {
bool json)
: VersionEditHandler(
/*read_only=*/true, column_families, version_set,
/*track_found_and_missing_files=*/false,
/*track_missing_files=*/false,
/*no_error_if_files_missing=*/false, io_tracer, read_options,
/*skip_load_table_files=*/true),
verbose_(verbose),
@@ -364,15 +356,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);
Loaded 100 of 271 files, more files were not shown because too many files have changed in this diff. Show more