mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd8247b1be | ||
|
|
fe352574b4 | ||
|
|
6fbe42a6fd | ||
|
|
e6c47cd6cc | ||
|
|
645c445978 | ||
|
|
f952de5be2 | ||
|
|
4362985805 | ||
|
|
85cdad116b | ||
|
|
07e3794972 | ||
|
|
1624f20934 | ||
|
|
1e96a70be4 | ||
|
|
efe9d5c823 | ||
|
|
a265ac75ab | ||
|
|
9435b2e959 | ||
|
|
0f8c041ea7 | ||
|
|
9da6019f9d | ||
|
|
d5e2462946 | ||
|
|
5b3ebdc3d1 | ||
|
|
7457c7cd00 | ||
|
|
3bfd3ed2f3 | ||
|
|
c8b0842bcd | ||
|
|
d9be6556aa | ||
|
|
c706324208 | ||
|
|
dd0447ae2c | ||
|
|
7ee41a5d25 | ||
|
|
576cff11da | ||
|
|
063a68b9cd | ||
|
|
c810947184 | ||
|
|
e7a60d01b2 | ||
|
|
08ec5e7321 | ||
|
|
27d57a035e | ||
|
|
c603f2f898 | ||
|
|
326670d265 | ||
|
|
8d9088464b | ||
|
|
1a343bc393 | ||
|
|
0304352882 | ||
|
|
85d4f2c8b3 | ||
|
|
3dff28cf9b | ||
|
|
b8f40f7f7b | ||
|
|
b708b166dc | ||
|
|
01c2ec3fcb | ||
|
|
119dda2195 | ||
|
|
589ea6bec2 | ||
|
|
82b3888433 | ||
|
|
64517d184a | ||
|
|
7a3444bf1f | ||
|
|
cc34da75b5 | ||
|
|
6fad38ebe8 | ||
|
|
670567db09 | ||
|
|
7381dad1b1 | ||
|
|
4b18c46d10 | ||
|
|
847ca9f964 | ||
|
|
0028e3398b | ||
|
|
430842f948 | ||
|
|
8643d63bb4 | ||
|
|
67d72fb5dc | ||
|
|
d2e9eab1ea | ||
|
|
02974c9437 | ||
|
|
ef392fb04e | ||
|
|
0ff0b625a1 | ||
|
|
ce391ff84b | ||
|
|
cb25bc1128 | ||
|
|
4126bdc0e1 | ||
|
|
72d1e258cd | ||
|
|
a9046f3c45 | ||
|
|
a46f080cce | ||
|
|
f19612970d | ||
|
|
1f11d07f24 | ||
|
|
9fdc9fbeea | ||
|
|
a8b3b9a20c | ||
|
|
c370d8aa12 | ||
|
|
cef4a6c49f | ||
|
|
2772eb7735 | ||
|
|
b085ee13e0 | ||
|
|
cd79a00903 | ||
|
|
e017af15c1 | ||
|
|
75c6ffb9de | ||
|
|
f91fd0c944 | ||
|
|
46cf5fbfdd | ||
|
|
daca92c17a | ||
|
|
59d91796d2 | ||
|
|
6790a983eb |
No files matched your search
+9
-9
@@ -196,30 +196,30 @@ script:
|
||||
- if [ `command -v ccache` ]; then ccache -C; fi
|
||||
- case $TEST_GROUP in
|
||||
platform_dependent)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=only make -j4 all_but_some_tests check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=only make -j4 all_but_some_tests check_some
|
||||
;;
|
||||
1)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_END=backupable_db_test make -j4 check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_END=backupable_db_test make -j4 check_some
|
||||
;;
|
||||
2)
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" V=1 make -j4 tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=backupable_db_test ROCKSDBTESTS_END=db_universal_compaction_test make -j4 check_some
|
||||
OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 make -j4 tools && OPT="-DTRAVIS -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=backupable_db_test ROCKSDBTESTS_END=db_universal_compaction_test make -j4 check_some
|
||||
;;
|
||||
3)
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=db_universal_compaction_test ROCKSDBTESTS_END=table_properties_collector_test make -j4 check_some
|
||||
;;
|
||||
4)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=table_properties_collector_test make -j4 check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ROCKSDBTESTS_PLATFORM_DEPENDENT=exclude ROCKSDBTESTS_START=table_properties_collector_test make -j4 check_some
|
||||
;;
|
||||
esac
|
||||
- case $JOB_NAME in
|
||||
java_test)
|
||||
OPT=-DTRAVIS V=1 make rocksdbjava jtest
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 all
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' LIB_MODE=shared V=1 make -j4 all
|
||||
;;
|
||||
examples)
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 make -j4 static_lib && cd examples && make -j4
|
||||
;;
|
||||
cmake-mingw)
|
||||
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix;
|
||||
@@ -235,10 +235,10 @@ script:
|
||||
mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=0 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 .. && make -j4 && cd .. && rm -rf build && mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
make-gcc4.8)
|
||||
OPT=-DTRAVIS V=1 SKIP_LINK=1 make -j4 all && [ "Linking broken because libgflags compiled with newer ABI" ]
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 SKIP_LINK=1 make -j4 all && [ "Linking broken because libgflags compiled with newer ABI" ]
|
||||
;;
|
||||
status_checked)
|
||||
OPT=-DTRAVIS V=1 ASSERT_STATUS_CHECKED=1 make -j4 check_some
|
||||
OPT=-DTRAVIS LIB_MODE=shared V=1 ASSERT_STATUS_CHECKED=1 make -j4 check_some
|
||||
;;
|
||||
esac
|
||||
notifications:
|
||||
|
||||
+29
-17
@@ -88,10 +88,9 @@ if( NOT DEFINED CMAKE_CXX_STANDARD )
|
||||
endif()
|
||||
|
||||
include(CMakeDependentOption)
|
||||
CMAKE_DEPENDENT_OPTION(WITH_GFLAGS "build with GFlags" ON
|
||||
"NOT MSVC;NOT MINGW" OFF)
|
||||
|
||||
if(MSVC)
|
||||
option(WITH_GFLAGS "build with GFlags" OFF)
|
||||
option(WITH_XPRESS "build with windows built in compression" OFF)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
@@ -107,6 +106,11 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
option(WITH_GFLAGS "build with GFlags" OFF)
|
||||
else()
|
||||
option(WITH_GFLAGS "build with GFlags" ON)
|
||||
endif()
|
||||
set(GFLAGS_LIB)
|
||||
if(WITH_GFLAGS)
|
||||
# Config with namespace available since gflags 2.2.2
|
||||
@@ -208,6 +212,9 @@ if(MSVC)
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers -Wno-strict-aliasing")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
|
||||
endif()
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format -fno-asynchronous-unwind-tables")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
@@ -637,6 +644,7 @@ set(SOURCES
|
||||
db/write_batch_base.cc
|
||||
db/write_controller.cc
|
||||
db/write_thread.cc
|
||||
env/composite_env.cc
|
||||
env/env.cc
|
||||
env/env_chroot.cc
|
||||
env/env_encryption.cc
|
||||
@@ -648,6 +656,7 @@ set(SOURCES
|
||||
file/file_prefetch_buffer.cc
|
||||
file/file_util.cc
|
||||
file/filename.cc
|
||||
file/line_file_reader.cc
|
||||
file/random_access_file_reader.cc
|
||||
file/read_write_util.cc
|
||||
file/readahead_raf.cc
|
||||
@@ -761,6 +770,7 @@ set(SOURCES
|
||||
util/murmurhash.cc
|
||||
util/random.cc
|
||||
util/rate_limiter.cc
|
||||
util/ribbon_config.cc
|
||||
util/slice.cc
|
||||
util/file_checksum_helper.cc
|
||||
util/status.cc
|
||||
@@ -1060,6 +1070,8 @@ if(WITH_TESTS)
|
||||
db/blob/blob_file_garbage_test.cc
|
||||
db/blob/blob_file_reader_test.cc
|
||||
db/blob/db_blob_basic_test.cc
|
||||
db/blob/db_blob_compaction_test.cc
|
||||
db/blob/db_blob_corruption_test.cc
|
||||
db/blob/db_blob_index_test.cc
|
||||
db/column_family_test.cc
|
||||
db/compact_files_test.cc
|
||||
@@ -1090,7 +1102,7 @@ if(WITH_TESTS)
|
||||
db/db_options_test.cc
|
||||
db/db_properties_test.cc
|
||||
db/db_range_del_test.cc
|
||||
db/db_impl/db_secondary_test.cc
|
||||
db/db_secondary_test.cc
|
||||
db/db_sst_test.cc
|
||||
db/db_statistics_test.cc
|
||||
db/db_table_properties_test.cc
|
||||
@@ -1296,40 +1308,40 @@ if(WITH_TESTS)
|
||||
endif()
|
||||
|
||||
if(WITH_BENCHMARK_TOOLS)
|
||||
add_executable(db_bench
|
||||
add_executable(db_bench${ARTIFACT_SUFFIX}
|
||||
tools/db_bench.cc
|
||||
tools/db_bench_tool.cc)
|
||||
target_link_libraries(db_bench
|
||||
target_link_libraries(db_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${THIRDPARTY_LIBS})
|
||||
|
||||
add_executable(cache_bench
|
||||
add_executable(cache_bench${ARTIFACT_SUFFIX}
|
||||
cache/cache_bench.cc)
|
||||
target_link_libraries(cache_bench
|
||||
target_link_libraries(cache_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
|
||||
add_executable(memtablerep_bench
|
||||
add_executable(memtablerep_bench${ARTIFACT_SUFFIX}
|
||||
memtable/memtablerep_bench.cc)
|
||||
target_link_libraries(memtablerep_bench
|
||||
target_link_libraries(memtablerep_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
|
||||
add_executable(range_del_aggregator_bench
|
||||
add_executable(range_del_aggregator_bench${ARTIFACT_SUFFIX}
|
||||
db/range_del_aggregator_bench.cc)
|
||||
target_link_libraries(range_del_aggregator_bench
|
||||
target_link_libraries(range_del_aggregator_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
|
||||
add_executable(table_reader_bench
|
||||
add_executable(table_reader_bench${ARTIFACT_SUFFIX}
|
||||
table/table_reader_bench.cc)
|
||||
target_link_libraries(table_reader_bench
|
||||
target_link_libraries(table_reader_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} testharness ${GFLAGS_LIB})
|
||||
|
||||
add_executable(filter_bench
|
||||
add_executable(filter_bench${ARTIFACT_SUFFIX}
|
||||
util/filter_bench.cc)
|
||||
target_link_libraries(filter_bench
|
||||
target_link_libraries(filter_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
|
||||
add_executable(hash_table_bench
|
||||
add_executable(hash_table_bench${ARTIFACT_SUFFIX}
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
target_link_libraries(hash_table_bench
|
||||
target_link_libraries(hash_table_bench${ARTIFACT_SUFFIX}
|
||||
${ROCKSDB_LIB} ${GFLAGS_LIB})
|
||||
endif()
|
||||
|
||||
|
||||
+50
-1
@@ -1,5 +1,52 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 6.19.4 (04/23/2021)
|
||||
### Bug Fixes
|
||||
* Fixed a bug where ingested files were written with incorrect boundary key metadata. In rare cases this could have led to a level's files being wrongly ordered and queries for the boundary keys returning wrong results.
|
||||
* Fixed the false-positive alert when recovering from the WAL file. Avoid reporting "SST file is ahead of WAL" on a newly created empty column family, if the previous WAL file is corrupted.
|
||||
|
||||
### Behavior Changes
|
||||
* Due to the fix of false-postive alert of "SST file is ahead of WAL", all the CFs with no SST file (CF empty) will bypass the consistency check. We fixed a false-positive, but introduced a very rare true-negative which will be triggered in the following conditions: A CF with some delete operations in the last a few queries which will result in an empty CF (those are flushed to SST file and a compaction triggered which combines this file and all other SST files and generates an empty CF, or there is another reason to write a manifest entry for this CF after a flush that generates no SST file from an empty CF). The deletion entries are logged in a WAL and this WAL was corrupted, while the CF's log number points to the next WAL (due to the flush). Therefore, the DB can only recover to the point without these trailing deletions and cause the inconsistent DB status.
|
||||
|
||||
## 6.19.3 (04/19/2021)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in handling file rename error in distributed/network file systems when the server succeeds but client returns error. The bug can cause CURRENT file to point to non-existing MANIFEST file, thus DB cannot be opened.
|
||||
|
||||
## 6.19.2 (04/08/2021)
|
||||
### Bug Fixes
|
||||
* Fixed a backward iteration bug with partitioned filter enabled: not including the prefix of the last key of the previous filter partition in current filter partition can cause wrong iteration result.
|
||||
|
||||
## 6.19.1 (04/01/2021)
|
||||
### Bug Fixes
|
||||
* Fixed crash (divide by zero) when compression dictionary is applied to a file containing only range tombstones.
|
||||
|
||||
## 6.19.0 (03/21/2021)
|
||||
### Bug Fixes
|
||||
* Fixed the truncation error found in APIs/tools when dumping block-based SST files in a human-readable format. After fix, the block-based table can be fully dumped as a readable file.
|
||||
* When hitting a write slowdown condition, no write delay (previously 1 millisecond) is imposed until `delayed_write_rate` is actually exceeded, with an initial burst allowance of 1 millisecond worth of bytes. Also, beyond the initial burst allowance, `delayed_write_rate` is now more strictly enforced, especially with multiple column families.
|
||||
|
||||
### Public API change
|
||||
* Changed default `BackupableDBOptions::share_files_with_checksum` to `true` and deprecated `false` because of potential for data loss. Note that accepting this change in behavior can temporarily increase backup data usage because files are not shared between backups using the two different settings. Also removed obsolete option kFlagMatchInterimNaming.
|
||||
* Add a new option BlockBasedTableOptions::max_auto_readahead_size. RocksDB does auto-readahead for iterators on noticing more than two reads for a table file if user doesn't provide readahead_size. The readahead starts at 8KB and doubles on every additional read upto max_auto_readahead_size and now max_auto_readahead_size can be configured dynamically as well. Found that 256 KB readahead size provides the best performance, based on experiments, for auto readahead. Experiment data is in PR #3282. If value is set 0 then no automatic prefetching will be done by rocksdb. Also changing the value will only affect files opened after the change.
|
||||
* Add suppport to extend DB::VerifyFileChecksums API to also verify blob files checksum.
|
||||
* When using the new BlobDB, the amount of data written by flushes/compactions is now broken down into table files and blob files in the compaction statistics; namely, Write(GB) denotes the amount of data written to table files, while Wblob(GB) means the amount of data written to blob files.
|
||||
* New default BlockBasedTableOptions::format_version=5 to enable new Bloom filter implementation by default, compatible with RocksDB versions >= 6.6.0.
|
||||
* Add new SetBufferSize API to WriteBufferManager to allow dynamic management of memory allotted to all write buffers. This allows user code to adjust memory monitoring provided by WriteBufferManager as process memory needs change datasets grow and shrink.
|
||||
* Clarified the required semantics of Read() functions in FileSystem and Env APIs. Please ensure any custom implementations are compliant.
|
||||
* For the new integrated BlobDB implementation, compaction statistics now include the amount of data read from blob files during compaction (due to garbage collection or compaction filters). Write amplification metrics have also been extended to account for data read from blob files.
|
||||
* Add EqualWithoutTimestamp() to Comparator.
|
||||
* Extend support to track blob files in SSTFileManager whenever a blob file is created/deleted. Blob files will be scheduled to delete via SSTFileManager and SStFileManager will now take blob files in account while calculating size and space limits along with SST files.
|
||||
* Add new Append and PositionedAppend API with checksum handoff to legacy Env.
|
||||
|
||||
### New Features
|
||||
* Support compaction filters for the new implementation of BlobDB. Add `FilterBlobByKey()` to `CompactionFilter`. Subclasses can override this method so that compaction filters can determine whether the actual blob value has to be read during compaction. Use a new `kUndetermined` in `CompactionFilter::Decision` to indicated that further action is necessary for compaction filter to make a decision.
|
||||
* Add support to extend retrieval of checksums for blob files from the MANIFEST when checkpointing. During backup, rocksdb can detect corruption in blob files during file copies.
|
||||
* Add new options for db_bench --benchmarks: flush, waitforcompaction, compact0, compact1.
|
||||
* Add an option to BackupEngine::GetBackupInfo to include the name and size of each backed-up file. Especially in the presence of file sharing among backups, this offers detailed insight into backup space usage.
|
||||
* Enable backward iteration on keys with user-defined timestamps.
|
||||
* Add statistics and info log for error handler: counters for bg error, bg io error, bg retryable io error, auto resume count, auto resume total retry number, and auto resume sucess; Histogram for auto resume retry count in each recovery call. Note that, each auto resume attempt will have one or multiple retries.
|
||||
|
||||
### Behavior Changes
|
||||
* During flush, only WAL sync retryable IO error is mapped to hard error, which will stall the writes. When WAL is used but only SST file write has retryable IO error, it will be mapped to soft error and write will not be affected.
|
||||
|
||||
## 6.18.0 (02/19/2021)
|
||||
### Behavior Changes
|
||||
@@ -24,6 +71,7 @@
|
||||
* Add new Append and PositionedAppend APIs to FileSystem to bring the data verification information (data checksum information) from upper layer (e.g., WritableFileWriter) to the storage layer. In this way, the customized FileSystem is able to verify the correctness of data being written to the storage on time. Add checksum_handoff_file_types to DBOptions. User can use this option to control which file types (Currently supported file tyes: kWALFile, kTableFile, kDescriptorFile.) should use the new Append and PositionedAppend APIs to handoff the verification information. Currently, RocksDB only use crc32c to calculate the checksum for write handoff.
|
||||
* Add an option, `CompressionOptions::max_dict_buffer_bytes`, to limit the in-memory buffering for selecting samples for generating/training a dictionary. The limit is currently loosely adhered to.
|
||||
|
||||
|
||||
## 6.17.0 (01/15/2021)
|
||||
### Behavior Changes
|
||||
* When verifying full file checksum with `DB::VerifyFileChecksums()`, we now fail with `Status::InvalidArgument` if the name of the checksum generator used for verification does not match the name of the checksum generator used for protecting the file when it was created.
|
||||
@@ -35,6 +83,7 @@
|
||||
|
||||
### Public API Change
|
||||
* Add a public API WriteBufferManager::dummy_entries_in_cache_usage() which reports the size of dummy entries stored in cache (passed to WriteBufferManager). Dummy entries are used to account for DataBlocks.
|
||||
* Add a SystemClock class that contains the time-related methods from Env. The original methods in Env may be deprecated in a future release. This class will allow easier testing, development, and expansion of time-related features.
|
||||
* Add a public API GetRocksBuildProperties and GetRocksBuildInfoAsString to get properties about the current build. These properties may include settings related to the GIT settings (branch, timestamp). This change also sets the "build date" based on the GIT properties, rather than the actual build time, thereby enabling more reproducible builds.
|
||||
|
||||
## 6.16.0 (12/18/2020)
|
||||
|
||||
@@ -436,6 +436,10 @@ default: all
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wunused-parameter
|
||||
|
||||
ifeq (,$(filter amd64, $(MACHINE)))
|
||||
C_WARNING_FLAGS = -Wstrict-prototypes
|
||||
endif
|
||||
|
||||
ifdef USE_CLANG
|
||||
# Used by some teams in Facebook
|
||||
WARNING_FLAGS += -Wshift-sign-overflow
|
||||
@@ -480,7 +484,7 @@ ifeq ($(NO_THREEWAY_CRC32C), 1)
|
||||
CXXFLAGS += -DNO_THREEWAY_CRC32C
|
||||
endif
|
||||
|
||||
CFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CFLAGS += $(C_WARNING_FLAGS) $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers
|
||||
|
||||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
@@ -598,6 +602,8 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
db_log_iter_test \
|
||||
db_bloom_filter_test \
|
||||
db_blob_basic_test \
|
||||
db_blob_compaction_test \
|
||||
db_blob_corruption_test \
|
||||
db_blob_index_test \
|
||||
db_block_cache_test \
|
||||
db_compaction_test \
|
||||
@@ -761,7 +767,6 @@ endif
|
||||
TESTS_PLATFORM_DEPENDENT := \
|
||||
db_basic_test \
|
||||
db_blob_basic_test \
|
||||
db_with_timestamp_basic_test \
|
||||
db_encryption_test \
|
||||
db_test2 \
|
||||
external_sst_file_basic_test \
|
||||
@@ -1046,7 +1051,7 @@ gen_parallel_tests:
|
||||
# 107.816 PASS t/DBTest.EncodeDecompressedBlockSizeTest
|
||||
#
|
||||
slow_test_regexp = \
|
||||
^.*SnapshotConcurrentAccessTest.*$$|^t/run-table_test-HarnessTest.Randomized$$|^t/run-db_test-.*(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$|^.*RecoverFromCorruptedWALWithoutFlush$$
|
||||
^.*SnapshotConcurrentAccessTest.*$$|^.*SeqAdvanceConcurrentTest.*$$|^t/run-table_test-HarnessTest.Randomized$$|^t/run-db_test-.*(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$|^.*RecoverFromCorruptedWALWithoutFlush$$
|
||||
prioritize_long_running_tests = \
|
||||
perl -pe 's,($(slow_test_regexp)),100 $$1,' \
|
||||
| sort -k1,1gr \
|
||||
@@ -1574,6 +1579,9 @@ db_basic_test: $(OBJ_DIR)/db/db_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
db_blob_basic_test: $(OBJ_DIR)/db/blob/db_blob_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_blob_compaction_test: $(OBJ_DIR)/db/blob/db_blob_compaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_with_timestamp_basic_test: $(OBJ_DIR)/db/db_with_timestamp_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2017,7 +2025,7 @@ range_tombstone_fragmenter_test: $(OBJ_DIR)/db/range_tombstone_fragmenter_test.o
|
||||
sst_file_reader_test: $(OBJ_DIR)/table/sst_file_reader_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_secondary_test: $(OBJ_DIR)/db/db_impl/db_secondary_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
db_secondary_test: $(OBJ_DIR)/db/db_secondary_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_tracer_test: $(OBJ_DIR)/trace_replay/block_cache_tracer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -2065,6 +2073,8 @@ io_tracer_parser_test: $(OBJ_DIR)/tools/io_tracer_parser_test.o $(OBJ_DIR)/tools
|
||||
io_tracer_parser: $(OBJ_DIR)/tools/io_tracer_parser.o $(TOOLS_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_blob_corruption_test: $(OBJ_DIR)/db/blob/db_blob_corruption_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
PREFIX ?= /usr/local
|
||||
|
||||
@@ -87,10 +87,12 @@ ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
|
||||
# Added missing flags from output of build_detect_platform
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
]
|
||||
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
# Directories with files for #include
|
||||
ROCKSDB_INCLUDE_PATHS = [
|
||||
"",
|
||||
"include",
|
||||
]
|
||||
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
@@ -207,6 +209,7 @@ cpp_library(
|
||||
"db/write_batch_base.cc",
|
||||
"db/write_controller.cc",
|
||||
"db/write_thread.cc",
|
||||
"env/composite_env.cc",
|
||||
"env/env.cc",
|
||||
"env/env_chroot.cc",
|
||||
"env/env_encryption.cc",
|
||||
@@ -221,6 +224,7 @@ cpp_library(
|
||||
"file/file_prefetch_buffer.cc",
|
||||
"file/file_util.cc",
|
||||
"file/filename.cc",
|
||||
"file/line_file_reader.cc",
|
||||
"file/random_access_file_reader.cc",
|
||||
"file/read_write_util.cc",
|
||||
"file/readahead_raf.cc",
|
||||
@@ -341,6 +345,7 @@ cpp_library(
|
||||
"util/murmurhash.cc",
|
||||
"util/random.cc",
|
||||
"util/rate_limiter.cc",
|
||||
"util/ribbon_config.cc",
|
||||
"util/slice.cc",
|
||||
"util/status.cc",
|
||||
"util/string_util.cc",
|
||||
@@ -425,6 +430,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
@@ -511,6 +517,7 @@ cpp_library(
|
||||
"db/write_batch_base.cc",
|
||||
"db/write_controller.cc",
|
||||
"db/write_thread.cc",
|
||||
"env/composite_env.cc",
|
||||
"env/env.cc",
|
||||
"env/env_chroot.cc",
|
||||
"env/env_encryption.cc",
|
||||
@@ -525,6 +532,7 @@ cpp_library(
|
||||
"file/file_prefetch_buffer.cc",
|
||||
"file/file_util.cc",
|
||||
"file/filename.cc",
|
||||
"file/line_file_reader.cc",
|
||||
"file/random_access_file_reader.cc",
|
||||
"file/read_write_util.cc",
|
||||
"file/readahead_raf.cc",
|
||||
@@ -645,6 +653,7 @@ cpp_library(
|
||||
"util/murmurhash.cc",
|
||||
"util/random.cc",
|
||||
"util/rate_limiter.cc",
|
||||
"util/ribbon_config.cc",
|
||||
"util/slice.cc",
|
||||
"util/status.cc",
|
||||
"util/string_util.cc",
|
||||
@@ -729,6 +738,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = True,
|
||||
@@ -752,6 +762,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
@@ -773,6 +784,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
@@ -800,6 +812,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
@@ -811,6 +824,7 @@ cpp_binary(
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
) if not is_opt_mode else None
|
||||
|
||||
@@ -832,6 +846,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
@@ -1147,6 +1162,20 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_blob_compaction_test",
|
||||
"db/blob/db_blob_compaction_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_blob_corruption_test",
|
||||
"db/blob/db_blob_corruption_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_blob_index_test",
|
||||
"db/blob/db_blob_index_test.cc",
|
||||
@@ -1303,7 +1332,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"db_secondary_test",
|
||||
"db/db_impl/db_secondary_test.cc",
|
||||
"db/db_secondary_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
@@ -2098,6 +2127,7 @@ ROCKS_TESTS = [
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS + extra_compiler_flags,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
|
||||
@@ -87,6 +87,7 @@ cpp_binary(
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
) if not is_opt_mode else None
|
||||
|
||||
|
||||
@@ -94,10 +94,12 @@ ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
|
||||
# Added missing flags from output of build_detect_platform
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
]
|
||||
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
# Directories with files for #include
|
||||
ROCKSDB_INCLUDE_PATHS = [
|
||||
"",
|
||||
"include",
|
||||
]
|
||||
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {{
|
||||
@@ -145,6 +147,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [{deps}],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS{extra_external_deps},
|
||||
link_whole = {link_whole},
|
||||
@@ -161,6 +164,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
@@ -173,6 +177,7 @@ cpp_binary(
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [{deps}],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
@@ -203,6 +208,7 @@ ROCKS_TESTS = [
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS + extra_compiler_flags,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
|
||||
@@ -68,7 +68,7 @@ $BinariesFolder = -Join($RootFolder, "\build\Debug\")
|
||||
|
||||
if($WorkFolder -eq "") {
|
||||
|
||||
# If TEST_TMPDIR is set use it
|
||||
# If TEST_TMPDIR is set use it
|
||||
[string]$var = $Env:TEST_TMPDIR
|
||||
if($var -eq "") {
|
||||
$WorkFolder = -Join($RootFolder, "\db_tests\")
|
||||
@@ -93,7 +93,7 @@ $ExcludeCasesSet = New-Object System.Collections.Generic.HashSet[string]
|
||||
if($ExcludeCases -ne "") {
|
||||
Write-Host "ExcludeCases: $ExcludeCases"
|
||||
$l = $ExcludeCases -split ' '
|
||||
ForEach($t in $l) {
|
||||
ForEach($t in $l) {
|
||||
$ExcludeCasesSet.Add($t) | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ $ExcludeExesSet = New-Object System.Collections.Generic.HashSet[string]
|
||||
if($ExcludeExes -ne "") {
|
||||
Write-Host "ExcludeExe: $ExcludeExes"
|
||||
$l = $ExcludeExes -split ' '
|
||||
ForEach($t in $l) {
|
||||
ForEach($t in $l) {
|
||||
$ExcludeExesSet.Add($t) | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,10 @@ if($ExcludeExes -ne "") {
|
||||
# MultiThreaded/MultiThreadedDBTest.
|
||||
# MultiThreaded/0 # GetParam() = 0
|
||||
# MultiThreaded/1 # GetParam() = 1
|
||||
# RibbonTypeParamTest/0. # TypeParam = struct DefaultTypesAndSettings
|
||||
# CompactnessAndBacktrackAndFpRate
|
||||
# Extremes
|
||||
# FindOccupancyForSuccessRate
|
||||
#
|
||||
# into this:
|
||||
#
|
||||
@@ -125,6 +129,9 @@ if($ExcludeExes -ne "") {
|
||||
# DBTest.WriteEmptyBatch
|
||||
# MultiThreaded/MultiThreadedDBTest.MultiThreaded/0
|
||||
# MultiThreaded/MultiThreadedDBTest.MultiThreaded/1
|
||||
# RibbonTypeParamTest/0.CompactnessAndBacktrackAndFpRate
|
||||
# RibbonTypeParamTest/0.Extremes
|
||||
# RibbonTypeParamTest/0.FindOccupancyForSuccessRate
|
||||
#
|
||||
# Output into the parameter in a form TestName -> Log File Name
|
||||
function ExtractTestCases([string]$GTestExe, $HashTable) {
|
||||
@@ -138,6 +145,8 @@ function ExtractTestCases([string]$GTestExe, $HashTable) {
|
||||
|
||||
ForEach( $l in $Tests) {
|
||||
|
||||
# remove trailing comment if any
|
||||
$l = $l -replace '\s+\#.*',''
|
||||
# Leading whitespace is fine
|
||||
$l = $l -replace '^\s+',''
|
||||
# Trailing dot is a test group but no whitespace
|
||||
@@ -146,8 +155,7 @@ function ExtractTestCases([string]$GTestExe, $HashTable) {
|
||||
} else {
|
||||
# Otherwise it is a test name, remove leading space
|
||||
$test = $l
|
||||
# remove trailing comment if any and create a log name
|
||||
$test = $test -replace '\s+\#.*',''
|
||||
# create a log name
|
||||
$test = "$Group$test"
|
||||
|
||||
if($ExcludeCasesSet.Contains($test)) {
|
||||
@@ -253,7 +261,7 @@ if($Run -ne "") {
|
||||
|
||||
$DiscoveredExe = @()
|
||||
dir -Path $search_path | ForEach-Object {
|
||||
$DiscoveredExe += ($_.Name)
|
||||
$DiscoveredExe += ($_.Name)
|
||||
}
|
||||
|
||||
# Remove exclusions
|
||||
@@ -293,7 +301,7 @@ if($SuiteRun -ne "") {
|
||||
|
||||
$ListOfExe = @()
|
||||
dir -Path $search_path | ForEach-Object {
|
||||
$ListOfExe += ($_.Name)
|
||||
$ListOfExe += ($_.Name)
|
||||
}
|
||||
|
||||
# Exclude those in RunOnly from running as suites
|
||||
@@ -348,7 +356,7 @@ function RunJobs($Suites, $TestCmds, [int]$ConcurrencyVal)
|
||||
|
||||
# Wait for all to finish and get the results
|
||||
while(($JobToLog.Count -gt 0) -or
|
||||
($TestCmds.Count -gt 0) -or
|
||||
($TestCmds.Count -gt 0) -or
|
||||
($Suites.Count -gt 0)) {
|
||||
|
||||
# Make sure we have maximum concurrent jobs running if anything
|
||||
@@ -468,8 +476,8 @@ RunJobs -Suites $CasesToRun -TestCmds $TestExes -ConcurrencyVal $Concurrency
|
||||
|
||||
$EndDate = (Get-Date)
|
||||
|
||||
New-TimeSpan -Start $StartDate -End $EndDate |
|
||||
ForEach-Object {
|
||||
New-TimeSpan -Start $StartDate -End $EndDate |
|
||||
ForEach-Object {
|
||||
"Elapsed time: {0:g}" -f $_
|
||||
}
|
||||
|
||||
@@ -484,4 +492,4 @@ if(!$script:success) {
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
|
||||
Vendored
+6
-3
@@ -11,15 +11,17 @@ int main() {
|
||||
}
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/hash.h"
|
||||
@@ -210,6 +212,7 @@ class CacheBench {
|
||||
|
||||
bool Run() {
|
||||
ROCKSDB_NAMESPACE::Env* env = ROCKSDB_NAMESPACE::Env::Default();
|
||||
const auto& clock = env->GetSystemClock();
|
||||
|
||||
PrintEnv();
|
||||
SharedState shared(this);
|
||||
@@ -224,7 +227,7 @@ class CacheBench {
|
||||
shared.GetCondVar()->Wait();
|
||||
}
|
||||
// Record start time
|
||||
uint64_t start_time = env->NowMicros();
|
||||
uint64_t start_time = clock->NowMicros();
|
||||
|
||||
// Start all threads
|
||||
shared.SetStart();
|
||||
@@ -236,7 +239,7 @@ class CacheBench {
|
||||
}
|
||||
|
||||
// Record end time
|
||||
uint64_t end_time = env->NowMicros();
|
||||
uint64_t end_time = clock->NowMicros();
|
||||
double elapsed = static_cast<double>(end_time - start_time) * 1e-6;
|
||||
uint32_t qps = static_cast<uint32_t>(
|
||||
static_cast<double>(FLAGS_threads * FLAGS_ops_per_thread) / elapsed);
|
||||
|
||||
Vendored
+2
-2
@@ -9,8 +9,8 @@
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cassert>
|
||||
|
||||
#include "db/blob/blob_file_addition.h"
|
||||
#include "db/blob/blob_file_completion_callback.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/blob/blob_log_writer.h"
|
||||
@@ -27,33 +28,34 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobFileBuilder::BlobFileBuilder(
|
||||
VersionSet* versions, Env* env, FileSystem* fs,
|
||||
VersionSet* versions, FileSystem* fs,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const MutableCFOptions* mutable_cf_options, const FileOptions* file_options,
|
||||
int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCompletionCallback* blob_callback,
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: BlobFileBuilder([versions]() { return versions->NewFileNumber(); }, env,
|
||||
fs, immutable_cf_options, mutable_cf_options,
|
||||
file_options, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, io_tracer,
|
||||
blob_file_paths, blob_file_additions) {}
|
||||
: BlobFileBuilder([versions]() { return versions->NewFileNumber(); }, fs,
|
||||
immutable_cf_options, mutable_cf_options, file_options,
|
||||
job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, io_tracer, blob_callback, blob_file_paths,
|
||||
blob_file_additions) {}
|
||||
|
||||
BlobFileBuilder::BlobFileBuilder(
|
||||
std::function<uint64_t()> file_number_generator, Env* env, FileSystem* fs,
|
||||
std::function<uint64_t()> file_number_generator, FileSystem* fs,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const MutableCFOptions* mutable_cf_options, const FileOptions* file_options,
|
||||
int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCompletionCallback* blob_callback,
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: file_number_generator_(std::move(file_number_generator)),
|
||||
env_(env),
|
||||
fs_(fs),
|
||||
immutable_cf_options_(immutable_cf_options),
|
||||
min_blob_size_(mutable_cf_options->min_blob_size),
|
||||
@@ -66,12 +68,12 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
io_priority_(io_priority),
|
||||
write_hint_(write_hint),
|
||||
io_tracer_(io_tracer),
|
||||
blob_callback_(blob_callback),
|
||||
blob_file_paths_(blob_file_paths),
|
||||
blob_file_additions_(blob_file_additions),
|
||||
blob_count_(0),
|
||||
blob_bytes_(0) {
|
||||
assert(file_number_generator_);
|
||||
assert(env_);
|
||||
assert(fs_);
|
||||
assert(immutable_cf_options_);
|
||||
assert(file_options_);
|
||||
@@ -185,16 +187,17 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
FileTypeSet tmp_set = immutable_cf_options_->checksum_handoff_file_types;
|
||||
Statistics* const statistics = immutable_cf_options_->statistics;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), blob_file_paths_->back(), *file_options_, env_,
|
||||
io_tracer_, statistics, immutable_cf_options_->listeners,
|
||||
std::move(file), blob_file_paths_->back(), *file_options_,
|
||||
immutable_cf_options_->clock, io_tracer_, statistics,
|
||||
immutable_cf_options_->listeners,
|
||||
immutable_cf_options_->file_checksum_gen_factory,
|
||||
tmp_set.Contains(FileType::kBlobFile)));
|
||||
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
std::unique_ptr<BlobLogWriter> blob_log_writer(new BlobLogWriter(
|
||||
std::move(file_writer), env_, statistics, blob_file_number,
|
||||
immutable_cf_options_->use_fsync, do_flush));
|
||||
std::move(file_writer), immutable_cf_options_->clock, statistics,
|
||||
blob_file_number, immutable_cf_options_->use_fsync, do_flush));
|
||||
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
@@ -304,11 +307,15 @@ Status BlobFileBuilder::CloseBlobFile() {
|
||||
column_family_name_.c_str(), job_id_, blob_file_number,
|
||||
blob_count_, blob_bytes_);
|
||||
|
||||
if (blob_callback_) {
|
||||
s = blob_callback_->OnBlobFileCompleted(blob_file_paths_->back());
|
||||
}
|
||||
|
||||
writer_.reset();
|
||||
blob_count_ = 0;
|
||||
blob_bytes_ = 0;
|
||||
|
||||
return Status::OK();
|
||||
return s;
|
||||
}
|
||||
|
||||
Status BlobFileBuilder::CloseBlobFileIfNeeded() {
|
||||
@@ -324,4 +331,20 @@ Status BlobFileBuilder::CloseBlobFileIfNeeded() {
|
||||
return CloseBlobFile();
|
||||
}
|
||||
|
||||
void BlobFileBuilder::Abandon() {
|
||||
if (!IsBlobFileOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blob_callback_) {
|
||||
// BlobFileBuilder::Abandon() is called because of error while writing to
|
||||
// Blob files. So we can ignore the below error.
|
||||
blob_callback_->OnBlobFileCompleted(blob_file_paths_->back())
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
writer_.reset();
|
||||
blob_count_ = 0;
|
||||
blob_bytes_ = 0;
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -18,6 +18,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class VersionSet;
|
||||
class FileSystem;
|
||||
class SystemClock;
|
||||
struct ImmutableCFOptions;
|
||||
struct MutableCFOptions;
|
||||
struct FileOptions;
|
||||
@@ -26,10 +27,11 @@ class Status;
|
||||
class Slice;
|
||||
class BlobLogWriter;
|
||||
class IOTracer;
|
||||
class BlobFileCompletionCallback;
|
||||
|
||||
class BlobFileBuilder {
|
||||
public:
|
||||
BlobFileBuilder(VersionSet* versions, Env* env, FileSystem* fs,
|
||||
BlobFileBuilder(VersionSet* versions, FileSystem* fs,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const MutableCFOptions* mutable_cf_options,
|
||||
const FileOptions* file_options, int job_id,
|
||||
@@ -38,10 +40,11 @@ class BlobFileBuilder {
|
||||
Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCompletionCallback* blob_callback,
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions);
|
||||
|
||||
BlobFileBuilder(std::function<uint64_t()> file_number_generator, Env* env,
|
||||
BlobFileBuilder(std::function<uint64_t()> file_number_generator,
|
||||
FileSystem* fs,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const MutableCFOptions* mutable_cf_options,
|
||||
@@ -51,6 +54,7 @@ class BlobFileBuilder {
|
||||
Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCompletionCallback* blob_callback,
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions);
|
||||
|
||||
@@ -61,6 +65,7 @@ class BlobFileBuilder {
|
||||
|
||||
Status Add(const Slice& key, const Slice& value, std::string* blob_index);
|
||||
Status Finish();
|
||||
void Abandon();
|
||||
|
||||
private:
|
||||
bool IsBlobFileOpen() const;
|
||||
@@ -72,7 +77,6 @@ class BlobFileBuilder {
|
||||
Status CloseBlobFileIfNeeded();
|
||||
|
||||
std::function<uint64_t()> file_number_generator_;
|
||||
Env* env_;
|
||||
FileSystem* fs_;
|
||||
const ImmutableCFOptions* immutable_cf_options_;
|
||||
uint64_t min_blob_size_;
|
||||
@@ -85,6 +89,7 @@ class BlobFileBuilder {
|
||||
Env::IOPriority io_priority_;
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
BlobFileCompletionCallback* blob_callback_;
|
||||
std::vector<std::string>* blob_file_paths_;
|
||||
std::vector<BlobFileAddition>* blob_file_additions_;
|
||||
std::unique_ptr<BlobLogWriter> writer_;
|
||||
|
||||
@@ -39,8 +39,10 @@ class TestFileNumberGenerator {
|
||||
|
||||
class BlobFileBuilderTest : public testing::Test {
|
||||
protected:
|
||||
BlobFileBuilderTest()
|
||||
: mock_env_(Env::Default()), fs_(mock_env_.GetFileSystem().get()) {}
|
||||
BlobFileBuilderTest() : mock_env_(Env::Default()) {
|
||||
fs_ = mock_env_.GetFileSystem().get();
|
||||
clock_ = mock_env_.GetSystemClock().get();
|
||||
}
|
||||
|
||||
void VerifyBlobFile(uint64_t blob_file_number,
|
||||
const std::string& blob_file_path,
|
||||
@@ -57,11 +59,10 @@ class BlobFileBuilderTest : public testing::Test {
|
||||
fs_->NewRandomAccessFile(blob_file_path, file_options_, &file, dbg));
|
||||
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), blob_file_path,
|
||||
&mock_env_));
|
||||
new RandomAccessFileReader(std::move(file), blob_file_path, clock_));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
BlobLogSequentialReader blob_log_reader(std::move(file_reader), &mock_env_,
|
||||
BlobLogSequentialReader blob_log_reader(std::move(file_reader), clock_,
|
||||
statistics);
|
||||
|
||||
BlobLogHeader header;
|
||||
@@ -109,6 +110,7 @@ class BlobFileBuilderTest : public testing::Test {
|
||||
|
||||
MockEnv mock_env_;
|
||||
FileSystem* fs_;
|
||||
SystemClock* clock_;
|
||||
FileOptions file_options_;
|
||||
};
|
||||
|
||||
@@ -125,6 +127,7 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
"BlobFileBuilderTest_BuildAndCheckOneFile"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
@@ -138,11 +141,12 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
|
||||
number_of_blobs);
|
||||
@@ -208,6 +212,7 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = value_size;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
@@ -221,11 +226,12 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
|
||||
number_of_blobs);
|
||||
@@ -293,6 +299,7 @@ TEST_F(BlobFileBuilderTest, InlinedValues) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 1024;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
@@ -306,11 +313,12 @@ TEST_F(BlobFileBuilderTest, InlinedValues) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
for (size_t i = 0; i < number_of_blobs; ++i) {
|
||||
const std::string key = std::to_string(i);
|
||||
@@ -345,6 +353,7 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileBuilderTest_Compression"), 0);
|
||||
options.enable_blob_files = true;
|
||||
options.blob_compression_type = kSnappyCompression;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
@@ -358,11 +367,12 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
const std::string key("1");
|
||||
const std::string uncompressed_value(value_size, 'x');
|
||||
@@ -427,7 +437,7 @@ TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.blob_compression_type = kSnappyCompression;
|
||||
|
||||
options.env = &mock_env_;
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
@@ -440,11 +450,12 @@ TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack("CompressData:TamperWithReturnValue",
|
||||
[](void* arg) {
|
||||
@@ -504,6 +515,7 @@ TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
options.enable_blob_files = true;
|
||||
options.file_checksum_gen_factory =
|
||||
std::make_shared<DummyFileChecksumGenFactory>();
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
@@ -517,11 +529,12 @@ TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
const std::string key("1");
|
||||
const std::string value("deadbeef");
|
||||
@@ -613,11 +626,12 @@ TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* arg) {
|
||||
Status* const s = static_cast<Status*>(arg);
|
||||
|
||||
@@ -42,14 +42,14 @@ void WriteBlobFile(uint32_t column_family_id,
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), blob_file_path, FileOptions(),
|
||||
immutable_cf_options.env));
|
||||
immutable_cf_options.clock));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.env, statistics,
|
||||
immutable_cf_options.clock, statistics,
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -64,7 +64,6 @@ void WriteBlobFile(uint32_t column_family_id,
|
||||
constexpr char blob[] = "blob";
|
||||
|
||||
std::string compressed_blob;
|
||||
Slice blob_to_write;
|
||||
|
||||
uint64_t key_offset = 0;
|
||||
uint64_t blob_offset = 0;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#pragma once
|
||||
|
||||
#include "db/error_handler.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class BlobFileCompletionCallback {
|
||||
public:
|
||||
BlobFileCompletionCallback(SstFileManager* sst_file_manager,
|
||||
InstrumentedMutex* mutex,
|
||||
ErrorHandler* error_handler)
|
||||
: sst_file_manager_(sst_file_manager),
|
||||
mutex_(mutex),
|
||||
error_handler_(error_handler) {}
|
||||
|
||||
Status OnBlobFileCompleted(const std::string& file_name) {
|
||||
Status s;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager_);
|
||||
if (sfm) {
|
||||
// Report new blob files to SstFileManagerImpl
|
||||
s = sfm->OnAddFile(file_name);
|
||||
if (sfm->IsMaxAllowedSpaceReached()) {
|
||||
s = Status::SpaceLimit("Max allowed space was reached");
|
||||
TEST_SYNC_POINT(
|
||||
"BlobFileCompletionCallback::CallBack::MaxAllowedSpaceReached");
|
||||
InstrumentedMutexLock l(mutex_);
|
||||
error_handler_->SetBGError(s, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)file_name;
|
||||
#endif // ROCKSDB_LITE
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
SstFileManager* sst_file_manager_;
|
||||
InstrumentedMutex* mutex_;
|
||||
ErrorHandler* error_handler_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -118,8 +118,7 @@ Status BlobFileReader::OpenFile(
|
||||
}
|
||||
|
||||
file_reader->reset(new RandomAccessFileReader(
|
||||
std::move(file), blob_file_path,
|
||||
immutable_cf_options.env, io_tracer,
|
||||
std::move(file), blob_file_path, immutable_cf_options.clock, io_tracer,
|
||||
immutable_cf_options.statistics, BLOB_DB_BLOB_FILE_READ_MICROS,
|
||||
blob_file_read_hist, immutable_cf_options.rate_limiter,
|
||||
immutable_cf_options.listeners));
|
||||
@@ -271,7 +270,8 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
const Slice& user_key, uint64_t offset,
|
||||
uint64_t value_size,
|
||||
CompressionType compression_type,
|
||||
PinnableSlice* value) const {
|
||||
PinnableSlice* value,
|
||||
uint64_t* bytes_read) const {
|
||||
assert(value);
|
||||
|
||||
const uint64_t key_size = user_key.size();
|
||||
@@ -294,6 +294,9 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
: 0;
|
||||
assert(offset >= adjustment);
|
||||
|
||||
const uint64_t record_offset = offset - adjustment;
|
||||
const uint64_t record_size = value_size + adjustment;
|
||||
|
||||
Slice record_slice;
|
||||
Buffer buf;
|
||||
AlignedBuf aligned_buf;
|
||||
@@ -301,9 +304,6 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
{
|
||||
TEST_SYNC_POINT("BlobFileReader::GetBlob:ReadFromFile");
|
||||
|
||||
const uint64_t record_offset = offset - adjustment;
|
||||
const uint64_t record_size = value_size + adjustment;
|
||||
|
||||
const Status s = ReadFromFile(file_reader_.get(), record_offset,
|
||||
static_cast<size_t>(record_size),
|
||||
&record_slice, &buf, &aligned_buf);
|
||||
@@ -332,6 +332,10 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes_read) {
|
||||
*bytes_read = record_size;
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ class BlobFileReader {
|
||||
|
||||
Status GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
uint64_t offset, uint64_t value_size,
|
||||
CompressionType compression_type, PinnableSlice* value) const;
|
||||
CompressionType compression_type, PinnableSlice* value,
|
||||
uint64_t* bytes_read) const;
|
||||
|
||||
private:
|
||||
BlobFileReader(std::unique_ptr<RandomAccessFileReader>&& file_reader,
|
||||
|
||||
@@ -50,14 +50,14 @@ void WriteBlobFile(const ImmutableCFOptions& immutable_cf_options,
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), blob_file_path, FileOptions(),
|
||||
immutable_cf_options.env));
|
||||
immutable_cf_options.clock));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.env, statistics,
|
||||
immutable_cf_options.clock, statistics,
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
|
||||
BlobLogHeader header(column_family_id, compression_type, has_ttl,
|
||||
@@ -152,83 +152,103 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_OK(reader->GetBlob(read_options, key, blob_offset, blob_size,
|
||||
kNoCompression, &value));
|
||||
kNoCompression, &value, &bytes_read));
|
||||
ASSERT_EQ(value, blob);
|
||||
ASSERT_EQ(bytes_read, blob_size);
|
||||
}
|
||||
|
||||
read_options.verify_checksums = true;
|
||||
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_OK(reader->GetBlob(read_options, key, blob_offset, blob_size,
|
||||
kNoCompression, &value));
|
||||
kNoCompression, &value, &bytes_read));
|
||||
ASSERT_EQ(value, blob);
|
||||
|
||||
constexpr uint64_t key_size = sizeof(key) - 1;
|
||||
ASSERT_EQ(bytes_read,
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(key_size) +
|
||||
blob_size);
|
||||
}
|
||||
|
||||
// Invalid offset (too close to start of file)
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, key, blob_offset - 1, blob_size,
|
||||
kNoCompression, &value)
|
||||
kNoCompression, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
|
||||
// Invalid offset (too close to end of file)
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, key, blob_offset + 1, blob_size,
|
||||
kNoCompression, &value)
|
||||
kNoCompression, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
|
||||
// Incorrect compression type
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(
|
||||
reader
|
||||
->GetBlob(read_options, key, blob_offset, blob_size, kZSTD, &value)
|
||||
.IsCorruption());
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, key, blob_offset, blob_size, kZSTD,
|
||||
&value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
|
||||
// Incorrect key size
|
||||
{
|
||||
constexpr char shorter_key[] = "k";
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, shorter_key,
|
||||
blob_offset - (sizeof(key) - sizeof(shorter_key)),
|
||||
blob_size, kNoCompression, &value)
|
||||
blob_size, kNoCompression, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
|
||||
// Incorrect key
|
||||
{
|
||||
constexpr char incorrect_key[] = "foo";
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, incorrect_key, blob_offset,
|
||||
blob_size, kNoCompression, &value)
|
||||
blob_size, kNoCompression, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
|
||||
// Incorrect value size
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, key, blob_offset, blob_size + 1,
|
||||
kNoCompression, &value)
|
||||
kNoCompression, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,14 +280,14 @@ TEST_F(BlobFileReaderTest, Malformed) {
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), blob_file_path, FileOptions(),
|
||||
immutable_cf_options.env));
|
||||
immutable_cf_options.clock));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.env, statistics,
|
||||
immutable_cf_options.clock, statistics,
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
|
||||
BlobLogHeader header(column_family_id, kNoCompression, has_ttl,
|
||||
@@ -479,11 +499,13 @@ TEST_F(BlobFileReaderTest, BlobCRCError) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(ReadOptions(), key, blob_offset, blob_size,
|
||||
kNoCompression, &value)
|
||||
kNoCompression, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -530,20 +552,28 @@ TEST_F(BlobFileReaderTest, Compression) {
|
||||
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_OK(reader->GetBlob(read_options, key, blob_offset, blob_size,
|
||||
kSnappyCompression, &value));
|
||||
kSnappyCompression, &value, &bytes_read));
|
||||
ASSERT_EQ(value, blob);
|
||||
ASSERT_EQ(bytes_read, blob_size);
|
||||
}
|
||||
|
||||
read_options.verify_checksums = true;
|
||||
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_OK(reader->GetBlob(read_options, key, blob_offset, blob_size,
|
||||
kSnappyCompression, &value));
|
||||
kSnappyCompression, &value, &bytes_read));
|
||||
ASSERT_EQ(value, blob);
|
||||
|
||||
constexpr uint64_t key_size = sizeof(key) - 1;
|
||||
ASSERT_EQ(bytes_read,
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(key_size) +
|
||||
blob_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,11 +626,13 @@ TEST_F(BlobFileReaderTest, UncompressionError) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(ReadOptions(), key, blob_offset, blob_size,
|
||||
kSnappyCompression, &value)
|
||||
kSnappyCompression, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -678,11 +710,13 @@ TEST_P(BlobFileReaderIOErrorTest, IOError) {
|
||||
ASSERT_OK(s);
|
||||
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(ReadOptions(), key, blob_offset, blob_size,
|
||||
kNoCompression, &value)
|
||||
kNoCompression, &value, &bytes_read)
|
||||
.IsIOError());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -758,11 +792,13 @@ TEST_P(BlobFileReaderDecodingErrorTest, DecodingError) {
|
||||
ASSERT_OK(s);
|
||||
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(ReadOptions(), key, blob_offset, blob_size,
|
||||
kNoCompression, &value)
|
||||
kNoCompression, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobLogSequentialReader::BlobLogSequentialReader(
|
||||
std::unique_ptr<RandomAccessFileReader>&& file_reader, Env* env,
|
||||
std::unique_ptr<RandomAccessFileReader>&& file_reader, SystemClock* clock,
|
||||
Statistics* statistics)
|
||||
: file_(std::move(file_reader)),
|
||||
env_(env),
|
||||
clock_(clock),
|
||||
statistics_(statistics),
|
||||
next_byte_(0) {}
|
||||
|
||||
@@ -27,7 +27,7 @@ Status BlobLogSequentialReader::ReadSlice(uint64_t size, Slice* slice,
|
||||
assert(slice);
|
||||
assert(file_);
|
||||
|
||||
StopWatch read_sw(env_, statistics_, BLOB_DB_BLOB_FILE_READ_MICROS);
|
||||
StopWatch read_sw(clock_, statistics_, BLOB_DB_BLOB_FILE_READ_MICROS);
|
||||
Status s = file_->Read(IOOptions(), next_byte_, static_cast<size_t>(size),
|
||||
slice, buf, nullptr);
|
||||
next_byte_ += size;
|
||||
|
||||
@@ -16,6 +16,7 @@ class RandomAccessFileReader;
|
||||
class Env;
|
||||
class Statistics;
|
||||
class Status;
|
||||
class SystemClock;
|
||||
|
||||
/**
|
||||
* BlobLogSequentialReader is a general purpose log stream reader
|
||||
@@ -35,7 +36,7 @@ class BlobLogSequentialReader {
|
||||
|
||||
// Create a reader that will return log records from "*file_reader".
|
||||
BlobLogSequentialReader(std::unique_ptr<RandomAccessFileReader>&& file_reader,
|
||||
Env* env, Statistics* statistics);
|
||||
SystemClock* clock, Statistics* statistics);
|
||||
|
||||
// No copying allowed
|
||||
BlobLogSequentialReader(const BlobLogSequentialReader&) = delete;
|
||||
@@ -63,7 +64,8 @@ class BlobLogSequentialReader {
|
||||
Status ReadSlice(uint64_t size, Slice* slice, char* buf);
|
||||
|
||||
const std::unique_ptr<RandomAccessFileReader> file_;
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
|
||||
Statistics* statistics_;
|
||||
|
||||
Slice buffer_;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/stop_watch.h"
|
||||
@@ -19,11 +19,11 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobLogWriter::BlobLogWriter(std::unique_ptr<WritableFileWriter>&& dest,
|
||||
Env* env, Statistics* statistics,
|
||||
SystemClock* clock, Statistics* statistics,
|
||||
uint64_t log_number, bool use_fs, bool do_flush,
|
||||
uint64_t boffset)
|
||||
: dest_(std::move(dest)),
|
||||
env_(env),
|
||||
clock_(clock),
|
||||
statistics_(statistics),
|
||||
log_number_(log_number),
|
||||
block_offset_(boffset),
|
||||
@@ -36,7 +36,7 @@ BlobLogWriter::~BlobLogWriter() = default;
|
||||
Status BlobLogWriter::Sync() {
|
||||
TEST_SYNC_POINT("BlobLogWriter::Sync");
|
||||
|
||||
StopWatch sync_sw(env_, statistics_, BLOB_DB_BLOB_FILE_SYNC_MICROS);
|
||||
StopWatch sync_sw(clock_, statistics_, BLOB_DB_BLOB_FILE_SYNC_MICROS);
|
||||
Status s = dest_->Sync(use_fsync_);
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_SYNCED);
|
||||
return s;
|
||||
@@ -148,7 +148,7 @@ Status BlobLogWriter::EmitPhysicalRecord(const std::string& headerbuf,
|
||||
const Slice& key, const Slice& val,
|
||||
uint64_t* key_offset,
|
||||
uint64_t* blob_offset) {
|
||||
StopWatch write_sw(env_, statistics_, BLOB_DB_BLOB_FILE_WRITE_MICROS);
|
||||
StopWatch write_sw(clock_, statistics_, BLOB_DB_BLOB_FILE_WRITE_MICROS);
|
||||
Status s = dest_->Append(Slice(headerbuf));
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(key);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
@@ -18,7 +17,7 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class WritableFileWriter;
|
||||
|
||||
class SystemClock;
|
||||
/**
|
||||
* BlobLogWriter is the blob log stream writer. It provides an append-only
|
||||
* abstraction for writing blob data.
|
||||
@@ -32,7 +31,7 @@ class BlobLogWriter {
|
||||
// Create a writer that will append data to "*dest".
|
||||
// "*dest" must be initially empty.
|
||||
// "*dest" must remain live while this BlobLogWriter is in use.
|
||||
BlobLogWriter(std::unique_ptr<WritableFileWriter>&& dest, Env* env,
|
||||
BlobLogWriter(std::unique_ptr<WritableFileWriter>&& dest, SystemClock* clock,
|
||||
Statistics* statistics, uint64_t log_number, bool use_fsync,
|
||||
bool do_flush, uint64_t boffset = 0);
|
||||
// No copying allowed
|
||||
@@ -69,7 +68,7 @@ class BlobLogWriter {
|
||||
|
||||
private:
|
||||
std::unique_ptr<WritableFileWriter> dest_;
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
Statistics* statistics_;
|
||||
uint64_t log_number_;
|
||||
uint64_t block_offset_; // Current offset in block
|
||||
|
||||
@@ -355,6 +355,59 @@ TEST_P(DBBlobBasicIOErrorTest, MultiGetBlobs_IOError) {
|
||||
ASSERT_TRUE(statuses[1].IsIOError());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ReadBlobCompactionFilter : public CompactionFilter {
|
||||
public:
|
||||
ReadBlobCompactionFilter() = default;
|
||||
const char* Name() const override {
|
||||
return "rocksdb.compaction.filter.read.blob";
|
||||
}
|
||||
CompactionFilter::Decision FilterV2(
|
||||
int /*level*/, const Slice& /*key*/, ValueType value_type,
|
||||
const Slice& existing_value, std::string* new_value,
|
||||
std::string* /*skip_until*/) const override {
|
||||
if (value_type != CompactionFilter::ValueType::kValue) {
|
||||
return CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
assert(new_value);
|
||||
new_value->assign(existing_value.data(), existing_value.size());
|
||||
return CompactionFilter::Decision::kChangeValue;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_P(DBBlobBasicIOErrorTest, CompactionFilterReadBlob_IOError) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_injection_env_.get();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.create_if_missing = true;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new ReadBlobCompactionFilter);
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
constexpr char key[] = "foo";
|
||||
constexpr char blob_value[] = "foo_blob_value";
|
||||
ASSERT_OK(Put(key, blob_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_->SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr)
|
||||
.IsIOError());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBBlobCompactionTest : public DBTestBase {
|
||||
public:
|
||||
explicit DBBlobCompactionTest()
|
||||
: DBTestBase("/db_blob_compaction_test", /*env_do_fsync=*/false) {}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const std::vector<InternalStats::CompactionStats>& GetCompactionStats() {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
assert(internal_stats);
|
||||
|
||||
return internal_stats->TEST_GetCompactionStats();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
class FilterByKeyLength : public CompactionFilter {
|
||||
public:
|
||||
explicit FilterByKeyLength(size_t len) : length_threshold_(len) {}
|
||||
const char* Name() const override {
|
||||
return "rocksdb.compaction.filter.by.key.length";
|
||||
}
|
||||
CompactionFilter::Decision FilterBlobByKey(
|
||||
int /*level*/, const Slice& key, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
if (key.size() < length_threshold_) {
|
||||
return CompactionFilter::Decision::kRemove;
|
||||
}
|
||||
return CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t length_threshold_;
|
||||
};
|
||||
|
||||
class BadBlobCompactionFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit BadBlobCompactionFilter(std::string prefix,
|
||||
CompactionFilter::Decision filter_by_key,
|
||||
CompactionFilter::Decision filter_v2)
|
||||
: prefix_(std::move(prefix)),
|
||||
filter_blob_by_key_(filter_by_key),
|
||||
filter_v2_(filter_v2) {}
|
||||
const char* Name() const override { return "rocksdb.compaction.filter.bad"; }
|
||||
CompactionFilter::Decision FilterBlobByKey(
|
||||
int /*level*/, const Slice& key, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
if (key.size() >= prefix_.size() &&
|
||||
0 == strncmp(prefix_.data(), key.data(), prefix_.size())) {
|
||||
return CompactionFilter::Decision::kUndetermined;
|
||||
}
|
||||
return filter_blob_by_key_;
|
||||
}
|
||||
CompactionFilter::Decision FilterV2(
|
||||
int /*level*/, const Slice& /*key*/, ValueType /*value_type*/,
|
||||
const Slice& /*existing_value*/, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
return filter_v2_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string prefix_;
|
||||
const CompactionFilter::Decision filter_blob_by_key_;
|
||||
const CompactionFilter::Decision filter_v2_;
|
||||
};
|
||||
|
||||
class ValueBlindWriteFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ValueBlindWriteFilter(std::string new_val)
|
||||
: new_value_(std::move(new_val)) {}
|
||||
const char* Name() const override {
|
||||
return "rocksdb.compaction.filter.blind.write";
|
||||
}
|
||||
CompactionFilter::Decision FilterBlobByKey(
|
||||
int level, const Slice& key, std::string* new_value,
|
||||
std::string* skip_until) const override;
|
||||
|
||||
private:
|
||||
const std::string new_value_;
|
||||
};
|
||||
|
||||
CompactionFilter::Decision ValueBlindWriteFilter::FilterBlobByKey(
|
||||
int /*level*/, const Slice& /*key*/, std::string* new_value,
|
||||
std::string* /*skip_until*/) const {
|
||||
assert(new_value);
|
||||
new_value->assign(new_value_);
|
||||
return CompactionFilter::Decision::kChangeValue;
|
||||
}
|
||||
|
||||
class ValueMutationFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ValueMutationFilter(std::string padding)
|
||||
: padding_(std::move(padding)) {}
|
||||
const char* Name() const override {
|
||||
return "rocksdb.compaction.filter.value.mutation";
|
||||
}
|
||||
CompactionFilter::Decision FilterV2(int level, const Slice& key,
|
||||
ValueType value_type,
|
||||
const Slice& existing_value,
|
||||
std::string* new_value,
|
||||
std::string* skip_until) const override;
|
||||
|
||||
private:
|
||||
const std::string padding_;
|
||||
};
|
||||
|
||||
CompactionFilter::Decision ValueMutationFilter::FilterV2(
|
||||
int /*level*/, const Slice& /*key*/, ValueType value_type,
|
||||
const Slice& existing_value, std::string* new_value,
|
||||
std::string* /*skip_until*/) const {
|
||||
assert(CompactionFilter::ValueType::kBlobIndex != value_type);
|
||||
if (CompactionFilter::ValueType::kValue != value_type) {
|
||||
return CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
assert(new_value);
|
||||
new_value->assign(existing_value.data(), existing_value.size());
|
||||
new_value->append(padding_);
|
||||
return CompactionFilter::Decision::kChangeValue;
|
||||
}
|
||||
|
||||
class AlwaysKeepFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit AlwaysKeepFilter() = default;
|
||||
const char* Name() const override {
|
||||
return "rocksdb.compaction.filter.always.keep";
|
||||
}
|
||||
CompactionFilter::Decision FilterV2(
|
||||
int /*level*/, const Slice& /*key*/, ValueType /*value_type*/,
|
||||
const Slice& /*existing_value*/, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
return CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
class DBBlobBadCompactionFilterTest
|
||||
: public DBBlobCompactionTest,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<std::string, CompactionFilter::Decision,
|
||||
CompactionFilter::Decision>> {
|
||||
public:
|
||||
explicit DBBlobBadCompactionFilterTest()
|
||||
: compaction_filter_guard_(new BadBlobCompactionFilter(
|
||||
std::get<0>(GetParam()), std::get<1>(GetParam()),
|
||||
std::get<2>(GetParam()))) {}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
BadCompactionFilter, DBBlobBadCompactionFilterTest,
|
||||
testing::Combine(
|
||||
testing::Values("a"),
|
||||
testing::Values(CompactionFilter::Decision::kChangeBlobIndex,
|
||||
CompactionFilter::Decision::kIOError),
|
||||
testing::Values(CompactionFilter::Decision::kUndetermined,
|
||||
CompactionFilter::Decision::kChangeBlobIndex,
|
||||
CompactionFilter::Decision::kIOError)));
|
||||
|
||||
TEST_F(DBBlobCompactionTest, FilterByKeyLength) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.create_if_missing = true;
|
||||
constexpr size_t kKeyLength = 2;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new FilterByKeyLength(kKeyLength));
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
|
||||
constexpr char short_key[] = "a";
|
||||
constexpr char long_key[] = "abc";
|
||||
constexpr char blob_value[] = "value";
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put(short_key, blob_value));
|
||||
ASSERT_OK(Put(long_key, blob_value));
|
||||
ASSERT_OK(Flush());
|
||||
CompactRangeOptions cro;
|
||||
ASSERT_OK(db_->CompactRange(cro, /*begin=*/nullptr, /*end=*/nullptr));
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), short_key, &value).IsNotFound());
|
||||
value.clear();
|
||||
ASSERT_OK(db_->Get(ReadOptions(), long_key, &value));
|
||||
ASSERT_EQ("value", value);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const auto& compaction_stats = GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
|
||||
// Filter decides between kKeep and kRemove solely based on key;
|
||||
// this involves neither reading nor writing blobs
|
||||
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, BlindWriteFilter) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.create_if_missing = true;
|
||||
constexpr char new_blob_value[] = "new_blob_value";
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new ValueBlindWriteFilter(new_blob_value));
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<std::string> keys = {"a", "b", "c"};
|
||||
const std::vector<std::string> values = {"a_value", "b_value", "c_value"};
|
||||
assert(keys.size() == values.size());
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
ASSERT_OK(Put(keys[i], values[i]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
for (const auto& key : keys) {
|
||||
ASSERT_EQ(new_blob_value, Get(key));
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const auto& compaction_stats = GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
|
||||
// Filter unconditionally changes value in FilterBlobByKey;
|
||||
// this involves writing but not reading blobs
|
||||
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_GT(compaction_stats[1].bytes_written_blob, 0);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBBlobBadCompactionFilterTest, BadDecisionFromCompactionFilter) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_filter = compaction_filter_guard_.get();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("b", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr)
|
||||
.IsNotSupported());
|
||||
Close();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
std::string key(std::get<0>(GetParam()));
|
||||
ASSERT_OK(Put(key, "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr)
|
||||
.IsNotSupported());
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, CompactionFilter_InlinedTTLIndex) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new ValueMutationFilter(""));
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
DestroyAndReopen(options);
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob[] = "blob";
|
||||
// Fake an inlined TTL blob index.
|
||||
std::string blob_index;
|
||||
constexpr uint64_t expiration = 1234567890;
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, expiration, blob);
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr)
|
||||
.IsCorruption());
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, CompactionFilter) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
constexpr char padding[] = "_delta";
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new ValueMutationFilter(padding));
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<std::pair<std::string, std::string>> kvs = {
|
||||
{"a", "a_value"}, {"b", "b_value"}, {"c", "c_value"}};
|
||||
for (const auto& kv : kvs) {
|
||||
ASSERT_OK(Put(kv.first, kv.second));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
for (const auto& kv : kvs) {
|
||||
ASSERT_EQ(kv.second + std::string(padding), Get(kv.first));
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const auto& compaction_stats = GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
|
||||
// Filter changes the value using the previous value in FilterV2;
|
||||
// this involves reading and writing blobs
|
||||
ASSERT_GT(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_GT(compaction_stats[1].bytes_written_blob, 0);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, CorruptedBlobIndex) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new ValueMutationFilter(""));
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
DestroyAndReopen(options);
|
||||
// Mock a corrupted blob index
|
||||
constexpr char key[] = "key";
|
||||
std::string blob_idx("blob_idx");
|
||||
WriteBatch write_batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&write_batch, 0, key, blob_idx));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &write_batch));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr)
|
||||
.IsCorruption());
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, CompactionFilterReadBlobAndKeep) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new AlwaysKeepFilter());
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "foo_value"));
|
||||
ASSERT_OK(Flush());
|
||||
std::vector<uint64_t> blob_files = GetBlobFileNumbers();
|
||||
ASSERT_EQ(1, blob_files.size());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
ASSERT_EQ(blob_files, GetBlobFileNumbers());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const auto& compaction_stats = GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
|
||||
// Filter decides to keep the existing value in FilterV2;
|
||||
// this involves reading but not writing blobs
|
||||
ASSERT_GT(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBBlobCorruptionTest : public DBTestBase {
|
||||
protected:
|
||||
DBBlobCorruptionTest()
|
||||
: DBTestBase("/db_blob_corruption_test", /* env_do_fsync */ false) {}
|
||||
|
||||
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
||||
// Pick file to corrupt
|
||||
std::vector<std::string> filenames;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &filenames));
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
std::string fname;
|
||||
uint64_t picked_number = kInvalidBlobFileNumber;
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == filetype &&
|
||||
number > picked_number) { // Pick latest file
|
||||
fname = dbname_ + "/" + filenames[i];
|
||||
picked_number = number;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(!fname.empty()) << filetype;
|
||||
ASSERT_OK(test::CorruptFile(env_, fname, offset, bytes_to_corrupt));
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBlobCorruptionTest, VerifyWholeBlobFileChecksum) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.create_if_missing = true;
|
||||
options.file_checksum_gen_factory =
|
||||
ROCKSDB_NAMESPACE::GetFileChecksumGenCrc32cFactory();
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put(Slice("key_1"), Slice("blob_value_1")));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Slice("key_2"), Slice("blob_value_2")));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
|
||||
Close();
|
||||
|
||||
Corrupt(kBlobFile, 0, 2);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
int count{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::VerifyFullFileChecksum:mismatch", [&](void* arg) {
|
||||
const Status* s = static_cast<Status*>(arg);
|
||||
ASSERT_NE(s, nullptr);
|
||||
++count;
|
||||
ASSERT_NOK(*s);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_TRUE(db_->VerifyFileChecksums(ReadOptions()).IsCorruption());
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+16
-10
@@ -23,6 +23,7 @@
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/read_write_util.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
@@ -92,7 +93,8 @@ Status BuildTable(
|
||||
TableProperties* table_properties, int level, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, Env::WriteLifeTimeHint write_hint,
|
||||
const uint64_t file_creation_time, const std::string& db_id,
|
||||
const std::string& db_session_id, const std::string* full_history_ts_low) {
|
||||
const std::string& db_session_id, const std::string* full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -125,6 +127,7 @@ Status BuildTable(
|
||||
assert(env);
|
||||
FileSystem* fs = db_options.fs.get();
|
||||
assert(fs);
|
||||
|
||||
TableProperties tp;
|
||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||
TableBuilder* builder;
|
||||
@@ -152,7 +155,7 @@ Status BuildTable(
|
||||
file->SetIOPriority(io_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
file_writer.reset(new WritableFileWriter(
|
||||
std::move(file), fname, file_options, env, io_tracer,
|
||||
std::move(file), fname, file_options, ioptions.clock, io_tracer,
|
||||
ioptions.statistics, ioptions.listeners,
|
||||
ioptions.file_checksum_gen_factory,
|
||||
tmp_set.Contains(FileType::kTableFile)));
|
||||
@@ -174,11 +177,11 @@ Status BuildTable(
|
||||
|
||||
std::unique_ptr<BlobFileBuilder> blob_file_builder(
|
||||
(mutable_cf_options.enable_blob_files && blob_file_additions)
|
||||
? new BlobFileBuilder(versions, env, fs, &ioptions,
|
||||
&mutable_cf_options, &file_options, job_id,
|
||||
column_family_id, column_family_name,
|
||||
io_priority, write_hint, io_tracer,
|
||||
&blob_file_paths, blob_file_additions)
|
||||
? new BlobFileBuilder(versions, fs, &ioptions, &mutable_cf_options,
|
||||
&file_options, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
io_tracer, blob_callback, &blob_file_paths,
|
||||
blob_file_additions)
|
||||
: nullptr);
|
||||
|
||||
CompactionIterator c_iter(
|
||||
@@ -256,7 +259,7 @@ Status BuildTable(
|
||||
// Finish and check for file errors
|
||||
TEST_SYNC_POINT("BuildTable:BeforeSyncTable");
|
||||
if (s.ok() && !empty) {
|
||||
StopWatch sw(env, ioptions.statistics, TABLE_SYNC_MICROS);
|
||||
StopWatch sw(ioptions.clock, ioptions.statistics, TABLE_SYNC_MICROS);
|
||||
*io_status = file_writer->Sync(ioptions.use_fsync);
|
||||
}
|
||||
TEST_SYNC_POINT("BuildTable:BeforeCloseTableFile");
|
||||
@@ -278,8 +281,9 @@ Status BuildTable(
|
||||
if (blob_file_builder) {
|
||||
if (s.ok()) {
|
||||
s = blob_file_builder->Finish();
|
||||
} else {
|
||||
blob_file_builder->Abandon();
|
||||
}
|
||||
|
||||
blob_file_builder.reset();
|
||||
}
|
||||
|
||||
@@ -339,8 +343,10 @@ Status BuildTable(
|
||||
|
||||
if (blob_file_additions) {
|
||||
for (const std::string& blob_file_path : blob_file_paths) {
|
||||
ignored = fs->DeleteFile(blob_file_path, IOOptions(), dbg);
|
||||
ignored = DeleteDBFile(&db_options, blob_file_path, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/false);
|
||||
ignored.PermitUncheckedError();
|
||||
TEST_SYNC_POINT("BuildTable::AfterDeleteFile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -38,6 +38,7 @@ class VersionEdit;
|
||||
class TableBuilder;
|
||||
class WritableFileWriter;
|
||||
class InternalStats;
|
||||
class BlobFileCompletionCallback;
|
||||
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown. It must outlive the
|
||||
@@ -90,6 +91,7 @@ extern Status BuildTable(
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const uint64_t file_creation_time = 0, const std::string& db_id = "",
|
||||
const std::string& db_session_id = "",
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
const std::string* full_history_ts_low = nullptr,
|
||||
BlobFileCompletionCallback* blob_callback = nullptr);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -11,7 +11,11 @@
|
||||
|
||||
#include "rocksdb/c.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -24,6 +28,7 @@
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
@@ -39,13 +44,8 @@
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
|
||||
using ROCKSDB_NAMESPACE::BackupableDBOptions;
|
||||
using ROCKSDB_NAMESPACE::BackupEngine;
|
||||
using ROCKSDB_NAMESPACE::BackupID;
|
||||
@@ -2959,6 +2959,8 @@ void rocksdb_options_set_access_hint_on_compaction_start(
|
||||
opt->rep.access_hint_on_compaction_start =
|
||||
ROCKSDB_NAMESPACE::Options::WILLNEED;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -1119,9 +1119,8 @@ int main(int argc, char** argv) {
|
||||
// Essentially a fingerprint of the block-based Bloom schema
|
||||
CheckCondition(hits == 241);
|
||||
} else {
|
||||
// Essentially a fingerprint of the full Bloom schema(s),
|
||||
// format_version < 5, which vary for three different CACHE_LINE_SIZEs
|
||||
CheckCondition(hits == 224 || hits == 180 || hits == 125);
|
||||
// Essentially a fingerprint of full Bloom schema, format_version=5
|
||||
CheckCondition(hits == 188);
|
||||
}
|
||||
CheckCondition(
|
||||
(keys_to_query - hits) ==
|
||||
@@ -2881,7 +2880,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
#else
|
||||
|
||||
int main() {
|
||||
int main(void) {
|
||||
fprintf(stderr, "SKIPPED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
+9
-5
@@ -558,7 +558,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
internal_stats_.reset(
|
||||
new InternalStats(ioptions_.num_levels, db_options.env, this));
|
||||
new InternalStats(ioptions_.num_levels, ioptions_.clock, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, file_options, _table_cache,
|
||||
block_cache_tracer, io_tracer));
|
||||
blob_file_cache_.reset(
|
||||
@@ -633,7 +633,7 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
|
||||
if (dummy_versions_ != nullptr) {
|
||||
// List must be empty
|
||||
assert(dummy_versions_->TEST_Next() == dummy_versions_);
|
||||
assert(dummy_versions_->Next() == dummy_versions_);
|
||||
bool deleted __attribute__((__unused__));
|
||||
deleted = dummy_versions_->Unref();
|
||||
assert(deleted);
|
||||
@@ -835,7 +835,8 @@ std::pair<WriteStallCondition, ColumnFamilyData::WriteStallCause>
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
int num_unflushed_memtables, int num_l0_files,
|
||||
uint64_t num_compaction_needed_bytes,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableCFOptions& immutable_cf_options) {
|
||||
if (num_unflushed_memtables >= mutable_cf_options.max_write_buffer_number) {
|
||||
return {WriteStallCondition::kStopped, WriteStallCause::kMemtableLimit};
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
@@ -849,7 +850,9 @@ ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
WriteStallCause::kPendingCompactionBytes};
|
||||
} else if (mutable_cf_options.max_write_buffer_number > 3 &&
|
||||
num_unflushed_memtables >=
|
||||
mutable_cf_options.max_write_buffer_number - 1) {
|
||||
mutable_cf_options.max_write_buffer_number - 1 &&
|
||||
num_unflushed_memtables - 1 >=
|
||||
immutable_cf_options.min_write_buffer_number_to_merge) {
|
||||
return {WriteStallCondition::kDelayed, WriteStallCause::kMemtableLimit};
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
@@ -877,7 +880,8 @@ WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
|
||||
auto write_stall_condition_and_cause = GetWriteStallConditionAndCause(
|
||||
imm()->NumNotFlushed(), vstorage->l0_delay_trigger_count(),
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options);
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options,
|
||||
*ioptions());
|
||||
write_stall_condition = write_stall_condition_and_cause.first;
|
||||
auto write_stall_cause = write_stall_condition_and_cause.second;
|
||||
|
||||
|
||||
+5
-3
@@ -475,9 +475,11 @@ class ColumnFamilyData {
|
||||
kPendingCompactionBytes,
|
||||
};
|
||||
static std::pair<WriteStallCondition, WriteStallCause>
|
||||
GetWriteStallConditionAndCause(int num_unflushed_memtables, int num_l0_files,
|
||||
uint64_t num_compaction_needed_bytes,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
GetWriteStallConditionAndCause(
|
||||
int num_unflushed_memtables, int num_l0_files,
|
||||
uint64_t num_compaction_needed_bytes,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableCFOptions& immutable_cf_options);
|
||||
|
||||
// Recalculate some small conditions, which are changed only during
|
||||
// compaction, adding new memtable and/or
|
||||
|
||||
@@ -34,4 +34,8 @@ struct CompactionIterationStats {
|
||||
// Single-Delete diagnostics for exceptional situations
|
||||
uint64_t num_single_del_fallthru = 0;
|
||||
uint64_t num_single_del_mismatch = 0;
|
||||
|
||||
// Blob related statistics
|
||||
uint64_t num_blobs_read = 0;
|
||||
uint64_t total_blob_bytes_read = 0;
|
||||
};
|
||||
@@ -80,6 +80,7 @@ CompactionIterator::CompactionIterator(
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
env_(env),
|
||||
clock_(env_->GetSystemClock().get()),
|
||||
report_detailed_time_(report_detailed_time),
|
||||
expect_valid_internal_key_(expect_valid_internal_key),
|
||||
range_del_agg_(range_del_agg),
|
||||
@@ -203,72 +204,146 @@ void CompactionIterator::Next() {
|
||||
|
||||
bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
Slice* skip_until) {
|
||||
if (compaction_filter_ != nullptr &&
|
||||
(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex)) {
|
||||
// If the user has specified a compaction filter and the sequence
|
||||
// number is greater than any external snapshot, then invoke the
|
||||
// filter. If the return value of the compaction filter is true,
|
||||
// replace the entry with a deletion marker.
|
||||
CompactionFilter::Decision filter;
|
||||
compaction_filter_value_.clear();
|
||||
compaction_filter_skip_until_.Clear();
|
||||
CompactionFilter::ValueType value_type =
|
||||
ikey_.type == kTypeValue ? CompactionFilter::ValueType::kValue
|
||||
: CompactionFilter::ValueType::kBlobIndex;
|
||||
// Hack: pass internal key to BlobIndexCompactionFilter since it needs
|
||||
// to get sequence number.
|
||||
Slice& filter_key = ikey_.type == kTypeValue ? ikey_.user_key : key_;
|
||||
{
|
||||
StopWatchNano timer(env_, report_detailed_time_);
|
||||
if (!compaction_filter_ ||
|
||||
(ikey_.type != kTypeValue && ikey_.type != kTypeBlobIndex)) {
|
||||
return true;
|
||||
}
|
||||
bool error = false;
|
||||
// If the user has specified a compaction filter and the sequence
|
||||
// number is greater than any external snapshot, then invoke the
|
||||
// filter. If the return value of the compaction filter is true,
|
||||
// replace the entry with a deletion marker.
|
||||
CompactionFilter::Decision filter = CompactionFilter::Decision::kUndetermined;
|
||||
compaction_filter_value_.clear();
|
||||
compaction_filter_skip_until_.Clear();
|
||||
CompactionFilter::ValueType value_type =
|
||||
ikey_.type == kTypeValue ? CompactionFilter::ValueType::kValue
|
||||
: CompactionFilter::ValueType::kBlobIndex;
|
||||
// Hack: pass internal key to BlobIndexCompactionFilter since it needs
|
||||
// to get sequence number.
|
||||
assert(compaction_filter_);
|
||||
Slice& filter_key =
|
||||
(ikey_.type == kTypeValue ||
|
||||
!compaction_filter_->IsStackedBlobDbInternalCompactionFilter())
|
||||
? ikey_.user_key
|
||||
: key_;
|
||||
{
|
||||
StopWatchNano timer(clock_, report_detailed_time_);
|
||||
if (kTypeBlobIndex == ikey_.type) {
|
||||
blob_value_.Reset();
|
||||
filter = compaction_filter_->FilterBlobByKey(
|
||||
compaction_->level(), filter_key, &compaction_filter_value_,
|
||||
compaction_filter_skip_until_.rep());
|
||||
if (CompactionFilter::Decision::kUndetermined == filter &&
|
||||
!compaction_filter_->IsStackedBlobDbInternalCompactionFilter()) {
|
||||
// For integrated BlobDB impl, CompactionIterator reads blob value.
|
||||
// For Stacked BlobDB impl, the corresponding CompactionFilter's
|
||||
// FilterV2 method should read the blob value.
|
||||
BlobIndex blob_index;
|
||||
Status s = blob_index.DecodeFrom(value_);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (blob_index.HasTTL() || blob_index.IsInlined()) {
|
||||
status_ = Status::Corruption("Unexpected TTL/inlined blob index");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
const Version* const version = compaction_->input_version();
|
||||
assert(version);
|
||||
|
||||
uint64_t bytes_read = 0;
|
||||
s = version->GetBlob(ReadOptions(), ikey_.user_key, blob_index,
|
||||
&blob_value_, &bytes_read);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
++iter_stats_.num_blobs_read;
|
||||
iter_stats_.total_blob_bytes_read += bytes_read;
|
||||
|
||||
value_type = CompactionFilter::ValueType::kValue;
|
||||
}
|
||||
}
|
||||
if (CompactionFilter::Decision::kUndetermined == filter) {
|
||||
filter = compaction_filter_->FilterV2(
|
||||
compaction_->level(), filter_key, value_type, value_,
|
||||
&compaction_filter_value_, compaction_filter_skip_until_.rep());
|
||||
iter_stats_.total_filter_time +=
|
||||
env_ != nullptr && report_detailed_time_ ? timer.ElapsedNanos() : 0;
|
||||
compaction_->level(), filter_key, value_type,
|
||||
blob_value_.empty() ? value_ : blob_value_, &compaction_filter_value_,
|
||||
compaction_filter_skip_until_.rep());
|
||||
}
|
||||
iter_stats_.total_filter_time +=
|
||||
env_ != nullptr && report_detailed_time_ ? timer.ElapsedNanos() : 0;
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil &&
|
||||
cmp_->Compare(*compaction_filter_skip_until_.rep(), ikey_.user_key) <=
|
||||
0) {
|
||||
// Can't skip to a key smaller than the current one.
|
||||
// Keep the key as per FilterV2 documentation.
|
||||
filter = CompactionFilter::Decision::kKeep;
|
||||
if (CompactionFilter::Decision::kUndetermined == filter) {
|
||||
// Should not reach here, since FilterV2 should never return kUndetermined.
|
||||
status_ =
|
||||
Status::NotSupported("FilterV2() should never return kUndetermined");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil &&
|
||||
cmp_->Compare(*compaction_filter_skip_until_.rep(), ikey_.user_key) <=
|
||||
0) {
|
||||
// Can't skip to a key smaller than the current one.
|
||||
// Keep the key as per FilterV2 documentation.
|
||||
filter = CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemove) {
|
||||
// convert the current key to a delete; key_ is pointing into
|
||||
// current_key_ at this point, so updating current_key_ updates key()
|
||||
ikey_.type = kTypeDeletion;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, kTypeDeletion);
|
||||
// no value associated with delete
|
||||
value_.clear();
|
||||
iter_stats_.num_record_drop_user++;
|
||||
} else if (filter == CompactionFilter::Decision::kChangeValue) {
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
// value transfer from blob file to inlined data
|
||||
ikey_.type = kTypeValue;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemove) {
|
||||
// convert the current key to a delete; key_ is pointing into
|
||||
// current_key_ at this point, so updating current_key_ updates key()
|
||||
ikey_.type = kTypeDeletion;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, kTypeDeletion);
|
||||
// no value associated with delete
|
||||
value_.clear();
|
||||
iter_stats_.num_record_drop_user++;
|
||||
} else if (filter == CompactionFilter::Decision::kChangeValue) {
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
// value transfer from blob file to inlined data
|
||||
ikey_.type = kTypeValue;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
value_ = compaction_filter_value_;
|
||||
} else if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil) {
|
||||
*need_skip = true;
|
||||
compaction_filter_skip_until_.ConvertFromUserKey(kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
*skip_until = compaction_filter_skip_until_.Encode();
|
||||
} else if (filter == CompactionFilter::Decision::kChangeBlobIndex) {
|
||||
if (ikey_.type == kTypeValue) {
|
||||
// value transfer from inlined data to blob file
|
||||
ikey_.type = kTypeBlobIndex;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
value_ = compaction_filter_value_;
|
||||
} else if (filter == CompactionFilter::Decision::kIOError) {
|
||||
status_ =
|
||||
Status::IOError("Failed to access blob during compaction filter");
|
||||
value_ = compaction_filter_value_;
|
||||
} else if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil) {
|
||||
*need_skip = true;
|
||||
compaction_filter_skip_until_.ConvertFromUserKey(kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
*skip_until = compaction_filter_skip_until_.Encode();
|
||||
} else if (filter == CompactionFilter::Decision::kChangeBlobIndex) {
|
||||
// Only the StackableDB-based BlobDB impl's compaction filter should return
|
||||
// kChangeBlobIndex. Decision about rewriting blob and changing blob index
|
||||
// in the integrated BlobDB impl is made in subsequent call to
|
||||
// PrepareOutput() and its callees.
|
||||
if (!compaction_filter_->IsStackedBlobDbInternalCompactionFilter()) {
|
||||
status_ = Status::NotSupported(
|
||||
"Only stacked BlobDB's internal compaction filter can return "
|
||||
"kChangeBlobIndex.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (ikey_.type == kTypeValue) {
|
||||
// value transfer from inlined data to blob file
|
||||
ikey_.type = kTypeBlobIndex;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
value_ = compaction_filter_value_;
|
||||
} else if (filter == CompactionFilter::Decision::kIOError) {
|
||||
if (!compaction_filter_->IsStackedBlobDbInternalCompactionFilter()) {
|
||||
status_ = Status::NotSupported(
|
||||
"CompactionFilter for integrated BlobDB should not return kIOError");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
status_ = Status::IOError("Failed to access blob during compaction filter");
|
||||
error = true;
|
||||
}
|
||||
return true;
|
||||
return !error;
|
||||
}
|
||||
|
||||
void CompactionIterator::NextFromInput() {
|
||||
@@ -315,13 +390,11 @@ void CompactionIterator::NextFromInput() {
|
||||
// merge_helper_->compaction_filter_skip_until_.
|
||||
Slice skip_until;
|
||||
|
||||
int cmp_user_key_without_ts = 0;
|
||||
bool user_key_equal_without_ts = false;
|
||||
int cmp_ts = 0;
|
||||
if (has_current_user_key_) {
|
||||
cmp_user_key_without_ts =
|
||||
timestamp_size_
|
||||
? cmp_->CompareWithoutTimestamp(ikey_.user_key, current_user_key_)
|
||||
: cmp_->Compare(ikey_.user_key, current_user_key_);
|
||||
user_key_equal_without_ts =
|
||||
cmp_->EqualWithoutTimestamp(ikey_.user_key, current_user_key_);
|
||||
// if timestamp_size_ > 0, then curr_ts_ has been initialized by a
|
||||
// previous key.
|
||||
cmp_ts = timestamp_size_ ? cmp_->CompareTimestamp(
|
||||
@@ -334,7 +407,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// Check whether the user key changed. After this if statement current_key_
|
||||
// is a copy of the current input key (maybe converted to a delete by the
|
||||
// compaction filter). ikey_.user_key is pointing to the copy.
|
||||
if (!has_current_user_key_ || cmp_user_key_without_ts != 0 || cmp_ts != 0) {
|
||||
if (!has_current_user_key_ || !user_key_equal_without_ts || cmp_ts != 0) {
|
||||
// First occurrence of this user key
|
||||
// Copy key for output
|
||||
key_ = current_key_.SetInternalKey(key_, &ikey_);
|
||||
@@ -355,7 +428,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// consider this key for GC, e.g. it may be dropped if certain conditions
|
||||
// match.
|
||||
if (!has_current_user_key_ || !timestamp_size_ || !full_history_ts_low_ ||
|
||||
0 != cmp_user_key_without_ts || cmp_with_history_ts_low_ >= 0) {
|
||||
!user_key_equal_without_ts || cmp_with_history_ts_low_ >= 0) {
|
||||
// Initialize for future comparison for rule (A) and etc.
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
@@ -649,8 +722,7 @@ void CompactionIterator::NextFromInput() {
|
||||
input_->Valid() &&
|
||||
(ParseInternalKey(input_->key(), &next_ikey, allow_data_in_errors_)
|
||||
.ok()) &&
|
||||
0 == cmp_->CompareWithoutTimestamp(ikey_.user_key,
|
||||
next_ikey.user_key) &&
|
||||
cmp_->EqualWithoutTimestamp(ikey_.user_key, next_ikey.user_key) &&
|
||||
(prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot))) {
|
||||
input_->Next();
|
||||
@@ -660,8 +732,7 @@ void CompactionIterator::NextFromInput() {
|
||||
if (input_->Valid() &&
|
||||
(ParseInternalKey(input_->key(), &next_ikey, allow_data_in_errors_)
|
||||
.ok()) &&
|
||||
0 == cmp_->CompareWithoutTimestamp(ikey_.user_key,
|
||||
next_ikey.user_key)) {
|
||||
cmp_->EqualWithoutTimestamp(ikey_.user_key, next_ikey.user_key)) {
|
||||
valid_ = true;
|
||||
at_next_ = true;
|
||||
}
|
||||
@@ -814,9 +885,11 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
const Version* const version = compaction_->input_version();
|
||||
assert(version);
|
||||
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
{
|
||||
const Status s =
|
||||
version->GetBlob(ReadOptions(), user_key(), blob_index, &blob_value_);
|
||||
const Status s = version->GetBlob(ReadOptions(), user_key(), blob_index,
|
||||
&blob_value_, &bytes_read);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
@@ -826,6 +899,9 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
}
|
||||
}
|
||||
|
||||
++iter_stats_.num_blobs_read;
|
||||
iter_stats_.total_blob_bytes_read += bytes_read;
|
||||
|
||||
value_ = blob_value_;
|
||||
|
||||
if (ExtractLargeValueIfNeededImpl()) {
|
||||
@@ -839,7 +915,8 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
}
|
||||
|
||||
// GC for stacked BlobDB
|
||||
if (compaction_filter_) {
|
||||
if (compaction_filter_ &&
|
||||
compaction_filter_->IsStackedBlobDbInternalCompactionFilter()) {
|
||||
const auto blob_decision = compaction_filter_->PrepareBlobOutput(
|
||||
user_key(), value_, &compaction_filter_value_);
|
||||
|
||||
|
||||
@@ -248,6 +248,7 @@ class CompactionIterator {
|
||||
const SequenceNumber earliest_write_conflict_snapshot_;
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
bool report_detailed_time_;
|
||||
bool expect_valid_internal_key_;
|
||||
CompactionRangeDelAggregator* range_del_agg_;
|
||||
|
||||
@@ -310,7 +310,8 @@ CompactionJob::CompactionJob(
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* manual_compaction_paused, const std::string& db_id,
|
||||
const std::string& db_session_id, std::string full_history_ts_low)
|
||||
const std::string& db_session_id, std::string full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback)
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_job_stats_(compaction_job_stats),
|
||||
@@ -346,7 +347,8 @@ CompactionJob::CompactionJob(
|
||||
measure_io_stats_(measure_io_stats),
|
||||
write_hint_(Env::WLTH_NOT_SET),
|
||||
thread_pri_(thread_pri),
|
||||
full_history_ts_low_(std::move(full_history_ts_low)) {
|
||||
full_history_ts_low_(std::move(full_history_ts_low)),
|
||||
blob_callback_(blob_callback) {
|
||||
assert(compaction_job_stats_ != nullptr);
|
||||
assert(log_buffer_ != nullptr);
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
@@ -420,7 +422,7 @@ void CompactionJob::Prepare() {
|
||||
|
||||
if (c->ShouldFormSubcompactions()) {
|
||||
{
|
||||
StopWatch sw(env_, stats_, SUBCOMPACTION_SETUP_TIME);
|
||||
StopWatch sw(db_options_.clock, stats_, SUBCOMPACTION_SETUP_TIME);
|
||||
GenSubcompactionBoundaries();
|
||||
}
|
||||
assert(sizes_.size() == boundaries_.size() + 1);
|
||||
@@ -586,7 +588,7 @@ Status CompactionJob::Run() {
|
||||
|
||||
const size_t num_threads = compact_->sub_compact_states.size();
|
||||
assert(num_threads > 0);
|
||||
const uint64_t start_micros = env_->NowMicros();
|
||||
const uint64_t start_micros = db_options_.clock->NowMicros();
|
||||
|
||||
// Launch a thread for each of subcompactions 1...num_threads-1
|
||||
std::vector<port::Thread> thread_pool;
|
||||
@@ -605,7 +607,7 @@ Status CompactionJob::Run() {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
compaction_stats_.micros = env_->NowMicros() - start_micros;
|
||||
compaction_stats_.micros = db_options_.clock->NowMicros() - start_micros;
|
||||
compaction_stats_.cpu_micros = 0;
|
||||
for (size_t i = 0; i < compact_->sub_compact_states.size(); i++) {
|
||||
compaction_stats_.cpu_micros +=
|
||||
@@ -793,39 +795,45 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
double bytes_read_per_sec = 0;
|
||||
double bytes_written_per_sec = 0;
|
||||
|
||||
if (stats.bytes_read_non_output_levels > 0) {
|
||||
read_write_amp = (stats.bytes_written + stats.bytes_read_output_level +
|
||||
stats.bytes_read_non_output_levels) /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels);
|
||||
write_amp = stats.bytes_written /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels);
|
||||
const uint64_t bytes_read_non_output_and_blob =
|
||||
stats.bytes_read_non_output_levels + stats.bytes_read_blob;
|
||||
const uint64_t bytes_read_all =
|
||||
stats.bytes_read_output_level + bytes_read_non_output_and_blob;
|
||||
const uint64_t bytes_written_all =
|
||||
stats.bytes_written + stats.bytes_written_blob;
|
||||
|
||||
if (bytes_read_non_output_and_blob > 0) {
|
||||
read_write_amp = (bytes_written_all + bytes_read_all) /
|
||||
static_cast<double>(bytes_read_non_output_and_blob);
|
||||
write_amp =
|
||||
bytes_written_all / static_cast<double>(bytes_read_non_output_and_blob);
|
||||
}
|
||||
if (stats.micros > 0) {
|
||||
bytes_read_per_sec =
|
||||
(stats.bytes_read_non_output_levels + stats.bytes_read_output_level) /
|
||||
static_cast<double>(stats.micros);
|
||||
bytes_read_per_sec = bytes_read_all / static_cast<double>(stats.micros);
|
||||
bytes_written_per_sec =
|
||||
stats.bytes_written / static_cast<double>(stats.micros);
|
||||
bytes_written_all / static_cast<double>(stats.micros);
|
||||
}
|
||||
|
||||
const std::string& column_family_name = cfd->GetName();
|
||||
|
||||
constexpr double kMB = 1048576.0;
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
"files in(%d, %d) out(%d) "
|
||||
"MB in(%.1f, %.1f) out(%.1f), read-write-amplify(%.1f) "
|
||||
"write-amplify(%.1f) %s, records in: %" PRIu64
|
||||
"files in(%d, %d) out(%d +%d blob) "
|
||||
"MB in(%.1f, %.1f +%.1f blob) out(%.1f +%.1f blob), "
|
||||
"read-write-amplify(%.1f) write-amplify(%.1f) %s, records in: %" PRIu64
|
||||
", records dropped: %" PRIu64 " output_compression: %s\n",
|
||||
column_family_name.c_str(), vstorage->LevelSummary(&tmp),
|
||||
bytes_read_per_sec, bytes_written_per_sec,
|
||||
compact_->compaction->output_level(),
|
||||
stats.num_input_files_in_non_output_levels,
|
||||
stats.num_input_files_in_output_level, stats.num_output_files,
|
||||
stats.bytes_read_non_output_levels / 1048576.0,
|
||||
stats.bytes_read_output_level / 1048576.0,
|
||||
stats.bytes_written / 1048576.0, read_write_amp, write_amp,
|
||||
status.ToString().c_str(), stats.num_input_records,
|
||||
stats.num_output_files_blob, stats.bytes_read_non_output_levels / kMB,
|
||||
stats.bytes_read_output_level / kMB, stats.bytes_read_blob / kMB,
|
||||
stats.bytes_written / kMB, stats.bytes_written_blob / kMB, read_write_amp,
|
||||
write_amp, status.ToString().c_str(), stats.num_input_records,
|
||||
stats.num_dropped_records,
|
||||
CompressionTypeToString(compact_->compaction->output_compression())
|
||||
.c_str());
|
||||
@@ -895,7 +903,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact);
|
||||
assert(sub_compact->compaction);
|
||||
|
||||
uint64_t prev_cpu_micros = env_->NowCPUNanos() / 1000;
|
||||
uint64_t prev_cpu_micros = db_options_.clock->CPUNanos() / 1000;
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
|
||||
@@ -972,12 +980,13 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
std::unique_ptr<BlobFileBuilder> blob_file_builder(
|
||||
mutable_cf_options->enable_blob_files
|
||||
? new BlobFileBuilder(
|
||||
versions_, env_, fs_.get(),
|
||||
sub_compact->compaction->immutable_cf_options(),
|
||||
mutable_cf_options, &file_options_, job_id_, cfd->GetID(),
|
||||
cfd->GetName(), Env::IOPriority::IO_LOW, write_hint_,
|
||||
io_tracer_, &blob_file_paths, &sub_compact->blob_file_additions)
|
||||
? new BlobFileBuilder(versions_, fs_.get(),
|
||||
sub_compact->compaction->immutable_cf_options(),
|
||||
mutable_cf_options, &file_options_, job_id_,
|
||||
cfd->GetID(), cfd->GetName(),
|
||||
Env::IOPriority::IO_LOW, write_hint_,
|
||||
io_tracer_, blob_callback_, &blob_file_paths,
|
||||
&sub_compact->blob_file_additions)
|
||||
: nullptr);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
@@ -1120,6 +1129,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
}
|
||||
|
||||
sub_compact->compaction_job_stats.num_blobs_read =
|
||||
c_iter_stats.num_blobs_read;
|
||||
sub_compact->compaction_job_stats.total_blob_bytes_read =
|
||||
c_iter_stats.total_blob_bytes_read;
|
||||
sub_compact->compaction_job_stats.num_input_deletion_records =
|
||||
c_iter_stats.num_input_deletion_records;
|
||||
sub_compact->compaction_job_stats.num_corrupt_keys =
|
||||
@@ -1179,13 +1192,14 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
if (blob_file_builder) {
|
||||
if (status.ok()) {
|
||||
status = blob_file_builder->Finish();
|
||||
} else {
|
||||
blob_file_builder->Abandon();
|
||||
}
|
||||
|
||||
blob_file_builder.reset();
|
||||
}
|
||||
|
||||
sub_compact->compaction_job_stats.cpu_micros =
|
||||
env_->NowCPUNanos() / 1000 - prev_cpu_micros;
|
||||
db_options_.clock->CPUNanos() / 1000 - prev_cpu_micros;
|
||||
|
||||
if (measure_io_stats_) {
|
||||
sub_compact->compaction_job_stats.file_write_nanos +=
|
||||
@@ -1464,7 +1478,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
|
||||
// Finish and check for file errors
|
||||
if (s.ok()) {
|
||||
StopWatch sw(env_, stats_, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
StopWatch sw(db_options_.clock, stats_, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
io_s = sub_compact->outfile->Sync(db_options_.use_fsync);
|
||||
}
|
||||
if (s.ok() && io_s.ok()) {
|
||||
@@ -1703,7 +1717,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
|
||||
// Try to figure out the output file's oldest ancester time.
|
||||
int64_t temp_current_time = 0;
|
||||
auto get_time_status = env_->GetCurrentTime(&temp_current_time);
|
||||
auto get_time_status = db_options_.clock->GetCurrentTime(&temp_current_time);
|
||||
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
|
||||
if (!get_time_status.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
@@ -1740,8 +1754,8 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
const auto& listeners =
|
||||
sub_compact->compaction->immutable_cf_options()->listeners;
|
||||
sub_compact->outfile.reset(new WritableFileWriter(
|
||||
std::move(writable_file), fname, file_options_, env_, io_tracer_,
|
||||
db_options_.statistics.get(), listeners,
|
||||
std::move(writable_file), fname, file_options_, db_options_.clock,
|
||||
io_tracer_, db_options_.statistics.get(), listeners,
|
||||
db_options_.file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kTableFile)));
|
||||
|
||||
@@ -1823,11 +1837,16 @@ void CompactionJob::UpdateCompactionStats() {
|
||||
}
|
||||
}
|
||||
|
||||
assert(compaction_job_stats_);
|
||||
compaction_stats_.bytes_read_blob =
|
||||
compaction_job_stats_->total_blob_bytes_read;
|
||||
|
||||
compaction_stats_.num_output_files =
|
||||
static_cast<int>(compact_->num_output_files) +
|
||||
static_cast<int>(compact_->num_output_files);
|
||||
compaction_stats_.num_output_files_blob =
|
||||
static_cast<int>(compact_->num_blob_output_files);
|
||||
compaction_stats_.bytes_written =
|
||||
compact_->total_bytes + compact_->total_blob_bytes;
|
||||
compaction_stats_.bytes_written = compact_->total_bytes;
|
||||
compaction_stats_.bytes_written_blob = compact_->total_blob_bytes;
|
||||
|
||||
if (compaction_stats_.num_input_records > compact_->num_output_records) {
|
||||
compaction_stats_.num_dropped_records =
|
||||
@@ -1867,8 +1886,10 @@ void CompactionJob::UpdateCompactionJobStats(
|
||||
|
||||
// output information
|
||||
compaction_job_stats_->total_output_bytes = stats.bytes_written;
|
||||
compaction_job_stats_->total_output_bytes_blob = stats.bytes_written_blob;
|
||||
compaction_job_stats_->num_output_records = compact_->num_output_records;
|
||||
compaction_job_stats_->num_output_files = stats.num_output_files;
|
||||
compaction_job_stats_->num_output_files_blob = stats.num_output_files_blob;
|
||||
|
||||
if (stats.num_output_files > 0) {
|
||||
CopyPrefix(compact_->SmallestUserKey(),
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_file_completion_callback.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
@@ -50,6 +51,7 @@ class Arena;
|
||||
class ErrorHandler;
|
||||
class MemTable;
|
||||
class SnapshotChecker;
|
||||
class SystemClock;
|
||||
class TableCache;
|
||||
class Version;
|
||||
class VersionEdit;
|
||||
@@ -79,7 +81,8 @@ class CompactionJob {
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "",
|
||||
std::string full_history_ts_low = "");
|
||||
std::string full_history_ts_low = "",
|
||||
BlobFileCompletionCallback* blob_callback = nullptr);
|
||||
|
||||
~CompactionJob();
|
||||
|
||||
@@ -203,6 +206,7 @@ class CompactionJob {
|
||||
Env::Priority thread_pri_;
|
||||
IOStatus io_status_;
|
||||
std::string full_history_ts_low_;
|
||||
BlobFileCompletionCallback* blob_callback_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/compaction/compaction_job.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
@@ -14,13 +16,13 @@
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/mock_table.h"
|
||||
@@ -277,12 +279,13 @@ class CompactionJobTestBase : public testing::Test {
|
||||
new_db.SetLastSequence(0);
|
||||
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
std::unique_ptr<WritableFile> file;
|
||||
Status s = env_->NewWritableFile(
|
||||
manifest, &file, env_->OptimizeForManifestWrite(env_options_));
|
||||
std::unique_ptr<WritableFileWriter> file_writer;
|
||||
const auto& fs = env_->GetFileSystem();
|
||||
Status s = WritableFileWriter::Create(
|
||||
fs, manifest, fs->OptimizeForManifestWrite(env_options_), &file_writer,
|
||||
nullptr);
|
||||
|
||||
ASSERT_OK(s);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
NewLegacyWritableFileWrapper(std::move(file)), manifest, env_options_));
|
||||
{
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
|
||||
@@ -45,7 +45,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
uint64_t total_size = GetTotalFilesSize(level_files);
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions_.env->GetCurrentTime(&_current_time);
|
||||
auto status = ioptions_.clock->GetCurrentTime(&_current_time);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: Couldn't get current time: %s. "
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "file/filename.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
@@ -539,14 +538,15 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
ASSERT_EQ(static_cast<size_t>(1), metadata.size());
|
||||
std::string filename = dbname_ + metadata[0].name;
|
||||
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
ASSERT_OK(options_.env->NewRandomAccessFile(filename, &file, EnvOptions()));
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(NewLegacyRandomAccessFileWrapper(file),
|
||||
filename));
|
||||
FileOptions file_opts;
|
||||
const auto& fs = options_.env->GetFileSystem();
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader;
|
||||
ASSERT_OK(RandomAccessFileReader::Create(fs, filename, file_opts,
|
||||
&file_reader, nullptr));
|
||||
|
||||
uint64_t file_size;
|
||||
ASSERT_OK(options_.env->GetFileSize(filename, &file_size));
|
||||
ASSERT_OK(
|
||||
fs->GetFileSize(filename, file_opts.io_options, &file_size, nullptr));
|
||||
|
||||
BlockHandle range_del_handle;
|
||||
ASSERT_OK(FindMetaBlock(
|
||||
@@ -882,7 +882,7 @@ TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
int count{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::VerifySstFileChecksum:mismatch", [&](void* arg) {
|
||||
"DBImpl::VerifyFullFileChecksum:mismatch", [&](void* arg) {
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
ASSERT_NE(s, nullptr);
|
||||
++count;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -2117,6 +2120,54 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, SeekForPrevWithPartitionedFilters) {
|
||||
Options options = CurrentOptions();
|
||||
constexpr size_t kNumKeys = 10000;
|
||||
static_assert(kNumKeys <= 10000, "kNumKeys have to be <= 10000");
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(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');
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+97
-43
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/concurrent_task_limiter.h"
|
||||
@@ -32,31 +33,6 @@ class DBCompactionTest : public DBTestBase {
|
||||
public:
|
||||
DBCompactionTest()
|
||||
: DBTestBase("/db_compaction_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::vector<uint64_t> GetBlobFileNumbers() {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
assert(current);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
|
||||
std::vector<uint64_t> result;
|
||||
result.reserve(blob_files.size());
|
||||
|
||||
for (const auto& blob_file : blob_files) {
|
||||
result.emplace_back(blob_file.first);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class DBCompactionTestWithParam
|
||||
@@ -205,6 +181,7 @@ Options DeletionTriggerOptions(Options options) {
|
||||
options.target_file_size_base * options.target_file_size_multiplier;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.disable_auto_compactions = false;
|
||||
options.compaction_options_universal.max_size_amplification_percent = 100;
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -359,12 +336,34 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
|
||||
for (int k = 0; k < kTestSize; ++k) {
|
||||
ASSERT_OK(Delete(Key(k)));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[1]));
|
||||
|
||||
// must have much smaller db size.
|
||||
ASSERT_GT(db_size[0] / 3, db_size[1]);
|
||||
if (options.compaction_style == kCompactionStyleUniversal) {
|
||||
// Claim: in universal compaction none of the original data will remain
|
||||
// once compactions settle.
|
||||
//
|
||||
// Proof: The compensated size of the file containing the most tombstones
|
||||
// is enough on its own to trigger size amp compaction. Size amp
|
||||
// compaction is a full compaction, so all tombstones meet the obsolete
|
||||
// keys they cover.
|
||||
ASSERT_EQ(0, db_size[1]);
|
||||
} else {
|
||||
// Claim: in level compaction at most `db_size[0] / 2` of the original
|
||||
// data will remain once compactions settle.
|
||||
//
|
||||
// Proof: Assume the original data is all in the bottom level. If it were
|
||||
// not, it would meet its tombstone sooner. The original data size is
|
||||
// large enough to require fanout to bottom level to be greater than
|
||||
// `max_bytes_for_level_multiplier == 2`. In the level just above,
|
||||
// tombstones must cover less than `db_size[0] / 4` bytes since fanout >=
|
||||
// 2 and file size is compensated by doubling the size of values we expect
|
||||
// are covered (`kDeletionWeightOnCompaction == 2`). The tombstones in
|
||||
// levels above must cover less than `db_size[0] / 8` bytes of original
|
||||
// data, `db_size[0] / 16`, and so on.
|
||||
ASSERT_GT(db_size[0] / 2, db_size[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
@@ -631,9 +630,8 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTriggerReopen) {
|
||||
}
|
||||
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[1]));
|
||||
Close();
|
||||
// as auto_compaction is off, we shouldn't see too much reduce
|
||||
// in db size.
|
||||
ASSERT_LT(db_size[0] / 3, db_size[1]);
|
||||
// as auto_compaction is off, we shouldn't see any reduction in db size.
|
||||
ASSERT_LE(db_size[0], db_size[1]);
|
||||
|
||||
// round 3 --- reopen db with auto_compaction on and see if
|
||||
// deletion compensation still work.
|
||||
@@ -647,7 +645,13 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTriggerReopen) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[2]));
|
||||
// this time we're expecting significant drop in size.
|
||||
ASSERT_GT(db_size[0] / 3, db_size[2]);
|
||||
//
|
||||
// See "CompactionDeletionTrigger" test for proof that at most
|
||||
// `db_size[0] / 2` of the original data remains. In addition to that, this
|
||||
// test inserts `db_size[0] / 10` to push the tombstones into SST files and
|
||||
// then through automatic compactions. So in total `3 * db_size[0] / 5` of
|
||||
// the original data may remain.
|
||||
ASSERT_GT(3 * db_size[0] / 5, db_size[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,8 +738,15 @@ TEST_F(DBCompactionTest, DisableStatsUpdateReopen) {
|
||||
values.push_back(rnd.RandomString(kCDTValueSize));
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
// L1 and L2 can fit deletions iff size compensation does not take effect,
|
||||
// i.e., when `skip_stats_update_on_db_open == true`. Move any remaining
|
||||
// files at or above L2 down to L3 to ensure obsolete data does not
|
||||
// accidentally meet its tombstone above L3. This makes the final size more
|
||||
// deterministic and easy to see whether size compensation for deletions
|
||||
// took effect.
|
||||
MoveFilesToLevel(3 /* level */);
|
||||
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[0]));
|
||||
Close();
|
||||
|
||||
@@ -751,9 +762,8 @@ TEST_F(DBCompactionTest, DisableStatsUpdateReopen) {
|
||||
}
|
||||
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[1]));
|
||||
Close();
|
||||
// as auto_compaction is off, we shouldn't see too much reduce
|
||||
// in db size.
|
||||
ASSERT_LT(db_size[0] / 3, db_size[1]);
|
||||
// as auto_compaction is off, we shouldn't see any reduction in db size.
|
||||
ASSERT_LE(db_size[0], db_size[1]);
|
||||
|
||||
// round 3 --- reopen db with auto_compaction on and see if
|
||||
// deletion compensation still work.
|
||||
@@ -766,10 +776,17 @@ TEST_F(DBCompactionTest, DisableStatsUpdateReopen) {
|
||||
if (options.skip_stats_update_on_db_open) {
|
||||
// If update stats on DB::Open is disable, we don't expect
|
||||
// deletion entries taking effect.
|
||||
ASSERT_LT(db_size[0] / 3, db_size[2]);
|
||||
//
|
||||
// The deletions are small enough to fit in L1 and L2, and obsolete keys
|
||||
// were moved to L3+, so none of the original data should have been
|
||||
// dropped.
|
||||
ASSERT_LE(db_size[0], db_size[2]);
|
||||
} else {
|
||||
// Otherwise, we should see a significant drop in db size.
|
||||
ASSERT_GT(db_size[0] / 3, db_size[2]);
|
||||
//
|
||||
// See "CompactionDeletionTrigger" test for proof that at most
|
||||
// `db_size[0] / 2` of the original data remains.
|
||||
ASSERT_GT(db_size[0] / 2, db_size[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5945,13 +5962,14 @@ TEST_F(DBCompactionTest, CompactionWithBlob) {
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
ASSERT_NE(internal_stats, nullptr);
|
||||
|
||||
const uint64_t expected_bytes =
|
||||
table_file->fd.GetFileSize() + blob_file->GetTotalBlobBytes();
|
||||
|
||||
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written, expected_bytes);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files, 2);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written, table_file->fd.GetFileSize());
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written_blob,
|
||||
blob_file->GetTotalBlobBytes());
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files, 1);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files_blob, 1);
|
||||
}
|
||||
|
||||
class DBCompactionTestBlobError
|
||||
@@ -6038,12 +6056,18 @@ TEST_P(DBCompactionTestBlobError, CompactionError) {
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
|
||||
if (sync_point_ == "BlobFileBuilder::WriteBlobToFile:AddRecord") {
|
||||
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files, 0);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files_blob, 0);
|
||||
} else {
|
||||
// SST file writing succeeded; blob file writing failed (during Finish)
|
||||
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_GT(compaction_stats[1].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files, 1);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files_blob, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6128,6 +6152,36 @@ TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGC) {
|
||||
for (size_t i = cutoff_index; i < original_blob_files.size(); ++i) {
|
||||
ASSERT_EQ(new_blob_files[i - cutoff_index], original_blob_files[i]);
|
||||
}
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
assert(internal_stats);
|
||||
|
||||
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
|
||||
if (blob_gc_age_cutoff_ > 0.0) {
|
||||
ASSERT_GT(compaction_stats[1].bytes_read_blob, 0);
|
||||
|
||||
if (updated_enable_blob_files_) {
|
||||
// GC relocated some blobs to new blob files
|
||||
ASSERT_GT(compaction_stats[1].bytes_written_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_read_blob,
|
||||
compaction_stats[1].bytes_written_blob);
|
||||
} else {
|
||||
// GC moved some blobs back to the LSM, no new blob files
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
|
||||
}
|
||||
} else {
|
||||
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionWithBlobGCError_CorruptIndex) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/random.h"
|
||||
|
||||
+16
-24
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -81,29 +82,16 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncFail:GetVersionRefCount:1",
|
||||
"DBImpl::FlushMemTableToOutputFile:BeforePickMemtables"},
|
||||
{"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables",
|
||||
"DBFlushTest::SyncFail:GetVersionRefCount:2"},
|
||||
{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{"DBImpl::SyncClosedLogs:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(Put("key", "value"));
|
||||
auto* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(db_->DefaultColumnFamily())
|
||||
->cfd();
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = false;
|
||||
ASSERT_OK(dbfull()->Flush(flush_options));
|
||||
// Flush installs a new super-version. Get the ref count after that.
|
||||
auto current_before = cfd->current();
|
||||
int refs_before = cfd->current()->TEST_refs();
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:GetVersionRefCount:1");
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:GetVersionRefCount:2");
|
||||
int refs_after_picking_memtables = cfd->current()->TEST_refs();
|
||||
ASSERT_EQ(refs_before + 1, refs_after_picking_memtables);
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:1");
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:2");
|
||||
@@ -114,9 +102,6 @@ TEST_F(DBFlushTest, SyncFail) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("", FilesPerLevel()); // flush failed.
|
||||
#endif // ROCKSDB_LITE
|
||||
// Backgroun flush job should release ref count to current version.
|
||||
ASSERT_EQ(current_before, cfd->current());
|
||||
ASSERT_EQ(refs_before, cfd->current()->TEST_refs());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
@@ -512,16 +497,18 @@ TEST_F(DBFlushTest, FlushWithBlob) {
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
assert(internal_stats);
|
||||
|
||||
const uint64_t expected_bytes =
|
||||
table_file->fd.GetFileSize() + blob_file->GetTotalBlobBytes();
|
||||
|
||||
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
|
||||
ASSERT_FALSE(compaction_stats.empty());
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written, expected_bytes);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 2);
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written, table_file->fd.GetFileSize());
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written_blob,
|
||||
blob_file->GetTotalBlobBytes());
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 1);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files_blob, 1);
|
||||
|
||||
const uint64_t* const cf_stats_value = internal_stats->TEST_GetCFStatsValue();
|
||||
ASSERT_EQ(cf_stats_value[InternalStats::BYTES_FLUSHED], expected_bytes);
|
||||
ASSERT_EQ(cf_stats_value[InternalStats::BYTES_FLUSHED],
|
||||
compaction_stats[0].bytes_written +
|
||||
compaction_stats[0].bytes_written_blob);
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -801,16 +788,21 @@ TEST_P(DBFlushTestBlobError, FlushError) {
|
||||
|
||||
if (sync_point_ == "BlobFileBuilder::WriteBlobToFile:AddRecord") {
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 0);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files_blob, 0);
|
||||
} else {
|
||||
// SST file writing succeeded; blob file writing failed (during Finish)
|
||||
ASSERT_GT(compaction_stats[0].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written_blob, 0);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 1);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files_blob, 0);
|
||||
}
|
||||
|
||||
const uint64_t* const cf_stats_value = internal_stats->TEST_GetCFStatsValue();
|
||||
ASSERT_EQ(cf_stats_value[InternalStats::BYTES_FLUSHED],
|
||||
compaction_stats[0].bytes_written);
|
||||
compaction_stats[0].bytes_written +
|
||||
compaction_stats[0].bytes_written_blob);
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
|
||||
+79
-41
@@ -156,7 +156,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
fs_(immutable_db_options_.fs, io_tracer_),
|
||||
mutable_db_options_(initial_db_options_),
|
||||
stats_(immutable_db_options_.statistics.get()),
|
||||
mutex_(stats_, env_, DB_MUTEX_WAIT_MICROS,
|
||||
mutex_(stats_, immutable_db_options_.clock, DB_MUTEX_WAIT_MICROS,
|
||||
immutable_db_options_.use_adaptive_mutex),
|
||||
default_cf_handle_(nullptr),
|
||||
max_total_in_memory_state_(0),
|
||||
@@ -191,7 +191,7 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
bg_purge_scheduled_(0),
|
||||
disable_delete_obsolete_files_(0),
|
||||
pending_purge_obsolete_files_(0),
|
||||
delete_obsolete_files_last_run_(env_->NowMicros()),
|
||||
delete_obsolete_files_last_run_(immutable_db_options_.clock->NowMicros()),
|
||||
last_stats_dump_time_microsec_(0),
|
||||
next_job_id_(1),
|
||||
has_unpersisted_data_(false),
|
||||
@@ -231,7 +231,9 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
preserve_deletes_(options.preserve_deletes),
|
||||
closed_(false),
|
||||
error_handler_(this, immutable_db_options_, &mutex_),
|
||||
atomic_flush_install_cv_(&mutex_) {
|
||||
atomic_flush_install_cv_(&mutex_),
|
||||
blob_callback_(immutable_db_options_.sst_file_manager.get(), &mutex_,
|
||||
&error_handler_) {
|
||||
// !batch_per_trx_ implies seq_per_batch_ because it is only unset for
|
||||
// WriteUnprepared, which should use seq_per_batch_.
|
||||
assert(batch_per_txn_ || seq_per_batch_);
|
||||
@@ -511,6 +513,11 @@ Status DBImpl::CloseHelper() {
|
||||
}
|
||||
mutex_.Unlock();
|
||||
|
||||
// Below check is added as recovery_error_ is not checked and it causes crash
|
||||
// in DBSSTTest.DBWithMaxSpaceAllowedWithBlobFiles when space limit is
|
||||
// reached.
|
||||
error_handler_.GetRecoveryError().PermitUncheckedError();
|
||||
|
||||
// CancelAllBackgroundWork called with false means we just set the shutdown
|
||||
// marker. After this we do a variant of the waiting and unschedule work
|
||||
// (to consider: moving all the waiting into CancelAllBackgroundWork(true))
|
||||
@@ -752,7 +759,8 @@ void DBImpl::PersistStats() {
|
||||
return;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::PersistStats:StartRunning");
|
||||
uint64_t now_seconds = env_->NowMicros() / kMicrosInSecond;
|
||||
uint64_t now_seconds =
|
||||
immutable_db_options_.clock->NowMicros() / kMicrosInSecond;
|
||||
|
||||
Statistics* statistics = immutable_db_options_.statistics.get();
|
||||
if (!statistics) {
|
||||
@@ -1653,8 +1661,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_GET);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(
|
||||
@@ -1844,8 +1852,8 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values,
|
||||
std::vector<std::string>* timestamps) {
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_MULTIGET);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_MULTIGET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -1974,9 +1982,8 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_options.deadline.count() &&
|
||||
env_->NowMicros() >
|
||||
immutable_db_options_.clock->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count())) {
|
||||
break;
|
||||
}
|
||||
@@ -1985,8 +1992,8 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
if (keys_read < num_keys) {
|
||||
// The only reason to break out of the loop is when the deadline is
|
||||
// exceeded
|
||||
assert(env_->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count()));
|
||||
assert(immutable_db_options_.clock->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count()));
|
||||
for (++keys_read; keys_read < num_keys; ++keys_read) {
|
||||
stat_list[keys_read] = Status::TimedOut();
|
||||
}
|
||||
@@ -2425,8 +2432,8 @@ Status DBImpl::MultiGetImpl(
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys,
|
||||
SuperVersion* super_version, SequenceNumber snapshot,
|
||||
ReadCallback* callback) {
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_MULTIGET);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_MULTIGET);
|
||||
|
||||
// For each of the given keys, apply the entire "get" process as follows:
|
||||
// First look in the memtable, then in the immutable memtable (if any).
|
||||
@@ -2437,7 +2444,7 @@ Status DBImpl::MultiGetImpl(
|
||||
uint64_t curr_value_size = 0;
|
||||
while (keys_left) {
|
||||
if (read_options.deadline.count() &&
|
||||
env_->NowMicros() >
|
||||
immutable_db_options_.clock->NowMicros() >
|
||||
static_cast<uint64_t>(read_options.deadline.count())) {
|
||||
s = Status::TimedOut();
|
||||
break;
|
||||
@@ -2990,7 +2997,8 @@ const Snapshot* DBImpl::GetSnapshotForWriteConflictBoundary() {
|
||||
SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
bool lock) {
|
||||
int64_t unix_time = 0;
|
||||
env_->GetCurrentTime(&unix_time).PermitUncheckedError(); // Ignore error
|
||||
immutable_db_options_.clock->GetCurrentTime(&unix_time)
|
||||
.PermitUncheckedError(); // Ignore error
|
||||
SnapshotImpl* s = new SnapshotImpl;
|
||||
|
||||
if (lock) {
|
||||
@@ -3135,12 +3143,17 @@ FileSystem* DBImpl::GetFileSystem() const {
|
||||
return immutable_db_options_.fs.get();
|
||||
}
|
||||
|
||||
SystemClock* DBImpl::GetSystemClock() const {
|
||||
return immutable_db_options_.clock;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status DBImpl::StartIOTrace(Env* env, const TraceOptions& trace_options,
|
||||
Status DBImpl::StartIOTrace(Env* /*env*/, const TraceOptions& trace_options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) {
|
||||
assert(trace_writer != nullptr);
|
||||
return io_tracer_->StartIOTrace(env, trace_options, std::move(trace_writer));
|
||||
return io_tracer_->StartIOTrace(GetSystemClock(), trace_options,
|
||||
std::move(trace_writer));
|
||||
}
|
||||
|
||||
Status DBImpl::EndIOTrace() {
|
||||
@@ -3937,7 +3950,8 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
std::string path_to_delete = dbname + "/" + fname;
|
||||
if (type == kMetaDatabase) {
|
||||
del = DestroyDB(path_to_delete, options);
|
||||
} else if (type == kTableFile || type == kWalFile) {
|
||||
} else if (type == kTableFile || type == kWalFile ||
|
||||
type == kBlobFile) {
|
||||
del = DeleteDBFile(&soptions, path_to_delete, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
|
||||
} else {
|
||||
@@ -3962,9 +3976,10 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
if (env->GetChildren(path, &filenames).ok()) {
|
||||
for (const auto& fname : filenames) {
|
||||
if (ParseFileName(fname, &number, &type) &&
|
||||
type == kTableFile) { // Lock file will be deleted at end
|
||||
std::string table_path = path + "/" + fname;
|
||||
Status del = DeleteDBFile(&soptions, table_path, dbname,
|
||||
(type == kTableFile ||
|
||||
type == kBlobFile)) { // Lock file will be deleted at end
|
||||
std::string file_path = path + "/" + fname;
|
||||
Status del = DeleteDBFile(&soptions, file_path, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/false);
|
||||
if (!del.ok() && result.ok()) {
|
||||
result = del;
|
||||
@@ -4420,9 +4435,9 @@ Status DBImpl::IngestExternalFiles(
|
||||
std::vector<ExternalSstFileIngestionJob> ingestion_jobs;
|
||||
for (const auto& arg : args) {
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(arg.column_family)->cfd();
|
||||
ingestion_jobs.emplace_back(
|
||||
env_, versions_.get(), cfd, immutable_db_options_, file_options_,
|
||||
&snapshots_, arg.options, &directories_, &event_logger_, io_tracer_);
|
||||
ingestion_jobs.emplace_back(versions_.get(), cfd, immutable_db_options_,
|
||||
file_options_, &snapshots_, arg.options,
|
||||
&directories_, &event_logger_, io_tracer_);
|
||||
}
|
||||
|
||||
// TODO(yanqin) maybe make jobs run in parallel
|
||||
@@ -4689,9 +4704,9 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
// Import sst files from metadata.
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(*handle);
|
||||
auto cfd = cfh->cfd();
|
||||
ImportColumnFamilyJob import_job(env_, versions_.get(), cfd,
|
||||
immutable_db_options_, file_options_,
|
||||
import_options, metadata.files, io_tracer_);
|
||||
ImportColumnFamilyJob import_job(versions_.get(), cfd, immutable_db_options_,
|
||||
file_options_, import_options,
|
||||
metadata.files, io_tracer_);
|
||||
|
||||
SuperVersionContext dummy_sv_ctx(/* create_superversion */ true);
|
||||
VersionEdit dummy_edit;
|
||||
@@ -4853,17 +4868,37 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
std::string fname = TableFileName(cfd->ioptions()->cf_paths,
|
||||
fd.GetNumber(), fd.GetPathId());
|
||||
if (use_file_checksum) {
|
||||
s = VerifySstFileChecksum(*fmeta, fname, read_options);
|
||||
s = VerifyFullFileChecksum(fmeta->file_checksum,
|
||||
fmeta->file_checksum_func_name, fname,
|
||||
read_options);
|
||||
} else {
|
||||
s = ROCKSDB_NAMESPACE::VerifySstFileChecksum(opts, file_options_,
|
||||
read_options, fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && use_file_checksum) {
|
||||
const auto& blob_files = vstorage->GetBlobFiles();
|
||||
for (const auto& pair : blob_files) {
|
||||
const uint64_t blob_file_number = pair.first;
|
||||
const auto& meta = pair.second;
|
||||
assert(meta);
|
||||
const std::string blob_file_name = BlobFileName(
|
||||
cfd->ioptions()->cf_paths.front().path, blob_file_number);
|
||||
s = VerifyFullFileChecksum(meta->GetChecksumValue(),
|
||||
meta->GetChecksumMethod(), blob_file_name,
|
||||
read_options);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool defer_purge =
|
||||
immutable_db_options().avoid_unnecessary_blocking_io;
|
||||
{
|
||||
@@ -4888,29 +4923,31 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifySstFileChecksum(const FileMetaData& fmeta,
|
||||
const std::string& fname,
|
||||
const ReadOptions& read_options) {
|
||||
Status DBImpl::VerifyFullFileChecksum(const std::string& file_checksum_expected,
|
||||
const std::string& func_name_expected,
|
||||
const std::string& fname,
|
||||
const ReadOptions& read_options) {
|
||||
Status s;
|
||||
if (fmeta.file_checksum == kUnknownFileChecksum) {
|
||||
if (file_checksum_expected == kUnknownFileChecksum) {
|
||||
return s;
|
||||
}
|
||||
std::string file_checksum;
|
||||
std::string func_name;
|
||||
s = ROCKSDB_NAMESPACE::GenerateOneFileChecksum(
|
||||
fs_.get(), fname, immutable_db_options_.file_checksum_gen_factory.get(),
|
||||
fmeta.file_checksum_func_name, &file_checksum, &func_name,
|
||||
func_name_expected, &file_checksum, &func_name,
|
||||
read_options.readahead_size, immutable_db_options_.allow_mmap_reads,
|
||||
io_tracer_, immutable_db_options_.rate_limiter.get());
|
||||
if (s.ok()) {
|
||||
assert(fmeta.file_checksum_func_name == func_name);
|
||||
if (file_checksum != fmeta.file_checksum) {
|
||||
assert(func_name_expected == func_name);
|
||||
if (file_checksum != file_checksum_expected) {
|
||||
std::ostringstream oss;
|
||||
oss << fname << " file checksum mismatch, ";
|
||||
oss << "expecting " << Slice(fmeta.file_checksum).ToString(/*hex=*/true);
|
||||
oss << "expecting "
|
||||
<< Slice(file_checksum_expected).ToString(/*hex=*/true);
|
||||
oss << ", but actual " << Slice(file_checksum).ToString(/*hex=*/true);
|
||||
s = Status::Corruption(oss.str());
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::VerifySstFileChecksum:mismatch", &s);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::VerifyFullFileChecksum:mismatch", &s);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
@@ -4945,7 +4982,8 @@ void DBImpl::WaitForIngestFile() {
|
||||
Status DBImpl::StartTrace(const TraceOptions& trace_options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
tracer_.reset(new Tracer(env_, trace_options, std::move(trace_writer)));
|
||||
tracer_.reset(new Tracer(immutable_db_options_.clock, trace_options,
|
||||
std::move(trace_writer)));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -4964,8 +5002,8 @@ Status DBImpl::EndTrace() {
|
||||
Status DBImpl::StartBlockCacheTrace(
|
||||
const TraceOptions& trace_options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) {
|
||||
return block_cache_tracer_.StartTrace(env_, trace_options,
|
||||
std::move(trace_writer));
|
||||
return block_cache_tracer_.StartTrace(immutable_db_options_.clock,
|
||||
trace_options, std::move(trace_writer));
|
||||
}
|
||||
|
||||
Status DBImpl::EndBlockCacheTrace() {
|
||||
|
||||
@@ -54,9 +54,6 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "trace_replay/block_cache_tracer.h"
|
||||
#include "trace_replay/io_tracer.h"
|
||||
#include "trace_replay/trace_replay.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/repeatable_thread.h"
|
||||
@@ -450,9 +447,10 @@ class DBImpl : public DB {
|
||||
Status VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
bool use_file_checksum);
|
||||
|
||||
Status VerifySstFileChecksum(const FileMetaData& fmeta,
|
||||
const std::string& fpath,
|
||||
const ReadOptions& read_options);
|
||||
Status VerifyFullFileChecksum(const std::string& file_checksum_expected,
|
||||
const std::string& func_name_expected,
|
||||
const std::string& fpath,
|
||||
const ReadOptions& read_options);
|
||||
|
||||
using DB::StartTrace;
|
||||
virtual Status StartTrace(
|
||||
@@ -488,6 +486,7 @@ class DBImpl : public DB {
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// ---- End of implementations of the DB interface ----
|
||||
SystemClock* GetSystemClock() const;
|
||||
|
||||
struct GetImplOptions {
|
||||
ColumnFamilyHandle* column_family = nullptr;
|
||||
@@ -2220,6 +2219,8 @@ class DBImpl : public DB {
|
||||
InstrumentedCondVar atomic_flush_install_cv_;
|
||||
|
||||
bool wal_in_db_path_;
|
||||
|
||||
BlobFileCompletionCallback blob_callback_;
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db, const Options& src);
|
||||
|
||||
@@ -131,16 +131,11 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
MarkLogsNotSynced(current_log_number - 1);
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
if (total_log_size_ > 0) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return io_s;
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:end");
|
||||
return io_s;
|
||||
}
|
||||
|
||||
@@ -166,20 +161,18 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||
io_tracer_, db_id_, db_session_id_, cfd->GetFullHistoryTsLow());
|
||||
io_tracer_, db_id_, db_session_id_, cfd->GetFullHistoryTsLow(),
|
||||
&blob_callback_);
|
||||
FileMetaData file_meta;
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforePickMemtables");
|
||||
flush_job.PickMemTable();
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:AfterPickMemtables");
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status s;
|
||||
IOStatus io_s = IOStatus::OK();
|
||||
bool need_cancel = false;
|
||||
IOStatus log_io_s = IOStatus::OK();
|
||||
if (logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1) {
|
||||
// If there are more than one column families, we need to make sure that
|
||||
@@ -188,15 +181,24 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// flushed SST may contain data from write batches whose updates to
|
||||
// other column families are missing.
|
||||
// SyncClosedLogs() may unlock and re-lock the db_mutex.
|
||||
io_s = SyncClosedLogs(job_context);
|
||||
s = io_s;
|
||||
if (!io_s.ok() && !io_s.IsShutdownInProgress() &&
|
||||
!io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
log_io_s = SyncClosedLogs(job_context);
|
||||
if (!log_io_s.ok() && !log_io_s.IsShutdownInProgress() &&
|
||||
!log_io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(log_io_s, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Skip");
|
||||
}
|
||||
s = log_io_s;
|
||||
|
||||
// If the log sync failed, we do not need to pick memtable. Otherwise,
|
||||
// num_flush_not_started_ needs to be rollback.
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforePickMemtables");
|
||||
if (s.ok()) {
|
||||
flush_job.PickMemTable();
|
||||
need_cancel = true;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:AfterPickMemtables");
|
||||
|
||||
// Within flush_job.Run, rocksdb may call event listener to notify
|
||||
// file creation and deletion.
|
||||
@@ -206,11 +208,16 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// is unlocked by the current thread.
|
||||
if (s.ok()) {
|
||||
s = flush_job.Run(&logs_with_prep_tracker_, &file_meta);
|
||||
} else {
|
||||
need_cancel = false;
|
||||
}
|
||||
|
||||
if (!s.ok() && need_cancel) {
|
||||
flush_job.Cancel();
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
io_s = flush_job.io_status();
|
||||
IOStatus io_s = IOStatus::OK();
|
||||
io_s = flush_job.io_status();
|
||||
if (s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -246,29 +253,30 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
if (!s.ok() && !s.IsShutdownInProgress() && !s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && !io_s.IsShutdownInProgress() &&
|
||||
!io_s.IsColumnFamilyDropped()) {
|
||||
assert(log_io_s.ok());
|
||||
// Error while writing to MANIFEST.
|
||||
// In fact, versions_->io_status() can also be the result of renaming
|
||||
// CURRENT file. With current code, it's just difficult to tell. So just
|
||||
// be pessimistic and try write to a new MANIFEST.
|
||||
// TODO: distinguish between MANIFEST write and CURRENT renaming
|
||||
if (!versions_->io_status().ok()) {
|
||||
if (total_log_size_ > 0) {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
} else {
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
}
|
||||
} else if (total_log_size_ > 0) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
// If WAL sync is successful (either WAL size is 0 or there is no IO
|
||||
// error), all the Manifest write will be map to soft error.
|
||||
// TODO: kManifestWriteNoWAL and kFlushNoWAL are misleading. Refactor is
|
||||
// needed.
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
// If WAL sync is successful (either WAL size is 0 or there is no IO
|
||||
// error), all the other SST file write errors will be set as
|
||||
// kFlushNoWAL.
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
if (log_io_s.ok()) {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we got here, then we decided not to care about the i_os status (either
|
||||
@@ -401,12 +409,11 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri, io_tracer_, db_id_, db_session_id_,
|
||||
cfd->GetFullHistoryTsLow()));
|
||||
jobs.back()->PickMemTable();
|
||||
}
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
IOStatus log_io_s = IOStatus::OK();
|
||||
assert(num_cfs == static_cast<int>(jobs.size()));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -421,18 +428,36 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
if (logfile_number_ > 0) {
|
||||
// TODO (yanqin) investigate whether we should sync the closed logs for
|
||||
// single column family case.
|
||||
io_s = SyncClosedLogs(job_context);
|
||||
s = io_s;
|
||||
log_io_s = SyncClosedLogs(job_context);
|
||||
if (!log_io_s.ok() && !log_io_s.IsShutdownInProgress() &&
|
||||
!log_io_s.IsColumnFamilyDropped()) {
|
||||
if (total_log_size_ > 0) {
|
||||
error_handler_.SetBGError(log_io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(log_io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
s = log_io_s;
|
||||
|
||||
// exec_status stores the execution status of flush_jobs as
|
||||
// <bool /* executed */, Status /* status code */>
|
||||
autovector<std::pair<bool, Status>> exec_status;
|
||||
autovector<IOStatus> io_status;
|
||||
std::vector<bool> pick_status;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
// Initially all jobs are not executed, with status OK.
|
||||
exec_status.emplace_back(false, Status::OK());
|
||||
io_status.emplace_back(IOStatus::OK());
|
||||
pick_status.push_back(false);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
jobs[i]->PickMemTable();
|
||||
pick_status[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -473,6 +498,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
s = error_status.ok() ? s : error_status;
|
||||
}
|
||||
|
||||
IOStatus io_s = IOStatus::OK();
|
||||
if (io_s.ok()) {
|
||||
IOStatus io_error = IOStatus::OK();
|
||||
for (int i = 0; i != static_cast<int>(io_status.size()); i++) {
|
||||
@@ -508,12 +534,12 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// Have to cancel the flush jobs that have NOT executed because we need to
|
||||
// unref the versions.
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (!exec_status[i].first) {
|
||||
if (pick_status[i] && !exec_status[i].first) {
|
||||
jobs[i]->Cancel();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
if (exec_status[i].first && exec_status[i].second.ok()) {
|
||||
if (exec_status[i].second.ok() && exec_status[i].first) {
|
||||
auto& mems = jobs[i]->GetMemTables();
|
||||
cfds[i]->imm()->RollbackMemtableFlush(mems,
|
||||
file_meta[i].fd.GetNumber());
|
||||
@@ -652,29 +678,30 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && !io_s.IsColumnFamilyDropped()) {
|
||||
assert(log_io_s.ok());
|
||||
// Error while writing to MANIFEST.
|
||||
// In fact, versions_->io_status() can also be the result of renaming
|
||||
// CURRENT file. With current code, it's just difficult to tell. So just
|
||||
// be pessimistic and try write to a new MANIFEST.
|
||||
// TODO: distinguish between MANIFEST write and CURRENT renaming
|
||||
if (!versions_->io_status().ok()) {
|
||||
if (total_log_size_ > 0) {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
} else {
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
}
|
||||
} else if (total_log_size_ > 0) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
// If WAL sync is successful (either WAL size is 0 or there is no IO
|
||||
// error), all the Manifest write will be map to soft error.
|
||||
// TODO: kManifestWriteNoWAL and kFlushNoWAL are misleading. Refactor
|
||||
// is needed.
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
// If WAL sync is successful (either WAL size is 0 or there is no IO
|
||||
// error), all the other SST file write errors will be set as
|
||||
// kFlushNoWAL.
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
if (log_io_s.ok()) {
|
||||
Status new_bg_error = s;
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2061,12 +2088,12 @@ Status DBImpl::WaitUntilFlushWouldNotStallWrites(ColumnFamilyData* cfd,
|
||||
// check whether one extra immutable memtable or an extra L0 file would
|
||||
// cause write stalling mode to be entered. It could still enter stall
|
||||
// mode due to pending compaction bytes, but that's less common
|
||||
write_stall_condition =
|
||||
ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->imm()->NumNotFlushed() + 1,
|
||||
vstorage->l0_delay_trigger_count() + 1,
|
||||
vstorage->estimated_compaction_needed_bytes(), mutable_cf_options)
|
||||
.first;
|
||||
write_stall_condition = ColumnFamilyData::GetWriteStallConditionAndCause(
|
||||
cfd->imm()->NumNotFlushed() + 1,
|
||||
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();
|
||||
@@ -2536,6 +2563,8 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL,
|
||||
immutable_db_options_.info_log.get());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:Start:1");
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:Start:2");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
assert(bg_flush_scheduled_);
|
||||
@@ -2564,7 +2593,7 @@ void DBImpl::BackgroundCallFlush(Env::Priority thread_pri) {
|
||||
s.ToString().c_str(), error_cnt);
|
||||
log_buffer.FlushBufferToLog();
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
env_->SleepForMicroseconds(1000000);
|
||||
immutable_db_options_.clock->SleepForMicroseconds(1000000);
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
@@ -2637,7 +2666,8 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
if (s.IsBusy()) {
|
||||
bg_cv_.SignalAll(); // In case a waiter can proceed despite the error
|
||||
mutex_.Unlock();
|
||||
env_->SleepForMicroseconds(10000); // prevent hot loop
|
||||
immutable_db_options_.clock->SleepForMicroseconds(
|
||||
10000); // prevent hot loop
|
||||
mutex_.Lock();
|
||||
} else if (!s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() && !s.IsColumnFamilyDropped()) {
|
||||
@@ -2655,7 +2685,7 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
"Accumulated background error counts: %" PRIu64,
|
||||
s.ToString().c_str(), error_cnt);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
env_->SleepForMicroseconds(1000000);
|
||||
immutable_db_options_.clock->SleepForMicroseconds(1000000);
|
||||
mutex_.Lock();
|
||||
} else if (s.IsManualCompactionPaused()) {
|
||||
ManualCompactionState* m = prepicked_compaction->manual_compaction_state;
|
||||
|
||||
@@ -120,7 +120,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
mutable_db_options_.delete_obsolete_files_period_micros == 0) {
|
||||
doing_the_full_scan = true;
|
||||
} else {
|
||||
const uint64_t now_micros = env_->NowMicros();
|
||||
const uint64_t now_micros = immutable_db_options_.clock->NowMicros();
|
||||
if ((delete_obsolete_files_last_run_ +
|
||||
mutable_db_options_.delete_obsolete_files_period_micros) <
|
||||
now_micros) {
|
||||
@@ -937,7 +937,7 @@ Status DBImpl::DeleteUnreferencedSstFiles() {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (largest_file_number > next_file_number) {
|
||||
if (largest_file_number >= next_file_number) {
|
||||
versions_->next_file_number_.store(largest_file_number + 1);
|
||||
}
|
||||
|
||||
|
||||
+46
-22
@@ -175,7 +175,7 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
NewSstFileManager(result.env, result.info_log));
|
||||
result.sst_file_manager = sst_file_manager;
|
||||
}
|
||||
#endif
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
if (!result.paranoid_checks) {
|
||||
result.skip_checking_sst_file_sizes_on_db_open = true;
|
||||
@@ -283,6 +283,9 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Creating manifest 1 \n");
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
{
|
||||
if (fs_->FileExists(manifest, IOOptions(), nullptr).ok()) {
|
||||
fs_->DeleteFile(manifest, IOOptions(), nullptr).PermitUncheckedError();
|
||||
}
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
FileOptions file_options = fs_->OptimizeForManifestWrite(file_options_);
|
||||
s = NewWritableFile(fs_.get(), manifest, &file, file_options);
|
||||
@@ -293,15 +296,15 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
file->SetPreallocationBlockSize(
|
||||
immutable_db_options_.manifest_preallocation_size);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), manifest, file_options, env_, io_tracer_,
|
||||
nullptr /* stats */, immutable_db_options_.listeners, nullptr,
|
||||
tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
std::move(file), manifest, file_options, immutable_db_options_.clock,
|
||||
io_tracer_, nullptr /* stats */, immutable_db_options_.listeners,
|
||||
nullptr, tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
new_db.EncodeTo(&record);
|
||||
s = log.AddRecord(record);
|
||||
if (s.ok()) {
|
||||
s = SyncManifest(env_, &immutable_db_options_, log.file());
|
||||
s = SyncManifest(&immutable_db_options_, log.file());
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
@@ -312,7 +315,7 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
manifest.substr(manifest.find_last_of("/\\") + 1));
|
||||
}
|
||||
} else {
|
||||
fs_->DeleteFile(manifest, IOOptions(), nullptr);
|
||||
fs_->DeleteFile(manifest, IOOptions(), nullptr).PermitUncheckedError();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -1132,11 +1135,29 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords)) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->GetLogNumber() > corrupted_wal_number) {
|
||||
// One special case cause cfd->GetLogNumber() > corrupted_wal_number but
|
||||
// the CF is still consistent: If a new column family is created during
|
||||
// the flush and the WAL sync fails at the same time, the new CF points to
|
||||
// the new WAL but the old WAL is curropted. Since the new CF is empty, it
|
||||
// is still consistent. We add the check of CF sst file size to avoid the
|
||||
// false positive alert.
|
||||
|
||||
// Note that, the check of (cfd->GetLiveSstFilesSize() > 0) may leads to
|
||||
// the ignorance of a very rare inconsistency case caused in data
|
||||
// canclation. One CF is empty due to KV deletion. But those operations
|
||||
// are in the WAL. If the WAL is corrupted, the status of this CF might
|
||||
// not be consistent with others. However, the consistency check will be
|
||||
// bypassed due to empty CF.
|
||||
// TODO: a better and complete implementation is needed to ensure strict
|
||||
// consistency check in WAL recovery including hanlding the tailing
|
||||
// issues.
|
||||
if (cfd->GetLogNumber() > corrupted_wal_number &&
|
||||
cfd->GetLiveSstFilesSize() > 0) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Column family inconsistency: SST file contains data"
|
||||
" beyond the point of corruption.");
|
||||
return Status::Corruption("SST file is ahead of WALs");
|
||||
return Status::Corruption("SST file is ahead of WALs in CF " +
|
||||
cfd->GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1297,7 +1318,7 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
MemTable* mem, VersionEdit* edit) {
|
||||
mutex_.AssertHeld();
|
||||
const uint64_t start_micros = env_->NowMicros();
|
||||
const uint64_t start_micros = immutable_db_options_.clock->NowMicros();
|
||||
|
||||
FileMetaData meta;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
@@ -1325,7 +1346,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
cfd->GetLatestMutableCFOptions()->paranoid_file_checks;
|
||||
|
||||
int64_t _current_time = 0;
|
||||
env_->GetCurrentTime(&_current_time)
|
||||
immutable_db_options_.clock->GetCurrentTime(&_current_time)
|
||||
.PermitUncheckedError(); // ignore error
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
meta.oldest_ancester_time = current_time;
|
||||
@@ -1364,7 +1385,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
io_tracer_, &event_logger_, job_id, Env::IO_HIGH,
|
||||
nullptr /* table_properties */, -1 /* level */, current_time,
|
||||
0 /* oldest_key_time */, write_hint, 0 /* file_creation_time */,
|
||||
db_id_, db_session_id_);
|
||||
db_id_, db_session_id_, nullptr /*full_history_ts_low*/,
|
||||
&blob_callback_);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
@@ -1399,7 +1421,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
}
|
||||
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
stats.micros = env_->NowMicros() - start_micros;
|
||||
stats.micros = immutable_db_options_.clock->NowMicros() - start_micros;
|
||||
|
||||
if (has_output) {
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
@@ -1408,14 +1430,15 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
|
||||
const auto& blobs = edit->GetBlobFileAdditions();
|
||||
for (const auto& blob : blobs) {
|
||||
stats.bytes_written += blob.GetTotalBlobBytes();
|
||||
stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
stats.num_output_files += static_cast<int>(blobs.size());
|
||||
stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
|
||||
cfd->internal_stats()->AddCompactionStats(level, Env::Priority::USER, stats);
|
||||
cfd->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written);
|
||||
cfd->internal_stats()->AddCFStats(
|
||||
InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written + stats.bytes_written_blob);
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, meta.fd.GetFileSize());
|
||||
return s;
|
||||
}
|
||||
@@ -1491,9 +1514,9 @@ IOStatus DBImpl::CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
const auto& listeners = immutable_db_options_.listeners;
|
||||
FileTypeSet tmp_set = immutable_db_options_.checksum_handoff_file_types;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(lfile), log_fname, opt_file_options, env_, io_tracer_,
|
||||
nullptr /* stats */, listeners, nullptr,
|
||||
tmp_set.Contains(FileType::kWalFile)));
|
||||
std::move(lfile), log_fname, opt_file_options,
|
||||
immutable_db_options_.clock, io_tracer_, nullptr /* stats */, listeners,
|
||||
nullptr, tmp_set.Contains(FileType::kWalFile)));
|
||||
*new_log = new log::Writer(std::move(file_writer), log_file_num,
|
||||
immutable_db_options_.recycle_log_file_num > 0,
|
||||
immutable_db_options_.manual_wal_flush);
|
||||
@@ -1722,6 +1745,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
|
||||
// TODO: Once GetLiveFilesMetaData supports blob files, update the logic
|
||||
// below to get known_file_sizes for blob files.
|
||||
impl->mutex_.Lock();
|
||||
impl->versions_->GetLiveFilesMetaData(&metadata);
|
||||
impl->mutex_.Unlock();
|
||||
@@ -1754,13 +1779,12 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
FileType file_type;
|
||||
std::string file_path = path + "/" + file_name;
|
||||
if (ParseFileName(file_name, &file_number, &file_type) &&
|
||||
file_type == kTableFile) {
|
||||
(file_type == kTableFile || file_type == kBlobFile)) {
|
||||
// TODO: Check for errors from OnAddFile?
|
||||
if (known_file_sizes.count(file_name)) {
|
||||
// We're assuming that each sst file name exists in at most one of
|
||||
// the paths.
|
||||
sfm->OnAddFile(file_path, known_file_sizes.at(file_name),
|
||||
/* compaction */ false)
|
||||
sfm->OnAddFile(file_path, known_file_sizes.at(file_name))
|
||||
.PermitUncheckedError();
|
||||
} else {
|
||||
sfm->OnAddFile(file_path).PermitUncheckedError();
|
||||
|
||||
@@ -327,8 +327,8 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* pinnable_val) {
|
||||
assert(pinnable_val != nullptr);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_GET);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
@@ -519,7 +519,8 @@ Status DBImplSecondary::TryCatchUpWithPrimary() {
|
||||
{
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
s = static_cast_with_check<ReactiveVersionSet>(versions_.get())
|
||||
->ReadAndApply(&mutex_, &manifest_reader_, &cfds_changed);
|
||||
->ReadAndApply(&mutex_, &manifest_reader_,
|
||||
manifest_reader_status_.get(), &cfds_changed);
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Last sequence is %" PRIu64,
|
||||
static_cast<uint64_t>(versions_->LastSequence()));
|
||||
|
||||
+20
-12
@@ -160,7 +160,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
}
|
||||
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
StopWatch write_sw(immutable_db_options_.clock,
|
||||
immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
@@ -465,7 +466,8 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable, uint64_t* seq_used) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
StopWatch write_sw(immutable_db_options_.clock,
|
||||
immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
WriteContext write_context;
|
||||
|
||||
@@ -621,7 +623,8 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
SequenceNumber seq,
|
||||
const size_t sub_batch_cnt) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
StopWatch write_sw(immutable_db_options_.clock,
|
||||
immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
false /*disable_memtable*/);
|
||||
@@ -676,7 +679,8 @@ Status DBImpl::WriteImplWALOnly(
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, sub_batch_cnt, pre_release_callback);
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
StopWatch write_sw(immutable_db_options_.clock,
|
||||
immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
write_thread->JoinBatchGroup(&w);
|
||||
assert(w.state != WriteThread::STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
@@ -1093,7 +1097,7 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
}
|
||||
|
||||
if (io_s.ok() && need_log_sync) {
|
||||
StopWatch sw(env_, stats_, WAL_FILE_SYNC_MICROS);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, WAL_FILE_SYNC_MICROS);
|
||||
// It's safe to access logs_ with unlocked mutex_ here because:
|
||||
// - we've set getting_synced=true for all logs,
|
||||
// so other threads won't pop from logs_ while we're here,
|
||||
@@ -1457,8 +1461,10 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
||||
uint64_t time_delayed = 0;
|
||||
bool delayed = false;
|
||||
{
|
||||
StopWatch sw(env_, stats_, WRITE_STALL, &time_delayed);
|
||||
uint64_t delay = write_controller_.GetDelay(env_, num_bytes);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, WRITE_STALL,
|
||||
&time_delayed);
|
||||
uint64_t delay =
|
||||
write_controller_.GetDelay(immutable_db_options_.clock, num_bytes);
|
||||
if (delay > 0) {
|
||||
if (write_options.no_slowdown) {
|
||||
return Status::Incomplete("Write stall");
|
||||
@@ -1470,19 +1476,21 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
||||
write_thread_.BeginWriteStall();
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:BeginWriteStallDone");
|
||||
mutex_.Unlock();
|
||||
// We will delay the write until we have slept for delay ms or
|
||||
// we don't need a delay anymore
|
||||
const uint64_t kDelayInterval = 1000;
|
||||
// We will delay the write until we have slept for `delay` microseconds
|
||||
// or we don't need a delay anymore. We check for cancellation every 1ms
|
||||
// (slightly longer because WriteController minimum delay is 1ms, in
|
||||
// case of sleep imprecision, rounding, etc.)
|
||||
const uint64_t kDelayInterval = 1001;
|
||||
uint64_t stall_end = sw.start_time() + delay;
|
||||
while (write_controller_.NeedsDelay()) {
|
||||
if (env_->NowMicros() >= stall_end) {
|
||||
if (immutable_db_options_.clock->NowMicros() >= stall_end) {
|
||||
// We already delayed this write `delay` microseconds
|
||||
break;
|
||||
}
|
||||
|
||||
delayed = true;
|
||||
// Sleep for 0.001 seconds
|
||||
env_->SleepForMicroseconds(kDelayInterval);
|
||||
immutable_db_options_.clock->SleepForMicroseconds(kDelayInterval);
|
||||
}
|
||||
mutex_.Lock();
|
||||
write_thread_.EndWriteStall();
|
||||
|
||||
+99
-62
@@ -8,9 +8,10 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_iter.h"
|
||||
#include <string>
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/merge_context.h"
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
#include "trace_replay/trace_replay.h"
|
||||
@@ -43,6 +45,7 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index)
|
||||
: prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
|
||||
env_(_env),
|
||||
clock_(_env->GetSystemClock().get()),
|
||||
logger_(cf_options.info_log),
|
||||
user_comparator_(cmp),
|
||||
merge_operator_(cf_options.merge_operator),
|
||||
@@ -111,8 +114,7 @@ Status DBIter::GetProperty(std::string prop_name, std::string* prop) {
|
||||
}
|
||||
|
||||
bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
Status s =
|
||||
ParseInternalKey(iter_.key(), ikey, false /* log_err_key */); // TODO
|
||||
Status s = ParseInternalKey(iter_.key(), ikey, false /* log_err_key */);
|
||||
if (!s.ok()) {
|
||||
status_ = Status::Corruption("In DBIter: ", s.getState());
|
||||
valid_ = false;
|
||||
@@ -127,7 +129,7 @@ void DBIter::Next() {
|
||||
assert(valid_);
|
||||
assert(status_.ok());
|
||||
|
||||
PERF_CPU_TIMER_GUARD(iter_next_cpu_nanos, env_);
|
||||
PERF_CPU_TIMER_GUARD(iter_next_cpu_nanos, clock_);
|
||||
// Release temporarily pinned blocks from last operation
|
||||
ReleaseTempPinnedData();
|
||||
local_stats_.skip_count_ += num_internal_keys_skipped_;
|
||||
@@ -190,8 +192,10 @@ bool DBIter::SetBlobValueIfNeeded(const Slice& user_key,
|
||||
read_options.read_tier = read_tier_;
|
||||
read_options.verify_checksums = verify_checksums_;
|
||||
|
||||
const Status s =
|
||||
version_->GetBlob(read_options, user_key, blob_index, &blob_value_);
|
||||
constexpr uint64_t* bytes_read = nullptr;
|
||||
|
||||
const Status s = version_->GetBlob(read_options, user_key, blob_index,
|
||||
&blob_value_, bytes_read);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
@@ -484,7 +488,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), 0, kTypeDeletion));
|
||||
} else {
|
||||
const std::string kTsMin(timestamp_size_, static_cast<char>(0));
|
||||
const std::string kTsMin(timestamp_size_, '\0');
|
||||
AppendInternalKeyWithDifferentTimestamp(
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), 0, kTypeDeletion),
|
||||
@@ -574,7 +578,7 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
const Slice val = iter_.value();
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, ikey.user_key, &val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_, &pinned_value_, true);
|
||||
&saved_value_, logger_, statistics_, clock_, &pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
@@ -617,7 +621,7 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_, env_,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_, clock_,
|
||||
&pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
@@ -630,17 +634,10 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
}
|
||||
|
||||
void DBIter::Prev() {
|
||||
if (timestamp_size_ > 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::NotSupported(
|
||||
"SeekToLast/SeekForPrev/Prev currently not supported with timestamp.");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(valid_);
|
||||
assert(status_.ok());
|
||||
|
||||
PERF_CPU_TIMER_GUARD(iter_prev_cpu_nanos, env_);
|
||||
PERF_CPU_TIMER_GUARD(iter_prev_cpu_nanos, clock_);
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
bool ok = true;
|
||||
@@ -675,9 +672,16 @@ bool DBIter::ReverseToForward() {
|
||||
// If that's the case, seek iter_ to current key.
|
||||
if (!expect_total_order_inner_iter() || !iter_.Valid()) {
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(ParsedInternalKey(
|
||||
saved_key_.GetUserKey(), kMaxSequenceNumber, kValueTypeForSeek));
|
||||
ParsedInternalKey pikey(saved_key_.GetUserKey(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
if (timestamp_size_ > 0) {
|
||||
// TODO: pre-create kTsMax.
|
||||
const std::string kTsMax(timestamp_size_, '\xff');
|
||||
pikey.SetTimestamp(kTsMax);
|
||||
}
|
||||
last_key.SetInternalKey(pikey);
|
||||
iter_.Seek(last_key.GetInternalKey());
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
}
|
||||
|
||||
direction_ = kForward;
|
||||
@@ -728,6 +732,7 @@ bool DBIter::ReverseToBackward() {
|
||||
iter_.SeekToLast();
|
||||
}
|
||||
}
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
}
|
||||
|
||||
direction_ = kReverse;
|
||||
@@ -753,11 +758,13 @@ void DBIter::PrevInternal(const Slice* prefix) {
|
||||
}
|
||||
|
||||
assert(iterate_lower_bound_ == nullptr || iter_.MayBeOutOfLowerBound() ||
|
||||
user_comparator_.Compare(saved_key_.GetUserKey(),
|
||||
*iterate_lower_bound_) >= 0);
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
saved_key_.GetUserKey(), /*a_has_ts=*/true,
|
||||
*iterate_lower_bound_, /*b_has_ts=*/false) >= 0);
|
||||
if (iterate_lower_bound_ != nullptr && iter_.MayBeOutOfLowerBound() &&
|
||||
user_comparator_.Compare(saved_key_.GetUserKey(),
|
||||
*iterate_lower_bound_) < 0) {
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
saved_key_.GetUserKey(), /*a_has_ts=*/true, *iterate_lower_bound_,
|
||||
/*b_has_ts=*/false) < 0) {
|
||||
// We've iterated earlier than the user-specified lower bound.
|
||||
valid_ = false;
|
||||
return;
|
||||
@@ -802,8 +809,8 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
assert(iter_.Valid());
|
||||
merge_context_.Clear();
|
||||
current_entry_is_merged_ = false;
|
||||
// last entry before merge (could be kTypeDeletion, kTypeSingleDeletion or
|
||||
// kTypeValue)
|
||||
// last entry before merge (could be kTypeDeletion,
|
||||
// kTypeDeletionWithTimestamp, kTypeSingleDeletion or kTypeValue)
|
||||
ValueType last_not_merge_type = kTypeDeletion;
|
||||
ValueType last_key_entry_type = kTypeDeletion;
|
||||
|
||||
@@ -824,9 +831,13 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
timestamp_size_);
|
||||
}
|
||||
if (!IsVisible(ikey.sequence, ts) ||
|
||||
!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
!user_comparator_.EqualWithoutTimestamp(ikey.user_key,
|
||||
saved_key_.GetUserKey())) {
|
||||
break;
|
||||
}
|
||||
if (!ts.empty()) {
|
||||
saved_timestamp_.assign(ts.data(), ts.size());
|
||||
}
|
||||
if (TooManyInternalKeysSkipped()) {
|
||||
return false;
|
||||
}
|
||||
@@ -866,6 +877,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
}
|
||||
break;
|
||||
case kTypeDeletion:
|
||||
case kTypeDeletionWithTimestamp:
|
||||
case kTypeSingleDeletion:
|
||||
merge_context_.Clear();
|
||||
last_not_merge_type = last_key_entry_type;
|
||||
@@ -909,6 +921,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
is_blob_ = false;
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeDeletionWithTimestamp:
|
||||
case kTypeSingleDeletion:
|
||||
case kTypeRangeDeletion:
|
||||
valid_ = false;
|
||||
@@ -921,7 +934,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_, true);
|
||||
clock_, &pinned_value_, true);
|
||||
} else if (last_not_merge_type == kTypeBlobIndex) {
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
@@ -932,7 +945,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), &pinned_value_,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_, true);
|
||||
clock_, &pinned_value_, true);
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
@@ -969,8 +982,17 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
// FindValueForCurrentKeyUsingSeek()
|
||||
assert(pinned_iters_mgr_.PinningEnabled());
|
||||
std::string last_key;
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetUserKey(),
|
||||
sequence_, kValueTypeForSeek));
|
||||
if (0 == timestamp_size_) {
|
||||
AppendInternalKey(&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
} else {
|
||||
AppendInternalKeyWithDifferentTimestamp(
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), sequence_,
|
||||
kValueTypeForSeek),
|
||||
*timestamp_ub_);
|
||||
}
|
||||
iter_.Seek(last_key);
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
|
||||
@@ -994,7 +1016,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
timestamp_size_);
|
||||
}
|
||||
|
||||
if (!user_comparator_.Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
if (!user_comparator_.EqualWithoutTimestamp(ikey.user_key,
|
||||
saved_key_.GetUserKey())) {
|
||||
// No visible values for this key, even though FindValueForCurrentKey()
|
||||
// has seen some. This is possible if we're using a tailing iterator, and
|
||||
// the entries were discarded in a compaction.
|
||||
@@ -1011,7 +1034,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelPositioningMode::kBackwardTraversal)) {
|
||||
ikey, RangeDelPositioningMode::kBackwardTraversal) ||
|
||||
kTypeDeletionWithTimestamp == ikey.type) {
|
||||
valid_ = false;
|
||||
return true;
|
||||
}
|
||||
@@ -1019,6 +1043,10 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (timestamp_size_ > 0) {
|
||||
Slice ts = ExtractTimestampFromUserKey(ikey.user_key, timestamp_size_);
|
||||
saved_timestamp_.assign(ts.data(), ts.size());
|
||||
}
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeBlobIndex) {
|
||||
assert(iter_.iter()->IsValuePinned());
|
||||
pinned_value_ = iter_.value();
|
||||
@@ -1070,7 +1098,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), &val,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_, true);
|
||||
clock_, &pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
@@ -1097,7 +1125,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_, env_,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_, clock_,
|
||||
&pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
@@ -1135,7 +1163,8 @@ bool DBIter::FindUserKeyBeforeSavedKey() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user_comparator_.Compare(ikey.user_key, saved_key_.GetUserKey()) < 0) {
|
||||
if (user_comparator_.CompareWithoutTimestamp(ikey.user_key,
|
||||
saved_key_.GetUserKey()) < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1159,8 +1188,14 @@ bool DBIter::FindUserKeyBeforeSavedKey() {
|
||||
if (num_skipped >= max_skip_) {
|
||||
num_skipped = 0;
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(ParsedInternalKey(
|
||||
saved_key_.GetUserKey(), kMaxSequenceNumber, kValueTypeForSeek));
|
||||
ParsedInternalKey pikey(saved_key_.GetUserKey(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
if (timestamp_size_ > 0) {
|
||||
// TODO: pre-create kTsMax.
|
||||
const std::string kTsMax(timestamp_size_, '\xff');
|
||||
pikey.SetTimestamp(kTsMax);
|
||||
}
|
||||
last_key.SetInternalKey(pikey);
|
||||
// It would be more efficient to use SeekForPrev() here, but some
|
||||
// iterators may not support it.
|
||||
iter_.Seek(last_key.GetInternalKey());
|
||||
@@ -1237,19 +1272,33 @@ void DBIter::SetSavedKeyToSeekForPrevTarget(const Slice& target) {
|
||||
saved_key_.Clear();
|
||||
// now saved_key is used to store internal key.
|
||||
saved_key_.SetInternalKey(target, 0 /* sequence_number */,
|
||||
kValueTypeForSeekForPrev);
|
||||
kValueTypeForSeekForPrev, timestamp_ub_);
|
||||
|
||||
if (timestamp_size_ > 0) {
|
||||
const std::string kTsMin(timestamp_size_, '\0');
|
||||
Slice ts = kTsMin;
|
||||
saved_key_.UpdateInternalKey(/*seq=*/0, kValueTypeForSeekForPrev, &ts);
|
||||
}
|
||||
|
||||
if (iterate_upper_bound_ != nullptr &&
|
||||
user_comparator_.Compare(saved_key_.GetUserKey(),
|
||||
*iterate_upper_bound_) >= 0) {
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
saved_key_.GetUserKey(), /*a_has_ts=*/true, *iterate_upper_bound_,
|
||||
/*b_has_ts=*/false) >= 0) {
|
||||
saved_key_.Clear();
|
||||
saved_key_.SetInternalKey(*iterate_upper_bound_, kMaxSequenceNumber);
|
||||
saved_key_.SetInternalKey(*iterate_upper_bound_, kMaxSequenceNumber,
|
||||
kValueTypeForSeekForPrev, timestamp_ub_);
|
||||
if (timestamp_size_ > 0) {
|
||||
const std::string kTsMax(timestamp_size_, '\xff');
|
||||
Slice ts = kTsMax;
|
||||
saved_key_.UpdateInternalKey(kMaxSequenceNumber, kValueTypeForSeekForPrev,
|
||||
&ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::Seek(const Slice& target) {
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, env_);
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
StopWatch sw(clock_, statistics_, DB_SEEK);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
@@ -1322,8 +1371,8 @@ void DBIter::Seek(const Slice& target) {
|
||||
}
|
||||
|
||||
void DBIter::SeekForPrev(const Slice& target) {
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, env_);
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
StopWatch sw(clock_, statistics_, DB_SEEK);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
@@ -1346,13 +1395,6 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
if (timestamp_size_ > 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::NotSupported(
|
||||
"SeekToLast/SeekForPrev/Prev currently not supported with timestamp.");
|
||||
return;
|
||||
}
|
||||
|
||||
status_ = Status::OK();
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
@@ -1403,7 +1445,7 @@ void DBIter::SeekToFirst() {
|
||||
Seek(*iterate_lower_bound_);
|
||||
return;
|
||||
}
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, env_);
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
// Don't use iter_::Seek() if we set a prefix extractor
|
||||
// because prefix seek will be used.
|
||||
if (!expect_total_order_inner_iter()) {
|
||||
@@ -1447,24 +1489,19 @@ void DBIter::SeekToFirst() {
|
||||
}
|
||||
|
||||
void DBIter::SeekToLast() {
|
||||
if (timestamp_size_ > 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::NotSupported(
|
||||
"SeekToLast/SeekForPrev/Prev currently not supported with timestamp.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (iterate_upper_bound_ != nullptr) {
|
||||
// Seek to last key strictly less than ReadOptions.iterate_upper_bound.
|
||||
SeekForPrev(*iterate_upper_bound_);
|
||||
if (Valid() && user_comparator_.Equal(*iterate_upper_bound_, key())) {
|
||||
if (Valid() && 0 == user_comparator_.CompareWithoutTimestamp(
|
||||
*iterate_upper_bound_, /*a_has_ts=*/false, key(),
|
||||
/*b_has_ts=*/false)) {
|
||||
ReleaseTempPinnedData();
|
||||
PrevInternal(nullptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, env_);
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
// Don't use iter_::Seek() if we set a prefix extractor
|
||||
// because prefix seek will be used.
|
||||
if (!expect_total_order_inner_iter()) {
|
||||
|
||||
+8
-3
@@ -8,8 +8,9 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
@@ -21,7 +22,6 @@
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class Version;
|
||||
|
||||
// This file declares the factory functions of DBIter, in its original form
|
||||
@@ -68,7 +68,7 @@ class DBIter final : public Iterator {
|
||||
// this->key().
|
||||
// (2) When moving backwards, the internal iterator is positioned
|
||||
// just before all entries whose user key == this->key().
|
||||
enum Direction { kForward, kReverse };
|
||||
enum Direction : uint8_t { kForward, kReverse };
|
||||
|
||||
// LocalStatistics contain Statistics counters that will be aggregated per
|
||||
// each iterator instance and then will be sent to the global statistics when
|
||||
@@ -185,6 +185,9 @@ class DBIter final : public Iterator {
|
||||
Slice timestamp() const override {
|
||||
assert(valid_);
|
||||
assert(timestamp_size_ > 0);
|
||||
if (direction_ == kReverse) {
|
||||
return saved_timestamp_;
|
||||
}
|
||||
const Slice ukey_and_ts = saved_key_.GetUserKey();
|
||||
assert(timestamp_size_ < ukey_and_ts.size());
|
||||
return ExtractTimestampFromUserKey(ukey_and_ts, timestamp_size_);
|
||||
@@ -298,6 +301,7 @@ class DBIter final : public Iterator {
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
Env* const env_;
|
||||
SystemClock* clock_;
|
||||
Logger* logger_;
|
||||
UserComparatorWrapper user_comparator_;
|
||||
const MergeOperator* const merge_operator_;
|
||||
@@ -372,6 +376,7 @@ class DBIter final : public Iterator {
|
||||
const Slice* const timestamp_ub_;
|
||||
const Slice* const timestamp_lb_;
|
||||
const size_t timestamp_size_;
|
||||
std::string saved_timestamp_;
|
||||
};
|
||||
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
|
||||
+44
-5
@@ -18,6 +18,7 @@
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "table/block_based/flush_block_policy.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend2.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -619,6 +620,40 @@ TEST_P(DBIteratorTest, IterReseek) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, ReseekUponDirectionChange) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.merge_operator.reset(
|
||||
new StringAppendTESTOperator(/*delim_char=*/' '));
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
it->SeekToLast();
|
||||
it->Prev();
|
||||
it->Next();
|
||||
}
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
|
||||
const std::string merge_key("good");
|
||||
ASSERT_OK(Put(merge_key, "orig"));
|
||||
ASSERT_OK(Merge(merge_key, "suffix"));
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
it->Seek(merge_key);
|
||||
ASSERT_TRUE(it->Valid());
|
||||
const uint64_t prev_reseek_count =
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
it->Prev();
|
||||
ASSERT_EQ(prev_reseek_count + 1, options.statistics->getTickerCount(
|
||||
NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, IterSmallAndLargeMix) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
@@ -1518,6 +1553,10 @@ TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedFlush) {
|
||||
PinnedDataIteratorRandomized(TestConfig::FLUSH_EVERY_1000);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBIteratorTestForPinnedDataInstance,
|
||||
DBIteratorTestForPinnedData,
|
||||
testing::Values(true, false));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(DBIteratorTest, PinnedDataIteratorMultipleFiles) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -2997,18 +3036,18 @@ TEST_P(DBIteratorTest, Blob) {
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 6);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
|
||||
// Switch from forward to reverse
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 6);
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 6);
|
||||
iter->Prev();
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 7);
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 8);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "port/stack_trace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -73,6 +73,15 @@ TEST_F(DBRangeDelTest, FlushOutputHasOnlyRangeTombstones) {
|
||||
} while (ChangeOptions(kRangeDelSkipConfigs));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, DictionaryCompressionWithOnlyRangeTombstones) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.compression_opts.max_dict_bytes = 16384;
|
||||
Reopen(opts);
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "dr1",
|
||||
"dr2"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, CompactionOutputHasOnlyRangeTombstone) {
|
||||
do {
|
||||
Options opts = CurrentOptions();
|
||||
|
||||
@@ -459,20 +459,6 @@ TEST_F(DBSecondaryTest, MissingTableFileDuringOpen) {
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, MissingTableFile) {
|
||||
int table_files_not_exist = 0;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"ReactiveVersionSet::ApplyOneVersionEditToBuilder:AfterLoadTableHandlers",
|
||||
[&](void* arg) {
|
||||
Status s = *reinterpret_cast<Status*>(arg);
|
||||
if (s.IsPathNotFound()) {
|
||||
++table_files_not_exist;
|
||||
} else if (!s.ok()) {
|
||||
assert(false); // Should not reach here
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
@@ -499,7 +485,6 @@ TEST_F(DBSecondaryTest, MissingTableFile) {
|
||||
ASSERT_NOK(db_secondary_->Get(ropts, "bar", &value));
|
||||
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
ASSERT_EQ(options.level0_file_num_compaction_trigger, table_files_not_exist);
|
||||
ASSERT_OK(db_secondary_->Get(ropts, "foo", &value));
|
||||
ASSERT_EQ("foo_value" +
|
||||
std::to_string(options.level0_file_num_compaction_trigger - 1),
|
||||
@@ -615,10 +600,7 @@ TEST_F(DBSecondaryTest, SwitchManifest) {
|
||||
range_scan_db();
|
||||
}
|
||||
|
||||
// Here, "Snapshot" refers to the version edits written by
|
||||
// VersionSet::WriteSnapshot() at the beginning of the new MANIFEST after
|
||||
// switching from the old one.
|
||||
TEST_F(DBSecondaryTest, SkipSnapshotAfterManifestSwitch) {
|
||||
TEST_F(DBSecondaryTest, SwitchManifestTwice) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -640,10 +622,15 @@ TEST_F(DBSecondaryTest, SkipSnapshotAfterManifestSwitch) {
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("0", "value1"));
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
|
||||
ASSERT_OK(db_secondary_->Get(ropts, "0", &value));
|
||||
ASSERT_EQ("value1", value);
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, SwitchWAL) {
|
||||
TEST_F(DBSecondaryTest, DISABLED_SwitchWAL) {
|
||||
const int kNumKeysPerMemtable = 1;
|
||||
Options options;
|
||||
options.env = env_;
|
||||
@@ -692,7 +679,7 @@ TEST_F(DBSecondaryTest, SwitchWAL) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, SwitchWALMultiColumnFamilies) {
|
||||
TEST_F(DBSecondaryTest, DISABLED_SwitchWALMultiColumnFamilies) {
|
||||
const int kNumKeysPerMemtable = 1;
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
+314
-6
@@ -306,13 +306,13 @@ TEST_F(DBSSTTest, DBWithSstFileManager) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
// Verify that we are tracking all sst files in dbname_
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllSSTFiles(&files_in_db));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
|
||||
ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
|
||||
}
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllSSTFiles(&files_in_db));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
|
||||
// Verify that we are tracking all sst files in dbname_
|
||||
ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
|
||||
// Verify the total files size
|
||||
@@ -346,6 +346,260 @@ TEST_F(DBSSTTest, DBWithSstFileManager) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, DBWithSstFileManagerForBlobFiles) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
|
||||
int files_added = 0;
|
||||
int files_deleted = 0;
|
||||
int files_moved = 0;
|
||||
int files_scheduled_to_delete = 0;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnAddFile", [&](void* arg) {
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (file_path->find(".blob") != std::string::npos) {
|
||||
files_added++;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnDeleteFile", [&](void* arg) {
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (file_path->find(".blob") != std::string::npos) {
|
||||
files_deleted++;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::ScheduleFileDeletion", [&](void* arg) {
|
||||
assert(arg);
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (file_path->find(".blob") != std::string::npos) {
|
||||
++files_scheduled_to_delete;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnMoveFile", [&](void* /*arg*/) { files_moved++; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.sst_file_manager = sst_file_manager;
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = 32; // create one blob per file
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put("Key_" + std::to_string(i), "Value_" + std::to_string(i)));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// Verify that we are tracking all sst and blob files in dbname_
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
|
||||
ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db));
|
||||
ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> blob_files = GetBlobFileNumbers();
|
||||
ASSERT_EQ(files_added, blob_files.size());
|
||||
// No blob file is obsoleted.
|
||||
ASSERT_EQ(files_deleted, 0);
|
||||
ASSERT_EQ(files_scheduled_to_delete, 0);
|
||||
// No files were moved.
|
||||
ASSERT_EQ(files_moved, 0);
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
|
||||
ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db));
|
||||
|
||||
// Verify that we are tracking all sst and blob files in dbname_
|
||||
ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
|
||||
// Verify the total files size
|
||||
uint64_t total_files_size = 0;
|
||||
for (auto& file_to_size : files_in_db) {
|
||||
total_files_size += file_to_size.second;
|
||||
}
|
||||
ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
|
||||
Close();
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
|
||||
ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
|
||||
|
||||
// Verify that we track all the files again after the DB is closed and opened.
|
||||
Close();
|
||||
|
||||
sst_file_manager.reset(NewSstFileManager(env_));
|
||||
options.sst_file_manager = sst_file_manager;
|
||||
sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
|
||||
ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
|
||||
|
||||
// Destroy DB and it will remove all the blob files from sst file manager and
|
||||
// blob files deletion will go through ScheduleFileDeletion.
|
||||
ASSERT_EQ(files_deleted, 0);
|
||||
ASSERT_EQ(files_scheduled_to_delete, 0);
|
||||
Close();
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
ASSERT_EQ(files_deleted, blob_files.size());
|
||||
ASSERT_EQ(files_scheduled_to_delete, blob_files.size());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, DBWithSstFileManagerForBlobFilesWithGC) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
Options options = CurrentOptions();
|
||||
options.sst_file_manager = sst_file_manager;
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = 32; // create one blob per file
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_garbage_collection = true;
|
||||
options.blob_garbage_collection_age_cutoff = 0.5;
|
||||
|
||||
int files_added = 0;
|
||||
int files_deleted = 0;
|
||||
int files_moved = 0;
|
||||
int files_scheduled_to_delete = 0;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnAddFile", [&](void* arg) {
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (file_path->find(".blob") != std::string::npos) {
|
||||
files_added++;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnDeleteFile", [&](void* arg) {
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (file_path->find(".blob") != std::string::npos) {
|
||||
files_deleted++;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::ScheduleFileDeletion", [&](void* arg) {
|
||||
assert(arg);
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (file_path->find(".blob") != std::string::npos) {
|
||||
++files_scheduled_to_delete;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnMoveFile", [&](void* /*arg*/) { files_moved++; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char second_value[] = "second_value";
|
||||
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr char third_key[] = "third_key";
|
||||
constexpr char third_value[] = "third_value";
|
||||
constexpr char fourth_key[] = "fourth_key";
|
||||
constexpr char fourth_value[] = "fourth_value";
|
||||
constexpr char fifth_key[] = "fifth_key";
|
||||
constexpr char fifth_value[] = "fifth_value";
|
||||
|
||||
ASSERT_OK(Put(third_key, third_value));
|
||||
ASSERT_OK(Put(fourth_key, fourth_value));
|
||||
ASSERT_OK(Put(fifth_key, fifth_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
const std::vector<uint64_t> original_blob_files = GetBlobFileNumbers();
|
||||
|
||||
ASSERT_EQ(original_blob_files.size(), 5);
|
||||
ASSERT_EQ(files_added, 5);
|
||||
ASSERT_EQ(files_deleted, 0);
|
||||
ASSERT_EQ(files_scheduled_to_delete, 0);
|
||||
ASSERT_EQ(files_moved, 0);
|
||||
{
|
||||
// Verify that we are tracking all sst and blob files in dbname_
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
|
||||
ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db));
|
||||
ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
|
||||
}
|
||||
|
||||
const size_t cutoff_index = static_cast<size_t>(
|
||||
options.blob_garbage_collection_age_cutoff * original_blob_files.size());
|
||||
|
||||
size_t expected_number_of_files = original_blob_files.size();
|
||||
// Note: turning off enable_blob_files before the compaction results in
|
||||
// garbage collected values getting inlined.
|
||||
ASSERT_OK(db_->SetOptions({{"enable_blob_files", "false"}}));
|
||||
expected_number_of_files -= cutoff_index;
|
||||
files_added = 0;
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
|
||||
ASSERT_EQ(Get(first_key), first_value);
|
||||
ASSERT_EQ(Get(second_key), second_value);
|
||||
ASSERT_EQ(Get(third_key), third_value);
|
||||
ASSERT_EQ(Get(fourth_key), fourth_value);
|
||||
ASSERT_EQ(Get(fifth_key), fifth_value);
|
||||
|
||||
const std::vector<uint64_t> new_blob_files = GetBlobFileNumbers();
|
||||
|
||||
ASSERT_EQ(new_blob_files.size(), expected_number_of_files);
|
||||
// No new file is added.
|
||||
ASSERT_EQ(files_added, 0);
|
||||
ASSERT_EQ(files_deleted, cutoff_index);
|
||||
ASSERT_EQ(files_scheduled_to_delete, cutoff_index);
|
||||
ASSERT_EQ(files_moved, 0);
|
||||
|
||||
// Original blob files below the cutoff should be gone, original blob files at
|
||||
// or above the cutoff should be still there
|
||||
for (size_t i = cutoff_index; i < original_blob_files.size(); ++i) {
|
||||
ASSERT_EQ(new_blob_files[i - cutoff_index], original_blob_files[i]);
|
||||
}
|
||||
|
||||
{
|
||||
// Verify that we are tracking all sst and blob files in dbname_
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db));
|
||||
ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db));
|
||||
ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db);
|
||||
}
|
||||
|
||||
Close();
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
ASSERT_EQ(files_deleted, 5);
|
||||
ASSERT_EQ(files_scheduled_to_delete, 5);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
Destroy(last_options_);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
@@ -780,7 +1034,7 @@ TEST_F(DBSSTTest, DBWithMaxSpaceAllowed) {
|
||||
|
||||
uint64_t first_file_size = 0;
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllSSTFiles(&files_in_db, &first_file_size));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &first_file_size));
|
||||
ASSERT_EQ(sfm->GetTotalSize(), first_file_size);
|
||||
|
||||
// Set the maximum allowed space usage to the current total size
|
||||
@@ -791,6 +1045,60 @@ TEST_F(DBSSTTest, DBWithMaxSpaceAllowed) {
|
||||
ASSERT_NOK(Flush());
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, DBWithMaxSpaceAllowedWithBlobFiles) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.sst_file_manager = sst_file_manager;
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_files = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
// Generate a file containing keys.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
uint64_t files_size = 0;
|
||||
uint64_t total_files_size = 0;
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
|
||||
ASSERT_OK(GetAllDataFiles(kBlobFile, &files_in_db, &files_size));
|
||||
// Make sure blob files are considered by SSTFileManage in size limits.
|
||||
ASSERT_GT(files_size, 0);
|
||||
total_files_size = files_size;
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &files_size));
|
||||
total_files_size += files_size;
|
||||
ASSERT_EQ(sfm->GetTotalSize(), total_files_size);
|
||||
|
||||
// Set the maximum allowed space usage to the current total size.
|
||||
sfm->SetMaxAllowedSpaceUsage(files_size + 1);
|
||||
|
||||
bool max_allowed_space_reached = false;
|
||||
bool delete_blob_file = false;
|
||||
// Sync point called after blob file is closed and max allowed space is
|
||||
// checked.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileCompletionCallback::CallBack::MaxAllowedSpaceReached",
|
||||
[&](void* /*arg*/) { max_allowed_space_reached = true; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable::AfterDeleteFile",
|
||||
[&](void* /*arg*/) { delete_blob_file = true; });
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
// This flush will fail
|
||||
ASSERT_NOK(Flush());
|
||||
ASSERT_TRUE(max_allowed_space_reached);
|
||||
ASSERT_TRUE(delete_blob_file);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
@@ -821,7 +1129,7 @@ TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
ASSERT_OK(Flush());
|
||||
uint64_t total_file_size = 0;
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllSSTFiles(&files_in_db, &total_file_size));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &total_file_size));
|
||||
// Set the maximum allowed space usage to the current total size
|
||||
sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1);
|
||||
|
||||
@@ -869,7 +1177,7 @@ TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
|
||||
ASSERT_OK(Flush());
|
||||
uint64_t total_file_size = 0;
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllSSTFiles(&files_in_db, &total_file_size));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &total_file_size));
|
||||
// Set the maximum allowed space usage to the current total size
|
||||
sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1);
|
||||
|
||||
@@ -986,7 +1294,7 @@ TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
|
||||
ASSERT_TRUE(bg_error_set);
|
||||
uint64_t total_sst_files_size = 0;
|
||||
std::unordered_map<std::string, uint64_t> files_in_db;
|
||||
ASSERT_OK(GetAllSSTFiles(&files_in_db, &total_sst_files_size));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &files_in_db, &total_sst_files_size));
|
||||
ASSERT_GE(total_sst_files_size, limit_mb * 1024 * 1024);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
+15
-4
@@ -247,17 +247,21 @@ TEST_F(DBTest, SkipDelay) {
|
||||
wo.sync = sync;
|
||||
wo.disableWAL = disableWAL;
|
||||
wo.no_slowdown = true;
|
||||
dbfull()->Put(wo, "foo", "bar");
|
||||
// Large enough to exceed allowance for one time interval
|
||||
std::string large_value(1024, 'x');
|
||||
// Perhaps ideally this first write would fail because of delay, but
|
||||
// the current implementation does not guarantee that.
|
||||
dbfull()->Put(wo, "foo", large_value).PermitUncheckedError();
|
||||
// We need the 2nd write to trigger delay. This is because delay is
|
||||
// estimated based on the last write size which is 0 for the first write.
|
||||
ASSERT_NOK(dbfull()->Put(wo, "foo2", "bar2"));
|
||||
ASSERT_NOK(dbfull()->Put(wo, "foo2", large_value));
|
||||
ASSERT_GE(sleep_count.load(), 0);
|
||||
ASSERT_GE(wait_count.load(), 0);
|
||||
token.reset();
|
||||
|
||||
token = dbfull()->TEST_write_controler().GetDelayToken(1000000000);
|
||||
token = dbfull()->TEST_write_controler().GetDelayToken(1000000);
|
||||
wo.no_slowdown = false;
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo3", "bar3"));
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo3", large_value));
|
||||
ASSERT_GE(sleep_count.load(), 1);
|
||||
token.reset();
|
||||
}
|
||||
@@ -2332,6 +2336,13 @@ TEST_F(DBTest, ReadonlyDBGetLiveManifestSize) {
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetLiveBlobFiles) {
|
||||
// Note: the following prevents an otherwise harmless data race between the
|
||||
// test setup code (AddBlobFile) below and the periodic stat dumping thread.
|
||||
Options options = CurrentOptions();
|
||||
options.stats_dump_period_sec = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
+135
-7
@@ -154,8 +154,14 @@ class PartitionedIndexTestListener : public EventListener {
|
||||
};
|
||||
|
||||
TEST_F(DBTest2, PartitionedIndexUserToInternalKey) {
|
||||
const int kValueSize = 10500;
|
||||
const int kNumEntriesPerFile = 1000;
|
||||
const int kNumFiles = 3;
|
||||
const int kNumDistinctKeys = 30;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
|
||||
PartitionedIndexTestListener* listener = new PartitionedIndexTestListener();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
@@ -164,14 +170,15 @@ TEST_F(DBTest2, PartitionedIndexUserToInternalKey) {
|
||||
Reopen(options);
|
||||
Random rnd(301);
|
||||
|
||||
for (int i = 0; i < 3000; i++) {
|
||||
int j = i % 30;
|
||||
std::string value = rnd.RandomString(10500);
|
||||
ASSERT_OK(Put("keykey_" + std::to_string(j), value));
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
for (int i = 0; i < kNumFiles; i++) {
|
||||
for (int j = 0; j < kNumEntriesPerFile; j++) {
|
||||
int key_id = (i * kNumEntriesPerFile + j) % kNumDistinctKeys;
|
||||
std::string value = rnd.RandomString(kValueSize);
|
||||
ASSERT_OK(Put("keykey_" + std::to_string(key_id), value));
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
for (auto s : snapshots) {
|
||||
db_->ReleaseSnapshot(s);
|
||||
@@ -5421,6 +5428,98 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
class RenameCurrentTest : public DBTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
RenameCurrentTest()
|
||||
: DBTestBase("rename_current_test", /*env_do_fsync=*/true),
|
||||
sync_point_(GetParam()) {}
|
||||
|
||||
~RenameCurrentTest() override {}
|
||||
|
||||
void SetUp() override {
|
||||
env_->no_file_overwrite_.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
env_->no_file_overwrite_.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
void SetupSyncPoints() {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [&](void* arg) {
|
||||
Status* s = reinterpret_cast<Status*>(arg);
|
||||
assert(s);
|
||||
*s = Status::IOError("Injected IO error.");
|
||||
});
|
||||
}
|
||||
|
||||
const std::string sync_point_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DistributedFS, RenameCurrentTest,
|
||||
::testing::Values("SetCurrentFile:BeforeRename",
|
||||
"SetCurrentFile:AfterRename"));
|
||||
|
||||
TEST_P(RenameCurrentTest, Open) {
|
||||
Destroy(last_options_);
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
SetupSyncPoints();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_NOK(s);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
TEST_P(RenameCurrentTest, Flush) {
|
||||
Destroy(last_options_);
|
||||
Options options = GetDefaultOptions();
|
||||
options.max_manifest_file_size = 1;
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("key", "value"));
|
||||
SetupSyncPoints();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_NOK(Flush());
|
||||
|
||||
ASSERT_NOK(Put("foo", "value"));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ("value", Get("key"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
}
|
||||
|
||||
TEST_P(RenameCurrentTest, Compaction) {
|
||||
Destroy(last_options_);
|
||||
Options options = GetDefaultOptions();
|
||||
options.max_manifest_file_size = 1;
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("a", "a_value"));
|
||||
ASSERT_OK(Put("c", "c_value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("b", "b_value"));
|
||||
ASSERT_OK(Put("d", "d_value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SetupSyncPoints();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_NOK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
|
||||
ASSERT_NOK(Put("foo", "value"));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Reopen(options);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ("d_value", Get("d"));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// WAL recovery mode is WALRecoveryMode::kPointInTimeRecovery.
|
||||
@@ -5448,6 +5547,35 @@ TEST_F(DBTest2, PointInTimeRecoveryWithIOErrorWhileReadingWal) {
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, PointInTimeRecoveryWithSyncFailureInCFCreation) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCallFlush:Start:1",
|
||||
"PointInTimeRecoveryWithSyncFailureInCFCreation:1"},
|
||||
{"PointInTimeRecoveryWithSyncFailureInCFCreation:2",
|
||||
"DBImpl::BackgroundCallFlush:Start:2"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateColumnFamilies({"test1"}, Options());
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
|
||||
// Creating a CF when a flush is going on, log is synced but the
|
||||
// closed log file is not synced and corrupted.
|
||||
port::Thread flush_thread([&]() { ASSERT_NOK(Flush()); });
|
||||
TEST_SYNC_POINT("PointInTimeRecoveryWithSyncFailureInCFCreation:1");
|
||||
CreateColumnFamilies({"test2"}, Options());
|
||||
env_->corrupt_in_sync_ = true;
|
||||
TEST_SYNC_POINT("PointInTimeRecoveryWithSyncFailureInCFCreation:2");
|
||||
flush_thread.join();
|
||||
env_->corrupt_in_sync_ = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Reopening the DB should not corrupt anything
|
||||
Options options = CurrentOptions();
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
ReopenWithColumnFamilies({"default", "test1", "test2"}, options);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
+36
-7
@@ -10,6 +10,7 @@
|
||||
#include "db/db_test_util.h"
|
||||
|
||||
#include "db/forward_iterator.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/env_encryption.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
@@ -43,6 +44,7 @@ SpecialEnv::SpecialEnv(Env* base, bool time_elapse_only_sleep)
|
||||
manifest_sync_error_.store(false, std::memory_order_release);
|
||||
manifest_write_error_.store(false, std::memory_order_release);
|
||||
log_write_error_.store(false, std::memory_order_release);
|
||||
no_file_overwrite_.store(false, std::memory_order_release);
|
||||
random_file_open_counter_.store(0, std::memory_order_relaxed);
|
||||
delete_count_.store(0, std::memory_order_relaxed);
|
||||
num_open_wal_file_.store(0);
|
||||
@@ -499,6 +501,7 @@ Options DBTestBase::GetOptions(
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithPartitionedIndex: {
|
||||
table_options.format_version = 3;
|
||||
table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
break;
|
||||
@@ -1125,8 +1128,34 @@ std::string DBTestBase::FilesPerLevel(int cf) {
|
||||
result.resize(last_non_zero_offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::vector<uint64_t> DBTestBase::GetBlobFileNumbers() {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
assert(current);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
|
||||
std::vector<uint64_t> result;
|
||||
result.reserve(blob_files.size());
|
||||
|
||||
for (const auto& blob_file : blob_files) {
|
||||
result.emplace_back(blob_file.first);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t DBTestBase::CountFiles() {
|
||||
size_t count = 0;
|
||||
std::vector<std::string> files;
|
||||
@@ -1435,26 +1464,26 @@ void DBTestBase::CopyFile(const std::string& source,
|
||||
ASSERT_OK(destfile->Close());
|
||||
}
|
||||
|
||||
Status DBTestBase::GetAllSSTFiles(
|
||||
std::unordered_map<std::string, uint64_t>* sst_files,
|
||||
Status DBTestBase::GetAllDataFiles(
|
||||
const FileType file_type, std::unordered_map<std::string, uint64_t>* files,
|
||||
uint64_t* total_size /* = nullptr */) {
|
||||
if (total_size) {
|
||||
*total_size = 0;
|
||||
}
|
||||
std::vector<std::string> files;
|
||||
Status s = env_->GetChildren(dbname_, &files);
|
||||
std::vector<std::string> children;
|
||||
Status s = env_->GetChildren(dbname_, &children);
|
||||
if (s.ok()) {
|
||||
for (auto& file_name : files) {
|
||||
for (auto& file_name : children) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(file_name, &number, &type) && type == kTableFile) {
|
||||
if (ParseFileName(file_name, &number, &type) && type == file_type) {
|
||||
std::string file_path = dbname_ + "/" + file_name;
|
||||
uint64_t file_size = 0;
|
||||
s = env_->GetFileSize(file_path, &file_size);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
(*sst_files)[file_path] = file_size;
|
||||
(*files)[file_path] = file_size;
|
||||
if (total_size) {
|
||||
*total_size += file_size;
|
||||
}
|
||||
|
||||
+47
-4
@@ -23,7 +23,6 @@
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "rocksdb/cache.h"
|
||||
@@ -40,7 +39,6 @@
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/mock_time_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/cast_util.h"
|
||||
@@ -50,6 +48,7 @@
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class MockEnv;
|
||||
|
||||
namespace anon {
|
||||
class AtomicCounter {
|
||||
@@ -230,6 +229,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
return base_->Append(data);
|
||||
}
|
||||
}
|
||||
Status Append(
|
||||
const Slice& data,
|
||||
const DataVerificationInfo& /* verification_info */) override {
|
||||
return Append(data);
|
||||
}
|
||||
Status PositionedAppend(const Slice& data, uint64_t offset) override {
|
||||
if (env_->table_write_callback_) {
|
||||
(*env_->table_write_callback_)();
|
||||
@@ -244,6 +248,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
return base_->PositionedAppend(data, offset);
|
||||
}
|
||||
}
|
||||
Status PositionedAppend(
|
||||
const Slice& data, uint64_t offset,
|
||||
const DataVerificationInfo& /* verification_info */) override {
|
||||
return PositionedAppend(data, offset);
|
||||
}
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status RangeSync(uint64_t offset, uint64_t nbytes) override {
|
||||
Status s = base_->RangeSync(offset, nbytes);
|
||||
@@ -306,6 +315,12 @@ class SpecialEnv : public EnvWrapper {
|
||||
return base_->Append(data);
|
||||
}
|
||||
}
|
||||
Status Append(
|
||||
const Slice& data,
|
||||
const DataVerificationInfo& /*verification_info*/) override {
|
||||
return Append(data);
|
||||
}
|
||||
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status Close() override { return base_->Close(); }
|
||||
Status Flush() override { return base_->Flush(); }
|
||||
@@ -357,6 +372,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
Status Append(
|
||||
const Slice& data,
|
||||
const DataVerificationInfo& /* verification_info */) override {
|
||||
return Append(data);
|
||||
}
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status Close() override {
|
||||
// SyncPoint is not supported in Released Windows Mode.
|
||||
@@ -373,6 +393,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
Status Flush() override { return base_->Flush(); }
|
||||
Status Sync() override {
|
||||
++env_->sync_counter_;
|
||||
if (env_->corrupt_in_sync_) {
|
||||
Append(std::string(33000, ' '));
|
||||
return Status::IOError("Ingested Sync Failure");
|
||||
}
|
||||
if (env_->skip_fsync_) {
|
||||
return Status::OK();
|
||||
} else {
|
||||
@@ -395,6 +419,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
OtherFile(SpecialEnv* env, std::unique_ptr<WritableFile>&& b)
|
||||
: env_(env), base_(std::move(b)) {}
|
||||
Status Append(const Slice& data) override { return base_->Append(data); }
|
||||
Status Append(
|
||||
const Slice& data,
|
||||
const DataVerificationInfo& /*verification_info*/) override {
|
||||
return Append(data);
|
||||
}
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status Close() override { return base_->Close(); }
|
||||
Status Flush() override { return base_->Flush(); }
|
||||
@@ -415,6 +444,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
std::unique_ptr<WritableFile> base_;
|
||||
};
|
||||
|
||||
if (no_file_overwrite_.load(std::memory_order_acquire) &&
|
||||
target()->FileExists(f).ok()) {
|
||||
return Status::NotSupported("SpecialEnv::no_file_overwrite_ is true.");
|
||||
}
|
||||
|
||||
if (non_writeable_rate_.load(std::memory_order_acquire) > 0) {
|
||||
uint32_t random_number;
|
||||
{
|
||||
@@ -662,6 +696,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Slow down every log write, in micro-seconds.
|
||||
std::atomic<int> log_write_slowdown_;
|
||||
|
||||
// If true, returns Status::NotSupported for file overwrite.
|
||||
std::atomic<bool> no_file_overwrite_;
|
||||
|
||||
// Number of WAL files that are still open for write.
|
||||
std::atomic<int> num_open_wal_file_;
|
||||
|
||||
@@ -684,6 +721,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// If true, all fsync to files and directories are skipped.
|
||||
bool skip_fsync_ = false;
|
||||
|
||||
// If true, ingest the corruption to file during sync.
|
||||
bool corrupt_in_sync_ = false;
|
||||
|
||||
std::atomic<uint32_t> non_writeable_rate_;
|
||||
|
||||
std::atomic<uint32_t> new_writable_count_;
|
||||
@@ -1065,6 +1105,8 @@ class DBTestBase : public testing::Test {
|
||||
int TotalTableFiles(int cf = 0, int levels = -1);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
std::vector<uint64_t> GetBlobFileNumbers();
|
||||
|
||||
// Return spread of files per level
|
||||
std::string FilesPerLevel(int cf = 0);
|
||||
|
||||
@@ -1154,8 +1196,9 @@ class DBTestBase : public testing::Test {
|
||||
void CopyFile(const std::string& source, const std::string& destination,
|
||||
uint64_t size = 0);
|
||||
|
||||
Status GetAllSSTFiles(std::unordered_map<std::string, uint64_t>* sst_files,
|
||||
uint64_t* total_size = nullptr);
|
||||
Status GetAllDataFiles(const FileType file_type,
|
||||
std::unordered_map<std::string, uint64_t>* sst_files,
|
||||
uint64_t* total_size = nullptr);
|
||||
|
||||
std::vector<std::uint64_t> ListTableFiles(Env* env, const std::string& path);
|
||||
|
||||
|
||||
+16
-14
@@ -8,10 +8,10 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
@@ -425,16 +425,18 @@ TEST_F(DBWALTest, RecoverWithBlob) {
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
ASSERT_NE(internal_stats, nullptr);
|
||||
|
||||
const uint64_t expected_bytes =
|
||||
table_file->fd.GetFileSize() + blob_file->GetTotalBlobBytes();
|
||||
|
||||
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
|
||||
ASSERT_FALSE(compaction_stats.empty());
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written, expected_bytes);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 2);
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written, table_file->fd.GetFileSize());
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written_blob,
|
||||
blob_file->GetTotalBlobBytes());
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 1);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files_blob, 1);
|
||||
|
||||
const uint64_t* const cf_stats_value = internal_stats->TEST_GetCFStatsValue();
|
||||
ASSERT_EQ(cf_stats_value[InternalStats::BYTES_FLUSHED], expected_bytes);
|
||||
ASSERT_EQ(cf_stats_value[InternalStats::BYTES_FLUSHED],
|
||||
compaction_stats[0].bytes_written +
|
||||
compaction_stats[0].bytes_written_blob);
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
@@ -1229,7 +1231,7 @@ class RecoveryTestHelper {
|
||||
*count = 0;
|
||||
|
||||
std::shared_ptr<Cache> table_cache = NewLRUCache(50, 0);
|
||||
EnvOptions env_options;
|
||||
FileOptions file_options;
|
||||
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
|
||||
|
||||
std::unique_ptr<VersionSet> versions;
|
||||
@@ -1237,22 +1239,22 @@ class RecoveryTestHelper {
|
||||
WriteController write_controller;
|
||||
|
||||
versions.reset(new VersionSet(
|
||||
test->dbname_, &db_options, env_options, table_cache.get(),
|
||||
test->dbname_, &db_options, file_options, table_cache.get(),
|
||||
&write_buffer_manager, &write_controller,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr));
|
||||
|
||||
wal_manager.reset(
|
||||
new WalManager(db_options, env_options, /*io_tracer=*/nullptr));
|
||||
new WalManager(db_options, file_options, /*io_tracer=*/nullptr));
|
||||
|
||||
std::unique_ptr<log::Writer> current_log_writer;
|
||||
|
||||
for (size_t j = kWALFileOffset; j < wal_count + kWALFileOffset; j++) {
|
||||
uint64_t current_log_number = j;
|
||||
std::string fname = LogFileName(test->dbname_, current_log_number);
|
||||
std::unique_ptr<WritableFile> file;
|
||||
ASSERT_OK(db_options.env->NewWritableFile(fname, &file, env_options));
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
NewLegacyWritableFileWrapper(std::move(file)), fname, env_options));
|
||||
std::unique_ptr<WritableFileWriter> file_writer;
|
||||
ASSERT_OK(WritableFileWriter::Create(db_options.env->GetFileSystem(),
|
||||
fname, file_options, &file_writer,
|
||||
nullptr));
|
||||
current_log_writer.reset(
|
||||
new log::Writer(std::move(file_writer), current_log_number,
|
||||
db_options.recycle_log_file_num > 0));
|
||||
|
||||
@@ -402,12 +402,11 @@ TEST_F(DBBasicTestWithTimestamp, GetApproximateSizes) {
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
std::cout << size << std::endl;
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
TEST_F(DBBasicTestWithTimestamp, SimpleIterate) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
Options options = CurrentOptions();
|
||||
@@ -439,6 +438,7 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
int count = 0;
|
||||
uint64_t key = 0;
|
||||
// Forward iterate.
|
||||
for (it->Seek(Key1(0)), key = start_keys[i]; it->Valid();
|
||||
it->Next(), ++count, ++key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
@@ -447,7 +447,16 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
size_t expected_count = kMaxKey - start_keys[i] + 1;
|
||||
ASSERT_EQ(expected_count, count);
|
||||
|
||||
// SeekToFirst() with lower bound.
|
||||
// Backward iterate.
|
||||
count = 0;
|
||||
for (it->SeekForPrev(Key1(kMaxKey)), key = kMaxKey; it->Valid();
|
||||
it->Prev(), ++count, --key) {
|
||||
CheckIterUserEntry(it.get(), Key1(key), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
}
|
||||
ASSERT_EQ(static_cast<size_t>(kMaxKey) - start_keys[i] + 1, count);
|
||||
|
||||
// SeekToFirst()/SeekToLast() with lower/upper bounds.
|
||||
// Then iter with lower and upper bounds.
|
||||
uint64_t l = 0;
|
||||
uint64_t r = kMaxKey + 1;
|
||||
@@ -465,6 +474,12 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
}
|
||||
ASSERT_EQ(r - std::max(l, start_keys[i]), count);
|
||||
|
||||
for (it->SeekToLast(), key = std::min(r, kMaxKey + 1), count = 0;
|
||||
it->Valid(); it->Prev(), --key, ++count) {
|
||||
CheckIterUserEntry(it.get(), Key1(key - 1), kTypeValue,
|
||||
"value" + std::to_string(i), write_timestamps[i]);
|
||||
}
|
||||
l += (kMaxKey / 100);
|
||||
r -= (kMaxKey / 100);
|
||||
}
|
||||
@@ -472,7 +487,93 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SeekWithPrefixLessThanKey) {
|
||||
class DBBasicTestWithTimestampTableOptions
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<BlockBasedTableOptions::IndexType> {
|
||||
public:
|
||||
explicit DBBasicTestWithTimestampTableOptions()
|
||||
: DBBasicTestWithTimestampBase(
|
||||
"db_basic_test_with_timestamp_table_options") {}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Timestamp, DBBasicTestWithTimestampTableOptions,
|
||||
testing::Values(
|
||||
BlockBasedTableOptions::IndexType::kBinarySearch,
|
||||
BlockBasedTableOptions::IndexType::kHashSearch,
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch,
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey));
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, GetAndMultiGet) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
options.compression = kNoCompression;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.index_type = GetParam();
|
||||
bbto.block_size = 100;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator cmp(kTimestampSize);
|
||||
options.comparator = &cmp;
|
||||
DestroyAndReopen(options);
|
||||
constexpr uint64_t kNumKeys = 1024;
|
||||
for (uint64_t k = 0; k < kNumKeys; ++k) {
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(k), "value" + std::to_string(k)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
{
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
std::string ts_str = Timestamp(2, 0);
|
||||
Slice ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
// verify Get()
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
std::string value_from_get;
|
||||
std::string key_str(it->key().data(), it->key().size());
|
||||
std::string timestamp;
|
||||
ASSERT_OK(db_->Get(read_opts, key_str, &value_from_get, ×tamp));
|
||||
ASSERT_EQ(it->value(), value_from_get);
|
||||
ASSERT_EQ(Timestamp(1, 0), timestamp);
|
||||
}
|
||||
|
||||
// verify MultiGet()
|
||||
constexpr uint64_t step = 2;
|
||||
static_assert(0 == (kNumKeys % step),
|
||||
"kNumKeys must be a multiple of step");
|
||||
for (uint64_t k = 0; k < kNumKeys; k += 2) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
for (size_t i = 0; i < step; ++i) {
|
||||
key_strs.push_back(Key1(k + i));
|
||||
}
|
||||
for (size_t i = 0; i < step; ++i) {
|
||||
keys.emplace_back(key_strs[i]);
|
||||
}
|
||||
std::vector<std::string> values;
|
||||
std::vector<std::string> timestamps;
|
||||
std::vector<Status> statuses =
|
||||
db_->MultiGet(read_opts, keys, &values, ×tamps);
|
||||
ASSERT_EQ(step, statuses.size());
|
||||
ASSERT_EQ(step, values.size());
|
||||
ASSERT_EQ(step, timestamps.size());
|
||||
for (uint64_t i = 0; i < step; ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
ASSERT_EQ("value" + std::to_string(k + i), values[i]);
|
||||
ASSERT_EQ(Timestamp(1, 0), timestamps[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, SeekWithPrefixLessThanKey) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -483,6 +584,7 @@ TEST_F(DBBasicTestWithTimestamp, SeekWithPrefixLessThanKey) {
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = true;
|
||||
bbto.index_type = GetParam();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
@@ -527,7 +629,7 @@ TEST_F(DBBasicTestWithTimestamp, SeekWithPrefixLessThanKey) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SeekWithPrefixLargerThanKey) {
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, SeekWithPrefixLongerThanKey) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -538,6 +640,7 @@ TEST_F(DBBasicTestWithTimestamp, SeekWithPrefixLargerThanKey) {
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = true;
|
||||
bbto.index_type = GetParam();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
@@ -580,7 +683,7 @@ TEST_F(DBBasicTestWithTimestamp, SeekWithPrefixLargerThanKey) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SeekWithBound) {
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, SeekWithBound) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -589,6 +692,7 @@ TEST_F(DBBasicTestWithTimestamp, SeekWithBound) {
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = true;
|
||||
bbto.index_type = GetParam();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
@@ -600,38 +704,135 @@ TEST_F(DBBasicTestWithTimestamp, SeekWithBound) {
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo1", "bar"));
|
||||
ASSERT_OK(db_->Put(write_opts, "foo1", "bar1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo2", "bar"));
|
||||
ASSERT_OK(db_->Put(write_opts, "foo2", "bar2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Move sst file to next level
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo3", "bar"));
|
||||
for (int i = 3; i < 9; ++i) {
|
||||
ASSERT_OK(db_->Put(write_opts, "foo" + std::to_string(i),
|
||||
"bar" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string read_ts = Timestamp(2, 0);
|
||||
ts = read_ts;
|
||||
read_opts.timestamp = &ts;
|
||||
std::string up_bound = "foo5";
|
||||
std::string up_bound = "foo5"; // exclusive
|
||||
Slice up_bound_slice = up_bound;
|
||||
std::string lo_bound = "foo2"; // inclusive
|
||||
Slice lo_bound_slice = lo_bound;
|
||||
read_opts.iterate_upper_bound = &up_bound_slice;
|
||||
read_opts.iterate_lower_bound = &lo_bound_slice;
|
||||
read_opts.auto_prefix_mode = true;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
// Make sure the prefix extractor doesn't include timestamp, otherwise it
|
||||
// may return invalid result.
|
||||
iter->Seek("foo");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
CheckIterUserEntry(iter.get(), lo_bound, kTypeValue, "bar2",
|
||||
Timestamp(1, 0));
|
||||
iter->SeekToFirst();
|
||||
CheckIterUserEntry(iter.get(), lo_bound, kTypeValue, "bar2",
|
||||
Timestamp(1, 0));
|
||||
iter->SeekForPrev("g");
|
||||
CheckIterUserEntry(iter.get(), "foo4", kTypeValue, "bar4", Timestamp(1, 0));
|
||||
iter->SeekToLast();
|
||||
CheckIterUserEntry(iter.get(), "foo4", kTypeValue, "bar4", Timestamp(1, 0));
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, ChangeIterationDirection) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
const std::vector<std::string> timestamps = {Timestamp(1, 1), Timestamp(0, 2),
|
||||
Timestamp(4, 3)};
|
||||
const std::vector<std::tuple<std::string, std::string>> kvs = {
|
||||
std::make_tuple("aa", "value1"), std::make_tuple("ab", "value2")};
|
||||
for (const auto& ts : timestamps) {
|
||||
WriteBatch wb(0, 0, kTimestampSize);
|
||||
for (const auto& kv : kvs) {
|
||||
const std::string& key = std::get<0>(kv);
|
||||
const std::string& value = std::get<1>(kv);
|
||||
ASSERT_OK(wb.Put(key, value));
|
||||
}
|
||||
|
||||
ASSERT_OK(wb.AssignTimestamp(ts));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &wb));
|
||||
}
|
||||
std::string read_ts_str = Timestamp(5, 3);
|
||||
Slice read_ts = read_ts_str;
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
|
||||
it->SeekToFirst();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
it->Prev();
|
||||
ASSERT_FALSE(it->Valid());
|
||||
|
||||
it->SeekToLast();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
uint64_t prev_reseek_count =
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
ASSERT_EQ(0, prev_reseek_count);
|
||||
it->Next();
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_EQ(1 + prev_reseek_count,
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
|
||||
it->Seek(std::get<0>(kvs[0]));
|
||||
CheckIterUserEntry(it.get(), std::get<0>(kvs[0]), kTypeValue,
|
||||
std::get<1>(kvs[0]), Timestamp(4, 3));
|
||||
it->Next();
|
||||
CheckIterUserEntry(it.get(), std::get<0>(kvs[1]), kTypeValue,
|
||||
std::get<1>(kvs[1]), Timestamp(4, 3));
|
||||
it->Prev();
|
||||
CheckIterUserEntry(it.get(), std::get<0>(kvs[0]), kTypeValue,
|
||||
std::get<1>(kvs[0]), Timestamp(4, 3));
|
||||
|
||||
prev_reseek_count =
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
ASSERT_EQ(1, prev_reseek_count);
|
||||
it->Next();
|
||||
CheckIterUserEntry(it.get(), std::get<0>(kvs[1]), kTypeValue,
|
||||
std::get<1>(kvs[1]), Timestamp(4, 3));
|
||||
ASSERT_EQ(1 + prev_reseek_count,
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
|
||||
it->SeekForPrev(std::get<0>(kvs[1]));
|
||||
CheckIterUserEntry(it.get(), std::get<0>(kvs[1]), kTypeValue,
|
||||
std::get<1>(kvs[1]), Timestamp(4, 3));
|
||||
it->Prev();
|
||||
CheckIterUserEntry(it.get(), std::get<0>(kvs[0]), kTypeValue,
|
||||
std::get<1>(kvs[0]), Timestamp(4, 3));
|
||||
|
||||
prev_reseek_count =
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
it->Next();
|
||||
CheckIterUserEntry(it.get(), std::get<0>(kvs[1]), kTypeValue,
|
||||
std::get<1>(kvs[1]), Timestamp(4, 3));
|
||||
ASSERT_EQ(1 + prev_reseek_count,
|
||||
options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
|
||||
it.reset();
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterateLowerTsBound) {
|
||||
constexpr int kNumKeysPerFile = 128;
|
||||
constexpr uint64_t kMaxKey = 1024;
|
||||
@@ -830,6 +1031,16 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToTargetTimestamp) {
|
||||
CheckIterUserEntry(iter.get(), "foo", kTypeValue, "value0", ts_str);
|
||||
ASSERT_EQ(
|
||||
1, options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
|
||||
ts_str = Timestamp(kNumKeys, 0);
|
||||
ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
iter.reset(db_->NewIterator(read_opts));
|
||||
iter->SeekToLast();
|
||||
CheckIterUserEntry(iter.get(), "foo", kTypeValue,
|
||||
"value" + std::to_string(kNumKeys - 1), ts_str);
|
||||
ASSERT_EQ(
|
||||
2, options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
Close();
|
||||
}
|
||||
@@ -880,6 +1091,47 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToNextUserKey) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, ReseekToUserKeyBeforeSavedKey) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
constexpr size_t kNumKeys = 16;
|
||||
options.max_sequential_skip_in_iterations = kNumKeys / 2;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
std::string ts_str = Timestamp(static_cast<uint64_t>(i + 1), 0);
|
||||
Slice ts = ts_str;
|
||||
WriteOptions write_opts;
|
||||
write_opts.timestamp = &ts;
|
||||
Status s = db_->Put(write_opts, "b", "value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
{
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
WriteOptions write_opts;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, "a", "value"));
|
||||
}
|
||||
{
|
||||
ReadOptions read_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
iter->SeekToLast();
|
||||
iter->Prev();
|
||||
CheckIterUserEntry(iter.get(), "a", kTypeValue, "value", ts_str);
|
||||
ASSERT_EQ(
|
||||
1, options.statistics->getTickerCount(NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetWithFastLocalBloom) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -920,7 +1172,7 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetWithFastLocalBloom) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetWithPrefix) {
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, MultiGetWithPrefix) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -929,6 +1181,7 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetWithPrefix) {
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = false;
|
||||
bbto.index_type = GetParam();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
@@ -961,7 +1214,7 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetWithPrefix) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetWithMemBloomFilter) {
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, MultiGetWithMemBloomFilter) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -970,6 +1223,7 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetWithMemBloomFilter) {
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = false;
|
||||
bbto.index_type = GetParam();
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
@@ -1059,7 +1313,7 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetRangeFiltering) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetPrefixFilter) {
|
||||
TEST_P(DBBasicTestWithTimestampTableOptions, MultiGetPrefixFilter) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -1068,6 +1322,7 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetPrefixFilter) {
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = false;
|
||||
bbto.index_type = GetParam();
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
@@ -1102,7 +1357,7 @@ TEST_F(DBBasicTestWithTimestamp, MultiGetPrefixFilter) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MaxKeysSkipped) {
|
||||
TEST_F(DBBasicTestWithTimestamp, MaxKeysSkippedDuringNext) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
@@ -1141,6 +1396,45 @@ TEST_F(DBBasicTestWithTimestamp, MaxKeysSkipped) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MaxKeysSkippedDuringPrev) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
constexpr size_t max_skippable_internal_keys = 2;
|
||||
const size_t kNumKeys = max_skippable_internal_keys + 2;
|
||||
WriteOptions write_opts;
|
||||
Status s;
|
||||
{
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, "b", "value"));
|
||||
}
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
std::string ts_str = Timestamp(static_cast<uint64_t>(i + 1), 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
s = db_->Put(write_opts, "a", "value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
{
|
||||
ReadOptions read_opts;
|
||||
read_opts.max_skippable_internal_keys = max_skippable_internal_keys;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
iter->SeekToLast();
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->status().IsIncomplete());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
// Create two L0, and compact them to a new L1. In this test, L1 is L_bottom.
|
||||
// Two L0s:
|
||||
// f1 f2
|
||||
@@ -1204,7 +1498,8 @@ class DBBasicTestWithTimestampFilterPrefixSettings
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<std::shared_ptr<const FilterPolicy>, bool, bool,
|
||||
std::shared_ptr<const SliceTransform>, bool, double>> {
|
||||
std::shared_ptr<const SliceTransform>, bool, double,
|
||||
BlockBasedTableOptions::IndexType>> {
|
||||
public:
|
||||
DBBasicTestWithTimestampFilterPrefixSettings()
|
||||
: DBBasicTestWithTimestampBase(
|
||||
@@ -1219,6 +1514,7 @@ TEST_P(DBBasicTestWithTimestampFilterPrefixSettings, GetAndMultiGet) {
|
||||
bbto.filter_policy = std::get<0>(GetParam());
|
||||
bbto.whole_key_filtering = std::get<1>(GetParam());
|
||||
bbto.cache_index_and_filter_blocks = std::get<2>(GetParam());
|
||||
bbto.index_type = std::get<6>(GetParam());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.prefix_extractor = std::get<3>(GetParam());
|
||||
options.memtable_whole_key_filtering = std::get<4>(GetParam());
|
||||
@@ -1323,7 +1619,12 @@ INSTANTIATE_TEST_CASE_P(
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(4)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(7)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(8))),
|
||||
::testing::Bool(), ::testing::Values(0, 0.1)));
|
||||
::testing::Bool(), ::testing::Values(0, 0.1),
|
||||
::testing::Values(
|
||||
BlockBasedTableOptions::IndexType::kBinarySearch,
|
||||
BlockBasedTableOptions::IndexType::kHashSearch,
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch,
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey)));
|
||||
|
||||
class DataVisibilityTest : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
@@ -2404,14 +2705,15 @@ class DBBasicTestWithTimestampPrefixSeek
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<std::shared_ptr<const SliceTransform>,
|
||||
std::shared_ptr<const FilterPolicy>, bool>> {
|
||||
std::shared_ptr<const FilterPolicy>, bool,
|
||||
BlockBasedTableOptions::IndexType>> {
|
||||
public:
|
||||
DBBasicTestWithTimestampPrefixSeek()
|
||||
: DBBasicTestWithTimestampBase(
|
||||
"/db_basic_test_with_timestamp_prefix_seek") {}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
TEST_P(DBBasicTestWithTimestampPrefixSeek, IterateWithPrefix) {
|
||||
const size_t kNumKeysPerFile = 128;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -2423,6 +2725,7 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy = std::get<1>(GetParam());
|
||||
bbto.index_type = std::get<3>(GetParam());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -2461,6 +2764,13 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
"value" + std::to_string(i), write_ts_list[i]);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
// Seek to kMinKey
|
||||
iter->Seek(Key1(kMinKey));
|
||||
CheckIterUserEntry(iter.get(), Key1(kMinKey), kTypeValue,
|
||||
"value" + std::to_string(i), write_ts_list[i]);
|
||||
iter->Prev();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
}
|
||||
const std::vector<uint64_t> targets = {kMinKey, kMinKey + 0x10,
|
||||
kMinKey + 0x100, kMaxKey};
|
||||
@@ -2476,6 +2786,7 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
Slice read_ts = read_ts_list[i];
|
||||
read_opts.timestamp = &read_ts;
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
// Forward and backward iterate.
|
||||
for (size_t j = 0; j != targets.size(); ++j) {
|
||||
std::string start_key = Key1(targets[j]);
|
||||
uint64_t expected_ub =
|
||||
@@ -2499,6 +2810,24 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
it->Next();
|
||||
}
|
||||
ASSERT_EQ(expected_ub - targets[j] + 1, count);
|
||||
|
||||
count = 0;
|
||||
expected_key = targets[j];
|
||||
it->SeekForPrev(start_key);
|
||||
uint64_t expected_lb = (targets[j] & kPrefixMask);
|
||||
while (it->Valid()) {
|
||||
// Out of prefix
|
||||
if (!read_opts.prefix_same_as_start &&
|
||||
pe->Transform(it->key()) != pe->Transform(start_key)) {
|
||||
break;
|
||||
}
|
||||
CheckIterUserEntry(it.get(), Key1(expected_key), kTypeValue,
|
||||
"value" + std::to_string(i), write_ts_list[i]);
|
||||
++count;
|
||||
--expected_key;
|
||||
it->Prev();
|
||||
}
|
||||
ASSERT_EQ(targets[j] - std::max(expected_lb, kMinKey) + 1, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2521,19 +2850,25 @@ INSTANTIATE_TEST_CASE_P(
|
||||
std::shared_ptr<const FilterPolicy>(
|
||||
NewBloomFilterPolicy(20 /*bits_per_key*/,
|
||||
false))),
|
||||
::testing::Bool()));
|
||||
::testing::Bool(),
|
||||
::testing::Values(
|
||||
BlockBasedTableOptions::IndexType::kBinarySearch,
|
||||
BlockBasedTableOptions::IndexType::kHashSearch,
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch,
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey)));
|
||||
|
||||
class DBBasicTestWithTsIterTombstones
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<std::shared_ptr<const SliceTransform>,
|
||||
std::shared_ptr<const FilterPolicy>, int>> {
|
||||
std::shared_ptr<const FilterPolicy>, int,
|
||||
BlockBasedTableOptions::IndexType>> {
|
||||
public:
|
||||
DBBasicTestWithTsIterTombstones()
|
||||
: DBBasicTestWithTimestampBase("/db_basic_ts_iter_tombstones") {}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithTsIterTombstones, ForwardIterDelete) {
|
||||
TEST_P(DBBasicTestWithTsIterTombstones, IterWithDelete) {
|
||||
constexpr size_t kNumKeysPerFile = 128;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -2544,6 +2879,7 @@ TEST_P(DBBasicTestWithTsIterTombstones, ForwardIterDelete) {
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy = std::get<1>(GetParam());
|
||||
bbto.index_type = std::get<3>(GetParam());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.num_levels = std::get<2>(GetParam());
|
||||
DestroyAndReopen(options);
|
||||
@@ -2563,7 +2899,7 @@ TEST_P(DBBasicTestWithTsIterTombstones, ForwardIterDelete) {
|
||||
}
|
||||
++key;
|
||||
} while (true);
|
||||
// Delete them all
|
||||
|
||||
ts = write_ts_strs[1];
|
||||
write_opts.timestamp = &ts;
|
||||
for (key = kMaxKey; key >= kMinKey; --key) {
|
||||
@@ -2590,6 +2926,13 @@ TEST_P(DBBasicTestWithTsIterTombstones, ForwardIterDelete) {
|
||||
ASSERT_EQ("value1" + std::to_string(key), iter->value());
|
||||
}
|
||||
ASSERT_EQ((kMaxKey - kMinKey + 1) / 2, count);
|
||||
|
||||
for (iter->SeekToLast(), count = 0, key = kMaxKey; iter->Valid();
|
||||
key -= 2, ++count, iter->Prev()) {
|
||||
ASSERT_EQ(Key1(key), iter->key());
|
||||
ASSERT_EQ("value1" + std::to_string(key), iter->value());
|
||||
}
|
||||
ASSERT_EQ((kMaxKey - kMinKey + 1) / 2, count);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
@@ -2605,7 +2948,12 @@ INSTANTIATE_TEST_CASE_P(
|
||||
NewBloomFilterPolicy(10, false)),
|
||||
std::shared_ptr<const FilterPolicy>(
|
||||
NewBloomFilterPolicy(20, false))),
|
||||
::testing::Values(2, 6)));
|
||||
::testing::Values(2, 6),
|
||||
::testing::Values(
|
||||
BlockBasedTableOptions::IndexType::kBinarySearch,
|
||||
BlockBasedTableOptions::IndexType::kHashSearch,
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch,
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey)));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
+2
-1
@@ -122,7 +122,7 @@ struct ParsedInternalKey {
|
||||
|
||||
void SetTimestamp(const Slice& ts) {
|
||||
assert(ts.size() <= user_key.size());
|
||||
const char* addr = user_key.data() - ts.size();
|
||||
const char* addr = user_key.data() + user_key.size() - ts.size();
|
||||
memcpy(const_cast<char*>(addr), ts.data(), ts.size());
|
||||
}
|
||||
};
|
||||
@@ -512,6 +512,7 @@ class IterKey {
|
||||
|
||||
bool IsKeyPinned() const { return (key_ != buf_); }
|
||||
|
||||
// user_key does not have timestamp.
|
||||
void SetInternalKey(const Slice& key_prefix, const Slice& user_key,
|
||||
SequenceNumber s,
|
||||
ValueType value_type = kValueTypeForSeek,
|
||||
|
||||
+63
-2
@@ -4,9 +4,11 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#include "db/error_handler.h"
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -274,6 +276,12 @@ const Status& ErrorHandler::SetBGError(const Status& bg_err,
|
||||
return bg_err;
|
||||
}
|
||||
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set regular background error\n");
|
||||
|
||||
bool paranoid = db_options_.paranoid_checks;
|
||||
Status::Severity sev = Status::Severity::kFatalError;
|
||||
Status new_bg_err;
|
||||
@@ -399,6 +407,13 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
if (recovery_in_prog_ && recovery_error_.ok()) {
|
||||
recovery_error_ = bg_err;
|
||||
}
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
}
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Set background IO error as unrecoverable error\n");
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&bg_err, db_mutex_, &auto_recovery);
|
||||
recover_context_ = context;
|
||||
@@ -416,12 +431,26 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&new_bg_io_err, db_mutex_,
|
||||
&auto_recovery);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set background retryable IO error\n");
|
||||
if (BackgroundErrorReason::kCompaction == reason) {
|
||||
// We map the retryable IO error during compaction to soft error. Since
|
||||
// compaction can reschedule by itself. We will not set the BG error in
|
||||
// this case
|
||||
// TODO: a better way to set or clean the retryable IO error which
|
||||
// happens during compaction SST file write.
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_AUTORESUME_COUNT);
|
||||
}
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Compaction will schedule by itself to resume\n");
|
||||
return bg_error_;
|
||||
} else if (BackgroundErrorReason::kFlushNoWAL == reason ||
|
||||
BackgroundErrorReason::kManifestWriteNoWAL == reason) {
|
||||
@@ -455,6 +484,9 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
}
|
||||
} else {
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
}
|
||||
return SetBGError(new_bg_io_err, reason);
|
||||
}
|
||||
}
|
||||
@@ -603,7 +635,12 @@ const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
// Auto resume BG error is not enabled, directly return bg_error_.
|
||||
return bg_error_;
|
||||
}
|
||||
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_AUTORESUME_COUNT);
|
||||
}
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Call StartRecoverFromRetryableBGIOError to resume\n");
|
||||
if (recovery_thread_) {
|
||||
// In this case, if recovery_in_prog_ is false, current thread should
|
||||
// wait the previous recover thread to finish and create a new thread
|
||||
@@ -642,6 +679,7 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
DBRecoverContext context = recover_context_;
|
||||
int resume_count = db_options_.max_bgerror_resume_count;
|
||||
uint64_t wait_interval = db_options_.bgerror_resume_retry_interval;
|
||||
uint64_t retry_count = 0;
|
||||
// Recover from the retryable error. Create a separate thread to do it.
|
||||
while (resume_count > 0) {
|
||||
if (end_recovery_) {
|
||||
@@ -651,15 +689,24 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:BeforeResume1");
|
||||
recovery_io_error_ = IOStatus::OK();
|
||||
recovery_error_ = Status::OK();
|
||||
retry_count++;
|
||||
Status s = db_->ResumeImpl(context);
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:AfterResume0");
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:AfterResume1");
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT);
|
||||
}
|
||||
if (s.IsShutdownInProgress() ||
|
||||
bg_error_.severity() >= Status::Severity::kFatalError) {
|
||||
// If DB shutdown in progress or the error severity is higher than
|
||||
// Hard Error, stop auto resume and returns.
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:RecoverFail0");
|
||||
recovery_in_prog_ = false;
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!recovery_io_error_.ok() &&
|
||||
@@ -670,7 +717,7 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
// a period of time and redo auto resume if it is allowed.
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:BeforeWait0");
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:BeforeWait1");
|
||||
int64_t wait_until = db_->env_->NowMicros() + wait_interval;
|
||||
int64_t wait_until = db_options_.clock->NowMicros() + wait_interval;
|
||||
cv_.TimedWait(wait_until);
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:AfterWait0");
|
||||
} else {
|
||||
@@ -686,6 +733,12 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
bg_error_.PermitUncheckedError();
|
||||
EventHelpers::NotifyOnErrorRecoveryCompleted(db_options_.listeners,
|
||||
old_bg_error, db_mutex_);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT);
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
recovery_in_prog_ = false;
|
||||
if (soft_error_no_bg_work_) {
|
||||
soft_error_no_bg_work_ = false;
|
||||
@@ -696,6 +749,10 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
// In this case: 1) recovery_io_error is more serious or not retryable
|
||||
// 2) other Non IO recovery_error happens. The auto recovery stops.
|
||||
recovery_in_prog_ = false;
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -703,6 +760,10 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
}
|
||||
recovery_in_prog_ = false;
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:LoopOut");
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_COUNT, retry_count);
|
||||
}
|
||||
return;
|
||||
#else
|
||||
return;
|
||||
|
||||
+5
-1
@@ -37,7 +37,8 @@ class ErrorHandler {
|
||||
db_mutex_(db_mutex),
|
||||
auto_recovery_(false),
|
||||
recovery_in_prog_(false),
|
||||
soft_error_no_bg_work_(false) {
|
||||
soft_error_no_bg_work_(false),
|
||||
bg_error_stats_(db_options.statistics) {
|
||||
// Clear the checked flag for uninitialized errors
|
||||
bg_error_.PermitUncheckedError();
|
||||
recovery_error_.PermitUncheckedError();
|
||||
@@ -108,6 +109,9 @@ class ErrorHandler {
|
||||
// Used to store the context for recover, such as flush reason.
|
||||
DBRecoverContext recover_context_;
|
||||
|
||||
// The pointer of DB statistics.
|
||||
std::shared_ptr<Statistics> bg_error_stats_;
|
||||
|
||||
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
|
||||
void RecoverFromNoSpace();
|
||||
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
|
||||
+199
-240
@@ -158,6 +158,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWriteError) {
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
@@ -174,13 +175,25 @@ TEST_F(DBErrorHandlingFSTest, FLushWriteError) {
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT));
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableError) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWriteRetryableError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
@@ -188,6 +201,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableError) {
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
@@ -202,11 +216,23 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT));
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
|
||||
@@ -216,7 +242,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
@@ -230,7 +256,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
@@ -241,7 +267,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritFileScopeError) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWriteFileScopeError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
@@ -266,7 +292,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritFileScopeError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
@@ -280,7 +306,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritFileScopeError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
@@ -294,7 +320,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritFileScopeError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
@@ -314,7 +340,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritFileScopeError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
@@ -325,6 +351,95 @@ TEST_F(DBErrorHandlingFSTest, FLushWritFileScopeError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWALWriteRetryableError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
Status s;
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::SyncClosedLogs:Start",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu, sdfsdfsdf"}, options);
|
||||
|
||||
WriteOptions wo = WriteOptions();
|
||||
wo.disableWAL = false;
|
||||
ASSERT_OK(Put(Key(1), "val1", wo));
|
||||
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
auto cfh = dbfull()->GetColumnFamilyHandle(1);
|
||||
s = dbfull()->DropColumnFamily(cfh);
|
||||
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_OK(Put(Key(3), "val3", wo));
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWALAtomicWriteRetryableError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
options.atomic_flush = true;
|
||||
Status s;
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::SyncClosedLogs:Start",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
CreateAndReopenWithCF({"pikachu, sdfsdfsdf"}, options);
|
||||
|
||||
WriteOptions wo = WriteOptions();
|
||||
wo.disableWAL = false;
|
||||
ASSERT_OK(Put(Key(1), "val1", wo));
|
||||
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
auto cfh = dbfull()->GetColumnFamilyHandle(1);
|
||||
s = dbfull()->DropColumnFamily(cfh);
|
||||
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_OK(Put(Key(3), "val3", wo));
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError1) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -333,6 +448,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError1) {
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
@@ -363,11 +479,23 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError1) {
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError2) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWriteNoWALRetryableError2) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
@@ -410,7 +538,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError3) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWriteNoWALRetryableError3) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
@@ -521,7 +649,7 @@ TEST_F(DBErrorHandlingFSTest, ManifestWriteRetryableError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
@@ -567,7 +695,7 @@ TEST_F(DBErrorHandlingFSTest, ManifestWriteFileScopeError) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
@@ -1010,6 +1138,7 @@ TEST_F(DBErrorHandlingFSTest, AutoRecoverFlushError) {
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery();
|
||||
@@ -1028,6 +1157,18 @@ TEST_F(DBErrorHandlingFSTest, AutoRecoverFlushError) {
|
||||
|
||||
s = Put(Key(1), "val");
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT));
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
@@ -1557,7 +1698,7 @@ TEST_F(DBErrorHandlingFSTest, MultiDBVariousErrors) {
|
||||
// to soft error and trigger auto resume. During auto resume, SwitchMemtable
|
||||
// is disabled to avoid small SST tables. Write can still be applied before
|
||||
// the bg error is cleaned unless the memtable is full.
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover1) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableErrorAutoRecover1) {
|
||||
// Activate the FS before the first resume
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -1567,6 +1708,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover1) {
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 100000; // 0.1 second
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
@@ -1594,6 +1736,22 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover1) {
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(3, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(3, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(3, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_EQ(2, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT));
|
||||
HistogramData autoresume_retry;
|
||||
options.statistics->histogramData(ERROR_HANDLER_AUTORESUME_RETRY_COUNT,
|
||||
&autoresume_retry);
|
||||
ASSERT_EQ(autoresume_retry.max, 2);
|
||||
ASSERT_OK(Put(Key(2), "val2", wo));
|
||||
s = Flush();
|
||||
// Since auto resume fails, the bg error is not cleand, flush will
|
||||
@@ -1610,7 +1768,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover1) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover2) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableErrorAutoRecover2) {
|
||||
// Activate the FS before the first resume
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -1620,6 +1778,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover2) {
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 100000; // 0.1 second
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
@@ -1643,6 +1802,22 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover2) {
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_LE(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
ASSERT_LE(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT));
|
||||
HistogramData autoresume_retry;
|
||||
options.statistics->histogramData(ERROR_HANDLER_AUTORESUME_RETRY_COUNT,
|
||||
&autoresume_retry);
|
||||
ASSERT_GE(autoresume_retry.max, 0);
|
||||
ASSERT_OK(Put(Key(2), "val2", wo));
|
||||
s = Flush();
|
||||
// Since auto resume is successful, the bg error is cleaned, flush will
|
||||
@@ -1652,56 +1827,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableeErrorAutoRecover2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, DISABLED_FLushWritRetryableeErrorAutoRecover1) {
|
||||
// Fail the first resume and make the second resume successful
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 100000; // 0.1 second
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"RecoverFromRetryableBGIOError:BeforeWait0",
|
||||
"FLushWritRetryableeErrorAutoRecover1:0"},
|
||||
{"FLushWritRetryableeErrorAutoRecover1:1",
|
||||
"RecoverFromRetryableBGIOError:BeforeWait1"},
|
||||
{"RecoverFromRetryableBGIOError:RecoverSuccess",
|
||||
"FLushWritRetryableeErrorAutoRecover1:2"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover1:0");
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover1:1");
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover1:2");
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeErrorAutoRecover2) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAutoRecover1) {
|
||||
// Activate the FS before the first resume
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -1726,7 +1852,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeErrorAutoRecover2) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
@@ -1741,7 +1867,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeErrorAutoRecover2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeErrorAutoRecover3) {
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAutoRecover2) {
|
||||
// Fail all the resume and let user to resume
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -1761,18 +1887,18 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeErrorAutoRecover3) {
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"FLushWritRetryableeErrorAutoRecover3:0",
|
||||
{{"FLushWritRetryableeErrorAutoRecover2:0",
|
||||
"RecoverFromRetryableBGIOError:BeforeStart"},
|
||||
{"RecoverFromRetryableBGIOError:LoopOut",
|
||||
"FLushWritRetryableeErrorAutoRecover3:1"}});
|
||||
"FLushWritRetryableeErrorAutoRecover2:1"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover3:0");
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover3:1");
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover2:0");
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover2:1");
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -1790,173 +1916,6 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeErrorAutoRecover3) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, DISABLED_FLushWritRetryableeErrorAutoRecover4) {
|
||||
// Fail the first resume and does not do resume second time because
|
||||
// the IO error severity is Fatal Error and not Retryable.
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 10; // 0.1 second
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
IOStatus nr_msg = IOStatus::IOError("No Retryable Fatal IO Error");
|
||||
nr_msg.SetRetryable(false);
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"RecoverFromRetryableBGIOError:BeforeStart",
|
||||
"FLushWritRetryableeErrorAutoRecover4:0"},
|
||||
{"FLushWritRetryableeErrorAutoRecover4:2",
|
||||
"RecoverFromRetryableBGIOError:RecoverFail0"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RecoverFromRetryableBGIOError:BeforeResume1",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, nr_msg); });
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover4:0");
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover4:2");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
// Even the FS is recoverd, due to the Fatal Error in bg_error_ the resume
|
||||
// and flush will all fail.
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_NOK(dbfull()->Resume());
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
ASSERT_NOK(Flush());
|
||||
ASSERT_EQ("NOT_FOUND", Get(Key(2)));
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, DISABLED_FLushWritRetryableeErrorAutoRecover5) {
|
||||
// During the resume, call DB->CLose, make sure the resume thread exist
|
||||
// before close continues. Due to the shutdown, the resume is not successful
|
||||
// and the FS does not become active, so close status is still IO error
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 10; // 0.1 second
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"RecoverFromRetryableBGIOError:BeforeStart",
|
||||
"FLushWritRetryableeErrorAutoRecover5:0"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover5:0");
|
||||
// The first resume will cause recovery_error and its severity is the
|
||||
// Fatal error
|
||||
s = dbfull()->Close();
|
||||
ASSERT_NOK(s);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_NE("val1", Get(Key(1)));
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableeErrorAutoRecover6) {
|
||||
// During the resume, call DB->CLose, make sure the resume thread exist
|
||||
// before close continues. Due to the shutdown, the resume is not successful
|
||||
// and the FS does not become active, so close status is still IO error
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 10; // 0.1 second
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"FLushWritRetryableeErrorAutoRecover6:0",
|
||||
"RecoverFromRetryableBGIOError:BeforeStart"},
|
||||
{"RecoverFromRetryableBGIOError:BeforeWait0",
|
||||
"FLushWritRetryableeErrorAutoRecover6:1"},
|
||||
{"FLushWritRetryableeErrorAutoRecover6:2",
|
||||
"RecoverFromRetryableBGIOError:BeforeWait1"},
|
||||
{"RecoverFromRetryableBGIOError:AfterWait0",
|
||||
"FLushWritRetryableeErrorAutoRecover6:3"}});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover6:0");
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover6:1");
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover6:2");
|
||||
TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover6:3");
|
||||
// The first resume will cause recovery_error and its severity is the
|
||||
// Fatal error
|
||||
s = dbfull()->Close();
|
||||
ASSERT_OK(s);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, ManifestWriteRetryableErrorAutoRecover) {
|
||||
// Fail the first resume and let the second resume be successful
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
@@ -1993,7 +1952,7 @@ TEST_F(DBErrorHandlingFSTest, ManifestWriteRetryableErrorAutoRecover) {
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
TEST_SYNC_POINT("ManifestWriteRetryableErrorAutoRecover:0");
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
@@ -1542,6 +1542,44 @@ TEST_F(ExternalSSTFileBasicTest, OverlappingFiles) {
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestFileAfterDBPut) {
|
||||
// Repro https://github.com/facebook/rocksdb/issues/6245.
|
||||
// Flush three files to L0. Ingest one more file to trigger L0->L1 compaction
|
||||
// via trivial move. The bug happened when L1 files were incorrectly sorted
|
||||
// resulting in an old value for "k" returned by `Get()`.
|
||||
Options options = CurrentOptions();
|
||||
|
||||
ASSERT_OK(Put("k", "a"));
|
||||
Flush();
|
||||
ASSERT_OK(Put("k", "a"));
|
||||
Flush();
|
||||
ASSERT_OK(Put("k", "a"));
|
||||
Flush();
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Current file size should be 0 after sst_file_writer init and before open a
|
||||
// file.
|
||||
ASSERT_EQ(sst_file_writer.FileSize(), 0);
|
||||
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
ASSERT_OK(sst_file_writer.Put("k", "b"));
|
||||
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
|
||||
IngestExternalFileOptions ifo;
|
||||
s = db_->IngestExternalFile({file1}, ifo);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(Get("k"), "b");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
|
||||
testing::Values(std::make_tuple(true, true),
|
||||
std::make_tuple(true, false),
|
||||
|
||||
@@ -293,12 +293,13 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
|
||||
// TODO: The following is duplicated with Cleanup().
|
||||
if (!status.ok()) {
|
||||
IOOptions io_opts;
|
||||
// We failed, remove all files that we copied into the db
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
if (f.internal_file_path.empty()) {
|
||||
continue;
|
||||
}
|
||||
Status s = env_->DeleteFile(f.internal_file_path);
|
||||
Status s = fs_->DeleteFile(f.internal_file_path, io_opts, nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
@@ -367,9 +368,32 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
super_version, force_global_seqno, cfd_->ioptions()->compaction_style,
|
||||
last_seqno, &f, &assigned_seqno);
|
||||
}
|
||||
|
||||
// Modify the smallest/largest internal key to include the sequence number
|
||||
// that we just learned. Only overwrite sequence number zero. There could
|
||||
// be a nonzero sequence number already to indicate a range tombstone's
|
||||
// exclusive endpoint.
|
||||
ParsedInternalKey smallest_parsed, largest_parsed;
|
||||
if (status.ok()) {
|
||||
status = ParseInternalKey(*f.smallest_internal_key.rep(),
|
||||
&smallest_parsed, false /* log_err_key */);
|
||||
}
|
||||
if (status.ok()) {
|
||||
status = ParseInternalKey(*f.largest_internal_key.rep(), &largest_parsed,
|
||||
false /* log_err_key */);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (smallest_parsed.sequence == 0) {
|
||||
UpdateInternalKey(f.smallest_internal_key.rep(), assigned_seqno,
|
||||
smallest_parsed.type);
|
||||
}
|
||||
if (largest_parsed.sequence == 0) {
|
||||
UpdateInternalKey(f.largest_internal_key.rep(), assigned_seqno,
|
||||
largest_parsed.type);
|
||||
}
|
||||
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, assigned_seqno);
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Run",
|
||||
&assigned_seqno);
|
||||
@@ -392,7 +416,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
int64_t temp_current_time = 0;
|
||||
uint64_t current_time = kUnknownFileCreationTime;
|
||||
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
|
||||
if (env_->GetCurrentTime(&temp_current_time).ok()) {
|
||||
if (clock_->GetCurrentTime(&temp_current_time).ok()) {
|
||||
current_time = oldest_ancester_time =
|
||||
static_cast<uint64_t>(temp_current_time);
|
||||
}
|
||||
@@ -410,7 +434,7 @@ void ExternalSstFileIngestionJob::UpdateStats() {
|
||||
// Update internal stats for new ingested files
|
||||
uint64_t total_keys = 0;
|
||||
uint64_t total_l0_files = 0;
|
||||
uint64_t total_time = env_->NowMicros() - job_start_time_;
|
||||
uint64_t total_time = clock_->NowMicros() - job_start_time_;
|
||||
|
||||
EventLoggerStream stream = event_logger_->Log();
|
||||
stream << "event"
|
||||
@@ -466,6 +490,7 @@ void ExternalSstFileIngestionJob::UpdateStats() {
|
||||
}
|
||||
|
||||
void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
IOOptions io_opts;
|
||||
if (!status.ok()) {
|
||||
// We failed to add the files to the database
|
||||
// remove all the files we copied
|
||||
@@ -473,7 +498,7 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
if (f.internal_file_path.empty()) {
|
||||
continue;
|
||||
}
|
||||
Status s = env_->DeleteFile(f.internal_file_path);
|
||||
Status s = fs_->DeleteFile(f.internal_file_path, io_opts, nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
@@ -485,7 +510,7 @@ void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
} else if (status.ok() && ingestion_options_.move_files) {
|
||||
// The files were moved and added successfully, remove original file links
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
Status s = env_->DeleteFile(f.external_file_path);
|
||||
Status s = fs_->DeleteFile(f.external_file_path, io_opts, nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
#include "logging/event_logger.h"
|
||||
#include "options/db_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class Directories;
|
||||
class SystemClock;
|
||||
|
||||
struct IngestedFileInfo {
|
||||
// External file path
|
||||
@@ -73,13 +74,13 @@ struct IngestedFileInfo {
|
||||
class ExternalSstFileIngestionJob {
|
||||
public:
|
||||
ExternalSstFileIngestionJob(
|
||||
Env* env, VersionSet* versions, ColumnFamilyData* cfd,
|
||||
VersionSet* versions, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options, const EnvOptions& env_options,
|
||||
SnapshotList* db_snapshots,
|
||||
const IngestExternalFileOptions& ingestion_options,
|
||||
Directories* directories, EventLogger* event_logger,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: env_(env),
|
||||
: clock_(db_options.clock),
|
||||
fs_(db_options.fs, io_tracer),
|
||||
versions_(versions),
|
||||
cfd_(cfd),
|
||||
@@ -89,7 +90,7 @@ class ExternalSstFileIngestionJob {
|
||||
ingestion_options_(ingestion_options),
|
||||
directories_(directories),
|
||||
event_logger_(event_logger),
|
||||
job_start_time_(env_->NowMicros()),
|
||||
job_start_time_(clock_->NowMicros()),
|
||||
consumed_seqno_count_(0),
|
||||
io_tracer_(io_tracer) {
|
||||
assert(directories != nullptr);
|
||||
@@ -169,7 +170,7 @@ class ExternalSstFileIngestionJob {
|
||||
template <typename TWritableFile>
|
||||
Status SyncIngestedFile(TWritableFile* file);
|
||||
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
FileSystemPtr fs_;
|
||||
VersionSet* versions_;
|
||||
ColumnFamilyData* cfd_;
|
||||
|
||||
+30
-29
@@ -80,24 +80,22 @@ const char* GetFlushReasonString (FlushReason flush_reason) {
|
||||
}
|
||||
}
|
||||
|
||||
FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
uint64_t max_memtable_id, const FileOptions& file_options,
|
||||
VersionSet* versions, InstrumentedMutex* db_mutex,
|
||||
std::atomic<bool>* shutting_down,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_file_directory,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
Env::Priority thread_pri,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string full_history_ts_low)
|
||||
FlushJob::FlushJob(
|
||||
const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options, uint64_t max_memtable_id,
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||
FSDirectory* output_file_directory, CompressionType output_compression,
|
||||
Statistics* stats, EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string full_history_ts_low, BlobFileCompletionCallback* blob_callback)
|
||||
: dbname_(dbname),
|
||||
db_id_(db_id),
|
||||
db_session_id_(db_session_id),
|
||||
@@ -127,7 +125,9 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
pick_memtable_called(false),
|
||||
thread_pri_(thread_pri),
|
||||
io_tracer_(io_tracer),
|
||||
full_history_ts_low_(std::move(full_history_ts_low)) {
|
||||
clock_(db_options_.clock),
|
||||
full_history_ts_low_(std::move(full_history_ts_low)),
|
||||
blob_callback_(blob_callback) {
|
||||
// Update the thread status to indicate flush.
|
||||
ReportStartedFlush();
|
||||
TEST_SYNC_POINT("FlushJob::FlushJob()");
|
||||
@@ -309,8 +309,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = db_options_.env->NowMicros();
|
||||
const uint64_t start_cpu_micros = db_options_.env->NowCPUNanos() / 1000;
|
||||
const uint64_t start_micros = clock_->NowMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUNanos() / 1000;
|
||||
Status s;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
@@ -371,7 +371,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:output_compression",
|
||||
&output_compression_);
|
||||
int64_t _current_time = 0;
|
||||
auto status = db_options_.env->GetCurrentTime(&_current_time);
|
||||
auto status = clock_->GetCurrentTime(&_current_time);
|
||||
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
@@ -417,7 +417,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
TableFileCreationReason::kFlush, &io_s, io_tracer_, event_logger_,
|
||||
job_context_->job_id, Env::IO_HIGH, &table_properties_, 0 /* level */,
|
||||
creation_time, oldest_key_time, write_hint, current_time, db_id_,
|
||||
db_session_id_, full_history_ts_low);
|
||||
db_session_id_, full_history_ts_low, blob_callback_);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
}
|
||||
@@ -466,8 +466,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
stats.micros = db_options_.env->NowMicros() - start_micros;
|
||||
stats.cpu_micros = db_options_.env->NowCPUNanos() / 1000 - start_cpu_micros;
|
||||
stats.micros = clock_->NowMicros() - start_micros;
|
||||
stats.cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
|
||||
if (has_output) {
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
@@ -476,15 +476,16 @@ Status FlushJob::WriteLevel0Table() {
|
||||
|
||||
const auto& blobs = edit_->GetBlobFileAdditions();
|
||||
for (const auto& blob : blobs) {
|
||||
stats.bytes_written += blob.GetTotalBlobBytes();
|
||||
stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
stats.num_output_files += static_cast<int>(blobs.size());
|
||||
stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
|
||||
RecordTimeToHistogram(stats_, FLUSH_TIME, stats.micros);
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, thread_pri_, stats);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written);
|
||||
cfd_->internal_stats()->AddCFStats(
|
||||
InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written + stats.bytes_written_blob);
|
||||
RecordFlushIOStats();
|
||||
return s;
|
||||
}
|
||||
|
||||
+5
-1
@@ -17,6 +17,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_file_completion_callback.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
@@ -73,7 +74,8 @@ class FlushJob {
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "",
|
||||
std::string full_history_ts_low = "");
|
||||
std::string full_history_ts_low = "",
|
||||
BlobFileCompletionCallback* blob_callback = nullptr);
|
||||
|
||||
~FlushJob();
|
||||
|
||||
@@ -162,8 +164,10 @@ class FlushJob {
|
||||
IOStatus io_status_;
|
||||
|
||||
const std::shared_ptr<IOTracer> io_tracer_;
|
||||
SystemClock* clock_;
|
||||
|
||||
const std::string full_history_ts_low_;
|
||||
BlobFileCompletionCallback* blob_callback_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "db/version_set.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "test_util/testharness.h"
|
||||
@@ -47,6 +48,8 @@ class FlushJobTestBase : public testing::Test {
|
||||
if (getenv("KEEP_DB")) {
|
||||
fprintf(stdout, "db is still in %s\n", dbname_.c_str());
|
||||
} else {
|
||||
// destroy versions_ to release all file handles
|
||||
versions_.reset();
|
||||
EXPECT_OK(DestroyDir(env_, dbname_));
|
||||
}
|
||||
}
|
||||
@@ -74,12 +77,13 @@ class FlushJobTestBase : public testing::Test {
|
||||
}
|
||||
|
||||
const std::string manifest = DescriptorFileName(dbname_, 1);
|
||||
std::unique_ptr<WritableFile> file;
|
||||
Status s = env_->NewWritableFile(
|
||||
manifest, &file, env_->OptimizeForManifestWrite(env_options_));
|
||||
const auto& fs = env_->GetFileSystem();
|
||||
std::unique_ptr<WritableFileWriter> file_writer;
|
||||
Status s = WritableFileWriter::Create(
|
||||
fs, manifest, fs->OptimizeForManifestWrite(env_options_), &file_writer,
|
||||
nullptr);
|
||||
ASSERT_OK(s);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
NewLegacyWritableFileWrapper(std::move(file)), manifest, EnvOptions()));
|
||||
|
||||
{
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -993,9 +993,9 @@ bool ForwardIterator::TEST_CheckDeletedIters(int* pdeleted_iters,
|
||||
uint32_t ForwardIterator::FindFileInRange(
|
||||
const std::vector<FileMetaData*>& files, const Slice& internal_key,
|
||||
uint32_t left, uint32_t right) {
|
||||
auto cmp = [&](const FileMetaData* f, const Slice& key) -> bool {
|
||||
auto cmp = [&](const FileMetaData* f, const Slice& k) -> bool {
|
||||
return cfd_->internal_comparator().InternalKeyComparator::Compare(
|
||||
f->largest.Encode(), key) < 0;
|
||||
f->largest.Encode(), k) < 0;
|
||||
};
|
||||
const auto &b = files.begin();
|
||||
return static_cast<uint32_t>(std::lower_bound(b + left,
|
||||
|
||||
@@ -140,7 +140,7 @@ Status ImportColumnFamilyJob::Run() {
|
||||
int64_t temp_current_time = 0;
|
||||
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
|
||||
uint64_t current_time = kUnknownOldestAncesterTime;
|
||||
if (env_->GetCurrentTime(&temp_current_time).ok()) {
|
||||
if (clock_->GetCurrentTime(&temp_current_time).ok()) {
|
||||
current_time = oldest_ancester_time =
|
||||
static_cast<uint64_t>(temp_current_time);
|
||||
}
|
||||
|
||||
@@ -9,24 +9,25 @@
|
||||
#include "db/snapshot_impl.h"
|
||||
#include "options/db_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/metadata.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
struct EnvOptions;
|
||||
class SystemClock;
|
||||
|
||||
// Imports a set of sst files as is into a new column family. Logic is similar
|
||||
// to ExternalSstFileIngestionJob.
|
||||
class ImportColumnFamilyJob {
|
||||
public:
|
||||
ImportColumnFamilyJob(Env* env, VersionSet* versions, ColumnFamilyData* cfd,
|
||||
ImportColumnFamilyJob(VersionSet* versions, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const std::vector<LiveFileMetaData>& metadata,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: env_(env),
|
||||
: clock_(db_options.clock),
|
||||
versions_(versions),
|
||||
cfd_(cfd),
|
||||
db_options_(db_options),
|
||||
@@ -59,7 +60,7 @@ class ImportColumnFamilyJob {
|
||||
IngestedFileInfo* file_to_import,
|
||||
SuperVersion* sv);
|
||||
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
VersionSet* versions_;
|
||||
ColumnFamilyData* cfd_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
|
||||
+47
-21
@@ -50,6 +50,8 @@ const std::map<LevelStatType, LevelStat> InternalStats::compaction_level_stats =
|
||||
{LevelStatType::AVG_SEC, LevelStat{"AvgSec", "Avg(sec)"}},
|
||||
{LevelStatType::KEY_IN, LevelStat{"KeyIn", "KeyIn"}},
|
||||
{LevelStatType::KEY_DROP, LevelStat{"KeyDrop", "KeyDrop"}},
|
||||
{LevelStatType::R_BLOB_GB, LevelStat{"RblobGB", "Rblob(GB)"}},
|
||||
{LevelStatType::W_BLOB_GB, LevelStat{"WblobGB", "Wblob(GB)"}},
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -67,7 +69,8 @@ void PrintLevelStatsHeader(char* buf, size_t len, const std::string& cf_name,
|
||||
};
|
||||
int line_size = snprintf(
|
||||
buf + written_size, len - written_size,
|
||||
"%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n",
|
||||
"%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s "
|
||||
"%s\n",
|
||||
// Note that we skip COMPACTED_FILES and merge it with Files column
|
||||
group_by.c_str(), hdr(LevelStatType::NUM_FILES),
|
||||
hdr(LevelStatType::SIZE_BYTES), hdr(LevelStatType::SCORE),
|
||||
@@ -78,7 +81,8 @@ void PrintLevelStatsHeader(char* buf, size_t len, const std::string& cf_name,
|
||||
hdr(LevelStatType::WRITE_MBPS), hdr(LevelStatType::COMP_SEC),
|
||||
hdr(LevelStatType::COMP_CPU_SEC), hdr(LevelStatType::COMP_COUNT),
|
||||
hdr(LevelStatType::AVG_SEC), hdr(LevelStatType::KEY_IN),
|
||||
hdr(LevelStatType::KEY_DROP));
|
||||
hdr(LevelStatType::KEY_DROP), hdr(LevelStatType::R_BLOB_GB),
|
||||
hdr(LevelStatType::W_BLOB_GB));
|
||||
|
||||
written_size += line_size;
|
||||
written_size = std::min(written_size, static_cast<int>(len));
|
||||
@@ -90,10 +94,12 @@ void PrepareLevelStats(std::map<LevelStatType, double>* level_stats,
|
||||
int num_files, int being_compacted,
|
||||
double total_file_size, double score, double w_amp,
|
||||
const InternalStats::CompactionStats& stats) {
|
||||
uint64_t bytes_read =
|
||||
stats.bytes_read_non_output_levels + stats.bytes_read_output_level;
|
||||
int64_t bytes_new = stats.bytes_written - stats.bytes_read_output_level;
|
||||
double elapsed = (stats.micros + 1) / kMicrosInSec;
|
||||
const uint64_t bytes_read = stats.bytes_read_non_output_levels +
|
||||
stats.bytes_read_output_level +
|
||||
stats.bytes_read_blob;
|
||||
const uint64_t bytes_written = stats.bytes_written + stats.bytes_written_blob;
|
||||
const int64_t bytes_new = stats.bytes_written - stats.bytes_read_output_level;
|
||||
const double elapsed = (stats.micros + 1) / kMicrosInSec;
|
||||
|
||||
(*level_stats)[LevelStatType::NUM_FILES] = num_files;
|
||||
(*level_stats)[LevelStatType::COMPACTED_FILES] = being_compacted;
|
||||
@@ -108,8 +114,7 @@ void PrepareLevelStats(std::map<LevelStatType, double>* level_stats,
|
||||
(*level_stats)[LevelStatType::MOVED_GB] = stats.bytes_moved / kGB;
|
||||
(*level_stats)[LevelStatType::WRITE_AMP] = w_amp;
|
||||
(*level_stats)[LevelStatType::READ_MBPS] = bytes_read / kMB / elapsed;
|
||||
(*level_stats)[LevelStatType::WRITE_MBPS] =
|
||||
stats.bytes_written / kMB / elapsed;
|
||||
(*level_stats)[LevelStatType::WRITE_MBPS] = bytes_written / kMB / elapsed;
|
||||
(*level_stats)[LevelStatType::COMP_SEC] = stats.micros / kMicrosInSec;
|
||||
(*level_stats)[LevelStatType::COMP_CPU_SEC] = stats.cpu_micros / kMicrosInSec;
|
||||
(*level_stats)[LevelStatType::COMP_COUNT] = stats.count;
|
||||
@@ -119,6 +124,8 @@ void PrepareLevelStats(std::map<LevelStatType, double>* level_stats,
|
||||
static_cast<double>(stats.num_input_records);
|
||||
(*level_stats)[LevelStatType::KEY_DROP] =
|
||||
static_cast<double>(stats.num_dropped_records);
|
||||
(*level_stats)[LevelStatType::R_BLOB_GB] = stats.bytes_read_blob / kGB;
|
||||
(*level_stats)[LevelStatType::W_BLOB_GB] = stats.bytes_written_blob / kGB;
|
||||
}
|
||||
|
||||
void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
@@ -143,7 +150,9 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
"%9d " /* Comp(cnt) */
|
||||
"%8.3f " /* Avg(sec) */
|
||||
"%7s " /* KeyIn */
|
||||
"%6s\n", /* KeyDrop */
|
||||
"%6s " /* KeyDrop */
|
||||
"%9.1f " /* Rblob(GB) */
|
||||
"%9.1f\n", /* Wblob(GB) */
|
||||
name.c_str(), static_cast<int>(stat_value.at(LevelStatType::NUM_FILES)),
|
||||
static_cast<int>(stat_value.at(LevelStatType::COMPACTED_FILES)),
|
||||
BytesToHumanString(
|
||||
@@ -168,7 +177,9 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
.c_str(),
|
||||
NumberToHumanString(
|
||||
static_cast<std::int64_t>(stat_value.at(LevelStatType::KEY_DROP)))
|
||||
.c_str());
|
||||
.c_str(),
|
||||
stat_value.at(LevelStatType::R_BLOB_GB),
|
||||
stat_value.at(LevelStatType::W_BLOB_GB));
|
||||
}
|
||||
|
||||
void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
@@ -990,7 +1001,7 @@ bool InternalStats::HandleBlockCachePinnedUsage(uint64_t* value, DBImpl* /*db*/,
|
||||
void InternalStats::DumpDBStats(std::string* value) {
|
||||
char buf[1000];
|
||||
// DB-level stats, only available from default column family
|
||||
double seconds_up = (env_->NowMicros() - started_at_ + 1) / kMicrosInSec;
|
||||
double seconds_up = (clock_->NowMicros() - started_at_ + 1) / kMicrosInSec;
|
||||
double interval_seconds_up = seconds_up - db_stats_snapshot_.seconds_up;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"\n** DB Stats **\nUptime(secs): %.1f total, %.1f interval\n",
|
||||
@@ -1112,9 +1123,10 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
*/
|
||||
void InternalStats::DumpCFMapStats(
|
||||
std::map<std::string, std::string>* cf_stats) {
|
||||
const VersionStorageInfo* vstorage = cfd_->current()->storage_info();
|
||||
CompactionStats compaction_stats_sum;
|
||||
std::map<int, std::map<LevelStatType, double>> levels_stats;
|
||||
DumpCFMapStats(&levels_stats, &compaction_stats_sum);
|
||||
DumpCFMapStats(vstorage, &levels_stats, &compaction_stats_sum);
|
||||
for (auto const& level_ent : levels_stats) {
|
||||
auto level_str =
|
||||
level_ent.first == -1 ? "Sum" : "L" + ToString(level_ent.first);
|
||||
@@ -1131,9 +1143,10 @@ void InternalStats::DumpCFMapStats(
|
||||
}
|
||||
|
||||
void InternalStats::DumpCFMapStats(
|
||||
const VersionStorageInfo* vstorage,
|
||||
std::map<int, std::map<LevelStatType, double>>* levels_stats,
|
||||
CompactionStats* compaction_stats_sum) {
|
||||
const VersionStorageInfo* vstorage = cfd_->current()->storage_info();
|
||||
assert(vstorage);
|
||||
|
||||
int num_levels_to_check =
|
||||
(cfd_->ioptions()->compaction_style != kCompactionStyleFIFO)
|
||||
@@ -1174,12 +1187,14 @@ void InternalStats::DumpCFMapStats(
|
||||
if (level == 0) {
|
||||
input_bytes = curr_ingest;
|
||||
} else {
|
||||
input_bytes = comp_stats_[level].bytes_read_non_output_levels;
|
||||
input_bytes = comp_stats_[level].bytes_read_non_output_levels +
|
||||
comp_stats_[level].bytes_read_blob;
|
||||
}
|
||||
double w_amp =
|
||||
(input_bytes == 0)
|
||||
? 0.0
|
||||
: static_cast<double>(comp_stats_[level].bytes_written) /
|
||||
: static_cast<double>(comp_stats_[level].bytes_written +
|
||||
comp_stats_[level].bytes_written_blob) /
|
||||
input_bytes;
|
||||
std::map<LevelStatType, double> level_stats;
|
||||
PrepareLevelStats(&level_stats, files, files_being_compacted[level],
|
||||
@@ -1189,7 +1204,8 @@ void InternalStats::DumpCFMapStats(
|
||||
}
|
||||
}
|
||||
// Cumulative summary
|
||||
double w_amp = compaction_stats_sum->bytes_written /
|
||||
double w_amp = (compaction_stats_sum->bytes_written +
|
||||
compaction_stats_sum->bytes_written_blob) /
|
||||
static_cast<double>(curr_ingest + 1);
|
||||
// Stats summary across levels
|
||||
std::map<LevelStatType, double> sum_stats;
|
||||
@@ -1256,9 +1272,10 @@ void InternalStats::DumpCFStatsNoFileHistogram(std::string* value) {
|
||||
value->append(buf);
|
||||
|
||||
// Print stats for each level
|
||||
const VersionStorageInfo* vstorage = cfd_->current()->storage_info();
|
||||
std::map<int, std::map<LevelStatType, double>> levels_stats;
|
||||
CompactionStats compaction_stats_sum;
|
||||
DumpCFMapStats(&levels_stats, &compaction_stats_sum);
|
||||
DumpCFMapStats(vstorage, &levels_stats, &compaction_stats_sum);
|
||||
for (int l = 0; l < number_levels_; ++l) {
|
||||
if (levels_stats.find(l) != levels_stats.end()) {
|
||||
PrintLevelStats(buf, sizeof(buf), "L" + ToString(l), levels_stats[l]);
|
||||
@@ -1294,7 +1311,8 @@ void InternalStats::DumpCFStatsNoFileHistogram(std::string* value) {
|
||||
CompactionStats interval_stats(compaction_stats_sum);
|
||||
interval_stats.Subtract(cf_stats_snapshot_.comp_stats);
|
||||
double w_amp =
|
||||
interval_stats.bytes_written / static_cast<double>(interval_ingest);
|
||||
(interval_stats.bytes_written + interval_stats.bytes_written_blob) /
|
||||
static_cast<double>(interval_ingest);
|
||||
PrintLevelStats(buf, sizeof(buf), "Int", 0, 0, 0, 0, w_amp, interval_stats);
|
||||
value->append(buf);
|
||||
|
||||
@@ -1313,7 +1331,13 @@ void InternalStats::DumpCFStatsNoFileHistogram(std::string* value) {
|
||||
}
|
||||
}
|
||||
|
||||
double seconds_up = (env_->NowMicros() - started_at_ + 1) / kMicrosInSec;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"\nBlob file count: %" ROCKSDB_PRIszt ", total size: %.1f GB\n\n",
|
||||
vstorage->GetBlobFiles().size(),
|
||||
vstorage->GetTotalBlobFileSize() / kGB);
|
||||
value->append(buf);
|
||||
|
||||
double seconds_up = (clock_->NowMicros() - started_at_ + 1) / kMicrosInSec;
|
||||
double interval_seconds_up = seconds_up - cf_stats_snapshot_.seconds_up;
|
||||
snprintf(buf, sizeof(buf), "Uptime(secs): %.1f total, %.1f interval\n",
|
||||
seconds_up, interval_seconds_up);
|
||||
@@ -1353,8 +1377,10 @@ void InternalStats::DumpCFStatsNoFileHistogram(std::string* value) {
|
||||
uint64_t compact_micros = 0;
|
||||
for (int level = 0; level < number_levels_; level++) {
|
||||
compact_bytes_read += comp_stats_[level].bytes_read_output_level +
|
||||
comp_stats_[level].bytes_read_non_output_levels;
|
||||
compact_bytes_write += comp_stats_[level].bytes_written;
|
||||
comp_stats_[level].bytes_read_non_output_levels +
|
||||
comp_stats_[level].bytes_read_blob;
|
||||
compact_bytes_write += comp_stats_[level].bytes_written +
|
||||
comp_stats_[level].bytes_written_blob;
|
||||
compact_micros += comp_stats_[level].micros;
|
||||
}
|
||||
|
||||
|
||||
+53
-14
@@ -14,6 +14,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
|
||||
class ColumnFamilyData;
|
||||
|
||||
@@ -78,6 +79,8 @@ enum class LevelStatType {
|
||||
AVG_SEC,
|
||||
KEY_IN,
|
||||
KEY_DROP,
|
||||
R_BLOB_GB,
|
||||
W_BLOB_GB,
|
||||
TOTAL // total number of types
|
||||
};
|
||||
|
||||
@@ -122,7 +125,7 @@ class InternalStats {
|
||||
kIntStatsNumMax,
|
||||
};
|
||||
|
||||
InternalStats(int num_levels, Env* env, ColumnFamilyData* cfd)
|
||||
InternalStats(int num_levels, SystemClock* clock, ColumnFamilyData* cfd)
|
||||
: db_stats_{},
|
||||
cf_stats_value_{},
|
||||
cf_stats_count_{},
|
||||
@@ -131,9 +134,9 @@ class InternalStats {
|
||||
file_read_latency_(num_levels),
|
||||
bg_error_count_(0),
|
||||
number_levels_(num_levels),
|
||||
env_(env),
|
||||
clock_(clock),
|
||||
cfd_(cfd),
|
||||
started_at_(env->NowMicros()) {}
|
||||
started_at_(clock->NowMicros()) {}
|
||||
|
||||
// Per level compaction stats. comp_stats_[level] stores the stats for
|
||||
// compactions that produced data for the specified "level".
|
||||
@@ -141,32 +144,42 @@ class InternalStats {
|
||||
uint64_t micros;
|
||||
uint64_t cpu_micros;
|
||||
|
||||
// The number of bytes read from all non-output levels
|
||||
// The number of bytes read from all non-output levels (table files)
|
||||
uint64_t bytes_read_non_output_levels;
|
||||
|
||||
// The number of bytes read from the compaction output level.
|
||||
// The number of bytes read from the compaction output level (table files)
|
||||
uint64_t bytes_read_output_level;
|
||||
|
||||
// Total number of bytes written during compaction
|
||||
// The number of bytes read from blob files
|
||||
uint64_t bytes_read_blob;
|
||||
|
||||
// Total number of bytes written to table files during compaction
|
||||
uint64_t bytes_written;
|
||||
|
||||
// Total number of bytes moved to the output level
|
||||
// Total number of bytes written to blob files during compaction
|
||||
uint64_t bytes_written_blob;
|
||||
|
||||
// Total number of bytes moved to the output level (table files)
|
||||
uint64_t bytes_moved;
|
||||
|
||||
// The number of compaction input files in all non-output levels.
|
||||
// The number of compaction input files in all non-output levels (table
|
||||
// files)
|
||||
int num_input_files_in_non_output_levels;
|
||||
|
||||
// The number of compaction input files in the output level.
|
||||
// The number of compaction input files in the output level (table files)
|
||||
int num_input_files_in_output_level;
|
||||
|
||||
// The number of compaction output files.
|
||||
// The number of compaction output files (table files)
|
||||
int num_output_files;
|
||||
|
||||
// The number of compaction output files (blob files)
|
||||
int num_output_files_blob;
|
||||
|
||||
// Total incoming entries during compaction between levels N and N+1
|
||||
uint64_t num_input_records;
|
||||
|
||||
// Accumulated diff number of entries
|
||||
// (num input entries - num output entires) for compaction levels N and N+1
|
||||
// (num input entries - num output entries) for compaction levels N and N+1
|
||||
uint64_t num_dropped_records;
|
||||
|
||||
// Number of compactions done
|
||||
@@ -180,11 +193,14 @@ class InternalStats {
|
||||
cpu_micros(0),
|
||||
bytes_read_non_output_levels(0),
|
||||
bytes_read_output_level(0),
|
||||
bytes_read_blob(0),
|
||||
bytes_written(0),
|
||||
bytes_written_blob(0),
|
||||
bytes_moved(0),
|
||||
num_input_files_in_non_output_levels(0),
|
||||
num_input_files_in_output_level(0),
|
||||
num_output_files(0),
|
||||
num_output_files_blob(0),
|
||||
num_input_records(0),
|
||||
num_dropped_records(0),
|
||||
count(0) {
|
||||
@@ -199,11 +215,14 @@ class InternalStats {
|
||||
cpu_micros(0),
|
||||
bytes_read_non_output_levels(0),
|
||||
bytes_read_output_level(0),
|
||||
bytes_read_blob(0),
|
||||
bytes_written(0),
|
||||
bytes_written_blob(0),
|
||||
bytes_moved(0),
|
||||
num_input_files_in_non_output_levels(0),
|
||||
num_input_files_in_output_level(0),
|
||||
num_output_files(0),
|
||||
num_output_files_blob(0),
|
||||
num_input_records(0),
|
||||
num_dropped_records(0),
|
||||
count(c) {
|
||||
@@ -224,12 +243,15 @@ class InternalStats {
|
||||
cpu_micros(c.cpu_micros),
|
||||
bytes_read_non_output_levels(c.bytes_read_non_output_levels),
|
||||
bytes_read_output_level(c.bytes_read_output_level),
|
||||
bytes_read_blob(c.bytes_read_blob),
|
||||
bytes_written(c.bytes_written),
|
||||
bytes_written_blob(c.bytes_written_blob),
|
||||
bytes_moved(c.bytes_moved),
|
||||
num_input_files_in_non_output_levels(
|
||||
c.num_input_files_in_non_output_levels),
|
||||
num_input_files_in_output_level(c.num_input_files_in_output_level),
|
||||
num_output_files(c.num_output_files),
|
||||
num_output_files_blob(c.num_output_files_blob),
|
||||
num_input_records(c.num_input_records),
|
||||
num_dropped_records(c.num_dropped_records),
|
||||
count(c.count) {
|
||||
@@ -244,12 +266,15 @@ class InternalStats {
|
||||
cpu_micros = c.cpu_micros;
|
||||
bytes_read_non_output_levels = c.bytes_read_non_output_levels;
|
||||
bytes_read_output_level = c.bytes_read_output_level;
|
||||
bytes_read_blob = c.bytes_read_blob;
|
||||
bytes_written = c.bytes_written;
|
||||
bytes_written_blob = c.bytes_written_blob;
|
||||
bytes_moved = c.bytes_moved;
|
||||
num_input_files_in_non_output_levels =
|
||||
c.num_input_files_in_non_output_levels;
|
||||
num_input_files_in_output_level = c.num_input_files_in_output_level;
|
||||
num_output_files = c.num_output_files;
|
||||
num_output_files_blob = c.num_output_files_blob;
|
||||
num_input_records = c.num_input_records;
|
||||
num_dropped_records = c.num_dropped_records;
|
||||
count = c.count;
|
||||
@@ -266,11 +291,14 @@ class InternalStats {
|
||||
this->cpu_micros = 0;
|
||||
this->bytes_read_non_output_levels = 0;
|
||||
this->bytes_read_output_level = 0;
|
||||
this->bytes_read_blob = 0;
|
||||
this->bytes_written = 0;
|
||||
this->bytes_written_blob = 0;
|
||||
this->bytes_moved = 0;
|
||||
this->num_input_files_in_non_output_levels = 0;
|
||||
this->num_input_files_in_output_level = 0;
|
||||
this->num_output_files = 0;
|
||||
this->num_output_files_blob = 0;
|
||||
this->num_input_records = 0;
|
||||
this->num_dropped_records = 0;
|
||||
this->count = 0;
|
||||
@@ -285,13 +313,16 @@ class InternalStats {
|
||||
this->cpu_micros += c.cpu_micros;
|
||||
this->bytes_read_non_output_levels += c.bytes_read_non_output_levels;
|
||||
this->bytes_read_output_level += c.bytes_read_output_level;
|
||||
this->bytes_read_blob += c.bytes_read_blob;
|
||||
this->bytes_written += c.bytes_written;
|
||||
this->bytes_written_blob += c.bytes_written_blob;
|
||||
this->bytes_moved += c.bytes_moved;
|
||||
this->num_input_files_in_non_output_levels +=
|
||||
c.num_input_files_in_non_output_levels;
|
||||
this->num_input_files_in_output_level +=
|
||||
c.num_input_files_in_output_level;
|
||||
this->num_output_files += c.num_output_files;
|
||||
this->num_output_files_blob += c.num_output_files_blob;
|
||||
this->num_input_records += c.num_input_records;
|
||||
this->num_dropped_records += c.num_dropped_records;
|
||||
this->count += c.count;
|
||||
@@ -306,13 +337,16 @@ class InternalStats {
|
||||
this->cpu_micros -= c.cpu_micros;
|
||||
this->bytes_read_non_output_levels -= c.bytes_read_non_output_levels;
|
||||
this->bytes_read_output_level -= c.bytes_read_output_level;
|
||||
this->bytes_read_blob -= c.bytes_read_blob;
|
||||
this->bytes_written -= c.bytes_written;
|
||||
this->bytes_written_blob -= c.bytes_written_blob;
|
||||
this->bytes_moved -= c.bytes_moved;
|
||||
this->num_input_files_in_non_output_levels -=
|
||||
c.num_input_files_in_non_output_levels;
|
||||
this->num_input_files_in_output_level -=
|
||||
c.num_input_files_in_output_level;
|
||||
this->num_output_files -= c.num_output_files;
|
||||
this->num_output_files_blob -= c.num_output_files_blob;
|
||||
this->num_input_records -= c.num_input_records;
|
||||
this->num_dropped_records -= c.num_dropped_records;
|
||||
this->count -= c.count;
|
||||
@@ -341,7 +375,7 @@ class InternalStats {
|
||||
cf_stats_snapshot_.Clear();
|
||||
db_stats_snapshot_.Clear();
|
||||
bg_error_count_ = 0;
|
||||
started_at_ = env_->NowMicros();
|
||||
started_at_ = clock_->NowMicros();
|
||||
}
|
||||
|
||||
void AddCompactionStats(int level, Env::Priority thread_pri,
|
||||
@@ -411,6 +445,7 @@ class InternalStats {
|
||||
void DumpDBStats(std::string* value);
|
||||
void DumpCFMapStats(std::map<std::string, std::string>* cf_stats);
|
||||
void DumpCFMapStats(
|
||||
const VersionStorageInfo* vstorage,
|
||||
std::map<int, std::map<LevelStatType, double>>* level_stats,
|
||||
CompactionStats* compaction_stats_sum);
|
||||
void DumpCFMapStatsByPriority(
|
||||
@@ -602,7 +637,7 @@ class InternalStats {
|
||||
uint64_t bg_error_count_;
|
||||
|
||||
const int number_levels_;
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
ColumnFamilyData* cfd_;
|
||||
uint64_t started_at_;
|
||||
};
|
||||
@@ -641,18 +676,22 @@ class InternalStats {
|
||||
kIntStatsNumMax,
|
||||
};
|
||||
|
||||
InternalStats(int /*num_levels*/, Env* /*env*/, ColumnFamilyData* /*cfd*/) {}
|
||||
InternalStats(int /*num_levels*/, SystemClock* /*clock*/,
|
||||
ColumnFamilyData* /*cfd*/) {}
|
||||
|
||||
struct CompactionStats {
|
||||
uint64_t micros;
|
||||
uint64_t cpu_micros;
|
||||
uint64_t bytes_read_non_output_levels;
|
||||
uint64_t bytes_read_output_level;
|
||||
uint64_t bytes_read_blob;
|
||||
uint64_t bytes_written;
|
||||
uint64_t bytes_written_blob;
|
||||
uint64_t bytes_moved;
|
||||
int num_input_files_in_non_output_levels;
|
||||
int num_input_files_in_output_level;
|
||||
int num_output_files;
|
||||
int num_output_files_blob;
|
||||
uint64_t num_input_records;
|
||||
uint64_t num_dropped_records;
|
||||
int count;
|
||||
|
||||
+1
-2
@@ -8,8 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "db/log_format.h"
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
+10
-9
@@ -104,7 +104,7 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
: 0),
|
||||
prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
|
||||
flush_state_(FLUSH_NOT_REQUESTED),
|
||||
env_(ioptions.env),
|
||||
clock_(ioptions.clock),
|
||||
insert_with_hint_prefix_extractor_(
|
||||
ioptions.memtable_insert_with_hint_prefix_extractor),
|
||||
oldest_key_time_(std::numeric_limits<uint64_t>::max()),
|
||||
@@ -223,7 +223,7 @@ void MemTable::UpdateOldestKeyTime() {
|
||||
uint64_t oldest_key_time = oldest_key_time_.load(std::memory_order_relaxed);
|
||||
if (oldest_key_time == std::numeric_limits<uint64_t>::max()) {
|
||||
int64_t current_time = 0;
|
||||
auto s = env_->GetCurrentTime(¤t_time);
|
||||
auto s = clock_->GetCurrentTime(¤t_time);
|
||||
if (s.ok()) {
|
||||
assert(current_time >= 0);
|
||||
// If fail, the timestamp is already set.
|
||||
@@ -684,7 +684,8 @@ struct Saver {
|
||||
Statistics* statistics;
|
||||
bool inplace_update_support;
|
||||
bool do_merge;
|
||||
Env* env_;
|
||||
SystemClock* clock;
|
||||
|
||||
ReadCallback* callback_;
|
||||
bool* is_blob_index;
|
||||
bool allow_data_in_errors;
|
||||
@@ -722,8 +723,8 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
const Comparator* user_comparator =
|
||||
s->mem->GetInternalKeyComparator().user_comparator();
|
||||
size_t ts_sz = user_comparator->timestamp_size();
|
||||
if (user_comparator->CompareWithoutTimestamp(user_key_slice,
|
||||
s->key->user_key()) == 0) {
|
||||
if (user_comparator->EqualWithoutTimestamp(user_key_slice,
|
||||
s->key->user_key())) {
|
||||
// Correct user key
|
||||
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
|
||||
ValueType type;
|
||||
@@ -768,7 +769,7 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), &v,
|
||||
merge_context->GetOperands(), s->value, s->logger,
|
||||
s->statistics, s->env_, nullptr /* result_operand */, true);
|
||||
s->statistics, s->clock, nullptr /* result_operand */, true);
|
||||
}
|
||||
} else {
|
||||
// Preserve the value with the goal of returning it as part of
|
||||
@@ -807,7 +808,7 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), nullptr,
|
||||
merge_context->GetOperands(), s->value, s->logger,
|
||||
s->statistics, s->env_, nullptr /* result_operand */, true);
|
||||
s->statistics, s->clock, nullptr /* result_operand */, true);
|
||||
}
|
||||
} else {
|
||||
*(s->status) = Status::NotFound();
|
||||
@@ -835,7 +836,7 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), nullptr,
|
||||
merge_context->GetOperands(), s->value, s->logger, s->statistics,
|
||||
s->env_, nullptr /* result_operand */, true);
|
||||
s->clock, nullptr /* result_operand */, true);
|
||||
*(s->found_final_value) = true;
|
||||
return false;
|
||||
}
|
||||
@@ -943,7 +944,7 @@ void MemTable::GetFromTable(const LookupKey& key,
|
||||
saver.logger = moptions_.info_log;
|
||||
saver.inplace_update_support = moptions_.inplace_update_support;
|
||||
saver.statistics = moptions_.statistics;
|
||||
saver.env_ = env_;
|
||||
saver.clock = clock_;
|
||||
saver.callback_ = callback;
|
||||
saver.is_blob_index = is_blob_index;
|
||||
saver.do_merge = do_merge;
|
||||
|
||||
+2
-2
@@ -26,7 +26,6 @@
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "table/multiget_context.h"
|
||||
#include "util/dynamic_bloom.h"
|
||||
@@ -38,6 +37,7 @@ struct FlushJobInfo;
|
||||
class Mutex;
|
||||
class MemTableIterator;
|
||||
class MergeContext;
|
||||
class SystemClock;
|
||||
|
||||
struct ImmutableMemTableOptions {
|
||||
explicit ImmutableMemTableOptions(const ImmutableCFOptions& ioptions,
|
||||
@@ -512,7 +512,7 @@ class MemTable {
|
||||
|
||||
std::atomic<FlushStateEnum> flush_state_;
|
||||
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
|
||||
// Extract sequential insert prefixes.
|
||||
const SliceTransform* insert_with_hint_prefix_extractor_;
|
||||
|
||||
+8
-6
@@ -14,6 +14,7 @@
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "table/format.h"
|
||||
#include "table/internal_iterator.h"
|
||||
|
||||
@@ -28,6 +29,7 @@ MergeHelper::MergeHelper(Env* env, const Comparator* user_comparator,
|
||||
Statistics* stats,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
: env_(env),
|
||||
clock_(env->GetSystemClock().get()),
|
||||
user_comparator_(user_comparator),
|
||||
user_merge_operator_(user_merge_operator),
|
||||
compaction_filter_(compaction_filter),
|
||||
@@ -39,7 +41,7 @@ MergeHelper::MergeHelper(Env* env, const Comparator* user_comparator,
|
||||
snapshot_checker_(snapshot_checker),
|
||||
level_(level),
|
||||
keys_(),
|
||||
filter_timer_(env_),
|
||||
filter_timer_(clock_),
|
||||
total_filter_time_(0U),
|
||||
stats_(stats) {
|
||||
assert(user_comparator_ != nullptr);
|
||||
@@ -52,7 +54,7 @@ Status MergeHelper::TimedFullMerge(const MergeOperator* merge_operator,
|
||||
const Slice& key, const Slice* value,
|
||||
const std::vector<Slice>& operands,
|
||||
std::string* result, Logger* logger,
|
||||
Statistics* statistics, Env* env,
|
||||
Statistics* statistics, SystemClock* clock,
|
||||
Slice* result_operand,
|
||||
bool update_num_ops_stats) {
|
||||
assert(merge_operator != nullptr);
|
||||
@@ -75,7 +77,7 @@ Status MergeHelper::TimedFullMerge(const MergeOperator* merge_operator,
|
||||
MergeOperator::MergeOperationOutput merge_out(*result, tmp_result_operand);
|
||||
{
|
||||
// Setup to time the merge
|
||||
StopWatchNano timer(env, statistics != nullptr);
|
||||
StopWatchNano timer(clock, statistics != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
|
||||
// Do the merge
|
||||
@@ -213,7 +215,7 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
std::string merge_result;
|
||||
s = TimedFullMerge(user_merge_operator_, ikey.user_key, val_ptr,
|
||||
merge_context_.GetOperands(), &merge_result, logger_,
|
||||
stats_, env_);
|
||||
stats_, clock_);
|
||||
|
||||
// We store the result in keys_.back() and operands_.back()
|
||||
// if nothing went wrong (i.e.: no operand corruption on disk)
|
||||
@@ -324,7 +326,7 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
std::string merge_result;
|
||||
s = TimedFullMerge(user_merge_operator_, orig_ikey.user_key, nullptr,
|
||||
merge_context_.GetOperands(), &merge_result, logger_,
|
||||
stats_, env_);
|
||||
stats_, clock_);
|
||||
if (s.ok()) {
|
||||
// The original key encountered
|
||||
// We are certain that keys_ is not empty here (see assertions couple of
|
||||
@@ -347,7 +349,7 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
bool merge_success = false;
|
||||
std::string merge_result;
|
||||
{
|
||||
StopWatchNano timer(env_, stats_ != nullptr);
|
||||
StopWatchNano timer(clock_, stats_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
merge_success = user_merge_operator_->PartialMergeMulti(
|
||||
orig_ikey.user_key,
|
||||
|
||||
+3
-1
@@ -25,6 +25,7 @@ class Iterator;
|
||||
class Logger;
|
||||
class MergeOperator;
|
||||
class Statistics;
|
||||
class SystemClock;
|
||||
|
||||
class MergeHelper {
|
||||
public:
|
||||
@@ -48,7 +49,7 @@ class MergeHelper {
|
||||
const Slice& key, const Slice* value,
|
||||
const std::vector<Slice>& operands,
|
||||
std::string* result, Logger* logger,
|
||||
Statistics* statistics, Env* env,
|
||||
Statistics* statistics, SystemClock* clock,
|
||||
Slice* result_operand = nullptr,
|
||||
bool update_num_ops_stats = false);
|
||||
|
||||
@@ -140,6 +141,7 @@ class MergeHelper {
|
||||
|
||||
private:
|
||||
Env* env_;
|
||||
SystemClock* clock_;
|
||||
const Comparator* user_comparator_;
|
||||
const MergeOperator* user_merge_operator_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
|
||||
@@ -94,6 +94,12 @@ class ObsoleteFilesTest : public DBTestBase {
|
||||
options.WAL_ttl_seconds = 300; // Used to test log files
|
||||
options.WAL_size_limit_MB = 1024; // Used to test log files
|
||||
options.wal_dir = wal_dir_;
|
||||
|
||||
// Note: the following prevents an otherwise harmless data race between the
|
||||
// test setup code (AddBlobFile) in ObsoleteFilesTest.BlobFiles and the
|
||||
// periodic stat dumping thread.
|
||||
options.stats_dump_period_sec = 0;
|
||||
|
||||
Destroy(options);
|
||||
Reopen(options);
|
||||
}
|
||||
@@ -192,6 +198,8 @@ TEST_F(ObsoleteFilesTest, DeleteObsoleteOptionsFile) {
|
||||
}
|
||||
|
||||
TEST_F(ObsoleteFilesTest, BlobFiles) {
|
||||
ReopenDB();
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
+32
-28
@@ -3,6 +3,8 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
#include "rocksdb/perf_context.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
@@ -15,8 +17,8 @@
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -91,7 +93,7 @@ TEST_F(PerfContextTest, SeekIntoDeletion) {
|
||||
std::string value;
|
||||
|
||||
get_perf_context()->Reset();
|
||||
StopWatchNano timer(Env::Default());
|
||||
StopWatchNano timer(SystemClock::Default().get());
|
||||
timer.Start();
|
||||
auto status = db->Get(read_options, key, &value);
|
||||
auto elapsed_nanos = timer.ElapsedNanos();
|
||||
@@ -110,7 +112,7 @@ TEST_F(PerfContextTest, SeekIntoDeletion) {
|
||||
std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
StopWatchNano timer(Env::Default(), true);
|
||||
StopWatchNano timer(SystemClock::Default().get(), true);
|
||||
iter->SeekToFirst();
|
||||
hist_seek_to_first.Add(get_perf_context()->user_key_comparison_count);
|
||||
auto elapsed_nanos = timer.ElapsedNanos();
|
||||
@@ -131,7 +133,7 @@ TEST_F(PerfContextTest, SeekIntoDeletion) {
|
||||
std::string key = "k" + ToString(i);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
StopWatchNano timer(Env::Default(), true);
|
||||
StopWatchNano timer(SystemClock::Default().get(), true);
|
||||
iter->Seek(key);
|
||||
auto elapsed_nanos = timer.ElapsedNanos();
|
||||
hist_seek.Add(get_perf_context()->user_key_comparison_count);
|
||||
@@ -145,7 +147,7 @@ TEST_F(PerfContextTest, SeekIntoDeletion) {
|
||||
|
||||
get_perf_context()->Reset();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
StopWatchNano timer2(Env::Default(), true);
|
||||
StopWatchNano timer2(SystemClock::Default().get(), true);
|
||||
iter->Next();
|
||||
auto elapsed_nanos2 = timer2.ElapsedNanos();
|
||||
if (FLAGS_verbose) {
|
||||
@@ -164,7 +166,7 @@ TEST_F(PerfContextTest, StopWatchNanoOverhead) {
|
||||
const int kTotalIterations = 1000000;
|
||||
std::vector<uint64_t> timings(kTotalIterations);
|
||||
|
||||
StopWatchNano timer(Env::Default(), true);
|
||||
StopWatchNano timer(SystemClock::Default().get(), true);
|
||||
for (auto& timing : timings) {
|
||||
timing = timer.ElapsedNanos(true /* reset */);
|
||||
}
|
||||
@@ -185,7 +187,7 @@ TEST_F(PerfContextTest, StopWatchOverhead) {
|
||||
uint64_t elapsed = 0;
|
||||
std::vector<uint64_t> timings(kTotalIterations);
|
||||
|
||||
StopWatch timer(Env::Default(), nullptr, 0, &elapsed);
|
||||
StopWatch timer(SystemClock::Default().get(), nullptr, 0, &elapsed);
|
||||
for (auto& timing : timings) {
|
||||
timing = elapsed;
|
||||
}
|
||||
@@ -539,7 +541,7 @@ TEST_F(PerfContextTest, SeekKeyComparison) {
|
||||
HistogramImpl hist_time_diff;
|
||||
|
||||
SetPerfLevel(kEnableTime);
|
||||
StopWatchNano timer(Env::Default());
|
||||
StopWatchNano timer(SystemClock::Default().get());
|
||||
for (const int i : keys) {
|
||||
std::string key = "k" + ToString(i);
|
||||
std::string value = "v" + ToString(i);
|
||||
@@ -592,25 +594,26 @@ TEST_F(PerfContextTest, DBMutexLockCounter) {
|
||||
for (PerfLevel perf_level_test :
|
||||
{PerfLevel::kEnableTimeExceptForMutex, PerfLevel::kEnableTime}) {
|
||||
for (int c = 0; c < 2; ++c) {
|
||||
InstrumentedMutex mutex(nullptr, Env::Default(), stats_code[c]);
|
||||
mutex.Lock();
|
||||
ROCKSDB_NAMESPACE::port::Thread child_thread([&] {
|
||||
SetPerfLevel(perf_level_test);
|
||||
get_perf_context()->Reset();
|
||||
ASSERT_EQ(get_perf_context()->db_mutex_lock_nanos, 0);
|
||||
InstrumentedMutex mutex(nullptr, SystemClock::Default().get(),
|
||||
stats_code[c]);
|
||||
mutex.Lock();
|
||||
mutex.Unlock();
|
||||
if (perf_level_test == PerfLevel::kEnableTimeExceptForMutex ||
|
||||
stats_code[c] != DB_MUTEX_WAIT_MICROS) {
|
||||
ROCKSDB_NAMESPACE::port::Thread child_thread([&] {
|
||||
SetPerfLevel(perf_level_test);
|
||||
get_perf_context()->Reset();
|
||||
ASSERT_EQ(get_perf_context()->db_mutex_lock_nanos, 0);
|
||||
} else {
|
||||
// increment the counter only when it's a DB Mutex
|
||||
ASSERT_GT(get_perf_context()->db_mutex_lock_nanos, 0);
|
||||
}
|
||||
});
|
||||
Env::Default()->SleepForMicroseconds(100);
|
||||
mutex.Unlock();
|
||||
child_thread.join();
|
||||
mutex.Lock();
|
||||
mutex.Unlock();
|
||||
if (perf_level_test == PerfLevel::kEnableTimeExceptForMutex ||
|
||||
stats_code[c] != DB_MUTEX_WAIT_MICROS) {
|
||||
ASSERT_EQ(get_perf_context()->db_mutex_lock_nanos, 0);
|
||||
} else {
|
||||
// increment the counter only when it's a DB Mutex
|
||||
ASSERT_GT(get_perf_context()->db_mutex_lock_nanos, 0);
|
||||
}
|
||||
});
|
||||
SystemClock::Default()->SleepForMicroseconds(100);
|
||||
mutex.Unlock();
|
||||
child_thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,7 +622,8 @@ TEST_F(PerfContextTest, FalseDBMutexWait) {
|
||||
SetPerfLevel(kEnableTime);
|
||||
int stats_code[] = {0, static_cast<int>(DB_MUTEX_WAIT_MICROS)};
|
||||
for (int c = 0; c < 2; ++c) {
|
||||
InstrumentedMutex mutex(nullptr, Env::Default(), stats_code[c]);
|
||||
InstrumentedMutex mutex(nullptr, SystemClock::Default().get(),
|
||||
stats_code[c]);
|
||||
InstrumentedCondVar lock(&mutex);
|
||||
get_perf_context()->Reset();
|
||||
mutex.Lock();
|
||||
@@ -824,8 +828,8 @@ TEST_F(PerfContextTest, PerfContextByLevelGetSet) {
|
||||
}
|
||||
|
||||
TEST_F(PerfContextTest, CPUTimer) {
|
||||
if (Env::Default()->NowCPUNanos() == 0) {
|
||||
ROCKSDB_GTEST_SKIP("Target without NowCPUNanos support");
|
||||
if (SystemClock::Default()->CPUNanos() == 0) {
|
||||
ROCKSDB_GTEST_SKIP("Target without CPUNanos support");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
#include "db/periodic_work_scheduler.h"
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
PeriodicWorkScheduler::PeriodicWorkScheduler(Env* env) : timer_mu_(env) {
|
||||
timer = std::unique_ptr<Timer>(new Timer(env));
|
||||
PeriodicWorkScheduler::PeriodicWorkScheduler(
|
||||
const std::shared_ptr<SystemClock>& clock) {
|
||||
timer = std::unique_ptr<Timer>(new Timer(clock.get()));
|
||||
}
|
||||
|
||||
void PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
@@ -52,10 +54,10 @@ void PeriodicWorkScheduler::Unregister(DBImpl* dbi) {
|
||||
}
|
||||
|
||||
PeriodicWorkScheduler* PeriodicWorkScheduler::Default() {
|
||||
// Always use the default Env for the scheduler, as we only use the NowMicros
|
||||
// which is the same for all env.
|
||||
// The Env could only be overridden in test.
|
||||
static PeriodicWorkScheduler scheduler(Env::Default());
|
||||
// Always use the default SystemClock for the scheduler, as we only use the
|
||||
// NowMicros which is the same for all clocks. The Env could only be
|
||||
// overridden in test.
|
||||
static PeriodicWorkScheduler scheduler(SystemClock::Default());
|
||||
return &scheduler;
|
||||
}
|
||||
|
||||
@@ -69,12 +71,13 @@ std::string PeriodicWorkScheduler::GetTaskName(DBImpl* dbi,
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
// Get the static scheduler. For a new env, it needs to re-create the internal
|
||||
// timer, so only re-create it when there's no running task. Otherwise, return
|
||||
// the existing scheduler. Which means if the unittest needs to update MockEnv,
|
||||
// Close all db instances and then re-open them.
|
||||
PeriodicWorkTestScheduler* PeriodicWorkTestScheduler::Default(Env* env) {
|
||||
static PeriodicWorkTestScheduler scheduler(env);
|
||||
// Get the static scheduler. For a new SystemClock, it needs to re-create the
|
||||
// internal timer, so only re-create it when there's no running task. Otherwise,
|
||||
// return the existing scheduler. Which means if the unittest needs to update
|
||||
// MockClock, Close all db instances and then re-open them.
|
||||
PeriodicWorkTestScheduler* PeriodicWorkTestScheduler::Default(
|
||||
const std::shared_ptr<SystemClock>& clock) {
|
||||
static PeriodicWorkTestScheduler scheduler(clock);
|
||||
static port::Mutex mutex;
|
||||
{
|
||||
MutexLock l(&mutex);
|
||||
@@ -84,7 +87,7 @@ PeriodicWorkTestScheduler* PeriodicWorkTestScheduler::Default(Env* env) {
|
||||
MutexLock timer_mu_guard(&scheduler.timer_mu_);
|
||||
scheduler.timer->Shutdown();
|
||||
}
|
||||
scheduler.timer.reset(new Timer(env));
|
||||
scheduler.timer.reset(new Timer(clock.get()));
|
||||
}
|
||||
}
|
||||
return &scheduler;
|
||||
@@ -104,8 +107,9 @@ size_t PeriodicWorkTestScheduler::TEST_GetValidTaskNum() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PeriodicWorkTestScheduler::PeriodicWorkTestScheduler(Env* env)
|
||||
: PeriodicWorkScheduler(env) {}
|
||||
PeriodicWorkTestScheduler::PeriodicWorkTestScheduler(
|
||||
const std::shared_ptr<SystemClock>& clock)
|
||||
: PeriodicWorkScheduler(clock) {}
|
||||
|
||||
#endif // !NDEBUG
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "util/timer.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class SystemClock;
|
||||
|
||||
// PeriodicWorkScheduler is a singleton object, which is scheduling/running
|
||||
// DumpStats(), PersistStats(), and FlushInfoLog() for all DB instances. All DB
|
||||
@@ -49,25 +50,26 @@ class PeriodicWorkScheduler {
|
||||
// the `Timer::Cancel()`s and `Timer::Shutdown()` run atomically.
|
||||
port::Mutex timer_mu_;
|
||||
|
||||
explicit PeriodicWorkScheduler(Env* env);
|
||||
explicit PeriodicWorkScheduler(const std::shared_ptr<SystemClock>& clock);
|
||||
|
||||
private:
|
||||
std::string GetTaskName(DBImpl* dbi, const std::string& func_name);
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
// PeriodicWorkTestScheduler is for unittest, which can specify the Env like
|
||||
// SafeMockTimeEnv. It also contains functions for unittest.
|
||||
// PeriodicWorkTestScheduler is for unittest, which can specify the SystemClock
|
||||
// It also contains functions for unittest.
|
||||
class PeriodicWorkTestScheduler : public PeriodicWorkScheduler {
|
||||
public:
|
||||
static PeriodicWorkTestScheduler* Default(Env* env);
|
||||
static PeriodicWorkTestScheduler* Default(
|
||||
const std::shared_ptr<SystemClock>& clock);
|
||||
|
||||
void TEST_WaitForRun(std::function<void()> callback) const;
|
||||
|
||||
size_t TEST_GetValidTaskNum() const;
|
||||
|
||||
private:
|
||||
explicit PeriodicWorkTestScheduler(Env* env);
|
||||
explicit PeriodicWorkTestScheduler(const std::shared_ptr<SystemClock>& clock);
|
||||
};
|
||||
#endif // !NDEBUG
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "db/periodic_work_scheduler.h"
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "test_util/mock_time_env.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -14,20 +16,22 @@ class PeriodicWorkSchedulerTest : public DBTestBase {
|
||||
public:
|
||||
PeriodicWorkSchedulerTest()
|
||||
: DBTestBase("/periodic_work_scheduler_test", /*env_do_fsync=*/true) {
|
||||
mock_env_.reset(new MockTimeEnv(env_));
|
||||
mock_clock_ = std::make_shared<MockSystemClock>(env_->GetSystemClock());
|
||||
mock_env_.reset(new CompositeEnvWrapper(env_, mock_clock_));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<MockTimeEnv> mock_env_;
|
||||
std::unique_ptr<Env> mock_env_;
|
||||
std::shared_ptr<MockSystemClock> mock_clock_;
|
||||
|
||||
void SetUp() override {
|
||||
mock_env_->InstallTimedWaitFixCallback();
|
||||
mock_clock_->InstallTimedWaitFixCallback();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::StartPeriodicWorkScheduler:Init", [&](void* arg) {
|
||||
auto* periodic_work_scheduler_ptr =
|
||||
reinterpret_cast<PeriodicWorkScheduler**>(arg);
|
||||
*periodic_work_scheduler_ptr =
|
||||
PeriodicWorkTestScheduler::Default(mock_env_.get());
|
||||
PeriodicWorkTestScheduler::Default(mock_clock_);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -63,7 +67,7 @@ TEST_F(PeriodicWorkSchedulerTest, Basic) {
|
||||
|
||||
ASSERT_GT(kPeriodSec, 1u);
|
||||
dbfull()->TEST_WaitForStatsDumpRun([&] {
|
||||
mock_env_->MockSleepForSeconds(static_cast<int>(kPeriodSec) - 1);
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(kPeriodSec) - 1);
|
||||
});
|
||||
|
||||
auto scheduler = dbfull()->TEST_GetPeriodicWorkScheduler();
|
||||
@@ -75,14 +79,14 @@ TEST_F(PeriodicWorkSchedulerTest, Basic) {
|
||||
ASSERT_EQ(1, flush_info_log_counter);
|
||||
|
||||
dbfull()->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
|
||||
ASSERT_EQ(2, dump_st_counter);
|
||||
ASSERT_EQ(2, pst_st_counter);
|
||||
ASSERT_EQ(2, flush_info_log_counter);
|
||||
|
||||
dbfull()->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
|
||||
ASSERT_EQ(3, dump_st_counter);
|
||||
ASSERT_EQ(3, pst_st_counter);
|
||||
@@ -96,7 +100,7 @@ TEST_F(PeriodicWorkSchedulerTest, Basic) {
|
||||
|
||||
// Info log flush should still run.
|
||||
dbfull()->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
ASSERT_EQ(3, dump_st_counter);
|
||||
ASSERT_EQ(3, pst_st_counter);
|
||||
ASSERT_EQ(4, flush_info_log_counter);
|
||||
@@ -114,7 +118,7 @@ TEST_F(PeriodicWorkSchedulerTest, Basic) {
|
||||
ASSERT_EQ(2, scheduler->TEST_GetValidTaskNum());
|
||||
|
||||
dbfull()->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(static_cast<int>(kPeriodSec)); });
|
||||
ASSERT_EQ(4, dump_st_counter);
|
||||
ASSERT_EQ(3, pst_st_counter);
|
||||
ASSERT_EQ(5, flush_info_log_counter);
|
||||
@@ -154,19 +158,19 @@ TEST_F(PeriodicWorkSchedulerTest, MultiInstances) {
|
||||
|
||||
int expected_run = kInstanceNum;
|
||||
dbi->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(kPeriodSec - 1); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(kPeriodSec - 1); });
|
||||
ASSERT_EQ(expected_run, dump_st_counter);
|
||||
ASSERT_EQ(expected_run, pst_st_counter);
|
||||
|
||||
expected_run += kInstanceNum;
|
||||
dbi->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
|
||||
ASSERT_EQ(expected_run, dump_st_counter);
|
||||
ASSERT_EQ(expected_run, pst_st_counter);
|
||||
|
||||
expected_run += kInstanceNum;
|
||||
dbi->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
|
||||
ASSERT_EQ(expected_run, dump_st_counter);
|
||||
ASSERT_EQ(expected_run, pst_st_counter);
|
||||
|
||||
@@ -178,9 +182,9 @@ TEST_F(PeriodicWorkSchedulerTest, MultiInstances) {
|
||||
expected_run += (kInstanceNum - half) * 2;
|
||||
|
||||
dbi->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
|
||||
dbi->TEST_WaitForStatsDumpRun(
|
||||
[&] { mock_env_->MockSleepForSeconds(kPeriodSec); });
|
||||
[&] { mock_clock_->MockSleepForSeconds(kPeriodSec); });
|
||||
ASSERT_EQ(expected_run, dump_st_counter);
|
||||
ASSERT_EQ(expected_run, pst_st_counter);
|
||||
|
||||
@@ -202,7 +206,8 @@ TEST_F(PeriodicWorkSchedulerTest, MultiEnv) {
|
||||
|
||||
Reopen(options1);
|
||||
|
||||
std::unique_ptr<MockTimeEnv> mock_env2(new MockTimeEnv(Env::Default()));
|
||||
std::unique_ptr<Env> mock_env2(
|
||||
new CompositeEnvWrapper(Env::Default(), mock_clock_));
|
||||
Options options2;
|
||||
options2.stats_dump_period_sec = kDumpPeriodSec;
|
||||
options2.stats_persist_period_sec = kPersistPeriodSec;
|
||||
|
||||
+4
-4
@@ -25,6 +25,7 @@ int main() {
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/cast_util.h"
|
||||
@@ -597,7 +598,6 @@ TEST_F(PrefixTest, DynamicPrefixIterator) {
|
||||
|
||||
HistogramImpl hist_put_time;
|
||||
HistogramImpl hist_put_comparison;
|
||||
|
||||
// insert x random prefix, each with y continuous element.
|
||||
for (auto prefix : prefixes) {
|
||||
for (uint64_t sorted = 0; sorted < FLAGS_items_per_prefix; sorted++) {
|
||||
@@ -608,7 +608,7 @@ TEST_F(PrefixTest, DynamicPrefixIterator) {
|
||||
std::string value(FLAGS_value_size, 0);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
StopWatchNano timer(Env::Default(), true);
|
||||
StopWatchNano timer(SystemClock::Default().get(), true);
|
||||
ASSERT_OK(db->Put(write_options, key, value));
|
||||
hist_put_time.Add(timer.ElapsedNanos());
|
||||
hist_put_comparison.Add(get_perf_context()->user_key_comparison_count);
|
||||
@@ -631,7 +631,7 @@ TEST_F(PrefixTest, DynamicPrefixIterator) {
|
||||
std::string value = "v" + ToString(0);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
StopWatchNano timer(Env::Default(), true);
|
||||
StopWatchNano timer(SystemClock::Default().get(), true);
|
||||
auto key_prefix = options.prefix_extractor->Transform(key);
|
||||
uint64_t total_keys = 0;
|
||||
for (iter->Seek(key);
|
||||
@@ -665,7 +665,7 @@ TEST_F(PrefixTest, DynamicPrefixIterator) {
|
||||
Slice key = TestKeyToSlice(s, test_key);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
StopWatchNano timer(Env::Default(), true);
|
||||
StopWatchNano timer(SystemClock::Default().get(), true);
|
||||
iter->Seek(key);
|
||||
hist_no_seek_time.Add(timer.ElapsedNanos());
|
||||
hist_no_seek_comparison.Add(get_perf_context()->user_key_comparison_count);
|
||||
|
||||
@@ -11,8 +11,8 @@ int main() {
|
||||
}
|
||||
#else
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <set>
|
||||
@@ -22,14 +22,13 @@ int main() {
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/random.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
#include "util/gflags_compat.h"
|
||||
|
||||
using GFLAGS_NAMESPACE::ParseCommandLineFlags;
|
||||
|
||||
DEFINE_int32(num_range_tombstones, 1000, "number of range tombstones created");
|
||||
@@ -172,6 +171,8 @@ int main(int argc, char** argv) {
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
Stats stats;
|
||||
ROCKSDB_NAMESPACE::SystemClock* clock =
|
||||
ROCKSDB_NAMESPACE::SystemClock::Default().get();
|
||||
ROCKSDB_NAMESPACE::Random64 rnd(FLAGS_seed);
|
||||
std::default_random_engine random_gen(FLAGS_seed);
|
||||
std::normal_distribution<double> normal_dist(FLAGS_tombstone_width_mean,
|
||||
@@ -220,7 +221,7 @@ int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::kMaxSequenceNumber));
|
||||
|
||||
ROCKSDB_NAMESPACE::StopWatchNano stop_watch_add_tombstones(
|
||||
ROCKSDB_NAMESPACE::Env::Default(), true /* auto_start */);
|
||||
clock, true /* auto_start */);
|
||||
range_del_agg.AddTombstones(std::move(fragmented_range_del_iter));
|
||||
stats.time_add_tombstones += stop_watch_add_tombstones.ElapsedNanos();
|
||||
}
|
||||
@@ -237,7 +238,7 @@ int main(int argc, char** argv) {
|
||||
parsed_key.user_key = key_string;
|
||||
|
||||
ROCKSDB_NAMESPACE::StopWatchNano stop_watch_should_delete(
|
||||
ROCKSDB_NAMESPACE::Env::Default(), true /* auto_start */);
|
||||
clock, true /* auto_start */);
|
||||
range_del_agg.ShouldDelete(parsed_key, mode);
|
||||
uint64_t call_time = stop_watch_should_delete.ElapsedNanos();
|
||||
|
||||
|
||||
@@ -6,12 +6,11 @@
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "util/autovector.h"
|
||||
#include "util/kv_map.h"
|
||||
#include "util/vector_iterator.h"
|
||||
|
||||
+7
-7
@@ -71,7 +71,6 @@
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -358,14 +357,14 @@ class Repairer {
|
||||
|
||||
// Open the log file
|
||||
std::string logname = LogFileName(db_options_.wal_dir, log);
|
||||
std::unique_ptr<SequentialFile> lfile;
|
||||
Status status = env_->NewSequentialFile(
|
||||
logname, &lfile, env_->OptimizeForLogRead(env_options_));
|
||||
const auto& fs = env_->GetFileSystem();
|
||||
std::unique_ptr<SequentialFileReader> lfile_reader;
|
||||
Status status = SequentialFileReader::Create(
|
||||
fs, logname, fs->OptimizeForLogRead(env_options_), &lfile_reader,
|
||||
nullptr);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
std::unique_ptr<SequentialFileReader> lfile_reader(new SequentialFileReader(
|
||||
NewLegacySequentialFileWrapper(lfile), logname));
|
||||
|
||||
// Create the log reader.
|
||||
LogReporter reporter;
|
||||
@@ -426,7 +425,8 @@ class Repairer {
|
||||
Arena arena;
|
||||
ScopedArenaIterator iter(mem->NewIterator(ro, &arena));
|
||||
int64_t _current_time = 0;
|
||||
status = env_->GetCurrentTime(&_current_time); // ignore error
|
||||
immutable_db_options_.clock->GetCurrentTime(&_current_time)
|
||||
.PermitUncheckedError(); // ignore error
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
SnapshotChecker* snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
|
||||
|
||||
+4
-4
@@ -184,7 +184,7 @@ TEST_F(RepairTest, UnflushedSst) {
|
||||
{
|
||||
uint64_t total_ssts_size;
|
||||
std::unordered_map<std::string, uint64_t> sst_files;
|
||||
ASSERT_OK(GetAllSSTFiles(&sst_files, &total_ssts_size));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
||||
ASSERT_EQ(total_ssts_size, 0);
|
||||
}
|
||||
// Need to get path before Close() deletes db_, but delete it after Close() to
|
||||
@@ -203,7 +203,7 @@ TEST_F(RepairTest, UnflushedSst) {
|
||||
{
|
||||
uint64_t total_ssts_size;
|
||||
std::unordered_map<std::string, uint64_t> sst_files;
|
||||
ASSERT_OK(GetAllSSTFiles(&sst_files, &total_ssts_size));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
||||
ASSERT_GT(total_ssts_size, 0);
|
||||
}
|
||||
ASSERT_EQ(Get("key"), "val");
|
||||
@@ -221,7 +221,7 @@ TEST_F(RepairTest, SeparateWalDir) {
|
||||
{
|
||||
uint64_t total_ssts_size;
|
||||
std::unordered_map<std::string, uint64_t> sst_files;
|
||||
ASSERT_OK(GetAllSSTFiles(&sst_files, &total_ssts_size));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
||||
ASSERT_EQ(total_ssts_size, 0);
|
||||
}
|
||||
std::string manifest_path =
|
||||
@@ -241,7 +241,7 @@ TEST_F(RepairTest, SeparateWalDir) {
|
||||
{
|
||||
uint64_t total_ssts_size;
|
||||
std::unordered_map<std::string, uint64_t> sst_files;
|
||||
ASSERT_OK(GetAllSSTFiles(&sst_files, &total_ssts_size));
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
||||
ASSERT_GT(total_ssts_size, 0);
|
||||
}
|
||||
ASSERT_EQ(Get("key"), "val");
|
||||
|
||||
+5
-5
@@ -106,14 +106,14 @@ Status TableCache::GetTableReader(
|
||||
TableFileName(ioptions_.cf_paths, fd.GetNumber(), fd.GetPathId());
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
FileOptions fopts = file_options;
|
||||
Status s = PrepareIOFromReadOptions(ro, ioptions_.env, fopts.io_options);
|
||||
Status s = PrepareIOFromReadOptions(ro, ioptions_.clock, fopts.io_options);
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, fopts, &file, nullptr);
|
||||
}
|
||||
RecordTick(ioptions_.statistics, NO_FILE_OPENS);
|
||||
if (s.IsPathNotFound()) {
|
||||
fname = Rocks2LevelTableFileName(fname);
|
||||
s = PrepareIOFromReadOptions(ro, ioptions_.env, fopts.io_options);
|
||||
s = PrepareIOFromReadOptions(ro, ioptions_.clock, fopts.io_options);
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, file_options, &file,
|
||||
nullptr);
|
||||
@@ -125,10 +125,10 @@ Status TableCache::GetTableReader(
|
||||
if (!sequential_mode && ioptions_.advise_random_on_open) {
|
||||
file->Hint(FSRandomAccessFile::kRandom);
|
||||
}
|
||||
StopWatch sw(ioptions_.env, ioptions_.statistics, TABLE_OPEN_IO_MICROS);
|
||||
StopWatch sw(ioptions_.clock, ioptions_.statistics, TABLE_OPEN_IO_MICROS);
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(
|
||||
std::move(file), fname, ioptions_.env, io_tracer_,
|
||||
std::move(file), fname, ioptions_.clock, io_tracer_,
|
||||
record_read_stats ? ioptions_.statistics : nullptr, SST_READ_MICROS,
|
||||
file_read_hist, ioptions_.rate_limiter, ioptions_.listeners));
|
||||
s = ioptions_.table_factory->NewTableReader(
|
||||
@@ -161,7 +161,7 @@ Status TableCache::FindTable(const ReadOptions& ro,
|
||||
HistogramImpl* file_read_hist, bool skip_filters,
|
||||
int level, bool prefetch_index_and_filter_in_cache,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
PERF_TIMER_GUARD_WITH_ENV(find_table_nanos, ioptions_.env);
|
||||
PERF_TIMER_GUARD_WITH_CLOCK(find_table_nanos, ioptions_.clock);
|
||||
uint64_t number = fd.GetNumber();
|
||||
Slice key = GetSliceForFileNumber(&number);
|
||||
*handle = cache_->Lookup(key);
|
||||
|
||||
+1
-1
@@ -10,9 +10,9 @@
|
||||
// Thread-safe (provides internal synchronization)
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
|
||||
+126
-12
@@ -62,8 +62,6 @@ void VersionEditHandlerBase::Iterate(log::Reader& reader,
|
||||
s = *log_read_status;
|
||||
}
|
||||
|
||||
read_buffer_.Clear();
|
||||
|
||||
CheckIterationResult(reader, &s);
|
||||
|
||||
if (!s.ok()) {
|
||||
@@ -111,17 +109,31 @@ Status FileChecksumRetriever::ApplyVersionEdit(VersionEdit& edit,
|
||||
return s;
|
||||
}
|
||||
}
|
||||
for (const auto& new_blob_file : edit.GetBlobFileAdditions()) {
|
||||
std::string checksum_value = new_blob_file.GetChecksumValue();
|
||||
std::string checksum_method = new_blob_file.GetChecksumMethod();
|
||||
assert(checksum_value.empty() == checksum_method.empty());
|
||||
if (checksum_method.empty()) {
|
||||
checksum_value = kUnknownFileChecksum;
|
||||
checksum_method = kUnknownFileChecksumFuncName;
|
||||
}
|
||||
Status s = file_checksum_list_.InsertOneFileChecksum(
|
||||
new_blob_file.GetBlobFileNumber(), checksum_value, checksum_method);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
VersionEditHandler::VersionEditHandler(
|
||||
bool read_only, const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only, std::vector<ColumnFamilyDescriptor> column_families,
|
||||
VersionSet* version_set, bool track_missing_files,
|
||||
bool no_error_if_table_files_missing,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, bool skip_load_table_files)
|
||||
: VersionEditHandlerBase(),
|
||||
read_only_(read_only),
|
||||
column_families_(column_families),
|
||||
column_families_(std::move(column_families)),
|
||||
version_set_(version_set),
|
||||
track_missing_files_(track_missing_files),
|
||||
no_error_if_table_files_missing_(no_error_if_table_files_missing),
|
||||
@@ -337,7 +349,8 @@ void VersionEditHandler::CheckIterationResult(const log::Reader& reader,
|
||||
}
|
||||
// There were some column families in the MANIFEST that weren't specified
|
||||
// in the argument. This is OK in read_only mode
|
||||
if (s->ok() && !read_only_ && !column_families_not_found_.empty()) {
|
||||
if (s->ok() && MustOpenAllColumnFamilies() &&
|
||||
!column_families_not_found_.empty()) {
|
||||
std::string msg;
|
||||
for (const auto& cf : column_families_not_found_) {
|
||||
msg.append(", ");
|
||||
@@ -354,6 +367,9 @@ void VersionEditHandler::CheckIterationResult(const log::Reader& reader,
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.prev_log_number_);
|
||||
version_set_->MarkFileNumberUsed(version_edit_params_.log_number_);
|
||||
for (auto* cfd : *(version_set_->GetColumnFamilySet())) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
auto builder_iter = builders_.find(cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
auto* builder = builder_iter->second->version_builder();
|
||||
@@ -438,11 +454,9 @@ ColumnFamilyData* VersionEditHandler::DestroyCfAndCleanup(
|
||||
ColumnFamilyData* ret =
|
||||
version_set_->GetColumnFamilySet()->GetColumnFamily(edit.column_family_);
|
||||
assert(ret != nullptr);
|
||||
if (ret->UnrefAndTryDelete()) {
|
||||
ret = nullptr;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
ret->SetDropped();
|
||||
ret->UnrefAndTryDelete();
|
||||
ret = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -558,7 +572,7 @@ Status VersionEditHandler::ExtractInfoFromVersionEdit(ColumnFamilyData* cfd,
|
||||
}
|
||||
|
||||
VersionEditHandlerPointInTime::VersionEditHandlerPointInTime(
|
||||
bool read_only, const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only, std::vector<ColumnFamilyDescriptor> column_families,
|
||||
VersionSet* version_set, const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: VersionEditHandler(read_only, column_families, version_set,
|
||||
/*track_missing_files=*/true,
|
||||
@@ -627,7 +641,7 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
uint64_t file_num = fd.GetNumber();
|
||||
const std::string fpath =
|
||||
MakeTableFileName(cfd->ioptions()->cf_paths[0].path, file_num);
|
||||
s = version_set_->VerifyFileMetadata(fpath, meta);
|
||||
s = VerifyFile(fpath, meta);
|
||||
if (s.IsPathNotFound() || s.IsNotFound() || s.IsCorruption()) {
|
||||
missing_files.insert(file_num);
|
||||
s = Status::OK();
|
||||
@@ -668,6 +682,106 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
return s;
|
||||
}
|
||||
|
||||
Status VersionEditHandlerPointInTime::VerifyFile(const std::string& fpath,
|
||||
const FileMetaData& fmeta) {
|
||||
return version_set_->VerifyFileMetadata(fpath, fmeta);
|
||||
}
|
||||
|
||||
Status ManifestTailer::Initialize() {
|
||||
if (Mode::kRecovery == mode_) {
|
||||
return VersionEditHandler::Initialize();
|
||||
}
|
||||
assert(Mode::kCatchUp == mode_);
|
||||
Status s;
|
||||
if (!initialized_) {
|
||||
ColumnFamilySet* cfd_set = version_set_->GetColumnFamilySet();
|
||||
assert(cfd_set);
|
||||
ColumnFamilyData* default_cfd = cfd_set->GetDefault();
|
||||
assert(default_cfd);
|
||||
auto builder_iter = builders_.find(default_cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
|
||||
Version* dummy_version = default_cfd->dummy_versions();
|
||||
assert(dummy_version);
|
||||
Version* base_version = dummy_version->Next();
|
||||
assert(base_version);
|
||||
base_version->Ref();
|
||||
VersionBuilderUPtr new_builder(
|
||||
new BaseReferencedVersionBuilder(default_cfd, base_version));
|
||||
builder_iter->second = std::move(new_builder);
|
||||
|
||||
initialized_ = true;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status ManifestTailer::ApplyVersionEdit(VersionEdit& edit,
|
||||
ColumnFamilyData** cfd) {
|
||||
Status s = VersionEditHandler::ApplyVersionEdit(edit, cfd);
|
||||
if (s.ok()) {
|
||||
assert(cfd);
|
||||
if (*cfd) {
|
||||
cfds_changed_.insert(*cfd);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status ManifestTailer::OnColumnFamilyAdd(VersionEdit& edit,
|
||||
ColumnFamilyData** cfd) {
|
||||
if (Mode::kRecovery == mode_) {
|
||||
return VersionEditHandler::OnColumnFamilyAdd(edit, cfd);
|
||||
}
|
||||
assert(Mode::kCatchUp == mode_);
|
||||
ColumnFamilySet* cfd_set = version_set_->GetColumnFamilySet();
|
||||
assert(cfd_set);
|
||||
ColumnFamilyData* tmp_cfd = cfd_set->GetColumnFamily(edit.GetColumnFamily());
|
||||
assert(cfd);
|
||||
*cfd = tmp_cfd;
|
||||
if (!tmp_cfd) {
|
||||
// For now, ignore new column families created after Recover() succeeds.
|
||||
return Status::OK();
|
||||
}
|
||||
auto builder_iter = builders_.find(edit.GetColumnFamily());
|
||||
assert(builder_iter != builders_.end());
|
||||
|
||||
Version* dummy_version = tmp_cfd->dummy_versions();
|
||||
assert(dummy_version);
|
||||
Version* base_version = dummy_version->Next();
|
||||
assert(base_version);
|
||||
base_version->Ref();
|
||||
VersionBuilderUPtr new_builder(
|
||||
new BaseReferencedVersionBuilder(tmp_cfd, base_version));
|
||||
builder_iter->second = std::move(new_builder);
|
||||
|
||||
#ifndef NDEBUG
|
||||
auto version_iter = versions_.find(edit.GetColumnFamily());
|
||||
assert(version_iter != versions_.end());
|
||||
#endif // !NDEBUG
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void ManifestTailer::CheckIterationResult(const log::Reader& reader,
|
||||
Status* s) {
|
||||
VersionEditHandlerPointInTime::CheckIterationResult(reader, s);
|
||||
assert(s);
|
||||
if (s->ok()) {
|
||||
if (Mode::kRecovery == mode_) {
|
||||
mode_ = Mode::kCatchUp;
|
||||
} else {
|
||||
assert(Mode::kCatchUp == mode_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status ManifestTailer::VerifyFile(const std::string& fpath,
|
||||
const FileMetaData& fmeta) {
|
||||
Status s = VersionEditHandlerPointInTime::VerifyFile(fpath, fmeta);
|
||||
// TODO: Open file or create hard link to prevent the file from being
|
||||
// deleted.
|
||||
return s;
|
||||
}
|
||||
|
||||
void DumpManifestHandler::CheckIterationResult(const log::Reader& reader,
|
||||
Status* s) {
|
||||
VersionEditHandler::CheckIterationResult(reader, s);
|
||||
|
||||
+60
-13
@@ -15,6 +15,8 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
struct FileMetaData;
|
||||
|
||||
class VersionEditHandlerBase {
|
||||
public:
|
||||
explicit VersionEditHandlerBase()
|
||||
@@ -26,6 +28,8 @@ class VersionEditHandlerBase {
|
||||
|
||||
const Status& status() const { return status_; }
|
||||
|
||||
AtomicGroupReadBuffer& GetReadBuffer() { return read_buffer_; }
|
||||
|
||||
protected:
|
||||
explicit VersionEditHandlerBase(uint64_t max_read_size)
|
||||
: max_manifest_read_size_(max_read_size) {}
|
||||
@@ -37,6 +41,8 @@ class VersionEditHandlerBase {
|
||||
virtual void CheckIterationResult(const log::Reader& /*reader*/,
|
||||
Status* /*s*/) {}
|
||||
|
||||
void ClearReadBuffer() { read_buffer_.Clear(); }
|
||||
|
||||
Status status_;
|
||||
|
||||
private:
|
||||
@@ -125,15 +131,14 @@ class VersionEditHandler : public VersionEditHandlerBase {
|
||||
|
||||
protected:
|
||||
explicit VersionEditHandler(
|
||||
bool read_only,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only, std::vector<ColumnFamilyDescriptor> column_families,
|
||||
VersionSet* version_set, bool track_missing_files,
|
||||
bool no_error_if_table_files_missing,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, bool skip_load_table_files);
|
||||
|
||||
Status ApplyVersionEdit(VersionEdit& edit, ColumnFamilyData** cfd) override;
|
||||
|
||||
Status OnColumnFamilyAdd(VersionEdit& edit, ColumnFamilyData** cfd);
|
||||
virtual Status OnColumnFamilyAdd(VersionEdit& edit, ColumnFamilyData** cfd);
|
||||
|
||||
Status OnColumnFamilyDrop(VersionEdit& edit, ColumnFamilyData** cfd);
|
||||
|
||||
@@ -163,8 +168,10 @@ class VersionEditHandler : public VersionEditHandlerBase {
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
bool is_initial_load);
|
||||
|
||||
virtual bool MustOpenAllColumnFamilies() const { return !read_only_; }
|
||||
|
||||
const bool read_only_;
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families_;
|
||||
std::vector<ColumnFamilyDescriptor> column_families_;
|
||||
VersionSet* version_set_;
|
||||
std::unordered_map<uint32_t, VersionBuilderUPtr> builders_;
|
||||
std::unordered_map<std::string, ColumnFamilyOptions> name_to_options_;
|
||||
@@ -179,12 +186,11 @@ class VersionEditHandler : public VersionEditHandlerBase {
|
||||
bool no_error_if_table_files_missing_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
bool skip_load_table_files_;
|
||||
bool initialized_;
|
||||
|
||||
private:
|
||||
Status ExtractInfoFromVersionEdit(ColumnFamilyData* cfd,
|
||||
const VersionEdit& edit);
|
||||
|
||||
bool initialized_;
|
||||
};
|
||||
|
||||
// A class similar to its base class, i.e. VersionEditHandler.
|
||||
@@ -196,8 +202,7 @@ class VersionEditHandler : public VersionEditHandlerBase {
|
||||
class VersionEditHandlerPointInTime : public VersionEditHandler {
|
||||
public:
|
||||
VersionEditHandlerPointInTime(
|
||||
bool read_only,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only, std::vector<ColumnFamilyDescriptor> column_families,
|
||||
VersionSet* version_set, const std::shared_ptr<IOTracer>& io_tracer);
|
||||
~VersionEditHandlerPointInTime() override;
|
||||
|
||||
@@ -206,17 +211,59 @@ class VersionEditHandlerPointInTime : public VersionEditHandler {
|
||||
ColumnFamilyData* DestroyCfAndCleanup(const VersionEdit& edit) override;
|
||||
Status MaybeCreateVersion(const VersionEdit& edit, ColumnFamilyData* cfd,
|
||||
bool force_create_version) override;
|
||||
virtual Status VerifyFile(const std::string& fpath,
|
||||
const FileMetaData& fmeta);
|
||||
|
||||
private:
|
||||
std::unordered_map<uint32_t, Version*> versions_;
|
||||
};
|
||||
|
||||
class ManifestTailer : public VersionEditHandlerPointInTime {
|
||||
public:
|
||||
explicit ManifestTailer(std::vector<ColumnFamilyDescriptor> column_families,
|
||||
VersionSet* version_set,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: VersionEditHandlerPointInTime(/*read_only=*/false, column_families,
|
||||
version_set, io_tracer),
|
||||
mode_(Mode::kRecovery) {}
|
||||
|
||||
void PrepareToReadNewManifest() {
|
||||
initialized_ = false;
|
||||
ClearReadBuffer();
|
||||
}
|
||||
|
||||
std::unordered_set<ColumnFamilyData*>& GetUpdatedColumnFamilies() {
|
||||
return cfds_changed_;
|
||||
}
|
||||
|
||||
protected:
|
||||
Status Initialize() override;
|
||||
|
||||
bool MustOpenAllColumnFamilies() const override { return false; }
|
||||
|
||||
Status ApplyVersionEdit(VersionEdit& edit, ColumnFamilyData** cfd) override;
|
||||
|
||||
Status OnColumnFamilyAdd(VersionEdit& edit, ColumnFamilyData** cfd) override;
|
||||
|
||||
void CheckIterationResult(const log::Reader& reader, Status* s) override;
|
||||
|
||||
Status VerifyFile(const std::string& fpath,
|
||||
const FileMetaData& fmeta) override;
|
||||
|
||||
enum Mode : uint8_t {
|
||||
kRecovery = 0,
|
||||
kCatchUp = 1,
|
||||
};
|
||||
|
||||
Mode mode_;
|
||||
std::unordered_set<ColumnFamilyData*> cfds_changed_;
|
||||
};
|
||||
|
||||
class DumpManifestHandler : public VersionEditHandler {
|
||||
public:
|
||||
DumpManifestHandler(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
VersionSet* version_set, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
bool verbose, bool hex, bool json)
|
||||
DumpManifestHandler(std::vector<ColumnFamilyDescriptor> column_families,
|
||||
VersionSet* version_set,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, bool verbose,
|
||||
bool hex, bool json)
|
||||
: VersionEditHandler(
|
||||
/*read_only=*/true, column_families, version_set,
|
||||
/*track_missing_files=*/false,
|
||||
|
||||
+97
-571
@@ -9,11 +9,10 @@
|
||||
|
||||
#include "db/version_set.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -1762,6 +1761,7 @@ Version::Version(ColumnFamilyData* column_family_data, VersionSet* vset,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
uint64_t version_number)
|
||||
: env_(vset->env_),
|
||||
clock_(vset->clock_),
|
||||
cfd_(column_family_data),
|
||||
info_log_((cfd_ == nullptr) ? nullptr : cfd_->ioptions()->info_log),
|
||||
db_statistics_((cfd_ == nullptr) ? nullptr
|
||||
@@ -1792,8 +1792,8 @@ Version::Version(ColumnFamilyData* column_family_data, VersionSet* vset,
|
||||
io_tracer_(io_tracer) {}
|
||||
|
||||
Status Version::GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
const Slice& blob_index_slice,
|
||||
PinnableSlice* value) const {
|
||||
const Slice& blob_index_slice, PinnableSlice* value,
|
||||
uint64_t* bytes_read) const {
|
||||
if (read_options.read_tier == kBlockCacheTier) {
|
||||
return Status::Incomplete("Cannot read blob: no disk I/O allowed");
|
||||
}
|
||||
@@ -1807,12 +1807,12 @@ Status Version::GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
}
|
||||
}
|
||||
|
||||
return GetBlob(read_options, user_key, blob_index, value);
|
||||
return GetBlob(read_options, user_key, blob_index, value, bytes_read);
|
||||
}
|
||||
|
||||
Status Version::GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
const BlobIndex& blob_index,
|
||||
PinnableSlice* value) const {
|
||||
const BlobIndex& blob_index, PinnableSlice* value,
|
||||
uint64_t* bytes_read) const {
|
||||
assert(value);
|
||||
|
||||
if (blob_index.HasTTL() || blob_index.IsInlined()) {
|
||||
@@ -1842,7 +1842,7 @@ Status Version::GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
assert(blob_file_reader.GetValue());
|
||||
const Status s = blob_file_reader.GetValue()->GetBlob(
|
||||
read_options, user_key, blob_index.offset(), blob_index.size(),
|
||||
blob_index.compression(), value);
|
||||
blob_index.compression(), value, bytes_read);
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -1880,7 +1880,7 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
user_comparator(), merge_operator_, info_log_, db_statistics_,
|
||||
status->ok() ? GetContext::kNotFound : GetContext::kMerge, user_key,
|
||||
do_merge ? value : nullptr, do_merge ? timestamp : nullptr, value_found,
|
||||
merge_context, do_merge, max_covering_tombstone_seq, this->env_, seq,
|
||||
merge_context, do_merge, max_covering_tombstone_seq, clock_, seq,
|
||||
merge_operator_ ? &pinned_iters_mgr : nullptr, callback, is_blob_to_use,
|
||||
tracing_get_id);
|
||||
|
||||
@@ -1908,7 +1908,7 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
bool timer_enabled =
|
||||
GetPerfLevel() >= PerfLevel::kEnableTimeExceptForMutex &&
|
||||
get_perf_context()->per_level_perf_context_enabled;
|
||||
StopWatchNano timer(env_, timer_enabled /* auto_start */);
|
||||
StopWatchNano timer(clock_, timer_enabled /* auto_start */);
|
||||
*status = table_cache_->Get(
|
||||
read_options, *internal_comparator(), *f->file_metadata, ikey,
|
||||
&get_context, mutable_cf_options_.prefix_extractor.get(),
|
||||
@@ -1952,7 +1952,10 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
|
||||
if (is_blob_index) {
|
||||
if (do_merge && value) {
|
||||
*status = GetBlob(read_options, user_key, *value, value);
|
||||
constexpr uint64_t* bytes_read = nullptr;
|
||||
|
||||
*status =
|
||||
GetBlob(read_options, user_key, *value, value, bytes_read);
|
||||
if (!status->ok()) {
|
||||
if (status->IsIncomplete()) {
|
||||
get_context.MarkKeyMayExist();
|
||||
@@ -1997,7 +2000,7 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
std::string* str_value = value != nullptr ? value->GetSelf() : nullptr;
|
||||
*status = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, user_key, nullptr, merge_context->GetOperands(),
|
||||
str_value, info_log_, db_statistics_, env_,
|
||||
str_value, info_log_, db_statistics_, clock_,
|
||||
nullptr /* result_operand */, true);
|
||||
if (LIKELY(value != nullptr)) {
|
||||
value->PinSelf();
|
||||
@@ -2034,9 +2037,9 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
user_comparator(), merge_operator_, info_log_, db_statistics_,
|
||||
iter->s->ok() ? GetContext::kNotFound : GetContext::kMerge,
|
||||
iter->ukey_with_ts, iter->value, iter->timestamp, nullptr,
|
||||
&(iter->merge_context), true, &iter->max_covering_tombstone_seq,
|
||||
this->env_, nullptr, merge_operator_ ? &pinned_iters_mgr : nullptr,
|
||||
callback, &iter->is_blob_index, tracing_mget_id);
|
||||
&(iter->merge_context), true, &iter->max_covering_tombstone_seq, clock_,
|
||||
nullptr, merge_operator_ ? &pinned_iters_mgr : nullptr, callback,
|
||||
&iter->is_blob_index, tracing_mget_id);
|
||||
// MergeInProgress status, if set, has been transferred to the get_context
|
||||
// state, so we set status to ok here. From now on, the iter status will
|
||||
// be used for IO errors, and get_context state will be used for any
|
||||
@@ -2066,7 +2069,7 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
bool timer_enabled =
|
||||
GetPerfLevel() >= PerfLevel::kEnableTimeExceptForMutex &&
|
||||
get_perf_context()->per_level_perf_context_enabled;
|
||||
StopWatchNano timer(env_, timer_enabled /* auto_start */);
|
||||
StopWatchNano timer(clock_, timer_enabled /* auto_start */);
|
||||
s = table_cache_->MultiGet(
|
||||
read_options, *internal_comparator(), *f->file_metadata, &file_range,
|
||||
mutable_cf_options_.prefix_extractor.get(),
|
||||
@@ -2146,8 +2149,10 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
|
||||
if (iter->is_blob_index) {
|
||||
if (iter->value) {
|
||||
constexpr uint64_t* bytes_read = nullptr;
|
||||
|
||||
*status = GetBlob(read_options, iter->ukey_with_ts, *iter->value,
|
||||
iter->value);
|
||||
iter->value, bytes_read);
|
||||
if (!status->ok()) {
|
||||
if (status->IsIncomplete()) {
|
||||
get_context.MarkKeyMayExist();
|
||||
@@ -2229,7 +2234,7 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
iter->value != nullptr ? iter->value->GetSelf() : nullptr;
|
||||
*status = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, user_key, nullptr, iter->merge_context.GetOperands(),
|
||||
str_value, info_log_, db_statistics_, env_,
|
||||
str_value, info_log_, db_statistics_, clock_,
|
||||
nullptr /* result_operand */, true);
|
||||
if (LIKELY(iter->value != nullptr)) {
|
||||
iter->value->PinSelf();
|
||||
@@ -2528,7 +2533,7 @@ uint32_t GetExpiredTtlFilesCount(const ImmutableCFOptions& ioptions,
|
||||
uint32_t ttl_expired_files_count = 0;
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions.env->GetCurrentTime(&_current_time);
|
||||
auto status = ioptions.clock->GetCurrentTime(&_current_time);
|
||||
if (status.ok()) {
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
for (FileMetaData* f : files) {
|
||||
@@ -2697,7 +2702,7 @@ void VersionStorageInfo::ComputeExpiredTtlFiles(
|
||||
expired_ttl_files_.clear();
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions.env->GetCurrentTime(&_current_time);
|
||||
auto status = ioptions.clock->GetCurrentTime(&_current_time);
|
||||
if (!status.ok()) {
|
||||
return;
|
||||
}
|
||||
@@ -2724,7 +2729,7 @@ void VersionStorageInfo::ComputeFilesMarkedForPeriodicCompaction(
|
||||
files_marked_for_periodic_compaction_.clear();
|
||||
|
||||
int64_t temp_current_time;
|
||||
auto status = ioptions.env->GetCurrentTime(&temp_current_time);
|
||||
auto status = ioptions.clock->GetCurrentTime(&temp_current_time);
|
||||
if (!status.ok()) {
|
||||
return;
|
||||
}
|
||||
@@ -3783,6 +3788,7 @@ VersionSet::VersionSet(const std::string& dbname,
|
||||
table_cache_(table_cache),
|
||||
env_(_db_options->env),
|
||||
fs_(_db_options->fs, io_tracer),
|
||||
clock_(_db_options->clock),
|
||||
dbname_(dbname),
|
||||
db_options_(_db_options),
|
||||
next_file_number_(2),
|
||||
@@ -4077,6 +4083,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
uint64_t new_manifest_file_size = 0;
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
IOStatus manifest_io_status;
|
||||
{
|
||||
FileOptions opt_file_opts = fs_->OptimizeForManifestWrite(file_options_);
|
||||
mu->Unlock();
|
||||
@@ -4120,7 +4127,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
db_options_->manifest_preallocation_size);
|
||||
FileTypeSet tmp_set = db_options_->checksum_handoff_file_types;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(descriptor_file), descriptor_fname, opt_file_opts, env_,
|
||||
std::move(descriptor_file), descriptor_fname, opt_file_opts, clock_,
|
||||
io_tracer_, nullptr, db_options_->listeners, nullptr,
|
||||
tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
descriptor_log_.reset(
|
||||
@@ -4128,6 +4135,7 @@ Status VersionSet::ProcessManifestWrites(
|
||||
s = WriteCurrentStateToManifest(curr_state, wal_additions,
|
||||
descriptor_log_.get(), io_s);
|
||||
} else {
|
||||
manifest_io_status = io_s;
|
||||
s = io_s;
|
||||
}
|
||||
}
|
||||
@@ -4165,11 +4173,13 @@ Status VersionSet::ProcessManifestWrites(
|
||||
io_s = descriptor_log_->AddRecord(record);
|
||||
if (!io_s.ok()) {
|
||||
s = io_s;
|
||||
manifest_io_status = io_s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
io_s = SyncManifest(env_, db_options_, descriptor_log_->file());
|
||||
io_s = SyncManifest(db_options_, descriptor_log_->file());
|
||||
manifest_io_status = io_s;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"VersionSet::ProcessManifestWrites:AfterSyncManifest", &io_s);
|
||||
}
|
||||
@@ -4182,6 +4192,9 @@ Status VersionSet::ProcessManifestWrites(
|
||||
|
||||
// If we just created a new descriptor file, install it by writing a
|
||||
// new CURRENT file that points to it.
|
||||
if (s.ok()) {
|
||||
assert(manifest_io_status.ok());
|
||||
}
|
||||
if (s.ok() && new_descriptor_log) {
|
||||
io_s = SetCurrentFile(fs_.get(), dbname_, pending_manifest_file_number_,
|
||||
db_directory);
|
||||
@@ -4297,11 +4310,41 @@ Status VersionSet::ProcessManifestWrites(
|
||||
for (auto v : versions) {
|
||||
delete v;
|
||||
}
|
||||
if (manifest_io_status.ok()) {
|
||||
manifest_file_number_ = pending_manifest_file_number_;
|
||||
manifest_file_size_ = new_manifest_file_size;
|
||||
}
|
||||
// If manifest append failed for whatever reason, the file could be
|
||||
// corrupted. So we need to force the next version update to start a
|
||||
// new manifest file.
|
||||
descriptor_log_.reset();
|
||||
if (new_descriptor_log) {
|
||||
// If manifest operations failed, then we know the CURRENT file still
|
||||
// points to the original MANIFEST. Therefore, we can safely delete the
|
||||
// new MANIFEST.
|
||||
// If manifest operations succeeded, and we are here, then it is possible
|
||||
// that renaming tmp file to CURRENT failed.
|
||||
//
|
||||
// On local POSIX-compliant FS, the CURRENT must point to the original
|
||||
// MANIFEST. We can delete the new MANIFEST for simplicity, but we can also
|
||||
// keep it. Future recovery will ignore this MANIFEST. It's also ok for the
|
||||
// process not to crash and continue using the db. Any future LogAndApply()
|
||||
// call will switch to a new MANIFEST and update CURRENT, still ignoring
|
||||
// this one.
|
||||
//
|
||||
// On non-local FS, it is
|
||||
// possible that the rename operation succeeded on the server (remote)
|
||||
// side, but the client somehow returns a non-ok status to RocksDB. Note
|
||||
// that this does not violate atomicity. Should we delete the new MANIFEST
|
||||
// successfully, a subsequent recovery attempt will likely see the CURRENT
|
||||
// pointing to the new MANIFEST, thus fail. We will not be able to open the
|
||||
// DB again. Therefore, if manifest operations succeed, we should keep the
|
||||
// the new MANIFEST. If the process proceeds, any future LogAndApply() call
|
||||
// will switch to a new MANIFEST and update CURRENT. If user tries to
|
||||
// re-open the DB,
|
||||
// a) CURRENT points to the new MANIFEST, and the new MANIFEST is present.
|
||||
// b) CURRENT points to the original MANIFEST, and the original MANIFEST
|
||||
// also exists.
|
||||
if (new_descriptor_log && !manifest_io_status.ok()) {
|
||||
ROCKS_LOG_INFO(db_options_->info_log,
|
||||
"Deleting manifest %" PRIu64 " current manifest %" PRIu64
|
||||
"\n",
|
||||
@@ -4484,110 +4527,6 @@ Status VersionSet::LogAndApplyHelper(ColumnFamilyData* cfd,
|
||||
return builder ? builder->Apply(edit) : Status::OK();
|
||||
}
|
||||
|
||||
Status VersionSet::ApplyOneVersionEditToBuilder(
|
||||
VersionEdit& edit,
|
||||
const std::unordered_map<std::string, ColumnFamilyOptions>& name_to_options,
|
||||
std::unordered_map<int, std::string>& column_families_not_found,
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>&
|
||||
builders,
|
||||
VersionEditParams* version_edit_params) {
|
||||
// Not found means that user didn't supply that column
|
||||
// family option AND we encountered column family add
|
||||
// record. Once we encounter column family drop record,
|
||||
// we will delete the column family from
|
||||
// column_families_not_found.
|
||||
bool cf_in_not_found = (column_families_not_found.find(edit.column_family_) !=
|
||||
column_families_not_found.end());
|
||||
// in builders means that user supplied that column family
|
||||
// option AND that we encountered column family add record
|
||||
bool cf_in_builders = builders.find(edit.column_family_) != builders.end();
|
||||
|
||||
// they can't both be true
|
||||
assert(!(cf_in_not_found && cf_in_builders));
|
||||
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
|
||||
if (edit.is_column_family_add_) {
|
||||
if (cf_in_builders || cf_in_not_found) {
|
||||
return Status::Corruption(
|
||||
"Manifest adding the same column family twice: " +
|
||||
edit.column_family_name_);
|
||||
}
|
||||
auto cf_options = name_to_options.find(edit.column_family_name_);
|
||||
// implicitly add persistent_stats column family without requiring user
|
||||
// to specify
|
||||
bool is_persistent_stats_column_family =
|
||||
edit.column_family_name_.compare(kPersistentStatsColumnFamilyName) == 0;
|
||||
if (cf_options == name_to_options.end() &&
|
||||
!is_persistent_stats_column_family) {
|
||||
column_families_not_found.insert(
|
||||
{edit.column_family_, edit.column_family_name_});
|
||||
} else {
|
||||
// recover persistent_stats CF from a DB that already contains it
|
||||
if (is_persistent_stats_column_family) {
|
||||
ColumnFamilyOptions cfo;
|
||||
OptimizeForPersistentStats(&cfo);
|
||||
cfd = CreateColumnFamily(cfo, &edit);
|
||||
} else {
|
||||
cfd = CreateColumnFamily(cf_options->second, &edit);
|
||||
}
|
||||
cfd->set_initialized();
|
||||
builders.insert(std::make_pair(
|
||||
edit.column_family_, std::unique_ptr<BaseReferencedVersionBuilder>(
|
||||
new BaseReferencedVersionBuilder(cfd))));
|
||||
}
|
||||
} else if (edit.is_column_family_drop_) {
|
||||
if (cf_in_builders) {
|
||||
auto builder = builders.find(edit.column_family_);
|
||||
assert(builder != builders.end());
|
||||
builders.erase(builder);
|
||||
cfd = column_family_set_->GetColumnFamily(edit.column_family_);
|
||||
assert(cfd != nullptr);
|
||||
if (cfd->UnrefAndTryDelete()) {
|
||||
cfd = nullptr;
|
||||
} else {
|
||||
// who else can have reference to cfd!?
|
||||
assert(false);
|
||||
}
|
||||
} else if (cf_in_not_found) {
|
||||
column_families_not_found.erase(edit.column_family_);
|
||||
} else {
|
||||
return Status::Corruption(
|
||||
"Manifest - dropping non-existing column family");
|
||||
}
|
||||
} else if (edit.IsWalAddition()) {
|
||||
Status s = wals_.AddWals(edit.GetWalAdditions());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else if (edit.IsWalDeletion()) {
|
||||
Status s = wals_.DeleteWalsBefore(edit.GetWalDeletion().GetLogNumber());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else if (!cf_in_not_found) {
|
||||
if (!cf_in_builders) {
|
||||
return Status::Corruption(
|
||||
"Manifest record referencing unknown column family");
|
||||
}
|
||||
|
||||
cfd = column_family_set_->GetColumnFamily(edit.column_family_);
|
||||
// this should never happen since cf_in_builders is true
|
||||
assert(cfd != nullptr);
|
||||
|
||||
// if it is not column family add or column family drop,
|
||||
// then it's a file add/delete, which should be forwarded
|
||||
// to builder
|
||||
auto builder = builders.find(edit.column_family_);
|
||||
assert(builder != builders.end());
|
||||
Status s = builder->second->version_builder()->Apply(&edit);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return ExtractInfoFromVersionEdit(cfd, edit, version_edit_params);
|
||||
}
|
||||
|
||||
Status VersionSet::ExtractInfoFromVersionEdit(
|
||||
ColumnFamilyData* cfd, const VersionEdit& from_edit,
|
||||
VersionEditParams* version_edit_params) {
|
||||
@@ -4673,77 +4612,6 @@ Status VersionSet::GetCurrentManifestPath(const std::string& dbname,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status VersionSet::ReadAndRecover(
|
||||
log::Reader& reader, AtomicGroupReadBuffer* read_buffer,
|
||||
const std::unordered_map<std::string, ColumnFamilyOptions>& name_to_options,
|
||||
std::unordered_map<int, std::string>& column_families_not_found,
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>&
|
||||
builders,
|
||||
Status* log_read_status, VersionEditParams* version_edit_params,
|
||||
std::string* db_id) {
|
||||
assert(read_buffer != nullptr);
|
||||
assert(log_read_status != nullptr);
|
||||
Status s;
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
size_t recovered_edits = 0;
|
||||
while (s.ok() && reader.ReadRecord(&record, &scratch) &&
|
||||
log_read_status->ok()) {
|
||||
VersionEdit edit;
|
||||
s = edit.DecodeFrom(record);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
if (edit.has_db_id_) {
|
||||
db_id_ = edit.GetDbId();
|
||||
if (db_id != nullptr) {
|
||||
db_id->assign(edit.GetDbId());
|
||||
}
|
||||
}
|
||||
s = read_buffer->AddEdit(&edit);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
if (edit.is_in_atomic_group_) {
|
||||
if (read_buffer->IsFull()) {
|
||||
// Apply edits in an atomic group when we have read all edits in the
|
||||
// group.
|
||||
for (auto& e : read_buffer->replay_buffer()) {
|
||||
s = ApplyOneVersionEditToBuilder(e, name_to_options,
|
||||
column_families_not_found, builders,
|
||||
version_edit_params);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
recovered_edits++;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
read_buffer->Clear();
|
||||
}
|
||||
} else {
|
||||
// Apply a normal edit immediately.
|
||||
s = ApplyOneVersionEditToBuilder(edit, name_to_options,
|
||||
column_families_not_found, builders,
|
||||
version_edit_params);
|
||||
if (s.ok()) {
|
||||
recovered_edits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!log_read_status->ok()) {
|
||||
s = *log_read_status;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
// Clear the buffer if we fail to decode/apply an edit.
|
||||
read_buffer->Clear();
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("VersionSet::ReadAndRecover:RecoveredEdits",
|
||||
&recovered_edits);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status VersionSet::Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families, bool read_only,
|
||||
std::string* db_id) {
|
||||
@@ -5879,8 +5747,7 @@ ReactiveVersionSet::ReactiveVersionSet(
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: VersionSet(dbname, _db_options, _file_options, table_cache,
|
||||
write_buffer_manager, write_controller,
|
||||
/*block_cache_tracer=*/nullptr, io_tracer),
|
||||
number_of_edits_to_skip_(0) {}
|
||||
/*block_cache_tracer=*/nullptr, io_tracer) {}
|
||||
|
||||
ReactiveVersionSet::~ReactiveVersionSet() {}
|
||||
|
||||
@@ -5893,394 +5760,44 @@ Status ReactiveVersionSet::Recover(
|
||||
assert(manifest_reporter != nullptr);
|
||||
assert(manifest_reader_status != nullptr);
|
||||
|
||||
std::unordered_map<std::string, ColumnFamilyOptions> cf_name_to_options;
|
||||
for (const auto& cf : column_families) {
|
||||
cf_name_to_options.insert({cf.name, cf.options});
|
||||
}
|
||||
|
||||
// add default column family
|
||||
auto default_cf_iter = cf_name_to_options.find(kDefaultColumnFamilyName);
|
||||
if (default_cf_iter == cf_name_to_options.end()) {
|
||||
return Status::InvalidArgument("Default column family not specified");
|
||||
}
|
||||
VersionEdit default_cf_edit;
|
||||
default_cf_edit.AddColumnFamily(kDefaultColumnFamilyName);
|
||||
default_cf_edit.SetColumnFamily(0);
|
||||
ColumnFamilyData* default_cfd =
|
||||
CreateColumnFamily(default_cf_iter->second, &default_cf_edit);
|
||||
// In recovery, nobody else can access it, so it's fine to set it to be
|
||||
// initialized earlier.
|
||||
default_cfd->set_initialized();
|
||||
VersionBuilderMap builders;
|
||||
std::unordered_map<int, std::string> column_families_not_found;
|
||||
builders.insert(
|
||||
std::make_pair(0, std::unique_ptr<BaseReferencedVersionBuilder>(
|
||||
new BaseReferencedVersionBuilder(default_cfd))));
|
||||
|
||||
manifest_reader_status->reset(new Status());
|
||||
manifest_reporter->reset(new LogReporter());
|
||||
static_cast_with_check<LogReporter>(manifest_reporter->get())->status =
|
||||
manifest_reader_status->get();
|
||||
Status s = MaybeSwitchManifest(manifest_reporter->get(), manifest_reader);
|
||||
log::Reader* reader = manifest_reader->get();
|
||||
assert(reader);
|
||||
|
||||
int retry = 0;
|
||||
VersionEdit version_edit;
|
||||
while (s.ok() && retry < 1) {
|
||||
assert(reader != nullptr);
|
||||
s = ReadAndRecover(*reader, &read_buffer_, cf_name_to_options,
|
||||
column_families_not_found, builders,
|
||||
manifest_reader_status->get(), &version_edit);
|
||||
if (s.ok()) {
|
||||
bool enough = version_edit.has_next_file_number_ &&
|
||||
version_edit.has_log_number_ &&
|
||||
version_edit.has_last_sequence_;
|
||||
if (enough) {
|
||||
for (const auto& cf : column_families) {
|
||||
auto cfd = column_family_set_->GetColumnFamily(cf.name);
|
||||
if (cfd == nullptr) {
|
||||
enough = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enough) {
|
||||
for (const auto& cf : column_families) {
|
||||
auto cfd = column_family_set_->GetColumnFamily(cf.name);
|
||||
assert(cfd != nullptr);
|
||||
if (!cfd->IsDropped()) {
|
||||
auto builder_iter = builders.find(cfd->GetID());
|
||||
assert(builder_iter != builders.end());
|
||||
auto builder = builder_iter->second->version_builder();
|
||||
assert(builder != nullptr);
|
||||
s = builder->LoadTableHandlers(
|
||||
cfd->internal_stats(), db_options_->max_file_opening_threads,
|
||||
false /* prefetch_index_and_filter_in_cache */,
|
||||
true /* is_initial_load */,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
MaxFileSizeForL0MetaPin(*cfd->GetLatestMutableCFOptions()));
|
||||
if (!s.ok()) {
|
||||
enough = false;
|
||||
if (s.IsPathNotFound()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enough) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++retry;
|
||||
}
|
||||
manifest_tailer_.reset(new ManifestTailer(
|
||||
column_families, const_cast<ReactiveVersionSet*>(this), io_tracer_));
|
||||
|
||||
if (s.ok()) {
|
||||
if (!version_edit.has_prev_log_number_) {
|
||||
version_edit.prev_log_number_ = 0;
|
||||
}
|
||||
column_family_set_->UpdateMaxColumnFamily(version_edit.max_column_family_);
|
||||
manifest_tailer_->Iterate(*reader, manifest_reader_status->get());
|
||||
|
||||
MarkMinLogNumberToKeep2PC(version_edit.min_log_number_to_keep_);
|
||||
MarkFileNumberUsed(version_edit.prev_log_number_);
|
||||
MarkFileNumberUsed(version_edit.log_number_);
|
||||
|
||||
for (auto cfd : *column_family_set_) {
|
||||
assert(builders.count(cfd->GetID()) > 0);
|
||||
auto builder = builders[cfd->GetID()]->version_builder();
|
||||
if (!builder->CheckConsistencyForNumLevels()) {
|
||||
s = Status::InvalidArgument(
|
||||
"db has more levels than options.num_levels");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
assert(cfd->initialized());
|
||||
auto builders_iter = builders.find(cfd->GetID());
|
||||
assert(builders_iter != builders.end());
|
||||
auto* builder = builders_iter->second->version_builder();
|
||||
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), io_tracer_,
|
||||
current_version_number_++);
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
|
||||
if (s.ok()) {
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(db_options_->skip_stats_update_on_db_open));
|
||||
AppendVersion(cfd, v);
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(db_options_->info_log,
|
||||
"[%s]: inconsistent version: %s\n",
|
||||
cfd->GetName().c_str(), s.ToString().c_str());
|
||||
delete v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
next_file_number_.store(version_edit.next_file_number_ + 1);
|
||||
last_allocated_sequence_ = version_edit.last_sequence_;
|
||||
last_published_sequence_ = version_edit.last_sequence_;
|
||||
last_sequence_ = version_edit.last_sequence_;
|
||||
prev_log_number_ = version_edit.prev_log_number_;
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_->info_log,
|
||||
"Column family [%s] (ID %u), log number is %" PRIu64 "\n",
|
||||
cfd->GetName().c_str(), cfd->GetID(), cfd->GetLogNumber());
|
||||
}
|
||||
}
|
||||
return s;
|
||||
return manifest_tailer_->status();
|
||||
}
|
||||
|
||||
Status ReactiveVersionSet::ReadAndApply(
|
||||
InstrumentedMutex* mu,
|
||||
std::unique_ptr<log::FragmentBufferedReader>* manifest_reader,
|
||||
Status* manifest_read_status,
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed) {
|
||||
assert(manifest_reader != nullptr);
|
||||
assert(cfds_changed != nullptr);
|
||||
mu->AssertHeld();
|
||||
|
||||
Status s;
|
||||
uint64_t applied_edits = 0;
|
||||
while (s.ok()) {
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
log::Reader* reader = manifest_reader->get();
|
||||
std::string old_manifest_path = reader->file()->file_name();
|
||||
while (reader->ReadRecord(&record, &scratch)) {
|
||||
VersionEdit edit;
|
||||
s = edit.DecodeFrom(record);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip the first VersionEdits of each MANIFEST generated by
|
||||
// VersionSet::WriteCurrentStatetoManifest.
|
||||
if (number_of_edits_to_skip_ > 0) {
|
||||
ColumnFamilyData* cfd =
|
||||
column_family_set_->GetColumnFamily(edit.column_family_);
|
||||
if (cfd != nullptr && !cfd->IsDropped()) {
|
||||
--number_of_edits_to_skip_;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
s = read_buffer_.AddEdit(&edit);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
VersionEdit temp_edit;
|
||||
if (edit.is_in_atomic_group_) {
|
||||
if (read_buffer_.IsFull()) {
|
||||
// Apply edits in an atomic group when we have read all edits in the
|
||||
// group.
|
||||
for (auto& e : read_buffer_.replay_buffer()) {
|
||||
s = ApplyOneVersionEditToBuilder(e, cfds_changed, &temp_edit);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
applied_edits++;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
read_buffer_.Clear();
|
||||
}
|
||||
} else {
|
||||
// Apply a normal edit immediately.
|
||||
s = ApplyOneVersionEditToBuilder(edit, cfds_changed, &temp_edit);
|
||||
if (s.ok()) {
|
||||
applied_edits++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
// Clear the buffer if we fail to decode/apply an edit.
|
||||
read_buffer_.Clear();
|
||||
}
|
||||
// It's possible that:
|
||||
// 1) s.IsCorruption(), indicating the current MANIFEST is corrupted.
|
||||
// Or the version(s) rebuilt from tailing the MANIFEST is inconsistent.
|
||||
// 2) we have finished reading the current MANIFEST.
|
||||
// 3) we have encountered an IOError reading the current MANIFEST.
|
||||
// We need to look for the next MANIFEST and start from there. If we cannot
|
||||
// find the next MANIFEST, we should exit the loop.
|
||||
Status tmp_s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
|
||||
reader = manifest_reader->get();
|
||||
if (tmp_s.ok()) {
|
||||
if (reader->file()->file_name() == old_manifest_path) {
|
||||
// Still processing the same MANIFEST, thus no need to continue this
|
||||
// loop since no record is available if we have reached here.
|
||||
break;
|
||||
} else {
|
||||
// We have switched to a new MANIFEST whose first records have been
|
||||
// generated by VersionSet::WriteCurrentStatetoManifest. Since the
|
||||
// secondary instance has already finished recovering upon start, there
|
||||
// is no need for the secondary to process these records. Actually, if
|
||||
// the secondary were to replay these records, the secondary may end up
|
||||
// adding the same SST files AGAIN to each column family, causing
|
||||
// consistency checks done by VersionBuilder to fail. Therefore, we
|
||||
// record the number of records to skip at the beginning of the new
|
||||
// MANIFEST and ignore them.
|
||||
number_of_edits_to_skip_ = 0;
|
||||
for (auto* cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
// Increase number_of_edits_to_skip by 2 because
|
||||
// WriteCurrentStatetoManifest() writes 2 version edits for each
|
||||
// column family at the beginning of the newly-generated MANIFEST.
|
||||
// TODO(yanqin) remove hard-coded value.
|
||||
if (db_options_->write_dbid_to_manifest) {
|
||||
number_of_edits_to_skip_ += 3;
|
||||
} else {
|
||||
number_of_edits_to_skip_ += 2;
|
||||
}
|
||||
}
|
||||
s = tmp_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *column_family_set_) {
|
||||
auto builder_iter = active_version_builders_.find(cfd->GetID());
|
||||
if (builder_iter == active_version_builders_.end()) {
|
||||
continue;
|
||||
}
|
||||
auto builder = builder_iter->second->version_builder();
|
||||
if (!builder->CheckConsistencyForNumLevels()) {
|
||||
s = Status::InvalidArgument(
|
||||
"db has more levels than options.num_levels");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("ReactiveVersionSet::ReadAndApply:AppliedEdits",
|
||||
&applied_edits);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status ReactiveVersionSet::ApplyOneVersionEditToBuilder(
|
||||
VersionEdit& edit, std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
VersionEdit* version_edit) {
|
||||
ColumnFamilyData* cfd =
|
||||
column_family_set_->GetColumnFamily(edit.column_family_);
|
||||
|
||||
// If we cannot find this column family in our column family set, then it
|
||||
// may be a new column family created by the primary after the secondary
|
||||
// starts. It is also possible that the secondary instance opens only a subset
|
||||
// of column families. Ignore it for now.
|
||||
if (nullptr == cfd) {
|
||||
return Status::OK();
|
||||
}
|
||||
if (active_version_builders_.find(edit.column_family_) ==
|
||||
active_version_builders_.end() &&
|
||||
!cfd->IsDropped()) {
|
||||
std::unique_ptr<BaseReferencedVersionBuilder> builder_guard(
|
||||
new BaseReferencedVersionBuilder(cfd));
|
||||
active_version_builders_.insert(
|
||||
std::make_pair(edit.column_family_, std::move(builder_guard)));
|
||||
}
|
||||
|
||||
auto builder_iter = active_version_builders_.find(edit.column_family_);
|
||||
assert(builder_iter != active_version_builders_.end());
|
||||
auto builder = builder_iter->second->version_builder();
|
||||
assert(builder != nullptr);
|
||||
|
||||
if (edit.is_column_family_add_) {
|
||||
// TODO (yanqin) for now the secondary ignores column families created
|
||||
// after Open. This also simplifies handling of switching to a new MANIFEST
|
||||
// and processing the snapshot of the system at the beginning of the
|
||||
// MANIFEST.
|
||||
} else if (edit.is_column_family_drop_) {
|
||||
// Drop the column family by setting it to be 'dropped' without destroying
|
||||
// the column family handle.
|
||||
// TODO (haoyu) figure out how to handle column faimly drop for
|
||||
// secondary instance. (Is it possible that the ref count for cfd is 0 but
|
||||
// the ref count for its versions is higher than 0?)
|
||||
cfd->SetDropped();
|
||||
if (cfd->UnrefAndTryDelete()) {
|
||||
cfd = nullptr;
|
||||
}
|
||||
active_version_builders_.erase(builder_iter);
|
||||
} else {
|
||||
Status s = builder->Apply(&edit);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
Status s = ExtractInfoFromVersionEdit(cfd, edit, version_edit);
|
||||
log::Reader* reader = manifest_reader->get();
|
||||
assert(reader);
|
||||
s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (cfd != nullptr && !cfd->IsDropped()) {
|
||||
s = builder->LoadTableHandlers(
|
||||
cfd->internal_stats(), db_options_->max_file_opening_threads,
|
||||
false /* prefetch_index_and_filter_in_cache */,
|
||||
false /* is_initial_load */,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
MaxFileSizeForL0MetaPin(*cfd->GetLatestMutableCFOptions()));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"ReactiveVersionSet::ApplyOneVersionEditToBuilder:"
|
||||
"AfterLoadTableHandlers",
|
||||
&s);
|
||||
|
||||
if (s.ok()) {
|
||||
auto version = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), io_tracer_,
|
||||
current_version_number_++);
|
||||
s = builder->SaveTo(version->storage_info());
|
||||
if (s.ok()) {
|
||||
version->PrepareApply(*cfd->GetLatestMutableCFOptions(), true);
|
||||
AppendVersion(cfd, version);
|
||||
active_version_builders_.erase(builder_iter);
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
}
|
||||
} else {
|
||||
delete version;
|
||||
}
|
||||
} else if (s.IsPathNotFound()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
// Some other error has occurred during LoadTableHandlers.
|
||||
}
|
||||
|
||||
manifest_tailer_->Iterate(*(manifest_reader->get()), manifest_read_status);
|
||||
s = manifest_tailer_->status();
|
||||
if (s.ok()) {
|
||||
if (version_edit->HasNextFile()) {
|
||||
next_file_number_.store(version_edit->next_file_number_ + 1);
|
||||
}
|
||||
if (version_edit->has_last_sequence_) {
|
||||
last_allocated_sequence_ = version_edit->last_sequence_;
|
||||
last_published_sequence_ = version_edit->last_sequence_;
|
||||
last_sequence_ = version_edit->last_sequence_;
|
||||
}
|
||||
if (version_edit->has_prev_log_number_) {
|
||||
prev_log_number_ = version_edit->prev_log_number_;
|
||||
MarkFileNumberUsed(version_edit->prev_log_number_);
|
||||
}
|
||||
if (version_edit->has_log_number_) {
|
||||
MarkFileNumberUsed(version_edit->log_number_);
|
||||
}
|
||||
column_family_set_->UpdateMaxColumnFamily(version_edit->max_column_family_);
|
||||
MarkMinLogNumberToKeep2PC(version_edit->min_log_number_to_keep_);
|
||||
*cfds_changed = std::move(manifest_tailer_->GetUpdatedColumnFamilies());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -6304,7 +5821,7 @@ Status ReactiveVersionSet::MaybeSwitchManifest(
|
||||
"ReactiveVersionSet::MaybeSwitchManifest:"
|
||||
"AfterGetCurrentManifestPath:1");
|
||||
s = fs_->NewSequentialFile(manifest_path,
|
||||
env_->OptimizeForManifestRead(file_options_),
|
||||
fs_->OptimizeForManifestRead(file_options_),
|
||||
&manifest_file, nullptr);
|
||||
} else {
|
||||
// No need to switch manifest.
|
||||
@@ -6321,15 +5838,24 @@ Status ReactiveVersionSet::MaybeSwitchManifest(
|
||||
true /* checksum */, 0 /* log_number */));
|
||||
ROCKS_LOG_INFO(db_options_->info_log, "Switched to new manifest: %s\n",
|
||||
manifest_path.c_str());
|
||||
// TODO (yanqin) every time we switch to a new MANIFEST, we clear the
|
||||
// active_version_builders_ map because we choose to construct the
|
||||
// versions from scratch, thanks to the first part of each MANIFEST
|
||||
// written by VersionSet::WriteCurrentStatetoManifest. This is not
|
||||
// necessary, but we choose this at present for the sake of simplicity.
|
||||
active_version_builders_.clear();
|
||||
if (manifest_tailer_) {
|
||||
manifest_tailer_->PrepareToReadNewManifest();
|
||||
}
|
||||
}
|
||||
} while (s.IsPathNotFound());
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
uint64_t ReactiveVersionSet::TEST_read_edits_in_atomic_group() const {
|
||||
assert(manifest_tailer_);
|
||||
return manifest_tailer_->GetReadBuffer().TEST_read_edits_in_atomic_group();
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
|
||||
std::vector<VersionEdit>& ReactiveVersionSet::replay_buffer() {
|
||||
assert(manifest_tailer_);
|
||||
return manifest_tailer_->GetReadBuffer().replay_buffer();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Loaded 100 of 323 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user