mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
110285073c | ||
|
|
59d972f58a |
No files matched your search
+2
-16
@@ -306,7 +306,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-clang-10
|
||||
- run: ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
|
||||
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check # aligned new doesn't work for reason we haven't figured out
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-mini-tsan:
|
||||
@@ -458,17 +458,6 @@ jobs:
|
||||
- run: DEBUG_LEVEL=0 make -j32 run_microbench
|
||||
- post-steps
|
||||
|
||||
build-linux-mini-crashtest:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-compression-libs
|
||||
- run: make V=1 -j8 CRASH_TEST_EXT_ARGS=--duration=960 blackbox_crash_test_with_atomic_flush
|
||||
- post-steps
|
||||
|
||||
build-windows:
|
||||
executor: windows-2xlarge
|
||||
parameters:
|
||||
@@ -530,7 +519,7 @@ jobs:
|
||||
${CMAKE_BIN} -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DJNI=1 << parameters.extra_cmake_opt >> ..
|
||||
cd ..
|
||||
echo "Building with VS version: ${CMAKE_GENERATOR}"
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount:64 -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Test RocksDB"
|
||||
shell: powershell.exe
|
||||
@@ -839,9 +828,6 @@ workflows:
|
||||
build-linux-unity-and-headers:
|
||||
jobs:
|
||||
- build-linux-unity-and-headers
|
||||
build-linux-mini-crashtest:
|
||||
jobs:
|
||||
- build-linux-mini-crashtest
|
||||
build-windows-vs2019:
|
||||
jobs:
|
||||
- build-windows:
|
||||
|
||||
+6
-8
@@ -321,8 +321,7 @@ if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
if (NOT PORTABLE OR FORCE_SSE42)
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
@@ -334,12 +333,11 @@ int main() {
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
|
||||
# Check if -latomic is required or not
|
||||
|
||||
+5
-16
@@ -1,34 +1,23 @@
|
||||
# Rocksdb Change Log
|
||||
## 7.1.0 (03/23/2022)
|
||||
## Unreleased
|
||||
### New Features
|
||||
* Allow WriteBatchWithIndex to index a WriteBatch that includes keys with user-defined timestamps. The index itself does not have timestamp.
|
||||
* Add support for user-defined timestamps to write-committed transaction without API change. The `TransactionDB` layer APIs do not allow timestamps because we require that all user-defined-timestamps-aware operations go through the `Transaction` APIs.
|
||||
* Added BlobDB options to `ldb`
|
||||
* `BlockBasedTableOptions::detect_filter_construct_corruption` can now be dynamically configured using `DB::SetOptions`.
|
||||
* Automatically recover from retryable read IO errors during backgorund flush/compaction.
|
||||
* Experimental support for preserving file Temperatures through backup and restore, and for updating DB metadata for outside changes to file Temperature (`UpdateManifestForFilesState` or `ldb update_manifest --update_temperatures`).
|
||||
* Experimental support for async_io in ReadOptions which is used by FilePrefetchBuffer to prefetch some of the data asynchronously, if reads are sequential and auto readahead is enabled by rocksdb internally.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a major performance bug in which Bloom filters generated by pre-7.0 releases are not read by early 7.0.x releases (and vice-versa) due to changes to FilterPolicy::Name() in #9590. This can severely impact read performance and read I/O on upgrade or downgrade with existing DB, but not data correctness.
|
||||
* Fixed a data race on `versions_` between `DBImpl::ResumeImpl()` and threads waiting for recovery to complete (#9496)
|
||||
* Fixed a bug caused by race among flush, incoming writes and taking snapshots. Queries to snapshots created with these race condition can return incorrect result, e.g. resurfacing deleted data.
|
||||
* Fixed a bug that DB flush uses `options.compression` even `options.compression_per_level` is set.
|
||||
* Fixed a bug that DisableManualCompaction may assert when disable an unscheduled manual compaction.
|
||||
* Fix a race condition when cancel manual compaction with `DisableManualCompaction`. Also DB close can cancel the manual compaction thread.
|
||||
* Fixed a potential timer crash when open close DB concurrently.
|
||||
* Fixed a race condition for `alive_log_files_` in non-two-write-queues mode. The race is between the write_thread_ in WriteToWAL() and another thread executing `FindObsoleteFiles()`. The race condition will be caught if `__glibcxx_requires_nonempty` is enabled.
|
||||
* Fixed a bug that `Iterator::Refresh()` reads stale keys after DeleteRange() performed.
|
||||
* Fixed a race condition when disable and re-enable manual compaction.
|
||||
* Fixed automatic error recovery failure in atomic flush.
|
||||
* Fixed a race condition when mmaping a WritableFile on POSIX.
|
||||
|
||||
### Public API changes
|
||||
* Added pure virtual FilterPolicy::CompatibilityName(), which is needed for fixing major performance bug involving FilterPolicy naming in SST metadata without affecting Customizable aspect of FilterPolicy. This change only affects those with their own custom or wrapper FilterPolicy classes.
|
||||
* Remove BlockBasedTableOptions.hash_index_allow_collision which already takes no effect.
|
||||
* `options.compression_per_level` is dynamically changeable with `SetOptions()`.
|
||||
* Added `WriteOptions::rate_limiter_priority`. When set to something other than `Env::IO_TOTAL`, the internal rate limiter (`DBOptions::rate_limiter`) will be charged at the specified priority for writes associated with the API to which the `WriteOptions` was provided. Currently the support covers automatic WAL flushes, which happen during live updates (`Put()`, `Write()`, `Delete()`, etc.) when `WriteOptions::disableWAL == false` and `DBOptions::manual_wal_flush == false`.
|
||||
* Add DB::OpenAndTrimHistory API. This API will open DB and trim data to the timestamp specified by trim_ts (The data with timestamp larger than specified trim bound will be removed). This API should only be used at a timestamp-enabled column families recovery. If the column family doesn't have timestamp enabled, this API won't trim any data on that column family. This API is not compatible with avoid_flush_during_recovery option.
|
||||
* Remove BlockBasedTableOptions.hash_index_allow_collision which already takes no effect.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a race condition when cancel manual compaction with `DisableManualCompaction`. Also DB close can cancel the manual compaction thread.
|
||||
|
||||
## 7.0.0 (02/20/2022)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
|
||||
BASH_EXISTS := $(shell which bash)
|
||||
SHELL := $(shell which bash)
|
||||
include python.mk
|
||||
# Default to python3. Some distros like CentOS 8 do not have `python`.
|
||||
ifeq ($(origin PYTHON), undefined)
|
||||
PYTHON := $(shell which python3 || which python || echo python3)
|
||||
endif
|
||||
export PYTHON
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
@@ -789,9 +793,7 @@ endif # PLATFORM_SHARED_EXT
|
||||
blackbox_crash_test_with_atomic_flush whitebox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_txn whitebox_crash_test_with_txn \
|
||||
blackbox_crash_test_with_best_efforts_recovery \
|
||||
blackbox_crash_test_with_ts whitebox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_multiops_wc_txn \
|
||||
blackbox_crash_test_with_multiops_wp_txn
|
||||
blackbox_crash_test_with_ts whitebox_crash_test_with_ts
|
||||
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
@@ -909,7 +911,7 @@ gen_parallel_tests:
|
||||
# 107.816 PASS t/DBTest.EncodeDecompressedBlockSizeTest
|
||||
#
|
||||
slow_test_regexp = \
|
||||
^.*MySQLStyleTransactionTest.*$$|^.*SnapshotConcurrentAccessTest.*$$|^.*SeqAdvanceConcurrentTest.*$$|^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 \
|
||||
@@ -1029,34 +1031,92 @@ check_some: $(ROCKSDBTESTS_SUBSET)
|
||||
ldb_tests: ldb
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
|
||||
include crash_test.mk
|
||||
crash_test:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test
|
||||
$(MAKE) blackbox_crash_test
|
||||
|
||||
crash_test_with_atomic_flush:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_atomic_flush
|
||||
$(MAKE) blackbox_crash_test_with_atomic_flush
|
||||
|
||||
crash_test_with_txn:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_txn
|
||||
$(MAKE) blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
crash_test_with_ts:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_ts
|
||||
$(MAKE) blackbox_crash_test_with_ts
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_atomic_flush: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_txn: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_best_efforts_recovery: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_ts: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --enable_ts blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_atomic_flush: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --cf_consistency whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_txn: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_ts: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --enable_ts whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
asan_check: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
whitebox_asan_crash_test: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) whitebox_crash_test
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) whitebox_crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
blackbox_asan_crash_test: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) blackbox_crash_test
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) blackbox_crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_atomic_flush: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_txn: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_best_efforts_recovery: clean
|
||||
ASAN_OPTIONS=detect_stack_use_after_return=1 COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_check: clean
|
||||
|
||||
@@ -106,11 +106,10 @@ SHM="TEST_TMPDIR=/dev/shm/rocksdb"
|
||||
NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
ASAN_TEST_OPTIONS="ASAN_OPTIONS=detect_stack_use_after_return=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
TSAN="COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
ASAN_CRASH="ASAN_OPTIONS=disable_coredump=0:detect_stack_use_after_return=1"
|
||||
ASAN_CRASH="ASAN_OPTIONS=disable_coredump=0"
|
||||
TSAN_CRASH="TSAN_OPTIONS=disable_coredump=0 CRASH_TEST_EXT_ARGS=\\\"--compression_type=zstd --log2_keys_per_lock=22\\\""
|
||||
NON_TSAN_CRASH="CRASH_TEST_EXT_ARGS=--compression_type=zstd"
|
||||
UBSAN_CRASH="UBSAN_OPTIONS=disable_coredump=0"
|
||||
@@ -598,7 +597,7 @@ ASAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
\"name\":\"Test RocksDB debug under ASAN\",
|
||||
\"shell\":\"cd $WORKING_DIR; set -o pipefail && $SHM $ASAN $ASAN_TEST_OPTIONS $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL\",
|
||||
\"shell\":\"cd $WORKING_DIR; set -o pipefail && $SHM $ASAN $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL\",
|
||||
\"user\":\"root\",
|
||||
$PARSER
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
# This file is used by Meta-internal infrastructure as well as by Makefile
|
||||
|
||||
# When included from Makefile, there are rules to build DB_STRESS_CMD. When
|
||||
# used directly with `make -f crashtest.mk ...` there will be no rules to
|
||||
# build DB_STRESS_CMD so it must exist prior.
|
||||
DB_STRESS_CMD?=./db_stress
|
||||
|
||||
include python.mk
|
||||
|
||||
CRASHTEST_MAKE=$(MAKE) -f crash_test.mk
|
||||
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD)
|
||||
|
||||
.PHONY: crash_test crash_test_with_atomic_flush crash_test_with_txn \
|
||||
crash_test_with_best_efforts_recovery crash_test_with_ts \
|
||||
blackbox_crash_test blackbox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_txn blackbox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_best_efforts_recovery \
|
||||
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
|
||||
whitebox_crash_test_with_txn whitebox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_multiops_wc_txn \
|
||||
blackbox_crash_test_with_multiops_wp_txn
|
||||
|
||||
crash_test: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test
|
||||
|
||||
crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_atomic_flush
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_atomic_flush
|
||||
|
||||
crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_ts
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_ts
|
||||
|
||||
blackbox_crash_test: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(CRASHTEST_PY) blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_best_efforts_recovery: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --enable_ts blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_multiops_txn --write_policy write_committed blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_multiops_txn --write_policy write_prepared blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
$(CRASHTEST_PY) whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --cf_consistency whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --enable_ts whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
+22
-47
@@ -58,55 +58,30 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
while (true) {
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
// Refresh range-tombstones in MemTable
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
SuperVersion* sv = cfd_->GetThreadLocalSuperVersion(db_impl_);
|
||||
ReadRangeDelAggregator* range_del_agg =
|
||||
db_iter_->GetRangeDelAggregator();
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter;
|
||||
range_del_iter.reset(
|
||||
sv->mem->NewRangeTombstoneIterator(read_options_, latest_seq));
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
cfd_->ReturnThreadLocalSuperVersion(sv);
|
||||
}
|
||||
// Refresh latest sequence number
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_valid(false);
|
||||
// Check again if the latest super version number is changed
|
||||
uint64_t latest_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (latest_sv_number != cur_sv_number) {
|
||||
// If the super version number is changed after refreshing,
|
||||
// fallback to Re-Init the InternalIterator
|
||||
cur_sv_number = latest_sv_number;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator(),
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(db_impl_->GetLatestSequenceNumber());
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -318,7 +318,6 @@ Status BuildTable(
|
||||
|
||||
// TODO Also check the IO status when create the Iterator.
|
||||
|
||||
TEST_SYNC_POINT("BuildTable:BeforeOutputValidation");
|
||||
if (s.ok() && !empty) {
|
||||
// Verify that the table is usable
|
||||
// We set for_compaction to false and don't OptimizeForCompactionTableRead
|
||||
|
||||
@@ -3752,9 +3752,6 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
return rep_->CompatibilityName();
|
||||
}
|
||||
// No need to override GetFilterBitsBuilder if this one is overridden
|
||||
ROCKSDB_NAMESPACE::FilterBitsBuilder* GetBuilderWithContext(
|
||||
const ROCKSDB_NAMESPACE::FilterBuildingContext& context)
|
||||
@@ -3792,9 +3789,6 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_format(
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
const char* CompatibilityName() const override {
|
||||
return rep_->CompatibilityName();
|
||||
}
|
||||
ROCKSDB_NAMESPACE::FilterBitsBuilder* GetBuilderWithContext(
|
||||
const ROCKSDB_NAMESPACE::FilterBuildingContext& context)
|
||||
const override {
|
||||
|
||||
+7
-35
@@ -7,13 +7,12 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE // Lite does not support C API
|
||||
|
||||
#include <assert.h>
|
||||
#include "rocksdb/c.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "rocksdb/c.h"
|
||||
#ifndef OS_WIN
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -90,8 +89,10 @@ static void CheckEqual(const char* expected, const char* v, size_t n) {
|
||||
// ok
|
||||
return;
|
||||
} else {
|
||||
fprintf(stderr, "%s: expected '%s', got '%s'\n", phase,
|
||||
(expected ? expected : "(null)"), (v ? v : "(null)"));
|
||||
fprintf(stderr, "%s: expected '%s', got '%s'\n",
|
||||
phase,
|
||||
(expected ? expected : "(null)"),
|
||||
(v ? v : "(null"));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -988,36 +989,7 @@ int main(int argc, char** argv) {
|
||||
CheckGet(db, roptions, "foo", NULL);
|
||||
rocksdb_release_snapshot(db, snap);
|
||||
}
|
||||
StartPhase("snapshot_with_memtable_inplace_update");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
const rocksdb_snapshot_t* snap = NULL;
|
||||
const char* s_key = "foo_snap";
|
||||
const char* value1 = "hello_s1";
|
||||
const char* value2 = "hello_s2";
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
|
||||
rocksdb_options_set_inplace_update_support(options, 1);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_put(db, woptions, s_key, 8, value1, 8, &err);
|
||||
snap = rocksdb_create_snapshot(db);
|
||||
assert(snap != NULL);
|
||||
rocksdb_put(db, woptions, s_key, 8, value2, 8, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_readoptions_set_snapshot(roptions, snap);
|
||||
CheckGet(db, roptions, "foo", NULL);
|
||||
// snapshot syntax is invalid, because of inplace update supported is set
|
||||
CheckGet(db, roptions, s_key, value2);
|
||||
// restore the data and options
|
||||
rocksdb_delete(db, woptions, s_key, 8, &err);
|
||||
CheckGet(db, roptions, s_key, NULL);
|
||||
rocksdb_release_snapshot(db, snap);
|
||||
rocksdb_readoptions_set_snapshot(roptions, NULL);
|
||||
rocksdb_options_set_inplace_update_support(options, 0);
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 1);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
}
|
||||
|
||||
StartPhase("repair");
|
||||
{
|
||||
// If we do not compact here, then the lazy deletion of
|
||||
|
||||
+2
-2
@@ -1164,12 +1164,12 @@ Compaction* ColumnFamilyData::CompactRange(
|
||||
int output_level, const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) {
|
||||
uint64_t max_file_num_to_ignore) {
|
||||
auto* result = compaction_picker_->CompactRange(
|
||||
GetName(), mutable_cf_options, mutable_db_options,
|
||||
current_->storage_info(), input_level, output_level,
|
||||
compact_range_options, begin, end, compaction_end, conflict,
|
||||
max_file_num_to_ignore, trim_ts);
|
||||
max_file_num_to_ignore);
|
||||
if (result != nullptr) {
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
|
||||
+1
-2
@@ -413,8 +413,7 @@ class ColumnFamilyData {
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore,
|
||||
const std::string& trim_ts);
|
||||
uint64_t max_file_num_to_ignore);
|
||||
|
||||
CompactionPicker* compaction_picker() { return compaction_picker_.get(); }
|
||||
// thread-safe
|
||||
|
||||
@@ -213,8 +213,8 @@ Compaction::Compaction(
|
||||
uint32_t _output_path_id, CompressionType _compression,
|
||||
CompressionOptions _compression_opts, Temperature _output_temperature,
|
||||
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
|
||||
bool _manual_compaction, const std::string& _trim_ts, double _score,
|
||||
bool _deletion_compaction, CompactionReason _compaction_reason)
|
||||
bool _manual_compaction, double _score, bool _deletion_compaction,
|
||||
CompactionReason _compaction_reason)
|
||||
: input_vstorage_(vstorage),
|
||||
start_level_(_inputs[0].level),
|
||||
output_level_(_output_level),
|
||||
@@ -237,7 +237,6 @@ Compaction::Compaction(
|
||||
bottommost_level_(IsBottommostLevel(output_level_, vstorage, inputs_)),
|
||||
is_full_compaction_(IsFullCompaction(vstorage, inputs_)),
|
||||
is_manual_compaction_(_manual_compaction),
|
||||
trim_ts_(_trim_ts),
|
||||
is_trivial_move_(false),
|
||||
compaction_reason_(_compaction_reason),
|
||||
notify_on_compaction_completion_(false) {
|
||||
|
||||
@@ -79,8 +79,8 @@ class Compaction {
|
||||
CompressionOptions compression_opts,
|
||||
Temperature output_temperature, uint32_t max_subcompactions,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
bool manual_compaction = false, const std::string& trim_ts = "",
|
||||
double score = -1, bool deletion_compaction = false,
|
||||
bool manual_compaction = false, double score = -1,
|
||||
bool deletion_compaction = false,
|
||||
CompactionReason compaction_reason = CompactionReason::kUnknown);
|
||||
|
||||
// No copying allowed
|
||||
@@ -208,8 +208,6 @@ class Compaction {
|
||||
// Was this compaction triggered manually by the client?
|
||||
bool is_manual_compaction() const { return is_manual_compaction_; }
|
||||
|
||||
std::string trim_ts() const { return trim_ts_; }
|
||||
|
||||
// Used when allow_trivial_move option is set in
|
||||
// Universal compaction. If all the input files are
|
||||
// non overlapping, then is_trivial_move_ variable
|
||||
@@ -387,9 +385,6 @@ class Compaction {
|
||||
// Is this compaction requested by the client?
|
||||
const bool is_manual_compaction_;
|
||||
|
||||
// The data with timestamp > trim_ts_ will be removed
|
||||
const std::string trim_ts_;
|
||||
|
||||
// True if we can do trivial move in Universal multi level
|
||||
// compaction
|
||||
bool is_trivial_move_;
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "db/dbformat.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/history_trimming_iterator.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable.h"
|
||||
@@ -430,8 +429,7 @@ CompactionJob::CompactionJob(
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string full_history_ts_low, std::string trim_ts,
|
||||
BlobFileCompletionCallback* blob_callback)
|
||||
std::string full_history_ts_low, BlobFileCompletionCallback* blob_callback)
|
||||
: compact_(new CompactionState(compaction)),
|
||||
compaction_stats_(compaction->compaction_reason(), 1),
|
||||
db_options_(db_options),
|
||||
@@ -470,7 +468,6 @@ CompactionJob::CompactionJob(
|
||||
measure_io_stats_(measure_io_stats),
|
||||
thread_pri_(thread_pri),
|
||||
full_history_ts_low_(std::move(full_history_ts_low)),
|
||||
trim_ts_(std::move(trim_ts)),
|
||||
blob_callback_(blob_callback) {
|
||||
assert(compaction_job_stats_ != nullptr);
|
||||
assert(log_buffer_ != nullptr);
|
||||
@@ -1315,7 +1312,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
uint64_t prev_cpu_micros = db_options_.clock->CPUMicros();
|
||||
uint64_t prev_cpu_micros = db_options_.clock->CPUNanos() / 1000;
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
|
||||
@@ -1383,28 +1380,21 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
std::unique_ptr<InternalIterator> clip;
|
||||
if (start || end) {
|
||||
clip = std::make_unique<ClippingIterator>(
|
||||
clip.reset(new ClippingIterator(
|
||||
raw_input.get(), start ? &start_slice : nullptr,
|
||||
end ? &end_slice : nullptr, &cfd->internal_comparator());
|
||||
end ? &end_slice : nullptr, &cfd->internal_comparator()));
|
||||
input = clip.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<InternalIterator> blob_counter;
|
||||
|
||||
if (sub_compact->compaction->DoesInputReferenceBlobFiles()) {
|
||||
sub_compact->blob_garbage_meter = std::make_unique<BlobGarbageMeter>();
|
||||
blob_counter = std::make_unique<BlobCountingIterator>(
|
||||
input, sub_compact->blob_garbage_meter.get());
|
||||
sub_compact->blob_garbage_meter.reset(new BlobGarbageMeter);
|
||||
blob_counter.reset(
|
||||
new BlobCountingIterator(input, sub_compact->blob_garbage_meter.get()));
|
||||
input = blob_counter.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<InternalIterator> trim_history_iter;
|
||||
if (cfd->user_comparator()->timestamp_size() > 0 && !trim_ts_.empty()) {
|
||||
trim_history_iter = std::make_unique<HistoryTrimmingIterator>(
|
||||
input, cfd->user_comparator(), trim_ts_);
|
||||
input = trim_history_iter.get();
|
||||
}
|
||||
|
||||
input->SeekToFirst();
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
@@ -1669,7 +1659,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
sub_compact->compaction_job_stats.cpu_micros =
|
||||
db_options_.clock->CPUMicros() - prev_cpu_micros;
|
||||
db_options_.clock->CPUNanos() / 1000 - prev_cpu_micros;
|
||||
|
||||
if (measure_io_stats_) {
|
||||
sub_compact->compaction_job_stats.file_write_nanos +=
|
||||
|
||||
@@ -82,7 +82,7 @@ class CompactionJob {
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "",
|
||||
std::string full_history_ts_low = "", std::string trim_ts = "",
|
||||
std::string full_history_ts_low = "",
|
||||
BlobFileCompletionCallback* blob_callback = nullptr);
|
||||
|
||||
virtual ~CompactionJob();
|
||||
@@ -226,7 +226,6 @@ class CompactionJob {
|
||||
std::vector<uint64_t> sizes_;
|
||||
Env::Priority thread_pri_;
|
||||
std::string full_history_ts_low_;
|
||||
std::string trim_ts_;
|
||||
BlobFileCompletionCallback* blob_callback_;
|
||||
|
||||
uint64_t GetCompactionId(SubcompactionState* sub_compact);
|
||||
|
||||
@@ -571,7 +571,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
int input_level, int output_level,
|
||||
const CompactRangeOptions& compact_range_options, const InternalKey* begin,
|
||||
const InternalKey* end, InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) {
|
||||
uint64_t max_file_num_to_ignore) {
|
||||
// CompactionPickerFIFO has its own implementation of compact range
|
||||
assert(ioptions_.compaction_style != kCompactionStyleFIFO);
|
||||
|
||||
@@ -640,7 +640,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* is manual */ true, trim_ts);
|
||||
/* grandparents */ {},
|
||||
/* is manual */ true);
|
||||
RegisterCompaction(c);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
return c;
|
||||
@@ -819,7 +820,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
std::move(grandparents),
|
||||
/* is manual compaction */ true, trim_ts);
|
||||
/* is manual compaction */ true);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::CompactRange:Return", compaction);
|
||||
RegisterCompaction(compaction);
|
||||
|
||||
@@ -78,7 +78,7 @@ class CompactionPicker {
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts);
|
||||
uint64_t max_file_num_to_ignore);
|
||||
|
||||
// The maximum allowed output level. Default value is NumberLevels() - 1.
|
||||
virtual int MaxOutputLevel() const { return NumberLevels() - 1; }
|
||||
@@ -270,8 +270,7 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
const InternalKey* /*end*/,
|
||||
InternalKey** /*compaction_end*/,
|
||||
bool* /*manual_conflict*/,
|
||||
uint64_t /*max_file_num_to_ignore*/,
|
||||
const std::string& /*trim_ts*/) override {
|
||||
uint64_t /*max_file_num_to_ignore*/) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
return c;
|
||||
}
|
||||
@@ -157,8 +157,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
0 /* max_subcompactions */, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
0 /* max_subcompactions */, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
return c;
|
||||
@@ -208,7 +208,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
@@ -313,7 +313,7 @@ Compaction* FIFOCompactionPicker::PickCompactionToWarm(
|
||||
0 /* max compaction bytes, not applicable */, 0 /* output path ID */,
|
||||
mutable_cf_options.compression, mutable_cf_options.compression_opts,
|
||||
Temperature::kWarm,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "",
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false, CompactionReason::kChangeTemperature);
|
||||
return c;
|
||||
@@ -349,7 +349,7 @@ Compaction* FIFOCompactionPicker::CompactRange(
|
||||
const CompactRangeOptions& /*compact_range_options*/,
|
||||
const InternalKey* /*begin*/, const InternalKey* /*end*/,
|
||||
InternalKey** compaction_end, bool* /*manual_conflict*/,
|
||||
uint64_t /*max_file_num_to_ignore*/, const std::string& /*trim_ts*/) {
|
||||
uint64_t /*max_file_num_to_ignore*/) {
|
||||
#ifdef NDEBUG
|
||||
(void)input_level;
|
||||
(void)output_level;
|
||||
|
||||
@@ -32,7 +32,7 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) override;
|
||||
uint64_t max_file_num_to_ignore) override;
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
virtual int MaxOutputLevel() const override { return 0; }
|
||||
|
||||
@@ -348,8 +348,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
|
||||
compaction_reason_);
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
|
||||
@@ -2648,13 +2648,12 @@ TEST_F(CompactionPickerTest, UniversalMarkedManualCompaction) {
|
||||
ASSERT_EQ(3U, vstorage_->FilesMarkedForCompaction().size());
|
||||
|
||||
bool manual_conflict = false;
|
||||
InternalKey* manual_end = nullptr;
|
||||
InternalKey* manual_end = NULL;
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.CompactRange(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
ColumnFamilyData::kCompactAllLevels, 6, CompactRangeOptions(),
|
||||
nullptr, nullptr, &manual_end, &manual_conflict, port::kMaxUint64,
|
||||
""));
|
||||
ColumnFamilyData::kCompactAllLevels, 6, CompactRangeOptions(), NULL,
|
||||
NULL, &manual_end, &manual_conflict, port::kMaxUint64));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
|
||||
|
||||
@@ -757,7 +757,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
start_level, enable_compression),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, grandparents,
|
||||
/* is manual */ false, /* trim_ts */ "", score_,
|
||||
/* is manual */ false, score_,
|
||||
false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
|
||||
@@ -1082,7 +1082,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
score_, false /* deletion_compaction */,
|
||||
CompactionReason::kUniversalSizeAmplification);
|
||||
}
|
||||
|
||||
@@ -1223,8 +1223,8 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false, score_,
|
||||
false /* deletion_compaction */,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
}
|
||||
|
||||
@@ -1299,8 +1299,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
compaction_reason);
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
|
||||
Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {
|
||||
|
||||
@@ -1638,15 +1638,9 @@ class LevelAndStyleCustomFilterPolicy : public FilterPolicy {
|
||||
policy_l0_other_(NewBloomFilterPolicy(bpk_l0_other)),
|
||||
policy_otherwise_(NewBloomFilterPolicy(bpk_otherwise)) {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "LevelAndStyleCustomFilterPolicy";
|
||||
}
|
||||
|
||||
// OK to use built-in policy name because we are deferring to a
|
||||
// built-in builder. We aren't changing the serialized format.
|
||||
const char* CompatibilityName() const override {
|
||||
return policy_fifo_->CompatibilityName();
|
||||
}
|
||||
const char* Name() const override { return policy_fifo_->Name(); }
|
||||
|
||||
FilterBitsBuilder* GetBuilderWithContext(
|
||||
const FilterBuildingContext& context) const override {
|
||||
|
||||
@@ -6896,7 +6896,6 @@ TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
// Generate 2 levels of file to make sure the manual compaction is not skipped
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Put(Key(i), "value"));
|
||||
if (i % 2) {
|
||||
@@ -6940,8 +6939,6 @@ TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
// Disable manual compaction should cancel both manual compactions and both
|
||||
// compaction should return incomplete.
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread1.join();
|
||||
@@ -6966,14 +6963,12 @@ TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
// make sure the manual compaction background is started but not yet set the
|
||||
// status to in_progress, then cancel the manual compaction, which should not
|
||||
// result in segfault
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkCompaction",
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"PreDisableManualCompaction"},
|
||||
{"DBImpl::RunManualCompaction:Unscheduled",
|
||||
{"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"ManualCompactionReturn",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
@@ -6982,6 +6977,9 @@ TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
cro.exclusive_manual_compaction = true;
|
||||
auto s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
"ManualCompactionReturn");
|
||||
});
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableJustStartedManualCompaction:"
|
||||
@@ -6991,43 +6989,6 @@ TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
compact_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableInProgressManualCompaction) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction:InProgress",
|
||||
"DBCompactionTest::DisableInProgressManualCompaction:"
|
||||
"PreDisableManualCompaction"},
|
||||
{"DBImpl::RunManualCompaction:Unscheduled",
|
||||
"CompactionJob::Run():Start"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// generate files, but avoid trigger auto compaction
|
||||
for (int i = 0; i < kNumL0Files / 2; i++) {
|
||||
ASSERT_OK(Put(Key(1), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
port::Thread compact_thread([&]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = true;
|
||||
auto s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::DisableInProgressManualCompaction:"
|
||||
"PreDisableManualCompaction");
|
||||
db_->DisableManualCompaction();
|
||||
|
||||
compact_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFull) {
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
|
||||
@@ -1374,140 +1374,6 @@ TEST_F(DBFlushTest, DISABLED_MemPurgeWALSupport) {
|
||||
} while (ChangeWalOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, MemPurgeCorrectLogNumberAndSSTFileCreation) {
|
||||
// Before our bug fix, we noticed that when 2 memtables were
|
||||
// being flushed (with one memtable being the output of a
|
||||
// previous MemPurge and one memtable being a newly-sealed memtable),
|
||||
// the SST file created was not properly added to the DB version
|
||||
// (via the VersionEdit obj), leading to data loss (the SST file
|
||||
// was later being purged as an obsolete file).
|
||||
// Therefore, we reproduce this scenario to test our fix.
|
||||
Options options = CurrentOptions();
|
||||
|
||||
options.create_if_missing = true;
|
||||
options.compression = kNoCompression;
|
||||
options.inplace_update_support = false;
|
||||
options.allow_concurrent_memtable_write = true;
|
||||
|
||||
// Enforce size of a single MemTable to 1MB (64MB = 1048576 bytes).
|
||||
options.write_buffer_size = 1 << 20;
|
||||
// Activate the MemPurge prototype.
|
||||
options.experimental_mempurge_threshold = 1.0;
|
||||
|
||||
// Force to have more than one memtable to trigger a flush.
|
||||
// For some reason this option does not seem to be enforced,
|
||||
// so the following test is designed to make sure that we
|
||||
// are testing the correct test case.
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.max_write_buffer_number = 5;
|
||||
options.max_write_buffer_size_to_maintain = 2 * (options.write_buffer_size);
|
||||
options.disable_auto_compactions = true;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
std::atomic<uint32_t> mempurge_count{0};
|
||||
std::atomic<uint32_t> sst_count{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushJob:MemPurgeSuccessful",
|
||||
[&](void* /*arg*/) { mempurge_count++; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushJob:SSTFileCreated", [&](void* /*arg*/) { sst_count++; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Dummy variable used for the following callback function.
|
||||
uint64_t ZERO = 0;
|
||||
// We will first execute mempurge operations exclusively.
|
||||
// Therefore, when the first flush is triggered, we want to make
|
||||
// sure there is at least 2 memtables being flushed: one output
|
||||
// from a previous mempurge, and one newly sealed memtable.
|
||||
// This is when we observed in the past that some SST files created
|
||||
// were not properly added to the DB version (via the VersionEdit obj).
|
||||
std::atomic<uint64_t> num_memtable_at_first_flush(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::WriteLevel0Table:num_memtables", [&](void* arg) {
|
||||
uint64_t* mems_size = reinterpret_cast<uint64_t*>(arg);
|
||||
// atomic_compare_exchange_strong sometimes updates the value
|
||||
// of ZERO (the "expected" object), so we make sure ZERO is indeed...
|
||||
// zero.
|
||||
ZERO = 0;
|
||||
std::atomic_compare_exchange_strong(&num_memtable_at_first_flush, &ZERO,
|
||||
*mems_size);
|
||||
});
|
||||
|
||||
const std::vector<std::string> KEYS = {
|
||||
"ThisIsKey1", "ThisIsKey2", "ThisIsKey3", "ThisIsKey4", "ThisIsKey5",
|
||||
"ThisIsKey6", "ThisIsKey7", "ThisIsKey8", "ThisIsKey9"};
|
||||
const std::string NOT_FOUND = "NOT_FOUND";
|
||||
|
||||
Random rnd(117);
|
||||
const uint64_t NUM_REPEAT_OVERWRITES = 100;
|
||||
const uint64_t NUM_RAND_INSERTS = 500;
|
||||
const uint64_t RAND_VALUES_LENGTH = 10240;
|
||||
|
||||
std::string key, value;
|
||||
std::vector<std::string> values(9, "");
|
||||
|
||||
// Keys used to check that no SST file disappeared.
|
||||
for (uint64_t k = 0; k < 5; k++) {
|
||||
values[k] = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
ASSERT_OK(Put(KEYS[k], values[k]));
|
||||
}
|
||||
|
||||
// Insertion of of K-V pairs, multiple times.
|
||||
// Trigger at least one mempurge and no SST file creation.
|
||||
for (size_t i = 0; i < NUM_REPEAT_OVERWRITES; i++) {
|
||||
// Create value strings of arbitrary length RAND_VALUES_LENGTH bytes.
|
||||
for (uint64_t k = 5; k < values.size(); k++) {
|
||||
values[k] = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
ASSERT_OK(Put(KEYS[k], values[k]));
|
||||
}
|
||||
// Check database consistency.
|
||||
for (uint64_t k = 0; k < values.size(); k++) {
|
||||
ASSERT_EQ(Get(KEYS[k]), values[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there was at least one mempurge
|
||||
uint32_t expected_min_mempurge_count = 1;
|
||||
// Check that there was no SST files created during flush.
|
||||
uint32_t expected_sst_count = 0;
|
||||
EXPECT_GE(mempurge_count.load(), expected_min_mempurge_count);
|
||||
EXPECT_EQ(sst_count.load(), expected_sst_count);
|
||||
|
||||
// Trigger an SST file creation and no mempurge.
|
||||
for (size_t i = 0; i < NUM_RAND_INSERTS; i++) {
|
||||
key = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
// Create value strings of arbitrary length RAND_VALUES_LENGTH bytes.
|
||||
value = rnd.RandomString(RAND_VALUES_LENGTH);
|
||||
ASSERT_OK(Put(key, value));
|
||||
// Check database consistency.
|
||||
for (uint64_t k = 0; k < values.size(); k++) {
|
||||
ASSERT_EQ(Get(KEYS[k]), values[k]);
|
||||
}
|
||||
ASSERT_EQ(Get(key), value);
|
||||
}
|
||||
|
||||
// Check that there was at least one SST files created during flush.
|
||||
expected_sst_count = 1;
|
||||
EXPECT_GE(sst_count.load(), expected_sst_count);
|
||||
|
||||
// Oddly enough, num_memtable_at_first_flush is not enforced to be
|
||||
// equal to min_write_buffer_number_to_merge. So by asserting that
|
||||
// the first SST file creation comes from one output memtable
|
||||
// from a previous mempurge, and one newly sealed memtable. This
|
||||
// is the scenario where we observed that some SST files created
|
||||
// were not properly added to the DB version before our bug fix.
|
||||
ASSERT_GE(num_memtable_at_first_flush.load(), 2);
|
||||
|
||||
// Check that no data was lost after SST file creation.
|
||||
for (uint64_t k = 0; k < values.size(); k++) {
|
||||
ASSERT_EQ(Get(KEYS[k]), values[k]);
|
||||
}
|
||||
// Extra check of database consistency.
|
||||
ASSERT_EQ(Get(key), value);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBFlushDirectIOTest, DirectIO) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
|
||||
@@ -171,9 +171,7 @@ Status CompactedDBImpl::Open(const Options& options,
|
||||
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
||||
Status s = db->Init(options);
|
||||
if (s.ok()) {
|
||||
s = db->StartPeriodicWorkScheduler();
|
||||
}
|
||||
if (s.ok()) {
|
||||
db->StartPeriodicWorkScheduler();
|
||||
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
|
||||
+4
-11
@@ -768,7 +768,7 @@ void DBImpl::PrintStatistics() {
|
||||
}
|
||||
}
|
||||
|
||||
Status DBImpl::StartPeriodicWorkScheduler() {
|
||||
void DBImpl::StartPeriodicWorkScheduler() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -778,7 +778,7 @@ Status DBImpl::StartPeriodicWorkScheduler() {
|
||||
"DBImpl::StartPeriodicWorkScheduler:DisableScheduler",
|
||||
&disable_scheduler);
|
||||
if (disable_scheduler) {
|
||||
return Status::OK();
|
||||
return;
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
|
||||
@@ -789,11 +789,10 @@ Status DBImpl::StartPeriodicWorkScheduler() {
|
||||
&periodic_work_scheduler_);
|
||||
}
|
||||
|
||||
return periodic_work_scheduler_->Register(
|
||||
periodic_work_scheduler_->Register(
|
||||
this, mutable_db_options_.stats_dump_period_sec,
|
||||
mutable_db_options_.stats_persist_period_sec);
|
||||
#endif // !ROCKSDB_LITE
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// esitmate the total size of stats_history_
|
||||
@@ -1227,7 +1226,7 @@ Status DBImpl::SetDBOptions(
|
||||
mutable_db_options_.stats_persist_period_sec) {
|
||||
mutex_.Unlock();
|
||||
periodic_work_scheduler_->Unregister(this);
|
||||
s = periodic_work_scheduler_->Register(
|
||||
periodic_work_scheduler_->Register(
|
||||
this, new_options.stats_dump_period_sec,
|
||||
new_options.stats_persist_period_sec);
|
||||
mutex_.Lock();
|
||||
@@ -3205,12 +3204,6 @@ bool CfdListContains(const CfdList& list, ColumnFamilyData* cfd) {
|
||||
} // namespace
|
||||
|
||||
void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
if (s == nullptr) {
|
||||
// DBImpl::GetSnapshot() can return nullptr when snapshot
|
||||
// not supported by specifying the condition:
|
||||
// inplace_update_support enabled.
|
||||
return;
|
||||
}
|
||||
const SnapshotImpl* casted_s = reinterpret_cast<const SnapshotImpl*>(s);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
+15
-33
@@ -663,8 +663,7 @@ class DBImpl : public DB {
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const Slice* begin, const Slice* end,
|
||||
bool exclusive, bool disallow_trivial_move,
|
||||
uint64_t max_file_num_to_ignore,
|
||||
const std::string& trim_ts);
|
||||
uint64_t max_file_num_to_ignore);
|
||||
|
||||
// Return an internal iterator over the current state of the database.
|
||||
// The keys of this iterator are internal keys (see format.h).
|
||||
@@ -1248,8 +1247,7 @@ class DBImpl : public DB {
|
||||
|
||||
Status CompactRangeInternal(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end,
|
||||
const std::string& trim_ts);
|
||||
const Slice* begin, const Slice* end);
|
||||
|
||||
// The following two functions can only be called when:
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
@@ -1516,31 +1514,19 @@ class DBImpl : public DB {
|
||||
|
||||
// Information for a manual compaction
|
||||
struct ManualCompactionState {
|
||||
ManualCompactionState(ColumnFamilyData* _cfd, int _input_level,
|
||||
int _output_level, uint32_t _output_path_id,
|
||||
bool _exclusive, bool _disallow_trivial_move,
|
||||
std::atomic<bool>* _canceled)
|
||||
: cfd(_cfd),
|
||||
input_level(_input_level),
|
||||
output_level(_output_level),
|
||||
output_path_id(_output_path_id),
|
||||
exclusive(_exclusive),
|
||||
disallow_trivial_move(_disallow_trivial_move),
|
||||
canceled(_canceled) {}
|
||||
|
||||
ColumnFamilyData* cfd;
|
||||
int input_level;
|
||||
int output_level;
|
||||
uint32_t output_path_id;
|
||||
Status status;
|
||||
bool done = false;
|
||||
bool in_progress = false; // compaction request being processed?
|
||||
bool incomplete = false; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin = nullptr; // nullptr means beginning of key range
|
||||
const InternalKey* end = nullptr; // nullptr means end of key range
|
||||
InternalKey* manual_end = nullptr; // how far we are compacting
|
||||
bool done;
|
||||
bool in_progress; // compaction request being processed?
|
||||
bool incomplete; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin; // nullptr means beginning of key range
|
||||
const InternalKey* end; // nullptr means end of key range
|
||||
InternalKey* manual_end; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
std::atomic<bool>* canceled; // Compaction canceled by the user?
|
||||
@@ -1788,8 +1774,7 @@ class DBImpl : public DB {
|
||||
// associated with this WriteToWAL
|
||||
IOStatus WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
bool with_db_mutex = false, bool with_log_mutex = false);
|
||||
Env::IOPriority rate_limiter_priority);
|
||||
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
@@ -1885,7 +1870,7 @@ class DBImpl : public DB {
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Schedule background tasks
|
||||
Status StartPeriodicWorkScheduler();
|
||||
void StartPeriodicWorkScheduler();
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
@@ -2115,15 +2100,12 @@ class DBImpl : public DB {
|
||||
bool persistent_stats_cfd_exists_ = true;
|
||||
|
||||
// Without two_write_queues, read and writes to alive_log_files_ are
|
||||
// protected by mutex_. With two_write_queues_, writes
|
||||
// protected by mutex_. However since back() is never popped, and push_back()
|
||||
// is done only from write_thread_, the same thread can access the item
|
||||
// reffered by back() without mutex_. With two_write_queues_, writes
|
||||
// are protected by locking both mutex_ and log_write_mutex_, and reads must
|
||||
// be under either mutex_ or log_write_mutex_.
|
||||
std::deque<LogFileNumberSize> alive_log_files_;
|
||||
// Caching the result of `alive_log_files_.back()` so that we do not have to
|
||||
// call `alive_log_files_.back()` in the write thread (WriteToWAL()) which
|
||||
// requires locking db mutex if log_mutex_ is not already held in
|
||||
// two-write-queues mode.
|
||||
std::deque<LogFileNumberSize>::reverse_iterator alive_log_files_tail_;
|
||||
// Log files that aren't fully synced, and the current log file.
|
||||
// Synchronization:
|
||||
// - push_back() is done from write_thread_ with locked mutex_ and
|
||||
|
||||
@@ -263,6 +263,11 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
if (!s.ok() && need_cancel) {
|
||||
flush_job.Cancel();
|
||||
}
|
||||
IOStatus io_s = IOStatus::OK();
|
||||
io_s = flush_job.io_status();
|
||||
if (s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, superversion_context,
|
||||
@@ -298,7 +303,9 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
|
||||
if (!s.ok() && !s.IsShutdownInProgress() && !s.IsColumnFamilyDropped()) {
|
||||
if (log_io_s.ok()) {
|
||||
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
|
||||
@@ -309,19 +316,24 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// error), all the Manifest write will be map to soft error.
|
||||
// TODO: kManifestWriteNoWAL and kFlushNoWAL are misleading. Refactor is
|
||||
// needed.
|
||||
error_handler_.SetBGError(s,
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
} else {
|
||||
// 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(s, BackgroundErrorReason::kFlushNoWAL);
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
assert(s == log_io_s);
|
||||
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
|
||||
// from never needing it or ignoring the flush job status
|
||||
io_s.PermitUncheckedError();
|
||||
}
|
||||
// If flush ran smoothly and no mempurge happened
|
||||
// install new SST file path.
|
||||
@@ -404,7 +416,6 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
for (const auto cfd : cfds) {
|
||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||
assert(cfd->imm()->IsFlushPending());
|
||||
assert(cfd->GetFlushReason() == cfds[0]->GetFlushReason());
|
||||
}
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
@@ -491,10 +502,12 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -514,6 +527,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
jobs[i]->Run(&logs_with_prep_tracker_, &file_meta[i],
|
||||
&(switched_to_mempurge.at(i)));
|
||||
exec_status[i].first = true;
|
||||
io_status[i] = jobs[i]->io_status();
|
||||
}
|
||||
if (num_cfs > 1) {
|
||||
TEST_SYNC_POINT(
|
||||
@@ -527,6 +541,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
&logs_with_prep_tracker_, file_meta.data() /* &file_meta[0] */,
|
||||
switched_to_mempurge.empty() ? nullptr : &(switched_to_mempurge.at(0)));
|
||||
exec_status[0].first = true;
|
||||
io_status[0] = jobs[0]->io_status();
|
||||
|
||||
Status error_status;
|
||||
for (const auto& e : exec_status) {
|
||||
@@ -545,6 +560,21 @@ 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++) {
|
||||
if (!io_status[i].ok() && !io_status[i].IsShutdownInProgress() &&
|
||||
!io_status[i].IsColumnFamilyDropped()) {
|
||||
io_error = io_status[i];
|
||||
}
|
||||
}
|
||||
io_s = io_error;
|
||||
if (s.ok() && !io_s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.IsColumnFamilyDropped()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
@@ -617,10 +647,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
return std::make_pair(Status::OK(), !ready);
|
||||
};
|
||||
|
||||
bool resuming_from_bg_err =
|
||||
error_handler_.IsDBStopped() ||
|
||||
(cfds[0]->GetFlushReason() == FlushReason::kErrorRecovery ||
|
||||
cfds[0]->GetFlushReason() == FlushReason::kErrorRecoveryRetryFlush);
|
||||
bool resuming_from_bg_err = error_handler_.IsDBStopped();
|
||||
while ((!resuming_from_bg_err || error_handler_.GetRecoveryError().ok())) {
|
||||
std::pair<Status, bool> res = wait_to_install_func();
|
||||
|
||||
@@ -635,10 +662,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
atomic_flush_install_cv_.Wait();
|
||||
|
||||
resuming_from_bg_err =
|
||||
error_handler_.IsDBStopped() ||
|
||||
(cfds[0]->GetFlushReason() == FlushReason::kErrorRecovery ||
|
||||
cfds[0]->GetFlushReason() == FlushReason::kErrorRecoveryRetryFlush);
|
||||
resuming_from_bg_err = error_handler_.IsDBStopped();
|
||||
}
|
||||
|
||||
if (!resuming_from_bg_err) {
|
||||
@@ -762,7 +786,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsColumnFamilyDropped()) {
|
||||
if (log_io_s.ok()) {
|
||||
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
|
||||
@@ -773,18 +798,19 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// error), all the Manifest write will be map to soft error.
|
||||
// TODO: kManifestWriteNoWAL and kFlushNoWAL are misleading. Refactor
|
||||
// is needed.
|
||||
error_handler_.SetBGError(s,
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
} else {
|
||||
// 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(s, BackgroundErrorReason::kFlushNoWAL);
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
assert(s == log_io_s);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,7 +927,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
if (ts_sz == 0) {
|
||||
return CompactRangeInternal(options, column_family, begin_without_ts,
|
||||
end_without_ts, "" /*trim_ts*/);
|
||||
end_without_ts);
|
||||
}
|
||||
|
||||
std::string begin_str;
|
||||
@@ -923,7 +949,7 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
Slice* end_with_ts = end_without_ts ? &end : nullptr;
|
||||
|
||||
return CompactRangeInternal(options, column_family, begin_with_ts,
|
||||
end_with_ts, "" /*trim_ts*/);
|
||||
end_with_ts);
|
||||
}
|
||||
|
||||
Status DBImpl::IncreaseFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
@@ -969,8 +995,7 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
|
||||
Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end,
|
||||
const std::string& trim_ts) {
|
||||
const Slice* begin, const Slice* end) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
@@ -1041,7 +1066,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
}
|
||||
s = RunManualCompaction(cfd, ColumnFamilyData::kCompactAllLevels,
|
||||
final_output_level, options, begin, end, exclusive,
|
||||
false, port::kMaxUint64, trim_ts);
|
||||
false, port::kMaxUint64);
|
||||
} else {
|
||||
int first_overlapped_level = kInvalidLevel;
|
||||
int max_overlapped_level = kInvalidLevel;
|
||||
@@ -1127,13 +1152,9 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
disallow_trivial_move = true;
|
||||
}
|
||||
}
|
||||
// trim_ts need real compaction to remove latest record
|
||||
if (!trim_ts.empty()) {
|
||||
disallow_trivial_move = true;
|
||||
}
|
||||
s = RunManualCompaction(cfd, level, output_level, options, begin, end,
|
||||
exclusive, disallow_trivial_move,
|
||||
max_file_num_to_ignore, trim_ts);
|
||||
max_file_num_to_ignore);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -1372,8 +1393,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
&manual_compaction_paused_, nullptr, db_id_, db_session_id_,
|
||||
c->column_family_data()->GetFullHistoryTsLow(), c->trim_ts(),
|
||||
&blob_callback_);
|
||||
c->column_family_data()->GetFullHistoryTsLow(), &blob_callback_);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
@@ -1762,7 +1782,7 @@ Status DBImpl::RunManualCompaction(
|
||||
ColumnFamilyData* cfd, int input_level, int output_level,
|
||||
const CompactRangeOptions& compact_range_options, const Slice* begin,
|
||||
const Slice* end, bool exclusive, bool disallow_trivial_move,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) {
|
||||
uint64_t max_file_num_to_ignore) {
|
||||
assert(input_level == ColumnFamilyData::kCompactAllLevels ||
|
||||
input_level >= 0);
|
||||
|
||||
@@ -1770,13 +1790,19 @@ Status DBImpl::RunManualCompaction(
|
||||
CompactionArg* ca = nullptr;
|
||||
|
||||
bool scheduled = false;
|
||||
bool unscheduled = false;
|
||||
Env::Priority thread_pool_priority = Env::Priority::TOTAL;
|
||||
bool manual_conflict = false;
|
||||
|
||||
ManualCompactionState manual(
|
||||
cfd, input_level, output_level, compact_range_options.target_path_id,
|
||||
exclusive, disallow_trivial_move, compact_range_options.canceled);
|
||||
ManualCompactionState manual;
|
||||
manual.cfd = cfd;
|
||||
manual.input_level = input_level;
|
||||
manual.output_level = output_level;
|
||||
manual.output_path_id = compact_range_options.target_path_id;
|
||||
manual.done = false;
|
||||
manual.in_progress = false;
|
||||
manual.incomplete = false;
|
||||
manual.exclusive = exclusive;
|
||||
manual.disallow_trivial_move = disallow_trivial_move;
|
||||
manual.canceled = compact_range_options.canceled;
|
||||
// For universal compaction, we enforce every manual compaction to compact
|
||||
// all files.
|
||||
if (begin == nullptr ||
|
||||
@@ -1874,29 +1900,22 @@ Status DBImpl::RunManualCompaction(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
manual.input_level, manual.output_level, compact_range_options,
|
||||
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
||||
max_file_num_to_ignore, trim_ts)) == nullptr &&
|
||||
max_file_num_to_ignore)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
// exclusive manual compactions should not see a conflict during
|
||||
// CompactRange
|
||||
assert(!exclusive || !manual_conflict);
|
||||
// Running either this or some other manual compaction
|
||||
bg_cv_.Wait();
|
||||
if (manual_compaction_paused_ > 0 && scheduled && !unscheduled) {
|
||||
if (manual_compaction_paused_ > 0 && !manual.done &&
|
||||
!manual.in_progress) {
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
assert(thread_pool_priority != Env::Priority::TOTAL);
|
||||
// unschedule all manual compactions
|
||||
auto unscheduled_task_num = env_->UnSchedule(
|
||||
GetTaskTag(TaskType::kManualCompaction), thread_pool_priority);
|
||||
if (unscheduled_task_num > 0) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"[%s] Unscheduled %d number of manual compactions from the "
|
||||
"thread-pool",
|
||||
cfd->GetName().c_str(), unscheduled_task_num);
|
||||
// it may unschedule other manual compactions, notify others.
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
unscheduled = true;
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:Unscheduled");
|
||||
env_->UnSchedule(GetTaskTag(TaskType::kManualCompaction),
|
||||
thread_pool_priority);
|
||||
break;
|
||||
}
|
||||
if (scheduled && manual.incomplete == true) {
|
||||
assert(!manual.in_progress);
|
||||
@@ -2680,12 +2699,6 @@ void DBImpl::UnscheduleCompactionCallback(void* arg) {
|
||||
CompactionArg ca = *(ca_ptr);
|
||||
delete reinterpret_cast<CompactionArg*>(arg);
|
||||
if (ca.prepicked_compaction != nullptr) {
|
||||
// if it's a manual compaction, set status to ManualCompactionPaused
|
||||
if (ca.prepicked_compaction->manual_compaction_state) {
|
||||
ca.prepicked_compaction->manual_compaction_state->done = true;
|
||||
ca.prepicked_compaction->manual_compaction_state->status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
if (ca.prepicked_compaction->compaction != nullptr) {
|
||||
ca.prepicked_compaction->compaction->ReleaseCompactionFiles(
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused));
|
||||
@@ -3070,8 +3083,6 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
manual_compaction->in_progress = true;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:InProgress");
|
||||
|
||||
std::unique_ptr<TaskLimiterToken> task_token;
|
||||
|
||||
// InternalKey manual_end_storage;
|
||||
@@ -3371,7 +3382,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr,
|
||||
is_manual ? manual_compaction->canceled : nullptr, db_id_,
|
||||
db_session_id_, c->column_family_data()->GetFullHistoryTsLow(),
|
||||
c->trim_ts(), &blob_callback_);
|
||||
&blob_callback_);
|
||||
compaction_job.Prepare();
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
|
||||
@@ -120,8 +120,7 @@ Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
: level + 1;
|
||||
return RunManualCompaction(cfd, level, output_level, CompactRangeOptions(),
|
||||
begin, end, true, disallow_trivial_move,
|
||||
port::kMaxUint64 /*max_file_num_to_ignore*/,
|
||||
"" /*trim_ts*/);
|
||||
port::kMaxUint64 /*max_file_num_to_ignore*/);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_SwitchMemtable(ColumnFamilyData* cfd) {
|
||||
|
||||
@@ -1364,7 +1364,6 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
total_log_size_ += log.size;
|
||||
alive_log_files_.push_back(log);
|
||||
}
|
||||
alive_log_files_tail_ = alive_log_files_.rbegin();
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -1547,72 +1546,6 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
!kSeqPerBatch, kBatchPerTxn);
|
||||
}
|
||||
|
||||
// TODO: Implement the trimming in flush code path.
|
||||
// TODO: Perform trimming before inserting into memtable during recovery.
|
||||
// TODO: Pick files with max_timestamp > trim_ts by each file's timestamp meta
|
||||
// info, and handle only these files to reduce io.
|
||||
Status DB::OpenAndTrimHistory(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
std::string trim_ts) {
|
||||
assert(dbptr != nullptr);
|
||||
assert(handles != nullptr);
|
||||
auto validate_options = [&db_options] {
|
||||
if (db_options.avoid_flush_during_recovery) {
|
||||
return Status::InvalidArgument(
|
||||
"avoid_flush_during_recovery incompatible with "
|
||||
"OpenAndTrimHistory");
|
||||
}
|
||||
return Status::OK();
|
||||
};
|
||||
auto s = validate_options();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
DB* db = nullptr;
|
||||
s = DB::Open(db_options, dbname, column_families, handles, &db);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
assert(db);
|
||||
CompactRangeOptions options;
|
||||
options.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
auto db_impl = static_cast_with_check<DBImpl>(db);
|
||||
for (auto handle : *handles) {
|
||||
assert(handle != nullptr);
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(handle);
|
||||
auto cfd = cfh->cfd();
|
||||
assert(cfd != nullptr);
|
||||
// Only compact column families with timestamp enabled
|
||||
if (cfd->user_comparator() != nullptr &&
|
||||
cfd->user_comparator()->timestamp_size() > 0) {
|
||||
s = db_impl->CompactRangeInternal(options, handle, nullptr, nullptr,
|
||||
trim_ts);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto clean_op = [&handles, &db] {
|
||||
for (auto handle : *handles) {
|
||||
auto temp_s = db->DestroyColumnFamilyHandle(handle);
|
||||
assert(temp_s.ok());
|
||||
}
|
||||
handles->clear();
|
||||
delete db;
|
||||
};
|
||||
if (!s.ok()) {
|
||||
clean_op();
|
||||
return s;
|
||||
}
|
||||
|
||||
*dbptr = db;
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
size_t preallocate_block_size,
|
||||
log::Writer** new_log) {
|
||||
@@ -1776,7 +1709,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
impl->alive_log_files_.push_back(
|
||||
DBImpl::LogFileNumberSize(impl->logfile_number_));
|
||||
impl->alive_log_files_tail_ = impl->alive_log_files_.rbegin();
|
||||
if (impl->two_write_queues_) {
|
||||
impl->log_write_mutex_.Unlock();
|
||||
}
|
||||
@@ -1798,7 +1730,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
uint64_t log_used, log_size;
|
||||
log::Writer* log_writer = impl->logs_.back().writer;
|
||||
s = impl->WriteToWAL(empty_batch, log_writer, &log_used, &log_size,
|
||||
Env::IO_TOTAL, /*with_db_mutex==*/true);
|
||||
Env::IO_TOTAL);
|
||||
if (s.ok()) {
|
||||
// Need to fsync, otherwise it might get lost after a power reset.
|
||||
s = impl->FlushWAL(false);
|
||||
@@ -1958,9 +1890,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
persist_options_status.ToString().c_str());
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = impl->StartPeriodicWorkScheduler();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
impl->StartPeriodicWorkScheduler();
|
||||
} else {
|
||||
for (auto* h : *handles) {
|
||||
delete h;
|
||||
}
|
||||
|
||||
@@ -1171,18 +1171,8 @@ WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
uint64_t* log_size,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
bool with_db_mutex, bool with_log_mutex) {
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
assert(log_size != nullptr);
|
||||
|
||||
// Assert mutex explicitly.
|
||||
if (with_db_mutex) {
|
||||
mutex_.AssertHeld();
|
||||
} else if (two_write_queues_) {
|
||||
log_write_mutex_.AssertHeld();
|
||||
assert(with_log_mutex);
|
||||
}
|
||||
|
||||
Slice log_entry = WriteBatchInternal::Contents(&merged_batch);
|
||||
*log_size = log_entry.size();
|
||||
// When two_write_queues_ WriteToWAL has to be protected from concurretn calls
|
||||
@@ -1205,12 +1195,9 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
*log_used = logfile_number_;
|
||||
}
|
||||
total_log_size_ += log_entry.size();
|
||||
if (with_db_mutex || with_log_mutex) {
|
||||
assert(alive_log_files_tail_ == alive_log_files_.rbegin());
|
||||
assert(alive_log_files_tail_ != alive_log_files_.rend());
|
||||
}
|
||||
LogFileNumberSize& last_alive_log = *alive_log_files_tail_;
|
||||
last_alive_log.AddSize(*log_size);
|
||||
// TODO(myabandeh): it might be unsafe to access alive_log_files_.back() here
|
||||
// since alive_log_files_ might be modified concurrently
|
||||
alive_log_files_.back().AddSize(log_entry.size());
|
||||
log_empty_ = false;
|
||||
return io_s;
|
||||
}
|
||||
@@ -1220,7 +1207,6 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence) {
|
||||
IOStatus io_s;
|
||||
assert(!two_write_queues_);
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
size_t write_with_wal = 0;
|
||||
@@ -1309,7 +1295,6 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
SequenceNumber* last_sequence, size_t seq_inc) {
|
||||
IOStatus io_s;
|
||||
|
||||
assert(two_write_queues_ || immutable_db_options_.unordered_write);
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
WriteBatch tmp_batch;
|
||||
@@ -1335,8 +1320,7 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
uint64_t log_size;
|
||||
io_s = WriteToWAL(*merged_batch, log_writer, log_used, &log_size,
|
||||
write_group.leader->rate_limiter_priority,
|
||||
/*with_db_mutex=*/false, /*with_log_mutex=*/true);
|
||||
write_group.leader->rate_limiter_priority);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
@@ -1990,7 +1974,6 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
log_dir_synced_ = false;
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
alive_log_files_tail_ = alive_log_files_.rbegin();
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
|
||||
@@ -169,36 +169,6 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdateCallbackNoAction) {
|
||||
ASSERT_EQ(Get(1, "key"), "NOT_FOUND");
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBTestInPlaceUpdate, InPlaceUpdateAndSnapshot) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.inplace_update_support = true;
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Update key with values of smaller size, and
|
||||
// run GetSnapshot and ReleaseSnapshot
|
||||
int numValues = 2;
|
||||
for (int i = numValues; i > 0; i--) {
|
||||
const Snapshot* s = db_->GetSnapshot();
|
||||
ASSERT_EQ(nullptr, s);
|
||||
std::string value = DummyString(i, 'a');
|
||||
ASSERT_OK(Put(1, "key", value));
|
||||
ASSERT_EQ(value, Get(1, "key"));
|
||||
// release s (nullptr)
|
||||
db_->ReleaseSnapshot(s);
|
||||
}
|
||||
|
||||
// Only 1 instance for that key.
|
||||
validateNumberOfEntries(1, 1);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+1
-29
@@ -500,7 +500,7 @@ TEST_F(DBRangeDelTest, ValidUniversalSubcompactionBoundaries) {
|
||||
1 /* input_level */, 2 /* output_level */, CompactRangeOptions(),
|
||||
nullptr /* begin */, nullptr /* end */, true /* exclusive */,
|
||||
true /* disallow_trivial_move */,
|
||||
port::kMaxUint64 /* max_file_num_to_ignore */, "" /*trim_ts*/));
|
||||
port::kMaxUint64 /* max_file_num_to_ignore */));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -1724,34 +1724,6 @@ TEST_F(DBRangeDelTest, OverlappedKeys) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, IteratorRefresh) {
|
||||
// Refreshing an iterator after a range tombstone is added should cause the
|
||||
// deleted range of keys to disappear.
|
||||
for (bool sv_changed : {false, true}) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key1", "value1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key2", "value2"));
|
||||
|
||||
auto* iter = db_->NewIterator(ReadOptions());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
"key2", "key3"));
|
||||
|
||||
if (sv_changed) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
|
||||
ASSERT_OK(iter->Refresh());
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ("key1", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+11
-113
@@ -17,7 +17,6 @@
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#include "rocksdb/persistent_cache.h"
|
||||
#include "rocksdb/trace_record.h"
|
||||
@@ -6950,7 +6949,7 @@ TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
temperatures.emplace(info.file_number, info.temperature);
|
||||
}
|
||||
|
||||
test_fs->PopRequestedSstFileTemperatures();
|
||||
test_fs->ClearRequestedFileTemperatures();
|
||||
Checkpoint* checkpoint;
|
||||
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
|
||||
ASSERT_OK(
|
||||
@@ -6958,121 +6957,20 @@ TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
|
||||
// checking src file src_temperature hints: 2 sst files: 1 sst is kWarm,
|
||||
// another is kUnknown
|
||||
std::vector<std::pair<uint64_t, Temperature>> requested_temps;
|
||||
test_fs->PopRequestedSstFileTemperatures(&requested_temps);
|
||||
// Two requests
|
||||
ASSERT_EQ(requested_temps.size(), 2);
|
||||
std::set<uint64_t> distinct_requests;
|
||||
for (const auto& requested_temp : requested_temps) {
|
||||
// Matching manifest temperatures
|
||||
ASSERT_EQ(temperatures.at(requested_temp.first), requested_temp.second);
|
||||
distinct_requests.insert(requested_temp.first);
|
||||
auto file_temperatures = test_fs->RequestedSstFileTemperatures();
|
||||
ASSERT_EQ(file_temperatures.size(), 2);
|
||||
bool has_only_one_warm_sst = false;
|
||||
for (const auto& file_temperature : file_temperatures) {
|
||||
ASSERT_EQ(temperatures.at(file_temperature.first), file_temperature.second);
|
||||
if (file_temperature.second == Temperature::kWarm) {
|
||||
ASSERT_FALSE(has_only_one_warm_sst);
|
||||
has_only_one_warm_sst = true;
|
||||
}
|
||||
}
|
||||
// Each request to distinct file
|
||||
ASSERT_EQ(distinct_requests.size(), requested_temps.size());
|
||||
|
||||
ASSERT_TRUE(has_only_one_warm_sst);
|
||||
delete checkpoint;
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, FileTemperatureManifestFixup) {
|
||||
auto test_fs = std::make_shared<FileTemperatureTestFS>(env_->GetFileSystem());
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, test_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.env = env.get();
|
||||
std::vector<std::string> cfs = {/*"default",*/ "test1", "test2"};
|
||||
CreateAndReopenWithCF(cfs, options);
|
||||
// Needed for later re-opens (weird)
|
||||
cfs.insert(cfs.begin(), kDefaultColumnFamilyName);
|
||||
|
||||
// Generate a bottommost file in all CFs
|
||||
for (int cf = 0; cf < 3; ++cf) {
|
||||
ASSERT_OK(Put(cf, "a", "val"));
|
||||
ASSERT_OK(Put(cf, "c", "val"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(Put(cf, "b", "val"));
|
||||
ASSERT_OK(Put(cf, "d", "val"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// verify
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kWarm), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
|
||||
// Generate a non-bottommost file in all CFs
|
||||
for (int cf = 0; cf < 3; ++cf) {
|
||||
ASSERT_OK(Put(cf, "e", "val"));
|
||||
ASSERT_OK(Flush(cf));
|
||||
}
|
||||
|
||||
// re-verify
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kWarm), 0);
|
||||
// Not supported: ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
|
||||
// Now change FS temperature on bottommost file(s) to kCold
|
||||
std::map<uint64_t, Temperature> current_temps;
|
||||
test_fs->CopyCurrentSstFileTemperatures(¤t_temps);
|
||||
for (auto e : current_temps) {
|
||||
if (e.second == Temperature::kWarm) {
|
||||
test_fs->OverrideSstFileTemperature(e.first, Temperature::kCold);
|
||||
}
|
||||
}
|
||||
// Metadata not yet updated
|
||||
ASSERT_EQ(Get("a"), "val");
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
// Update with Close and UpdateManifestForFilesState, but first save cf
|
||||
// descriptors
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (size_t i = 0; i < handles_.size(); ++i) {
|
||||
ColumnFamilyDescriptor cfdescriptor;
|
||||
// GetDescriptor is not implemented for ROCKSDB_LITE
|
||||
handles_[i]->GetDescriptor(&cfdescriptor).PermitUncheckedError();
|
||||
column_families.push_back(cfdescriptor);
|
||||
}
|
||||
Close();
|
||||
experimental::UpdateManifestForFilesStateOptions update_opts;
|
||||
update_opts.update_temperatures = true;
|
||||
|
||||
ASSERT_OK(experimental::UpdateManifestForFilesState(
|
||||
options, dbname_, column_families, update_opts));
|
||||
|
||||
// Re-open and re-verify after update
|
||||
ReopenWithColumnFamilies(cfs, options);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
// Not supported: ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kWarm), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
|
||||
// Change kUnknown to kHot
|
||||
test_fs->CopyCurrentSstFileTemperatures(¤t_temps);
|
||||
for (auto e : current_temps) {
|
||||
if (e.second == Temperature::kUnknown) {
|
||||
test_fs->OverrideSstFileTemperature(e.first, Temperature::kHot);
|
||||
}
|
||||
}
|
||||
|
||||
// Update with Close and UpdateManifestForFilesState
|
||||
Close();
|
||||
ASSERT_OK(experimental::UpdateManifestForFilesState(
|
||||
options, dbname_, column_families, update_opts));
|
||||
|
||||
// Re-open and re-verify after update
|
||||
ReopenWithColumnFamilies(cfs, options);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kWarm), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kHot), 0);
|
||||
|
||||
Close();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// WAL recovery mode is WALRecoveryMode::kPointInTimeRecovery.
|
||||
|
||||
+13
-88
@@ -14,7 +14,6 @@
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -24,15 +23,12 @@
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "file/filename.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
@@ -702,79 +698,29 @@ class FileTemperatureTestFS : public FileSystemWrapper {
|
||||
IOStatus NewSequentialFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSSequentialFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
IOStatus s = target()->NewSequentialFile(fname, opts, result, dbg);
|
||||
auto filename = GetFileName(fname);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(GetFileName(fname), &number, &type) &&
|
||||
type == kTableFile) {
|
||||
MutexLock lock(&mu_);
|
||||
requested_sst_file_temperatures_.emplace_back(number, opts.temperature);
|
||||
if (s.ok()) {
|
||||
*result = WrapWithTemperature<FSSequentialFileOwnerWrapper>(
|
||||
number, std::move(*result));
|
||||
}
|
||||
auto r = ParseFileName(filename, &number, &type);
|
||||
assert(r);
|
||||
if (type == kTableFile) {
|
||||
auto emplaced =
|
||||
requested_sst_file_temperatures_.emplace(number, opts.temperature);
|
||||
assert(emplaced.second); // assume no duplication
|
||||
}
|
||||
return s;
|
||||
return target()->NewSequentialFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
IOStatus s = target()->NewRandomAccessFile(fname, opts, result, dbg);
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(GetFileName(fname), &number, &type) &&
|
||||
type == kTableFile) {
|
||||
MutexLock lock(&mu_);
|
||||
requested_sst_file_temperatures_.emplace_back(number, opts.temperature);
|
||||
if (s.ok()) {
|
||||
*result = WrapWithTemperature<FSRandomAccessFileOwnerWrapper>(
|
||||
number, std::move(*result));
|
||||
}
|
||||
}
|
||||
return s;
|
||||
const std::map<uint64_t, Temperature>& RequestedSstFileTemperatures() {
|
||||
return requested_sst_file_temperatures_;
|
||||
}
|
||||
|
||||
void PopRequestedSstFileTemperatures(
|
||||
std::vector<std::pair<uint64_t, Temperature>>* out = nullptr) {
|
||||
MutexLock lock(&mu_);
|
||||
if (out) {
|
||||
*out = std::move(requested_sst_file_temperatures_);
|
||||
assert(requested_sst_file_temperatures_.empty());
|
||||
} else {
|
||||
requested_sst_file_temperatures_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus NewWritableFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(GetFileName(fname), &number, &type) &&
|
||||
type == kTableFile) {
|
||||
MutexLock lock(&mu_);
|
||||
current_sst_file_temperatures_[number] = opts.temperature;
|
||||
}
|
||||
return target()->NewWritableFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
void CopyCurrentSstFileTemperatures(std::map<uint64_t, Temperature>* out) {
|
||||
MutexLock lock(&mu_);
|
||||
*out = current_sst_file_temperatures_;
|
||||
}
|
||||
|
||||
void OverrideSstFileTemperature(uint64_t number, Temperature temp) {
|
||||
MutexLock lock(&mu_);
|
||||
current_sst_file_temperatures_[number] = temp;
|
||||
void ClearRequestedFileTemperatures() {
|
||||
requested_sst_file_temperatures_.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
port::Mutex mu_;
|
||||
std::vector<std::pair<uint64_t, Temperature>>
|
||||
requested_sst_file_temperatures_;
|
||||
std::map<uint64_t, Temperature> current_sst_file_temperatures_;
|
||||
std::map<uint64_t, Temperature> requested_sst_file_temperatures_;
|
||||
|
||||
std::string GetFileName(const std::string& fname) {
|
||||
auto filename = fname.substr(fname.find_last_of(kFilePathSeparator) + 1);
|
||||
@@ -783,27 +729,6 @@ class FileTemperatureTestFS : public FileSystemWrapper {
|
||||
filename = filename.substr(filename.find_last_of('/') + 1);
|
||||
return filename;
|
||||
}
|
||||
|
||||
template <class FileOwnerWrapperT, /*inferred*/ class FileT>
|
||||
std::unique_ptr<FileT> WrapWithTemperature(uint64_t number,
|
||||
std::unique_ptr<FileT>&& t) {
|
||||
class FileWithTemp : public FileOwnerWrapperT {
|
||||
public:
|
||||
FileWithTemp(FileTemperatureTestFS* fs, uint64_t number,
|
||||
std::unique_ptr<FileT>&& t)
|
||||
: FileOwnerWrapperT(std::move(t)), fs_(fs), number_(number) {}
|
||||
|
||||
Temperature GetTemperature() const override {
|
||||
MutexLock lock(&fs_->mu_);
|
||||
return fs_->current_sst_file_temperatures_[number_];
|
||||
}
|
||||
|
||||
private:
|
||||
FileTemperatureTestFS* fs_;
|
||||
uint64_t number_;
|
||||
};
|
||||
return std::make_unique<FileWithTemp>(this, number, std::move(t));
|
||||
}
|
||||
};
|
||||
|
||||
class OnFileDeletionListener : public EventListener {
|
||||
|
||||
@@ -704,77 +704,6 @@ TEST_F(DBBasicTestWithTimestamp, SimpleIterate) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, TrimHistoryTest) {
|
||||
Options options = CurrentOptions();
|
||||
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);
|
||||
auto check_value_by_ts = [](DB* db, Slice key, std::string readTs,
|
||||
Status status, std::string checkValue) {
|
||||
ReadOptions ropts;
|
||||
Slice ts = readTs;
|
||||
ropts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status s = db->Get(ropts, key, &value);
|
||||
ASSERT_TRUE(s == status);
|
||||
if (s.ok()) {
|
||||
ASSERT_EQ(checkValue, value);
|
||||
}
|
||||
};
|
||||
// Construct data of different versions with different ts
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "k1", Timestamp(2, 0), "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "k1", Timestamp(4, 0), "v2"));
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), "k1", Timestamp(5, 0)));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "k1", Timestamp(6, 0), "v3"));
|
||||
check_value_by_ts(db_, "k1", Timestamp(7, 0), Status::OK(), "v3");
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
DBOptions db_options(options);
|
||||
|
||||
// Trim data whose version > Timestamp(5, 0), read(k1, ts(7)) <- NOT_FOUND.
|
||||
ASSERT_OK(DB::OpenAndTrimHistory(db_options, dbname_, column_families,
|
||||
&handles_, &db_, Timestamp(5, 0)));
|
||||
check_value_by_ts(db_, "k1", Timestamp(7, 0), Status::NotFound(), "");
|
||||
Close();
|
||||
|
||||
// Trim data whose timestamp > Timestamp(4, 0), read(k1, ts(7)) <- v2
|
||||
ASSERT_OK(DB::OpenAndTrimHistory(db_options, dbname_, column_families,
|
||||
&handles_, &db_, Timestamp(4, 0)));
|
||||
check_value_by_ts(db_, "k1", Timestamp(7, 0), Status::OK(), "v2");
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, OpenAndTrimHistoryInvalidOptionTest) {
|
||||
Destroy(last_options_);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
DBOptions db_options(options);
|
||||
|
||||
// OpenAndTrimHistory should not work with avoid_flush_during_recovery
|
||||
db_options.avoid_flush_during_recovery = true;
|
||||
ASSERT_TRUE(DB::OpenAndTrimHistory(db_options, dbname_, column_families,
|
||||
&handles_, &db_, Timestamp(0, 0))
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTestWithTimestamp, GetTimestampTableProperties) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -207,13 +207,6 @@ inline Slice ExtractTimestampFromUserKey(const Slice& user_key, size_t ts_sz) {
|
||||
return Slice(user_key.data() + user_key.size() - ts_sz, ts_sz);
|
||||
}
|
||||
|
||||
inline Slice ExtractTimestampFromKey(const Slice& internal_key, size_t ts_sz) {
|
||||
const size_t key_size = internal_key.size();
|
||||
assert(key_size >= kNumInternalBytes + ts_sz);
|
||||
return Slice(internal_key.data() + key_size - ts_sz - kNumInternalBytes,
|
||||
ts_sz);
|
||||
}
|
||||
|
||||
inline uint64_t ExtractInternalKeyFooter(const Slice& internal_key) {
|
||||
assert(internal_key.size() >= kNumInternalBytes);
|
||||
const size_t n = internal_key.size();
|
||||
|
||||
+4
-11
@@ -272,8 +272,8 @@ STATIC_AVOID_DESTRUCTION(const Status, kOkStatus){Status::OK()};
|
||||
// This can also get called as part of a recovery operation. In that case, we
|
||||
// also track the error separately in recovery_error_ so we can tell in the
|
||||
// end whether recovery succeeded or not
|
||||
const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason) {
|
||||
const Status& ErrorHandler::SetBGError(const Status& bg_err,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
if (bg_err.ok()) {
|
||||
return kOkStatus;
|
||||
@@ -382,12 +382,9 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
// c) all other errors are mapped to hard error.
|
||||
// 3) for other cases, SetBGError(const Status& bg_err, BackgroundErrorReason
|
||||
// reason) will be called to handle other error cases.
|
||||
const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
Status tmp_status = bg_status;
|
||||
IOStatus bg_io_err = status_to_io_status(std::move(tmp_status));
|
||||
|
||||
if (bg_io_err.ok()) {
|
||||
return kOkStatus;
|
||||
}
|
||||
@@ -486,11 +483,7 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
}
|
||||
// HandleKnownErrors() will use recovery_error_, so ignore
|
||||
// recovery_io_error_.
|
||||
// TODO: Do some refactoring and use only one recovery_error_
|
||||
recovery_io_error_.PermitUncheckedError();
|
||||
return HandleKnownErrors(new_bg_io_err, reason);
|
||||
return SetBGError(new_bg_io_err, reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -53,6 +53,9 @@ class ErrorHandler {
|
||||
|
||||
const Status& SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
|
||||
const Status& SetBGError(const IOStatus& bg_io_err,
|
||||
BackgroundErrorReason reason);
|
||||
|
||||
Status GetBGError() const { return bg_error_; }
|
||||
|
||||
Status GetRecoveryError() const { return recovery_error_; }
|
||||
@@ -109,8 +112,6 @@ class ErrorHandler {
|
||||
// The pointer of DB statistics.
|
||||
std::shared_ptr<Statistics> bg_error_stats_;
|
||||
|
||||
const Status& HandleKnownErrors(const Status& bg_err,
|
||||
BackgroundErrorReason reason);
|
||||
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
|
||||
void RecoverFromNoSpace();
|
||||
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
|
||||
@@ -2468,210 +2468,6 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAbortRecovery) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FlushReadError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener =
|
||||
std::make_shared<ErrorHandlerFSListener>();
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeOutputValidation", [&](void*) {
|
||||
IOStatus st = IOStatus::IOError();
|
||||
st.SetRetryable(true);
|
||||
st.SetScope(IOStatus::IOErrorScope::kIOErrorScopeFile);
|
||||
fault_fs_->SetFilesystemActive(false, st);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeDeleteFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
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_LE(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_LE(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
s = dbfull()->TEST_GetBGError();
|
||||
ASSERT_OK(s);
|
||||
|
||||
Reopen(GetDefaultOptions());
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, AtomicFlushReadError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener =
|
||||
std::make_shared<ErrorHandlerFSListener>();
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
options.atomic_flush = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(0, Key(0), "val"));
|
||||
ASSERT_OK(Put(1, Key(0), "val"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeOutputValidation", [&](void*) {
|
||||
IOStatus st = IOStatus::IOError();
|
||||
st.SetRetryable(true);
|
||||
st.SetScope(IOStatus::IOErrorScope::kIOErrorScopeFile);
|
||||
fault_fs_->SetFilesystemActive(false, st);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeDeleteFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush({0, 1});
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
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_LE(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_LE(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
s = dbfull()->TEST_GetBGError();
|
||||
ASSERT_OK(s);
|
||||
|
||||
TryReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"},
|
||||
GetDefaultOptions());
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, AtomicFlushNoSpaceError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener =
|
||||
std::make_shared<ErrorHandlerFSListener>();
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(true);
|
||||
options.atomic_flush = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(0, Key(0), "val"));
|
||||
ASSERT_OK(Put(1, Key(0), "val"));
|
||||
SyncPoint::GetInstance()->SetCallBack("BuildTable:create_file", [&](void*) {
|
||||
IOStatus st = IOStatus::NoSpace();
|
||||
fault_fs_->SetFilesystemActive(false, st);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeDeleteFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush({0, 1});
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
ASSERT_EQ(listener->WaitForRecovery(5000000), true);
|
||||
ASSERT_LE(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_LE(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
s = dbfull()->TEST_GetBGError();
|
||||
ASSERT_OK(s);
|
||||
|
||||
TryReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"},
|
||||
GetDefaultOptions());
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, CompactionReadRetryableErrorAutoRecover) {
|
||||
// In this test, in the first round of compaction, the FS is set to error.
|
||||
// So the first compaction fails due to retryable IO error and it is mapped
|
||||
// to soft error. Then, compaction is rescheduled, in the second round of
|
||||
// compaction, the FS is set to active and compaction is successful, so
|
||||
// the test will hit the CompactionJob::FinishCompactionOutputFile1 sync
|
||||
// point.
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener =
|
||||
std::make_shared<ErrorHandlerFSListener>();
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.listeners.emplace_back(listener);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Status s;
|
||||
std::atomic<bool> fail_first(false);
|
||||
std::atomic<bool> fail_second(true);
|
||||
Random rnd(301);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(1024)));
|
||||
}
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
|
||||
listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError));
|
||||
listener->EnableAutoRecovery(false);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"CompactionJob::FinishCompactionOutputFile1",
|
||||
"CompactionWriteRetryableErrorAutoRecover0"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:Start",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(true); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BackgroundCallCompaction:0", [&](void*) { fail_first.store(true); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:2", [&](void*) {
|
||||
if (fail_first.load() && fail_second.load()) {
|
||||
fault_fs_->SetFilesystemActive(false, error_msg);
|
||||
fail_second.store(false);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val"));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(s);
|
||||
TEST_SYNC_POINT("CompactionWriteRetryableErrorAutoRecover0");
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Reopen(GetDefaultOptions());
|
||||
}
|
||||
|
||||
class DBErrorHandlingFencingTest : public DBErrorHandlingFSTest,
|
||||
public testing::WithParamInterface<bool> {};
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "rocksdb/experimental.h"
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/version_util.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace experimental {
|
||||
@@ -48,103 +46,5 @@ Status SuggestCompactRange(DB* db, const Slice* begin, const Slice* end) {
|
||||
return SuggestCompactRange(db, db->DefaultColumnFamily(), begin, end);
|
||||
}
|
||||
|
||||
Status UpdateManifestForFilesState(
|
||||
const DBOptions& db_opts, const std::string& db_name,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
const UpdateManifestForFilesStateOptions& opts) {
|
||||
OfflineManifestWriter w(db_opts, db_name);
|
||||
Status s = w.Recover(column_families);
|
||||
|
||||
size_t files_updated = 0;
|
||||
size_t cfs_updated = 0;
|
||||
auto fs = db_opts.env->GetFileSystem();
|
||||
|
||||
for (auto cfd : *w.Versions().GetColumnFamilySet()) {
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
assert(cfd);
|
||||
|
||||
if (cfd->IsDropped() || !cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* current = cfd->current();
|
||||
assert(current);
|
||||
|
||||
const auto* vstorage = current->storage_info();
|
||||
assert(vstorage);
|
||||
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
|
||||
/* SST files */
|
||||
for (int level = 0; level < cfd->NumberLevels(); level++) {
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
const auto& level_files = vstorage->LevelFiles(level);
|
||||
|
||||
for (const auto& lf : level_files) {
|
||||
assert(lf);
|
||||
|
||||
uint64_t number = lf->fd.GetNumber();
|
||||
std::string fname =
|
||||
TableFileName(w.IOptions().db_paths, number, lf->fd.GetPathId());
|
||||
|
||||
std::unique_ptr<FSSequentialFile> f;
|
||||
FileOptions fopts;
|
||||
fopts.temperature = lf->temperature;
|
||||
|
||||
IOStatus file_ios =
|
||||
fs->NewSequentialFile(fname, fopts, &f, /*dbg*/ nullptr);
|
||||
if (file_ios.ok()) {
|
||||
if (opts.update_temperatures) {
|
||||
Temperature temp = f->GetTemperature();
|
||||
if (temp != Temperature::kUnknown && temp != lf->temperature) {
|
||||
// Current state inconsistent with manifest
|
||||
++files_updated;
|
||||
edit.DeleteFile(level, number);
|
||||
edit.AddFile(level, number, lf->fd.GetPathId(),
|
||||
lf->fd.GetFileSize(), lf->smallest, lf->largest,
|
||||
lf->fd.smallest_seqno, lf->fd.largest_seqno,
|
||||
lf->marked_for_compaction, temp,
|
||||
lf->oldest_blob_file_number,
|
||||
lf->oldest_ancester_time, lf->file_creation_time,
|
||||
lf->file_checksum, lf->file_checksum_func_name,
|
||||
lf->min_timestamp, lf->max_timestamp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = file_ios;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && edit.NumEntries() > 0) {
|
||||
s = w.LogAndApply(cfd, &edit);
|
||||
if (s.ok()) {
|
||||
++cfs_updated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cfs_updated > 0) {
|
||||
ROCKS_LOG_INFO(db_opts.info_log,
|
||||
"UpdateManifestForFilesState: updated %zu files in %zu CFs",
|
||||
files_updated, cfs_updated);
|
||||
} else if (s.ok()) {
|
||||
ROCKS_LOG_INFO(db_opts.info_log,
|
||||
"UpdateManifestForFilesState: no updates needed");
|
||||
}
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_opts.info_log, "UpdateManifestForFilesState failed: %s",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+15
-29
@@ -136,6 +136,7 @@ FlushJob::FlushJob(
|
||||
}
|
||||
|
||||
FlushJob::~FlushJob() {
|
||||
io_status_.PermitUncheckedError();
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
}
|
||||
|
||||
@@ -169,20 +170,8 @@ void FlushJob::PickMemTable() {
|
||||
db_mutex_->AssertHeld();
|
||||
assert(!pick_memtable_called);
|
||||
pick_memtable_called = true;
|
||||
|
||||
// Maximum "NextLogNumber" of the memtables to flush.
|
||||
// When mempurge feature is turned off, this variable is useless
|
||||
// because the memtables are implicitly sorted by increasing order of creation
|
||||
// time. Therefore mems_->back()->GetNextLogNumber() is already equal to
|
||||
// max_next_log_number. However when Mempurge is on, the memtables are no
|
||||
// longer sorted by increasing order of creation time. Therefore this variable
|
||||
// becomes necessary because mems_->back()->GetNextLogNumber() is no longer
|
||||
// necessarily equal to max_next_log_number.
|
||||
uint64_t max_next_log_number = 0;
|
||||
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
cfd_->imm()->PickMemtablesToFlush(max_memtable_id_, &mems_,
|
||||
&max_next_log_number);
|
||||
cfd_->imm()->PickMemtablesToFlush(max_memtable_id_, &mems_);
|
||||
if (mems_.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -197,7 +186,7 @@ void FlushJob::PickMemTable() {
|
||||
edit_->SetPrevLogNumber(0);
|
||||
// SetLogNumber(log_num) indicates logs with number smaller than log_num
|
||||
// will no longer be picked up for recovery.
|
||||
edit_->SetLogNumber(max_next_log_number);
|
||||
edit_->SetLogNumber(mems_.back()->GetNextLogNumber());
|
||||
edit_->SetColumnFamily(cfd_->GetID());
|
||||
|
||||
// path 0 for level 0 file.
|
||||
@@ -289,13 +278,17 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker, FileMetaData* file_meta,
|
||||
} else if (write_manifest_) {
|
||||
TEST_SYNC_POINT("FlushJob::InstallResults");
|
||||
// Replace immutable memtable with the generated Table
|
||||
IOStatus tmp_io_s;
|
||||
s = cfd_->imm()->TryInstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems_, prep_tracker, versions_, db_mutex_,
|
||||
meta_.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_, &committed_flush_jobs_info_,
|
||||
log_buffer_, &committed_flush_jobs_info_, &tmp_io_s,
|
||||
!(mempurge_s.ok()) /* write_edit : true if no mempurge happened (or if aborted),
|
||||
but 'false' if mempurge successful: no new min log number
|
||||
or new level 0 file path to write to manifest. */);
|
||||
if (!tmp_io_s.ok()) {
|
||||
io_status_ = tmp_io_s;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && file_meta != nullptr) {
|
||||
@@ -361,7 +354,7 @@ Status FlushJob::MemPurge() {
|
||||
|
||||
// Measure purging time.
|
||||
const uint64_t start_micros = clock_->NowMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUNanos() / 1000;
|
||||
|
||||
MemTable* new_mem = nullptr;
|
||||
// For performance/log investigation purposes:
|
||||
@@ -576,7 +569,6 @@ Status FlushJob::MemPurge() {
|
||||
uint64_t new_mem_id = mems_[0]->GetID();
|
||||
|
||||
new_mem->SetID(new_mem_id);
|
||||
new_mem->SetNextLogNumber(mems_[0]->GetNextLogNumber());
|
||||
|
||||
// This addition will not trigger another flush, because
|
||||
// we do not call SchedulePendingFlush().
|
||||
@@ -614,7 +606,7 @@ Status FlushJob::MemPurge() {
|
||||
TEST_SYNC_POINT("DBImpl::FlushJob:MemPurgeUnsuccessful");
|
||||
}
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUMicros() - start_cpu_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Mempurge lasted %" PRIu64
|
||||
" microseconds, and %" PRIu64
|
||||
@@ -800,7 +792,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = clock_->NowMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUNanos() / 1000;
|
||||
Status s;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
@@ -823,12 +815,6 @@ Status FlushJob::WriteLevel0Table() {
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
uint64_t total_data_size = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
// Used for testing:
|
||||
uint64_t mems_size = mems_.size();
|
||||
(void)mems_size; // avoids unused variable error when
|
||||
// TEST_SYNC_POINT_CALLBACK not used.
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:num_memtables",
|
||||
&mems_size);
|
||||
for (MemTable* m : mems_) {
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
@@ -921,9 +907,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
job_context_->job_id, Env::IO_HIGH, &table_properties_, write_hint,
|
||||
full_history_ts_low, blob_callback_, &num_input_entries,
|
||||
&memtable_payload_bytes, &memtable_garbage_bytes);
|
||||
// TODO: Cleanup io_status in BuildTable and table builders
|
||||
assert(!s.ok() || io_s.ok());
|
||||
io_s.PermitUncheckedError();
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
}
|
||||
if (num_input_entries != total_num_entries && s.ok()) {
|
||||
std::string msg = "Expected " + ToString(total_num_entries) +
|
||||
" entries in memtables, but read " +
|
||||
@@ -993,7 +979,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUMicros() - start_cpu_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
stats.micros = micros;
|
||||
stats.cpu_micros = cpu_micros;
|
||||
|
||||
|
||||
@@ -93,6 +93,9 @@ class FlushJob {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Return the IO status
|
||||
IOStatus io_status() const { return io_status_; }
|
||||
|
||||
private:
|
||||
void ReportStartedFlush();
|
||||
void ReportFlushInputSize(const autovector<MemTable*>& mems);
|
||||
@@ -181,6 +184,7 @@ class FlushJob {
|
||||
Version* base_;
|
||||
bool pick_memtable_called;
|
||||
Env::Priority thread_pri_;
|
||||
IOStatus io_status_;
|
||||
|
||||
const std::shared_ptr<IOTracer> io_tracer_;
|
||||
SystemClock* clock_;
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
// 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).
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "table/internal_iterator.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class HistoryTrimmingIterator : public InternalIterator {
|
||||
public:
|
||||
explicit HistoryTrimmingIterator(InternalIterator* input,
|
||||
const Comparator* cmp, const std::string& ts)
|
||||
: input_(input), filter_ts_(ts), cmp_(cmp) {
|
||||
assert(cmp_->timestamp_size() > 0 && !ts.empty());
|
||||
}
|
||||
|
||||
bool filter() const {
|
||||
if (!input_->Valid()) {
|
||||
return true;
|
||||
}
|
||||
Slice current_ts = ExtractTimestampFromKey(key(), cmp_->timestamp_size());
|
||||
return cmp_->CompareTimestamp(current_ts, Slice(filter_ts_)) <= 0;
|
||||
}
|
||||
|
||||
bool Valid() const override { return input_->Valid(); }
|
||||
|
||||
void SeekToFirst() override {
|
||||
input_->SeekToFirst();
|
||||
while (!filter()) {
|
||||
input_->Next();
|
||||
}
|
||||
}
|
||||
|
||||
void SeekToLast() override {
|
||||
input_->SeekToLast();
|
||||
while (!filter()) {
|
||||
input_->Prev();
|
||||
}
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) override {
|
||||
input_->Seek(target);
|
||||
while (!filter()) {
|
||||
input_->Next();
|
||||
}
|
||||
}
|
||||
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
input_->SeekForPrev(target);
|
||||
while (!filter()) {
|
||||
input_->Prev();
|
||||
}
|
||||
}
|
||||
|
||||
void Next() override {
|
||||
do {
|
||||
input_->Next();
|
||||
} while (!filter());
|
||||
}
|
||||
|
||||
void Prev() override {
|
||||
do {
|
||||
input_->Prev();
|
||||
} while (!filter());
|
||||
}
|
||||
|
||||
Slice key() const override { return input_->key(); }
|
||||
|
||||
Slice value() const override { return input_->value(); }
|
||||
|
||||
Status status() const override { return input_->status(); }
|
||||
|
||||
bool IsKeyPinned() const override { return input_->IsKeyPinned(); }
|
||||
|
||||
bool IsValuePinned() const override { return input_->IsValuePinned(); }
|
||||
|
||||
private:
|
||||
InternalIterator* input_;
|
||||
const std::string filter_ts_;
|
||||
const Comparator* const cmp_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -519,7 +519,7 @@ const std::unordered_map<std::string, DBPropertyInfo>
|
||||
{false, nullptr, &InternalStats::HandleLiveSstFilesSize, nullptr,
|
||||
nullptr}},
|
||||
{DB::Properties::kLiveSstFilesSizeAtTemperature,
|
||||
{false, &InternalStats::HandleLiveSstFilesSizeAtTemperature, nullptr,
|
||||
{true, &InternalStats::HandleLiveSstFilesSizeAtTemperature, nullptr,
|
||||
nullptr, nullptr}},
|
||||
{DB::Properties::kEstimatePendingCompactionBytes,
|
||||
{false, nullptr, &InternalStats::HandleEstimatePendingCompactionBytes,
|
||||
|
||||
+1
-4
@@ -37,16 +37,13 @@ struct SuperVersionContext {
|
||||
explicit SuperVersionContext(bool create_superversion = false)
|
||||
: new_superversion(create_superversion ? new SuperVersion() : nullptr) {}
|
||||
|
||||
explicit SuperVersionContext(SuperVersionContext&& other) noexcept
|
||||
explicit SuperVersionContext(SuperVersionContext&& other)
|
||||
: superversions_to_free(std::move(other.superversions_to_free)),
|
||||
#ifndef ROCKSDB_DISABLE_STALL_NOTIFICATION
|
||||
write_stall_notifications(std::move(other.write_stall_notifications)),
|
||||
#endif
|
||||
new_superversion(std::move(other.new_superversion)) {
|
||||
}
|
||||
// No copies
|
||||
SuperVersionContext(const SuperVersionContext& other) = delete;
|
||||
void operator=(const SuperVersionContext& other) = delete;
|
||||
|
||||
void NewSuperVersion() {
|
||||
new_superversion = std::unique_ptr<SuperVersion>(new SuperVersion());
|
||||
|
||||
+9
-77
@@ -10,7 +10,6 @@
|
||||
#include "db/log_reader.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "file/sequence_file_reader.h"
|
||||
#include "port/lang.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -42,14 +41,10 @@ Reader::Reader(std::shared_ptr<Logger> info_log,
|
||||
recycled_(false),
|
||||
first_record_read_(false),
|
||||
compression_type_(kNoCompression),
|
||||
compression_type_record_read_(false),
|
||||
uncompress_(nullptr) {}
|
||||
compression_type_record_read_(false) {}
|
||||
|
||||
Reader::~Reader() {
|
||||
delete[] backing_store_;
|
||||
if (uncompress_) {
|
||||
delete uncompress_;
|
||||
}
|
||||
}
|
||||
|
||||
// For kAbsoluteConsistency, on clean shutdown we don't expect any error
|
||||
@@ -63,9 +58,6 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
WALRecoveryMode wal_recovery_mode) {
|
||||
scratch->clear();
|
||||
record->clear();
|
||||
if (uncompress_) {
|
||||
uncompress_->Reset();
|
||||
}
|
||||
bool in_fragmented_record = false;
|
||||
// Record offset of the logical record that we're reading
|
||||
// 0 is a dummy value to make compilers happy
|
||||
@@ -243,7 +235,8 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
ReportCorruption(fragment.size(),
|
||||
"could not decode SetCompressionType record");
|
||||
} else {
|
||||
InitCompression(compression_record);
|
||||
compression_type_ = compression_record.GetCompressionType();
|
||||
compression_type_record_read_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -457,54 +450,17 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size) {
|
||||
|
||||
buffer_.remove_prefix(header_size + length);
|
||||
|
||||
if (!uncompress_ || type == kSetCompressionType) {
|
||||
*result = Slice(header + header_size, length);
|
||||
return type;
|
||||
} else {
|
||||
// Uncompress compressed records
|
||||
uncompressed_record_.clear();
|
||||
size_t uncompressed_size = 0;
|
||||
int remaining = 0;
|
||||
do {
|
||||
remaining = uncompress_->Uncompress(header + header_size, length,
|
||||
uncompressed_buffer_.get(),
|
||||
&uncompressed_size);
|
||||
if (remaining < 0) {
|
||||
buffer_.clear();
|
||||
return kBadRecord;
|
||||
}
|
||||
if (uncompressed_size > 0) {
|
||||
uncompressed_record_.append(uncompressed_buffer_.get(),
|
||||
uncompressed_size);
|
||||
}
|
||||
} while (remaining > 0 || uncompressed_size == kBlockSize);
|
||||
*result = Slice(uncompressed_record_);
|
||||
return type;
|
||||
}
|
||||
*result = Slice(header + header_size, length);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize uncompress related fields
|
||||
void Reader::InitCompression(const CompressionTypeRecord& compression_record) {
|
||||
compression_type_ = compression_record.GetCompressionType();
|
||||
compression_type_record_read_ = true;
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
uncompress_ = StreamingUncompress::Create(
|
||||
compression_type_, compression_format_version, kBlockSize);
|
||||
assert(uncompress_ != nullptr);
|
||||
uncompressed_buffer_ = std::unique_ptr<char[]>(new char[kBlockSize]);
|
||||
assert(uncompressed_buffer_);
|
||||
}
|
||||
|
||||
bool FragmentBufferedReader::ReadRecord(Slice* record, std::string* scratch,
|
||||
WALRecoveryMode /*unused*/) {
|
||||
assert(record != nullptr);
|
||||
assert(scratch != nullptr);
|
||||
record->clear();
|
||||
scratch->clear();
|
||||
if (uncompress_) {
|
||||
uncompress_->Reset();
|
||||
}
|
||||
|
||||
uint64_t prospective_record_offset = 0;
|
||||
uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();
|
||||
@@ -606,7 +562,7 @@ bool FragmentBufferedReader::ReadRecord(Slice* record, std::string* scratch,
|
||||
ReportCorruption(fragment.size(),
|
||||
"could not decode SetCompressionType record");
|
||||
} else {
|
||||
InitCompression(compression_record);
|
||||
compression_type_ = compression_record.GetCompressionType();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -744,33 +700,9 @@ bool FragmentBufferedReader::TryReadFragment(
|
||||
|
||||
buffer_.remove_prefix(header_size + length);
|
||||
|
||||
if (!uncompress_ || type == kSetCompressionType) {
|
||||
*fragment = Slice(header + header_size, length);
|
||||
*fragment_type_or_err = type;
|
||||
return true;
|
||||
} else {
|
||||
// Uncompress compressed records
|
||||
uncompressed_record_.clear();
|
||||
size_t uncompressed_size = 0;
|
||||
int remaining = 0;
|
||||
do {
|
||||
remaining = uncompress_->Uncompress(header + header_size, length,
|
||||
uncompressed_buffer_.get(),
|
||||
&uncompressed_size);
|
||||
if (remaining < 0) {
|
||||
buffer_.clear();
|
||||
*fragment_type_or_err = kBadRecord;
|
||||
return true;
|
||||
}
|
||||
if (uncompressed_size > 0) {
|
||||
uncompressed_record_.append(uncompressed_buffer_.get(),
|
||||
uncompressed_size);
|
||||
}
|
||||
} while (remaining > 0 || uncompressed_size == kBlockSize);
|
||||
*fragment = Slice(std::move(uncompressed_record_));
|
||||
*fragment_type_or_err = type;
|
||||
return true;
|
||||
}
|
||||
*fragment = Slice(header + header_size, length);
|
||||
*fragment_type_or_err = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
|
||||
@@ -136,11 +136,6 @@ class Reader {
|
||||
CompressionType compression_type_;
|
||||
// Track whether the compression type record has been read or not.
|
||||
bool compression_type_record_read_;
|
||||
StreamingUncompress* uncompress_;
|
||||
// Reusable uncompressed output buffer
|
||||
std::unique_ptr<char[]> uncompressed_buffer_;
|
||||
// Reusable uncompressed record
|
||||
std::string uncompressed_record_;
|
||||
|
||||
// Extend record types with the following special values
|
||||
enum {
|
||||
@@ -172,8 +167,6 @@ class Reader {
|
||||
// buffer_ must be updated to remove the dropped bytes prior to invocation.
|
||||
void ReportCorruption(size_t bytes, const char* reason);
|
||||
void ReportDrop(size_t bytes, const Status& reason);
|
||||
|
||||
void InitCompression(const CompressionTypeRecord& compression_record);
|
||||
};
|
||||
|
||||
class FragmentBufferedReader : public Reader {
|
||||
|
||||
+11
-67
@@ -904,11 +904,6 @@ class CompressionLogTest : public LogTest {
|
||||
};
|
||||
|
||||
TEST_P(CompressionLogTest, Empty) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
const bool compression_enabled =
|
||||
std::get<2>(GetParam()) == kNoCompression ? false : true;
|
||||
@@ -918,57 +913,6 @@ TEST_P(CompressionLogTest, Empty) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST_P(CompressionLogTest, ReadWrite) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
Write("foo");
|
||||
Write("bar");
|
||||
Write("");
|
||||
Write("xxxx");
|
||||
ASSERT_EQ("foo", Read());
|
||||
ASSERT_EQ("bar", Read());
|
||||
ASSERT_EQ("", Read());
|
||||
ASSERT_EQ("xxxx", Read());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
ASSERT_EQ("EOF", Read()); // Make sure reads at eof work
|
||||
}
|
||||
|
||||
TEST_P(CompressionLogTest, ManyBlocks) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
Write(NumberString(i));
|
||||
}
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
ASSERT_EQ(NumberString(i), Read());
|
||||
}
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST_P(CompressionLogTest, Fragmentation) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
Write("small");
|
||||
Write(BigString("medium", 50000));
|
||||
Write(BigString("large", 100000));
|
||||
ASSERT_EQ("small", Read());
|
||||
ASSERT_EQ(BigString("medium", 50000), Read());
|
||||
ASSERT_EQ(BigString("large", 100000), Read());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Compression, CompressionLogTest,
|
||||
::testing::Combine(::testing::Values(0, 1), ::testing::Bool(),
|
||||
@@ -998,34 +942,34 @@ TEST_P(StreamingCompressionTest, Basic) {
|
||||
// Call compress till the entire input is consumed
|
||||
do {
|
||||
char* output_buffer = (char*)allocator->Allocate(kBlockSize);
|
||||
size_t output_pos;
|
||||
size_t output_size;
|
||||
remaining = compress->Compress(input_buffer.c_str(), input_size,
|
||||
output_buffer, &output_pos);
|
||||
if (output_pos > 0) {
|
||||
output_buffer, &output_size);
|
||||
if (output_size > 0) {
|
||||
std::string compressed_buffer;
|
||||
compressed_buffer.assign(output_buffer, output_pos);
|
||||
compressed_buffer.assign(output_buffer, output_size);
|
||||
compressed_buffers.emplace_back(std::move(compressed_buffer));
|
||||
}
|
||||
allocator->Deallocate((void*)output_buffer);
|
||||
} while (remaining > 0);
|
||||
std::string uncompressed_buffer = "";
|
||||
int ret_val = 0;
|
||||
size_t output_pos;
|
||||
size_t output_size;
|
||||
char* uncompressed_output_buffer = (char*)allocator->Allocate(kBlockSize);
|
||||
// Uncompress the fragments and concatenate them.
|
||||
for (int i = 0; i < (int)compressed_buffers.size(); i++) {
|
||||
// Call uncompress till either the entire input is consumed or the output
|
||||
// buffer size is equal to the allocated output buffer size.
|
||||
do {
|
||||
ret_val = uncompress->Uncompress(compressed_buffers[i].c_str(),
|
||||
compressed_buffers[i].size(),
|
||||
uncompressed_output_buffer, &output_pos);
|
||||
if (output_pos > 0) {
|
||||
ret_val = uncompress->Uncompress(
|
||||
compressed_buffers[i].c_str(), compressed_buffers[i].size(),
|
||||
uncompressed_output_buffer, &output_size);
|
||||
if (output_size > 0) {
|
||||
std::string uncompressed_fragment;
|
||||
uncompressed_fragment.assign(uncompressed_output_buffer, output_pos);
|
||||
uncompressed_fragment.assign(uncompressed_output_buffer, output_size);
|
||||
uncompressed_buffer += uncompressed_fragment;
|
||||
}
|
||||
} while (ret_val > 0 || output_pos == kBlockSize);
|
||||
} while (ret_val > 0 || output_size == kBlockSize);
|
||||
}
|
||||
allocator->Deallocate((void*)uncompressed_output_buffer);
|
||||
delete allocator;
|
||||
|
||||
+3
-50
@@ -10,7 +10,6 @@
|
||||
#include "db/log_writer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/coding.h"
|
||||
@@ -27,8 +26,7 @@ Writer::Writer(std::unique_ptr<WritableFileWriter>&& dest, uint64_t log_number,
|
||||
log_number_(log_number),
|
||||
recycle_log_files_(recycle_log_files),
|
||||
manual_flush_(manual_flush),
|
||||
compression_type_(compression_type),
|
||||
compress_(nullptr) {
|
||||
compression_type_(compression_type) {
|
||||
for (int i = 0; i <= kMaxRecordType; i++) {
|
||||
char t = static_cast<char>(i);
|
||||
type_crc_[i] = crc32c::Value(&t, 1);
|
||||
@@ -39,9 +37,6 @@ Writer::~Writer() {
|
||||
if (dest_) {
|
||||
WriteBuffer().PermitUncheckedError();
|
||||
}
|
||||
if (compress_) {
|
||||
delete compress_;
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus Writer::WriteBuffer() { return dest_->Flush(); }
|
||||
@@ -69,12 +64,6 @@ IOStatus Writer::AddRecord(const Slice& slice,
|
||||
// zero-length record
|
||||
IOStatus s;
|
||||
bool begin = true;
|
||||
int compress_remaining = 0;
|
||||
bool compress_start = false;
|
||||
if (compress_) {
|
||||
compress_->Reset();
|
||||
compress_start = true;
|
||||
}
|
||||
do {
|
||||
const int64_t leftover = kBlockSize - block_offset_;
|
||||
assert(leftover >= 0);
|
||||
@@ -98,34 +87,10 @@ IOStatus Writer::AddRecord(const Slice& slice,
|
||||
assert(static_cast<int64_t>(kBlockSize - block_offset_) >= header_size);
|
||||
|
||||
const size_t avail = kBlockSize - block_offset_ - header_size;
|
||||
|
||||
// Compress the record if compression is enabled.
|
||||
// Compress() is called at least once (compress_start=true) and after the
|
||||
// previous generated compressed chunk is written out as one or more
|
||||
// physical records (left=0).
|
||||
if (compress_ && (compress_start || left == 0)) {
|
||||
compress_remaining = compress_->Compress(slice.data(), slice.size(),
|
||||
compressed_buffer_.get(), &left);
|
||||
|
||||
if (compress_remaining < 0) {
|
||||
// Set failure status
|
||||
s = IOStatus::IOError("Unexpected WAL compression error");
|
||||
s.SetDataLoss(true);
|
||||
break;
|
||||
} else if (left == 0) {
|
||||
// Nothing left to compress
|
||||
if (!compress_start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
compress_start = false;
|
||||
ptr = compressed_buffer_.get();
|
||||
}
|
||||
|
||||
const size_t fragment_length = (left < avail) ? left : avail;
|
||||
|
||||
RecordType type;
|
||||
const bool end = (left == fragment_length && compress_remaining == 0);
|
||||
const bool end = (left == fragment_length);
|
||||
if (begin && end) {
|
||||
type = recycle_log_files_ ? kRecyclableFullType : kFullType;
|
||||
} else if (begin) {
|
||||
@@ -140,7 +105,7 @@ IOStatus Writer::AddRecord(const Slice& slice,
|
||||
ptr += fragment_length;
|
||||
left -= fragment_length;
|
||||
begin = false;
|
||||
} while (s.ok() && (left > 0 || compress_remaining > 0));
|
||||
} while (s.ok() && left > 0);
|
||||
|
||||
if (s.ok()) {
|
||||
if (!manual_flush_) {
|
||||
@@ -169,18 +134,6 @@ IOStatus Writer::AddCompressionTypeRecord() {
|
||||
if (!manual_flush_) {
|
||||
s = dest_->Flush();
|
||||
}
|
||||
// Initialize fields required for compression
|
||||
const size_t max_output_buffer_len =
|
||||
kBlockSize - (recycle_log_files_ ? kRecyclableHeaderSize : kHeaderSize);
|
||||
CompressionOptions opts;
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
compress_ = StreamingCompress::Create(compression_type_, opts,
|
||||
compression_format_version,
|
||||
max_output_buffer_len);
|
||||
assert(compress_ != nullptr);
|
||||
compressed_buffer_ =
|
||||
std::unique_ptr<char[]>(new char[max_output_buffer_len]);
|
||||
assert(compressed_buffer_);
|
||||
} else {
|
||||
// Disable compression if the record could not be added.
|
||||
compression_type_ = kNoCompression;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/compression.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -119,9 +118,6 @@ class Writer {
|
||||
|
||||
// Compression Type
|
||||
CompressionType compression_type_;
|
||||
StreamingCompress* compress_;
|
||||
// Reusable compressed output buffer
|
||||
std::unique_ptr<char[]> compressed_buffer_;
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
|
||||
+15
-8
@@ -338,8 +338,7 @@ bool MemTableList::IsFlushPending() const {
|
||||
|
||||
// Returns the memtables that need to be flushed.
|
||||
void MemTableList::PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
autovector<MemTable*>* ret,
|
||||
uint64_t* max_next_log_number) {
|
||||
autovector<MemTable*>* ret) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_PICK_MEMTABLES_TO_FLUSH);
|
||||
const auto& memlist = current_->memlist_;
|
||||
@@ -350,7 +349,8 @@ void MemTableList::PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
// iterating through the memlist starting at the end, the vector<MemTable*>
|
||||
// ret is filled with memtables already sorted in increasing MemTable ID.
|
||||
// However, when the mempurge feature is activated, new memtables with older
|
||||
// IDs will be added to the memlist.
|
||||
// IDs will be added to the memlist. Therefore we std::sort(ret) at the end to
|
||||
// return a vector of memtables sorted by increasing memtable ID.
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
if (!atomic_flush && m->atomic_flush_seqno_ != kMaxSequenceNumber) {
|
||||
@@ -366,16 +366,21 @@ void MemTableList::PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
imm_flush_needed.store(false, std::memory_order_release);
|
||||
}
|
||||
m->flush_in_progress_ = true; // flushing will start very soon
|
||||
if (max_next_log_number) {
|
||||
*max_next_log_number =
|
||||
std::max(m->GetNextLogNumber(), *max_next_log_number);
|
||||
}
|
||||
ret->push_back(m);
|
||||
}
|
||||
}
|
||||
if (!atomic_flush || num_flush_not_started_ == 0) {
|
||||
flush_requested_ = false; // start-flush request is complete
|
||||
}
|
||||
|
||||
// Sort the list of memtables by increasing memtable ID.
|
||||
// This is useful when the mempurge feature is activated
|
||||
// and the memtables are not guaranteed to be sorted in
|
||||
// the memlist vector.
|
||||
std::sort(ret->begin(), ret->end(),
|
||||
[](const MemTable* m1, const MemTable* m2) -> bool {
|
||||
return m1->GetID() < m2->GetID();
|
||||
});
|
||||
}
|
||||
|
||||
void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
@@ -407,7 +412,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info,
|
||||
bool write_edits) {
|
||||
IOStatus* io_s, bool write_edits) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
@@ -529,6 +534,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
db_directory, /*new_descriptor_log=*/false,
|
||||
/*column_family_options=*/nullptr,
|
||||
manifest_write_cb);
|
||||
*io_s = vset->io_status();
|
||||
} else {
|
||||
// If write_edit is false (e.g: successful mempurge),
|
||||
// then remove old memtables, wake up manifest write queue threads,
|
||||
@@ -544,6 +550,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// TODO(bjlemaire): explain full reason WakeUpWaitingManifestWriters
|
||||
// needed or investigate more.
|
||||
vset->WakeUpWaitingManifestWriters();
|
||||
*io_s = IOStatus::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -253,8 +253,7 @@ class MemTableList {
|
||||
// Returns the earliest memtables that needs to be flushed. The returned
|
||||
// memtables are guaranteed to be in the ascending order of created time.
|
||||
void PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
autovector<MemTable*>* mems,
|
||||
uint64_t* max_next_log_number = nullptr);
|
||||
autovector<MemTable*>* mems);
|
||||
|
||||
// Reset status of the given memtable list back to pending state so that
|
||||
// they can get picked up again on the next round of flush.
|
||||
@@ -270,7 +269,7 @@ class MemTableList {
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info,
|
||||
bool write_edits = true);
|
||||
IOStatus* io_s, bool write_edits = true);
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
// Takes ownership of the referenced held on *m by the caller of Add().
|
||||
|
||||
@@ -124,7 +124,7 @@ class MemTableListTest : public testing::Test {
|
||||
std::list<std::unique_ptr<FlushJobInfo>> flush_jobs_info;
|
||||
Status s = list->TryInstallMemtableFlushResults(
|
||||
cfd, mutable_cf_options, m, &dummy_prep_tracker, &versions, &mutex,
|
||||
file_num, to_delete, nullptr, &log_buffer, &flush_jobs_info);
|
||||
file_num, to_delete, nullptr, &log_buffer, &flush_jobs_info, &io_s);
|
||||
EXPECT_OK(io_s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -16,41 +16,31 @@ PeriodicWorkScheduler::PeriodicWorkScheduler(
|
||||
timer = std::unique_ptr<Timer>(new Timer(clock.get()));
|
||||
}
|
||||
|
||||
Status PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec) {
|
||||
void PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec) {
|
||||
MutexLock l(&timer_mu_);
|
||||
static std::atomic<uint64_t> initial_delay(0);
|
||||
timer->Start();
|
||||
if (stats_dump_period_sec > 0) {
|
||||
bool succeeded = timer->Add(
|
||||
[dbi]() { dbi->DumpStats(); }, GetTaskName(dbi, "dump_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task DumpStats");
|
||||
}
|
||||
timer->Add([dbi]() { dbi->DumpStats(); }, GetTaskName(dbi, "dump_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_dump_period_sec) *
|
||||
kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_dump_period_sec) * kMicrosInSecond);
|
||||
}
|
||||
if (stats_persist_period_sec > 0) {
|
||||
bool succeeded = timer->Add(
|
||||
timer->Add(
|
||||
[dbi]() { dbi->PersistStats(); }, GetTaskName(dbi, "pst_st"),
|
||||
initial_delay.fetch_add(1) %
|
||||
static_cast<uint64_t>(stats_persist_period_sec) * kMicrosInSecond,
|
||||
static_cast<uint64_t>(stats_persist_period_sec) * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task PersistStats");
|
||||
}
|
||||
}
|
||||
bool succeeded = timer->Add(
|
||||
[dbi]() { dbi->FlushInfoLog(); }, GetTaskName(dbi, "flush_info_log"),
|
||||
initial_delay.fetch_add(1) % kDefaultFlushInfoLogPeriodSec *
|
||||
kMicrosInSecond,
|
||||
kDefaultFlushInfoLogPeriodSec * kMicrosInSecond);
|
||||
if (!succeeded) {
|
||||
return Status::Aborted("Unable to add periodic task PersistStats");
|
||||
}
|
||||
return Status::OK();
|
||||
timer->Add([dbi]() { dbi->FlushInfoLog(); },
|
||||
GetTaskName(dbi, "flush_info_log"),
|
||||
initial_delay.fetch_add(1) % kDefaultFlushInfoLogPeriodSec *
|
||||
kMicrosInSecond,
|
||||
kDefaultFlushInfoLogPeriodSec * kMicrosInSecond);
|
||||
}
|
||||
|
||||
void PeriodicWorkScheduler::Unregister(DBImpl* dbi) {
|
||||
|
||||
@@ -30,8 +30,8 @@ class PeriodicWorkScheduler {
|
||||
PeriodicWorkScheduler& operator=(const PeriodicWorkScheduler&) = delete;
|
||||
PeriodicWorkScheduler& operator=(PeriodicWorkScheduler&&) = delete;
|
||||
|
||||
Status Register(DBImpl* dbi, unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec);
|
||||
void Register(DBImpl* dbi, unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec);
|
||||
|
||||
void Unregister(DBImpl* dbi);
|
||||
|
||||
|
||||
+3
-6
@@ -114,18 +114,15 @@ Status TableCache::GetTableReader(
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, fopts, &file, nullptr);
|
||||
}
|
||||
if (s.ok()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
} else if (s.IsPathNotFound()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
if (s.IsPathNotFound()) {
|
||||
fname = Rocks2LevelTableFileName(fname);
|
||||
s = PrepareIOFromReadOptions(ro, ioptions_.clock, fopts.io_options);
|
||||
if (s.ok()) {
|
||||
s = ioptions_.fs->NewRandomAccessFile(fname, file_options, &file,
|
||||
nullptr);
|
||||
}
|
||||
if (s.ok()) {
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
}
|
||||
RecordTick(ioptions_.stats, NO_FILE_OPENS);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. 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).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "db/version_set.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Instead of opening a `DB` to perform certain manifest updates, this
|
||||
// uses the underlying `VersionSet` API to read and modify the MANIFEST. This
|
||||
// allows us to use the user's real options, while not having to worry about
|
||||
// the DB persisting new SST files via flush/compaction or attempting to read/
|
||||
// compact files which may fail, particularly for the file we intend to remove
|
||||
// (the user may want to remove an already deleted file from MANIFEST).
|
||||
class OfflineManifestWriter {
|
||||
public:
|
||||
OfflineManifestWriter(const DBOptions& options, const std::string& db_path)
|
||||
: wc_(options.delayed_write_rate),
|
||||
wb_(options.db_write_buffer_size),
|
||||
immutable_db_options_(WithDbPath(options, db_path)),
|
||||
tc_(NewLRUCache(1 << 20 /* capacity */,
|
||||
options.table_cache_numshardbits)),
|
||||
versions_(db_path, &immutable_db_options_, sopt_, tc_.get(), &wb_, &wc_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
|
||||
/*db_session_id*/ "") {}
|
||||
|
||||
Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families) {
|
||||
return versions_.Recover(column_families);
|
||||
}
|
||||
|
||||
Status LogAndApply(ColumnFamilyData* cfd, VersionEdit* edit) {
|
||||
// Use `mutex` to imitate a locked DB mutex when calling `LogAndApply()`.
|
||||
InstrumentedMutex mutex;
|
||||
mutex.Lock();
|
||||
Status s = versions_.LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
edit, &mutex, nullptr /* db_directory */,
|
||||
false /* new_descriptor_log */);
|
||||
mutex.Unlock();
|
||||
return s;
|
||||
}
|
||||
|
||||
VersionSet& Versions() { return versions_; }
|
||||
const ImmutableDBOptions& IOptions() { return immutable_db_options_; }
|
||||
|
||||
private:
|
||||
WriteController wc_;
|
||||
WriteBufferManager wb_;
|
||||
ImmutableDBOptions immutable_db_options_;
|
||||
std::shared_ptr<Cache> tc_;
|
||||
EnvOptions sopt_;
|
||||
VersionSet versions_;
|
||||
|
||||
static ImmutableDBOptions WithDbPath(const DBOptions& options,
|
||||
const std::string& db_path) {
|
||||
ImmutableDBOptions rv(options);
|
||||
if (rv.db_paths.empty()) {
|
||||
// `VersionSet` expects options that have been through
|
||||
// `SanitizeOptions()`, which would sanitize an empty `db_paths`.
|
||||
rv.db_paths.emplace_back(db_path, 0 /* target_size */);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -2307,7 +2307,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(db_);
|
||||
|
||||
if (recovering_log_number_ != 0) {
|
||||
db_->mutex()->AssertHeld();
|
||||
// during recovery we rebuild a hollow transaction
|
||||
// from all encountered prepare sections of the wal
|
||||
if (db_->allow_2pc() == false) {
|
||||
@@ -2338,7 +2337,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert((rebuilding_trx_ != nullptr) == (recovering_log_number_ != 0));
|
||||
|
||||
if (recovering_log_number_ != 0) {
|
||||
db_->mutex()->AssertHeld();
|
||||
assert(db_->allow_2pc());
|
||||
size_t batch_cnt =
|
||||
write_after_commit_
|
||||
@@ -2359,9 +2357,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
Status MarkNoop(bool empty_batch) override {
|
||||
if (recovering_log_number_ != 0) {
|
||||
db_->mutex()->AssertHeld();
|
||||
}
|
||||
// A hack in pessimistic transaction could result into a noop at the start
|
||||
// of the write batch, that should be ignored.
|
||||
if (!empty_batch) {
|
||||
|
||||
@@ -115,7 +115,6 @@ DECLARE_int32(level0_stop_writes_trigger);
|
||||
DECLARE_int32(block_size);
|
||||
DECLARE_int32(format_version);
|
||||
DECLARE_int32(index_block_restart_interval);
|
||||
DECLARE_bool(disable_auto_compactions);
|
||||
DECLARE_int32(max_background_compactions);
|
||||
DECLARE_int32(num_bottom_pri_threads);
|
||||
DECLARE_int32(compaction_thread_pool_adjust_interval);
|
||||
@@ -275,13 +274,6 @@ DECLARE_int32(secondary_cache_fault_one_in);
|
||||
|
||||
DECLARE_int32(prepopulate_block_cache);
|
||||
|
||||
DECLARE_bool(two_write_queues);
|
||||
#ifndef ROCKSDB_LITE
|
||||
DECLARE_bool(use_only_the_last_commit_time_batch_for_recovery);
|
||||
DECLARE_uint64(wp_snapshot_cache_bits);
|
||||
DECLARE_uint64(wp_commit_cache_bits);
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
constexpr long KB = 1024;
|
||||
constexpr int kRandomValueMaxFactor = 3;
|
||||
constexpr int kValueMaxLen = 100;
|
||||
|
||||
@@ -225,10 +225,6 @@ DEFINE_int32(
|
||||
"Number of keys between restart points "
|
||||
"for delta encoding of keys in index block.");
|
||||
|
||||
DEFINE_bool(disable_auto_compactions,
|
||||
ROCKSDB_NAMESPACE::Options().disable_auto_compactions,
|
||||
"If true, RocksDB internally will not trigger compactions.");
|
||||
|
||||
DEFINE_int32(max_background_compactions,
|
||||
ROCKSDB_NAMESPACE::Options().max_background_compactions,
|
||||
"The maximum number of concurrent background compactions "
|
||||
@@ -894,22 +890,4 @@ DEFINE_int32(prepopulate_block_cache,
|
||||
"Options related to cache warming (see `enum "
|
||||
"PrepopulateBlockCache` in table.h)");
|
||||
|
||||
DEFINE_bool(two_write_queues, false,
|
||||
"Set to true to enable two write queues. Default: false");
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
DEFINE_bool(use_only_the_last_commit_time_batch_for_recovery, false,
|
||||
"If true, the commit-time write batch will not be immediately "
|
||||
"inserted into the memtables. Default: false");
|
||||
|
||||
DEFINE_uint64(
|
||||
wp_snapshot_cache_bits, 7ull,
|
||||
"Number of bits to represent write-prepared transaction db's snapshot "
|
||||
"cache. Default: 7 (128 entries)");
|
||||
|
||||
DEFINE_uint64(wp_commit_cache_bits, 23ull,
|
||||
"Number of bits to represent write-prepared transaction db's "
|
||||
"commit cache. Default: 23 (8M entries)");
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#endif // GFLAGS
|
||||
@@ -604,11 +604,8 @@ Status StressTest::NewTxn(WriteOptions& write_opts, Transaction** txn) {
|
||||
if (!FLAGS_use_txn) {
|
||||
return Status::InvalidArgument("NewTxn when FLAGS_use_txn is not set");
|
||||
}
|
||||
write_opts.disableWAL = FLAGS_disable_wal;
|
||||
static std::atomic<uint64_t> txn_id = {0};
|
||||
TransactionOptions txn_options;
|
||||
txn_options.use_only_the_last_commit_time_batch_for_recovery =
|
||||
FLAGS_use_only_the_last_commit_time_batch_for_recovery;
|
||||
txn_options.lock_timeout = 600000; // 10 min
|
||||
txn_options.deadlock_detect = true;
|
||||
*txn = txn_db_->BeginTransaction(write_opts, txn_options);
|
||||
@@ -1451,11 +1448,6 @@ Status StressTest::TestBackupRestore(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (thread->rand.OneIn(2)) {
|
||||
backup_opts.schema_version = 1;
|
||||
} else {
|
||||
backup_opts.schema_version = 2;
|
||||
}
|
||||
BackupEngine* backup_engine = nullptr;
|
||||
std::string from = "a backup/restore operation";
|
||||
Status s = BackupEngine::Open(db_stress_env, backup_opts, &backup_engine);
|
||||
@@ -1463,11 +1455,11 @@ Status StressTest::TestBackupRestore(
|
||||
from = "BackupEngine::Open";
|
||||
}
|
||||
if (s.ok()) {
|
||||
if (backup_opts.schema_version >= 2 && thread->rand.OneIn(2)) {
|
||||
TEST_BackupMetaSchemaOptions test_opts;
|
||||
if (thread->rand.OneIn(2)) {
|
||||
TEST_FutureSchemaVersion2Options test_opts;
|
||||
test_opts.crc32c_checksums = thread->rand.OneIn(2) == 0;
|
||||
test_opts.file_sizes = thread->rand.OneIn(2) == 0;
|
||||
TEST_SetBackupMetaSchemaOptions(backup_engine, test_opts);
|
||||
TEST_EnableWriteFutureSchemaVersion2(backup_engine, test_opts);
|
||||
}
|
||||
CreateBackupOptions create_opts;
|
||||
if (FLAGS_disable_wal) {
|
||||
@@ -2161,28 +2153,6 @@ void StressTest::PrintEnv() const {
|
||||
fprintf(stdout, "Format version : %d\n", FLAGS_format_version);
|
||||
fprintf(stdout, "TransactionDB : %s\n",
|
||||
FLAGS_use_txn ? "true" : "false");
|
||||
|
||||
if (FLAGS_use_txn) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
fprintf(stdout, "Two write queues: : %s\n",
|
||||
FLAGS_two_write_queues ? "true" : "false");
|
||||
fprintf(stdout, "Write policy : %d\n",
|
||||
static_cast<int>(FLAGS_txn_write_policy));
|
||||
if (static_cast<uint64_t>(TxnDBWritePolicy::WRITE_PREPARED) ==
|
||||
FLAGS_txn_write_policy ||
|
||||
static_cast<uint64_t>(TxnDBWritePolicy::WRITE_UNPREPARED) ==
|
||||
FLAGS_txn_write_policy) {
|
||||
fprintf(stdout, "Snapshot cache bits : %d\n",
|
||||
static_cast<int>(FLAGS_wp_snapshot_cache_bits));
|
||||
fprintf(stdout, "Commit cache bits : %d\n",
|
||||
static_cast<int>(FLAGS_wp_commit_cache_bits));
|
||||
}
|
||||
fprintf(stdout, "last cwb for recovery : %s\n",
|
||||
FLAGS_use_only_the_last_commit_time_batch_for_recovery ? "true"
|
||||
: "false");
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
fprintf(stdout, "Stacked BlobDB : %s\n",
|
||||
FLAGS_use_blob_db ? "true" : "false");
|
||||
@@ -2346,7 +2316,6 @@ void StressTest::Open() {
|
||||
options_.memtable_prefix_bloom_size_ratio =
|
||||
FLAGS_memtable_prefix_bloom_size_ratio;
|
||||
options_.memtable_whole_key_filtering = FLAGS_memtable_whole_key_filtering;
|
||||
options_.disable_auto_compactions = FLAGS_disable_auto_compactions;
|
||||
options_.max_background_compactions = FLAGS_max_background_compactions;
|
||||
options_.max_background_flushes = FLAGS_max_background_flushes;
|
||||
options_.compaction_style =
|
||||
@@ -2735,13 +2704,7 @@ void StressTest::Open() {
|
||||
options_.unordered_write = true;
|
||||
options_.two_write_queues = true;
|
||||
txn_db_options.skip_concurrency_control = true;
|
||||
} else {
|
||||
options_.two_write_queues = FLAGS_two_write_queues;
|
||||
}
|
||||
txn_db_options.wp_snapshot_cache_bits =
|
||||
static_cast<size_t>(FLAGS_wp_snapshot_cache_bits);
|
||||
txn_db_options.wp_commit_cache_bits =
|
||||
static_cast<size_t>(FLAGS_wp_commit_cache_bits);
|
||||
s = TransactionDB::Open(options_, txn_db_options, FLAGS_db,
|
||||
cf_descriptors, &column_families_, &txn_db_);
|
||||
if (!s.ok()) {
|
||||
@@ -2799,11 +2762,7 @@ void StressTest::Open() {
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
// Secondary instance does not support write-prepared/write-unprepared
|
||||
// transactions, thus just disable secondary instance if we use
|
||||
// transaction.
|
||||
if (s.ok() && FLAGS_continuous_verification_interval > 0 &&
|
||||
!FLAGS_use_txn && !cmp_db_) {
|
||||
if (s.ok() && FLAGS_continuous_verification_interval > 0 && !cmp_db_) {
|
||||
Options tmp_opts;
|
||||
// TODO(yanqin) support max_open_files != -1 for secondary instance.
|
||||
tmp_opts.max_open_files = -1;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -196,7 +196,7 @@ class MultiOpsTxnsStressTest : public StressTest {
|
||||
|
||||
void FinishInitDb(SharedState*) override;
|
||||
|
||||
void ReopenAndPreloadDbIfNeeded(SharedState* shared);
|
||||
void ReopenAndPreloadDb(SharedState* shared);
|
||||
|
||||
bool IsStateTracked() const override { return false; }
|
||||
|
||||
@@ -262,10 +262,10 @@ class MultiOpsTxnsStressTest : public StressTest {
|
||||
const std::vector<int>& rand_column_families) override;
|
||||
|
||||
Status PrimaryKeyUpdateTxn(ThreadState* thread, uint32_t old_a,
|
||||
uint32_t old_a_pos, uint32_t new_a);
|
||||
uint32_t new_a);
|
||||
|
||||
Status SecondaryKeyUpdateTxn(ThreadState* thread, uint32_t old_c,
|
||||
uint32_t old_c_pos, uint32_t new_c);
|
||||
uint32_t new_c);
|
||||
|
||||
Status UpdatePrimaryIndexValueTxn(ThreadState* thread, uint32_t a,
|
||||
uint32_t b_delta);
|
||||
@@ -276,88 +276,16 @@ class MultiOpsTxnsStressTest : public StressTest {
|
||||
|
||||
void VerifyDb(ThreadState* thread) const override;
|
||||
|
||||
void ContinuouslyVerifyDb(ThreadState* thread) const override {
|
||||
VerifyDb(thread);
|
||||
}
|
||||
|
||||
protected:
|
||||
using KeySet = std::set<uint32_t>;
|
||||
class KeyGenerator {
|
||||
public:
|
||||
explicit KeyGenerator(uint32_t s, uint32_t low, uint32_t high,
|
||||
KeySet&& existing_uniq, KeySet&& non_existing_uniq)
|
||||
: rand_(s),
|
||||
low_(low),
|
||||
high_(high),
|
||||
existing_uniq_(std::move(existing_uniq)),
|
||||
non_existing_uniq_(std::move(non_existing_uniq)) {}
|
||||
~KeyGenerator() {
|
||||
assert(!existing_uniq_.empty());
|
||||
assert(!non_existing_uniq_.empty());
|
||||
}
|
||||
void FinishInit();
|
||||
uint32_t ChooseA(ThreadState* thread);
|
||||
|
||||
std::pair<uint32_t, uint32_t> ChooseExisting();
|
||||
void Replace(uint32_t old_val, uint32_t old_pos, uint32_t new_val);
|
||||
uint32_t Allocate();
|
||||
void UndoAllocation(uint32_t new_val);
|
||||
|
||||
std::string ToString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "[" << low_ << ", " << high_ << "): " << existing_.size()
|
||||
<< " elements, " << existing_uniq_.size() << " unique values, "
|
||||
<< non_existing_uniq_.size() << " unique non-existing values";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
Random rand_;
|
||||
uint32_t low_ = 0;
|
||||
uint32_t high_ = 0;
|
||||
std::vector<uint32_t> existing_{};
|
||||
KeySet existing_uniq_{};
|
||||
KeySet non_existing_uniq_{};
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
// Return <a, pos>
|
||||
std::pair<uint32_t, uint32_t> ChooseExistingA(ThreadState* thread);
|
||||
|
||||
uint32_t GenerateNextA(ThreadState* thread);
|
||||
|
||||
// Return <c, pos>
|
||||
std::pair<uint32_t, uint32_t> ChooseExistingC(ThreadState* thread);
|
||||
|
||||
uint32_t GenerateNextC(ThreadState* thread);
|
||||
|
||||
std::vector<std::unique_ptr<KeyGenerator>> key_gen_for_a_;
|
||||
std::vector<std::unique_ptr<KeyGenerator>> key_gen_for_c_;
|
||||
uint32_t GenerateNextA();
|
||||
|
||||
private:
|
||||
struct KeySpaces {
|
||||
uint32_t lb_a = 0;
|
||||
uint32_t ub_a = 0;
|
||||
uint32_t lb_c = 0;
|
||||
uint32_t ub_c = 0;
|
||||
void PreloadDb(SharedState* shared, size_t num_c);
|
||||
|
||||
explicit KeySpaces() = default;
|
||||
explicit KeySpaces(uint32_t _lb_a, uint32_t _ub_a, uint32_t _lb_c,
|
||||
uint32_t _ub_c)
|
||||
: lb_a(_lb_a), ub_a(_ub_a), lb_c(_lb_c), ub_c(_ub_c) {}
|
||||
|
||||
std::string EncodeTo() const;
|
||||
bool DecodeFrom(Slice data);
|
||||
};
|
||||
|
||||
void PersistKeySpacesDesc(const std::string& key_spaces_path, uint32_t lb_a,
|
||||
uint32_t ub_a, uint32_t lb_c, uint32_t ub_c);
|
||||
|
||||
KeySpaces ReadKeySpacesDesc(const std::string& key_spaces_path);
|
||||
|
||||
void PreloadDb(SharedState* shared, int threads, uint32_t lb_a, uint32_t ub_a,
|
||||
uint32_t lb_c, uint32_t ub_c);
|
||||
|
||||
void ScanExistingDb(SharedState* shared, int threads);
|
||||
// TODO (yanqin) encapsulate the selection of keys a separate class.
|
||||
std::atomic<uint32_t> next_a_{0};
|
||||
};
|
||||
|
||||
class InvariantChecker {
|
||||
|
||||
Vendored
+1
-1
@@ -166,7 +166,7 @@ class PosixClock : public SystemClock {
|
||||
defined(OS_AIX) || (defined(__MACH__) && defined(__MAC_10_12))
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
|
||||
return (static_cast<uint64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec) / 1000;
|
||||
return static_cast<uint64_t>(ts.tv_sec) * 1000000000;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+5
-6
@@ -1256,7 +1256,7 @@ TEST_P(EnvPosixTestWithParam, MultiRead) {
|
||||
// Random Read
|
||||
Random rnd(301 + attempt);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UpdateResults:io_uring_result", [&](void* arg) {
|
||||
"PosixRandomAccessFile::MultiRead:io_uring_result", [&](void* arg) {
|
||||
if (attempt > 0) {
|
||||
// No failure in the first attempt.
|
||||
size_t& bytes_read = *static_cast<size_t*>(arg);
|
||||
@@ -1326,7 +1326,7 @@ TEST_F(EnvPosixTest, MultiReadNonAlignedLargeNum) {
|
||||
const int num_reads = rnd.Uniform(512) + 1;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UpdateResults:io_uring_result", [&](void* arg) {
|
||||
"PosixRandomAccessFile::MultiRead:io_uring_result", [&](void* arg) {
|
||||
if (attempt > 5) {
|
||||
// Improve partial result rates in second half of the run to
|
||||
// cover the case of repeated partial results.
|
||||
@@ -3203,11 +3203,10 @@ IOStatus ReadAsyncRandomAccessFile::ReadAsync(
|
||||
|
||||
// Submit read request asynchronously.
|
||||
std::function<void(FSReadRequest)> submit_request =
|
||||
[&opts, cb, cb_arg, dbg, create_io_error, this](FSReadRequest _req) {
|
||||
[&opts, cb, cb_arg, io_handle, del_fn, dbg, create_io_error,
|
||||
this](FSReadRequest _req) {
|
||||
if (!create_io_error) {
|
||||
_req.status = target()->Read(_req.offset, _req.len, opts,
|
||||
&(_req.result), _req.scratch, dbg);
|
||||
cb(_req, cb_arg);
|
||||
target()->ReadAsync(_req, opts, cb, cb_arg, io_handle, del_fn, dbg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Vendored
+23
-92
@@ -325,7 +325,14 @@ class PosixFileSystem : public FileSystem {
|
||||
SetFD_CLOEXEC(fd, &options);
|
||||
|
||||
if (options.use_mmap_writes) {
|
||||
MaybeForceDisableMmap(fd);
|
||||
if (!checkedDiskForMmap_) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fname)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
checkedDiskForMmap_ = true;
|
||||
}
|
||||
}
|
||||
if (options.use_mmap_writes && !forceMmapOff_) {
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
|
||||
@@ -424,7 +431,14 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
|
||||
if (options.use_mmap_writes) {
|
||||
MaybeForceDisableMmap(fd);
|
||||
if (!checkedDiskForMmap_) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fname)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
checkedDiskForMmap_ = true;
|
||||
}
|
||||
}
|
||||
if (options.use_mmap_writes && !forceMmapOff_) {
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
|
||||
@@ -985,7 +999,8 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
bool forceMmapOff_ = false; // do we override Env options?
|
||||
bool checkedDiskForMmap_;
|
||||
bool forceMmapOff_; // do we override Env options?
|
||||
|
||||
// Returns true iff the named directory exists and is a directory.
|
||||
virtual bool DirExists(const std::string& dname) {
|
||||
@@ -996,10 +1011,10 @@ class PosixFileSystem : public FileSystem {
|
||||
return false; // stat() failed return false
|
||||
}
|
||||
|
||||
bool SupportsFastAllocate(int fd) {
|
||||
bool SupportsFastAllocate(const std::string& path) {
|
||||
#ifdef ROCKSDB_FALLOCATE_PRESENT
|
||||
struct statfs s;
|
||||
if (fstatfs(fd, &s)) {
|
||||
if (statfs(path.c_str(), &s)) {
|
||||
return false;
|
||||
}
|
||||
switch (s.f_type) {
|
||||
@@ -1013,26 +1028,11 @@ class PosixFileSystem : public FileSystem {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
(void)fd;
|
||||
(void)path;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MaybeForceDisableMmap(int fd) {
|
||||
static std::once_flag s_check_disk_for_mmap_once;
|
||||
assert(this == FileSystem::Default().get());
|
||||
std::call_once(
|
||||
s_check_disk_for_mmap_once,
|
||||
[this](int fdesc) {
|
||||
// this will be executed once in the program's lifetime.
|
||||
// do not use mmapWrite on non ext-3/xfs/tmpfs systems.
|
||||
if (!SupportsFastAllocate(fdesc)) {
|
||||
forceMmapOff_ = true;
|
||||
}
|
||||
},
|
||||
fd);
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
bool IsIOUringEnabled() {
|
||||
if (RocksDbIOUringEnable && RocksDbIOUringEnable()) {
|
||||
@@ -1043,76 +1043,6 @@ class PosixFileSystem : public FileSystem {
|
||||
}
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
|
||||
// EXPERIMENTAL
|
||||
//
|
||||
// TODO akankshamahajan: Update Poll API to take into account min_completions
|
||||
// and returns if number of handles in io_handles (any order) completed is
|
||||
// equal to atleast min_completions.
|
||||
virtual IOStatus Poll(std::vector<void*>& io_handles,
|
||||
size_t /*min_completions*/) override {
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
// io_uring_queue_init.
|
||||
struct io_uring* iu = nullptr;
|
||||
if (thread_local_io_urings_) {
|
||||
iu = static_cast<struct io_uring*>(thread_local_io_urings_->Get());
|
||||
}
|
||||
|
||||
// Init failed, platform doesn't support io_uring.
|
||||
if (iu == nullptr) {
|
||||
return IOStatus::NotSupported("Poll");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < io_handles.size(); i++) {
|
||||
// The request has been completed in earlier runs.
|
||||
if ((static_cast<Posix_IOHandle*>(io_handles[i]))->is_finished) {
|
||||
continue;
|
||||
}
|
||||
// Loop until IO for io_handles[i] is completed.
|
||||
while (true) {
|
||||
// io_uring_wait_cqe.
|
||||
struct io_uring_cqe* cqe = nullptr;
|
||||
ssize_t ret = io_uring_wait_cqe(iu, &cqe);
|
||||
if (ret) {
|
||||
// abort as it shouldn't be in indeterminate state and there is no
|
||||
// good way currently to handle this error.
|
||||
abort();
|
||||
}
|
||||
|
||||
// Step 3: Populate the request.
|
||||
assert(cqe != nullptr);
|
||||
Posix_IOHandle* posix_handle =
|
||||
static_cast<Posix_IOHandle*>(io_uring_cqe_get_data(cqe));
|
||||
assert(posix_handle->iu == iu);
|
||||
if (posix_handle->iu != iu) {
|
||||
return IOStatus::IOError("");
|
||||
}
|
||||
// Reset cqe data to catch any stray reuse of it
|
||||
static_cast<struct io_uring_cqe*>(cqe)->user_data = 0xd5d5d5d5d5d5d5d5;
|
||||
|
||||
FSReadRequest req;
|
||||
req.scratch = posix_handle->scratch;
|
||||
req.offset = posix_handle->offset;
|
||||
req.len = posix_handle->len;
|
||||
size_t finished_len = 0;
|
||||
UpdateResult(cqe, "", req.len, posix_handle->iov.iov_len,
|
||||
true /*async_read*/, finished_len, &req);
|
||||
posix_handle->is_finished = true;
|
||||
io_uring_cqe_seen(iu, cqe);
|
||||
posix_handle->cb(req, posix_handle->cb_arg);
|
||||
(void)finished_len;
|
||||
|
||||
if (static_cast<Posix_IOHandle*>(io_handles[i]) == posix_handle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return IOStatus::OK();
|
||||
#else
|
||||
(void)io_handles;
|
||||
return IOStatus::NotSupported("Poll");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
// io_uring instance
|
||||
std::unique_ptr<ThreadLocalPtr> thread_local_io_urings_;
|
||||
@@ -1166,7 +1096,8 @@ size_t PosixFileSystem::GetLogicalBlockSizeForWriteIfNeeded(
|
||||
}
|
||||
|
||||
PosixFileSystem::PosixFileSystem()
|
||||
: forceMmapOff_(false),
|
||||
: checkedDiskForMmap_(false),
|
||||
forceMmapOff_(false),
|
||||
page_size_(getpagesize()),
|
||||
allow_non_owner_access_(true) {
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
|
||||
Vendored
+38
-96
@@ -744,31 +744,47 @@ IOStatus PosixRandomAccessFile::MultiRead(FSReadRequest* reqs,
|
||||
wrap_cache.erase(wrap_check);
|
||||
|
||||
FSReadRequest* req = req_wrap->req;
|
||||
UpdateResult(cqe, filename_, req->len, req_wrap->iov.iov_len,
|
||||
false /*async_read*/, req_wrap->finished_len, req);
|
||||
int32_t res = cqe->res;
|
||||
if (res == 0) {
|
||||
/// cqe->res == 0 can means EOF, or can mean partial results. See
|
||||
// comment
|
||||
// https://github.com/facebook/rocksdb/pull/6441#issuecomment-589843435
|
||||
// Fall back to pread in this case.
|
||||
if (use_direct_io() && !IsSectorAligned(req_wrap->finished_len,
|
||||
GetRequiredBufferAlignment())) {
|
||||
// Bytes reads don't fill sectors. Should only happen at the end
|
||||
// of the file.
|
||||
req->result = Slice(req->scratch, req_wrap->finished_len);
|
||||
if (cqe->res < 0) {
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOError("Req failed", filename_, cqe->res);
|
||||
} else {
|
||||
size_t bytes_read = static_cast<size_t>(cqe->res);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"PosixRandomAccessFile::MultiRead:io_uring_result", &bytes_read);
|
||||
if (bytes_read == req_wrap->iov.iov_len) {
|
||||
req->result = Slice(req->scratch, req->len);
|
||||
req->status = IOStatus::OK();
|
||||
} else if (bytes_read == 0) {
|
||||
// cqe->res == 0 can means EOF, or can mean partial results. See
|
||||
// comment
|
||||
// https://github.com/facebook/rocksdb/pull/6441#issuecomment-589843435
|
||||
// Fall back to pread in this case.
|
||||
if (use_direct_io() &&
|
||||
!IsSectorAligned(req_wrap->finished_len,
|
||||
GetRequiredBufferAlignment())) {
|
||||
// Bytes reads don't fill sectors. Should only happen at the end
|
||||
// of the file.
|
||||
req->result = Slice(req->scratch, req_wrap->finished_len);
|
||||
req->status = IOStatus::OK();
|
||||
} else {
|
||||
Slice tmp_slice;
|
||||
req->status =
|
||||
Read(req->offset + req_wrap->finished_len,
|
||||
req->len - req_wrap->finished_len, options, &tmp_slice,
|
||||
req->scratch + req_wrap->finished_len, dbg);
|
||||
req->result =
|
||||
Slice(req->scratch, req_wrap->finished_len + tmp_slice.size());
|
||||
}
|
||||
} else if (bytes_read < req_wrap->iov.iov_len) {
|
||||
assert(bytes_read > 0);
|
||||
assert(bytes_read + req_wrap->finished_len < req->len);
|
||||
req_wrap->finished_len += bytes_read;
|
||||
incomplete_rq_list.push_back(req_wrap);
|
||||
} else {
|
||||
Slice tmp_slice;
|
||||
req->status =
|
||||
Read(req->offset + req_wrap->finished_len,
|
||||
req->len - req_wrap->finished_len, options, &tmp_slice,
|
||||
req->scratch + req_wrap->finished_len, dbg);
|
||||
req->result =
|
||||
Slice(req->scratch, req_wrap->finished_len + tmp_slice.size());
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOError("Req returned more bytes than requested",
|
||||
filename_, cqe->res);
|
||||
}
|
||||
} else if (res > 0 && res < static_cast<int32_t>(req_wrap->iov.iov_len)) {
|
||||
incomplete_rq_list.push_back(req_wrap);
|
||||
}
|
||||
io_uring_cqe_seen(iu, cqe);
|
||||
}
|
||||
@@ -856,80 +872,6 @@ IOStatus PosixRandomAccessFile::InvalidateCache(size_t offset, size_t length) {
|
||||
#endif
|
||||
}
|
||||
|
||||
IOStatus PosixRandomAccessFile::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& /*opts*/,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn, IODebugContext* /*dbg*/) {
|
||||
if (use_direct_io()) {
|
||||
assert(IsSectorAligned(req.offset, GetRequiredBufferAlignment()));
|
||||
assert(IsSectorAligned(req.len, GetRequiredBufferAlignment()));
|
||||
assert(IsSectorAligned(req.scratch, GetRequiredBufferAlignment()));
|
||||
}
|
||||
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
// io_uring_queue_init.
|
||||
struct io_uring* iu = nullptr;
|
||||
if (thread_local_io_urings_) {
|
||||
iu = static_cast<struct io_uring*>(thread_local_io_urings_->Get());
|
||||
if (iu == nullptr) {
|
||||
iu = CreateIOUring();
|
||||
if (iu != nullptr) {
|
||||
thread_local_io_urings_->Reset(iu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init failed, platform doesn't support io_uring.
|
||||
if (iu == nullptr) {
|
||||
return IOStatus::NotSupported("ReadAsync");
|
||||
}
|
||||
|
||||
// Allocate io_handle.
|
||||
IOHandleDeleter deletefn = [](void* args) -> void {
|
||||
delete (static_cast<Posix_IOHandle*>(args));
|
||||
args = nullptr;
|
||||
};
|
||||
|
||||
Posix_IOHandle* posix_handle = new Posix_IOHandle();
|
||||
*io_handle = static_cast<void*>(posix_handle);
|
||||
*del_fn = deletefn;
|
||||
|
||||
// Initialize Posix_IOHandle.
|
||||
posix_handle->iu = iu;
|
||||
posix_handle->iov.iov_base = posix_handle->scratch;
|
||||
posix_handle->iov.iov_len = posix_handle->len;
|
||||
posix_handle->cb = cb;
|
||||
posix_handle->cb_arg = cb_arg;
|
||||
posix_handle->offset = req.offset;
|
||||
posix_handle->len = req.len;
|
||||
posix_handle->scratch = req.scratch;
|
||||
|
||||
// Step 3: io_uring_sqe_set_data
|
||||
struct io_uring_sqe* sqe;
|
||||
sqe = io_uring_get_sqe(iu);
|
||||
|
||||
io_uring_prep_readv(sqe, fd_, &posix_handle->iov, 1, posix_handle->offset);
|
||||
|
||||
io_uring_sqe_set_data(sqe, posix_handle);
|
||||
|
||||
// Step 4: io_uring_submit
|
||||
ssize_t ret = io_uring_submit(iu);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "io_uring_submit error: %ld\n", long(ret));
|
||||
return IOStatus::IOError("io_uring_submit() requested but returned " +
|
||||
ToString(ret));
|
||||
}
|
||||
return IOStatus::OK();
|
||||
#else
|
||||
(void)req;
|
||||
(void)cb;
|
||||
(void)cb_arg;
|
||||
(void)io_handle;
|
||||
(void)del_fn;
|
||||
return IOStatus::NotSupported("ReadAsync");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* PosixMmapReadableFile
|
||||
*
|
||||
|
||||
Vendored
-59
@@ -13,17 +13,14 @@
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -52,57 +49,6 @@ class PosixHelper {
|
||||
size_t* size);
|
||||
};
|
||||
|
||||
#if defined(ROCKSDB_IOURING_PRESENT)
|
||||
struct Posix_IOHandle {
|
||||
struct iovec iov;
|
||||
struct io_uring* iu;
|
||||
std::function<void(const FSReadRequest&, void*)> cb;
|
||||
void* cb_arg;
|
||||
uint64_t offset;
|
||||
size_t len;
|
||||
char* scratch;
|
||||
bool is_finished = false;
|
||||
};
|
||||
|
||||
inline void UpdateResult(struct io_uring_cqe* cqe, const std::string& file_name,
|
||||
size_t len, size_t iov_len, bool async_read,
|
||||
size_t& finished_len, FSReadRequest* req) {
|
||||
if (cqe->res < 0) {
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOError("Req failed", file_name, cqe->res);
|
||||
} else {
|
||||
size_t bytes_read = static_cast<size_t>(cqe->res);
|
||||
TEST_SYNC_POINT_CALLBACK("UpdateResults::io_uring_result", &bytes_read);
|
||||
if (bytes_read == iov_len) {
|
||||
req->result = Slice(req->scratch, req->len);
|
||||
req->status = IOStatus::OK();
|
||||
} else if (bytes_read == 0) {
|
||||
if (async_read) {
|
||||
// No bytes read. It can means EOF.
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOStatus::OK();
|
||||
}
|
||||
} else if (bytes_read < iov_len) {
|
||||
assert(bytes_read > 0);
|
||||
if (async_read) {
|
||||
req->result = Slice(req->scratch, bytes_read);
|
||||
req->status = IOStatus::OK();
|
||||
} else {
|
||||
assert(bytes_read + finished_len < len);
|
||||
finished_len += bytes_read;
|
||||
}
|
||||
} else {
|
||||
req->result = Slice(req->scratch, 0);
|
||||
req->status = IOError("Req returned more bytes than requested", file_name,
|
||||
cqe->res);
|
||||
}
|
||||
}
|
||||
#ifdef NDEBUG
|
||||
(void)len;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OS_LINUX
|
||||
// Files under a specific directory have the same logical block size.
|
||||
// This class caches the logical block size for the specified directories to
|
||||
@@ -264,11 +210,6 @@ class PosixRandomAccessFile : public FSRandomAccessFile {
|
||||
virtual size_t GetRequiredBufferAlignment() const override {
|
||||
return logical_sector_size_;
|
||||
}
|
||||
// EXPERIMENTAL
|
||||
virtual IOStatus ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn, IODebugContext* dbg) override;
|
||||
};
|
||||
|
||||
class PosixWritableFile : public FSWritableFile {
|
||||
|
||||
@@ -430,14 +430,13 @@ TEST_F(DeleteSchedulerTest, BackgroundError) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
// 1- Create kTestFileNum dummy files
|
||||
// 2- Delete kTestFileNum dummy files using DeleteScheduler
|
||||
// 1- Create 10 dummy files
|
||||
// 2- Delete 10 dummy files using DeleteScheduler
|
||||
// 3- Wait for DeleteScheduler to delete all files in queue
|
||||
// 4- Make sure all files in trash directory were deleted
|
||||
// 5- Repeat previous steps 5 times
|
||||
TEST_F(DeleteSchedulerTest, StartBGEmptyTrashMultipleTimes) {
|
||||
constexpr int kTestFileNum = 10;
|
||||
std::atomic_int bg_delete_file = 0;
|
||||
int bg_delete_file = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DeleteScheduler::DeleteTrashFile:DeleteFile",
|
||||
[&](void* /*arg*/) { bg_delete_file++; });
|
||||
@@ -446,30 +445,25 @@ TEST_F(DeleteSchedulerTest, StartBGEmptyTrashMultipleTimes) {
|
||||
rate_bytes_per_sec_ = 1024 * 1024; // 1 MB / sec
|
||||
NewDeleteScheduler();
|
||||
|
||||
// If trash file is generated faster than deleting, delete_scheduler will
|
||||
// delete it directly instead of waiting for background trash empty thread to
|
||||
// clean it. Set the ratio higher to avoid that.
|
||||
sst_file_mgr_->SetMaxTrashDBRatio(kTestFileNum + 1);
|
||||
|
||||
// Move files to trash, wait for empty trash, start again
|
||||
for (int run = 1; run <= 5; run++) {
|
||||
// Generate kTestFileNum dummy files and move them to trash
|
||||
for (int i = 0; i < kTestFileNum; i++) {
|
||||
// Generate 10 dummy files and move them to trash
|
||||
for (int i = 0; i < 10; i++) {
|
||||
std::string file_name = "data_" + ToString(i) + ".data";
|
||||
ASSERT_OK(delete_scheduler_->DeleteFile(NewDummyFile(file_name), ""));
|
||||
}
|
||||
ASSERT_EQ(CountNormalFiles(), 0);
|
||||
delete_scheduler_->WaitForEmptyTrash();
|
||||
ASSERT_EQ(bg_delete_file, kTestFileNum * run);
|
||||
ASSERT_EQ(bg_delete_file, 10 * run);
|
||||
ASSERT_EQ(CountTrashFiles(), 0);
|
||||
|
||||
auto bg_errors = delete_scheduler_->GetBackgroundErrors();
|
||||
ASSERT_EQ(bg_errors.size(), 0);
|
||||
ASSERT_EQ(kTestFileNum, stats_->getAndResetTickerCount(FILES_MARKED_TRASH));
|
||||
ASSERT_EQ(10, stats_->getAndResetTickerCount(FILES_MARKED_TRASH));
|
||||
ASSERT_EQ(0, stats_->getAndResetTickerCount(FILES_DELETED_IMMEDIATELY));
|
||||
}
|
||||
|
||||
ASSERT_EQ(bg_delete_file, 5 * kTestFileNum);
|
||||
ASSERT_EQ(bg_delete_file, 50);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
}
|
||||
|
||||
+81
-440
@@ -10,6 +10,7 @@
|
||||
#include "file/file_prefetch_buffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "monitoring/histogram.h"
|
||||
@@ -20,121 +21,6 @@
|
||||
#include "util/rate_limiter.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
void FilePrefetchBuffer::CalculateOffsetAndLen(size_t alignment,
|
||||
uint64_t offset,
|
||||
size_t roundup_len, size_t index,
|
||||
bool refit_tail,
|
||||
uint64_t& chunk_len) {
|
||||
uint64_t chunk_offset_in_buffer = 0;
|
||||
bool copy_data_to_new_buffer = false;
|
||||
// Check if requested bytes are in the existing buffer_.
|
||||
// If only a few bytes exist -- reuse them & read only what is really needed.
|
||||
// This is typically the case of incremental reading of data.
|
||||
// If no bytes exist in buffer -- full pread.
|
||||
if (bufs_[index].buffer_.CurrentSize() > 0 &&
|
||||
offset >= bufs_[index].offset_ &&
|
||||
offset <= bufs_[index].offset_ + bufs_[index].buffer_.CurrentSize()) {
|
||||
// Only a few requested bytes are in the buffer. memmove those chunk of
|
||||
// bytes to the beginning, and memcpy them back into the new buffer if a
|
||||
// new buffer is created.
|
||||
chunk_offset_in_buffer = Rounddown(
|
||||
static_cast<size_t>(offset - bufs_[index].offset_), alignment);
|
||||
chunk_len = static_cast<uint64_t>(bufs_[index].buffer_.CurrentSize()) -
|
||||
chunk_offset_in_buffer;
|
||||
assert(chunk_offset_in_buffer % alignment == 0);
|
||||
// assert(chunk_len % alignment == 0);
|
||||
assert(chunk_offset_in_buffer + chunk_len <=
|
||||
bufs_[index].offset_ + bufs_[index].buffer_.CurrentSize());
|
||||
if (chunk_len > 0) {
|
||||
copy_data_to_new_buffer = true;
|
||||
} else {
|
||||
// this reset is not necessary, but just to be safe.
|
||||
chunk_offset_in_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new buffer only if current capacity is not sufficient, and memcopy
|
||||
// bytes from old buffer if needed (i.e., if chunk_len is greater than 0).
|
||||
if (bufs_[index].buffer_.Capacity() < roundup_len) {
|
||||
bufs_[index].buffer_.Alignment(alignment);
|
||||
bufs_[index].buffer_.AllocateNewBuffer(
|
||||
static_cast<size_t>(roundup_len), copy_data_to_new_buffer,
|
||||
chunk_offset_in_buffer, static_cast<size_t>(chunk_len));
|
||||
} else if (chunk_len > 0 && refit_tail) {
|
||||
// New buffer not needed. But memmove bytes from tail to the beginning since
|
||||
// chunk_len is greater than 0.
|
||||
bufs_[index].buffer_.RefitTail(static_cast<size_t>(chunk_offset_in_buffer),
|
||||
static_cast<size_t>(chunk_len));
|
||||
} else if (chunk_len > 0) {
|
||||
// For async prefetching, it doesn't call RefitTail with chunk_len > 0.
|
||||
// Allocate new buffer if needed because aligned buffer calculate remaining
|
||||
// buffer as capacity_ - cursize_ which might not be the case in this as we
|
||||
// are not refitting.
|
||||
// TODO akanksha: Update the condition when asynchronous prefetching is
|
||||
// stable.
|
||||
bufs_[index].buffer_.Alignment(alignment);
|
||||
bufs_[index].buffer_.AllocateNewBuffer(
|
||||
static_cast<size_t>(roundup_len), copy_data_to_new_buffer,
|
||||
chunk_offset_in_buffer, static_cast<size_t>(chunk_len));
|
||||
}
|
||||
}
|
||||
|
||||
Status FilePrefetchBuffer::Read(const IOOptions& opts,
|
||||
RandomAccessFileReader* reader,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
uint64_t read_len, uint64_t chunk_len,
|
||||
uint64_t rounddown_start, uint32_t index) {
|
||||
Slice result;
|
||||
Status s = reader->Read(opts, rounddown_start + chunk_len, read_len, &result,
|
||||
bufs_[index].buffer_.BufferStart() + chunk_len,
|
||||
nullptr, rate_limiter_priority);
|
||||
#ifndef NDEBUG
|
||||
if (result.size() < read_len) {
|
||||
// Fake an IO error to force db_stress fault injection to ignore
|
||||
// truncated read errors
|
||||
IGNORE_STATUS_IF_ERROR(Status::IOError());
|
||||
}
|
||||
#endif
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Update the buffer offset and size.
|
||||
bufs_[index].offset_ = rounddown_start;
|
||||
bufs_[index].buffer_.Size(static_cast<size_t>(chunk_len) + result.size());
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FilePrefetchBuffer::ReadAsync(const IOOptions& opts,
|
||||
RandomAccessFileReader* reader,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
uint64_t read_len, uint64_t chunk_len,
|
||||
uint64_t rounddown_start, uint32_t index) {
|
||||
// Reset io_handle.
|
||||
if (io_handle_ != nullptr && del_fn_ != nullptr) {
|
||||
del_fn_(io_handle_);
|
||||
io_handle_ = nullptr;
|
||||
del_fn_ = nullptr;
|
||||
}
|
||||
|
||||
// callback for async read request.
|
||||
auto fp = std::bind(&FilePrefetchBuffer::PrefetchAsyncCallback, this,
|
||||
std::placeholders::_1, std::placeholders::_2);
|
||||
FSReadRequest req;
|
||||
Slice result;
|
||||
req.len = read_len;
|
||||
req.offset = rounddown_start + chunk_len;
|
||||
req.result = result;
|
||||
req.scratch = bufs_[index].buffer_.BufferStart() + chunk_len;
|
||||
Status s = reader->ReadAsync(req, opts, fp, nullptr /*cb_arg*/, &io_handle_,
|
||||
&del_fn_, rate_limiter_priority);
|
||||
if (s.ok()) {
|
||||
async_read_in_progress_ = true;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FilePrefetchBuffer::Prefetch(const IOOptions& opts,
|
||||
RandomAccessFileReader* reader,
|
||||
uint64_t offset, size_t n,
|
||||
@@ -143,13 +29,6 @@ Status FilePrefetchBuffer::Prefetch(const IOOptions& opts,
|
||||
return Status::OK();
|
||||
}
|
||||
TEST_SYNC_POINT("FilePrefetchBuffer::Prefetch:Start");
|
||||
|
||||
if (offset + n <= bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize()) {
|
||||
// All requested bytes are already in the curr_ buffer. So no need to Read
|
||||
// again.
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
size_t alignment = reader->file()->GetRequiredBufferAlignment();
|
||||
size_t offset_ = static_cast<size_t>(offset);
|
||||
uint64_t rounddown_offset = Rounddown(offset_, alignment);
|
||||
@@ -158,219 +37,74 @@ Status FilePrefetchBuffer::Prefetch(const IOOptions& opts,
|
||||
assert(roundup_len >= alignment);
|
||||
assert(roundup_len % alignment == 0);
|
||||
|
||||
uint64_t chunk_len = 0;
|
||||
CalculateOffsetAndLen(alignment, offset, roundup_len, curr_,
|
||||
true /*refit_tail*/, chunk_len);
|
||||
size_t read_len = static_cast<size_t>(roundup_len - chunk_len);
|
||||
// Check if requested bytes are in the existing buffer_.
|
||||
// If all bytes exist -- return.
|
||||
// If only a few bytes exist -- reuse them & read only what is really needed.
|
||||
// This is typically the case of incremental reading of data.
|
||||
// If no bytes exist in buffer -- full pread.
|
||||
|
||||
Status s = Read(opts, reader, rate_limiter_priority, read_len, chunk_len,
|
||||
rounddown_offset, curr_);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Copy data from src to third buffer.
|
||||
void FilePrefetchBuffer::CopyDataToBuffer(uint32_t src, uint64_t& offset,
|
||||
size_t& length) {
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
uint64_t copy_offset = (offset - bufs_[src].offset_);
|
||||
size_t copy_len = 0;
|
||||
if (offset + length <=
|
||||
bufs_[src].offset_ + bufs_[src].buffer_.CurrentSize()) {
|
||||
// All the bytes are in src.
|
||||
copy_len = length;
|
||||
} else {
|
||||
copy_len = bufs_[src].buffer_.CurrentSize() - copy_offset;
|
||||
}
|
||||
|
||||
memcpy(bufs_[2].buffer_.BufferStart() + bufs_[2].buffer_.CurrentSize(),
|
||||
bufs_[src].buffer_.BufferStart() + copy_offset, copy_len);
|
||||
|
||||
bufs_[2].buffer_.Size(bufs_[2].buffer_.CurrentSize() + copy_len);
|
||||
|
||||
// Update offset and length.
|
||||
offset += copy_len;
|
||||
length -= copy_len;
|
||||
|
||||
// length > 0 indicates it has consumed all data from the src buffer and it
|
||||
// still needs to read more other buffer.
|
||||
if (length > 0) {
|
||||
bufs_[src].buffer_.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// If async_read = true:
|
||||
// async_read is enabled in case of sequential reads. So when
|
||||
// buffers are switched, we clear the curr_ buffer as we assume the data has
|
||||
// been consumed because of sequential reads.
|
||||
//
|
||||
// Scenarios for prefetching asynchronously:
|
||||
// Case1: If both buffers are empty, prefetch n bytes
|
||||
// synchronously in curr_
|
||||
// and prefetch readahead_size_/2 async in second buffer.
|
||||
// Case2: If second buffer has partial or full data, make it current and
|
||||
// prefetch readahead_size_/2 async in second buffer. In case of
|
||||
// partial data, prefetch remaining bytes from size n synchronously to
|
||||
// fulfill the requested bytes request.
|
||||
// Case3: If curr_ has partial data, prefetch remaining bytes from size n
|
||||
// synchronously in curr_ to fulfill the requested bytes request and
|
||||
// prefetch readahead_size_/2 bytes async in second buffer.
|
||||
// Case4: If data is in both buffers, copy requested data from curr_ and second
|
||||
// buffer to third buffer. If all requested bytes have been copied, do
|
||||
// the asynchronous prefetching in second buffer.
|
||||
Status FilePrefetchBuffer::PrefetchAsync(const IOOptions& opts,
|
||||
RandomAccessFileReader* reader,
|
||||
FileSystem* fs, uint64_t offset,
|
||||
size_t length, size_t readahead_size,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
bool& copy_to_third_buffer) {
|
||||
if (!enable_) {
|
||||
return Status::OK();
|
||||
}
|
||||
if (async_read_in_progress_ && fs != nullptr) {
|
||||
// Wait for prefetch data to complete.
|
||||
// No mutex is needed as PrefetchAsyncCallback updates the result in second
|
||||
// buffer and FilePrefetchBuffer should wait for Poll before accessing the
|
||||
// second buffer.
|
||||
std::vector<void*> handles;
|
||||
handles.emplace_back(io_handle_);
|
||||
fs->Poll(handles, 1).PermitUncheckedError();
|
||||
}
|
||||
|
||||
// TODO akanksha: Update TEST_SYNC_POINT after new tests are added.
|
||||
TEST_SYNC_POINT("FilePrefetchBuffer::Prefetch:Start");
|
||||
Status s;
|
||||
size_t prefetch_size = length + readahead_size;
|
||||
|
||||
size_t alignment = reader->file()->GetRequiredBufferAlignment();
|
||||
// Index of second buffer.
|
||||
uint32_t second = curr_ ^ 1;
|
||||
|
||||
// First clear the buffers if it contains outdated data. Outdated data can be
|
||||
// because previous sequential reads were read from the cache instead of these
|
||||
// buffer.
|
||||
{
|
||||
if (bufs_[curr_].buffer_.CurrentSize() > 0 &&
|
||||
offset >= bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize()) {
|
||||
bufs_[curr_].buffer_.Clear();
|
||||
}
|
||||
if (bufs_[second].buffer_.CurrentSize() > 0 &&
|
||||
offset >= bufs_[second].offset_ + bufs_[second].buffer_.CurrentSize()) {
|
||||
bufs_[second].buffer_.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// If data is in second buffer, make it curr_. Second buffer can be either
|
||||
// partial filled or full.
|
||||
if (bufs_[second].buffer_.CurrentSize() > 0 &&
|
||||
offset >= bufs_[second].offset_ &&
|
||||
offset < bufs_[second].offset_ + bufs_[second].buffer_.CurrentSize()) {
|
||||
// Clear the curr_ as buffers have been swapped and curr_ contains the
|
||||
// outdated data and switch the buffers.
|
||||
bufs_[curr_].buffer_.Clear();
|
||||
curr_ = curr_ ^ 1;
|
||||
second = curr_ ^ 1;
|
||||
}
|
||||
// After swap check if all the requested bytes are in curr_, it will go for
|
||||
// async prefetching only.
|
||||
if (bufs_[curr_].buffer_.CurrentSize() > 0 &&
|
||||
offset + length <=
|
||||
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize()) {
|
||||
offset += length;
|
||||
length = 0;
|
||||
prefetch_size -= length;
|
||||
}
|
||||
// Data is overlapping i.e. some of the data is in curr_ buffer and remaining
|
||||
// in second buffer.
|
||||
if (bufs_[curr_].buffer_.CurrentSize() > 0 &&
|
||||
bufs_[second].buffer_.CurrentSize() > 0 &&
|
||||
offset >= bufs_[curr_].offset_ &&
|
||||
offset < bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize() &&
|
||||
offset + length > bufs_[second].offset_) {
|
||||
// Allocate new buffer to third buffer;
|
||||
bufs_[2].buffer_.Clear();
|
||||
bufs_[2].buffer_.Alignment(alignment);
|
||||
bufs_[2].buffer_.AllocateNewBuffer(length);
|
||||
bufs_[2].offset_ = offset;
|
||||
copy_to_third_buffer = true;
|
||||
|
||||
// Move data from curr_ buffer to third.
|
||||
CopyDataToBuffer(curr_, offset, length);
|
||||
if (length == 0) {
|
||||
// Requested data has been copied and curr_ still has unconsumed data.
|
||||
return s;
|
||||
}
|
||||
CopyDataToBuffer(second, offset, length);
|
||||
// Length == 0: All the requested data has been copied to third buffer. It
|
||||
// should go for only async prefetching.
|
||||
// Length > 0: More data needs to be consumed so it will continue async and
|
||||
// sync prefetching and copy the remaining data to third buffer in the end.
|
||||
// swap the buffers.
|
||||
curr_ = curr_ ^ 1;
|
||||
prefetch_size -= length;
|
||||
}
|
||||
|
||||
// Update second again if swap happened.
|
||||
second = curr_ ^ 1;
|
||||
size_t _offset = static_cast<size_t>(offset);
|
||||
|
||||
// offset and size alignment for curr_ buffer with synchronous prefetching
|
||||
uint64_t rounddown_start1 = Rounddown(_offset, alignment);
|
||||
uint64_t roundup_end1 = Roundup(_offset + prefetch_size, alignment);
|
||||
uint64_t roundup_len1 = roundup_end1 - rounddown_start1;
|
||||
assert(roundup_len1 >= alignment);
|
||||
assert(roundup_len1 % alignment == 0);
|
||||
uint64_t chunk_len1 = 0;
|
||||
uint64_t read_len1 = 0;
|
||||
|
||||
// For length == 0, skip the synchronous prefetching. read_len1 will be 0.
|
||||
if (length > 0) {
|
||||
CalculateOffsetAndLen(alignment, offset, roundup_len1, curr_,
|
||||
false /*refit_tail*/, chunk_len1);
|
||||
assert(roundup_len1 >= chunk_len1);
|
||||
read_len1 = static_cast<size_t>(roundup_len1 - chunk_len1);
|
||||
}
|
||||
{
|
||||
// offset and size alignment for second buffer for asynchronous
|
||||
// prefetching
|
||||
uint64_t rounddown_start2 = roundup_end1;
|
||||
uint64_t roundup_end2 =
|
||||
Roundup(rounddown_start2 + readahead_size, alignment);
|
||||
|
||||
// For length == 0, do the asynchronous prefetching in second instead of
|
||||
// synchronous prefetching in curr_.
|
||||
if (length == 0) {
|
||||
rounddown_start2 =
|
||||
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize();
|
||||
roundup_end2 = Roundup(rounddown_start2 + prefetch_size, alignment);
|
||||
}
|
||||
|
||||
uint64_t roundup_len2 = roundup_end2 - rounddown_start2;
|
||||
uint64_t chunk_len2 = 0;
|
||||
CalculateOffsetAndLen(alignment, rounddown_start2, roundup_len2, second,
|
||||
false /*refit_tail*/, chunk_len2);
|
||||
|
||||
// Update the buffer offset.
|
||||
bufs_[second].offset_ = rounddown_start2;
|
||||
assert(roundup_len2 >= chunk_len2);
|
||||
uint64_t read_len2 = static_cast<size_t>(roundup_len2 - chunk_len2);
|
||||
ReadAsync(opts, reader, rate_limiter_priority, read_len2, chunk_len2,
|
||||
rounddown_start2, second)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
if (read_len1 > 0) {
|
||||
s = Read(opts, reader, rate_limiter_priority, read_len1, chunk_len1,
|
||||
rounddown_start1, curr_);
|
||||
if (!s.ok()) {
|
||||
uint64_t chunk_offset_in_buffer = 0;
|
||||
uint64_t chunk_len = 0;
|
||||
bool copy_data_to_new_buffer = false;
|
||||
if (buffer_.CurrentSize() > 0 && offset >= buffer_offset_ &&
|
||||
offset <= buffer_offset_ + buffer_.CurrentSize()) {
|
||||
if (offset + n <= buffer_offset_ + buffer_.CurrentSize()) {
|
||||
// All requested bytes are already in the buffer. So no need to Read
|
||||
// again.
|
||||
return s;
|
||||
} else {
|
||||
// Only a few requested bytes are in the buffer. memmove those chunk of
|
||||
// bytes to the beginning, and memcpy them back into the new buffer if a
|
||||
// new buffer is created.
|
||||
chunk_offset_in_buffer =
|
||||
Rounddown(static_cast<size_t>(offset - buffer_offset_), alignment);
|
||||
chunk_len = buffer_.CurrentSize() - chunk_offset_in_buffer;
|
||||
assert(chunk_offset_in_buffer % alignment == 0);
|
||||
assert(chunk_len % alignment == 0);
|
||||
assert(chunk_offset_in_buffer + chunk_len <=
|
||||
buffer_offset_ + buffer_.CurrentSize());
|
||||
if (chunk_len > 0) {
|
||||
copy_data_to_new_buffer = true;
|
||||
} else {
|
||||
// this reset is not necessary, but just to be safe.
|
||||
chunk_offset_in_buffer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy remaining requested bytes to third_buffer.
|
||||
if (copy_to_third_buffer && length > 0) {
|
||||
CopyDataToBuffer(curr_, offset, length);
|
||||
|
||||
// Create a new buffer only if current capacity is not sufficient, and memcopy
|
||||
// bytes from old buffer if needed (i.e., if chunk_len is greater than 0).
|
||||
if (buffer_.Capacity() < roundup_len) {
|
||||
buffer_.Alignment(alignment);
|
||||
buffer_.AllocateNewBuffer(static_cast<size_t>(roundup_len),
|
||||
copy_data_to_new_buffer, chunk_offset_in_buffer,
|
||||
static_cast<size_t>(chunk_len));
|
||||
} else if (chunk_len > 0) {
|
||||
// New buffer not needed. But memmove bytes from tail to the beginning since
|
||||
// chunk_len is greater than 0.
|
||||
buffer_.RefitTail(static_cast<size_t>(chunk_offset_in_buffer),
|
||||
static_cast<size_t>(chunk_len));
|
||||
}
|
||||
|
||||
Slice result;
|
||||
size_t read_len = static_cast<size_t>(roundup_len - chunk_len);
|
||||
s = reader->Read(opts, rounddown_offset + chunk_len, read_len, &result,
|
||||
buffer_.BufferStart() + chunk_len, nullptr,
|
||||
rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (result.size() < read_len) {
|
||||
// Fake an IO error to force db_stress fault injection to ignore
|
||||
// truncated read errors
|
||||
IGNORE_STATUS_IF_ERROR(Status::IOError());
|
||||
}
|
||||
#endif
|
||||
buffer_offset_ = rounddown_offset;
|
||||
buffer_.Size(static_cast<size_t>(chunk_len) + result.size());
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -383,7 +117,7 @@ bool FilePrefetchBuffer::TryReadFromCache(const IOOptions& opts,
|
||||
if (track_min_offset_ && offset < min_offset_read_) {
|
||||
min_offset_read_ = static_cast<size_t>(offset);
|
||||
}
|
||||
if (!enable_ || (offset < bufs_[curr_].offset_)) {
|
||||
if (!enable_ || offset < buffer_offset_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -393,17 +127,28 @@ bool FilePrefetchBuffer::TryReadFromCache(const IOOptions& opts,
|
||||
// If readahead is not enabled: return false.
|
||||
TEST_SYNC_POINT_CALLBACK("FilePrefetchBuffer::TryReadFromCache",
|
||||
&readahead_size_);
|
||||
if (offset + n > bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize()) {
|
||||
if (offset + n > buffer_offset_ + buffer_.CurrentSize()) {
|
||||
if (readahead_size_ > 0) {
|
||||
Status s;
|
||||
assert(reader != nullptr);
|
||||
assert(max_readahead_size_ >= readahead_size_);
|
||||
Status s;
|
||||
if (for_compaction) {
|
||||
s = Prefetch(opts, reader, offset, std::max(n, readahead_size_),
|
||||
rate_limiter_priority);
|
||||
} else {
|
||||
if (implicit_auto_readahead_) {
|
||||
if (!IsEligibleForPrefetch(offset, n)) {
|
||||
// Prefetch only if this read is sequential otherwise reset
|
||||
// readahead_size_ to initial value.
|
||||
if (!IsBlockSequential(offset)) {
|
||||
UpdateReadPattern(offset, n);
|
||||
ResetValues();
|
||||
// Ignore status as Prefetch is not called.
|
||||
s.PermitUncheckedError();
|
||||
return false;
|
||||
}
|
||||
num_file_reads_++;
|
||||
if (num_file_reads_ <= kMinNumFileReadsToStartAutoReadahead) {
|
||||
UpdateReadPattern(offset, n);
|
||||
// Ignore status as Prefetch is not called.
|
||||
s.PermitUncheckedError();
|
||||
return false;
|
||||
@@ -426,113 +171,9 @@ bool FilePrefetchBuffer::TryReadFromCache(const IOOptions& opts,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
UpdateReadPattern(offset, n, false /*decrease_readaheadsize*/);
|
||||
|
||||
uint64_t offset_in_buffer = offset - bufs_[curr_].offset_;
|
||||
*result = Slice(bufs_[curr_].buffer_.BufferStart() + offset_in_buffer, n);
|
||||
UpdateReadPattern(offset, n);
|
||||
uint64_t offset_in_buffer = offset - buffer_offset_;
|
||||
*result = Slice(buffer_.BufferStart() + offset_in_buffer, n);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO akanksha: Merge this function with TryReadFromCache once async
|
||||
// functionality is stable.
|
||||
bool FilePrefetchBuffer::TryReadFromCacheAsync(
|
||||
const IOOptions& opts, RandomAccessFileReader* reader, uint64_t offset,
|
||||
size_t n, Slice* result, Status* status,
|
||||
Env::IOPriority rate_limiter_priority, bool for_compaction /* = false */,
|
||||
FileSystem* fs) {
|
||||
if (track_min_offset_ && offset < min_offset_read_) {
|
||||
min_offset_read_ = static_cast<size_t>(offset);
|
||||
}
|
||||
if (!enable_ || (offset < bufs_[curr_].offset_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool prefetched = false;
|
||||
bool copy_to_third_buffer = false;
|
||||
// If the buffer contains only a few of the requested bytes:
|
||||
// If readahead is enabled: prefetch the remaining bytes + readahead bytes
|
||||
// and satisfy the request.
|
||||
// If readahead is not enabled: return false.
|
||||
TEST_SYNC_POINT_CALLBACK("FilePrefetchBuffer::TryReadFromCache",
|
||||
&readahead_size_);
|
||||
if (offset + n > bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize()) {
|
||||
if (readahead_size_ > 0) {
|
||||
Status s;
|
||||
assert(reader != nullptr);
|
||||
assert(max_readahead_size_ >= readahead_size_);
|
||||
if (for_compaction) {
|
||||
s = Prefetch(opts, reader, offset, std::max(n, readahead_size_),
|
||||
rate_limiter_priority);
|
||||
} else {
|
||||
if (implicit_auto_readahead_) {
|
||||
if (!IsEligibleForPrefetch(offset, n)) {
|
||||
// Ignore status as Prefetch is not called.
|
||||
s.PermitUncheckedError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (implicit_auto_readahead_ && async_io_) {
|
||||
// Prefetch n + readahead_size_/2 synchronously as remaining
|
||||
// readahead_size_/2 will be prefetched asynchronously.
|
||||
s = PrefetchAsync(opts, reader, fs, offset, n, readahead_size_ / 2,
|
||||
rate_limiter_priority, copy_to_third_buffer);
|
||||
} else {
|
||||
s = Prefetch(opts, reader, offset, n + readahead_size_,
|
||||
rate_limiter_priority);
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
if (status) {
|
||||
*status = s;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
IGNORE_STATUS_IF_ERROR(s);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
prefetched = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
UpdateReadPattern(offset, n, false /*decrease_readaheadsize*/);
|
||||
|
||||
uint32_t index = curr_;
|
||||
if (copy_to_third_buffer) {
|
||||
index = 2;
|
||||
}
|
||||
uint64_t offset_in_buffer = offset - bufs_[index].offset_;
|
||||
*result = Slice(bufs_[index].buffer_.BufferStart() + offset_in_buffer, n);
|
||||
if (prefetched) {
|
||||
readahead_size_ = std::min(max_readahead_size_, readahead_size_ * 2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilePrefetchBuffer::PrefetchAsyncCallback(const FSReadRequest& req,
|
||||
void* /*cb_arg*/) {
|
||||
async_read_in_progress_ = false;
|
||||
uint32_t index = curr_ ^ 1;
|
||||
if (req.status.ok()) {
|
||||
if (req.offset + req.result.size() <=
|
||||
bufs_[index].offset_ + bufs_[index].buffer_.CurrentSize()) {
|
||||
// All requested bytes are already in the buffer. So no need to update.
|
||||
return;
|
||||
}
|
||||
if (req.offset < bufs_[index].offset_) {
|
||||
// Next block to be read has changed (Recent read was not a sequential
|
||||
// read). So ignore this read.
|
||||
return;
|
||||
}
|
||||
size_t current_size = bufs_[index].buffer_.CurrentSize();
|
||||
bufs_[index].buffer_.Size(current_size + req.result.size());
|
||||
}
|
||||
|
||||
// Release io_handle_.
|
||||
if (io_handle_ != nullptr && del_fn_ != nullptr) {
|
||||
del_fn_(io_handle_);
|
||||
io_handle_ = nullptr;
|
||||
del_fn_ = nullptr;
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+18
-100
@@ -16,7 +16,6 @@
|
||||
#include "file/readahead_file_info.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/aligned_buffer.h"
|
||||
|
||||
@@ -27,11 +26,6 @@ namespace ROCKSDB_NAMESPACE {
|
||||
struct IOOptions;
|
||||
class RandomAccessFileReader;
|
||||
|
||||
struct BufferInfo {
|
||||
AlignedBuffer buffer_;
|
||||
uint64_t offset_ = 0;
|
||||
};
|
||||
|
||||
// FilePrefetchBuffer is a smart buffer to store and read data from a file.
|
||||
class FilePrefetchBuffer {
|
||||
public:
|
||||
@@ -54,9 +48,6 @@ class FilePrefetchBuffer {
|
||||
// it. Used for adaptable readahead of the file footer/metadata.
|
||||
// implicit_auto_readahead : Readahead is enabled implicitly by rocksdb after
|
||||
// doing sequential scans for two times.
|
||||
// async_io : When async_io is enabled, if it's implicit_auto_readahead, it
|
||||
// prefetches data asynchronously in second buffer while curr_ is being
|
||||
// consumed.
|
||||
//
|
||||
// Automatic readhead is enabled for a file if readahead_size
|
||||
// and max_readahead_size are passed in.
|
||||
@@ -64,9 +55,8 @@ class FilePrefetchBuffer {
|
||||
// `Prefetch` to load data into the buffer.
|
||||
FilePrefetchBuffer(size_t readahead_size = 0, size_t max_readahead_size = 0,
|
||||
bool enable = true, bool track_min_offset = false,
|
||||
bool implicit_auto_readahead = false,
|
||||
bool async_io = false)
|
||||
: curr_(0),
|
||||
bool implicit_auto_readahead = false)
|
||||
: buffer_offset_(0),
|
||||
readahead_size_(readahead_size),
|
||||
max_readahead_size_(max_readahead_size),
|
||||
min_offset_read_(port::kMaxSizet),
|
||||
@@ -75,16 +65,7 @@ class FilePrefetchBuffer {
|
||||
implicit_auto_readahead_(implicit_auto_readahead),
|
||||
prev_offset_(0),
|
||||
prev_len_(0),
|
||||
num_file_reads_(kMinNumFileReadsToStartAutoReadahead + 1),
|
||||
io_handle_(nullptr),
|
||||
del_fn_(nullptr),
|
||||
async_read_in_progress_(false),
|
||||
async_io_(async_io) {
|
||||
// If async_io_ is enabled, data is asynchronously filled in second buffer
|
||||
// while curr_ is being consumed. If data is overlapping in two buffers,
|
||||
// data is copied to third buffer to return continuous buffer.
|
||||
bufs_.resize(3);
|
||||
}
|
||||
num_file_reads_(kMinNumFileReadsToStartAutoReadahead + 1) {}
|
||||
|
||||
// Load data into the buffer from a file.
|
||||
// reader : the file reader.
|
||||
@@ -92,19 +73,10 @@ class FilePrefetchBuffer {
|
||||
// n : the number of bytes to read.
|
||||
// rate_limiter_priority : rate limiting priority, or `Env::IO_TOTAL` to
|
||||
// bypass.
|
||||
// is_async_read : if the data should be prefetched by calling read
|
||||
// asynchronously. It should be set true when called
|
||||
// from TryReadFromCache.
|
||||
Status Prefetch(const IOOptions& opts, RandomAccessFileReader* reader,
|
||||
uint64_t offset, size_t n,
|
||||
Env::IOPriority rate_limiter_priority);
|
||||
|
||||
Status PrefetchAsync(const IOOptions& opts, RandomAccessFileReader* reader,
|
||||
FileSystem* fs, uint64_t offset, size_t length,
|
||||
size_t readahead_size,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
bool& copy_to_third_buffer);
|
||||
|
||||
// Tries returning the data for a file read from this buffer if that data is
|
||||
// in the buffer.
|
||||
// It handles tracking the minimum read offset if track_min_offset = true.
|
||||
@@ -125,20 +97,14 @@ class FilePrefetchBuffer {
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
bool for_compaction = false);
|
||||
|
||||
bool TryReadFromCacheAsync(const IOOptions& opts,
|
||||
RandomAccessFileReader* reader, uint64_t offset,
|
||||
size_t n, Slice* result, Status* status,
|
||||
Env::IOPriority rate_limiter_priority,
|
||||
bool for_compaction /* = false */, FileSystem* fs);
|
||||
|
||||
// The minimum `offset` ever passed to TryReadFromCache(). This will nly be
|
||||
// tracked if track_min_offset = true.
|
||||
size_t min_offset_read() const { return min_offset_read_; }
|
||||
|
||||
// Called in case of implicit auto prefetching.
|
||||
void UpdateReadPattern(const uint64_t& offset, const size_t& len,
|
||||
bool decrease_readaheadsize) {
|
||||
if (decrease_readaheadsize) {
|
||||
bool is_adaptive_readahead = false) {
|
||||
if (is_adaptive_readahead) {
|
||||
// Since this block was eligible for prefetch but it was found in
|
||||
// cache, so check and decrease the readahead_size by 8KB (default)
|
||||
// if eligible.
|
||||
@@ -148,6 +114,16 @@ class FilePrefetchBuffer {
|
||||
prev_len_ = len;
|
||||
}
|
||||
|
||||
bool IsBlockSequential(const size_t& offset) {
|
||||
return (prev_len_ == 0 || (prev_offset_ + prev_len_ == offset));
|
||||
}
|
||||
|
||||
// Called in case of implicit auto prefetching.
|
||||
void ResetValues() {
|
||||
num_file_reads_ = 1;
|
||||
readahead_size_ = kInitAutoReadaheadSize;
|
||||
}
|
||||
|
||||
void GetReadaheadState(ReadaheadFileInfo::ReadaheadInfo* readahead_info) {
|
||||
readahead_info->readahead_size = readahead_size_;
|
||||
readahead_info->num_file_reads = num_file_reads_;
|
||||
@@ -165,8 +141,7 @@ class FilePrefetchBuffer {
|
||||
// - num_file_reads_ + 1 (including this read) >
|
||||
// kMinNumFileReadsToStartAutoReadahead
|
||||
if (implicit_auto_readahead_ && readahead_size_ > 0) {
|
||||
if ((offset + size >
|
||||
bufs_[curr_].offset_ + bufs_[curr_].buffer_.CurrentSize()) &&
|
||||
if ((offset + size > buffer_offset_ + buffer_.CurrentSize()) &&
|
||||
IsBlockSequential(offset) &&
|
||||
(num_file_reads_ + 1 > kMinNumFileReadsToStartAutoReadahead)) {
|
||||
size_t initial_auto_readahead_size = kInitAutoReadaheadSize;
|
||||
@@ -177,59 +152,9 @@ class FilePrefetchBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEligibleForPrefetch(uint64_t offset, size_t n) {
|
||||
// Prefetch only if this read is sequential otherwise reset readahead_size_
|
||||
// to initial value.
|
||||
if (!IsBlockSequential(offset)) {
|
||||
UpdateReadPattern(offset, n, false /*decrease_readaheadsize*/);
|
||||
ResetValues();
|
||||
return false;
|
||||
}
|
||||
num_file_reads_++;
|
||||
if (num_file_reads_ <= kMinNumFileReadsToStartAutoReadahead) {
|
||||
UpdateReadPattern(offset, n, false /*decrease_readaheadsize*/);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback function passed to underlying FS in case of asynchronous reads.
|
||||
void PrefetchAsyncCallback(const FSReadRequest& req, void* cb_arg);
|
||||
|
||||
private:
|
||||
// Calculates roundoff offset and length to be prefetched based on alignment
|
||||
// and data present in buffer_. It also allocates new buffer or refit tail if
|
||||
// required.
|
||||
void CalculateOffsetAndLen(size_t alignment, uint64_t offset,
|
||||
size_t roundup_len, size_t index, bool refit_tail,
|
||||
uint64_t& chunk_len);
|
||||
|
||||
Status Read(const IOOptions& opts, RandomAccessFileReader* reader,
|
||||
Env::IOPriority rate_limiter_priority, uint64_t read_len,
|
||||
uint64_t chunk_len, uint64_t rounddown_start, uint32_t index);
|
||||
|
||||
Status ReadAsync(const IOOptions& opts, RandomAccessFileReader* reader,
|
||||
Env::IOPriority rate_limiter_priority, uint64_t read_len,
|
||||
uint64_t chunk_len, uint64_t rounddown_start,
|
||||
uint32_t index);
|
||||
|
||||
// Copy the data from src to third buffer.
|
||||
void CopyDataToBuffer(uint32_t src, uint64_t& offset, size_t& length);
|
||||
|
||||
bool IsBlockSequential(const size_t& offset) {
|
||||
return (prev_len_ == 0 || (prev_offset_ + prev_len_ == offset));
|
||||
}
|
||||
|
||||
// Called in case of implicit auto prefetching.
|
||||
void ResetValues() {
|
||||
num_file_reads_ = 1;
|
||||
readahead_size_ = kInitAutoReadaheadSize;
|
||||
}
|
||||
|
||||
std::vector<BufferInfo> bufs_;
|
||||
// curr_ represents the index for bufs_ indicating which buffer is being
|
||||
// consumed currently.
|
||||
uint32_t curr_;
|
||||
AlignedBuffer buffer_;
|
||||
uint64_t buffer_offset_;
|
||||
size_t readahead_size_;
|
||||
// FilePrefetchBuffer object won't be created from Iterator flow if
|
||||
// max_readahead_size_ = 0.
|
||||
@@ -249,12 +174,5 @@ class FilePrefetchBuffer {
|
||||
uint64_t prev_offset_;
|
||||
size_t prev_len_;
|
||||
int64_t num_file_reads_;
|
||||
|
||||
// io_handle_ is allocated and used by underlying file system in case of
|
||||
// asynchronous reads.
|
||||
void* io_handle_;
|
||||
IOHandleDeleter del_fn_;
|
||||
bool async_read_in_progress_;
|
||||
bool async_io_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -718,11 +718,9 @@ TEST_P(PrefetchTest1, DBIterLevelReadAhead) {
|
||||
|
||||
WriteBatch batch;
|
||||
Random rnd(309);
|
||||
int total_keys = 0;
|
||||
for (int j = 0; j < 5; j++) {
|
||||
for (int i = j * kNumKeys; i < (j + 1) * kNumKeys; i++) {
|
||||
ASSERT_OK(batch.Put(BuildKey(i), rnd.RandomString(1000)));
|
||||
total_keys++;
|
||||
}
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -763,16 +761,12 @@ TEST_P(PrefetchTest1, DBIterLevelReadAhead) {
|
||||
ReadOptions ro;
|
||||
if (is_adaptive_readahead) {
|
||||
ro.adaptive_readahead = true;
|
||||
// TODO akanksha: Remove after adding new units.
|
||||
ro.async_io = true;
|
||||
}
|
||||
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ro));
|
||||
int num_keys = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
num_keys++;
|
||||
}
|
||||
ASSERT_EQ(num_keys, total_keys);
|
||||
|
||||
ASSERT_GT(buff_prefetch_count, 0);
|
||||
buff_prefetch_count = 0;
|
||||
@@ -860,8 +854,6 @@ TEST_P(PrefetchTest2, NonSequentialReads) {
|
||||
// Iterate until prefetch is done.
|
||||
ReadOptions ro;
|
||||
ro.adaptive_readahead = true;
|
||||
// TODO akanksha: Remove after adding new units.
|
||||
ro.async_io = true;
|
||||
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ro));
|
||||
iter->SeekToFirst();
|
||||
while (iter->Valid() && buff_prefetch_count == 0) {
|
||||
@@ -948,8 +940,6 @@ TEST_P(PrefetchTest2, DecreaseReadAheadIfInCache) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ReadOptions ro;
|
||||
ro.adaptive_readahead = true;
|
||||
// TODO akanksha: Remove after adding new units.
|
||||
ro.async_io = true;
|
||||
{
|
||||
/*
|
||||
* Reseek keys from sequential Data Blocks within same partitioned
|
||||
@@ -968,35 +958,28 @@ TEST_P(PrefetchTest2, DecreaseReadAheadIfInCache) {
|
||||
// After caching, blocks will be read from cache (Sequential blocks)
|
||||
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ro));
|
||||
iter->Seek(BuildKey(0));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Seek(BuildKey(1000));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Seek(BuildKey(1004)); // Prefetch data (not in cache).
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(current_readahead_size, expected_current_readahead_size);
|
||||
|
||||
// Missed one sequential block but 1011 is already in buffer so
|
||||
// readahead will not be reset.
|
||||
iter->Seek(BuildKey(1011));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(current_readahead_size, expected_current_readahead_size);
|
||||
|
||||
// Eligible to Prefetch data (not in buffer) but block is in cache so no
|
||||
// prefetch will happen and will result in decrease in readahead_size.
|
||||
// readahead_size will be 8 * 1024
|
||||
iter->Seek(BuildKey(1015));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
expected_current_readahead_size -= decrease_readahead_size;
|
||||
|
||||
// 1016 is the same block as 1015. So no change in readahead_size.
|
||||
iter->Seek(BuildKey(1016));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
// Prefetch data (not in buffer) but found in cache. So decrease
|
||||
// readahead_size. Since it will 0 after decrementing so readahead_size will
|
||||
// be set to initial value.
|
||||
iter->Seek(BuildKey(1019));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
expected_current_readahead_size = std::max(
|
||||
decrease_readahead_size,
|
||||
(expected_current_readahead_size >= decrease_readahead_size
|
||||
@@ -1005,7 +988,6 @@ TEST_P(PrefetchTest2, DecreaseReadAheadIfInCache) {
|
||||
|
||||
// Prefetch next sequential data.
|
||||
iter->Seek(BuildKey(1022));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(current_readahead_size, expected_current_readahead_size);
|
||||
ASSERT_EQ(buff_prefetch_count, 2);
|
||||
buff_prefetch_count = 0;
|
||||
|
||||
@@ -424,20 +424,4 @@ IOStatus RandomAccessFileReader::PrepareIOOptions(const ReadOptions& ro,
|
||||
return PrepareIOFromReadOptions(ro, SystemClock::Default().get(), opts);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO akanksha: Add perf_times etc.
|
||||
IOStatus RandomAccessFileReader::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn,
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
if (use_direct_io()) {
|
||||
req.status = Read(opts, req.offset, req.len, &(req.result), req.scratch,
|
||||
nullptr /*dbg*/, rate_limiter_priority);
|
||||
cb(req, cb_arg);
|
||||
return IOStatus::OK();
|
||||
}
|
||||
return file_->ReadAsync(req, opts, cb, cb_arg, io_handle, del_fn,
|
||||
nullptr /*dbg*/);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -174,10 +174,5 @@ class RandomAccessFileReader {
|
||||
bool use_direct_io() const { return file_->use_direct_io(); }
|
||||
|
||||
IOStatus PrepareIOOptions(const ReadOptions& ro, IOOptions& opts);
|
||||
|
||||
IOStatus ReadAsync(FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(const FSReadRequest&, void*)> cb,
|
||||
void* cb_arg, void** io_handle, IOHandleDeleter* del_fn,
|
||||
Env::IOPriority rate_limiter_priority);
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+1
-14
@@ -274,19 +274,6 @@ class DB {
|
||||
const std::string& input, std::string* output,
|
||||
const CompactionServiceOptionsOverride& override_options);
|
||||
|
||||
// Experimental and subject to change
|
||||
// Open DB and trim data newer than specified timestamp.
|
||||
// The trim_ts specified the user-defined timestamp trim bound.
|
||||
// This API should only be used at timestamp enabled column families recovery.
|
||||
// If some input column families do not support timestamp, nothing will
|
||||
// be happened to them. The data with timestamp > trim_ts
|
||||
// will be removed after this API returns successfully.
|
||||
static Status OpenAndTrimHistory(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
std::string trim_ts);
|
||||
|
||||
virtual Status Resume() { return Status::NotSupported(); }
|
||||
|
||||
// Close the DB by releasing resources, closing files etc. This should be
|
||||
@@ -793,7 +780,7 @@ class DB {
|
||||
// snapshot is no longer needed.
|
||||
//
|
||||
// nullptr will be returned if the DB fails to take a snapshot or does
|
||||
// not support snapshot (eg: inplace_update_support enabled).
|
||||
// not support snapshot.
|
||||
virtual const Snapshot* GetSnapshot() = 0;
|
||||
|
||||
// Release a previously acquired snapshot. The caller must not
|
||||
|
||||
@@ -25,32 +25,5 @@ Status SuggestCompactRange(DB* db, const Slice* begin, const Slice* end);
|
||||
Status PromoteL0(DB* db, ColumnFamilyHandle* column_family,
|
||||
int target_level = 1);
|
||||
|
||||
struct UpdateManifestForFilesStateOptions {
|
||||
// When true, read current file temperatures from FileSystem and update in
|
||||
// DB manifest when a temperature other than Unknown is reported and
|
||||
// inconsistent with manifest.
|
||||
bool update_temperatures = true;
|
||||
|
||||
// TODO: new_checksums: to update files to latest file checksum algorithm
|
||||
};
|
||||
|
||||
// Utility for updating manifest of DB directory (not open) for current state
|
||||
// of files on filesystem. See UpdateManifestForFilesStateOptions.
|
||||
//
|
||||
// To minimize interference with ongoing DB operations, only the following
|
||||
// guarantee is provided, assuming no IO error encountered:
|
||||
// * Only files live in DB at start AND end of call to
|
||||
// UpdateManifestForFilesState() are guaranteed to be updated (as needed) in
|
||||
// manifest.
|
||||
// * For example, new files after start of call to
|
||||
// UpdateManifestForFilesState() might not be updated, but that is not
|
||||
// typically required to achieve goal of manifest consistency/completeness
|
||||
// (because current DB configuration would ensure new files get the desired
|
||||
// consistent metadata).
|
||||
Status UpdateManifestForFilesState(
|
||||
const DBOptions& db_opts, const std::string& db_name,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
const UpdateManifestForFilesStateOptions& opts = {});
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -651,8 +651,7 @@ class FileSystem : public Customizable {
|
||||
// Underlying FS is required to support Poll API. Poll implementation should
|
||||
// ensure that the callback gets called at IO completion, and return only
|
||||
// after the callback has been called.
|
||||
// If Poll returns partial results for any reads, its caller reponsibility to
|
||||
// call Read or ReadAsync in order to get the remaining bytes.
|
||||
//
|
||||
//
|
||||
// Default implementation is to return IOStatus::OK.
|
||||
|
||||
|
||||
@@ -90,19 +90,6 @@ class FilterPolicy : public Customizable {
|
||||
virtual ~FilterPolicy();
|
||||
static const char* Type() { return "FilterPolicy"; }
|
||||
|
||||
// The name used for identifying whether a filter on disk is readable
|
||||
// by this FilterPolicy. If this FilterPolicy is part of a family that
|
||||
// can read each others filters, such as built-in BloomFilterPolcy and
|
||||
// RibbonFilterPolicy, the CompatibilityName is a shared family name,
|
||||
// while kinds of filters in the family can have distinct Customizable
|
||||
// Names. This function is pure virtual so that wrappers around built-in
|
||||
// policies are prompted to defer to CompatibilityName() of the wrapped
|
||||
// policy, which is important for compatibility.
|
||||
//
|
||||
// For custom filter policies that are not part of a read-compatible
|
||||
// family (rare), implementations may return Name().
|
||||
virtual const char* CompatibilityName() const = 0;
|
||||
|
||||
// Creates a new FilterPolicy based on the input value string and returns the
|
||||
// result The value might be an ID, and ID with properties, or an old-style
|
||||
// policy string.
|
||||
|
||||
@@ -1598,15 +1598,6 @@ struct ReadOptions {
|
||||
// Default: `Env::IO_TOTAL`.
|
||||
Env::IOPriority rate_limiter_priority = Env::IO_TOTAL;
|
||||
|
||||
// Experimental
|
||||
//
|
||||
// If async_io is enabled, RocksDB will prefetch some of data asynchronously.
|
||||
// RocksDB apply it if reads are sequential and its internal automatic
|
||||
// prefetching.
|
||||
//
|
||||
// Default: false
|
||||
bool async_io;
|
||||
|
||||
ReadOptions();
|
||||
ReadOptions(bool cksum, bool cache);
|
||||
};
|
||||
|
||||
@@ -204,22 +204,6 @@ struct BackupEngineOptions {
|
||||
// and share_table_files are true.
|
||||
ShareFilesNaming share_files_with_checksum_naming;
|
||||
|
||||
// Major schema version to use when writing backup meta files
|
||||
// 1 (default) - compatible with very old versions of RocksDB.
|
||||
// 2 - can be read by RocksDB versions >= 6.19.0. Minimum schema version for
|
||||
// * (Experimental) saving and restoring file temperature metadata
|
||||
int schema_version = 1;
|
||||
|
||||
// (Experimental - subject to change or removal) When taking a backup and
|
||||
// saving file temperature info (minimum schema_version is 2), there are
|
||||
// two potential sources of truth for the placement of files into temperature
|
||||
// tiers: (a) the current file temperature reported by the FileSystem or
|
||||
// (b) the expected file temperature recorded in DB manifest. When this
|
||||
// option is false (default), (b) overrides (a) if both are not UNKNOWN.
|
||||
// When true, (a) overrides (b) if both are not UNKNOWN. Regardless of this
|
||||
// setting, a known temperature overrides UNKNOWN.
|
||||
bool current_temperatures_override_manifest = false;
|
||||
|
||||
void Dump(Logger* logger) const;
|
||||
|
||||
explicit BackupEngineOptions(
|
||||
|
||||
@@ -226,12 +226,8 @@ struct TransactionDBOptions {
|
||||
|
||||
private:
|
||||
// 128 entries
|
||||
// Should the default value change, please also update wp_snapshot_cache_bits
|
||||
// in db_stress_gflags.cc
|
||||
size_t wp_snapshot_cache_bits = static_cast<size_t>(7);
|
||||
// 8m entry, 64MB size
|
||||
// Should the default value change, please also update wp_commit_cache_bits
|
||||
// in db_stress_gflags.cc
|
||||
size_t wp_commit_cache_bits = static_cast<size_t>(23);
|
||||
|
||||
// For testing, whether transaction name should be auto-generated or not. This
|
||||
@@ -243,7 +239,6 @@ struct TransactionDBOptions {
|
||||
friend class WritePreparedTransactionTestBase;
|
||||
friend class TransactionTestBase;
|
||||
friend class MySQLStyleTransactionTest;
|
||||
friend class StressTest;
|
||||
};
|
||||
|
||||
struct TransactionOptions {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
#define ROCKSDB_MAJOR 7
|
||||
#define ROCKSDB_MINOR 1
|
||||
#define ROCKSDB_MINOR 0
|
||||
#define ROCKSDB_PATCH 0
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
|
||||
@@ -4261,10 +4261,9 @@ void Java_org_rocksdb_ColumnFamilyOptions_setSstPartitionerFactory(
|
||||
JNIEnv*, jobject, jlong jhandle, jlong factory_handle) {
|
||||
auto* options =
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyOptions*>(jhandle);
|
||||
auto factory = reinterpret_cast<
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::SstPartitionerFactory>*>(
|
||||
auto* factory = reinterpret_cast<ROCKSDB_NAMESPACE::SstPartitionerFactory*>(
|
||||
factory_handle);
|
||||
options->sst_partitioner_factory = *factory;
|
||||
options->sst_partitioner_factory.reset(factory);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
@@ -22,7 +21,7 @@ public class SstPartitionerTest {
|
||||
@Rule public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void sstFixedPrefix() throws RocksDBException {
|
||||
public void sstFixedPrefix() throws InterruptedException, RocksDBException {
|
||||
try (SstPartitionerFixedPrefixFactory factory = new SstPartitionerFixedPrefixFactory(4);
|
||||
final Options opt =
|
||||
new Options().setCreateIfMissing(true).setSstPartitionerFactory(factory);
|
||||
@@ -32,8 +31,7 @@ public class SstPartitionerTest {
|
||||
db.put("bbbb1".getBytes(), "B".getBytes());
|
||||
db.flush(new FlushOptions());
|
||||
|
||||
db.put("aaaa0".getBytes(), "A2".getBytes());
|
||||
db.put("aaaa2".getBytes(), "A2".getBytes());
|
||||
db.put("aaaa1".getBytes(), "A2".getBytes());
|
||||
db.flush(new FlushOptions());
|
||||
|
||||
db.compactRange();
|
||||
@@ -42,31 +40,4 @@ public class SstPartitionerTest {
|
||||
assertThat(metadata.size()).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sstFixedPrefixFamily() throws RocksDBException {
|
||||
final byte[] cfName = "new_cf".getBytes(UTF_8);
|
||||
final ColumnFamilyDescriptor cfDescriptor = new ColumnFamilyDescriptor(cfName,
|
||||
new ColumnFamilyOptions().setSstPartitionerFactory(
|
||||
new SstPartitionerFixedPrefixFactory(4)));
|
||||
|
||||
try (final Options opt = new Options().setCreateIfMissing(true);
|
||||
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) {
|
||||
final ColumnFamilyHandle columnFamilyHandle = db.createColumnFamily(cfDescriptor);
|
||||
|
||||
// writing (long)100 under key
|
||||
db.put(columnFamilyHandle, "aaaa1".getBytes(), "A".getBytes());
|
||||
db.put(columnFamilyHandle, "bbbb1".getBytes(), "B".getBytes());
|
||||
db.flush(new FlushOptions(), columnFamilyHandle);
|
||||
|
||||
db.put(columnFamilyHandle, "aaaa0".getBytes(), "A2".getBytes());
|
||||
db.put(columnFamilyHandle, "aaaa2".getBytes(), "A2".getBytes());
|
||||
db.flush(new FlushOptions(), columnFamilyHandle);
|
||||
|
||||
db.compactRange(columnFamilyHandle);
|
||||
|
||||
List<LiveFileMetaData> metadata = db.getLiveFilesMetaData();
|
||||
assertThat(metadata.size()).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1487,7 +1487,6 @@ class MockFilterPolicy : public FilterPolicy {
|
||||
public:
|
||||
static const char* kClassName() { return "MockFilterPolicy"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
const char* CompatibilityName() const override { return Name(); }
|
||||
FilterBitsBuilder* GetBuilderWithContext(
|
||||
const FilterBuildingContext&) const override {
|
||||
return nullptr;
|
||||
|
||||
+2
-4
@@ -665,8 +665,7 @@ ReadOptions::ReadOptions()
|
||||
deadline(std::chrono::microseconds::zero()),
|
||||
io_timeout(std::chrono::microseconds::zero()),
|
||||
value_size_soft_limit(std::numeric_limits<uint64_t>::max()),
|
||||
adaptive_readahead(false),
|
||||
async_io(false) {}
|
||||
adaptive_readahead(false) {}
|
||||
|
||||
ReadOptions::ReadOptions(bool cksum, bool cache)
|
||||
: snapshot(nullptr),
|
||||
@@ -690,7 +689,6 @@ ReadOptions::ReadOptions(bool cksum, bool cache)
|
||||
deadline(std::chrono::microseconds::zero()),
|
||||
io_timeout(std::chrono::microseconds::zero()),
|
||||
value_size_soft_limit(std::numeric_limits<uint64_t>::max()),
|
||||
adaptive_readahead(false),
|
||||
async_io(false) {}
|
||||
adaptive_readahead(false) {}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -321,12 +321,6 @@ std::map<CompactionStopStyle, std::string>
|
||||
{kCompactionStopStyleSimilarSize, "kCompactionStopStyleSimilarSize"},
|
||||
{kCompactionStopStyleTotalSize, "kCompactionStopStyleTotalSize"}};
|
||||
|
||||
std::map<Temperature, std::string> OptionsHelper::temperature_to_string = {
|
||||
{Temperature::kUnknown, "kUnknown"},
|
||||
{Temperature::kHot, "kHot"},
|
||||
{Temperature::kWarm, "kWarm"},
|
||||
{Temperature::kCold, "kCold"}};
|
||||
|
||||
std::unordered_map<std::string, ChecksumType>
|
||||
OptionsHelper::checksum_type_string_map = {{"kNoChecksum", kNoChecksum},
|
||||
{"kCRC32c", kCRC32c},
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
@@ -78,7 +77,6 @@ struct OptionsHelper {
|
||||
static std::map<CompactionPri, std::string> compaction_pri_to_string;
|
||||
static std::map<CompactionStopStyle, std::string>
|
||||
compaction_stop_style_to_string;
|
||||
static std::map<Temperature, std::string> temperature_to_string;
|
||||
static std::unordered_map<std::string, ChecksumType> checksum_type_string_map;
|
||||
static std::unordered_map<std::string, CompressionType>
|
||||
compression_type_string_map;
|
||||
@@ -100,7 +98,6 @@ static auto& compaction_style_to_string =
|
||||
static auto& compaction_pri_to_string = OptionsHelper::compaction_pri_to_string;
|
||||
static auto& compaction_stop_style_to_string =
|
||||
OptionsHelper::compaction_stop_style_to_string;
|
||||
static auto& temperature_to_string = OptionsHelper::temperature_to_string;
|
||||
static auto& checksum_type_string_map = OptionsHelper::checksum_type_string_map;
|
||||
#ifndef ROCKSDB_LITE
|
||||
static auto& compaction_stop_style_string_map =
|
||||
|
||||
+1
-3
@@ -9,8 +9,6 @@
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
#include "port/win/env_win.h"
|
||||
|
||||
#include <direct.h> // _rmdir, _mkdir, _getcwd
|
||||
#include <errno.h>
|
||||
#include <io.h> // _access
|
||||
@@ -19,7 +17,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
@@ -30,6 +27,7 @@
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/port_dirent.h"
|
||||
#include "port/win/env_win.h"
|
||||
#include "port/win/io_win.h"
|
||||
#include "port/win/win_logger.h"
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
ifndef PYTHON
|
||||
|
||||
# Default to python3. Some distros like CentOS 8 do not have `python`.
|
||||
ifeq ($(origin PYTHON), undefined)
|
||||
PYTHON := $(shell which python3 || which python || echo python3)
|
||||
endif
|
||||
export PYTHON
|
||||
|
||||
endif
|
||||
@@ -1605,7 +1605,7 @@ void BlockBasedTableBuilder::WriteFilterBlock(
|
||||
? BlockBasedTable::kPartitionedFilterBlockPrefix
|
||||
: BlockBasedTable::kFullFilterBlockPrefix;
|
||||
}
|
||||
key.append(rep_->table_options.filter_policy->CompatibilityName());
|
||||
key.append(rep_->table_options.filter_policy->Name());
|
||||
meta_index_builder->Add(key, filter_block_handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,9 +232,9 @@ void BlockBasedTableIterator::InitDataBlock() {
|
||||
// Enabled after 2 sequential IOs when ReadOptions.readahead_size == 0.
|
||||
// Explicit user requested readahead:
|
||||
// Enabled from the very first IO when ReadOptions.readahead_size is set.
|
||||
block_prefetcher_.PrefetchIfNeeded(
|
||||
rep, data_block_handle, read_options_.readahead_size, is_for_compaction,
|
||||
read_options_.async_io);
|
||||
block_prefetcher_.PrefetchIfNeeded(rep, data_block_handle,
|
||||
read_options_.readahead_size,
|
||||
is_for_compaction);
|
||||
Status s;
|
||||
table_->NewDataBlockIterator<DataBlockIter>(
|
||||
read_options_, data_block_handle, &block_iter_, BlockType::kData,
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -51,7 +50,6 @@
|
||||
#include "table/block_based/block_prefix_index.h"
|
||||
#include "table/block_based/block_type.h"
|
||||
#include "table/block_based/filter_block.h"
|
||||
#include "table/block_based/filter_policy_internal.h"
|
||||
#include "table/block_based/full_filter_block.h"
|
||||
#include "table/block_based/hash_index_reader.h"
|
||||
#include "table/block_based/partitioned_filter_block.h"
|
||||
@@ -899,59 +897,33 @@ Status BlockBasedTable::PrefetchIndexAndFilterBlocks(
|
||||
const BlockBasedTableOptions& table_options, const int level,
|
||||
size_t file_size, size_t max_file_size_for_l0_meta_pin,
|
||||
BlockCacheLookupContext* lookup_context) {
|
||||
Status s;
|
||||
|
||||
// Find filter handle and filter type
|
||||
if (rep_->filter_policy) {
|
||||
auto name = rep_->filter_policy->CompatibilityName();
|
||||
bool builtin_compatible =
|
||||
strcmp(name, BuiltinFilterPolicy::kCompatibilityName()) == 0;
|
||||
|
||||
for (const auto& [filter_type, prefix] :
|
||||
{std::make_pair(Rep::FilterType::kFullFilter, kFullFilterBlockPrefix),
|
||||
std::make_pair(Rep::FilterType::kPartitionedFilter,
|
||||
kPartitionedFilterBlockPrefix),
|
||||
std::make_pair(Rep::FilterType::kBlockFilter, kFilterBlockPrefix)}) {
|
||||
if (builtin_compatible) {
|
||||
// This code is only here to deal with a hiccup in early 7.0.x where
|
||||
// there was an unintentional name change in the SST files metadata.
|
||||
// It should be OK to remove this in the future (late 2022) and just
|
||||
// have the 'else' code.
|
||||
// NOTE: the test:: names below are likely not needed but included
|
||||
// out of caution
|
||||
static const std::unordered_set<std::string> kBuiltinNameAndAliases = {
|
||||
BuiltinFilterPolicy::kCompatibilityName(),
|
||||
test::LegacyBloomFilterPolicy::kClassName(),
|
||||
test::FastLocalBloomFilterPolicy::kClassName(),
|
||||
test::Standard128RibbonFilterPolicy::kClassName(),
|
||||
DeprecatedBlockBasedBloomFilterPolicy::kClassName(),
|
||||
BloomFilterPolicy::kClassName(),
|
||||
RibbonFilterPolicy::kClassName(),
|
||||
};
|
||||
|
||||
// For efficiency, do a prefix seek and see if the first match is
|
||||
// good.
|
||||
meta_iter->Seek(prefix);
|
||||
if (meta_iter->status().ok() && meta_iter->Valid()) {
|
||||
Slice key = meta_iter->key();
|
||||
if (key.starts_with(prefix)) {
|
||||
key.remove_prefix(prefix.size());
|
||||
if (kBuiltinNameAndAliases.find(key.ToString()) !=
|
||||
kBuiltinNameAndAliases.end()) {
|
||||
Slice v = meta_iter->value();
|
||||
Status s = rep_->filter_handle.DecodeFrom(&v);
|
||||
if (s.ok()) {
|
||||
rep_->filter_type = filter_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::string filter_block_key = prefix + name;
|
||||
if (FindMetaBlock(meta_iter, filter_block_key, &rep_->filter_handle)
|
||||
.ok()) {
|
||||
rep_->filter_type = filter_type;
|
||||
for (auto filter_type :
|
||||
{Rep::FilterType::kFullFilter, Rep::FilterType::kPartitionedFilter,
|
||||
Rep::FilterType::kBlockFilter}) {
|
||||
std::string prefix;
|
||||
switch (filter_type) {
|
||||
case Rep::FilterType::kFullFilter:
|
||||
prefix = kFullFilterBlockPrefix;
|
||||
break;
|
||||
}
|
||||
case Rep::FilterType::kPartitionedFilter:
|
||||
prefix = kPartitionedFilterBlockPrefix;
|
||||
break;
|
||||
case Rep::FilterType::kBlockFilter:
|
||||
prefix = kFilterBlockPrefix;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
std::string filter_block_key = prefix;
|
||||
filter_block_key.append(rep_->filter_policy->Name());
|
||||
if (FindMetaBlock(meta_iter, filter_block_key, &rep_->filter_handle)
|
||||
.ok()) {
|
||||
rep_->filter_type = filter_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -960,8 +932,8 @@ Status BlockBasedTable::PrefetchIndexAndFilterBlocks(
|
||||
rep_->index_type == BlockBasedTableOptions::kTwoLevelIndexSearch);
|
||||
|
||||
// Find compression dictionary handle
|
||||
Status s = FindOptionalMetaBlock(meta_iter, kCompressionDictBlockName,
|
||||
&rep_->compression_dict_handle);
|
||||
s = FindOptionalMetaBlock(meta_iter, kCompressionDictBlockName,
|
||||
&rep_->compression_dict_handle);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1500,9 +1472,9 @@ Status BlockBasedTable::MaybeReadBlockAndLoadToCache(
|
||||
// Update the block details so that PrefetchBuffer can use the read
|
||||
// pattern to determine if reads are sequential or not for
|
||||
// prefetching. It should also take in account blocks read from cache.
|
||||
prefetch_buffer->UpdateReadPattern(
|
||||
handle.offset(), BlockSizeWithTrailer(handle),
|
||||
ro.adaptive_readahead /*decrease_readahead_size*/);
|
||||
prefetch_buffer->UpdateReadPattern(handle.offset(),
|
||||
BlockSizeWithTrailer(handle),
|
||||
ro.adaptive_readahead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,21 +652,20 @@ struct BlockBasedTable::Rep {
|
||||
void CreateFilePrefetchBuffer(size_t readahead_size,
|
||||
size_t max_readahead_size,
|
||||
std::unique_ptr<FilePrefetchBuffer>* fpb,
|
||||
bool implicit_auto_readahead,
|
||||
bool async_io) const {
|
||||
bool implicit_auto_readahead) const {
|
||||
fpb->reset(new FilePrefetchBuffer(readahead_size, max_readahead_size,
|
||||
!ioptions.allow_mmap_reads /* enable */,
|
||||
false /* track_min_offset */,
|
||||
implicit_auto_readahead, async_io));
|
||||
implicit_auto_readahead));
|
||||
}
|
||||
|
||||
void CreateFilePrefetchBufferIfNotExists(
|
||||
size_t readahead_size, size_t max_readahead_size,
|
||||
std::unique_ptr<FilePrefetchBuffer>* fpb, bool implicit_auto_readahead,
|
||||
bool async_io) const {
|
||||
std::unique_ptr<FilePrefetchBuffer>* fpb,
|
||||
bool implicit_auto_readahead) const {
|
||||
if (!(*fpb)) {
|
||||
CreateFilePrefetchBuffer(readahead_size, max_readahead_size, fpb,
|
||||
implicit_auto_readahead, async_io);
|
||||
implicit_auto_readahead);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,18 +14,18 @@ namespace ROCKSDB_NAMESPACE {
|
||||
void BlockPrefetcher::PrefetchIfNeeded(const BlockBasedTable::Rep* rep,
|
||||
const BlockHandle& handle,
|
||||
size_t readahead_size,
|
||||
bool is_for_compaction, bool async_io) {
|
||||
bool is_for_compaction) {
|
||||
if (is_for_compaction) {
|
||||
rep->CreateFilePrefetchBufferIfNotExists(
|
||||
compaction_readahead_size_, compaction_readahead_size_,
|
||||
&prefetch_buffer_, false, async_io);
|
||||
rep->CreateFilePrefetchBufferIfNotExists(compaction_readahead_size_,
|
||||
compaction_readahead_size_,
|
||||
&prefetch_buffer_, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Explicit user requested readahead.
|
||||
if (readahead_size > 0) {
|
||||
rep->CreateFilePrefetchBufferIfNotExists(
|
||||
readahead_size, readahead_size, &prefetch_buffer_, false, async_io);
|
||||
rep->CreateFilePrefetchBufferIfNotExists(readahead_size, readahead_size,
|
||||
&prefetch_buffer_, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ void BlockPrefetcher::PrefetchIfNeeded(const BlockBasedTable::Rep* rep,
|
||||
if (rep->file->use_direct_io()) {
|
||||
rep->CreateFilePrefetchBufferIfNotExists(initial_auto_readahead_size_,
|
||||
max_auto_readahead_size,
|
||||
&prefetch_buffer_, true, async_io);
|
||||
&prefetch_buffer_, true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ void BlockPrefetcher::PrefetchIfNeeded(const BlockBasedTable::Rep* rep,
|
||||
if (s.IsNotSupported()) {
|
||||
rep->CreateFilePrefetchBufferIfNotExists(initial_auto_readahead_size_,
|
||||
max_auto_readahead_size,
|
||||
&prefetch_buffer_, true, async_io);
|
||||
&prefetch_buffer_, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class BlockPrefetcher {
|
||||
: compaction_readahead_size_(compaction_readahead_size) {}
|
||||
void PrefetchIfNeeded(const BlockBasedTable::Rep* rep,
|
||||
const BlockHandle& handle, size_t readahead_size,
|
||||
bool is_for_compaction, bool async_io);
|
||||
bool is_for_compaction);
|
||||
FilePrefetchBuffer* prefetch_buffer() { return prefetch_buffer_.get(); }
|
||||
|
||||
void UpdateReadPattern(const uint64_t& offset, const size_t& len) {
|
||||
|
||||
@@ -1325,16 +1325,6 @@ bool BuiltinFilterPolicy::IsInstanceOf(const std::string& name) const {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kBuiltinFilterMetadataName = "rocksdb.BuiltinBloomFilter";
|
||||
|
||||
const char* BuiltinFilterPolicy::kCompatibilityName() {
|
||||
return kBuiltinFilterMetadataName;
|
||||
}
|
||||
|
||||
const char* BuiltinFilterPolicy::CompatibilityName() const {
|
||||
return kBuiltinFilterMetadataName;
|
||||
}
|
||||
|
||||
BloomLikeFilterPolicy::BloomLikeFilterPolicy(double bits_per_key)
|
||||
: warned_(false), aggregate_rounding_balance_(0) {
|
||||
// Sanitize bits_per_key
|
||||
@@ -1382,7 +1372,7 @@ bool BloomLikeFilterPolicy::IsInstanceOf(const std::string& name) const {
|
||||
}
|
||||
|
||||
const char* ReadOnlyBuiltinFilterPolicy::kClassName() {
|
||||
return kBuiltinFilterMetadataName;
|
||||
return "rocksdb.BuiltinBloomFilter";
|
||||
}
|
||||
|
||||
const char* DeprecatedBlockBasedBloomFilterPolicy::kClassName() {
|
||||
|
||||
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user