mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87d4df1f07 | ||
|
|
faae96da2e | ||
|
|
165026074a | ||
|
|
6a060888aa | ||
|
|
d2ebdea566 | ||
|
|
6f7cabeac8 | ||
|
|
adb9bf5179 | ||
|
|
7dd5e91e39 | ||
|
|
e94141d848 | ||
|
|
bcf88d48ce | ||
|
|
f6d01f0f6e | ||
|
|
e223cd46c6 | ||
|
|
c449867236 | ||
|
|
b515a5db3f | ||
|
|
3b736c4aa3 | ||
|
|
088dc7283b | ||
|
|
bf98dcf9a8 | ||
|
|
63a105a481 | ||
|
|
98d8a85624 | ||
|
|
13e1c32a18 | ||
|
|
6a1c2abe9d | ||
|
|
4ce1dc930c | ||
|
|
3f3f4660bd | ||
|
|
6ddfa5f061 | ||
|
|
fc40165614 | ||
|
|
4868c10b44 | ||
|
|
b4e9f5a400 | ||
|
|
2443ebf810 | ||
|
|
a93edea7e5 | ||
|
|
db1dea22b1 | ||
|
|
3d5be596a5 | ||
|
|
f2546b6623 | ||
|
|
4d5ebad971 | ||
|
|
3f5bd46a07 | ||
|
|
c3c0cfc3a8 | ||
|
|
096fb9b67d | ||
|
|
1104eaa35e | ||
|
|
f77b788545 | ||
|
|
fa4978c566 | ||
|
|
e91263edb9 | ||
|
|
dd24bda137 | ||
|
|
7c290f72b8 | ||
|
|
ba022dd44c | ||
|
|
30243c6573 | ||
|
|
122510dc09 | ||
|
|
c0ae5be934 | ||
|
|
d9a441113e | ||
|
|
36c1b0aded | ||
|
|
c4d37da826 | ||
|
|
7622029101 | ||
|
|
179afd5bef | ||
|
|
210c8df820 | ||
|
|
27a2473668 | ||
|
|
583fded565 | ||
|
|
a53ed91691 | ||
|
|
3412195367 | ||
|
|
3fff57fa6a | ||
|
|
1cfdece85d | ||
|
|
d7b8756976 | ||
|
|
ced333ee45 | ||
|
|
965364972d | ||
|
|
c00c16855d | ||
|
|
5bcc184975 | ||
|
|
4aed229fa7 | ||
|
|
13ef21c22e | ||
|
|
5458eda5f0 | ||
|
|
eab876bb49 | ||
|
|
99cc36be9b | ||
|
|
1fa5dff7d1 | ||
|
|
3104e55f29 | ||
|
|
d780e7a561 | ||
|
|
a43481b3d0 | ||
|
|
41849210e9 | ||
|
|
a4ff83d1b2 | ||
|
|
2940acac00 | ||
|
|
f300438c20 | ||
|
|
d1386de632 | ||
|
|
e09b9d0cb9 | ||
|
|
d9c0d44dab | ||
|
|
cb4f4381f6 | ||
|
|
70cb330a4a | ||
|
|
f1ca47b904 | ||
|
|
8e29f243c9 | ||
|
|
5950907a82 | ||
|
|
003197f005 | ||
|
|
ca99a8f153 | ||
|
|
31dfc81e18 |
No files matched your search
@@ -15,30 +15,40 @@ runs:
|
||||
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.1.8;${{ github.workspace }}/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
run: |-
|
||||
# NOTE: if ... Exit $LASTEXITCODE lines needed to exit and report failure
|
||||
echo ===================== Install Dependencies =====================
|
||||
choco install liberica8jdk -y
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
mkdir $Env:THIRDPARTY_HOME
|
||||
cd $Env:THIRDPARTY_HOME
|
||||
echo "Building Snappy dependency..."
|
||||
curl -Lo snappy-1.1.8.zip https://github.com/google/snappy/archive/refs/tags/1.1.8.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
unzip -q snappy-1.1.8.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd snappy-1.1.8
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
msbuild Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ======================== Build RocksDB =========================
|
||||
cd ${{ github.workspace }}
|
||||
$env:Path = $env:JAVA_HOME + ";" + $env:Path
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DJNI=1 ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
msbuild build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ========================= Test RocksDB =========================
|
||||
build_tools\run_ci_db_test.ps1 -SuiteRun arena_test,db_basic_test,db_test,db_test2,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test -Concurrency 16
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ======================== Test RocksJava ========================
|
||||
cd build\java
|
||||
& ctest -C Debug -j 16
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
shell: pwsh
|
||||
+27
-1
@@ -44,6 +44,29 @@ project(rocksdb
|
||||
HOMEPAGE_URL https://rocksdb.org/
|
||||
LANGUAGES CXX C ASM)
|
||||
|
||||
if(APPLE)
|
||||
# On macOS Cmake, when cross-compiling, sometimes CMAKE_SYSTEM_PROCESSOR wrongfully stays
|
||||
# the same as CMAKE_HOST_SYSTEM_PROCESSOR regardless the target CPU.
|
||||
# The manual call to set(CMAKE_SYSTEM_PROCESSOR) has to be set after the project() call.
|
||||
# because project() might reset CMAKE_SYSTEM_PROCESSOR back to the value of CMAKE_HOST_SYSTEM_PROCESSOR.
|
||||
# Check if CMAKE_SYSTEM_PROCESSOR is not equal to CMAKE_OSX_ARCHITECTURES
|
||||
if(NOT CMAKE_OSX_ARCHITECTURES STREQUAL "")
|
||||
if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_OSX_ARCHITECTURES)
|
||||
# Split CMAKE_OSX_ARCHITECTURES into a list
|
||||
string(REPLACE ";" " " ARCH_LIST ${CMAKE_OSX_ARCHITECTURES})
|
||||
separate_arguments(ARCH_LIST UNIX_COMMAND ${ARCH_LIST})
|
||||
# Count the number of architectures
|
||||
list(LENGTH ARCH_LIST ARCH_COUNT)
|
||||
# Ensure that exactly one architecture is specified
|
||||
if(NOT ARCH_COUNT EQUAL 1)
|
||||
message(FATAL_ERROR "CMAKE_OSX_ARCHITECTURES must have exactly one value. Current value: ${CMAKE_OSX_ARCHITECTURES}")
|
||||
endif()
|
||||
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_OSX_ARCHITECTURES})
|
||||
message(STATUS "CMAKE_SYSTEM_PROCESSOR is manually set to ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
@@ -182,7 +205,7 @@ endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4800 /wd4996 /wd4351 /wd4100 /wd4204 /wd4324")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4996 /wd4100 /wd4324")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall -pthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers -Wno-strict-aliasing -Wno-invalid-offsetof")
|
||||
@@ -191,6 +214,7 @@ else()
|
||||
endif()
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
endif()
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
@@ -691,6 +715,7 @@ set(SOURCES
|
||||
db/memtable_list.cc
|
||||
db/merge_helper.cc
|
||||
db/merge_operator.cc
|
||||
db/multi_cf_iterator.cc
|
||||
db/output_validator.cc
|
||||
db/periodic_task_scheduler.cc
|
||||
db/range_del_aggregator.cc
|
||||
@@ -1343,6 +1368,7 @@ if(WITH_TESTS)
|
||||
db/memtable_list_test.cc
|
||||
db/merge_helper_test.cc
|
||||
db/merge_test.cc
|
||||
db/multi_cf_iterator_test.cc
|
||||
db/options_file_test.cc
|
||||
db/perf_context_test.cc
|
||||
db/periodic_task_scheduler_test.cc
|
||||
|
||||
+85
@@ -1,6 +1,91 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 9.1.2 (04/24/2024)
|
||||
### Bug Fixes
|
||||
* Fixed hang in `DisableManualCompactions()` where compactions waiting to be scheduled due to conflicts would not be canceled promptly
|
||||
|
||||
## 9.1.1 (04/17/2024)
|
||||
### Bug Fixes
|
||||
* Fixed Java `SstFileMetaData` to prevent throwing `java.lang.NoSuchMethodError`
|
||||
* Fixed a regression when `ColumnFamilyOptions::max_successive_merges > 0` where the CPU overhead for deciding whether to merge could have increased unless the user had set the option `ColumnFamilyOptions::strict_max_successive_merges`
|
||||
|
||||
## 9.1.0 (03/22/2024)
|
||||
### New Features
|
||||
* Added an option, `GetMergeOperandsOptions::continue_cb`, to give users the ability to end `GetMergeOperands()`'s lookup process before all merge operands were found.
|
||||
* *Add sanity checks for ingesting external files that currently checks if the user key comparator used to create the file is compatible with the column family's user key comparator.
|
||||
*Support ingesting external files for column family that has user-defined timestamps in memtable only enabled.
|
||||
* On file systems that support storage level data checksum and reconstruction, retry SST block reads for point lookups, scans, and flush and compaction if there's a checksum mismatch on the initial read.
|
||||
* Some enhancements and fixes to experimental Temperature handling features, including new `default_write_temperature` CF option and opening an `SstFileWriter` with a temperature.
|
||||
* `WriteBatchWithIndex` now supports wide-column point lookups via the `GetEntityFromBatch` API. See the API comments for more details.
|
||||
* *Implement experimental features: API `Iterator::GetProperty("rocksdb.iterator.write-time")` to allow users to get data's approximate write unix time and write data with a specific write time via `WriteBatch::TimedPut` API.
|
||||
|
||||
### Public API Changes
|
||||
* Best-effort recovery (`best_efforts_recovery == true`) may now be used together with atomic flush (`atomic_flush == true`). The all-or-nothing recovery guarantee for atomically flushed data will be upheld.
|
||||
* Remove deprecated option `bottommost_temperature`, already replaced by `last_level_temperature`
|
||||
* Added new PerfContext counters for block cache bytes read - block_cache_index_read_byte, block_cache_filter_read_byte, block_cache_compression_dict_read_byte, and block_cache_read_byte.
|
||||
* Deprecate experimental Remote Compaction APIs - StartV2() and WaitForCompleteV2() and introduce Schedule() and Wait(). The new APIs essentially does the same thing as the old APIs. They allow taking externally generated unique id to wait for remote compaction to complete.
|
||||
* *For API `WriteCommittedTransaction::GetForUpdate`, if the column family enables user-defined timestamp, it was mandated that argument `do_validate` cannot be false, and UDT based validation has to be done with a user set read timestamp. It's updated to make the UDT based validation optional if user sets `do_validate` to false and does not set a read timestamp. With this, `GetForUpdate` skips UDT based validation and it's users' responsibility to enforce the UDT invariant. SO DO NOT skip this UDT-based validation if users do not have ways to enforce the UDT invariant. Ways to enforce the invariant on the users side include manage a monotonically increasing timestamp, commit transactions in a single thread etc.
|
||||
* Defined a new PerfLevel `kEnableWait` to measure time spent by user threads blocked in RocksDB other than mutex, such as a write thread waiting to be added to a write group, a write thread delayed or stalled etc.
|
||||
* `RateLimiter`'s API no longer requires the burst size to be the refill size. Users of `NewGenericRateLimiter()` can now provide burst size in `single_burst_bytes`. Implementors of `RateLimiter::SetSingleBurstBytes()` need to adapt their implementations to match the changed API doc.
|
||||
* Add `write_memtable_time` to the newly introduced PerfLevel `kEnableWait`.
|
||||
|
||||
### Behavior Changes
|
||||
* `RateLimiter`s created by `NewGenericRateLimiter()` no longer modify the refill period when `SetSingleBurstBytes()` is called.
|
||||
* Merge writes will only keep merge operand count within `ColumnFamilyOptions::max_successive_merges` when the key's merge operands are all found in memory, unless `strict_max_successive_merges` is explicitly set.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed `kBlockCacheTier` reads to return `Status::Incomplete` when I/O is needed to fetch a merge chain's base value from a blob file.
|
||||
* Fixed `kBlockCacheTier` reads to return `Status::Incomplete` on table cache miss rather than incorrectly returning an empty value.
|
||||
* Fixed a data race in WalManager that may affect how frequent PurgeObsoleteWALFiles() runs.
|
||||
* Re-enable the recycle_log_file_num option in DBOptions for kPointInTimeRecovery WAL recovery mode, which was previously disabled due to a bug in the recovery logic. This option is incompatible with WriteOptions::disableWAL. A Status::InvalidArgument() will be returned if disableWAL is specified.
|
||||
|
||||
### Performance Improvements
|
||||
* Java API `multiGet()` variants now take advantage of the underlying batched `multiGet()` performance improvements.
|
||||
Before
|
||||
```
|
||||
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
|
||||
MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 64 thrpt 25 6315.541 ± 8.106 ops/s
|
||||
MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 6975.468 ± 68.964 ops/s
|
||||
```
|
||||
After
|
||||
```
|
||||
Benchmark (columnFamilyTestType) (keyCount) (keySize) (multiGetSize) (valueSize) Mode Cnt Score Error Units
|
||||
MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 64 thrpt 25 7046.739 ± 13.299 ops/s
|
||||
MultiGetBenchmarks.multiGetList10 no_column_family 10000 16 100 1024 thrpt 25 7654.521 ± 60.121 ops/s
|
||||
```
|
||||
|
||||
## 9.0.0 (02/16/2024)
|
||||
### New Features
|
||||
* Provide support for FSBuffer for point lookups. Also added support for scans and compactions that don't go through prefetching.
|
||||
* *Make `SstFileWriter` create SST files without persisting user defined timestamps when the `Option.persist_user_defined_timestamps` flag is set to false.
|
||||
* Add support for user-defined timestamps in APIs `DeleteFilesInRanges` and `GetPropertiesOfTablesInRange`.
|
||||
* Mark wal\_compression feature as production-ready. Currently only compatible with ZSTD compression.
|
||||
|
||||
### Public API Changes
|
||||
* Allow setting Stderr logger via C API
|
||||
* Declare one Get and one MultiGet variant as pure virtual, and make all the other variants non-overridable. The methods required to be implemented by derived classes of DB allow returning timestamps. It is up to the implementation to check and return an error if timestamps are not supported. The non-batched MultiGet APIs are reimplemented in terms of batched MultiGet, so callers might see a performance improvement.
|
||||
* Exposed mode option to Rate Limiter via c api.
|
||||
* Removed deprecated option `access_hint_on_compaction_start`
|
||||
* Removed deprecated option `ColumnFamilyOptions::check_flush_compaction_key_order`
|
||||
* *Remove the default `WritableFile::GetFileSize` and `FSWritableFile::GetFileSize` implementation that returns 0 and make it pure virtual, so that subclasses are enforced to explicitly provide an implementation.
|
||||
* Removed deprecated option `ColumnFamilyOptions::level_compaction_dynamic_file_size`
|
||||
* *Removed tickers with typos "rocksdb.error.handler.bg.errro.count", "rocksdb.error.handler.bg.io.errro.count", "rocksdb.error.handler.bg.retryable.io.errro.count".
|
||||
* Remove the force mode for `EnableFileDeletions` API because it is unsafe with no known legitimate use.
|
||||
* Removed deprecated option `ColumnFamilyOptions::ignore_max_compaction_bytes_for_input`
|
||||
* `sst_dump --command=check` now compares the number of records in a table with `num_entries` in table property, and reports corruption if there is a mismatch. API `SstFileDumper::ReadSequential()` is updated to optionally do this verification. (#12322)
|
||||
|
||||
### Behavior Changes
|
||||
* format\_version=6 is the new default setting in BlockBasedTableOptions, for more robust data integrity checking. DBs and SST files written with this setting cannot be read by RocksDB versions before 8.6.0.
|
||||
* Compactions can be scheduled in parallel in an additional scenario: multiple files are marked for compaction within a single column family
|
||||
* For leveled compaction, RocksDB will try to do intra-L0 compaction if the total L0 size is small compared to Lbase (#12214). Users with atomic_flush=true are more likely to see the impact of this change.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a data race in `DBImpl::RenameTempFileToOptionsFile`.
|
||||
* Fix some perf context statistics error in write steps. which include missing write_memtable_time in unordered_write. missing write_memtable_time in PipelineWrite when Writer stat is STATE_PARALLEL_MEMTABLE_WRITER. missing write_delay_time when calling DelayWrite in WriteImplWALOnly function.
|
||||
* Fixed a bug that can, under rare circumstances, cause MultiGet to return an incorrect result for a duplicate key in a MultiGet batch.
|
||||
* Fix a bug where older data of an ingested key can be returned for read when universal compaction is used
|
||||
|
||||
## 8.11.0 (01/19/2024)
|
||||
### New Features
|
||||
* Add new statistics: `rocksdb.sst.write.micros` measures time of each write to SST file; `rocksdb.file.write.{flush|compaction|db.open}.micros` measure time of each write to SST table (currently only block-based table format) and blob file for flush, compaction and db open.
|
||||
|
||||
@@ -126,7 +126,6 @@ most processors made since roughly 2013.
|
||||
* Update XCode: run `xcode-select --install` (or install it from XCode App's settting).
|
||||
* Install via [homebrew](http://brew.sh/).
|
||||
* If you're first time developer in MacOS, you still need to run: `xcode-select --install` in your command line.
|
||||
* run `brew tap homebrew/versions; brew install gcc7 --use-llvm` to install gcc 7 (or higher).
|
||||
* run `brew install rocksdb`
|
||||
|
||||
* **FreeBSD** (11.01):
|
||||
|
||||
@@ -1640,6 +1640,9 @@ wal_edit_test: $(OBJ_DIR)/db/wal_edit_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
dbformat_test: $(OBJ_DIR)/db/dbformat_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
multi_cf_iterator_test: $(OBJ_DIR)/db/multi_cf_iterator_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
env_basic_test: $(OBJ_DIR)/env/env_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2332,47 +2335,47 @@ rocksdbjavastaticreleasedocker: rocksdbjavastaticosx rocksdbjavastaticdockerx86
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64le:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_arm64v8-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_arm64v8-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:centos7_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockers390x:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_s390x-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu18_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerriscv64:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_riscv64-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:ubuntu20_riscv64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerx86musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
docker run --rm --name rocksdb_linux_x64-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_x64-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64lemusl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
docker run --rm --name rocksdb_linux_ppc64le-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
docker run --rm --name rocksdb_linux_arm64v8-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_arm64v8-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticdockers390xmusl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_s390x-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux-alpine.sh
|
||||
docker run --rm --name rocksdb_linux_s390x-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) --env J=$(J) evolvedbinary/rocksjava:alpine3_s390x-be /rocksdb-host/java/crossbuild/docker-build-linux.sh
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"db/memtable_list.cc",
|
||||
"db/merge_helper.cc",
|
||||
"db/merge_operator.cc",
|
||||
"db/multi_cf_iterator.cc",
|
||||
"db/output_validator.cc",
|
||||
"db/periodic_task_scheduler.cc",
|
||||
"db/range_del_aggregator.cc",
|
||||
@@ -5226,6 +5227,12 @@ cpp_unittest_wrapper(name="mock_env_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="multi_cf_iterator_test",
|
||||
srcs=["db/multi_cf_iterator_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="object_registry_test",
|
||||
srcs=["utilities/object_registry_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
|
||||
@@ -163,24 +163,6 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -ldl"
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if test "$ROCKSDB_USE_IO_URING" -ne 0; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
@@ -614,6 +596,24 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if [ "$ROCKSDB_USE_IO_URING" -ne 0 -a "$PLATFORM" = OS_LINUX ]; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# The file is generated using update_dependencies.sh.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/b30c2e3be652b0135fb3df27fdef5eb4ffc2dc6d/11.x/centos9-native/886b5eb
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/62de5a92e5f23c661c3d4b9f322e04eb14e7a5bd/11.x/centos8-native/886b5eb
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/1f6edd1ff15c99c861afc8f3cd69054cd974dd64/15/platform010/72a2ff8
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d1129753c8361ac8e9453c0f4291337a4507ebe6/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/fed6e93d87571fb162734c86636119d45a398963/2.34/platform010/f259413
|
||||
@@ -17,6 +17,6 @@ TBB_BASE=/mnt/gvfs/third-party2/tbb/53953ebc4e3eda85ad6fc3e429ba146035e97b90/201
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/624a2f8f6c93c3c1df8aa4a6255d8202631a6c80/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/8dec50d6a10944a2e851f090a3872f9ba0b0b402/2.37/centos9-native/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/39579e8603b48b3540f8b0633f43adf29acccb8b/2.37/centos8-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/cd9cc656d49ecb53797ce4d055e49fde29fd57ff/3.19.0/platform010/76ebdda
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/363787fa5cac2a8aa20638909210443278fa138e/5.3.4/platform010/9079c97
|
||||
@@ -75,7 +75,7 @@ touch "$OUTPUT"
|
||||
echo "Writing dependencies to $OUTPUT"
|
||||
|
||||
# Compilers locations
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos9-native/*/`
|
||||
GCC_BASE=`readlink -f $TP2_LATEST/gcc/11.x/centos8-native/*/`
|
||||
CLANG_BASE=`readlink -f $TP2_LATEST/llvm-fb/15/platform010/*/`
|
||||
|
||||
log_header
|
||||
@@ -99,7 +99,7 @@ get_lib_base liburing LATEST platform010
|
||||
get_lib_base benchmark LATEST platform010
|
||||
|
||||
get_lib_base kernel-headers fb platform010
|
||||
get_lib_base binutils LATEST centos9-native
|
||||
get_lib_base binutils LATEST centos8-native
|
||||
get_lib_base valgrind LATEST platform010
|
||||
get_lib_base lua 5.3.4 platform010
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -194,7 +194,7 @@ class SharedState {
|
||||
: cv_(&mu_),
|
||||
cache_bench_(cache_bench) {}
|
||||
|
||||
~SharedState() {}
|
||||
~SharedState() = default;
|
||||
|
||||
port::Mutex* GetMutex() { return &mu_; }
|
||||
|
||||
@@ -425,7 +425,7 @@ class CacheBench {
|
||||
}
|
||||
}
|
||||
|
||||
~CacheBench() {}
|
||||
~CacheBench() = default;
|
||||
|
||||
void PopulateCache() {
|
||||
Random64 rnd(FLAGS_seed);
|
||||
|
||||
-1
@@ -129,7 +129,6 @@ TEST_F(CacheReservationManagerTest,
|
||||
|
||||
TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
IncreaseCacheReservationOnFullCache) {
|
||||
;
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManagerImpl<CacheEntryRole::kMisc>::GetDummyEntrySize();
|
||||
constexpr std::size_t kSmallCacheCapacity = 4 * kSizeDummyEntry;
|
||||
|
||||
Vendored
+5
-3
@@ -106,7 +106,7 @@ class CacheTest : public testing::Test,
|
||||
type_ = GetParam();
|
||||
}
|
||||
|
||||
~CacheTest() override {}
|
||||
~CacheTest() override = default;
|
||||
|
||||
// These functions encode/decode keys in tests cases that use
|
||||
// int keys.
|
||||
@@ -766,7 +766,9 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
std::string key = EncodeKey(i + 1);
|
||||
auto h = cache.Lookup(key);
|
||||
ASSERT_TRUE(h != nullptr);
|
||||
if (h) cache.Release(h);
|
||||
if (h) {
|
||||
cache.Release(h);
|
||||
}
|
||||
}
|
||||
|
||||
// the cache is over capacity since nothing could be evicted
|
||||
@@ -777,7 +779,7 @@ TEST_P(CacheTest, OverCapacity) {
|
||||
|
||||
if (IsHyperClock()) {
|
||||
// Make sure eviction is triggered.
|
||||
ASSERT_OK(cache.Insert(EncodeKey(-1), nullptr, 1, &handles[0]));
|
||||
ASSERT_OK(cache.Insert(EncodeKey(-1), nullptr, 1, handles.data()));
|
||||
|
||||
// cache is under capacity now since elements were released
|
||||
ASSERT_GE(n, cache.get()->GetUsage());
|
||||
|
||||
Vendored
+2
-2
@@ -26,7 +26,7 @@ CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
cache_))),
|
||||
disable_cache_(opts.capacity == 0) {}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() {}
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() = default;
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
@@ -384,7 +384,7 @@ const Cache::CacheItemHelper* CompressedSecondaryCache::GetHelper(
|
||||
chunks_head = chunks_head->next;
|
||||
tmp_chunk->Free();
|
||||
obj = nullptr;
|
||||
};
|
||||
}
|
||||
}};
|
||||
return &kHelper;
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ const std::string key3 = "____ ____key3";
|
||||
class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
public WithCacheType {
|
||||
public:
|
||||
CompressedSecondaryCacheTestBase() {}
|
||||
CompressedSecondaryCacheTestBase() = default;
|
||||
~CompressedSecondaryCacheTestBase() override = default;
|
||||
|
||||
protected:
|
||||
|
||||
Vendored
+3
-1
@@ -277,8 +277,8 @@ void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
||||
e->SetInHighPriPool(false);
|
||||
e->SetInLowPriPool(true);
|
||||
low_pri_pool_usage_ += e->total_charge;
|
||||
MaintainPoolSize();
|
||||
lru_low_pri_ = e;
|
||||
MaintainPoolSize();
|
||||
} else {
|
||||
// Insert "e" to the head of bottom-pri pool.
|
||||
e->next = lru_bottom_pri_->next;
|
||||
@@ -301,6 +301,7 @@ void LRUCacheShard::MaintainPoolSize() {
|
||||
// Overflow last entry in high-pri pool to low-pri pool.
|
||||
lru_low_pri_ = lru_low_pri_->next;
|
||||
assert(lru_low_pri_ != &lru_);
|
||||
assert(lru_low_pri_->InHighPriPool());
|
||||
lru_low_pri_->SetInHighPriPool(false);
|
||||
lru_low_pri_->SetInLowPriPool(true);
|
||||
assert(high_pri_pool_usage_ >= lru_low_pri_->total_charge);
|
||||
@@ -312,6 +313,7 @@ void LRUCacheShard::MaintainPoolSize() {
|
||||
// Overflow last entry in low-pri pool to bottom-pri pool.
|
||||
lru_bottom_pri_ = lru_bottom_pri_->next;
|
||||
assert(lru_bottom_pri_ != &lru_);
|
||||
assert(lru_bottom_pri_->InLowPriPool());
|
||||
lru_bottom_pri_->SetInHighPriPool(false);
|
||||
lru_bottom_pri_->SetInLowPriPool(false);
|
||||
assert(low_pri_pool_usage_ >= lru_bottom_pri_->total_charge);
|
||||
|
||||
Vendored
+27
-7
@@ -32,7 +32,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class LRUCacheTest : public testing::Test {
|
||||
public:
|
||||
LRUCacheTest() {}
|
||||
LRUCacheTest() = default;
|
||||
~LRUCacheTest() override { DeleteCache(); }
|
||||
|
||||
void DeleteCache() {
|
||||
@@ -57,10 +57,11 @@ class LRUCacheTest : public testing::Test {
|
||||
}
|
||||
|
||||
void Insert(const std::string& key,
|
||||
Cache::Priority priority = Cache::Priority::LOW) {
|
||||
Cache::Priority priority = Cache::Priority::LOW,
|
||||
size_t charge = 1) {
|
||||
EXPECT_OK(cache_->Insert(key, 0 /*hash*/, nullptr /*value*/,
|
||||
&kNoopCacheItemHelper, 1 /*charge*/,
|
||||
nullptr /*handle*/, priority));
|
||||
&kNoopCacheItemHelper, charge, nullptr /*handle*/,
|
||||
priority));
|
||||
}
|
||||
|
||||
void Insert(char key, Cache::Priority priority = Cache::Priority::LOW) {
|
||||
@@ -144,8 +145,10 @@ class LRUCacheTest : public testing::Test {
|
||||
ASSERT_EQ(num_bottom_pri_pool_keys, bottom_pri_pool_keys);
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
LRUCacheShard* cache_ = nullptr;
|
||||
|
||||
private:
|
||||
Cache::EvictionCallback eviction_callback_;
|
||||
};
|
||||
|
||||
@@ -378,7 +381,7 @@ class ClockCacheTest : public testing::Test {
|
||||
using Table = typename Shard::Table;
|
||||
using TableOpts = typename Table::Opts;
|
||||
|
||||
ClockCacheTest() {}
|
||||
ClockCacheTest() = default;
|
||||
~ClockCacheTest() override { DeleteShard(); }
|
||||
|
||||
void DeleteShard() {
|
||||
@@ -1976,7 +1979,7 @@ TEST_P(BasicSecondaryCacheTest, BasicWaitAllTest) {
|
||||
ah.priority = Cache::Priority::LOW;
|
||||
cache->StartAsyncLookup(ah);
|
||||
}
|
||||
cache->WaitAll(&async_handles[0], async_handles.size());
|
||||
cache->WaitAll(async_handles.data(), async_handles.size());
|
||||
for (size_t i = 0; i < async_handles.size(); ++i) {
|
||||
SCOPED_TRACE("i = " + std::to_string(i));
|
||||
Cache::Handle* result = async_handles[i].Result();
|
||||
@@ -2703,6 +2706,23 @@ TEST_P(DBSecondaryCacheTest, TestSecondaryCacheOptionTwoDB) {
|
||||
ASSERT_OK(DestroyDB(dbname2, options));
|
||||
}
|
||||
|
||||
TEST_F(LRUCacheTest, InsertAfterReducingCapacity) {
|
||||
// Fix a bug in LRU cache where it may try to remove a low pri entry's
|
||||
// charge from high pri pool. It causes
|
||||
// Assertion failed: (high_pri_pool_usage_ >= lru_low_pri_->total_charge),
|
||||
// function MaintainPoolSize, file lru_cache.cc
|
||||
NewCache(/*capacity=*/10, /*high_pri_pool_ratio=*/0.2,
|
||||
/*low_pri_pool_ratio=*/0.8);
|
||||
// high pri pool size and usage are both 2
|
||||
Insert("x", Cache::Priority::HIGH);
|
||||
Insert("y", Cache::Priority::HIGH);
|
||||
cache_->SetCapacity(5);
|
||||
// high_pri_pool_size is 1, the next time we try to maintain pool size,
|
||||
// we will move entries from high pri pool to low pri pool
|
||||
// The bug was deducting this entry's charge from high pri pool usage.
|
||||
Insert("aaa", Cache::Priority::LOW, /*charge=*/3);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
+17
-17
@@ -386,7 +386,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
@@ -400,7 +400,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -414,7 +414,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -428,7 +428,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -442,7 +442,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -456,7 +456,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -470,7 +470,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -484,7 +484,7 @@ TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -528,7 +528,7 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
@@ -542,7 +542,7 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -561,7 +561,7 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
|
||||
@@ -582,7 +582,7 @@ TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
|
||||
@@ -629,7 +629,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
|
||||
@@ -644,7 +644,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(20));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -659,7 +659,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(8));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
|
||||
@@ -676,7 +676,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
@@ -691,7 +691,7 @@ TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
|
||||
keys.push_back(Key(36));
|
||||
values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (auto value : values) {
|
||||
for (const auto& value : values) {
|
||||
ASSERT_EQ(1007, value.size());
|
||||
}
|
||||
ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
# - Find zstd
|
||||
# Find the zstd compression library and includes
|
||||
#
|
||||
# zstd_INCLUDE_DIRS - where to find zstd.h, etc.
|
||||
# zstd_LIBRARIES - List of libraries when using zstd.
|
||||
# zstd_FOUND - True if zstd found.
|
||||
# ZSTD_INCLUDE_DIRS - where to find zstd.h, etc.
|
||||
# ZSTD_LIBRARIES - List of libraries when using zstd.
|
||||
# ZSTD_FOUND - True if zstd found.
|
||||
|
||||
find_path(zstd_INCLUDE_DIRS
|
||||
find_path(ZSTD_INCLUDE_DIRS
|
||||
NAMES zstd.h
|
||||
HINTS ${zstd_ROOT_DIR}/include)
|
||||
|
||||
find_library(zstd_LIBRARIES
|
||||
find_library(ZSTD_LIBRARIES
|
||||
NAMES zstd
|
||||
HINTS ${zstd_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES ZSTD_INCLUDE_DIRS)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG ZSTD_LIBRARIES ZSTD_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
zstd_LIBRARIES
|
||||
zstd_INCLUDE_DIRS)
|
||||
ZSTD_LIBRARIES
|
||||
ZSTD_INCLUDE_DIRS)
|
||||
|
||||
if(zstd_FOUND AND NOT (TARGET zstd::zstd))
|
||||
if(ZSTD_FOUND AND NOT (TARGET zstd::zstd))
|
||||
add_library (zstd::zstd UNKNOWN IMPORTED)
|
||||
set_target_properties(zstd::zstd
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${zstd_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${zstd_INCLUDE_DIRS})
|
||||
IMPORTED_LOCATION ${ZSTD_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIRS})
|
||||
endif()
|
||||
+30
-27
@@ -43,14 +43,13 @@ void ArenaWrappedDBIter::Init(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number, ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index, bool allow_refresh) {
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ =
|
||||
new (mem) DBIter(env, read_options, ioptions, mutable_cf_options,
|
||||
ioptions.user_comparator, /* iter */ nullptr, version,
|
||||
sequence, true, max_sequential_skip_in_iteration,
|
||||
read_callback, db_impl, cfd, expose_blob_index);
|
||||
db_iter_ = new (mem) DBIter(
|
||||
env, read_options, ioptions, mutable_cf_options, ioptions.user_comparator,
|
||||
/* iter */ nullptr, version, sequence, true,
|
||||
max_sequential_skip_in_iteration, read_callback, cfh, expose_blob_index);
|
||||
sv_number_ = version_number;
|
||||
read_options_ = read_options;
|
||||
allow_refresh_ = allow_refresh;
|
||||
@@ -65,40 +64,44 @@ void ArenaWrappedDBIter::Init(
|
||||
Status ArenaWrappedDBIter::Refresh() { return Refresh(nullptr); }
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr || !allow_refresh_) {
|
||||
if (cfh_ == nullptr || !allow_refresh_) {
|
||||
return Status::NotSupported("Creating renew iterator is not allowed.");
|
||||
}
|
||||
assert(db_iter_ != nullptr);
|
||||
auto cfd = cfh_->cfd();
|
||||
auto db_impl = cfh_->db();
|
||||
|
||||
// TODO(yiwu): For last_seq_same_as_publish_seq_==false, this is not the
|
||||
// correct behavior. Will be corrected automatically when we take a snapshot
|
||||
// here for the case of WritePreparedTxnDB.
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
uint64_t cur_sv_number = cfd->GetSuperVersionNumber();
|
||||
// If we recreate a new internal iterator below (NewInternalIterator()),
|
||||
// we will pass in read_options_. We need to make sure it
|
||||
// has the right snapshot.
|
||||
read_options_.snapshot = snapshot;
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
|
||||
auto reinit_internal_iter = [&]() {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
arena_.~Arena();
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(db_impl);
|
||||
assert(sv->version_number >= cur_sv_number);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl_, snapshot);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(read_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
Init(env, read_options_, *(cfd->ioptions()), sv->mutable_cf_options,
|
||||
sv->current, read_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
sv->version_number, read_callback_, cfh_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, read_seq,
|
||||
InternalIterator* internal_iter = db_impl->NewInternalIterator(
|
||||
read_options_, cfd, sv, &arena_, read_seq,
|
||||
/* allow_unprepared_value */ true, /* db_iter */ this);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
};
|
||||
@@ -107,10 +110,10 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
reinit_internal_iter();
|
||||
break;
|
||||
} else {
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl_, snapshot);
|
||||
SequenceNumber read_seq = GetSeqNum(db_impl, snapshot);
|
||||
// Refresh range-tombstones in MemTable
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
SuperVersion* sv = cfd_->GetThreadLocalSuperVersion(db_impl_);
|
||||
SuperVersion* sv = cfd->GetThreadLocalSuperVersion(db_impl);
|
||||
TEST_SYNC_POINT_CALLBACK("ArenaWrappedDBIter::Refresh:SV", nullptr);
|
||||
auto t = sv->mem->NewRangeTombstoneIterator(
|
||||
read_options_, read_seq, false /* immutable_memtable */);
|
||||
@@ -123,13 +126,13 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
// will be freed during db_iter destruction there.
|
||||
if (memtable_range_tombstone_iter_) {
|
||||
assert(!*memtable_range_tombstone_iter_ ||
|
||||
sv_number_ != cfd_->GetSuperVersionNumber());
|
||||
sv_number_ != cfd->GetSuperVersionNumber());
|
||||
}
|
||||
delete t;
|
||||
} else { // current mutable memtable has range tombstones
|
||||
if (!memtable_range_tombstone_iter_) {
|
||||
delete t;
|
||||
db_impl_->ReturnAndCleanupSuperVersion(cfd_, sv);
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
// The memtable under DBIter did not have range tombstone before
|
||||
// refresh.
|
||||
reinit_internal_iter();
|
||||
@@ -138,13 +141,13 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
delete *memtable_range_tombstone_iter_;
|
||||
*memtable_range_tombstone_iter_ = new TruncatedRangeDelIterator(
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator>(t),
|
||||
&cfd_->internal_comparator(), nullptr, nullptr);
|
||||
&cfd->internal_comparator(), nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
db_impl_->ReturnAndCleanupSuperVersion(cfd_, sv);
|
||||
db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
// Check again if the latest super version number is changed
|
||||
uint64_t latest_sv_number = cfd_->GetSuperVersionNumber();
|
||||
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
|
||||
@@ -163,14 +166,14 @@ ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index, bool allow_refresh) {
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, ioptions, mutable_cf_options, version, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
db_impl, cfd, expose_blob_index, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(db_impl, cfd, read_callback, expose_blob_index);
|
||||
cfh, expose_blob_index, allow_refresh);
|
||||
if (cfh != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(cfh, read_callback, expose_blob_index);
|
||||
}
|
||||
|
||||
return iter;
|
||||
|
||||
@@ -87,15 +87,14 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
|
||||
bool expose_blob_index, bool allow_refresh);
|
||||
|
||||
// Store some parameters so we can refresh the iterator at a later point
|
||||
// with these same params
|
||||
void StoreRefreshInfo(DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
void StoreRefreshInfo(ColumnFamilyHandleImpl* cfh,
|
||||
ReadCallback* read_callback, bool expose_blob_index) {
|
||||
db_impl_ = db_impl;
|
||||
cfd_ = cfd;
|
||||
cfh_ = cfh;
|
||||
read_callback_ = read_callback;
|
||||
expose_blob_index_ = expose_blob_index;
|
||||
}
|
||||
@@ -104,8 +103,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
DBIter* db_iter_ = nullptr;
|
||||
Arena arena_;
|
||||
uint64_t sv_number_;
|
||||
ColumnFamilyData* cfd_ = nullptr;
|
||||
DBImpl* db_impl_ = nullptr;
|
||||
ColumnFamilyHandleImpl* cfh_ = nullptr;
|
||||
ReadOptions read_options_;
|
||||
ReadCallback* read_callback_;
|
||||
bool expose_blob_index_ = false;
|
||||
@@ -116,13 +114,13 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
// `db_impl` and `cfd` are used for reneweal. If left null, renewal will not
|
||||
// `cfh` is used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
DBImpl* db_impl = nullptr, ColumnFamilyData* cfd = nullptr,
|
||||
bool expose_blob_index = false, bool allow_refresh = true);
|
||||
ColumnFamilyHandleImpl* cfh = nullptr, bool expose_blob_index = false,
|
||||
bool allow_refresh = true);
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -405,7 +405,7 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
requests_buf[0] =
|
||||
BlobReadRequest(key_refs[0], blob_offsets[0], blob_sizes[0],
|
||||
kNoCompression, nullptr, &statuses_buf[0]);
|
||||
kNoCompression, nullptr, statuses_buf.data());
|
||||
requests_buf[1] =
|
||||
BlobReadRequest(key_refs[1], blob_offsets[1], blob_sizes[1] + 1,
|
||||
kNoCompression, nullptr, &statuses_buf[1]);
|
||||
|
||||
+13
-13
@@ -168,8 +168,8 @@ TEST_F(BlobSourceTest, GetBlobsFromCache) {
|
||||
|
||||
uint64_t file_size = BlobLogHeader::kSize;
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
keys.emplace_back(key_strs[i]);
|
||||
blobs.emplace_back(blob_strs[i]);
|
||||
file_size += BlobLogRecord::kHeaderSize + keys[i].size() + blobs[i].size();
|
||||
}
|
||||
file_size += BlobLogFooter::kSize;
|
||||
@@ -482,8 +482,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
std::vector<Slice> blobs;
|
||||
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
keys.emplace_back(key_strs[i]);
|
||||
blobs.emplace_back(blob_strs[i]);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> blob_offsets(keys.size());
|
||||
@@ -610,8 +610,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromMultiFiles) {
|
||||
uint64_t file_size = BlobLogHeader::kSize;
|
||||
uint64_t blob_value_bytes = 0;
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
keys.emplace_back(key_strs[i]);
|
||||
blobs.emplace_back(blob_strs[i]);
|
||||
blob_value_bytes += blobs[i].size();
|
||||
file_size += BlobLogRecord::kHeaderSize + keys[i].size() + blobs[i].size();
|
||||
}
|
||||
@@ -802,8 +802,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
|
||||
|
||||
uint64_t file_size = BlobLogHeader::kSize;
|
||||
for (size_t i = 0; i < num_blobs; ++i) {
|
||||
keys.push_back({key_strs[i]});
|
||||
blobs.push_back({blob_strs[i]});
|
||||
keys.emplace_back(key_strs[i]);
|
||||
blobs.emplace_back(blob_strs[i]);
|
||||
file_size += BlobLogRecord::kHeaderSize + keys[i].size() + blobs[i].size();
|
||||
}
|
||||
file_size += BlobLogFooter::kSize;
|
||||
@@ -1164,7 +1164,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[0], file_number,
|
||||
blob_offsets[0], file_size, blob_sizes[0],
|
||||
kNoCompression, nullptr /* prefetch_buffer */,
|
||||
&values[0], nullptr /* bytes_read */));
|
||||
values.data(), nullptr /* bytes_read */));
|
||||
// Release cache handle
|
||||
values[0].Reset();
|
||||
|
||||
@@ -1183,7 +1183,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
ASSERT_OK(blob_source.GetBlob(read_options, keys[0], file_number,
|
||||
blob_offsets[0], file_size, blob_sizes[0],
|
||||
kNoCompression, nullptr /* prefetch_buffer */,
|
||||
&values[0], nullptr /* bytes_read */));
|
||||
values.data(), nullptr /* bytes_read */));
|
||||
ASSERT_EQ(values[0], blobs[0]);
|
||||
ASSERT_TRUE(
|
||||
blob_source.TEST_BlobInCache(file_number, file_size, blob_offsets[0]));
|
||||
@@ -1263,7 +1263,7 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
ASSERT_OK(blob_source.GetBlob(
|
||||
read_options, keys[0], file_number, blob_offsets[0], file_size,
|
||||
blob_sizes[0], kNoCompression, nullptr /* prefetch_buffer */,
|
||||
&values[0], nullptr /* bytes_read */));
|
||||
values.data(), nullptr /* bytes_read */));
|
||||
ASSERT_EQ(values[0], blobs[0]);
|
||||
|
||||
// Release cache handle
|
||||
@@ -1365,8 +1365,8 @@ class BlobSourceCacheReservationTest : public DBTestBase {
|
||||
|
||||
blob_file_size_ = BlobLogHeader::kSize;
|
||||
for (size_t i = 0; i < kNumBlobs; ++i) {
|
||||
keys_.push_back({key_strs_[i]});
|
||||
blobs_.push_back({blob_strs_[i]});
|
||||
keys_.emplace_back(key_strs_[i]);
|
||||
blobs_.emplace_back(blob_strs_[i]);
|
||||
blob_file_size_ +=
|
||||
BlobLogRecord::kHeaderSize + keys_[i].size() + blobs_[i].size();
|
||||
}
|
||||
|
||||
@@ -418,8 +418,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobs) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -441,8 +441,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobs) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -512,8 +512,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -534,8 +534,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -553,8 +553,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -574,8 +574,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromCache) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -758,8 +758,8 @@ TEST_F(DBBlobBasicTest, MultiGetWithDirectIO) {
|
||||
//
|
||||
// [offset=0, len=12288]
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -829,8 +829,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
{
|
||||
std::array<PinnableSlice, kNumKeys> values;
|
||||
std::array<Status, kNumKeys> statuses;
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
@@ -843,8 +843,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
{
|
||||
std::array<PinnableSlice, kNumKeys> values;
|
||||
std::array<Status, kNumKeys> statuses;
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_TRUE(statuses[i].IsIncomplete());
|
||||
@@ -858,8 +858,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
{
|
||||
std::array<PinnableSlice, kNumKeys> values;
|
||||
std::array<Status, kNumKeys> statuses;
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
@@ -872,8 +872,8 @@ TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
{
|
||||
std::array<PinnableSlice, kNumKeys> values;
|
||||
std::array<Status, kNumKeys> statuses;
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), kNumKeys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
for (size_t i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
@@ -1182,6 +1182,30 @@ TEST_F(DBBlobBasicTest, GetMergeBlobWithPut) {
|
||||
ASSERT_EQ(Get("Key1"), "v1,v2,v3");
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetMergeBlobFromMemoryTier) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge(Key(0), "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Regular `Get()` loads data block to cache.
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), Key(0), &value));
|
||||
ASSERT_EQ("v1,v2", value);
|
||||
|
||||
// Base value blob is still uncached, so an in-memory read will fail.
|
||||
ReadOptions read_options;
|
||||
read_options.read_tier = kBlockCacheTier;
|
||||
ASSERT_TRUE(db_->Get(read_options, Key(0), &value).IsIncomplete());
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetMergeBlobWithPut) {
|
||||
constexpr size_t num_keys = 3;
|
||||
|
||||
@@ -1206,8 +1230,8 @@ TEST_F(DBBlobBasicTest, MultiGetMergeBlobWithPut) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], "v0_0,v0_1,v0_2");
|
||||
@@ -1470,8 +1494,8 @@ TEST_P(DBBlobBasicIOErrorMultiGetTest, MultiGetBlobs_IOError) {
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -1820,7 +1844,7 @@ TEST_F(DBBlobBasicTest, GetEntityBlob) {
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
&keys[0], &results[0], &statuses[0]);
|
||||
keys.data(), results.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(results[0].columns(), expected_columns);
|
||||
@@ -1917,8 +1941,8 @@ TEST_F(DBBlobWithTimestampTest, MultiGetBlobs) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys,
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
@@ -2001,8 +2025,8 @@ TEST_F(DBBlobWithTimestampTest, MultiGetMergeBlobWithPut) {
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_opts, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
db_->MultiGet(read_opts, db_->DefaultColumnFamily(), num_keys, keys.data(),
|
||||
values.data(), statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], "v0_0,v0_1,v0_2");
|
||||
|
||||
@@ -45,10 +45,10 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
DBBlobIndexTest() : DBTestBase("db_blob_index_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
ColumnFamilyHandle* cfh() { return dbfull()->DefaultColumnFamily(); }
|
||||
|
||||
ColumnFamilyData* cfd() {
|
||||
return static_cast_with_check<ColumnFamilyHandleImpl>(cfh())->cfd();
|
||||
ColumnFamilyHandleImpl* cfh_impl() {
|
||||
return static_cast_with_check<ColumnFamilyHandleImpl>(cfh());
|
||||
}
|
||||
ColumnFamilyData* cfd() { return cfh_impl()->cfd(); }
|
||||
|
||||
Status PutBlobIndex(WriteBatch* batch, const Slice& key,
|
||||
const Slice& blob_index) {
|
||||
@@ -96,11 +96,9 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* GetBlobIterator() {
|
||||
ColumnFamilyData* column_family = cfd();
|
||||
DBImpl* db_impl = dbfull();
|
||||
return db_impl->NewIteratorImpl(
|
||||
ReadOptions(), column_family,
|
||||
column_family->GetReferencedSuperVersion(db_impl),
|
||||
ReadOptions(), cfh_impl(), cfd()->GetReferencedSuperVersion(db_impl),
|
||||
db_impl->GetLatestSequenceNumber(), nullptr /*read_callback*/,
|
||||
true /*expose_blob_index*/);
|
||||
}
|
||||
|
||||
+39
-13
@@ -70,8 +70,8 @@ Status BuildTable(
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
IOStatus* io_status, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCreationReason blob_creation_reason,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping, EventLogger* event_logger,
|
||||
int job_id, TableProperties* table_properties,
|
||||
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping,
|
||||
EventLogger* event_logger, int job_id, TableProperties* table_properties,
|
||||
Env::WriteLifeTimeHint write_hint, const std::string* full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback, Version* version,
|
||||
uint64_t* num_input_entries, uint64_t* memtable_payload_bytes,
|
||||
@@ -211,12 +211,16 @@ Status BuildTable(
|
||||
ts_sz > 0 && !ioptions.persist_user_defined_timestamps;
|
||||
|
||||
std::string key_after_flush_buf;
|
||||
std::string value_buf;
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
const Slice& key = c_iter.key();
|
||||
const Slice& value = c_iter.value();
|
||||
const ParsedInternalKey& ikey = c_iter.ikey();
|
||||
Slice key_after_flush = key;
|
||||
ParsedInternalKey ikey = c_iter.ikey();
|
||||
key_after_flush_buf.assign(key.data(), key.size());
|
||||
Slice key_after_flush = key_after_flush_buf;
|
||||
Slice value_after_flush = value;
|
||||
|
||||
// If user defined timestamps will be stripped from user key after flush,
|
||||
// the in memory version of the key act logically the same as one with a
|
||||
// minimum timestamp. We update the timestamp here so file boundary and
|
||||
@@ -227,17 +231,37 @@ Status BuildTable(
|
||||
key_after_flush = key_after_flush_buf;
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeValuePreferredSeqno) {
|
||||
auto [unpacked_value, unix_write_time] =
|
||||
ParsePackedValueWithWriteTime(value);
|
||||
SequenceNumber preferred_seqno =
|
||||
seqno_to_time_mapping
|
||||
? seqno_to_time_mapping->GetProximalSeqnoBeforeTime(
|
||||
unix_write_time)
|
||||
: kMaxSequenceNumber;
|
||||
if (preferred_seqno < ikey.sequence) {
|
||||
value_after_flush =
|
||||
PackValueAndSeqno(unpacked_value, preferred_seqno, &value_buf);
|
||||
} else {
|
||||
// Cannot get a useful preferred seqno, convert it to a kTypeValue.
|
||||
UpdateInternalKey(&key_after_flush_buf, ikey.sequence, kTypeValue);
|
||||
ikey = ParsedInternalKey(ikey.user_key, ikey.sequence, kTypeValue);
|
||||
key_after_flush = key_after_flush_buf;
|
||||
value_after_flush = ParsePackedValueForValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a rolling 64-bit hash of the key and values
|
||||
// Note :
|
||||
// Here "key" integrates 'sequence_number'+'kType'+'user key'.
|
||||
s = output_validator.Add(key_after_flush, value);
|
||||
s = output_validator.Add(key_after_flush, value_after_flush);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
builder->Add(key_after_flush, value);
|
||||
builder->Add(key_after_flush, value_after_flush);
|
||||
|
||||
s = meta->UpdateBoundaries(key_after_flush, value, ikey.sequence,
|
||||
ikey.type);
|
||||
s = meta->UpdateBoundaries(key_after_flush, value_after_flush,
|
||||
ikey.sequence, ikey.type);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -301,11 +325,13 @@ Status BuildTable(
|
||||
builder->Abandon();
|
||||
} else {
|
||||
SeqnoToTimeMapping relevant_mapping;
|
||||
relevant_mapping.CopyFromSeqnoRange(seqno_to_time_mapping,
|
||||
meta->fd.smallest_seqno,
|
||||
meta->fd.largest_seqno);
|
||||
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
|
||||
relevant_mapping.Enforce(tboptions.file_creation_time);
|
||||
if (seqno_to_time_mapping) {
|
||||
relevant_mapping.CopyFromSeqnoRange(*seqno_to_time_mapping,
|
||||
meta->fd.smallest_seqno,
|
||||
meta->fd.largest_seqno);
|
||||
relevant_mapping.SetCapacity(kMaxSeqnoTimePairsPerSST);
|
||||
relevant_mapping.Enforce(tboptions.file_creation_time);
|
||||
}
|
||||
builder->SetSeqnoTimeTableProperties(
|
||||
relevant_mapping,
|
||||
ioptions.compaction_style == CompactionStyle::kCompactionStyleFIFO
|
||||
|
||||
+1
-2
@@ -23,7 +23,6 @@
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -64,7 +63,7 @@ Status BuildTable(
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
IOStatus* io_status, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
BlobFileCreationReason blob_creation_reason,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping,
|
||||
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
TableProperties* table_properties = nullptr,
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
|
||||
@@ -446,7 +446,7 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
size_t new_value_len;
|
||||
char* tmp_new_value = (*full_merge_)(
|
||||
state_, merge_in.key.data(), merge_in.key.size(), existing_value_data,
|
||||
existing_value_len, &operand_pointers[0], &operand_sizes[0],
|
||||
existing_value_len, operand_pointers.data(), operand_sizes.data(),
|
||||
static_cast<int>(n), &success, &new_value_len);
|
||||
merge_out->new_value.assign(tmp_new_value, new_value_len);
|
||||
|
||||
@@ -475,8 +475,9 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
unsigned char success;
|
||||
size_t new_value_len;
|
||||
char* tmp_new_value = (*partial_merge_)(
|
||||
state_, key.data(), key.size(), &operand_pointers[0], &operand_sizes[0],
|
||||
static_cast<int>(operand_count), &success, &new_value_len);
|
||||
state_, key.data(), key.size(), operand_pointers.data(),
|
||||
operand_sizes.data(), static_cast<int>(operand_count), &success,
|
||||
&new_value_len);
|
||||
new_value->assign(tmp_new_value, new_value_len);
|
||||
|
||||
if (delete_value_ != nullptr) {
|
||||
@@ -886,9 +887,9 @@ rocksdb_t* rocksdb_open_and_trim_history(
|
||||
size_t trim_tslen, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
|
||||
std::string trim_ts_(trim_ts, trim_tslen);
|
||||
@@ -919,9 +920,9 @@ rocksdb_t* rocksdb_open_column_families(
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
|
||||
DB* db;
|
||||
@@ -953,9 +954,9 @@ rocksdb_t* rocksdb_open_column_families_with_ttl(
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
ttls_vec.push_back(ttls[i]);
|
||||
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::DBWithTTL* db;
|
||||
@@ -985,9 +986,9 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
unsigned char error_if_wal_file_exists, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
|
||||
DB* db;
|
||||
@@ -1081,7 +1082,7 @@ rocksdb_column_family_handle_t** rocksdb_create_column_families(
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
std::vector<std::string> names;
|
||||
for (int i = 0; i != num_column_families; ++i) {
|
||||
names.push_back(std::string(column_family_names[i]));
|
||||
names.emplace_back(column_family_names[i]);
|
||||
}
|
||||
SaveError(errptr, db->rep->CreateColumnFamilies(
|
||||
ColumnFamilyOptions(column_family_options->rep), names,
|
||||
@@ -2788,7 +2789,9 @@ void rocksdb_options_set_cuckoo_table_factory(
|
||||
void rocksdb_set_options(rocksdb_t* db, int count, const char* const keys[],
|
||||
const char* const values[], char** errptr) {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
for (int i = 0; i < count; i++) options_map[keys[i]] = values[i];
|
||||
for (int i = 0; i < count; i++) {
|
||||
options_map[keys[i]] = values[i];
|
||||
}
|
||||
SaveError(errptr, db->rep->SetOptions(options_map));
|
||||
}
|
||||
|
||||
@@ -2797,7 +2800,9 @@ void rocksdb_set_options_cf(rocksdb_t* db,
|
||||
const char* const keys[],
|
||||
const char* const values[], char** errptr) {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
for (int i = 0; i < count; i++) options_map[keys[i]] = values[i];
|
||||
for (int i = 0; i < count; i++) {
|
||||
options_map[keys[i]] = values[i];
|
||||
}
|
||||
SaveError(errptr, db->rep->SetOptions(handle->rep, options_map));
|
||||
}
|
||||
|
||||
@@ -5060,7 +5065,9 @@ void rocksdb_env_lower_high_priority_thread_pool_cpu_priority(
|
||||
}
|
||||
|
||||
void rocksdb_env_destroy(rocksdb_env_t* env) {
|
||||
if (!env->is_default) delete env->rep;
|
||||
if (!env->is_default) {
|
||||
delete env->rep;
|
||||
}
|
||||
delete env;
|
||||
}
|
||||
|
||||
@@ -5524,7 +5531,7 @@ size_t rocksdb_column_family_metadata_get_level_count(
|
||||
rocksdb_level_metadata_t* rocksdb_column_family_metadata_get_level_metadata(
|
||||
rocksdb_column_family_metadata_t* cf_meta, size_t i) {
|
||||
if (i >= cf_meta->rep.levels.size()) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_level_metadata_t* level_meta =
|
||||
(rocksdb_level_metadata_t*)malloc(sizeof(rocksdb_level_metadata_t));
|
||||
@@ -5739,9 +5746,9 @@ rocksdb_transactiondb_t* rocksdb_transactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
|
||||
TransactionDB* txn_db;
|
||||
@@ -6533,9 +6540,9 @@ rocksdb_optimistictransactiondb_open_column_families(
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
|
||||
OptimisticTransactionDB* otxn_db;
|
||||
|
||||
+6
-6
@@ -50,12 +50,13 @@ static void StartPhase(const char* name) {
|
||||
#endif
|
||||
static const char* GetTempDir(void) {
|
||||
const char* ret = getenv("TEST_TMPDIR");
|
||||
if (ret == NULL || ret[0] == '\0')
|
||||
if (ret == NULL || ret[0] == '\0') {
|
||||
#ifdef OS_WIN
|
||||
ret = getenv("TEMP");
|
||||
#else
|
||||
ret = "/tmp";
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
@@ -206,10 +207,11 @@ static int CmpCompare(void* arg, const char* a, size_t alen, const char* b,
|
||||
size_t n = (alen < blen) ? alen : blen;
|
||||
int r = memcmp(a, b, n);
|
||||
if (r == 0) {
|
||||
if (alen < blen)
|
||||
if (alen < blen) {
|
||||
r = -1;
|
||||
else if (alen > blen)
|
||||
} else if (alen > blen) {
|
||||
r = +1;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -883,9 +885,8 @@ int main(int argc, char** argv) {
|
||||
StartPhase("addfile");
|
||||
{
|
||||
rocksdb_envoptions_t* env_opt = rocksdb_envoptions_create();
|
||||
rocksdb_options_t* io_options = rocksdb_options_create();
|
||||
rocksdb_sstfilewriter_t* writer =
|
||||
rocksdb_sstfilewriter_create(env_opt, io_options);
|
||||
rocksdb_sstfilewriter_create(env_opt, options);
|
||||
|
||||
remove(sstfilename);
|
||||
rocksdb_sstfilewriter_open(writer, sstfilename, &err);
|
||||
@@ -944,7 +945,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
rocksdb_ingestexternalfileoptions_destroy(ing_opt);
|
||||
rocksdb_sstfilewriter_destroy(writer);
|
||||
rocksdb_options_destroy(io_options);
|
||||
rocksdb_envoptions_destroy(env_opt);
|
||||
|
||||
// Delete all keys we just ingested
|
||||
|
||||
+23
-7
@@ -411,6 +411,13 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
"periodic_compaction_seconds does not support FIFO compaction. You"
|
||||
"may want to set option TTL instead.");
|
||||
}
|
||||
if (result.last_level_temperature != Temperature::kUnknown) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options.info_log.get(),
|
||||
"last_level_temperature is ignored with FIFO compaction. Consider "
|
||||
"CompactionOptionsFIFO::file_temperature_age_thresholds.");
|
||||
result.last_level_temperature = Temperature::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
// For universal compaction, `ttl` and `periodic_compaction_seconds` mean the
|
||||
@@ -470,13 +477,16 @@ void SuperVersion::Cleanup() {
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
|
||||
void SuperVersion::Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
MemTableListVersion* new_imm, Version* new_current) {
|
||||
void SuperVersion::Init(
|
||||
ColumnFamilyData* new_cfd, MemTable* new_mem, MemTableListVersion* new_imm,
|
||||
Version* new_current,
|
||||
std::shared_ptr<const SeqnoToTimeMapping> new_seqno_to_time_mapping) {
|
||||
cfd = new_cfd;
|
||||
mem = new_mem;
|
||||
imm = new_imm;
|
||||
current = new_current;
|
||||
full_history_ts_low = cfd->GetFullHistoryTsLow();
|
||||
seqno_to_time_mapping = std::move(new_seqno_to_time_mapping);
|
||||
cfd->Ref();
|
||||
mem->Ref();
|
||||
imm->Ref();
|
||||
@@ -1189,11 +1199,12 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&internal_comparator_, &arena);
|
||||
merge_iter_builder.AddIterator(
|
||||
super_version->mem->NewIterator(read_opts, &arena));
|
||||
super_version->imm->AddIterators(read_opts, &merge_iter_builder,
|
||||
merge_iter_builder.AddIterator(super_version->mem->NewIterator(
|
||||
read_opts, /*seqno_to_time_mapping=*/nullptr, &arena));
|
||||
super_version->imm->AddIterators(read_opts, /*seqno_to_time_mapping=*/nullptr,
|
||||
&merge_iter_builder,
|
||||
false /* add_range_tombstone_iter */);
|
||||
ScopedArenaIterator memtable_iter(merge_iter_builder.Finish());
|
||||
ScopedArenaPtr<InternalIterator> memtable_iter(merge_iter_builder.Finish());
|
||||
|
||||
auto read_seq = super_version->current->version_set()->LastSequence();
|
||||
ReadRangeDelAggregator range_del_agg(&internal_comparator_, read_seq);
|
||||
@@ -1329,7 +1340,12 @@ void ColumnFamilyData::InstallSuperVersion(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
SuperVersion* new_superversion = sv_context->new_superversion.release();
|
||||
new_superversion->mutable_cf_options = mutable_cf_options;
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_);
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_,
|
||||
sv_context->new_seqno_to_time_mapping
|
||||
? std::move(sv_context->new_seqno_to_time_mapping)
|
||||
: super_version_
|
||||
? super_version_->ShareSeqnoToTimeMapping()
|
||||
: nullptr);
|
||||
SuperVersion* old_superversion = super_version_;
|
||||
super_version_ = new_superversion;
|
||||
if (old_superversion == nullptr || old_superversion->current != current() ||
|
||||
|
||||
+22
-2
@@ -26,6 +26,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "trace_replay/block_cache_tracer.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/hash_containers.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -168,6 +169,7 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
// destroy without mutex
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
virtual DBImpl* db() const { return db_; }
|
||||
|
||||
uint32_t GetID() const override;
|
||||
const std::string& GetName() const override;
|
||||
@@ -218,6 +220,9 @@ struct SuperVersion {
|
||||
// enable UDT feature, this is an empty string.
|
||||
std::string full_history_ts_low;
|
||||
|
||||
// A shared copy of the DB's seqno to time mapping.
|
||||
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping{nullptr};
|
||||
|
||||
// should be called outside the mutex
|
||||
SuperVersion() = default;
|
||||
~SuperVersion();
|
||||
@@ -231,8 +236,23 @@ struct SuperVersion {
|
||||
// that needs to be deleted in to_delete vector. Unrefing those
|
||||
// objects needs to be done in the mutex
|
||||
void Cleanup();
|
||||
void Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
MemTableListVersion* new_imm, Version* new_current);
|
||||
void Init(
|
||||
ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
MemTableListVersion* new_imm, Version* new_current,
|
||||
std::shared_ptr<const SeqnoToTimeMapping> new_seqno_to_time_mapping);
|
||||
|
||||
// Share the ownership of the seqno to time mapping object referred to in this
|
||||
// SuperVersion. To be used by the new SuperVersion to be installed after this
|
||||
// one if seqno to time mapping does not change in between these two
|
||||
// SuperVersions. Or to share the ownership of the mapping with a FlushJob.
|
||||
std::shared_ptr<const SeqnoToTimeMapping> ShareSeqnoToTimeMapping() {
|
||||
return seqno_to_time_mapping;
|
||||
}
|
||||
|
||||
// Access the seqno to time mapping object in this SuperVersion.
|
||||
UnownedPtr<const SeqnoToTimeMapping> GetSeqnoToTimeMapping() const {
|
||||
return seqno_to_time_mapping.get();
|
||||
}
|
||||
|
||||
// The value of dummy is not actually used. kSVInUse takes its address as a
|
||||
// mark in the thread local storage to indicate the SuperVersion is in use
|
||||
|
||||
@@ -270,7 +270,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
|
||||
void Reopen(const std::vector<ColumnFamilyOptions> options = {}) {
|
||||
std::vector<std::string> names;
|
||||
for (auto name : names_) {
|
||||
for (const auto& name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
@@ -607,7 +607,7 @@ TEST_P(FlushEmptyCFTestWithParam, FlushEmptyCFTest) {
|
||||
// Preserve file system state up to here to simulate a crash condition.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
std::vector<std::string> names;
|
||||
for (auto name : names_) {
|
||||
for (const auto& name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
@@ -669,7 +669,7 @@ TEST_P(FlushEmptyCFTestWithParam, FlushEmptyCFTest2) {
|
||||
// Preserve file system state up to here to simulate a crash condition.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
std::vector<std::string> names;
|
||||
for (auto name : names_) {
|
||||
for (const auto& name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
@@ -1034,7 +1034,7 @@ TEST_P(ColumnFamilyTest, CrashAfterFlush) {
|
||||
fault_env->SetFilesystemActive(false);
|
||||
|
||||
std::vector<std::string> names;
|
||||
for (auto name : names_) {
|
||||
for (const auto& name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
@@ -3407,9 +3407,13 @@ TEST_P(ColumnFamilyTest, DISABLED_LogTruncationTest) {
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (!(ParseFileName(filenames[i], &number, &type))) continue;
|
||||
if (!(ParseFileName(filenames[i], &number, &type))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type != kWalFile) continue;
|
||||
if (type != kWalFile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
logfs.push_back(filenames[i]);
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ class CompactFilesTest : public testing::Test {
|
||||
// A class which remembers the name of each flushed file.
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
FlushedFileCollector() = default;
|
||||
~FlushedFileCollector() override = default;
|
||||
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
@@ -45,7 +45,7 @@ class FlushedFileCollector : public EventListener {
|
||||
std::vector<std::string> GetFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> result;
|
||||
for (auto fname : flushed_files_) {
|
||||
for (const auto& fname : flushed_files_) {
|
||||
result.push_back(fname);
|
||||
}
|
||||
return result;
|
||||
@@ -159,7 +159,9 @@ TEST_F(CompactFilesTest, MultipleLevel) {
|
||||
// Compact files except the file in L3
|
||||
std::vector<std::string> files;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (i == 3) continue;
|
||||
if (i == 3) {
|
||||
continue;
|
||||
}
|
||||
for (auto& file : meta.levels[i].files) {
|
||||
files.push_back(file.db_path + "/" + file.name);
|
||||
}
|
||||
@@ -228,7 +230,7 @@ TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
ASSERT_OK(static_cast_with_check<DBImpl>(db)->TEST_WaitForCompact());
|
||||
|
||||
// verify all compaction input files are deleted
|
||||
for (auto fname : l0_files) {
|
||||
for (const auto& fname : l0_files) {
|
||||
ASSERT_EQ(Status::NotFound(), env_->FileExists(fname));
|
||||
}
|
||||
delete db;
|
||||
@@ -492,4 +494,3 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -160,7 +160,9 @@ std::vector<CompactionInputFiles> Compaction::PopulateWithAtomicBoundaries(
|
||||
AtomicCompactionUnitBoundary cur_boundary;
|
||||
size_t first_atomic_idx = 0;
|
||||
auto add_unit_boundary = [&](size_t to) {
|
||||
if (first_atomic_idx == to) return;
|
||||
if (first_atomic_idx == to) {
|
||||
return;
|
||||
}
|
||||
for (size_t k = first_atomic_idx; k < to; k++) {
|
||||
inputs[i].atomic_compaction_unit_boundaries.push_back(cur_boundary);
|
||||
}
|
||||
@@ -753,7 +755,9 @@ int InputSummary(const std::vector<FileMetaData*>& files, char* output,
|
||||
AppendHumanBytes(files.at(i)->fd.GetFileSize(), sztxt, 16);
|
||||
ret = snprintf(output + write, sz, "%" PRIu64 "(%s) ",
|
||||
files.at(i)->fd.GetNumber(), sztxt);
|
||||
if (ret < 0 || ret >= sz) break;
|
||||
if (ret < 0 || ret >= sz) {
|
||||
break;
|
||||
}
|
||||
write += ret;
|
||||
}
|
||||
// if files.size() is non-zero, overwrite the last space
|
||||
|
||||
@@ -44,6 +44,13 @@ struct CompactionIterationStats {
|
||||
uint64_t total_blob_bytes_read = 0;
|
||||
uint64_t num_blobs_relocated = 0;
|
||||
uint64_t total_blob_bytes_relocated = 0;
|
||||
|
||||
// TimedPut diagnostics
|
||||
// Total number of kTypeValuePreferredSeqno records encountered.
|
||||
uint64_t num_input_timed_put_records = 0;
|
||||
// Number of kTypeValuePreferredSeqno records we ended up swapping in
|
||||
// preferred seqno.
|
||||
uint64_t num_timed_put_swap_preferred_seqno = 0;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -491,6 +491,8 @@ void CompactionIterator::NextFromInput() {
|
||||
if (ikey_.type == kTypeDeletion || ikey_.type == kTypeSingleDeletion ||
|
||||
ikey_.type == kTypeDeletionWithTimestamp) {
|
||||
iter_stats_.num_input_deletion_records++;
|
||||
} else if (ikey_.type == kTypeValuePreferredSeqno) {
|
||||
iter_stats_.num_input_timed_put_records++;
|
||||
}
|
||||
iter_stats_.total_input_raw_key_bytes += key_.size();
|
||||
iter_stats_.total_input_raw_value_bytes += value_.size();
|
||||
@@ -618,7 +620,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// not compact out. We will keep this Put, but can drop it's data.
|
||||
// (See Optimization 3, below.)
|
||||
if (ikey_.type != kTypeValue && ikey_.type != kTypeBlobIndex &&
|
||||
ikey_.type != kTypeWideColumnEntity) {
|
||||
ikey_.type != kTypeWideColumnEntity &&
|
||||
ikey_.type != kTypeValuePreferredSeqno) {
|
||||
ROCKS_LOG_FATAL(info_log_, "Unexpected key %s for compaction output",
|
||||
ikey_.DebugString(allow_data_in_errors_, true).c_str());
|
||||
assert(false);
|
||||
@@ -632,7 +635,8 @@ void CompactionIterator::NextFromInput() {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex || ikey_.type == kTypeWideColumnEntity) {
|
||||
if (ikey_.type == kTypeBlobIndex || ikey_.type == kTypeWideColumnEntity ||
|
||||
ikey_.type == kTypeValuePreferredSeqno) {
|
||||
ikey_.type = kTypeValue;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
@@ -798,7 +802,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// happened
|
||||
if (next_ikey.type != kTypeValue &&
|
||||
next_ikey.type != kTypeBlobIndex &&
|
||||
next_ikey.type != kTypeWideColumnEntity) {
|
||||
next_ikey.type != kTypeWideColumnEntity &&
|
||||
next_ikey.type != kTypeValuePreferredSeqno) {
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
}
|
||||
|
||||
@@ -968,6 +973,50 @@ void CompactionIterator::NextFromInput() {
|
||||
validity_info_.SetValid(ValidContext::kKeepDel);
|
||||
at_next_ = true;
|
||||
}
|
||||
} else if (ikey_.type == kTypeValuePreferredSeqno &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
(bottommost_level_ ||
|
||||
(compaction_ != nullptr &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)))) {
|
||||
// This section that attempts to swap preferred sequence number will not
|
||||
// be invoked if this is a CompactionIterator created for flush, since
|
||||
// `compaction_` will be nullptr and it's not bottommost either.
|
||||
//
|
||||
// The entries with the same user key and smaller sequence numbers are
|
||||
// all in this earliest snapshot range to be iterated. Since those entries
|
||||
// will be hidden by this entry [rule A], it's safe to swap in the
|
||||
// preferred seqno now.
|
||||
//
|
||||
// It's otherwise not safe to swap in the preferred seqno since it's
|
||||
// possible for entries in earlier snapshots to have sequence number that
|
||||
// is smaller than this entry's sequence number but bigger than this
|
||||
// entry's preferred sequence number. Swapping in the preferred sequence
|
||||
// number will break the internal key ordering invariant for this key.
|
||||
//
|
||||
// A special case involving range deletion is handled separately below.
|
||||
auto [unpacked_value, preferred_seqno] =
|
||||
ParsePackedValueWithSeqno(value_);
|
||||
assert(preferred_seqno < ikey_.sequence);
|
||||
InternalKey ikey_after_swap(ikey_.user_key, preferred_seqno, kTypeValue);
|
||||
Slice ikey_after_swap_slice(*ikey_after_swap.rep());
|
||||
if (range_del_agg_->ShouldDelete(
|
||||
ikey_after_swap_slice,
|
||||
RangeDelPositioningMode::kForwardTraversal)) {
|
||||
// A range tombstone that doesn't cover this kTypeValuePreferredSeqno
|
||||
// entry may end up covering the entry, so it's not safe to swap
|
||||
// preferred sequence number. In this case, we output the entry as is.
|
||||
validity_info_.SetValid(ValidContext::kNewUserKey);
|
||||
} else {
|
||||
iter_stats_.num_timed_put_swap_preferred_seqno++;
|
||||
ikey_.sequence = preferred_seqno;
|
||||
ikey_.type = kTypeValue;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
key_ = current_key_.GetInternalKey();
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
value_ = unpacked_value;
|
||||
validity_info_.SetValid(ValidContext::kSwapPreferredSeqno);
|
||||
}
|
||||
} else if (ikey_.type == kTypeMerge) {
|
||||
if (!merge_helper_->HasOperator()) {
|
||||
status_ = Status::InvalidArgument(
|
||||
|
||||
@@ -410,6 +410,7 @@ class CompactionIterator {
|
||||
kKeepDel = 9,
|
||||
kNewUserKey = 10,
|
||||
kRangeDeletion = 11,
|
||||
kSwapPreferredSeqno = 12,
|
||||
};
|
||||
|
||||
struct ValidityInfo {
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
std::string ValueWithPreferredSeqno(std::string val,
|
||||
SequenceNumber preferred_seqno = 0) {
|
||||
std::string result = val;
|
||||
PutFixed64(&result, preferred_seqno);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Expects no merging attempts.
|
||||
class NoMergingMergeOp : public MergeOperator {
|
||||
@@ -392,6 +400,17 @@ TEST_P(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
// Tests compatibility of TimedPut and SingleDelete. TimedPut should act as if
|
||||
// it's a Put.
|
||||
TEST_P(CompactionIteratorTest, TimedPutAndSingleDelete) {
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValuePreferredSeqno)},
|
||||
{"", "val"}, {}, {}, 5);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValue),
|
||||
test::KeyStr("morning", 2, kTypeValue),
|
||||
@@ -431,6 +450,31 @@ TEST_P(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
// Tests compatibility of TimedPut and Range delete. TimedPut should act as if
|
||||
// it's a Put.
|
||||
TEST_P(CompactionIteratorTest, TimedPutAndRangeDeletion) {
|
||||
InitIterators(
|
||||
{test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("morning", 2, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("night", 3, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("zao5"), ValueWithPreferredSeqno("zao2"),
|
||||
ValueWithPreferredSeqno("wan")},
|
||||
{test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 5);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
c_iter_->key().ToString());
|
||||
ASSERT_EQ(ValueWithPreferredSeqno("zao5"), c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 3, kTypeValuePreferredSeqno),
|
||||
c_iter_->key().ToString());
|
||||
ASSERT_EQ(ValueWithPreferredSeqno("wan"), c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
class Filter : public CompactionFilter {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
@@ -502,9 +546,11 @@ TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
test::KeyStr("f", 25, kTypeValue), test::KeyStr("g", 90, kTypeValue),
|
||||
test::KeyStr("h", 91, kTypeValue), // keep
|
||||
test::KeyStr("i", 95, kTypeMerge), // skip to "z"
|
||||
test::KeyStr("j", 99, kTypeValue)},
|
||||
test::KeyStr("j", 99, kTypeValue),
|
||||
test::KeyStr("k", 100, kTypeValuePreferredSeqno)},
|
||||
{"av50", "am45", "bv60", "bv40", "cv35", "dm70", "em71", "fm65", "fm30",
|
||||
"fv25", "gv90", "hv91", "im95", "jv99"},
|
||||
"fv25", "gv90", "hv91", "im95", "jv99",
|
||||
ValueWithPreferredSeqno("kv100")},
|
||||
{}, {}, kMaxSequenceNumber, kMaxSequenceNumber, &merge_op, &filter);
|
||||
|
||||
// Compaction should output just "a", "e" and "h" keys.
|
||||
@@ -614,87 +660,87 @@ TEST_P(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
EXPECT_EQ(2, filter.last_seen.load());
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
class Filter : public CompactionFilter {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
class Filter : public CompactionFilter {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
|
||||
// See InitIterators() call below for the sequence of keys and their
|
||||
// filtering decisions. Here we closely assert that compaction filter is
|
||||
// called with the expected keys and only them, and with the right values.
|
||||
if (k == "a") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
EXPECT_EQ("av1", v);
|
||||
return Decision::kKeep;
|
||||
} else if (k == "b") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
return Decision::kKeep;
|
||||
} else if (k == "c") {
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
ADD_FAILURE();
|
||||
// See InitIterators() call below for the sequence of keys and their
|
||||
// filtering decisions. Here we closely assert that compaction filter is
|
||||
// called with the expected keys and only them, and with the right values.
|
||||
if (k == "a") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
EXPECT_EQ("av1", v);
|
||||
return Decision::kKeep;
|
||||
} else if (k == "b") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
return Decision::kKeep;
|
||||
} else if (k == "c") {
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest.SingleMergeOperand::Filter";
|
||||
}
|
||||
};
|
||||
ADD_FAILURE();
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
class SingleMergeOp : public MergeOperator {
|
||||
public:
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
// See InitIterators() call below for why "c" is the only key for which
|
||||
// FullMergeV2 should be called.
|
||||
EXPECT_EQ("c", merge_in.key.ToString());
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest.SingleMergeOperand::Filter";
|
||||
}
|
||||
};
|
||||
|
||||
std::string temp_value;
|
||||
if (merge_in.existing_value != nullptr) {
|
||||
temp_value = merge_in.existing_value->ToString();
|
||||
}
|
||||
class SingleMergeOp : public MergeOperator {
|
||||
public:
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
// See InitIterators() call below for why "c" is the only key for which
|
||||
// FullMergeV2 should be called.
|
||||
EXPECT_EQ("c", merge_in.key.ToString());
|
||||
|
||||
for (auto& operand : merge_in.operand_list) {
|
||||
temp_value.append(operand.ToString());
|
||||
}
|
||||
merge_out->new_value = temp_value;
|
||||
|
||||
return true;
|
||||
std::string temp_value;
|
||||
if (merge_in.existing_value != nullptr) {
|
||||
temp_value = merge_in.existing_value->ToString();
|
||||
}
|
||||
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* /*logger*/) const override {
|
||||
std::string string_key = key.ToString();
|
||||
EXPECT_TRUE(string_key == "a" || string_key == "b");
|
||||
for (auto& operand : merge_in.operand_list) {
|
||||
temp_value.append(operand.ToString());
|
||||
}
|
||||
merge_out->new_value = temp_value;
|
||||
|
||||
if (string_key == "a") {
|
||||
EXPECT_EQ(1, operand_list.size());
|
||||
} else if (string_key == "b") {
|
||||
EXPECT_EQ(2, operand_list.size());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string temp_value;
|
||||
for (auto& operand : operand_list) {
|
||||
temp_value.append(operand.ToString());
|
||||
}
|
||||
swap(temp_value, *new_value);
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* /*logger*/) const override {
|
||||
std::string string_key = key.ToString();
|
||||
EXPECT_TRUE(string_key == "a" || string_key == "b");
|
||||
|
||||
return true;
|
||||
if (string_key == "a") {
|
||||
EXPECT_EQ(1, operand_list.size());
|
||||
} else if (string_key == "b") {
|
||||
EXPECT_EQ(2, operand_list.size());
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest SingleMergeOp";
|
||||
std::string temp_value;
|
||||
for (auto& operand : operand_list) {
|
||||
temp_value.append(operand.ToString());
|
||||
}
|
||||
swap(temp_value, *new_value);
|
||||
|
||||
bool AllowSingleOperand() const override { return true; }
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest SingleMergeOp";
|
||||
}
|
||||
|
||||
bool AllowSingleOperand() const override { return true; }
|
||||
};
|
||||
|
||||
TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
SingleMergeOp merge_op;
|
||||
Filter filter;
|
||||
InitIterators(
|
||||
@@ -719,6 +765,24 @@ TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
ASSERT_EQ("cv1cv2", c_iter_->value().ToString());
|
||||
}
|
||||
|
||||
// Tests compatibility of TimedPut and Merge operation. When a TimedPut is
|
||||
// merged with some merge operand in compaction, it will become a regular Put
|
||||
// and lose its preferred sequence number.
|
||||
TEST_P(CompactionIteratorTest, TimedPutAndMerge) {
|
||||
SingleMergeOp merge_op;
|
||||
Filter filter;
|
||||
InitIterators({test::KeyStr("c", 90, kTypeMerge),
|
||||
test::KeyStr("c", 80, kTypeValuePreferredSeqno)},
|
||||
{"cv2", ValueWithPreferredSeqno("cv1")}, {}, {},
|
||||
kMaxSequenceNumber, kMaxSequenceNumber, &merge_op, &filter);
|
||||
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("c", 90, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_OK(c_iter_->status());
|
||||
ASSERT_EQ("cv1cv2", c_iter_->value().ToString());
|
||||
}
|
||||
|
||||
// In bottommost level, values earlier than earliest snapshot can be output
|
||||
// with sequence = 0.
|
||||
TEST_P(CompactionIteratorTest, ZeroOutSequenceAtBottomLevel) {
|
||||
@@ -963,6 +1027,22 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Value) {
|
||||
{"v4", "v3", "v1"}, 3 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_TimedPut) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest({test::KeyStr("foo", 4, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 3, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 2, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 1, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("v4"), ValueWithPreferredSeqno("v3"),
|
||||
ValueWithPreferredSeqno("v2"), ValueWithPreferredSeqno("v1")},
|
||||
{test::KeyStr("foo", 4, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 3, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 1, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("v4"), ValueWithPreferredSeqno("v3"),
|
||||
ValueWithPreferredSeqno("v1")},
|
||||
3 /*last_committed_seq*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, DedupSameSnapshot_Deletion) {
|
||||
AddSnapshot(2, 1);
|
||||
RunTest(
|
||||
@@ -1128,6 +1208,114 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
2 /* earliest_write_conflict_snapshot */);
|
||||
}
|
||||
|
||||
// Same as above but with a value with preferred seqno entry. In addition to the
|
||||
// value getting trimmed, the type of the KV is changed to kTypeValue.
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
KeepSingleDeletionForWriteConflictChecking_TimedPut) {
|
||||
AddSnapshot(2, 0);
|
||||
RunTest({test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValuePreferredSeqno)},
|
||||
{"", ValueWithPreferredSeqno("v1")},
|
||||
{test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValue)},
|
||||
{"", ""}, 2 /* last_committed_seq */, nullptr /* merge_operator */,
|
||||
nullptr /* compaction_filter */, false /* bottommost_level */,
|
||||
2 /* earliest_write_conflict_snapshot */);
|
||||
}
|
||||
|
||||
// Tests when a kTypeValuePreferredSeqno entry can have its preferred sequence
|
||||
// number swapped in. The required and sufficient conditions for an entry's
|
||||
// preferred sequence number to get swapped in are:
|
||||
// 1) The entry is visible to the earliest snapshot, AND
|
||||
// 2) No more entries with the same user key on lower levels, AND
|
||||
// This is either because:
|
||||
// 2a) This is a compaction to the bottommost level, OR
|
||||
// 2b) Keys do not exist beyond output level
|
||||
// 3) The entry will not resurface a range deletion entry after swapping in the
|
||||
// preferred sequence number.
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_NotVisibleToEarliestSnapshot_NoSwapPreferredSeqno) {
|
||||
AddSnapshot(3);
|
||||
RunTest({test::KeyStr("bar", 5, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("bv2", 2)},
|
||||
{test::KeyStr("bar", 5, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("bv2", 2), "bv1"}, 5 /*last_committed_seq*/,
|
||||
nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
|
||||
true /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
true /*key_not_exists_beyond_output_level*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_MoreEntriesInLowerLevels_NoSwapPreferredSeqno) {
|
||||
// This tests mimics more entries in lower levels with `bottommost_level` and
|
||||
// `key_not_exists_beyond_output_level` set to false.
|
||||
RunTest({test::KeyStr("bar", 5, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("bv2", 2)},
|
||||
{test::KeyStr("bar", 5, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("bv2", 2)}, 5 /*last_committed_seq*/,
|
||||
nullptr /*merge_operator*/, nullptr /*compaction_filter*/,
|
||||
false /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
false /*key_not_exists_beyond_output_level*/);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_WillBeHiddenByRangeDeletionAfterSwap_NoSwap) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("night", 6, kTypeValue)},
|
||||
{ValueWithPreferredSeqno("zao", 3), "wan"},
|
||||
{test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 6,
|
||||
kMaxSequenceNumber /*last_committed_sequence*/,
|
||||
nullptr /*merge_op*/, nullptr /*filter*/,
|
||||
false /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
true /*key_not_exists_beyond_output_level*/);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("morning", 5, kTypeValuePreferredSeqno),
|
||||
c_iter_->key().ToString());
|
||||
ASSERT_EQ(ValueWithPreferredSeqno("zao", 3), c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 6, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_EQ("wan", c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
ASSERT_OK(c_iter_->status());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_BottomMostLevelVisibleToEarliestSnapshot_SwapPreferredSeqno) {
|
||||
// Preferred seqno got swapped in and also zeroed out as a bottommost level
|
||||
// optimization.
|
||||
RunTest(
|
||||
{test::KeyStr("bar", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("bar", 4, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 6, kTypeValue)},
|
||||
{ValueWithPreferredSeqno("bv2", 2), ValueWithPreferredSeqno("bv1", 1),
|
||||
"fv1"},
|
||||
{test::KeyStr("bar", 0, kTypeValue), test::KeyStr("foo", 0, kTypeValue)},
|
||||
{"bv2", "fv1"}, 6 /*last_committed_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, true /*bottommost_level*/);
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CompactionIteratorWithSnapshotCheckerTest,
|
||||
TimedPut_NonBottomMostLevelVisibleToEarliestSnapshot_SwapPreferredSeqno) {
|
||||
RunTest(
|
||||
{test::KeyStr("bar", 5, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("bar", 4, kTypeValuePreferredSeqno),
|
||||
test::KeyStr("foo", 6, kTypeValue)},
|
||||
{ValueWithPreferredSeqno("bv2", 2), ValueWithPreferredSeqno("bv1", 1),
|
||||
"fv1"},
|
||||
{test::KeyStr("bar", 2, kTypeValue), test::KeyStr("foo", 6, kTypeValue)},
|
||||
{"bv2", "fv1"}, 6 /*last_committed_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, false /*bottommost_level*/,
|
||||
kMaxSequenceNumber /*earliest_write_conflict_snapshot*/,
|
||||
true /*key_not_exists_beyond_output_level*/);
|
||||
}
|
||||
|
||||
// Compaction filter should keep uncommitted key as-is, and
|
||||
// * Convert the latest value to deletion, and/or
|
||||
// * if latest value is a merge, apply filter to all subsequent merges.
|
||||
@@ -1145,6 +1333,22 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_Value) {
|
||||
nullptr /*merge_operator*/, compaction_filter.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_TimedPut) {
|
||||
// TODO(yuzhangyu): Add support for this type in compaction filter.
|
||||
// Type kTypeValuePreferredSeqno is not explicitly exposed in the compaction
|
||||
// filter API, so users can not operate on it through compaction filter API
|
||||
// to remove/purge/change value etc. But this type of entry can be impacted by
|
||||
// other entries' filter result, currently only kRemoveAndSkip type of result
|
||||
// can affect it.
|
||||
std::unique_ptr<CompactionFilter> compaction_filter(
|
||||
new FilterAllKeysCompactionFilter());
|
||||
RunTest({test::KeyStr("a", 2, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("v1")},
|
||||
{test::KeyStr("a", 2, kTypeValuePreferredSeqno)},
|
||||
{ValueWithPreferredSeqno("v1")}, 2 /*last_committed_seq*/,
|
||||
nullptr /*merge_operator*/, compaction_filter.get());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest, CompactionFilter_Deletion) {
|
||||
std::unique_ptr<CompactionFilter> compaction_filter(
|
||||
new FilterAllKeysCompactionFilter());
|
||||
|
||||
@@ -404,7 +404,9 @@ void CompactionJob::AcquireSubcompactionResources(
|
||||
|
||||
void CompactionJob::ShrinkSubcompactionResources(uint64_t num_extra_resources) {
|
||||
// Do nothing when we have zero resources to shrink
|
||||
if (num_extra_resources == 0) return;
|
||||
if (num_extra_resources == 0) {
|
||||
return;
|
||||
}
|
||||
db_mutex_->Lock();
|
||||
// We cannot release threads more than what we reserved before
|
||||
int extra_num_subcompaction_threads_released = env_->ReleaseThreads(
|
||||
@@ -584,7 +586,9 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::GenSubcompactionBoundaries:0",
|
||||
&num_planned_subcompactions);
|
||||
if (num_planned_subcompactions == 1) return;
|
||||
if (num_planned_subcompactions == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Group the ranges into subcompactions
|
||||
uint64_t target_range_size = std::max(
|
||||
@@ -641,7 +645,7 @@ Status CompactionJob::Run() {
|
||||
|
||||
// Always schedule the first subcompaction (whether or not there are also
|
||||
// others) in the current thread to be efficient with resources
|
||||
ProcessKeyValueCompaction(&compact_->sub_compact_states[0]);
|
||||
ProcessKeyValueCompaction(compact_->sub_compact_states.data());
|
||||
|
||||
// Wait for all other threads (if there are any) to finish execution
|
||||
for (auto& thread : thread_pool) {
|
||||
@@ -1850,13 +1854,14 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
// Pass temperature of the last level files to FileSystem.
|
||||
FileOptions fo_copy = file_options_;
|
||||
Temperature temperature = sub_compact->compaction->output_temperature();
|
||||
// only set for the last level compaction and also it's not output to
|
||||
// penultimate level (when preclude_last_level feature is enabled)
|
||||
if (temperature == Temperature::kUnknown &&
|
||||
Temperature last_level_temp =
|
||||
sub_compact->compaction->mutable_cf_options()->last_level_temperature;
|
||||
// Here last_level_temperature supersedes default_write_temperature, when
|
||||
// enabled and applicable
|
||||
if (last_level_temp != Temperature::kUnknown &&
|
||||
sub_compact->compaction->is_last_level() &&
|
||||
!sub_compact->IsCurrentPenultimateLevel()) {
|
||||
temperature =
|
||||
sub_compact->compaction->mutable_cf_options()->last_level_temperature;
|
||||
temperature = last_level_temp;
|
||||
}
|
||||
fo_copy.temperature = temperature;
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/plain/plain_table_factory.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
@@ -131,7 +130,7 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
ColumnFamilyOptions cf_opts(options);
|
||||
size_t cfi = handles_.size();
|
||||
handles_.resize(cfi + cfs.size());
|
||||
for (auto cf : cfs) {
|
||||
for (const auto& cf : cfs) {
|
||||
ASSERT_OK(db_->CreateColumnFamily(cf_opts, cf, &handles_[cfi++]));
|
||||
}
|
||||
}
|
||||
@@ -160,7 +159,7 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
EXPECT_EQ(cfs.size(), options.size());
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (size_t i = 0; i < cfs.size(); ++i) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(cfs[i], options[i]));
|
||||
column_families.emplace_back(cfs[i], options[i]);
|
||||
}
|
||||
DBOptions db_opts = DBOptions(options[0]);
|
||||
return DB::Open(db_opts, dbname_, column_families, &handles_, &db_);
|
||||
|
||||
@@ -308,7 +308,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
kDefaultColumnFamilyName, -1 /* level */),
|
||||
file_writer.get()));
|
||||
// Build table.
|
||||
for (auto kv : contents) {
|
||||
for (const auto& kv : contents) {
|
||||
std::string key;
|
||||
std::string value;
|
||||
std::tie(key, value) = kv;
|
||||
@@ -327,7 +327,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
SequenceNumber smallest_seqno = kMaxSequenceNumber;
|
||||
SequenceNumber largest_seqno = 0;
|
||||
uint64_t oldest_blob_file_number = kInvalidBlobFileNumber;
|
||||
for (auto kv : contents) {
|
||||
for (const auto& kv : contents) {
|
||||
ParsedInternalKey key;
|
||||
std::string skey;
|
||||
std::string value;
|
||||
@@ -1473,7 +1473,7 @@ TEST_F(CompactionJobTest, OldestBlobFileNumber) {
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTest, VerifyPenultimateLevelOutput) {
|
||||
cf_options_.bottommost_temperature = Temperature::kCold;
|
||||
cf_options_.last_level_temperature = Temperature::kCold;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"Compaction::SupportsPerKeyPlacement:Enabled", [&](void* arg) {
|
||||
auto supports_per_key_placement = static_cast<bool*>(arg);
|
||||
|
||||
@@ -130,7 +130,7 @@ CompactionPicker::CompactionPicker(const ImmutableOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: ioptions_(ioptions), icmp_(icmp) {}
|
||||
|
||||
CompactionPicker::~CompactionPicker() {}
|
||||
CompactionPicker::~CompactionPicker() = default;
|
||||
|
||||
// Delete this compaction from the list of running compactions.
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
|
||||
@@ -377,7 +377,8 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
output_level, compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id, compression_type,
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_options.max_subcompactions,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
@@ -664,7 +665,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
@@ -852,8 +854,9 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionType(vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
std::move(grandparents), /* is manual */ true, trim_ts, /* score */ -1,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
/* is manual */ true, trim_ts, /* score */ -1,
|
||||
/* deletion_compaction */ false, /* l0_files_might_overlap */ true,
|
||||
CompactionReason::kUnknown,
|
||||
compact_range_options.blob_garbage_collection_policy,
|
||||
|
||||
@@ -116,7 +116,8 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, /* l0_files_might_overlap */ true,
|
||||
@@ -185,7 +186,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
{comp_inputs}, 0, 16 * 1024 * 1024 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
0 /* max_subcompactions */, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
@@ -280,7 +282,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
/* target_file_size */ 0,
|
||||
/* max_compaction_bytes */ 0,
|
||||
/* output_path_id */ 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
/* trim_ts */ "", vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true,
|
||||
@@ -414,6 +417,7 @@ Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
if (inputs[0].files.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(compaction_target_temp != Temperature::kLastTemperature);
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
|
||||
@@ -355,7 +355,9 @@ void LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion() {
|
||||
TEST_SYNC_POINT("LevelCompactionPicker::RoundRobin");
|
||||
|
||||
// Only expand the inputs when we have selected a file in start_level_inputs_
|
||||
if (start_level_inputs_.size() == 0) return;
|
||||
if (start_level_inputs_.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t start_lvl_bytes_no_compacting = 0;
|
||||
uint64_t curr_bytes_to_compact = 0;
|
||||
@@ -550,7 +552,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level_,
|
||||
vstorage_->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
Temperature::kUnknown,
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
|
||||
l0_files_might_overlap, compaction_reason_);
|
||||
|
||||
@@ -77,7 +77,7 @@ class CompactionPickerTestBase : public testing::Test {
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
}
|
||||
|
||||
~CompactionPickerTestBase() override {}
|
||||
~CompactionPickerTestBase() override = default;
|
||||
|
||||
void NewVersionStorage(int num_levels, CompactionStyle style) {
|
||||
DeleteVersionStorage();
|
||||
@@ -214,7 +214,7 @@ class CompactionPickerTest : public CompactionPickerTestBase {
|
||||
explicit CompactionPickerTest()
|
||||
: CompactionPickerTestBase(BytewiseComparator()) {}
|
||||
|
||||
~CompactionPickerTest() override {}
|
||||
~CompactionPickerTest() override = default;
|
||||
};
|
||||
|
||||
class CompactionPickerU64TsTest : public CompactionPickerTestBase {
|
||||
@@ -222,7 +222,7 @@ class CompactionPickerU64TsTest : public CompactionPickerTestBase {
|
||||
explicit CompactionPickerU64TsTest()
|
||||
: CompactionPickerTestBase(test::BytewiseComparatorWithU64TsWrapper()) {}
|
||||
|
||||
~CompactionPickerU64TsTest() override {}
|
||||
~CompactionPickerU64TsTest() override = default;
|
||||
};
|
||||
|
||||
TEST_F(CompactionPickerTest, Empty) {
|
||||
|
||||
@@ -870,7 +870,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
output_level, 1, enable_compression),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_,
|
||||
output_level, enable_compression),
|
||||
Temperature::kUnknown,
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, grandparents,
|
||||
/* is manual */ false, /* trim_ts */ "", score_,
|
||||
false /* deletion_compaction */,
|
||||
@@ -1204,7 +1204,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
|
||||
true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
@@ -1347,7 +1347,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
/* max_grandparent_overlap_bytes */ GetMaxOverlappingBytes(), path_id,
|
||||
GetCompressionType(vstorage_, mutable_cf_options_, output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
Temperature::kUnknown,
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, grandparents, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true,
|
||||
@@ -1440,7 +1440,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
mutable_cf_options_.default_write_temperature,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
/* trim_ts */ "", score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ true, compaction_reason);
|
||||
|
||||
@@ -74,24 +74,24 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
compaction_input.output_level, input_files_oss.str().c_str());
|
||||
CompactionServiceJobInfo info(dbname_, db_id_, db_session_id_,
|
||||
GetCompactionId(sub_compact), thread_pri_);
|
||||
CompactionServiceJobStatus compaction_status =
|
||||
db_options_.compaction_service->StartV2(info, compaction_input_binary);
|
||||
switch (compaction_status) {
|
||||
CompactionServiceScheduleResponse response =
|
||||
db_options_.compaction_service->Schedule(info, compaction_input_binary);
|
||||
switch (response.status) {
|
||||
case CompactionServiceJobStatus::kSuccess:
|
||||
break;
|
||||
case CompactionServiceJobStatus::kFailure:
|
||||
sub_compact->status = Status::Incomplete(
|
||||
"CompactionService failed to start compaction job.");
|
||||
"CompactionService failed to schedule a remote compaction job.");
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction failed to start.",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return compaction_status;
|
||||
return response.status;
|
||||
case CompactionServiceJobStatus::kUseLocal:
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API Start.",
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API (Schedule)",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return compaction_status;
|
||||
return response.status;
|
||||
default:
|
||||
assert(false); // unknown status
|
||||
break;
|
||||
@@ -101,14 +101,15 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
"[%s] [JOB %d] Waiting for remote compaction...",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
std::string compaction_result_binary;
|
||||
compaction_status = db_options_.compaction_service->WaitForCompleteV2(
|
||||
info, &compaction_result_binary);
|
||||
CompactionServiceJobStatus compaction_status =
|
||||
db_options_.compaction_service->Wait(response.scheduled_job_id,
|
||||
&compaction_result_binary);
|
||||
|
||||
if (compaction_status == CompactionServiceJobStatus::kUseLocal) {
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API "
|
||||
"WaitForComplete.",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Remote compaction fallback to local by API (Wait)",
|
||||
compaction_input.column_family.name.c_str(), job_id_);
|
||||
return compaction_status;
|
||||
}
|
||||
|
||||
@@ -830,4 +831,3 @@ bool CompactionServiceInput::TEST_Equals(CompactionServiceInput* other,
|
||||
}
|
||||
#endif // NDEBUG
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -31,40 +31,44 @@ class MyTestCompactionService : public CompactionService {
|
||||
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
CompactionServiceJobStatus StartV2(
|
||||
CompactionServiceScheduleResponse Schedule(
|
||||
const CompactionServiceJobInfo& info,
|
||||
const std::string& compaction_service_input) override {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
start_info_ = info;
|
||||
assert(info.db_name == db_path_);
|
||||
jobs_.emplace(info.job_id, compaction_service_input);
|
||||
CompactionServiceJobStatus s = CompactionServiceJobStatus::kSuccess;
|
||||
if (is_override_start_status_) {
|
||||
return override_start_status_;
|
||||
}
|
||||
return s;
|
||||
std::string unique_id = Env::Default()->GenerateUniqueId();
|
||||
jobs_.emplace(unique_id, compaction_service_input);
|
||||
infos_.emplace(unique_id, info);
|
||||
CompactionServiceScheduleResponse response(
|
||||
unique_id, is_override_start_status_
|
||||
? override_start_status_
|
||||
: CompactionServiceJobStatus::kSuccess);
|
||||
return response;
|
||||
}
|
||||
|
||||
CompactionServiceJobStatus WaitForCompleteV2(
|
||||
const CompactionServiceJobInfo& info,
|
||||
std::string* compaction_service_result) override {
|
||||
CompactionServiceJobStatus Wait(const std::string& scheduled_job_id,
|
||||
std::string* result) override {
|
||||
std::string compaction_input;
|
||||
assert(info.db_name == db_path_);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
wait_info_ = info;
|
||||
auto i = jobs_.find(info.job_id);
|
||||
if (i == jobs_.end()) {
|
||||
auto job_index = jobs_.find(scheduled_job_id);
|
||||
if (job_index == jobs_.end()) {
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
compaction_input = std::move(i->second);
|
||||
jobs_.erase(i);
|
||||
}
|
||||
compaction_input = std::move(job_index->second);
|
||||
jobs_.erase(job_index);
|
||||
|
||||
auto info_index = infos_.find(scheduled_job_id);
|
||||
if (info_index == infos_.end()) {
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
wait_info_ = std::move(info_index->second);
|
||||
infos_.erase(info_index);
|
||||
}
|
||||
if (is_override_wait_status_) {
|
||||
return override_wait_status_;
|
||||
}
|
||||
|
||||
CompactionServiceOptionsOverride options_override;
|
||||
options_override.env = options_.env;
|
||||
options_override.file_checksum_gen_factory =
|
||||
@@ -90,11 +94,11 @@ class MyTestCompactionService : public CompactionService {
|
||||
OpenAndCompactOptions options;
|
||||
options.canceled = &canceled_;
|
||||
|
||||
Status s = DB::OpenAndCompact(
|
||||
options, db_path_, db_path_ + "/" + std::to_string(info.job_id),
|
||||
compaction_input, compaction_service_result, options_override);
|
||||
Status s =
|
||||
DB::OpenAndCompact(options, db_path_, db_path_ + "/" + scheduled_job_id,
|
||||
compaction_input, result, options_override);
|
||||
if (is_override_wait_result_) {
|
||||
*compaction_service_result = override_wait_result_;
|
||||
*result = override_wait_result_;
|
||||
}
|
||||
compaction_num_.fetch_add(1);
|
||||
if (s.ok()) {
|
||||
@@ -135,7 +139,8 @@ class MyTestCompactionService : public CompactionService {
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic_int compaction_num_{0};
|
||||
std::map<uint64_t, std::string> jobs_;
|
||||
std::map<std::string, std::string> jobs_;
|
||||
std::map<std::string, CompactionServiceJobInfo> infos_;
|
||||
const std::string db_path_;
|
||||
Options options_;
|
||||
std::shared_ptr<Statistics> statistics_;
|
||||
@@ -172,6 +177,7 @@ class CompactionServiceTest : public DBTestBase {
|
||||
remote_table_properties_collector_factories);
|
||||
options->compaction_service = compaction_service_;
|
||||
DestroyAndReopen(*options);
|
||||
CreateAndReopenWithCF({"cf_1", "cf_2", "cf_3"}, *options);
|
||||
}
|
||||
|
||||
Statistics* GetCompactorStatistics() { return compactor_statistics_.get(); }
|
||||
@@ -183,36 +189,45 @@ class CompactionServiceTest : public DBTestBase {
|
||||
return static_cast_with_check<MyTestCompactionService>(cs);
|
||||
}
|
||||
|
||||
void GenerateTestData() {
|
||||
// Generate 20 files @ L2
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
|
||||
void GenerateTestData(bool move_files_manually = false) {
|
||||
// Generate 20 files @ L2 Per CF
|
||||
for (int cf_id = 0; cf_id < static_cast<int>(handles_.size()); cf_id++) {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(cf_id, Key(key_id), "value" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush(cf_id));
|
||||
}
|
||||
if (move_files_manually) {
|
||||
MoveFilesToLevel(2, cf_id);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
// Generate 10 files @ L1 overlap with all 20 files @ L2
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
// Generate 10 files @ L1 overlap with all 20 files @ L2
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(
|
||||
Put(cf_id, Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush(cf_id));
|
||||
}
|
||||
if (move_files_manually) {
|
||||
MoveFilesToLevel(1, cf_id);
|
||||
ASSERT_EQ(FilesPerLevel(cf_id), "0,10,20");
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(FilesPerLevel(), "0,10,20");
|
||||
}
|
||||
|
||||
void VerifyTestData() {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
for (int cf_id = 0; cf_id < static_cast<int>(handles_.size()); cf_id++) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(cf_id, Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,32 +249,10 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
Statistics* primary_statistics = GetPrimaryStatistics();
|
||||
Statistics* compactor_statistics = GetCompactorStatistics();
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
GenerateTestData();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
VerifyTestData();
|
||||
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
auto my_cs = GetCompactionService();
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), 1);
|
||||
|
||||
@@ -322,7 +315,8 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
assert(*id != kNullUniqueId64x2);
|
||||
verify_passed++;
|
||||
});
|
||||
Reopen(options);
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "cf_1", "cf_2", "cf_3"},
|
||||
options);
|
||||
ASSERT_GT(verify_passed, 0);
|
||||
Close();
|
||||
}
|
||||
@@ -490,26 +484,9 @@ TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
new PartialDeleteCompactionFilter());
|
||||
options.compaction_filter = delete_comp_filter.get();
|
||||
ReopenWithCompactionService(&options);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
GenerateTestData();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
@@ -551,17 +528,17 @@ TEST_F(CompactionServiceTest, ConcurrentCompaction) {
|
||||
options.level0_file_num_compaction_trigger = 100;
|
||||
options.max_background_jobs = 20;
|
||||
ReopenWithCompactionService(&options);
|
||||
GenerateTestData();
|
||||
GenerateTestData(true);
|
||||
|
||||
ColumnFamilyMetaData meta;
|
||||
db_->GetColumnFamilyMetaData(&meta);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
for (const auto& file : meta.levels[1].files) {
|
||||
threads.emplace_back(std::thread([&]() {
|
||||
threads.emplace_back([&]() {
|
||||
std::string fname = file.db_path + "/" + file.name;
|
||||
ASSERT_OK(db_->CompactFiles(CompactionOptions(), {fname}, 2));
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
@@ -570,14 +547,7 @@ TEST_F(CompactionServiceTest, ConcurrentCompaction) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
VerifyTestData();
|
||||
auto my_cs = GetCompactionService();
|
||||
ASSERT_EQ(my_cs->GetCompactionNum(), 10);
|
||||
ASSERT_EQ(FilesPerLevel(), "0,0,10");
|
||||
@@ -587,21 +557,7 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
Options options = CurrentOptions();
|
||||
ReopenWithCompactionService(&options);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
GenerateTestData();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
auto my_cs =
|
||||
static_cast_with_check<MyTestCompactionService>(GetCompactionService());
|
||||
@@ -676,32 +632,9 @@ TEST_F(CompactionServiceTest, FallbackLocalAuto) {
|
||||
|
||||
my_cs->OverrideStartStatus(CompactionServiceJobStatus::kUseLocal);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 10 + j;
|
||||
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int key_id = i * 20 + j * 2;
|
||||
ASSERT_OK(Put(Key(key_id), "value_new" + std::to_string(key_id)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
GenerateTestData();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// verify result
|
||||
for (int i = 0; i < 200; i++) {
|
||||
auto result = Get(Key(i));
|
||||
if (i % 2) {
|
||||
ASSERT_EQ(result, "value" + std::to_string(i));
|
||||
} else {
|
||||
ASSERT_EQ(result, "value_new" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
VerifyTestData();
|
||||
|
||||
ASSERT_EQ(my_cs->GetCompactionNum(), 0);
|
||||
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "test_util/mock_time_env.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
|
||||
class TieredCompactionTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
class TieredCompactionTest : public DBTestBase {
|
||||
public:
|
||||
TieredCompactionTest()
|
||||
: DBTestBase("tiered_compaction_test", /*env_do_fsync=*/true),
|
||||
@@ -123,14 +122,8 @@ class TieredCompactionTest : public DBTestBase,
|
||||
pl_stats.Clear();
|
||||
}
|
||||
|
||||
// bottommost_temperature is renaming to last_level_temperature, set either
|
||||
// of them should have the same effect.
|
||||
void SetColdTemperature(Options& options) {
|
||||
if (GetParam()) {
|
||||
options.bottommost_temperature = Temperature::kCold;
|
||||
} else {
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
}
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -172,7 +165,7 @@ class TieredCompactionTest : public DBTestBase,
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
TEST_F(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -334,7 +327,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
|
||||
TEST_P(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
TEST_F(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -508,7 +501,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_P(TieredCompactionTest, LevelColdRangeDelete) {
|
||||
TEST_F(TieredCompactionTest, LevelColdRangeDelete) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -614,7 +607,7 @@ class SingleKeySstPartitionerFactory : public SstPartitionerFactory {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(TieredCompactionTest, LevelOutofBoundaryRangeDelete) {
|
||||
TEST_F(TieredCompactionTest, LevelOutofBoundaryRangeDelete) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 3;
|
||||
const int kNumKeys = 10;
|
||||
@@ -743,7 +736,7 @@ TEST_P(TieredCompactionTest, LevelOutofBoundaryRangeDelete) {
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
|
||||
TEST_P(TieredCompactionTest, UniversalRangeDelete) {
|
||||
TEST_F(TieredCompactionTest, UniversalRangeDelete) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 10;
|
||||
@@ -875,7 +868,7 @@ TEST_P(TieredCompactionTest, UniversalRangeDelete) {
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
|
||||
TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -1099,7 +1092,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
|
||||
TEST_P(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
TEST_F(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
@@ -1240,10 +1233,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
db_->ReleaseSnapshot(temp_snap);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(TieredCompactionTest, TieredCompactionTest,
|
||||
testing::Bool());
|
||||
|
||||
TEST_P(TieredCompactionTest, CheckInternalKeyRange) {
|
||||
TEST_F(TieredCompactionTest, CheckInternalKeyRange) {
|
||||
// When compacting keys from the last level to penultimate level,
|
||||
// output to penultimate level should be within internal key range
|
||||
// of input files from penultimate level.
|
||||
@@ -1318,8 +1308,8 @@ TEST_P(TieredCompactionTest, CheckInternalKeyRange) {
|
||||
|
||||
class PrecludeLastLevelTest : public DBTestBase {
|
||||
public:
|
||||
PrecludeLastLevelTest()
|
||||
: DBTestBase("preclude_last_level_test", /*env_do_fsync=*/false) {
|
||||
PrecludeLastLevelTest(std::string test_name = "preclude_last_level_test")
|
||||
: DBTestBase(test_name, /*env_do_fsync=*/false) {
|
||||
mock_clock_ = std::make_shared<MockSystemClock>(env_->GetSystemClock());
|
||||
mock_clock_->SetCurrentTime(kMockStartTime);
|
||||
mock_env_ = std::make_unique<CompositeEnvWrapper>(env_, mock_clock_);
|
||||
@@ -1594,6 +1584,62 @@ TEST_F(PrecludeLastLevelTest, SmallPrecludeTime) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(PrecludeLastLevelTest, FastTrackTimedPutToLastLevel) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.preclude_last_level_data_seconds = 60;
|
||||
options.preserve_internal_time_seconds = 0;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.num_levels = kNumLevels;
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(rnd.Uniform(10) + 1));
|
||||
});
|
||||
|
||||
for (int i = 0; i < kNumKeys / 2; i++) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun([&] {
|
||||
mock_clock_->MockSleepForSeconds(static_cast<int>(rnd.Uniform(2)));
|
||||
});
|
||||
}
|
||||
// Create one file with regular Put.
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Create one file with TimedPut.
|
||||
// With above mock clock operations, write_unix_time 50 should be before
|
||||
// current_time - preclude_last_level_seconds.
|
||||
// These data are eligible to be put on the last level once written to db
|
||||
// and compaction will fast track them to the last level.
|
||||
for (int i = kNumKeys / 2; i < kNumKeys; i++) {
|
||||
ASSERT_OK(TimedPut(0, Key(i), rnd.RandomString(100), 50));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// TimedPut file moved to the last level immediately.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
|
||||
// Wait more than preclude_last_level time, Put file eventually moved to the
|
||||
// last level.
|
||||
mock_clock_->MockSleepForSeconds(100);
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
@@ -2211,6 +2257,309 @@ TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// Tests DBIter::GetProperty("rocksdb.iterator.write-time") return a data's
|
||||
// approximate write unix time.
|
||||
// Test Param:
|
||||
// 1) use tailing iterator or regular iterator (when it applies)
|
||||
class IteratorWriteTimeTest : public PrecludeLastLevelTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
IteratorWriteTimeTest() : PrecludeLastLevelTest("iterator_write_time_test") {}
|
||||
|
||||
uint64_t VerifyKeyAndGetWriteTime(Iterator* iter,
|
||||
const std::string& expected_key) {
|
||||
std::string prop;
|
||||
uint64_t write_time = 0;
|
||||
EXPECT_TRUE(iter->Valid());
|
||||
EXPECT_EQ(expected_key, iter->key());
|
||||
EXPECT_OK(iter->GetProperty("rocksdb.iterator.write-time", &prop));
|
||||
Slice prop_slice = prop;
|
||||
EXPECT_TRUE(GetFixed64(&prop_slice, &write_time));
|
||||
return write_time;
|
||||
}
|
||||
|
||||
void VerifyKeyAndWriteTime(Iterator* iter, const std::string& expected_key,
|
||||
uint64_t expected_write_time) {
|
||||
std::string prop;
|
||||
uint64_t write_time = 0;
|
||||
EXPECT_TRUE(iter->Valid());
|
||||
EXPECT_EQ(expected_key, iter->key());
|
||||
EXPECT_OK(iter->GetProperty("rocksdb.iterator.write-time", &prop));
|
||||
Slice prop_slice = prop;
|
||||
EXPECT_TRUE(GetFixed64(&prop_slice, &write_time));
|
||||
EXPECT_EQ(expected_write_time, write_time);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(IteratorWriteTimeTest, ReadFromMemtables) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
const int kSecondsPerRecording = 101;
|
||||
const int kKeyWithWriteTime = 25;
|
||||
const uint64_t kUserSpecifiedWriteTime =
|
||||
kMockStartTime + kSecondsPerRecording * 15;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.preserve_internal_time_seconds = 10000;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
if (i == kKeyWithWriteTime) {
|
||||
ASSERT_OK(
|
||||
TimedPut(Key(i), rnd.RandomString(100), kUserSpecifiedWriteTime));
|
||||
} else {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
}
|
||||
}
|
||||
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = GetParam();
|
||||
int i;
|
||||
|
||||
// Forward iteration
|
||||
uint64_t start_time = 0;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
for (iter->SeekToFirst(), i = 0; iter->Valid(); iter->Next(), i++) {
|
||||
if (start_time == 0) {
|
||||
start_time = VerifyKeyAndGetWriteTime(iter.get(), Key(i));
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), kUserSpecifiedWriteTime);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Backward iteration
|
||||
{
|
||||
ropts.tailing = false;
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
for (iter->SeekToLast(), i = kNumKeys - 1; iter->Valid();
|
||||
iter->Prev(), i--) {
|
||||
if (i == 0) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), start_time);
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), kUserSpecifiedWriteTime);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Reopen the DB and disable the seqno to time recording, data with user
|
||||
// specified write time can still get a write time before it's flushed.
|
||||
options.preserve_internal_time_seconds = 0;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(TimedPut(Key(kKeyWithWriteTime), rnd.RandomString(100),
|
||||
kUserSpecifiedWriteTime));
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->Seek(Key(kKeyWithWriteTime));
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(kKeyWithWriteTime),
|
||||
kUserSpecifiedWriteTime);
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->Seek(Key(kKeyWithWriteTime));
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(kKeyWithWriteTime),
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(IteratorWriteTimeTest, ReadFromSstFile) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kNumKeys = 100;
|
||||
const int kSecondsPerRecording = 101;
|
||||
const int kKeyWithWriteTime = 25;
|
||||
const uint64_t kUserSpecifiedWriteTime =
|
||||
kMockStartTime + kSecondsPerRecording * 15;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.preserve_internal_time_seconds = 10000;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeys; i++) {
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
if (i == kKeyWithWriteTime) {
|
||||
ASSERT_OK(
|
||||
TimedPut(Key(i), rnd.RandomString(100), kUserSpecifiedWriteTime));
|
||||
} else {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = GetParam();
|
||||
std::string prop;
|
||||
int i;
|
||||
|
||||
// Forward iteration
|
||||
uint64_t start_time = 0;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
for (iter->SeekToFirst(), i = 0; iter->Valid(); iter->Next(), i++) {
|
||||
if (start_time == 0) {
|
||||
start_time = VerifyKeyAndGetWriteTime(iter.get(), Key(i));
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
// It's not precisely kUserSpecifiedWriteTime, instead it has a margin
|
||||
// of error that is one recording apart while we convert write time to
|
||||
// sequence number, and then back to write time.
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
kUserSpecifiedWriteTime - kSecondsPerRecording);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Backward iteration
|
||||
{
|
||||
ropts.tailing = false;
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
for (iter->SeekToLast(), i = kNumKeys - 1; iter->Valid();
|
||||
iter->Prev(), i--) {
|
||||
if (i == 0) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), start_time);
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
kUserSpecifiedWriteTime - kSecondsPerRecording);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// Reopen the DB and disable the seqno to time recording. Data retrieved from
|
||||
// SST files still have write time available.
|
||||
options.preserve_internal_time_seconds = 0;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
ASSERT_OK(Put("a", "val"));
|
||||
ASSERT_TRUE(dbfull()->TEST_GetSeqnoToTimeMapping().Empty());
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
// "a" is retrieved from memtable, its write time is unknown because the
|
||||
// seqno to time mapping recording is not available.
|
||||
VerifyKeyAndWriteTime(iter.get(), "a",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
for (iter->Next(), i = 0; iter->Valid(); iter->Next(), i++) {
|
||||
if (i == 0) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), start_time);
|
||||
} else if (i == kKeyWithWriteTime) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
kUserSpecifiedWriteTime - kSecondsPerRecording);
|
||||
} else {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i),
|
||||
start_time + kSecondsPerRecording * (i + 1));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
// There is no write time info for "a" after it's flushed to SST file either.
|
||||
ASSERT_OK(Flush());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
VerifyKeyAndWriteTime(iter.get(), "a",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
// Sequence number zeroed out after compacted to the last level, write time
|
||||
// all becomes zero.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
for (iter->Next(), i = 0; iter->Valid(); iter->Next(), i++) {
|
||||
VerifyKeyAndWriteTime(iter.get(), Key(i), 0);
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(IteratorWriteTimeTest, MergeReturnsBaseValueWriteTime) {
|
||||
const int kNumTrigger = 4;
|
||||
const int kNumLevels = 7;
|
||||
const int kSecondsPerRecording = 101;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.env = mock_env_.get();
|
||||
options.level0_file_num_compaction_trigger = kNumTrigger;
|
||||
options.preserve_internal_time_seconds = 10000;
|
||||
options.num_levels = kNumLevels;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
ASSERT_OK(Put("foo", "fv1"));
|
||||
|
||||
dbfull()->TEST_WaitForPeriodicTaskRun(
|
||||
[&] { mock_clock_->MockSleepForSeconds(kSecondsPerRecording); });
|
||||
ASSERT_OK(Put("bar", "bv1"));
|
||||
ASSERT_OK(Merge("foo", "bv1"));
|
||||
|
||||
ReadOptions ropts;
|
||||
ropts.tailing = GetParam();
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
uint64_t bar_time = VerifyKeyAndGetWriteTime(iter.get(), "bar");
|
||||
iter->Next();
|
||||
uint64_t foo_time = VerifyKeyAndGetWriteTime(iter.get(), "foo");
|
||||
// "foo" has an older write time because its base value's write time is used
|
||||
ASSERT_GT(bar_time, foo_time);
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(IteratorWriteTimeTest, IteratorWriteTimeTest,
|
||||
testing::Bool());
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -170,7 +170,7 @@ void DoRandomIteraratorTest(DB* db, std::vector<std::string> source_strings,
|
||||
|
||||
class DoubleComparator : public Comparator {
|
||||
public:
|
||||
DoubleComparator() {}
|
||||
DoubleComparator() = default;
|
||||
|
||||
const char* Name() const override { return "DoubleComparator"; }
|
||||
|
||||
@@ -198,7 +198,7 @@ class DoubleComparator : public Comparator {
|
||||
|
||||
class HashComparator : public Comparator {
|
||||
public:
|
||||
HashComparator() {}
|
||||
HashComparator() = default;
|
||||
|
||||
const char* Name() const override { return "HashComparator"; }
|
||||
|
||||
@@ -221,7 +221,7 @@ class HashComparator : public Comparator {
|
||||
|
||||
class TwoStrComparator : public Comparator {
|
||||
public:
|
||||
TwoStrComparator() {}
|
||||
TwoStrComparator() = default;
|
||||
|
||||
const char* Name() const override { return "TwoStrComparator"; }
|
||||
|
||||
@@ -372,7 +372,7 @@ TEST_P(ComparatorDBTest, Uint64Comparator) {
|
||||
uint64_t r = rnd64.Next();
|
||||
std::string str;
|
||||
str.resize(8);
|
||||
memcpy(&str[0], static_cast<void*>(&r), 8);
|
||||
memcpy(str.data(), static_cast<void*>(&r), 8);
|
||||
source_strings.push_back(str);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ static std::string Key(int i) {
|
||||
static std::string Uint64Key(uint64_t i) {
|
||||
std::string str;
|
||||
str.resize(8);
|
||||
memcpy(&str[0], static_cast<void*>(&i), 8);
|
||||
memcpy(str.data(), static_cast<void*>(&i), 8);
|
||||
return str;
|
||||
}
|
||||
} // namespace.
|
||||
|
||||
+72
-37
@@ -442,6 +442,41 @@ TEST_F(DBBasicTest, PutSingleDeleteGet) {
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, TimedPutBasic) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(TimedPut(1, "foo", "v1", /*write_unix_time=*/0));
|
||||
// Read from memtable
|
||||
ASSERT_EQ("v1", Get(1, "foo"));
|
||||
ASSERT_OK(TimedPut(1, "foo", "v2.1", /*write_unix_time=*/3));
|
||||
ASSERT_EQ("v2.1", Get(1, "foo"));
|
||||
|
||||
// Read from sst file
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), handles_[1]));
|
||||
ASSERT_OK(Merge(1, "foo", "v2.2"));
|
||||
ASSERT_EQ("v2.1,v2.2", Get(1, "foo"));
|
||||
ASSERT_OK(Delete(1, "foo"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
|
||||
|
||||
ASSERT_OK(TimedPut(1, "bar", "bv1", /*write_unix_time=*/0));
|
||||
ASSERT_EQ("bv1", Get(1, "bar"));
|
||||
ASSERT_OK(TimedPut(1, "baz", "bzv1", /*write_unix_time=*/0));
|
||||
ASSERT_EQ("bzv1", Get(1, "baz"));
|
||||
std::string range_del_begin = "b";
|
||||
std::string range_del_end = "baz";
|
||||
Slice begin_rdel = range_del_begin, end_rdel = range_del_end;
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), handles_[1], begin_rdel, end_rdel));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "bar"));
|
||||
|
||||
ASSERT_EQ("bzv1", Get(1, "baz"));
|
||||
ASSERT_OK(SingleDelete(1, "baz"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "baz"));
|
||||
} while (ChangeOptions(kSkipPlainTable));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, EmptyFlush) {
|
||||
// It is possible to produce empty flushes when using single deletes. Tests
|
||||
// whether empty flushes cause issues.
|
||||
@@ -1368,9 +1403,9 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCF) {
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
int cf = i / 3;
|
||||
int cf_key = 1 % 3;
|
||||
cf_kv_vec.emplace_back(std::make_tuple(
|
||||
cf_kv_vec.emplace_back(
|
||||
cf, "cf" + std::to_string(cf) + "_key_" + std::to_string(cf_key),
|
||||
"cf" + std::to_string(cf) + "_val_" + std::to_string(cf_key)));
|
||||
"cf" + std::to_string(cf) + "_val_" + std::to_string(cf_key));
|
||||
ASSERT_OK(Put(std::get<0>(cf_kv_vec[i]), std::get<1>(cf_kv_vec[i]),
|
||||
std::get<2>(cf_kv_vec[i])));
|
||||
}
|
||||
@@ -2607,9 +2642,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2652,9 +2687,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2717,9 +2752,9 @@ TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||
key_strs.push_back(Key(21));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2762,9 +2797,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(56));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2805,8 +2840,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
||||
// 19 and 26 are in L2, but overlap with L0 and L1 file ranges
|
||||
key_strs.push_back(Key(19));
|
||||
key_strs.push_back(Key(26));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2841,8 +2876,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeDelInL1) {
|
||||
// 139 and 163 are in L2, but overlap with a range deletes in L1
|
||||
key_strs.push_back(Key(139));
|
||||
key_strs.push_back(Key(163));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2871,9 +2906,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2WithRangeDelInL1) {
|
||||
key_strs.push_back(Key(139));
|
||||
key_strs.push_back(Key(144));
|
||||
key_strs.push_back(Key(163));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -2904,9 +2939,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetNoIOUring) {
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
keys.emplace_back(key_strs[0]);
|
||||
keys.emplace_back(key_strs[1]);
|
||||
keys.emplace_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
@@ -3285,9 +3320,9 @@ TEST_F(DBBasicTest, MultiGetIOBufferOverrun) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -3661,10 +3696,10 @@ TEST_F(DBBasicTest, ConcurrentlyCloseDB) {
|
||||
DestroyAndReopen(options);
|
||||
std::vector<std::thread> workers;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
workers.push_back(std::thread([&]() {
|
||||
workers.emplace_back([&]() {
|
||||
auto s = db_->Close();
|
||||
ASSERT_OK(s);
|
||||
}));
|
||||
});
|
||||
}
|
||||
for (auto& w : workers) {
|
||||
w.join();
|
||||
@@ -3938,9 +3973,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGet) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -4119,9 +4154,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetDirectIO) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -4189,9 +4224,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetWithChecksumMismatch) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -4237,9 +4272,9 @@ TEST_P(DBBasicTestWithParallelIO, MultiGetWithMissingFile) {
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
keys.emplace_back(key_data.back());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
@@ -4743,7 +4778,7 @@ TEST_F(DBBasicTest, VerifyFileChecksumsReadahead) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &filenames));
|
||||
for (auto name : filenames) {
|
||||
for (const auto& name : filenames) {
|
||||
if (ParseFileName(name, &number, &type)) {
|
||||
if (type == kTableFile) {
|
||||
sst_cnt++;
|
||||
|
||||
@@ -744,7 +744,7 @@ TEST_F(DBBlockCacheTest, AddRedundantStats) {
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
const int num_shard_bits = 0; // 1 shard
|
||||
int iterations_tested = 0;
|
||||
for (std::shared_ptr<Cache> base_cache :
|
||||
for (const std::shared_ptr<Cache>& base_cache :
|
||||
{NewLRUCache(capacity, num_shard_bits),
|
||||
// FixedHyperClockCache
|
||||
HyperClockCacheOptions(
|
||||
@@ -990,7 +990,7 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
int iterations_tested = 0;
|
||||
for (bool partition : {false, true}) {
|
||||
SCOPED_TRACE("Partition? " + std::to_string(partition));
|
||||
for (std::shared_ptr<Cache> cache :
|
||||
for (const std::shared_ptr<Cache>& cache :
|
||||
{NewLRUCache(capacity),
|
||||
HyperClockCacheOptions(
|
||||
capacity,
|
||||
@@ -1251,7 +1251,7 @@ void DummyFillCache(Cache& cache, size_t entry_size,
|
||||
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
~CountingLogger() override {}
|
||||
~CountingLogger() override = default;
|
||||
using Logger::Logv;
|
||||
void Logv(const InfoLogLevel log_level, const char* format,
|
||||
va_list /*ap*/) override {
|
||||
@@ -1373,7 +1373,7 @@ class StableCacheKeyTestFS : public FaultInjectionTestFS {
|
||||
SetFailGetUniqueId(true);
|
||||
}
|
||||
|
||||
~StableCacheKeyTestFS() override {}
|
||||
~StableCacheKeyTestFS() override = default;
|
||||
|
||||
IOStatus LinkFile(const std::string&, const std::string&, const IOOptions&,
|
||||
IODebugContext*) override {
|
||||
@@ -1566,7 +1566,7 @@ class CacheKeyTest : public testing::Test {
|
||||
tp_.db_id = std::to_string(db_id_);
|
||||
tp_.orig_file_number = file_number;
|
||||
bool is_stable;
|
||||
std::string cur_session_id = ""; // ignored
|
||||
std::string cur_session_id; // ignored
|
||||
uint64_t cur_file_number = 42; // ignored
|
||||
OffsetableCacheKey rv;
|
||||
BlockBasedTable::SetupBaseCacheKey(&tp_, cur_session_id, cur_file_number,
|
||||
@@ -1834,6 +1834,7 @@ class DBBlockCachePinningTest
|
||||
PinningTier unpartitioned_pinning_;
|
||||
};
|
||||
|
||||
#ifdef LZ4
|
||||
TEST_P(DBBlockCachePinningTest, TwoLevelDB) {
|
||||
// Creates one file in L0 and one file in L1. Both files have enough data that
|
||||
// their index and filter blocks are partitioned. The L1 file will also have
|
||||
@@ -1845,10 +1846,7 @@ TEST_P(DBBlockCachePinningTest, TwoLevelDB) {
|
||||
const int kNumKeysPerFile = kBlockSize * kNumBlocksPerFile / kKeySize;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
// `kNoCompression` makes the unit test more portable. But it relies on the
|
||||
// current behavior of persisting/accessing dictionary even when there's no
|
||||
// (de)compression happening, which seems fairly likely to change over time.
|
||||
options.compression = kNoCompression;
|
||||
options.compression = kLZ4Compression;
|
||||
options.compression_opts.max_dict_bytes = 4 << 10;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -1961,6 +1959,7 @@ TEST_P(DBBlockCachePinningTest, TwoLevelDB) {
|
||||
ASSERT_EQ(expected_compression_dict_misses,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
}
|
||||
#endif
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBBlockCachePinningTest, DBBlockCachePinningTest,
|
||||
|
||||
@@ -78,7 +78,7 @@ class DBBloomFilterTestWithParam
|
||||
DBBloomFilterTestWithParam()
|
||||
: DBTestBase("db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
|
||||
~DBBloomFilterTestWithParam() override {}
|
||||
~DBBloomFilterTestWithParam() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
bfp_impl_ = std::get<0>(GetParam());
|
||||
@@ -2051,7 +2051,7 @@ class DBBloomFilterTestVaryPrefixAndFormatVer
|
||||
DBBloomFilterTestVaryPrefixAndFormatVer()
|
||||
: DBTestBase("db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
|
||||
~DBBloomFilterTestVaryPrefixAndFormatVer() override {}
|
||||
~DBBloomFilterTestVaryPrefixAndFormatVer() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
use_prefix_ = std::get<0>(GetParam());
|
||||
@@ -2126,8 +2126,9 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
values[i] = PinnableSlice();
|
||||
}
|
||||
|
||||
db_->MultiGet(ropts, Q, &column_families[0], &key_slices[0], &values[0],
|
||||
/*timestamps=*/nullptr, &statuses[0], true);
|
||||
db_->MultiGet(ropts, Q, column_families.data(), key_slices.data(),
|
||||
values.data(),
|
||||
/*timestamps=*/nullptr, statuses.data(), true);
|
||||
|
||||
// Confirm correct status results
|
||||
uint32_t number_not_found = 0;
|
||||
@@ -2177,8 +2178,9 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
values[i] = PinnableSlice();
|
||||
}
|
||||
|
||||
db_->MultiGet(ropts, Q, &column_families[0], &key_slices[0], &values[0],
|
||||
/*timestamps=*/nullptr, &statuses[0], true);
|
||||
db_->MultiGet(ropts, Q, column_families.data(), key_slices.data(),
|
||||
values.data(),
|
||||
/*timestamps=*/nullptr, statuses.data(), true);
|
||||
|
||||
// Confirm correct status results
|
||||
uint32_t number_not_found = 0;
|
||||
|
||||
@@ -150,7 +150,7 @@ class ConditionalFilter : public CompactionFilter {
|
||||
|
||||
class ChangeFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ChangeFilter() {}
|
||||
explicit ChangeFilter() = default;
|
||||
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* new_value, bool* value_changed) const override {
|
||||
@@ -289,7 +289,7 @@ class ConditionalFilterFactory : public CompactionFilterFactory {
|
||||
|
||||
class ChangeFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit ChangeFilterFactory() {}
|
||||
explicit ChangeFilterFactory() = default;
|
||||
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
@@ -342,7 +342,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
ScopedArenaPtr<InternalIterator> iter(dbfull()->NewInternalIterator(
|
||||
read_options, &arena, kMaxSequenceNumber, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -434,7 +434,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(
|
||||
ScopedArenaPtr<InternalIterator> iter(dbfull()->NewInternalIterator(
|
||||
read_options, &arena, kMaxSequenceNumber, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -717,8 +717,8 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
Arena arena;
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber));
|
||||
ScopedArenaPtr<InternalIterator> iter(dbfull()->NewInternalIterator(
|
||||
read_options, &arena, kMaxSequenceNumber));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
|
||||
+169
-70
@@ -41,7 +41,7 @@ class CompactionStatsCollector : public EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
~CompactionStatsCollector() override {}
|
||||
~CompactionStatsCollector() override = default;
|
||||
|
||||
void OnCompactionCompleted(DB* /* db */,
|
||||
const CompactionJobInfo& info) override {
|
||||
@@ -241,8 +241,8 @@ class RoundRobinSubcompactionsAgainstResources
|
||||
namespace {
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
FlushedFileCollector() = default;
|
||||
~FlushedFileCollector() override = default;
|
||||
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
@@ -252,7 +252,7 @@ class FlushedFileCollector : public EventListener {
|
||||
std::vector<std::string> GetFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> result;
|
||||
for (auto fname : flushed_files_) {
|
||||
for (const auto& fname : flushed_files_) {
|
||||
result.push_back(fname);
|
||||
}
|
||||
return result;
|
||||
@@ -2090,9 +2090,9 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
Slice begin2(begin_str2), end2(end_str2);
|
||||
Slice begin3(begin_str3), end3(end_str3);
|
||||
std::vector<RangePtr> ranges;
|
||||
ranges.push_back(RangePtr(&begin1, &end1));
|
||||
ranges.push_back(RangePtr(&begin2, &end2));
|
||||
ranges.push_back(RangePtr(&begin3, &end3));
|
||||
ranges.emplace_back(&begin1, &end1);
|
||||
ranges.emplace_back(&begin2, &end2);
|
||||
ranges.emplace_back(&begin3, &end3);
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(),
|
||||
ranges.data(), ranges.size()));
|
||||
ASSERT_EQ("0,3,7", FilesPerLevel(0));
|
||||
@@ -2117,9 +2117,9 @@ TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
|
||||
Slice begin2(begin_str2), end2(end_str2);
|
||||
Slice begin3(begin_str3), end3(end_str3);
|
||||
std::vector<RangePtr> ranges;
|
||||
ranges.push_back(RangePtr(&begin1, &end1));
|
||||
ranges.push_back(RangePtr(&begin2, &end2));
|
||||
ranges.push_back(RangePtr(&begin3, &end3));
|
||||
ranges.emplace_back(&begin1, &end1);
|
||||
ranges.emplace_back(&begin2, &end2);
|
||||
ranges.emplace_back(&begin3, &end3);
|
||||
ASSERT_OK(DeleteFilesInRanges(db_, db_->DefaultColumnFamily(),
|
||||
ranges.data(), ranges.size(), false));
|
||||
ASSERT_EQ("0,1,4", FilesPerLevel(0));
|
||||
@@ -3997,7 +3997,7 @@ TEST_P(DBCompactionTestWithParam, FullCompactionInBottomPriThreadPool) {
|
||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CancelCompactionWaitingOnConflict) {
|
||||
TEST_F(DBCompactionTest, CancelCompactionWaitingOnRunningConflict) {
|
||||
// This test verifies cancellation of a compaction waiting to be scheduled due
|
||||
// to conflict with a running compaction.
|
||||
//
|
||||
@@ -4036,7 +4036,7 @@ TEST_F(DBCompactionTest, CancelCompactionWaitingOnConflict) {
|
||||
// Make sure the manual compaction has seen the conflict before being canceled
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ColumnFamilyData::CompactRange:Return",
|
||||
"DBCompactionTest::CancelCompactionWaitingOnConflict:"
|
||||
"DBCompactionTest::CancelCompactionWaitingOnRunningConflict:"
|
||||
"PreDisableManualCompaction"}});
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
@@ -4047,12 +4047,73 @@ TEST_F(DBCompactionTest, CancelCompactionWaitingOnConflict) {
|
||||
// despite finding a conflict with an automatic compaction that is still
|
||||
// running
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::CancelCompactionWaitingOnConflict:"
|
||||
"DBCompactionTest::CancelCompactionWaitingOnRunningConflict:"
|
||||
"PreDisableManualCompaction");
|
||||
db_->DisableManualCompaction();
|
||||
manual_compaction_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CancelCompactionWaitingOnScheduledConflict) {
|
||||
// This test verifies cancellation of a compaction waiting to be scheduled due
|
||||
// to conflict with a scheduled (but not running) compaction.
|
||||
//
|
||||
// A `CompactRange()` in universal compacts all files, waiting for files to
|
||||
// become available if they are locked for another compaction. This test
|
||||
// blocks the compaction thread pool and then calls `CompactRange()` twice.
|
||||
// The first call to `CompactRange()` schedules a compaction that is queued
|
||||
// in the thread pool. The second call to `CompactRange()` blocks on the first
|
||||
// call due to the conflict in file picking. The test verifies that
|
||||
// `DisableManualCompaction()` can cancel both while the thread pool remains
|
||||
// blocked.
|
||||
const int kNumSortedRuns = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.disable_auto_compactions = true;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
|
||||
Reopen(options);
|
||||
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
|
||||
// Fill overlapping files in L0
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumSortedRuns; ++i) {
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx, false /* nowait */);
|
||||
}
|
||||
|
||||
std::atomic<int> num_compact_range_calls{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ColumnFamilyData::CompactRange:Return",
|
||||
[&](void* /* arg */) { num_compact_range_calls++; });
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
const int kNumManualCompactions = 2;
|
||||
port::Thread manual_compaction_threads[kNumManualCompactions];
|
||||
for (int i = 0; i < kNumManualCompactions; i++) {
|
||||
manual_compaction_threads[i] = port::Thread([this]() {
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
.IsIncomplete());
|
||||
});
|
||||
}
|
||||
while (num_compact_range_calls < kNumManualCompactions) {
|
||||
}
|
||||
|
||||
// Cancel it. Threads should be joinable, i.e., both the scheduled and blocked
|
||||
// manual compactions were canceled despite no compaction could have ever run.
|
||||
db_->DisableManualCompaction();
|
||||
for (int i = 0; i < kNumManualCompactions; i++) {
|
||||
manual_compaction_threads[i].join();
|
||||
}
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, OptimizedDeletionObsoleting) {
|
||||
// Deletions can be dropped when compacted to non-last level if they fall
|
||||
// outside the lower-level files' key-ranges.
|
||||
@@ -6641,7 +6702,7 @@ TEST_F(DBCompactionTest, RoundRobinCutOutputAtCompactCursor) {
|
||||
|
||||
class NoopMergeOperator : public MergeOperator {
|
||||
public:
|
||||
NoopMergeOperator() {}
|
||||
NoopMergeOperator() = default;
|
||||
|
||||
bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
@@ -9164,66 +9225,104 @@ TEST_F(DBCompactionTest, CompactionWithChecksumHandoffManifest2) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, FIFOChangeTemperature) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.file_temperature_age_thresholds = {{Temperature::kCold, 1000}};
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
Reopen(options);
|
||||
for (bool write_time_default : {false, true}) {
|
||||
SCOPED_TRACE("write time default? " + std::to_string(write_time_default));
|
||||
|
||||
int total_cold = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.file_temperature_age_thresholds = {{Temperature::kCold, 1000}};
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
if (write_time_default) {
|
||||
options.default_write_temperature = Temperature::kWarm;
|
||||
}
|
||||
// Should be ignored (TODO: fail?)
|
||||
options.last_level_temperature = Temperature::kHot;
|
||||
Reopen(options);
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
int total_cold = 0;
|
||||
int total_warm = 0;
|
||||
int total_hot = 0;
|
||||
int total_unknown = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
} else if (temperature == Temperature::kWarm) {
|
||||
total_warm++;
|
||||
} else if (temperature == Temperature::kHot) {
|
||||
total_hot++;
|
||||
} else {
|
||||
assert(temperature == Temperature::kUnknown);
|
||||
total_unknown++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
if (write_time_default) {
|
||||
// Also test dynamic option change
|
||||
ASSERT_OK(db_->SetOptions({{"default_write_temperature", "kHot"}}));
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_cold);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
Destroy(options);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
if (write_time_default) {
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[1].temperature);
|
||||
// Includes obsolete/deleted files moved to cold
|
||||
ASSERT_EQ(total_warm, 3);
|
||||
ASSERT_EQ(total_hot, 1);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 0);
|
||||
} else {
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(total_warm, 0);
|
||||
ASSERT_EQ(total_hot, 0);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 4);
|
||||
}
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_cold);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
@@ -9840,7 +9939,7 @@ TEST_F(DBCompactionTest, TurnOnLevelCompactionDynamicLevelBytesUCToLC) {
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
std::string expected_lsm = "";
|
||||
std::string expected_lsm;
|
||||
for (int i = 0; i < 49; ++i) {
|
||||
expected_lsm += "0,";
|
||||
}
|
||||
@@ -10356,20 +10455,20 @@ TEST_F(DBCompactionTest, ReleaseCompactionDuringManifestWrite) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.emplace_back(std::thread([&]() {
|
||||
threads.emplace_back([&]() {
|
||||
std::string k1_str = Key(1);
|
||||
std::string k2_str = Key(2);
|
||||
Slice k1 = k1_str;
|
||||
Slice k2 = k2_str;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &k1, &k2));
|
||||
}));
|
||||
threads.emplace_back(std::thread([&]() {
|
||||
});
|
||||
threads.emplace_back([&]() {
|
||||
std::string k10_str = Key(10);
|
||||
std::string k11_str = Key(11);
|
||||
Slice k10 = k10_str;
|
||||
Slice k11 = k11_str;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &k10, &k11));
|
||||
}));
|
||||
});
|
||||
std::string k100_str = Key(100);
|
||||
std::string k101_str = Key(101);
|
||||
Slice k100 = k100_str;
|
||||
|
||||
@@ -390,8 +390,11 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
info.file_number = live_wal_files[i]->LogNumber();
|
||||
info.file_type = kWalFile;
|
||||
info.size = live_wal_files[i]->SizeFileBytes();
|
||||
// Only last should need to be trimmed
|
||||
info.trim_to_size = (i + 1 == wal_size);
|
||||
// Trim the log either if its the last one, or log file recycling is
|
||||
// enabled. In the latter case, a hard link doesn't prevent the file
|
||||
// from being renamed and recycled. So we need to copy it instead.
|
||||
info.trim_to_size = (i + 1 == wal_size) ||
|
||||
(immutable_db_options_.recycle_log_file_num > 0);
|
||||
if (opts.include_checksum_info) {
|
||||
info.file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
info.file_checksum = kUnknownFileChecksum;
|
||||
|
||||
+15
-11
@@ -1367,14 +1367,15 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
|
||||
ASSERT_OK(iter->status());
|
||||
key = (iter->key()).ToString(false);
|
||||
value = (iter->value()).ToString(false);
|
||||
if (key.compare(KEY3) == 0)
|
||||
if (key.compare(KEY3) == 0) {
|
||||
ASSERT_EQ(value, p_v3b);
|
||||
else if (key.compare(KEY4) == 0)
|
||||
} else if (key.compare(KEY4) == 0) {
|
||||
ASSERT_EQ(value, p_v4);
|
||||
else if (key.compare(KEY5) == 0)
|
||||
} else if (key.compare(KEY5) == 0) {
|
||||
ASSERT_EQ(value, p_v5);
|
||||
else
|
||||
} else {
|
||||
ASSERT_EQ(value, NOT_FOUND);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -1404,22 +1405,25 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
|
||||
ASSERT_OK(iter->status());
|
||||
key = (iter->key()).ToString(false);
|
||||
value = (iter->value()).ToString(false);
|
||||
if (key.compare(KEY2) == 0)
|
||||
if (key.compare(KEY2) == 0) {
|
||||
ASSERT_EQ(value, p_v2);
|
||||
else if (key.compare(KEY3) == 0)
|
||||
} else if (key.compare(KEY3) == 0) {
|
||||
ASSERT_EQ(value, p_v3b);
|
||||
else if (key.compare(KEY4) == 0)
|
||||
} else if (key.compare(KEY4) == 0) {
|
||||
ASSERT_EQ(value, p_v4);
|
||||
else if (key.compare(KEY5) == 0)
|
||||
} else if (key.compare(KEY5) == 0) {
|
||||
ASSERT_EQ(value, p_v5);
|
||||
else
|
||||
} else {
|
||||
ASSERT_EQ(value, NOT_FOUND);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
// Expected count here is 4: KEY2, KEY3, KEY4, KEY5.
|
||||
ASSERT_EQ(count, EXPECTED_COUNT_END);
|
||||
if (iter) delete iter;
|
||||
if (iter) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
@@ -2499,7 +2503,7 @@ TEST_F(DBFlushTest, TombstoneVisibleInSnapshot) {
|
||||
class SimpleTestFlushListener : public EventListener {
|
||||
public:
|
||||
explicit SimpleTestFlushListener(DBFlushTest* _test) : test_(_test) {}
|
||||
~SimpleTestFlushListener() override {}
|
||||
~SimpleTestFlushListener() override = default;
|
||||
|
||||
void OnFlushBegin(DB* db, const FlushJobInfo& info) override {
|
||||
ASSERT_EQ(static_cast<uint32_t>(0), info.cf_id);
|
||||
|
||||
@@ -21,7 +21,7 @@ CompactedDBImpl::CompactedDBImpl(const DBOptions& options,
|
||||
version_(nullptr),
|
||||
user_comparator_(nullptr) {}
|
||||
|
||||
CompactedDBImpl::~CompactedDBImpl() {}
|
||||
CompactedDBImpl::~CompactedDBImpl() = default;
|
||||
|
||||
size_t CompactedDBImpl::FindFile(const Slice& key) {
|
||||
size_t right = files_.num_files - 1;
|
||||
|
||||
+123
-47
@@ -8,7 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#ifdef OS_SOLARIS
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
@@ -44,7 +45,7 @@
|
||||
#include "db/memtable.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/multi_cf_iterator.h"
|
||||
#include "db/periodic_task_scheduler.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/table_cache.h"
|
||||
@@ -71,6 +72,9 @@
|
||||
#include "options/cf_options.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "options/options_parser.h"
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
#include "port/jemalloc_helper.h"
|
||||
#endif
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -148,6 +152,12 @@ void DumpSupportInfo(Logger* logger) {
|
||||
crc32c::IsFastCrc32Supported().c_str());
|
||||
|
||||
ROCKS_LOG_HEADER(logger, "DMutex implementation: %s", DMutex::kName());
|
||||
|
||||
bool jemalloc_supported = false;
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
jemalloc_supported = HasJemalloc();
|
||||
#endif
|
||||
ROCKS_LOG_HEADER(logger, "Jemalloc supported: %d", jemalloc_supported);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -950,7 +960,9 @@ size_t DBImpl::EstimateInMemoryStatsHistorySize() const {
|
||||
stats_history_mutex_.AssertHeld();
|
||||
size_t size_total =
|
||||
sizeof(std::map<uint64_t, std::map<std::string, uint64_t>>);
|
||||
if (stats_history_.size() == 0) return size_total;
|
||||
if (stats_history_.size() == 0) {
|
||||
return size_total;
|
||||
}
|
||||
size_t size_per_slice =
|
||||
sizeof(uint64_t) + sizeof(std::map<std::string, uint64_t>);
|
||||
// non-empty map, stats_history_.begin() guaranteed to exist
|
||||
@@ -1076,7 +1088,9 @@ bool DBImpl::FindStatsByTime(uint64_t start_time, uint64_t end_time,
|
||||
std::map<std::string, uint64_t>* stats_map) {
|
||||
assert(new_time);
|
||||
assert(stats_map);
|
||||
if (!new_time || !stats_map) return false;
|
||||
if (!new_time || !stats_map) {
|
||||
return false;
|
||||
}
|
||||
// lock when search for start_time
|
||||
{
|
||||
InstrumentedMutexLock l(&stats_history_mutex_);
|
||||
@@ -1483,7 +1497,9 @@ int DBImpl::FindMinimumEmptyLevelFitting(
|
||||
int minimum_level = level;
|
||||
for (int i = level - 1; i > 0; --i) {
|
||||
// stop if level i is not empty
|
||||
if (vstorage->NumLevelFiles(i) > 0) break;
|
||||
if (vstorage->NumLevelFiles(i) > 0) {
|
||||
break;
|
||||
}
|
||||
// stop if level i is too small (cannot fit the level files)
|
||||
if (vstorage->MaxBytesForLevel(i) < vstorage->NumLevelBytes(level)) {
|
||||
break;
|
||||
@@ -1741,7 +1757,11 @@ void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
|
||||
wal.GetPreSyncSize() > 0) {
|
||||
synced_wals->AddWal(wal.number, WalMetadata(wal.GetPreSyncSize()));
|
||||
}
|
||||
if (wal.GetPreSyncSize() == wal.writer->file()->GetFlushedSize()) {
|
||||
// Check if the file has been closed, i.e wal.writer->file() == nullptr
|
||||
// which can happen if log recycling is enabled, or if all the data in
|
||||
// the log has been synced
|
||||
if (wal.writer->file() == nullptr ||
|
||||
wal.GetPreSyncSize() == wal.writer->file()->GetFlushedSize()) {
|
||||
// Fully synced
|
||||
logs_to_free_.push_back(wal.ReleaseWriter());
|
||||
it = logs_.erase(it);
|
||||
@@ -1964,7 +1984,8 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
super_version->mutable_cf_options.prefix_extractor != nullptr,
|
||||
read_options.iterate_upper_bound);
|
||||
// Collect iterator for mutable memtable
|
||||
auto mem_iter = super_version->mem->NewIterator(read_options, arena);
|
||||
auto mem_iter = super_version->mem->NewIterator(
|
||||
read_options, super_version->GetSeqnoToTimeMapping(), arena);
|
||||
Status s;
|
||||
if (!read_options.ignore_range_deletions) {
|
||||
TruncatedRangeDelIterator* mem_tombstone_iter = nullptr;
|
||||
@@ -1986,8 +2007,9 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
|
||||
// Collect all needed child iterators for immutable memtables
|
||||
if (s.ok()) {
|
||||
super_version->imm->AddIterators(read_options, &merge_iter_builder,
|
||||
!read_options.ignore_range_deletions);
|
||||
super_version->imm->AddIterators(
|
||||
read_options, super_version->GetSeqnoToTimeMapping(),
|
||||
&merge_iter_builder, !read_options.ignore_range_deletions);
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::NewInternalIterator:StatusCallback", &s);
|
||||
if (s.ok()) {
|
||||
@@ -2308,6 +2330,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
|
||||
// Prepare to store a list of merge operations if merge occurs.
|
||||
MergeContext merge_context;
|
||||
merge_context.get_merge_operands_options =
|
||||
get_impl_options.get_merge_operands_options;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
|
||||
Status s;
|
||||
@@ -3609,9 +3633,9 @@ Iterator* DBImpl::NewIterator(const ReadOptions& _read_options,
|
||||
}
|
||||
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
assert(cfh != nullptr);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
assert(cfd != nullptr);
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
if (read_options.timestamp && read_options.timestamp->size() > 0) {
|
||||
const Status s =
|
||||
@@ -3627,24 +3651,24 @@ Iterator* DBImpl::NewIterator(const ReadOptions& _read_options,
|
||||
result = NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, sv->current, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations, read_callback,
|
||||
this, cfd);
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */, cfh);
|
||||
} else {
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
|
||||
// WritePreparedTxnDB
|
||||
result = NewIteratorImpl(read_options, cfd, sv,
|
||||
result = NewIteratorImpl(read_options, cfh, sv,
|
||||
(read_options.snapshot != nullptr)
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: kMaxSequenceNumber,
|
||||
read_callback);
|
||||
nullptr /* read_callback */);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* DBImpl::NewIteratorImpl(
|
||||
const ReadOptions& read_options, ColumnFamilyData* cfd, SuperVersion* sv,
|
||||
SequenceNumber snapshot, ReadCallback* read_callback,
|
||||
const ReadOptions& read_options, ColumnFamilyHandleImpl* cfh,
|
||||
SuperVersion* sv, SequenceNumber snapshot, ReadCallback* read_callback,
|
||||
bool expose_blob_index, bool allow_refresh) {
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:1");
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:2");
|
||||
@@ -3707,19 +3731,44 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(
|
||||
// likely that any iterator pointer is close to the iterator it points to so
|
||||
// that they are likely to be in the same cache line and/or page.
|
||||
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options, sv->current,
|
||||
snapshot, sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback, this, cfd, expose_blob_index,
|
||||
allow_refresh);
|
||||
env_, read_options, *cfh->cfd()->ioptions(), sv->mutable_cf_options,
|
||||
sv->current, snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback, cfh, expose_blob_index, allow_refresh);
|
||||
|
||||
InternalIterator* internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfd, sv, db_iter->GetArena(), snapshot,
|
||||
db_iter->GetReadOptions(), cfh->cfd(), sv, db_iter->GetArena(), snapshot,
|
||||
/* allow_unprepared_value */ true, db_iter);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> DBImpl::NewMultiCfIterator(
|
||||
const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) {
|
||||
if (column_families.size() == 0) {
|
||||
return std::unique_ptr<Iterator>(NewErrorIterator(
|
||||
Status::InvalidArgument("No Column Family was provided")));
|
||||
}
|
||||
const Comparator* first_comparator = column_families[0]->GetComparator();
|
||||
for (size_t i = 1; i < column_families.size(); ++i) {
|
||||
const Comparator* cf_comparator = column_families[i]->GetComparator();
|
||||
if (first_comparator != cf_comparator &&
|
||||
first_comparator->GetId().compare(cf_comparator->GetId()) != 0) {
|
||||
return std::unique_ptr<Iterator>(NewErrorIterator(Status::InvalidArgument(
|
||||
"Different comparators are being used across CFs")));
|
||||
}
|
||||
}
|
||||
std::vector<Iterator*> child_iterators;
|
||||
Status s = NewIterators(_read_options, column_families, &child_iterators);
|
||||
if (s.ok()) {
|
||||
return std::make_unique<MultiCfIterator>(first_comparator, column_families,
|
||||
std::move(child_iterators));
|
||||
}
|
||||
return std::unique_ptr<Iterator>(NewErrorIterator(s));
|
||||
}
|
||||
|
||||
Status DBImpl::NewIterators(
|
||||
const ReadOptions& _read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
@@ -3760,37 +3809,37 @@ Status DBImpl::NewIterators(
|
||||
}
|
||||
}
|
||||
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
iterators->clear();
|
||||
iterators->reserve(column_families.size());
|
||||
autovector<std::tuple<ColumnFamilyData*, SuperVersion*>> cfd_to_sv;
|
||||
autovector<std::tuple<ColumnFamilyHandleImpl*, SuperVersion*>> cfh_to_sv;
|
||||
const bool check_read_ts =
|
||||
read_options.timestamp && read_options.timestamp->size() > 0;
|
||||
for (auto cfh : column_families) {
|
||||
auto cfd = static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
|
||||
for (auto cf : column_families) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(cf);
|
||||
auto cfd = cfh->cfd();
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
cfd_to_sv.emplace_back(cfd, sv);
|
||||
cfh_to_sv.emplace_back(cfh, sv);
|
||||
if (check_read_ts) {
|
||||
const Status s =
|
||||
FailIfReadCollapsedHistory(cfd, sv, *(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
for (auto prev_entry : cfd_to_sv) {
|
||||
for (auto prev_entry : cfh_to_sv) {
|
||||
CleanupSuperVersion(std::get<1>(prev_entry));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(cfd_to_sv.size() == column_families.size());
|
||||
assert(cfh_to_sv.size() == column_families.size());
|
||||
if (read_options.tailing) {
|
||||
for (auto [cfd, sv] : cfd_to_sv) {
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv,
|
||||
for (auto [cfh, sv] : cfh_to_sv) {
|
||||
auto iter = new ForwardIterator(this, read_options, cfh->cfd(), sv,
|
||||
/* allow_unprepared_value */ true);
|
||||
iterators->push_back(NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, sv->current, kMaxSequenceNumber,
|
||||
env_, read_options, *cfh->cfd()->ioptions(), sv->mutable_cf_options,
|
||||
cfh->cfd()->user_comparator(), iter, sv->current, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
read_callback, this, cfd));
|
||||
nullptr /*read_callback*/, cfh));
|
||||
}
|
||||
} else {
|
||||
// Note: no need to consider the special case of
|
||||
@@ -3799,9 +3848,9 @@ Status DBImpl::NewIterators(
|
||||
auto snapshot = read_options.snapshot != nullptr
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: versions_->LastSequence();
|
||||
for (auto [cfd, sv] : cfd_to_sv) {
|
||||
iterators->push_back(
|
||||
NewIteratorImpl(read_options, cfd, sv, snapshot, read_callback));
|
||||
for (auto [cfh, sv] : cfh_to_sv) {
|
||||
iterators->push_back(NewIteratorImpl(read_options, cfh, sv, snapshot,
|
||||
nullptr /*read_callback*/));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4606,9 +4655,9 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, job_context.superversion_contexts.data(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
FindObsoleteFiles(&job_context, false);
|
||||
} // lock released here
|
||||
@@ -4719,9 +4768,9 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, job_context.superversion_contexts.data(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
for (auto* deleted_file : deleted_files) {
|
||||
deleted_file->being_compacted = false;
|
||||
@@ -4956,7 +5005,7 @@ Status DB::DestroyColumnFamilyHandle(ColumnFamilyHandle* column_family) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
DB::~DB() {}
|
||||
DB::~DB() = default;
|
||||
|
||||
Status DBImpl::Close() {
|
||||
InstrumentedMutexLock closing_lock_guard(&closing_mutex_);
|
||||
@@ -4983,7 +5032,7 @@ Status DB::ListColumnFamilies(const DBOptions& db_options,
|
||||
return VersionSet::ListColumnFamilies(column_families, name, fs.get());
|
||||
}
|
||||
|
||||
Snapshot::~Snapshot() {}
|
||||
Snapshot::~Snapshot() = default;
|
||||
|
||||
Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families) {
|
||||
@@ -6015,8 +6064,8 @@ Status DBImpl::ClipColumnFamily(ColumnFamilyHandle* column_family,
|
||||
if (status.ok()) {
|
||||
// DeleteFilesInRanges non-overlap files except L0
|
||||
std::vector<RangePtr> ranges;
|
||||
ranges.push_back(RangePtr(nullptr, &begin_key));
|
||||
ranges.push_back(RangePtr(&end_key, nullptr));
|
||||
ranges.emplace_back(nullptr, &begin_key);
|
||||
ranges.emplace_back(&end_key, nullptr);
|
||||
status = DeleteFilesInRanges(column_family, ranges.data(), ranges.size());
|
||||
}
|
||||
|
||||
@@ -6264,7 +6313,7 @@ void DBImpl::NotifyOnExternalFileIngested(
|
||||
info.internal_file_path = f.internal_file_path;
|
||||
info.global_seqno = f.assigned_seqno;
|
||||
info.table_properties = f.table_properties;
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnExternalFileIngested(this, info);
|
||||
}
|
||||
}
|
||||
@@ -6424,6 +6473,8 @@ void DBImpl::RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds) {
|
||||
immutable_db_options_.clock->GetCurrentTime(&unix_time_signed)
|
||||
.PermitUncheckedError(); // Ignore error
|
||||
uint64_t unix_time = static_cast<uint64_t>(unix_time_signed);
|
||||
|
||||
std::vector<SuperVersionContext> sv_contexts;
|
||||
if (populate_historical_seconds > 0) {
|
||||
bool success = true;
|
||||
{
|
||||
@@ -6434,6 +6485,7 @@ void DBImpl::RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds) {
|
||||
success = seqno_to_time_mapping_.PrePopulate(
|
||||
from_seqno, seqno, unix_time - populate_historical_seconds,
|
||||
unix_time);
|
||||
InstallSeqnoToTimeMappingInSV(&sv_contexts);
|
||||
} else {
|
||||
// One of these will fail
|
||||
assert(seqno > 1);
|
||||
@@ -6459,7 +6511,31 @@ void DBImpl::RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds) {
|
||||
// FIXME: assert(seqno > 0);
|
||||
// Always successful assuming seqno never go backwards
|
||||
seqno_to_time_mapping_.Append(seqno, unix_time);
|
||||
InstallSeqnoToTimeMappingInSV(&sv_contexts);
|
||||
}
|
||||
|
||||
// clean up outside db mutex
|
||||
for (SuperVersionContext& sv_context : sv_contexts) {
|
||||
sv_context.Clean();
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::InstallSeqnoToTimeMappingInSV(
|
||||
std::vector<SuperVersionContext>* sv_contexts) {
|
||||
mutex_.AssertHeld();
|
||||
std::shared_ptr<SeqnoToTimeMapping> new_seqno_to_time_mapping =
|
||||
std::make_shared<SeqnoToTimeMapping>();
|
||||
new_seqno_to_time_mapping->CopyFrom(seqno_to_time_mapping_);
|
||||
for (ColumnFamilyData* cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
sv_contexts->emplace_back(/*create_superversion=*/true);
|
||||
sv_contexts->back().new_seqno_to_time_mapping = new_seqno_to_time_mapping;
|
||||
cfd->InstallSuperVersion(&sv_contexts->back(),
|
||||
*(cfd->GetLatestMutableCFOptions()));
|
||||
}
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+25
-3
@@ -59,7 +59,6 @@
|
||||
#include "rocksdb/utilities/replayer.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/repeatable_thread.h"
|
||||
@@ -351,6 +350,13 @@ class DBImpl : public DB {
|
||||
|
||||
const Snapshot* GetSnapshot() override;
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
|
||||
// UNDER CONSTRUCTION - DO NOT USE
|
||||
// Return a cross-column-family iterator from a consistent database state.
|
||||
std::unique_ptr<Iterator> NewMultiCfIterator(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) override;
|
||||
|
||||
// Create a timestamped snapshot. This snapshot can be shared by multiple
|
||||
// readers. If any of them uses it for write conflict checking, then
|
||||
// is_write_conflict_boundary is true. For simplicity, set it to true by
|
||||
@@ -646,8 +652,8 @@ class DBImpl : public DB {
|
||||
|
||||
// If `snapshot` == kMaxSequenceNumber, set a recent one inside the file.
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& options,
|
||||
ColumnFamilyData* cfd, SuperVersion* sv,
|
||||
SequenceNumber snapshot,
|
||||
ColumnFamilyHandleImpl* cfh,
|
||||
SuperVersion* sv, SequenceNumber snapshot,
|
||||
ReadCallback* read_callback,
|
||||
bool expose_blob_index = false,
|
||||
bool allow_refresh = true);
|
||||
@@ -1219,6 +1225,22 @@ class DBImpl : public DB {
|
||||
// populate_historical_seconds, now].
|
||||
void RecordSeqnoToTimeMapping(uint64_t populate_historical_seconds);
|
||||
|
||||
// Everytime DB's seqno to time mapping changed (which already hold the db
|
||||
// mutex), we install a new SuperVersion in each column family with a shared
|
||||
// copy of the new mapping while holding the db mutex.
|
||||
// This is done for all column families even though the column family does not
|
||||
// explicitly enabled the
|
||||
// `preclude_last_level_data_seconds` or `preserve_internal_time_seconds`
|
||||
// features.
|
||||
// This mapping supports iterators to fulfill the
|
||||
// "rocksdb.iterator.write-time" iterator property for entries in memtables.
|
||||
//
|
||||
// Since this new SuperVersion doesn't involve an LSM tree shape change, we
|
||||
// don't schedule work after installing this SuperVersion. It returns the used
|
||||
// `SuperVersionContext` for clean up after release mutex.
|
||||
void InstallSeqnoToTimeMappingInSV(
|
||||
std::vector<SuperVersionContext>* sv_contexts);
|
||||
|
||||
// Interface to block and signal the DB in case of stalling writes by
|
||||
// WriteBufferManager. Each DBImpl object contains ptr to WBMStallInterface.
|
||||
// When DB needs to be blocked or signalled by WriteBufferManager,
|
||||
|
||||
@@ -163,8 +163,10 @@ IOStatus DBImpl::SyncClosedLogs(const WriteOptions& write_options,
|
||||
if (error_recovery_in_prog) {
|
||||
log->file()->reset_seen_error();
|
||||
}
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
io_s = log->Close(WriteOptions());
|
||||
// Normally the log file is closed when purging obsolete file, but if
|
||||
// log recycling is enabled, the log file is closed here so that it
|
||||
// can be reused.
|
||||
io_s = log->Close(write_options);
|
||||
if (!io_s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -270,8 +272,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||
io_tracer_, seqno_to_time_mapping_, db_id_, db_session_id_,
|
||||
cfd->GetFullHistoryTsLow(), &blob_callback_);
|
||||
io_tracer_, cfd->GetSuperVersion()->ShareSeqnoToTimeMapping(), db_id_,
|
||||
db_session_id_, cfd->GetFullHistoryTsLow(), &blob_callback_);
|
||||
FileMetaData file_meta;
|
||||
|
||||
Status s;
|
||||
@@ -545,8 +547,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri, io_tracer_, seqno_to_time_mapping_, db_id_, db_session_id_,
|
||||
cfd->GetFullHistoryTsLow(), &blob_callback_));
|
||||
thread_pri, io_tracer_,
|
||||
cfd->GetSuperVersion()->ShareSeqnoToTimeMapping(), db_id_,
|
||||
db_session_id_, cfd->GetFullHistoryTsLow(), &blob_callback_));
|
||||
}
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
@@ -970,7 +973,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
||||
info.largest_seqno = file_meta->fd.largest_seqno;
|
||||
info.flush_reason = flush_reason;
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushBegin(this, info);
|
||||
}
|
||||
}
|
||||
@@ -1002,7 +1005,7 @@ void DBImpl::NotifyOnFlushCompleted(
|
||||
for (auto& info : *flush_jobs_info) {
|
||||
info->triggered_writes_slowdown = triggered_writes_slowdown;
|
||||
info->triggered_writes_stop = triggered_writes_stop;
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnFlushCompleted(this, *info);
|
||||
}
|
||||
TEST_SYNC_POINT(
|
||||
@@ -1609,9 +1612,9 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
if (status.ok()) {
|
||||
assert(compaction_job.io_status().ok());
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
}
|
||||
// status above captures any error during compaction_job.Install, so its ok
|
||||
// not check compaction_job.io_status() explicitly if we're not calling
|
||||
@@ -1731,7 +1734,7 @@ void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
{
|
||||
CompactionJobInfo info{};
|
||||
BuildCompactionJobInfo(cfd, c, st, job_stats, job_id, &info);
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionBegin(this, info);
|
||||
}
|
||||
info.status.PermitUncheckedError();
|
||||
@@ -1760,7 +1763,7 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
{
|
||||
CompactionJobInfo info{};
|
||||
BuildCompactionJobInfo(cfd, c, st, compaction_job_stats, job_id, &info);
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnCompactionCompleted(this, info);
|
||||
}
|
||||
}
|
||||
@@ -1868,7 +1871,8 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
,
|
||||
LLONG_MAX /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID, not applicable */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
0 /* max_subcompactions, not applicable */,
|
||||
{} /* grandparents, not applicable */, false /* is manual */,
|
||||
"" /* trim_ts */, -1 /* score, not applicable */,
|
||||
@@ -2150,16 +2154,6 @@ Status DBImpl::RunManualCompaction(
|
||||
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
||||
max_file_num_to_ignore, trim_ts)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
if (!scheduled) {
|
||||
// There is a conflicting compaction
|
||||
if (manual_compaction_paused_ > 0 || manual.canceled == true) {
|
||||
// Stop waiting since it was canceled. Pretend the error came from
|
||||
// compaction so the below cleanup/error handling code can process it.
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
}
|
||||
if (!manual.done) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
@@ -2234,6 +2228,17 @@ Status DBImpl::RunManualCompaction(
|
||||
*final_output_level = compaction->output_level();
|
||||
}
|
||||
}
|
||||
if (!scheduled) {
|
||||
// There is nothing scheduled to wait on, so any cancellation can end the
|
||||
// manual now.
|
||||
if (manual_compaction_paused_ > 0 || manual.canceled == true) {
|
||||
// Stop waiting since it was canceled. Pretend the error came from
|
||||
// compaction so the below cleanup/error handling code can process it.
|
||||
manual.done = true;
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_buffer.FlushBufferToLog();
|
||||
@@ -3220,7 +3225,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
column_families_not_to_flush.push_back(cfd);
|
||||
continue;
|
||||
}
|
||||
superversion_contexts.emplace_back(SuperVersionContext(true));
|
||||
superversion_contexts.emplace_back(true);
|
||||
bg_flush_args.emplace_back(cfd, max_memtable_id,
|
||||
&(superversion_contexts.back()), flush_reason);
|
||||
}
|
||||
@@ -3725,9 +3730,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
compaction_released = true;
|
||||
});
|
||||
io_s = versions_->io_status();
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Deleted %d files\n",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
c->num_input_files(0));
|
||||
@@ -3800,9 +3805,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
});
|
||||
io_s = versions_->io_status();
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
c->column_family_data()->internal_stats()->IncBytesMoved(c->output_level(),
|
||||
@@ -3895,9 +3900,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
compaction_job.Install(*c->mutable_cf_options(), &compaction_released);
|
||||
io_s = compaction_job.io_status();
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
*c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data(),
|
||||
*c->mutable_cf_options());
|
||||
}
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
@@ -4044,7 +4049,6 @@ void DBImpl::RemoveManualCompaction(DBImpl::ManualCompactionState* m) {
|
||||
++it;
|
||||
}
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool DBImpl::ShouldntRunManualCompaction(ManualCompactionState* m) {
|
||||
|
||||
@@ -104,7 +104,9 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (i == 0) continue;
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
auto prev_f = l0_files[i - 1];
|
||||
if (icmp->Compare(prev_f->largest, f->smallest) >= 0) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
@@ -148,9 +150,9 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
read_options, write_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, job_context.superversion_contexts.data(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
} // lock released here
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
+21
-19
@@ -104,7 +104,6 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
if (result.recycle_log_file_num &&
|
||||
(result.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords ||
|
||||
result.wal_recovery_mode == WALRecoveryMode::kPointInTimeRecovery ||
|
||||
result.wal_recovery_mode == WALRecoveryMode::kAbsoluteConsistency)) {
|
||||
// - kTolerateCorruptedTailRecords is inconsistent with recycle log file
|
||||
// feature. WAL recycling expects recovery success upon encountering a
|
||||
@@ -281,12 +280,6 @@ Status DBImpl::ValidateOptions(const DBOptions& db_options) {
|
||||
"atomic_flush is incompatible with enable_pipelined_write");
|
||||
}
|
||||
|
||||
// TODO remove this restriction
|
||||
if (db_options.atomic_flush && db_options.best_efforts_recovery) {
|
||||
return Status::InvalidArgument(
|
||||
"atomic_flush is currently incompatible with best-efforts recovery");
|
||||
}
|
||||
|
||||
if (db_options.use_direct_io_for_flush_and_compaction &&
|
||||
0 == db_options.writable_file_max_buffer_size) {
|
||||
return Status::InvalidArgument(
|
||||
@@ -1092,6 +1085,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
Logger* info_log;
|
||||
const char* fname;
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
bool* old_log_record;
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
|
||||
(status == nullptr ? "(ignoring error) " : ""), fname,
|
||||
@@ -1100,10 +1094,19 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
*status = s;
|
||||
}
|
||||
}
|
||||
|
||||
void OldLogRecord(size_t bytes) override {
|
||||
if (old_log_record != nullptr) {
|
||||
*old_log_record = true;
|
||||
}
|
||||
ROCKS_LOG_WARN(info_log, "%s: dropping %d bytes; possibly recycled",
|
||||
fname, static_cast<int>(bytes));
|
||||
}
|
||||
};
|
||||
|
||||
mutex_.AssertHeld();
|
||||
Status status;
|
||||
bool old_log_record = false;
|
||||
std::unordered_map<int, VersionEdit> version_edits;
|
||||
// no need to refcount because iteration is under mutex
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -1194,6 +1197,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
reporter.env = env_;
|
||||
reporter.info_log = immutable_db_options_.info_log.get();
|
||||
reporter.fname = fname.c_str();
|
||||
reporter.old_log_record = &old_log_record;
|
||||
if (!immutable_db_options_.paranoid_checks ||
|
||||
immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kSkipAnyCorruptedRecords) {
|
||||
@@ -1341,7 +1345,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
if (!status.ok() || old_log_record) {
|
||||
if (status.IsNotSupported()) {
|
||||
// We should not treat NotSupported as corruption. It is rather a clear
|
||||
// sign that we are processing a WAL that is produced by an incompatible
|
||||
@@ -1366,6 +1370,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
}
|
||||
// We should ignore the error but not continue replaying
|
||||
status = Status::OK();
|
||||
old_log_record = false;
|
||||
stop_replay_for_corruption = true;
|
||||
corrupted_wal_number = wal_number;
|
||||
if (corrupted_wal_found != nullptr) {
|
||||
@@ -1636,7 +1641,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
Status s;
|
||||
TableProperties table_properties;
|
||||
{
|
||||
ScopedArenaIterator iter(mem->NewIterator(ro, &arena));
|
||||
ScopedArenaPtr<InternalIterator> iter(
|
||||
mem->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena));
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
" Level-0 table #%" PRIu64 ": started",
|
||||
@@ -1689,7 +1695,6 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
TableFileCreationReason::kRecovery, 0 /* oldest_key_time */,
|
||||
0 /* file_creation_time */, db_id_, db_session_id_,
|
||||
0 /* target_file_size */, meta.fd.GetNumber());
|
||||
SeqnoToTimeMapping empty_seqno_to_time_mapping;
|
||||
Version* version = cfd->current();
|
||||
version->Ref();
|
||||
uint64_t num_input_entries = 0;
|
||||
@@ -1700,7 +1705,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks, cfd->internal_stats(), &io_s,
|
||||
io_tracer_, BlobFileCreationReason::kRecovery,
|
||||
empty_seqno_to_time_mapping, &event_logger_, job_id,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_, job_id,
|
||||
nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
&num_input_entries);
|
||||
@@ -1799,11 +1804,9 @@ Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) {
|
||||
DBOptions db_options(options);
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
if (db_options.persist_stats_to_disk) {
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kPersistentStatsColumnFamilyName, cf_options));
|
||||
column_families.emplace_back(kPersistentStatsColumnFamilyName, cf_options);
|
||||
}
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
Status s = DB::Open(db_options, dbname, column_families, &handles, dbptr);
|
||||
@@ -1972,7 +1975,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
handles->clear();
|
||||
|
||||
size_t max_write_buffer_size = 0;
|
||||
for (auto cf : column_families) {
|
||||
for (const auto& cf : column_families) {
|
||||
max_write_buffer_size =
|
||||
std::max(max_write_buffer_size, cf.options.write_buffer_size);
|
||||
}
|
||||
@@ -2044,8 +2047,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
impl->alive_log_files_.push_back(
|
||||
DBImpl::LogFileNumberSize(impl->logfile_number_));
|
||||
impl->alive_log_files_.emplace_back(impl->logfile_number_);
|
||||
// In WritePrepared there could be gap in sequence numbers. This breaks
|
||||
// the trick we use in kPointInTimeRecovery which assumes the first seq in
|
||||
// the log right after the corrupted log is one larger than the last seq
|
||||
@@ -2093,7 +2095,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
|
||||
if (s.ok()) {
|
||||
// set column family handles
|
||||
for (auto cf : column_families) {
|
||||
for (const auto& cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (cfd != nullptr) {
|
||||
|
||||
@@ -26,7 +26,7 @@ DBImplReadOnly::DBImplReadOnly(const DBOptions& db_options,
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
DBImplReadOnly::~DBImplReadOnly() {}
|
||||
DBImplReadOnly::~DBImplReadOnly() = default;
|
||||
|
||||
// Implementations of the DB interface
|
||||
Status DBImplReadOnly::GetImpl(const ReadOptions& read_options,
|
||||
@@ -293,8 +293,7 @@ Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
DBOptions db_options(options);
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
s = DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
@@ -339,7 +338,7 @@ Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
error_if_wal_file_exists);
|
||||
if (s.ok()) {
|
||||
// set column family handles
|
||||
for (auto cf : column_families) {
|
||||
for (const auto& cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (cfd == nullptr) {
|
||||
|
||||
@@ -28,7 +28,7 @@ DBImplSecondary::DBImplSecondary(const DBOptions& db_options,
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
DBImplSecondary::~DBImplSecondary() {}
|
||||
DBImplSecondary::~DBImplSecondary() = default;
|
||||
|
||||
Status DBImplSecondary::Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
@@ -327,6 +327,7 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
status = *wal_read_status;
|
||||
}
|
||||
if (!status.ok()) {
|
||||
wal_read_status->PermitUncheckedError();
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -497,8 +498,8 @@ Iterator* DBImplSecondary::NewIterator(const ReadOptions& _read_options,
|
||||
|
||||
Iterator* result = nullptr;
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
assert(cfh != nullptr);
|
||||
auto cfd = cfh->cfd();
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (read_options.tailing) {
|
||||
return NewErrorIterator(Status::NotSupported(
|
||||
"tailing iterator not supported in secondary mode"));
|
||||
@@ -517,27 +518,28 @@ Iterator* DBImplSecondary::NewIterator(const ReadOptions& _read_options,
|
||||
return NewErrorIterator(s);
|
||||
}
|
||||
}
|
||||
result = NewIteratorImpl(read_options, cfd, sv, snapshot, read_callback);
|
||||
result = NewIteratorImpl(read_options, cfh, sv, snapshot,
|
||||
nullptr /*read_callback*/);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
|
||||
const ReadOptions& read_options, ColumnFamilyData* cfd,
|
||||
const ReadOptions& read_options, ColumnFamilyHandleImpl* cfh,
|
||||
SuperVersion* super_version, SequenceNumber snapshot,
|
||||
ReadCallback* read_callback, bool expose_blob_index, bool allow_refresh) {
|
||||
assert(nullptr != cfd);
|
||||
assert(nullptr != cfh);
|
||||
assert(snapshot == kMaxSequenceNumber);
|
||||
snapshot = versions_->LastSequence();
|
||||
assert(snapshot != kMaxSequenceNumber);
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), super_version->mutable_cf_options,
|
||||
super_version->current, snapshot,
|
||||
env_, read_options, *cfh->cfd()->ioptions(),
|
||||
super_version->mutable_cf_options, super_version->current, snapshot,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback, this, cfd,
|
||||
expose_blob_index, allow_refresh);
|
||||
super_version->version_number, read_callback, cfh, expose_blob_index,
|
||||
allow_refresh);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetReadOptions(), cfh->cfd(), super_version, db_iter->GetArena(),
|
||||
snapshot, /* allow_unprepared_value */ true, db_iter);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
@@ -596,28 +598,29 @@ Status DBImplSecondary::NewIterators(
|
||||
return Status::NotSupported("snapshot not supported in secondary mode");
|
||||
} else {
|
||||
SequenceNumber read_seq(kMaxSequenceNumber);
|
||||
autovector<std::tuple<ColumnFamilyData*, SuperVersion*>> cfd_to_sv;
|
||||
autovector<std::tuple<ColumnFamilyHandleImpl*, SuperVersion*>> cfh_to_sv;
|
||||
const bool check_read_ts =
|
||||
read_options.timestamp && read_options.timestamp->size() > 0;
|
||||
for (auto cfh : column_families) {
|
||||
ColumnFamilyData* cfd = static_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
for (auto cf : column_families) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(cf);
|
||||
auto cfd = cfh->cfd();
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
cfd_to_sv.emplace_back(cfd, sv);
|
||||
cfh_to_sv.emplace_back(cfh, sv);
|
||||
if (check_read_ts) {
|
||||
const Status s =
|
||||
FailIfReadCollapsedHistory(cfd, sv, *(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
for (auto prev_entry : cfd_to_sv) {
|
||||
for (auto prev_entry : cfh_to_sv) {
|
||||
CleanupSuperVersion(std::get<1>(prev_entry));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(cfd_to_sv.size() == column_families.size());
|
||||
for (auto [cfd, sv] : cfd_to_sv) {
|
||||
assert(cfh_to_sv.size() == column_families.size());
|
||||
for (auto [cfh, sv] : cfh_to_sv) {
|
||||
iterators->push_back(
|
||||
NewIteratorImpl(read_options, cfd, sv, read_seq, read_callback));
|
||||
NewIteratorImpl(read_options, cfh, sv, read_seq, read_callback));
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
@@ -802,7 +805,7 @@ Status DB::OpenAsSecondary(
|
||||
impl->mutex_.Lock();
|
||||
s = impl->Recover(column_families, true, false, false);
|
||||
if (s.ok()) {
|
||||
for (auto cf : column_families) {
|
||||
for (const auto& cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (nullptr == cfd) {
|
||||
|
||||
@@ -112,8 +112,8 @@ class DBImplSecondary : public DBImpl {
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd, SuperVersion* sv,
|
||||
SequenceNumber snapshot,
|
||||
ColumnFamilyHandleImpl* cfh,
|
||||
SuperVersion* sv, SequenceNumber snapshot,
|
||||
ReadCallback* read_callback,
|
||||
bool expose_blob_index = false,
|
||||
bool allow_refresh = true);
|
||||
|
||||
+18
-11
@@ -220,6 +220,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
write_options.protection_bytes_per_key != 8) {
|
||||
return Status::InvalidArgument(
|
||||
"`WriteOptions::protection_bytes_per_key` must be zero or eight");
|
||||
} else if (write_options.disableWAL &&
|
||||
immutable_db_options_.recycle_log_file_num > 0) {
|
||||
return Status::InvalidArgument(
|
||||
"WriteOptions::disableWAL option is not supported if "
|
||||
"DBOptions::recycle_log_file_num > 0");
|
||||
}
|
||||
// TODO: this use of operator bool on `tracer_` can avoid unnecessary lock
|
||||
// grabs but does not seem thread-safe.
|
||||
@@ -325,7 +330,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (w.ShouldWriteToMemtable()) {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_memtable_time);
|
||||
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
@@ -557,7 +562,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_memtable_time);
|
||||
|
||||
if (!parallel) {
|
||||
// w.sequence will be set inside InsertInto
|
||||
@@ -797,7 +802,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
WriteThread::WriteGroup memtable_write_group;
|
||||
|
||||
if (w.state == WriteThread::STATE_MEMTABLE_WRITER_LEADER) {
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_memtable_time);
|
||||
assert(w.ShouldWriteToMemtable());
|
||||
write_thread_.EnterAsMemTableWriter(&w, &memtable_write_group);
|
||||
if (memtable_write_group.size > 1 &&
|
||||
@@ -820,7 +825,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_memtable_time);
|
||||
|
||||
assert(w.ShouldWriteToMemtable());
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
@@ -868,7 +873,7 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_memtable_time);
|
||||
|
||||
ColumnFamilyMemTablesImpl column_family_memtables(
|
||||
versions_->GetColumnFamilySet());
|
||||
@@ -954,7 +959,7 @@ Status DBImpl::WriteImplWALOnly(
|
||||
}
|
||||
} else {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_delay_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_delay_time);
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
Status status =
|
||||
DelayWrite(/*num_bytes=*/0ull, *write_thread, write_options);
|
||||
@@ -1228,7 +1233,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
if (UNLIKELY(status.ok() && (write_controller_.IsStopped() ||
|
||||
write_controller_.NeedsDelay()))) {
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
PERF_TIMER_GUARD(write_delay_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_delay_time);
|
||||
// We don't know size of curent batch so that we always use the size
|
||||
// for previous one. It might create a fairness issue that expiration
|
||||
// might happen for smaller writes but larger writes can go through.
|
||||
@@ -2000,7 +2005,7 @@ Status DBImpl::ThrottleLowPriWritesIfNeeded(const WriteOptions& write_options,
|
||||
// is that in case the write is heavy, low pri writes may never have
|
||||
// a chance to run. Now we guarantee we are still slowly making
|
||||
// progress.
|
||||
PERF_TIMER_GUARD(write_delay_time);
|
||||
PERF_TIMER_FOR_WAIT_GUARD(write_delay_time);
|
||||
auto data_size = my_batch->GetDataSize();
|
||||
while (data_size > 0) {
|
||||
size_t allowed = write_controller_.low_pri_rate_limiter()->RequestToken(
|
||||
@@ -2135,7 +2140,7 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* /*cfd*/,
|
||||
}
|
||||
|
||||
mutex_.Unlock();
|
||||
for (auto listener : immutable_db_options_.listeners) {
|
||||
for (const auto& listener : immutable_db_options_.listeners) {
|
||||
listener->OnMemTableSealed(mem_table_info);
|
||||
}
|
||||
mutex_.Lock();
|
||||
@@ -2173,8 +2178,10 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
uint64_t recycle_log_number = 0;
|
||||
// If file deletion is disabled, don't recycle logs since it'll result in
|
||||
// the file getting renamed
|
||||
if (creating_new_log && immutable_db_options_.recycle_log_file_num &&
|
||||
!log_recycle_files_.empty()) {
|
||||
!log_recycle_files_.empty() && IsFileDeletionsEnabled()) {
|
||||
recycle_log_number = log_recycle_files_.front();
|
||||
}
|
||||
uint64_t new_log_number =
|
||||
@@ -2252,7 +2259,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
log_empty_ = true;
|
||||
log_dir_synced_ = false;
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.push_back(LogFileNumberSize(logfile_number_));
|
||||
alive_log_files_.emplace_back(logfile_number_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,120 @@
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
// A wrapper that allows injection of errors.
|
||||
class CorruptionFS : public FileSystemWrapper {
|
||||
public:
|
||||
bool writable_file_error_;
|
||||
int num_writable_file_errors_;
|
||||
|
||||
explicit CorruptionFS(const std::shared_ptr<FileSystem>& _target,
|
||||
bool fs_buffer)
|
||||
: FileSystemWrapper(_target),
|
||||
writable_file_error_(false),
|
||||
num_writable_file_errors_(0),
|
||||
corruption_trigger_(INT_MAX),
|
||||
read_count_(0),
|
||||
rnd_(300),
|
||||
fs_buffer_(fs_buffer) {}
|
||||
~CorruptionFS() override {
|
||||
// Assert that the corruption was reset, which means it got triggered
|
||||
assert(corruption_trigger_ == INT_MAX);
|
||||
}
|
||||
const char* Name() const override { return "ErrorEnv"; }
|
||||
|
||||
IOStatus NewWritableFile(const std::string& fname, const FileOptions& opts,
|
||||
std::unique_ptr<FSWritableFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
result->reset();
|
||||
if (writable_file_error_) {
|
||||
++num_writable_file_errors_;
|
||||
return IOStatus::IOError(fname, "fake error");
|
||||
}
|
||||
return target()->NewWritableFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
void SetCorruptionTrigger(const int trigger) {
|
||||
corruption_trigger_ = trigger;
|
||||
read_count_ = 0;
|
||||
}
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
IODebugContext* dbg) override {
|
||||
class CorruptionRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
|
||||
public:
|
||||
CorruptionRandomAccessFile(CorruptionFS& fs,
|
||||
std::unique_ptr<FSRandomAccessFile>& file)
|
||||
: FSRandomAccessFileOwnerWrapper(std::move(file)), fs_(fs) {}
|
||||
|
||||
IOStatus Read(uint64_t offset, size_t len, const IOOptions& opts,
|
||||
Slice* result, char* scratch,
|
||||
IODebugContext* dbg) const override {
|
||||
IOStatus s = target()->Read(offset, len, opts, result, scratch, dbg);
|
||||
if (opts.verify_and_reconstruct_read) {
|
||||
return s;
|
||||
}
|
||||
if (s.ok() && ++fs_.read_count_ >= fs_.corruption_trigger_) {
|
||||
fs_.read_count_ = 0;
|
||||
fs_.corruption_trigger_ = INT_MAX;
|
||||
char* data = const_cast<char*>(result->data());
|
||||
std::memcpy(
|
||||
data,
|
||||
fs_.rnd_.RandomString(static_cast<int>(result->size())).c_str(),
|
||||
result->size());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
IOStatus MultiRead(FSReadRequest* reqs, size_t num_reqs,
|
||||
const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
for (size_t i = 0; i < num_reqs; ++i) {
|
||||
FSReadRequest& req = reqs[i];
|
||||
if (fs_.fs_buffer_) {
|
||||
FSAllocationPtr buffer(new char[req.len], [](void* ptr) {
|
||||
delete[] static_cast<char*>(ptr);
|
||||
});
|
||||
req.fs_scratch = std::move(buffer);
|
||||
req.status = Read(req.offset, req.len, options, &req.result,
|
||||
static_cast<char*>(req.fs_scratch.get()), dbg);
|
||||
} else {
|
||||
req.status = Read(req.offset, req.len, options, &req.result,
|
||||
req.scratch, dbg);
|
||||
}
|
||||
}
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
CorruptionFS& fs_;
|
||||
};
|
||||
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
IOStatus s = target()->NewRandomAccessFile(fname, opts, &file, dbg);
|
||||
EXPECT_OK(s);
|
||||
result->reset(new CorruptionRandomAccessFile(*this, file));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void SupportedOps(int64_t& supported_ops) override {
|
||||
supported_ops = 1 << FSSupportedOps::kVerifyAndReconstructRead |
|
||||
1 << FSSupportedOps::kAsyncIO;
|
||||
if (fs_buffer_) {
|
||||
supported_ops |= 1 << FSSupportedOps::kFSBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int corruption_trigger_;
|
||||
int read_count_;
|
||||
Random rnd_;
|
||||
bool fs_buffer_;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
class DBIOFailureTest : public DBTestBase {
|
||||
public:
|
||||
@@ -579,6 +693,132 @@ TEST_F(DBIOFailureTest, CompactionSstSyncError) {
|
||||
ASSERT_EQ("bar3", Get(1, "foo"));
|
||||
}
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
|
||||
class DBIOCorruptionTest
|
||||
: public DBIOFailureTest,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
DBIOCorruptionTest() : DBIOFailureTest() {
|
||||
BlockBasedTableOptions bbto;
|
||||
Options options = CurrentOptions();
|
||||
|
||||
base_env_ = env_;
|
||||
EXPECT_NE(base_env_, nullptr);
|
||||
fs_.reset(
|
||||
new CorruptionFS(base_env_->GetFileSystem(), std::get<0>(GetParam())));
|
||||
env_guard_ = NewCompositeEnv(fs_);
|
||||
options.env = env_guard_.get();
|
||||
bbto.num_file_reads_for_auto_readahead = 0;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
~DBIOCorruptionTest() {
|
||||
Close();
|
||||
db_ = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Env> env_guard_;
|
||||
std::shared_ptr<CorruptionFS> fs_;
|
||||
Env* base_env_;
|
||||
};
|
||||
|
||||
TEST_P(DBIOCorruptionTest, GetReadCorruptionRetry) {
|
||||
CorruptionFS* fs =
|
||||
static_cast<CorruptionFS*>(env_guard_->GetFileSystem().get());
|
||||
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
ASSERT_OK(Flush());
|
||||
fs->SetCorruptionTrigger(1);
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
ASSERT_OK(dbfull()->Get(ReadOptions(), "key1", &val));
|
||||
ASSERT_EQ(val, "val1");
|
||||
}
|
||||
|
||||
TEST_P(DBIOCorruptionTest, IterReadCorruptionRetry) {
|
||||
CorruptionFS* fs =
|
||||
static_cast<CorruptionFS*>(env_guard_->GetFileSystem().get());
|
||||
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
ASSERT_OK(Flush());
|
||||
fs->SetCorruptionTrigger(1);
|
||||
|
||||
ReadOptions ro;
|
||||
ro.readahead_size = 65536;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
|
||||
Iterator* iter = dbfull()->NewIterator(ro);
|
||||
iter->SeekToFirst();
|
||||
while (iter->status().ok() && iter->Valid()) {
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_P(DBIOCorruptionTest, MultiGetReadCorruptionRetry) {
|
||||
CorruptionFS* fs =
|
||||
static_cast<CorruptionFS*>(env_guard_->GetFileSystem().get());
|
||||
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
ASSERT_OK(Put("key2", "val2"));
|
||||
ASSERT_OK(Flush());
|
||||
fs->SetCorruptionTrigger(1);
|
||||
|
||||
std::vector<std::string> keystr{"key1", "key2"};
|
||||
std::vector<Slice> keys{Slice(keystr[0]), Slice(keystr[1])};
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<Status> statuses(keys.size());
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
ASSERT_EQ(values[0].ToString(), "val1");
|
||||
ASSERT_EQ(values[1].ToString(), "val2");
|
||||
}
|
||||
|
||||
TEST_P(DBIOCorruptionTest, CompactionReadCorruptionRetry) {
|
||||
CorruptionFS* fs =
|
||||
static_cast<CorruptionFS*>(env_guard_->GetFileSystem().get());
|
||||
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
ASSERT_OK(Put("key3", "val3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("key2", "val2"));
|
||||
ASSERT_OK(Flush());
|
||||
fs->SetCorruptionTrigger(1);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
ASSERT_OK(dbfull()->Get(ro, "key1", &val));
|
||||
ASSERT_EQ(val, "val1");
|
||||
}
|
||||
|
||||
TEST_P(DBIOCorruptionTest, FlushReadCorruptionRetry) {
|
||||
CorruptionFS* fs =
|
||||
static_cast<CorruptionFS*>(env_guard_->GetFileSystem().get());
|
||||
|
||||
ASSERT_OK(Put("key1", "val1"));
|
||||
fs->SetCorruptionTrigger(1);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::string val;
|
||||
ReadOptions ro;
|
||||
ro.async_io = std::get<1>(GetParam());
|
||||
ASSERT_OK(dbfull()->Get(ro, "key1", &val));
|
||||
ASSERT_EQ(val, "val1");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBIOCorruptionTest, DBIOCorruptionTest,
|
||||
testing::Combine(testing::Bool(), testing::Bool()));
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+87
-48
@@ -43,8 +43,8 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const Comparator* cmp, InternalIterator* iter,
|
||||
const Version* version, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index)
|
||||
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
|
||||
bool expose_blob_index)
|
||||
: prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
|
||||
env_(_env),
|
||||
clock_(ioptions.clock),
|
||||
@@ -79,8 +79,7 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
is_blob_(false),
|
||||
arena_mode_(arena_mode),
|
||||
io_activity_(read_options.io_activity),
|
||||
db_impl_(db_impl),
|
||||
cfd_(cfd),
|
||||
cfh_(cfh),
|
||||
timestamp_ub_(read_options.timestamp),
|
||||
timestamp_lb_(read_options.iter_start_ts),
|
||||
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0) {
|
||||
@@ -114,8 +113,8 @@ Status DBIter::GetProperty(std::string prop_name, std::string* prop) {
|
||||
*prop = saved_key_.GetUserKey().ToString();
|
||||
return Status::OK();
|
||||
} else if (prop_name == "rocksdb.iterator.write-time") {
|
||||
// TODO(yuzhangyu): implement return the actual write time.
|
||||
return Status::NotSupported("write time property is under construction");
|
||||
PutFixed64(prop, saved_write_unix_time_);
|
||||
return Status::OK();
|
||||
}
|
||||
return Status::InvalidArgument("Unidentified property.");
|
||||
}
|
||||
@@ -393,6 +392,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
case kTypeValuePreferredSeqno:
|
||||
case kTypeBlobIndex:
|
||||
case kTypeWideColumnEntity:
|
||||
if (!PrepareValue()) {
|
||||
@@ -418,8 +418,14 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(ikey_.type == kTypeValue);
|
||||
SetValueAndColumnsFromPlain(iter_.value());
|
||||
assert(ikey_.type == kTypeValue ||
|
||||
ikey_.type == kTypeValuePreferredSeqno);
|
||||
Slice value = iter_.value();
|
||||
saved_write_unix_time_ = iter_.write_unix_time();
|
||||
if (ikey_.type == kTypeValuePreferredSeqno) {
|
||||
value = ParsePackedValueForValue(value);
|
||||
}
|
||||
SetValueAndColumnsFromPlain(value);
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
@@ -575,10 +581,15 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kTypeValue == ikey.type) {
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
if (!MergeWithPlainBaseValue(iter_.value(), ikey.user_key)) {
|
||||
if (kTypeValue == ikey.type || kTypeValuePreferredSeqno == ikey.type) {
|
||||
Slice value = iter_.value();
|
||||
saved_write_unix_time_ = iter_.write_unix_time();
|
||||
if (kTypeValuePreferredSeqno == ikey.type) {
|
||||
value = ParsePackedValueForValue(value);
|
||||
}
|
||||
// hit a put or put equivalent, merge the put value with operands and
|
||||
// store the final result in saved_value_. We are done!
|
||||
if (!MergeWithPlainBaseValue(value, ikey.user_key)) {
|
||||
return false;
|
||||
}
|
||||
// iter_ is positioned after put
|
||||
@@ -701,16 +712,21 @@ bool DBIter::ReverseToForward() {
|
||||
// not exist or may have different prefix than the current key().
|
||||
// If that's the case, seek iter_ to current key.
|
||||
if (!expect_total_order_inner_iter() || !iter_.Valid()) {
|
||||
IterKey last_key;
|
||||
ParsedInternalKey pikey(saved_key_.GetUserKey(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
if (timestamp_size_ > 0) {
|
||||
std::string last_key;
|
||||
if (timestamp_size_ == 0) {
|
||||
AppendInternalKey(
|
||||
&last_key, ParsedInternalKey(saved_key_.GetUserKey(),
|
||||
kMaxSequenceNumber, kValueTypeForSeek));
|
||||
} else {
|
||||
// TODO: pre-create kTsMax.
|
||||
const std::string kTsMax(timestamp_size_, '\xff');
|
||||
pikey.SetTimestamp(kTsMax);
|
||||
AppendInternalKeyWithDifferentTimestamp(
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek),
|
||||
kTsMax);
|
||||
}
|
||||
last_key.SetInternalKey(pikey);
|
||||
iter_.Seek(last_key.GetInternalKey());
|
||||
iter_.Seek(last_key);
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
}
|
||||
|
||||
@@ -840,8 +856,8 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
merge_context_.Clear();
|
||||
current_entry_is_merged_ = false;
|
||||
// last entry before merge (could be kTypeDeletion,
|
||||
// kTypeDeletionWithTimestamp, kTypeSingleDeletion, kTypeValue,
|
||||
// kTypeBlobIndex, or kTypeWideColumnEntity)
|
||||
// kTypeDeletionWithTimestamp, kTypeSingleDeletion, kTypeValue
|
||||
// kTypeBlobIndex, kTypeWideColumnEntity or kTypeValuePreferredSeqno)
|
||||
ValueType last_not_merge_type = kTypeDeletion;
|
||||
ValueType last_key_entry_type = kTypeDeletion;
|
||||
|
||||
@@ -918,10 +934,16 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
last_key_entry_type = ikey.type;
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeValue:
|
||||
case kTypeValuePreferredSeqno:
|
||||
case kTypeBlobIndex:
|
||||
case kTypeWideColumnEntity:
|
||||
if (iter_.iter()->IsValuePinned()) {
|
||||
pinned_value_ = iter_.value();
|
||||
saved_write_unix_time_ = iter_.write_unix_time();
|
||||
if (last_key_entry_type == kTypeValuePreferredSeqno) {
|
||||
pinned_value_ = ParsePackedValueForValue(iter_.value());
|
||||
} else {
|
||||
pinned_value_ = iter_.value();
|
||||
}
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = Status::NotSupported(
|
||||
@@ -1031,7 +1053,8 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
|
||||
return true;
|
||||
} else {
|
||||
assert(last_not_merge_type == kTypeValue);
|
||||
assert(last_not_merge_type == kTypeValue ||
|
||||
last_not_merge_type == kTypeValuePreferredSeqno);
|
||||
if (!MergeWithPlainBaseValue(pinned_value_, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
@@ -1039,6 +1062,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
case kTypeValuePreferredSeqno:
|
||||
SetValueAndColumnsFromPlain(pinned_value_);
|
||||
|
||||
break;
|
||||
@@ -1143,10 +1167,15 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
Slice ts = ExtractTimestampFromUserKey(ikey.user_key, timestamp_size_);
|
||||
saved_timestamp_.assign(ts.data(), ts.size());
|
||||
}
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeBlobIndex ||
|
||||
ikey.type == kTypeWideColumnEntity) {
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeValuePreferredSeqno ||
|
||||
ikey.type == kTypeBlobIndex || ikey.type == kTypeWideColumnEntity) {
|
||||
assert(iter_.iter()->IsValuePinned());
|
||||
pinned_value_ = iter_.value();
|
||||
saved_write_unix_time_ = iter_.write_unix_time();
|
||||
if (ikey.type == kTypeValuePreferredSeqno) {
|
||||
pinned_value_ = ParsePackedValueForValue(iter_.value());
|
||||
} else {
|
||||
pinned_value_ = iter_.value();
|
||||
}
|
||||
if (ikey.type == kTypeBlobIndex) {
|
||||
if (!SetBlobValueIfNeeded(ikey.user_key, pinned_value_)) {
|
||||
return false;
|
||||
@@ -1159,7 +1188,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(ikey.type == kTypeValue);
|
||||
assert(ikey.type == kTypeValue || ikey.type == kTypeValuePreferredSeqno);
|
||||
SetValueAndColumnsFromPlain(pinned_value_);
|
||||
}
|
||||
|
||||
@@ -1205,8 +1234,12 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeValue) {
|
||||
if (!MergeWithPlainBaseValue(iter_.value(), saved_key_.GetUserKey())) {
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeValuePreferredSeqno) {
|
||||
Slice value = iter_.value();
|
||||
if (ikey.type == kTypeValuePreferredSeqno) {
|
||||
value = ParsePackedValueForValue(value);
|
||||
}
|
||||
if (!MergeWithPlainBaseValue(value, saved_key_.GetUserKey())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1343,18 +1376,23 @@ bool DBIter::FindUserKeyBeforeSavedKey() {
|
||||
|
||||
if (num_skipped >= max_skip_) {
|
||||
num_skipped = 0;
|
||||
IterKey last_key;
|
||||
ParsedInternalKey pikey(saved_key_.GetUserKey(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
if (timestamp_size_ > 0) {
|
||||
std::string last_key;
|
||||
if (timestamp_size_ == 0) {
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetUserKey(),
|
||||
kMaxSequenceNumber,
|
||||
kValueTypeForSeek));
|
||||
} else {
|
||||
// TODO: pre-create kTsMax.
|
||||
const std::string kTsMax(timestamp_size_, '\xff');
|
||||
pikey.SetTimestamp(kTsMax);
|
||||
AppendInternalKeyWithDifferentTimestamp(
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek),
|
||||
kTsMax);
|
||||
}
|
||||
last_key.SetInternalKey(pikey);
|
||||
// It would be more efficient to use SeekForPrev() here, but some
|
||||
// iterators may not support it.
|
||||
iter_.Seek(last_key.GetInternalKey());
|
||||
iter_.Seek(last_key);
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
if (!iter_.Valid()) {
|
||||
break;
|
||||
@@ -1459,7 +1497,7 @@ void DBIter::Seek(const Slice& target) {
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
StopWatch sw(clock_, statistics_, DB_SEEK);
|
||||
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
if (cfh_ != nullptr) {
|
||||
// TODO: What do we do if this returns an error?
|
||||
Slice lower_bound, upper_bound;
|
||||
if (iterate_lower_bound_ != nullptr) {
|
||||
@@ -1472,7 +1510,9 @@ void DBIter::Seek(const Slice& target) {
|
||||
} else {
|
||||
upper_bound = Slice("");
|
||||
}
|
||||
db_impl_->TraceIteratorSeek(cfd_->GetID(), target, lower_bound, upper_bound)
|
||||
cfh_->db()
|
||||
->TraceIteratorSeek(cfh_->cfd()->GetID(), target, lower_bound,
|
||||
upper_bound)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
|
||||
@@ -1533,7 +1573,7 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
StopWatch sw(clock_, statistics_, DB_SEEK);
|
||||
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
if (cfh_ != nullptr) {
|
||||
// TODO: What do we do if this returns an error?
|
||||
Slice lower_bound, upper_bound;
|
||||
if (iterate_lower_bound_ != nullptr) {
|
||||
@@ -1546,8 +1586,8 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
} else {
|
||||
upper_bound = Slice("");
|
||||
}
|
||||
db_impl_
|
||||
->TraceIteratorSeekForPrev(cfd_->GetID(), target, lower_bound,
|
||||
cfh_->db()
|
||||
->TraceIteratorSeekForPrev(cfh_->cfd()->GetID(), target, lower_bound,
|
||||
upper_bound)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
@@ -1711,13 +1751,12 @@ Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
InternalIterator* internal_iter, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index) {
|
||||
DBIter* db_iter =
|
||||
new DBIter(env, read_options, ioptions, mutable_cf_options,
|
||||
user_key_comparator, internal_iter, version, sequence, false,
|
||||
max_sequential_skip_in_iterations, read_callback, db_impl, cfd,
|
||||
expose_blob_index);
|
||||
ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, read_options, ioptions, mutable_cf_options, user_key_comparator,
|
||||
internal_iter, version, sequence, false,
|
||||
max_sequential_skip_in_iterations, read_callback, cfh, expose_blob_index);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
|
||||
+15
-13
@@ -118,7 +118,7 @@ class DBIter final : public Iterator {
|
||||
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, const Version* version, SequenceNumber s,
|
||||
bool arena_mode, uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
|
||||
bool expose_blob_index);
|
||||
|
||||
// No copying allowed
|
||||
@@ -367,6 +367,12 @@ class DBIter final : public Iterator {
|
||||
// and should not be used across functions. Reusing this object can reduce
|
||||
// overhead of calling construction of the function if creating it each time.
|
||||
ParsedInternalKey ikey_;
|
||||
|
||||
// The approximate write time for the entry. It is deduced from the entry's
|
||||
// sequence number if the seqno to time mapping is available. For a
|
||||
// kTypeValuePreferredSeqno entry, this is the write time specified by the
|
||||
// user.
|
||||
uint64_t saved_write_unix_time_;
|
||||
std::string saved_value_;
|
||||
Slice pinned_value_;
|
||||
// for prefix seek mode to support prev()
|
||||
@@ -417,8 +423,7 @@ class DBIter final : public Iterator {
|
||||
MergeContext merge_context_;
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
DBImpl* db_impl_;
|
||||
ColumnFamilyData* cfd_;
|
||||
ColumnFamilyHandleImpl* cfh_;
|
||||
const Slice* const timestamp_ub_;
|
||||
const Slice* const timestamp_lb_;
|
||||
const size_t timestamp_size_;
|
||||
@@ -428,15 +433,12 @@ class DBIter final : public Iterator {
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified `sequence` number
|
||||
// into appropriate user keys.
|
||||
Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr,
|
||||
bool expose_blob_index = false);
|
||||
Iterator* NewDBIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const Version* version, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh = nullptr, bool expose_blob_index = false);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -45,6 +45,14 @@ class TestIterator : public InternalIterator {
|
||||
Add(argkey, kTypeValue, argvalue);
|
||||
}
|
||||
|
||||
void AddTimedPut(std::string argkey, std::string argvalue,
|
||||
uint64_t write_unix_time) {
|
||||
std::string packed_value;
|
||||
[[maybe_unused]] auto packed_value_slice =
|
||||
PackValueAndWriteTime(argvalue, write_unix_time, &packed_value);
|
||||
Add(argkey, kTypeValuePreferredSeqno, packed_value);
|
||||
}
|
||||
|
||||
void AddDeletion(std::string argkey) {
|
||||
Add(argkey, kTypeDeletion, std::string());
|
||||
}
|
||||
@@ -1388,6 +1396,60 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIteratorTimedPutBasic) {
|
||||
ReadOptions ro;
|
||||
Options options;
|
||||
options.merge_operator = MergeOperators::CreateFromStringId("stringappend");
|
||||
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddTimedPut("a", "0", /*write_unix_time=*/0);
|
||||
internal_iter->AddMerge("a", "1");
|
||||
internal_iter->AddTimedPut("b", "0", /*write_unix_time=*/0);
|
||||
internal_iter->AddDeletion("b");
|
||||
internal_iter->AddTimedPut("c", "01", /*write_unix_time=*/0);
|
||||
internal_iter->AddTimedPut("c", "02", /*write_unix_time=*/0);
|
||||
internal_iter->AddTimedPut("c", "2", /*write_unix_time=*/0);
|
||||
internal_iter->AddTimedPut("d", "3", /*write_unix_time=*/0);
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
7 /* sequence */, /*max_sequential_skip_in_iterations*/ 1,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "0,1");
|
||||
db_iter->Next();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "2");
|
||||
db_iter->Next();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "3");
|
||||
db_iter->Next();
|
||||
ASSERT_FALSE(db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "3");
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "2");
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "0,1");
|
||||
db_iter->Prev();
|
||||
ASSERT_FALSE(db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIterator1) {
|
||||
ReadOptions ro;
|
||||
Options options;
|
||||
|
||||
+15
-8
@@ -93,8 +93,8 @@ class DBIteratorTest : public DBIteratorBaseTest,
|
||||
if (column_family == nullptr) {
|
||||
column_family = db_->DefaultColumnFamily();
|
||||
}
|
||||
auto* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(column_family)->cfd();
|
||||
auto* cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto* cfd = cfh->cfd();
|
||||
SequenceNumber seq = read_options.snapshot != nullptr
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: db_->GetLatestSequenceNumber();
|
||||
@@ -109,7 +109,7 @@ class DBIteratorTest : public DBIteratorBaseTest,
|
||||
}
|
||||
DBImpl* db_impl = dbfull();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(db_impl);
|
||||
return db_impl->NewIteratorImpl(read_options, cfd, super_version, seq,
|
||||
return db_impl->NewIteratorImpl(read_options, cfh, super_version, seq,
|
||||
read_callback);
|
||||
}
|
||||
|
||||
@@ -142,6 +142,13 @@ TEST_P(DBIteratorTest, IteratorProperty) {
|
||||
// Get internal key at which the iteration stopped (tombstone in this case).
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.internal-key", &prop_value));
|
||||
ASSERT_EQ("2", prop_value);
|
||||
|
||||
prop_value.clear();
|
||||
ASSERT_OK(iter->GetProperty("rocksdb.iterator.write-time", &prop_value));
|
||||
uint64_t write_time;
|
||||
Slice prop_slice = prop_value;
|
||||
ASSERT_TRUE(GetFixed64(&prop_slice, &write_time));
|
||||
ASSERT_EQ(std::numeric_limits<uint64_t>::max(), write_time);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
@@ -3206,13 +3213,13 @@ TEST_F(DBIteratorWithReadCallbackTest, ReadCallback) {
|
||||
ASSERT_OK(Put("bar", "v7"));
|
||||
|
||||
SequenceNumber seq2 = db_->GetLatestSequenceNumber();
|
||||
auto* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(db_->DefaultColumnFamily())
|
||||
->cfd();
|
||||
auto* cfh = static_cast_with_check<ColumnFamilyHandleImpl>(
|
||||
db_->DefaultColumnFamily());
|
||||
auto* cfd = cfh->cfd();
|
||||
// The iterator are suppose to see data before seq1.
|
||||
DBImpl* db_impl = dbfull();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(db_impl);
|
||||
Iterator* iter = db_impl->NewIteratorImpl(ReadOptions(), cfd, super_version,
|
||||
Iterator* iter = db_impl->NewIteratorImpl(ReadOptions(), cfh, super_version,
|
||||
seq2, &callback1);
|
||||
|
||||
// Seek
|
||||
@@ -3292,7 +3299,7 @@ TEST_F(DBIteratorWithReadCallbackTest, ReadCallback) {
|
||||
|
||||
// The iterator is suppose to see data before seq3.
|
||||
super_version = cfd->GetReferencedSuperVersion(db_impl);
|
||||
iter = db_impl->NewIteratorImpl(ReadOptions(), cfd, super_version, seq4,
|
||||
iter = db_impl->NewIteratorImpl(ReadOptions(), cfh, super_version, seq4,
|
||||
&callback2);
|
||||
// Seek to "z", which is visible.
|
||||
iter->Seek("z");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/atomic.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -147,6 +148,7 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorRace) {
|
||||
}
|
||||
|
||||
TEST_F(DBTestXactLogIterator, TransactionLogIteratorCheckWhenArchive) {
|
||||
RelaxedAtomic<bool> callback_hit{};
|
||||
do {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearTrace();
|
||||
Options options = OptionsForLogIterTest();
|
||||
@@ -168,17 +170,23 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorCheckWhenArchive) {
|
||||
ASSERT_OK(dbfull()->Put(WriteOptions(), "key4", DummyString(1024)));
|
||||
ASSERT_OK(dbfull()->Flush(FlushOptions()));
|
||||
|
||||
callback_hit.StoreRelaxed(false);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WalManager::PurgeObsoleteFiles:1", [&](void*) {
|
||||
auto iter = OpenTransactionLogIter(0);
|
||||
ExpectRecords(4, iter);
|
||||
callback_hit.StoreRelaxed(true);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(dbfull()->Flush(FlushOptions(), cf));
|
||||
|
||||
delete cf;
|
||||
// Normally hit several times; WART: perhaps more in parallel after flush
|
||||
// FIXME: this test is flaky
|
||||
// ASSERT_TRUE(callback_hit.LoadRelaxed());
|
||||
} while (ChangeCompactOptions());
|
||||
Close();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -430,6 +430,111 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsLargeResultOptimization) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsShortCircuitInMemtable) {
|
||||
const int kNumOperands = 10;
|
||||
const int kNumOperandsToFetch = 5;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::vector<std::string> expected_merge_operands;
|
||||
expected_merge_operands.reserve(kNumOperands);
|
||||
for (int i = 0; i < kNumOperands; ++i) {
|
||||
expected_merge_operands.emplace_back(rnd.RandomString(7 /* len */));
|
||||
ASSERT_OK(Merge("key", expected_merge_operands.back()));
|
||||
}
|
||||
|
||||
std::vector<PinnableSlice> merge_operands(kNumOperands);
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = kNumOperands;
|
||||
int num_fetched = 0;
|
||||
merge_operands_info.continue_cb = [&](Slice /* value */) {
|
||||
num_fetched++;
|
||||
return num_fetched != kNumOperandsToFetch;
|
||||
};
|
||||
int num_merge_operands = 0;
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"key", merge_operands.data(),
|
||||
&merge_operands_info, &num_merge_operands));
|
||||
ASSERT_EQ(kNumOperandsToFetch, num_merge_operands);
|
||||
ASSERT_EQ(kNumOperandsToFetch, num_fetched);
|
||||
|
||||
for (int i = 0; i < kNumOperandsToFetch; ++i) {
|
||||
ASSERT_EQ(expected_merge_operands[kNumOperands - kNumOperandsToFetch + i],
|
||||
merge_operands[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsShortCircuitBaseValue) {
|
||||
// The continuation callback doesn't need to be called on a base value because
|
||||
// there's no remaining work to be saved.
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::string expected_value = rnd.RandomString(7 /* len */);
|
||||
ASSERT_OK(Put("key", expected_value));
|
||||
|
||||
std::vector<PinnableSlice> merge_operands(1);
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = 1;
|
||||
int num_fetched = 0;
|
||||
merge_operands_info.continue_cb = [&num_fetched](Slice /* value */) {
|
||||
num_fetched++;
|
||||
return true;
|
||||
};
|
||||
int num_merge_operands = 0;
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"key", merge_operands.data(),
|
||||
&merge_operands_info, &num_merge_operands));
|
||||
ASSERT_EQ(1, num_merge_operands);
|
||||
ASSERT_EQ(0, num_fetched);
|
||||
|
||||
ASSERT_EQ(expected_value, merge_operands[0]);
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsShortCircuitInSst) {
|
||||
const int kNumOperands = 10;
|
||||
const int kNumOperandsToFetch = 5;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::vector<std::string> expected_merge_operands;
|
||||
expected_merge_operands.reserve(kNumOperands);
|
||||
for (int i = 0; i < kNumOperands; ++i) {
|
||||
expected_merge_operands.emplace_back(rnd.RandomString(7 /* len */));
|
||||
ASSERT_OK(Merge("key", expected_merge_operands.back()));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
std::vector<PinnableSlice> merge_operands(kNumOperands);
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = kNumOperands;
|
||||
int num_fetched = 0;
|
||||
merge_operands_info.continue_cb = [&](Slice /* value */) {
|
||||
num_fetched++;
|
||||
return num_fetched != kNumOperandsToFetch;
|
||||
};
|
||||
int num_merge_operands = 0;
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"key", merge_operands.data(),
|
||||
&merge_operands_info, &num_merge_operands));
|
||||
ASSERT_EQ(kNumOperandsToFetch, num_merge_operands);
|
||||
ASSERT_EQ(kNumOperandsToFetch, num_fetched);
|
||||
|
||||
for (int i = 0; i < kNumOperandsToFetch; ++i) {
|
||||
ASSERT_EQ(expected_merge_operands[kNumOperands - kNumOperandsToFetch + i],
|
||||
merge_operands[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBaseDeletionInImmMem) {
|
||||
// In this test, "k1" has a MERGE in a mutable memtable on top of a base
|
||||
// DELETE in an immutable memtable.
|
||||
|
||||
@@ -2833,8 +2833,8 @@ TEST_F(DBRangeDelTest, LeftSentinelKeyTestWithNewerKey) {
|
||||
Arena arena;
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter;
|
||||
iter.set(
|
||||
ScopedArenaPtr<InternalIterator> iter;
|
||||
iter.reset(
|
||||
dbfull()->NewInternalIterator(read_options, &arena, kMaxSequenceNumber));
|
||||
|
||||
auto k = Key(4);
|
||||
|
||||
@@ -280,7 +280,7 @@ class DBTablePropertiesInRangeTest : public DBTestBase,
|
||||
// run the query
|
||||
TablePropertiesCollection props;
|
||||
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
|
||||
EXPECT_OK(db_->GetPropertiesOfTablesInRange(default_cf, &ranges[0],
|
||||
EXPECT_OK(db_->GetPropertiesOfTablesInRange(default_cf, ranges.data(),
|
||||
ranges.size(), &props));
|
||||
|
||||
const Comparator* ucmp = default_cf->GetComparator();
|
||||
|
||||
+8
-1
@@ -59,7 +59,6 @@
|
||||
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
@@ -3199,6 +3198,14 @@ class ModelDB : public DB {
|
||||
std::vector<Iterator*>* /*iterators*/) override {
|
||||
return Status::NotSupported("Not supported yet");
|
||||
}
|
||||
|
||||
// UNDER CONSTRUCTION - DO NOT USE
|
||||
std::unique_ptr<Iterator> NewMultiCfIterator(
|
||||
const ReadOptions& /*options*/,
|
||||
const std::vector<ColumnFamilyHandle*>& /*column_families*/) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Snapshot* GetSnapshot() override {
|
||||
ModelSnapshot* snapshot = new ModelSnapshot;
|
||||
snapshot->map_ = map_;
|
||||
|
||||
+140
-80
@@ -6604,7 +6604,7 @@ TEST_F(DBTest2, LastLevelTemperature) {
|
||||
auto* listener = new TestListener();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
options.last_level_temperature = Temperature::kWarm;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.num_levels = kNumLevels;
|
||||
@@ -6816,8 +6816,8 @@ TEST_F(DBTest2, LastLevelTemperatureUniversal) {
|
||||
size = GetSstSizeHelper(Temperature::kWarm);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
// Update bottommost temperature
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
// Update last level temperature
|
||||
options.last_level_temperature = Temperature::kWarm;
|
||||
Reopen(options);
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
// Should not impact existing ones
|
||||
@@ -6869,10 +6869,10 @@ TEST_F(DBTest2, LastLevelTemperatureUniversal) {
|
||||
&prop));
|
||||
ASSERT_EQ(std::atoi(prop.c_str()), 0);
|
||||
|
||||
// Update bottommost temperature dynamically with SetOptions
|
||||
// Update last level temperature dynamically with SetOptions
|
||||
auto s = db_->SetOptions({{"last_level_temperature", "kCold"}});
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(db_->GetOptions().bottommost_temperature, Temperature::kCold);
|
||||
ASSERT_EQ(db_->GetOptions().last_level_temperature, Temperature::kCold);
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
// Should not impact the existing files
|
||||
ASSERT_EQ(Temperature::kWarm,
|
||||
@@ -6898,94 +6898,133 @@ TEST_F(DBTest2, LastLevelTemperatureUniversal) {
|
||||
ASSERT_GT(size, 0);
|
||||
|
||||
// kLastTemperature is an invalid temperature
|
||||
options.bottommost_temperature = Temperature::kLastTemperature;
|
||||
options.last_level_temperature = Temperature::kLastTemperature;
|
||||
s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, LastLevelStatistics) {
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
options.default_temperature = Temperature::kHot;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
Reopen(options);
|
||||
for (bool write_time_default : {false, true}) {
|
||||
SCOPED_TRACE("write time default? " + std::to_string(write_time_default));
|
||||
Options options = CurrentOptions();
|
||||
options.last_level_temperature = Temperature::kWarm;
|
||||
if (write_time_default) {
|
||||
options.default_write_temperature = Temperature::kHot;
|
||||
ASSERT_EQ(options.default_temperature, Temperature::kUnknown);
|
||||
} else {
|
||||
options.default_temperature = Temperature::kHot;
|
||||
ASSERT_EQ(options.default_write_temperature, Temperature::kUnknown);
|
||||
}
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
|
||||
// generate 1 sst on level 0
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("bar", Get("bar"));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_GT(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_COUNT));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT), 0);
|
||||
// generate 1 sst on level 0
|
||||
ASSERT_OK(Put("foo1", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("bar", Get("bar"));
|
||||
|
||||
// 2nd flush to trigger compaction
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("bar", Get("bar"));
|
||||
ASSERT_GT(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_COUNT));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT), 0);
|
||||
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_COUNT));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_COUNT));
|
||||
// 2nd flush to trigger compaction
|
||||
ASSERT_OK(Put("foo2", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("bar", Get("bar"));
|
||||
|
||||
auto pre_bytes =
|
||||
options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES);
|
||||
auto pre_count =
|
||||
options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_COUNT));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_COUNT));
|
||||
|
||||
// 3rd flush to generate 1 sst on level 0
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("bar", Get("bar"));
|
||||
auto pre_bytes =
|
||||
options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES);
|
||||
auto pre_count =
|
||||
options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT);
|
||||
|
||||
ASSERT_GT(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
pre_bytes);
|
||||
ASSERT_GT(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
pre_count);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_COUNT));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_COUNT));
|
||||
// 3rd flush to generate 1 sst on level 0
|
||||
ASSERT_OK(Put("foo3", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("bar", Get("foo1"));
|
||||
ASSERT_EQ("bar", Get("foo2"));
|
||||
ASSERT_EQ("bar", Get("foo3"));
|
||||
ASSERT_EQ("bar", Get("bar"));
|
||||
|
||||
// Not a realistic setting to make last level kWarm and default temp kCold.
|
||||
// This is just for testing default temp can be reset on reopen while the
|
||||
// last level temp is consistent across DB reopen because those file's temp
|
||||
// are persisted in manifest.
|
||||
options.default_temperature = Temperature::kCold;
|
||||
ASSERT_OK(options.statistics->Reset());
|
||||
Reopen(options);
|
||||
ASSERT_EQ("bar", Get("bar"));
|
||||
ASSERT_GT(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
pre_bytes);
|
||||
ASSERT_GT(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
pre_count);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_COUNT));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_COUNT));
|
||||
// Control
|
||||
ASSERT_NE(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT));
|
||||
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
// Not a realistic setting to make last level kWarm and default temp kCold.
|
||||
// This is just for testing default temp can be reset on reopen while the
|
||||
// last level temp is consistent across DB reopen because those file's temp
|
||||
// are persisted in manifest.
|
||||
options.default_temperature = Temperature::kCold;
|
||||
ASSERT_OK(options.statistics->Reset());
|
||||
Reopen(options);
|
||||
ASSERT_EQ("bar", Get("foo1"));
|
||||
ASSERT_EQ("bar", Get("foo2"));
|
||||
ASSERT_EQ("bar", Get("foo3"));
|
||||
ASSERT_EQ("bar", Get("bar"));
|
||||
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(COLD_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(COLD_FILE_READ_COUNT));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_COUNT));
|
||||
if (write_time_default) {
|
||||
// Unchanged
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(HOT_FILE_READ_COUNT));
|
||||
|
||||
ASSERT_LT(0, options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(COLD_FILE_READ_BYTES));
|
||||
} else {
|
||||
// Changed (in how we map kUnknown)
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(COLD_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(COLD_FILE_READ_COUNT));
|
||||
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(HOT_FILE_READ_BYTES));
|
||||
ASSERT_LT(0, options.statistics->getTickerCount(COLD_FILE_READ_BYTES));
|
||||
}
|
||||
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_BYTES),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_BYTES));
|
||||
ASSERT_EQ(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(WARM_FILE_READ_COUNT));
|
||||
// Control
|
||||
ASSERT_NE(options.statistics->getTickerCount(LAST_LEVEL_READ_COUNT),
|
||||
options.statistics->getTickerCount(NON_LAST_LEVEL_READ_COUNT));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
@@ -7002,7 +7041,7 @@ TEST_F(DBTest2, CheckpointFileTemperature) {
|
||||
auto test_fs = std::make_shared<NoLinkTestFS>(env_->GetFileSystem());
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, test_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
options.last_level_temperature = Temperature::kWarm;
|
||||
// set dynamic_level to true so the compaction would compact the data to the
|
||||
// last level directly which will have the last_level_temperature
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
@@ -7061,7 +7100,7 @@ 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.last_level_temperature = Temperature::kWarm;
|
||||
// set dynamic_level to true so the compaction would compact the data to the
|
||||
// last level directly which will have the last_level_temperature
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
@@ -7769,6 +7808,27 @@ TEST_F(DBTest2, ZSTDChecksum) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(DBTest2, TableCacheMissDuringReadFromBlockCacheTier) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
Reopen(options);
|
||||
|
||||
// Give table cache zero capacity to prevent preloading tables. That way,
|
||||
// `kBlockCacheTier` reads will fail due to table cache misses.
|
||||
dbfull()->TEST_table_cache()->SetCapacity(0);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
uint64_t orig_num_file_opens = TestGetTickerCount(options, NO_FILE_OPENS);
|
||||
|
||||
ReadOptions non_blocking_opts;
|
||||
non_blocking_opts.read_tier = kBlockCacheTier;
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(non_blocking_opts, "foo", &value).IsIncomplete());
|
||||
|
||||
ASSERT_EQ(orig_num_file_opens, TestGetTickerCount(options, NO_FILE_OPENS));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+29
-11
@@ -759,6 +759,24 @@ Status DBTestBase::Put(int cf, const Slice& k, const Slice& v,
|
||||
}
|
||||
}
|
||||
|
||||
Status DBTestBase::TimedPut(const Slice& k, const Slice& v,
|
||||
uint64_t write_unix_time, WriteOptions wo) {
|
||||
return TimedPut(0, k, v, write_unix_time, wo);
|
||||
}
|
||||
|
||||
Status DBTestBase::TimedPut(int cf, const Slice& k, const Slice& v,
|
||||
uint64_t write_unix_time, WriteOptions wo) {
|
||||
WriteBatch wb;
|
||||
ColumnFamilyHandle* cfh;
|
||||
if (cf != 0) {
|
||||
cfh = handles_[cf];
|
||||
} else {
|
||||
cfh = db_->DefaultColumnFamily();
|
||||
}
|
||||
EXPECT_OK(wb.TimedPut(cfh, k, v, write_unix_time));
|
||||
return db_->Write(wo, &wb);
|
||||
}
|
||||
|
||||
Status DBTestBase::Merge(const Slice& k, const Slice& v, WriteOptions wo) {
|
||||
return db_->Merge(wo, k, v);
|
||||
}
|
||||
@@ -974,13 +992,13 @@ std::string DBTestBase::AllEntriesFor(const Slice& user_key, int cf) {
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter;
|
||||
ScopedArenaPtr<InternalIterator> iter;
|
||||
if (cf == 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber));
|
||||
iter.reset(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber));
|
||||
} else {
|
||||
iter.set(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber, handles_[cf]));
|
||||
iter.reset(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber, handles_[cf]));
|
||||
}
|
||||
InternalKey target(user_key, kMaxSequenceNumber, kTypeValue);
|
||||
iter->Seek(target.Encode());
|
||||
@@ -1152,7 +1170,7 @@ int DBTestBase::TotalTableFiles(int cf, int levels) {
|
||||
// Return spread of files per level
|
||||
std::string DBTestBase::FilesPerLevel(int cf) {
|
||||
int num_levels =
|
||||
(cf == 0) ? db_->NumberLevels() : db_->NumberLevels(handles_[1]);
|
||||
(cf == 0) ? db_->NumberLevels() : db_->NumberLevels(handles_[cf]);
|
||||
std::string result;
|
||||
size_t last_non_zero_offset = 0;
|
||||
for (int level = 0; level < num_levels; level++) {
|
||||
@@ -1453,13 +1471,13 @@ void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
ReadOptions read_options;
|
||||
ScopedArenaIterator iter;
|
||||
ScopedArenaPtr<InternalIterator> iter;
|
||||
if (cf != 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber, handles_[cf]));
|
||||
iter.reset(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber, handles_[cf]));
|
||||
} else {
|
||||
iter.set(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber));
|
||||
iter.reset(dbfull()->NewInternalIterator(read_options, &arena,
|
||||
kMaxSequenceNumber));
|
||||
}
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
+43
-3
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "file/filename.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -40,7 +41,6 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/cast_util.h"
|
||||
@@ -729,7 +729,11 @@ class FileTemperatureTestFS : public FileSystemWrapper {
|
||||
if (e != current_sst_file_temperatures_.end() &&
|
||||
e->second != opts.temperature) {
|
||||
result->reset();
|
||||
return IOStatus::PathNotFound("Temperature mismatch on " + fname);
|
||||
return IOStatus::PathNotFound(
|
||||
"Read requested temperature " +
|
||||
temperature_to_string[opts.temperature] +
|
||||
" but stored with temperature " +
|
||||
temperature_to_string[e->second] + " for " + fname);
|
||||
}
|
||||
}
|
||||
*result = WrapWithTemperature<FSSequentialFileOwnerWrapper>(
|
||||
@@ -758,7 +762,11 @@ class FileTemperatureTestFS : public FileSystemWrapper {
|
||||
if (e != current_sst_file_temperatures_.end() &&
|
||||
e->second != opts.temperature) {
|
||||
result->reset();
|
||||
return IOStatus::PathNotFound("Temperature mismatch on " + fname);
|
||||
return IOStatus::PathNotFound(
|
||||
"Read requested temperature " +
|
||||
temperature_to_string[opts.temperature] +
|
||||
" but stored with temperature " +
|
||||
temperature_to_string[e->second] + " for " + fname);
|
||||
}
|
||||
}
|
||||
*result = WrapWithTemperature<FSRandomAccessFileOwnerWrapper>(
|
||||
@@ -792,11 +800,37 @@ class FileTemperatureTestFS : public FileSystemWrapper {
|
||||
return target()->NewWritableFile(fname, opts, result, dbg);
|
||||
}
|
||||
|
||||
IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
|
||||
IODebugContext* dbg) override {
|
||||
IOStatus ios = target()->DeleteFile(fname, options, dbg);
|
||||
if (ios.ok()) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(GetFileName(fname), &number, &type) &&
|
||||
type == kTableFile) {
|
||||
MutexLock lock(&mu_);
|
||||
current_sst_file_temperatures_.erase(number);
|
||||
}
|
||||
}
|
||||
return ios;
|
||||
}
|
||||
|
||||
void CopyCurrentSstFileTemperatures(std::map<uint64_t, Temperature>* out) {
|
||||
MutexLock lock(&mu_);
|
||||
*out = current_sst_file_temperatures_;
|
||||
}
|
||||
|
||||
size_t CountCurrentSstFilesWithTemperature(Temperature temp) {
|
||||
MutexLock lock(&mu_);
|
||||
size_t count = 0;
|
||||
for (const auto& e : current_sst_file_temperatures_) {
|
||||
if (e.second == temp) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void OverrideSstFileTemperature(uint64_t number, Temperature temp) {
|
||||
MutexLock lock(&mu_);
|
||||
current_sst_file_temperatures_[number] = temp;
|
||||
@@ -1141,6 +1175,12 @@ class DBTestBase : public testing::Test {
|
||||
Status Put(int cf, const Slice& k, const Slice& v,
|
||||
WriteOptions wo = WriteOptions());
|
||||
|
||||
Status TimedPut(const Slice& k, const Slice& v, uint64_t write_unix_time,
|
||||
WriteOptions wo = WriteOptions());
|
||||
|
||||
Status TimedPut(int cf, const Slice& k, const Slice& v,
|
||||
uint64_t write_unix_time, WriteOptions wo = WriteOptions());
|
||||
|
||||
Status Merge(const Slice& k, const Slice& v,
|
||||
WriteOptions wo = WriteOptions());
|
||||
|
||||
|
||||
+4
-8
@@ -1123,15 +1123,13 @@ TEST_F(DBWALTest, PreallocateBlock) {
|
||||
}
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
|
||||
TEST_F(DBWALTest, DISABLED_FullPurgePreservesRecycledLog) {
|
||||
// TODO(ajkr): Disabled until WAL recycling is fixed for
|
||||
// `kPointInTimeRecovery`.
|
||||
|
||||
TEST_F(DBWALTest, FullPurgePreservesRecycledLog) {
|
||||
// For github issue #1303
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.recycle_log_file_num = 2;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
if (i != 0) {
|
||||
options.wal_dir = alternative_wal_dir_;
|
||||
}
|
||||
@@ -1162,16 +1160,14 @@ TEST_F(DBWALTest, DISABLED_FullPurgePreservesRecycledLog) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, DISABLED_FullPurgePreservesLogPendingReuse) {
|
||||
// TODO(ajkr): Disabled until WAL recycling is fixed for
|
||||
// `kPointInTimeRecovery`.
|
||||
|
||||
TEST_F(DBWALTest, FullPurgePreservesLogPendingReuse) {
|
||||
// Ensures full purge cannot delete a WAL while it's in the process of being
|
||||
// recycled. In particular, we force the full purge after a file has been
|
||||
// chosen for reuse, but before it has been renamed.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Options options = CurrentOptions();
|
||||
options.recycle_log_file_num = 1;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
if (i != 0) {
|
||||
options.wal_dir = alternative_wal_dir_;
|
||||
}
|
||||
|
||||
@@ -269,6 +269,47 @@ TEST_P(DBWriteTest, WriteThreadHangOnWriteStall) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, WriteThreadWaitNanosCounter) {
|
||||
Options options = GetOptions();
|
||||
std::vector<port::Thread> threads;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
std::function<void()> write_func = [&]() {
|
||||
PerfContext* perf_ctx = get_perf_context();
|
||||
SetPerfLevel(PerfLevel::kEnableWait);
|
||||
perf_ctx->Reset();
|
||||
TEST_SYNC_POINT("DBWriteTest::WriteThreadWaitNanosCounter:WriteFunc");
|
||||
ASSERT_OK(dbfull()->Put(WriteOptions(), "bar", "val2"));
|
||||
ASSERT_GT(perf_ctx->write_thread_wait_nanos, 2000000U);
|
||||
};
|
||||
|
||||
std::function<void()> sleep_func = [&]() {
|
||||
TEST_SYNC_POINT("DBWriteTest::WriteThreadWaitNanosCounter:SleepFunc:1");
|
||||
SystemClock::Default()->SleepForMicroseconds(2000);
|
||||
TEST_SYNC_POINT("DBWriteTest::WriteThreadWaitNanosCounter:SleepFunc:2");
|
||||
};
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"WriteThread::EnterAsBatchGroupLeader:End",
|
||||
"DBWriteTest::WriteThreadWaitNanosCounter:WriteFunc"},
|
||||
{"WriteThread::AwaitState:BlockingWaiting",
|
||||
"DBWriteTest::WriteThreadWaitNanosCounter:SleepFunc:1"},
|
||||
{"DBWriteTest::WriteThreadWaitNanosCounter:SleepFunc:2",
|
||||
"WriteThread::ExitAsBatchGroupLeader:Start"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
threads.emplace_back(sleep_func);
|
||||
threads.emplace_back(write_func);
|
||||
|
||||
ASSERT_OK(dbfull()->Put(WriteOptions(), "foo", "val1"));
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, IOErrorOnWALWritePropagateToWriteThreadFollower) {
|
||||
constexpr int kNumThreads = 5;
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
@@ -780,6 +821,95 @@ TEST_P(DBWriteTest, ConcurrentlyDisabledWAL) {
|
||||
ASSERT_LE(bytes_num, 1024 * 100);
|
||||
}
|
||||
|
||||
void CorruptLogFile(Env* env, Options& options, std::string log_path,
|
||||
uint64_t log_num, int record_num) {
|
||||
std::shared_ptr<FileSystem> fs = env->GetFileSystem();
|
||||
std::unique_ptr<SequentialFileReader> file_reader;
|
||||
Status status;
|
||||
{
|
||||
std::unique_ptr<FSSequentialFile> file;
|
||||
status = fs->NewSequentialFile(log_path, FileOptions(), &file, nullptr);
|
||||
ASSERT_EQ(status, IOStatus::OK());
|
||||
file_reader.reset(new SequentialFileReader(std::move(file), log_path));
|
||||
}
|
||||
std::unique_ptr<log::Reader> reader(new log::Reader(
|
||||
nullptr, std::move(file_reader), nullptr, false, log_num));
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
uint64_t record_checksum;
|
||||
for (int i = 0; i < record_num; ++i) {
|
||||
ASSERT_TRUE(reader->ReadRecord(&record, &scratch, options.wal_recovery_mode,
|
||||
&record_checksum));
|
||||
}
|
||||
uint64_t rec_start = reader->LastRecordOffset();
|
||||
reader.reset();
|
||||
{
|
||||
std::unique_ptr<FSRandomRWFile> file;
|
||||
status = fs->NewRandomRWFile(log_path, FileOptions(), &file, nullptr);
|
||||
ASSERT_EQ(status, IOStatus::OK());
|
||||
uint32_t bad_lognum = 0xff;
|
||||
ASSERT_EQ(file->Write(
|
||||
rec_start + 7,
|
||||
Slice(reinterpret_cast<char*>(&bad_lognum), sizeof(uint32_t)),
|
||||
IOOptions(), nullptr),
|
||||
IOStatus::OK());
|
||||
ASSERT_OK(file->Close(IOOptions(), nullptr));
|
||||
file.reset();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, RecycleLogTest) {
|
||||
Options options = GetOptions();
|
||||
options.recycle_log_file_num = 0;
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
ASSERT_OK(Put(Key(2), "val1"));
|
||||
|
||||
uint64_t latest_log_num = 0;
|
||||
std::unique_ptr<LogFile> log_file;
|
||||
ASSERT_OK(dbfull()->GetCurrentWalFile(&log_file));
|
||||
latest_log_num = log_file->LogNumber();
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put(Key(3), "val3"));
|
||||
|
||||
// Corrupt second entry of first log
|
||||
std::string log_path = LogFileName(dbname_, latest_log_num);
|
||||
CorruptLogFile(env_, options, log_path, latest_log_num, 2);
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ(Get(Key(1)), "val1");
|
||||
ASSERT_EQ(Get(Key(2)), "NOT_FOUND");
|
||||
ASSERT_EQ(Get(Key(3)), "NOT_FOUND");
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, RecycleLogTestCFAheadOfWAL) {
|
||||
Options options = GetOptions();
|
||||
options.recycle_log_file_num = 0;
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_OK(Put(1, Key(1), "val1"));
|
||||
ASSERT_OK(Put(0, Key(2), "val2"));
|
||||
|
||||
uint64_t latest_log_num = 0;
|
||||
std::unique_ptr<LogFile> log_file;
|
||||
ASSERT_OK(dbfull()->GetCurrentWalFile(&log_file));
|
||||
latest_log_num = log_file->LogNumber();
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, Key(3), "val3"));
|
||||
|
||||
// Corrupt second entry of first log
|
||||
std::string log_path = LogFileName(dbname_, latest_log_num);
|
||||
CorruptLogFile(env_, options, log_path, latest_log_num, 2);
|
||||
|
||||
ASSERT_EQ(TryReopenWithColumnFamilies({"default", "pikachu"}, options),
|
||||
Status::Corruption());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBWriteTestInstance, DBWriteTest,
|
||||
testing::Values(DBTestBase::kDefault,
|
||||
DBTestBase::kConcurrentWALWrites,
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// and the value type is embedded as the low 8 bits in the sequence
|
||||
// number in internal keys, we need to use the highest-numbered
|
||||
// ValueType, not the lowest).
|
||||
const ValueType kValueTypeForSeek = kTypeWideColumnEntity;
|
||||
const ValueType kValueTypeForSeek = kTypeValuePreferredSeqno;
|
||||
const ValueType kValueTypeForSeekForPrev = kTypeDeletion;
|
||||
const std::string kDisableUserTimestamp;
|
||||
|
||||
|
||||
+7
-3
@@ -68,7 +68,9 @@ enum ValueType : unsigned char {
|
||||
kTypeDeletionWithTimestamp = 0x14,
|
||||
kTypeCommitXIDAndTimestamp = 0x15, // WAL only
|
||||
kTypeWideColumnEntity = 0x16,
|
||||
kTypeColumnFamilyWideColumnEntity = 0x17, // WAL only
|
||||
kTypeColumnFamilyWideColumnEntity = 0x17, // WAL only
|
||||
kTypeValuePreferredSeqno = 0x18, // Value with a unix write time
|
||||
kTypeColumnFamilyValuePreferredSeqno = 0x19, // WAL only
|
||||
kTypeMaxValid, // Should be after the last valid type, only used for
|
||||
// validation
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
@@ -108,7 +110,8 @@ struct UserKeyRangePtr {
|
||||
// (i.e. a type used in memtable skiplist and sst file datablock).
|
||||
inline bool IsValueType(ValueType t) {
|
||||
return t <= kTypeMerge || kTypeSingleDeletion == t || kTypeBlobIndex == t ||
|
||||
kTypeDeletionWithTimestamp == t || kTypeWideColumnEntity == t;
|
||||
kTypeDeletionWithTimestamp == t || kTypeWideColumnEntity == t ||
|
||||
kTypeValuePreferredSeqno == t;
|
||||
}
|
||||
|
||||
// Checks whether a type is from user operation
|
||||
@@ -909,7 +912,8 @@ bool ReadKeyFromWriteBatchEntry(Slice* input, Slice* key, bool cf_record);
|
||||
// resulting from this call will include timestamp.
|
||||
Status ReadRecordFromWriteBatch(Slice* input, char* tag,
|
||||
uint32_t* column_family, Slice* key,
|
||||
Slice* value, Slice* blob, Slice* xid);
|
||||
Slice* value, Slice* blob, Slice* xid,
|
||||
uint64_t* write_unix_time);
|
||||
|
||||
// When user call DeleteRange() to delete a range of keys,
|
||||
// we will store a serialized RangeTombstone in MemTable and SST.
|
||||
|
||||
+4
-7
@@ -17,9 +17,7 @@
|
||||
#include "logging/logging.h"
|
||||
#include "util/atomic.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace experimental {
|
||||
|
||||
namespace ROCKSDB_NAMESPACE::experimental {
|
||||
|
||||
Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
@@ -378,7 +376,7 @@ enum BuiltinSstQueryFilters : char {
|
||||
|
||||
class SstQueryFilterBuilder {
|
||||
public:
|
||||
virtual ~SstQueryFilterBuilder() {}
|
||||
virtual ~SstQueryFilterBuilder() = default;
|
||||
virtual void Add(const Slice& key,
|
||||
const KeySegmentsExtractor::Result& extracted,
|
||||
const Slice* prev_key,
|
||||
@@ -395,7 +393,7 @@ class SstQueryFilterConfigImpl : public SstQueryFilterConfig {
|
||||
const KeySegmentsExtractor::KeyCategorySet& categories)
|
||||
: input_(input), categories_(categories) {}
|
||||
|
||||
virtual ~SstQueryFilterConfigImpl() {}
|
||||
virtual ~SstQueryFilterConfigImpl() = default;
|
||||
|
||||
virtual std::unique_ptr<SstQueryFilterBuilder> NewBuilder(
|
||||
bool sanity_checks) const = 0;
|
||||
@@ -1210,5 +1208,4 @@ Status SstQueryFilterConfigsManager::MakeShared(
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
} // namespace ROCKSDB_NAMESPACE::experimental
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
@@ -1861,100 +1862,166 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileAfterDBPut) {
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestWithTemperature) {
|
||||
Options options = CurrentOptions();
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
Reopen(options);
|
||||
// Rather than doubling the running time of this test, this boolean
|
||||
// field gets a random starting value and then alternates between
|
||||
// true and false.
|
||||
bool alternate_hint = Random::GetTLSInstance()->OneIn(2);
|
||||
Destroy(CurrentOptions());
|
||||
|
||||
auto size = GetSstSizeHelper(Temperature::kUnknown);
|
||||
ASSERT_EQ(size, 0);
|
||||
size = GetSstSizeHelper(Temperature::kWarm);
|
||||
ASSERT_EQ(size, 0);
|
||||
size = GetSstSizeHelper(Temperature::kHot);
|
||||
ASSERT_EQ(size, 0);
|
||||
for (std::string mode : {"ingest_behind", "fail_if_not", "neither"}) {
|
||||
SCOPED_TRACE("Mode: " + mode);
|
||||
|
||||
// create file01.sst (1000 => 1099) and ingest it
|
||||
std::string file1 = sst_files_dir_ + "file01.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 1000; k < 1100; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
|
||||
Options options = CurrentOptions();
|
||||
|
||||
auto test_fs =
|
||||
std::make_shared<FileTemperatureTestFS>(options.env->GetFileSystem());
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(options.env, test_fs));
|
||||
options.env = env.get();
|
||||
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
options.last_level_temperature = Temperature::kCold;
|
||||
options.default_write_temperature = Temperature::kHot;
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.allow_ingest_behind = (mode == "ingest_behind");
|
||||
Reopen(options);
|
||||
Defer destroyer([&]() { Destroy(options); });
|
||||
|
||||
#define VERIFY_SST_COUNT(temp, expected_count_in_db, \
|
||||
expected_count_outside_db) \
|
||||
{ \
|
||||
/* Partially verify against FileSystem */ \
|
||||
ASSERT_EQ( \
|
||||
test_fs->CountCurrentSstFilesWithTemperature(temp), \
|
||||
size_t{expected_count_in_db} + size_t{expected_count_outside_db}); \
|
||||
/* Partially verify against DB manifest */ \
|
||||
if (expected_count_in_db == 0) { \
|
||||
ASSERT_EQ(GetSstSizeHelper(temp), 0); \
|
||||
} else { \
|
||||
ASSERT_GE(GetSstSizeHelper(temp), 1); \
|
||||
} \
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(1000));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(1099));
|
||||
|
||||
std::vector<std::string> files;
|
||||
std::vector<std::string> files_checksums;
|
||||
std::vector<std::string> files_checksum_func_names;
|
||||
Temperature file_temperature = Temperature::kWarm;
|
||||
size_t ex_unknown_in_db = 0;
|
||||
size_t ex_hot_in_db = 0;
|
||||
size_t ex_warm_in_db = 0;
|
||||
size_t ex_cold_in_db = 0;
|
||||
size_t ex_unknown_outside_db = 0;
|
||||
size_t ex_hot_outside_db = 0;
|
||||
size_t ex_warm_outside_db = 0;
|
||||
size_t ex_cold_outside_db = 0;
|
||||
#define VERIFY_SST_COUNTS() \
|
||||
{ \
|
||||
VERIFY_SST_COUNT(Temperature::kUnknown, ex_unknown_in_db, \
|
||||
ex_unknown_outside_db); \
|
||||
VERIFY_SST_COUNT(Temperature::kHot, ex_hot_in_db, ex_hot_outside_db); \
|
||||
VERIFY_SST_COUNT(Temperature::kWarm, ex_warm_in_db, ex_warm_outside_db); \
|
||||
VERIFY_SST_COUNT(Temperature::kCold, ex_cold_in_db, ex_cold_outside_db); \
|
||||
}
|
||||
|
||||
files.push_back(file1);
|
||||
IngestExternalFileOptions in_opts;
|
||||
in_opts.move_files = false;
|
||||
in_opts.snapshot_consistency = true;
|
||||
in_opts.allow_global_seqno = false;
|
||||
in_opts.allow_blocking_flush = false;
|
||||
in_opts.write_global_seqno = true;
|
||||
in_opts.verify_file_checksum = false;
|
||||
IngestExternalFileArg arg;
|
||||
arg.column_family = db_->DefaultColumnFamily();
|
||||
arg.external_files = files;
|
||||
arg.options = in_opts;
|
||||
arg.files_checksums = files_checksums;
|
||||
arg.files_checksum_func_names = files_checksum_func_names;
|
||||
arg.file_temperature = file_temperature;
|
||||
s = db_->IngestExternalFiles({arg});
|
||||
ASSERT_OK(s);
|
||||
// Create sst file, using a name recognized by FileTemperatureTestFS and
|
||||
// specified temperature
|
||||
std::string file1 = sst_files_dir_ + "9000000.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1, Temperature::kWarm));
|
||||
for (int k = 1000; k < 1100; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// check the temperature of the file being ingested
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(1, metadata.file_count);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[6].files[0].temperature);
|
||||
size = GetSstSizeHelper(Temperature::kUnknown);
|
||||
ASSERT_EQ(size, 0);
|
||||
size = GetSstSizeHelper(Temperature::kWarm);
|
||||
ASSERT_GT(size, 1);
|
||||
ex_warm_outside_db++;
|
||||
VERIFY_SST_COUNTS();
|
||||
|
||||
// non-bottommost file still has unknown temperature
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(2, metadata.file_count);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
size = GetSstSizeHelper(Temperature::kUnknown);
|
||||
ASSERT_GT(size, 0);
|
||||
size = GetSstSizeHelper(Temperature::kWarm);
|
||||
ASSERT_GT(size, 0);
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(1000));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(1099));
|
||||
|
||||
// reopen and check the information is persisted
|
||||
Reopen(options);
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(2, metadata.file_count);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[6].files[0].temperature);
|
||||
size = GetSstSizeHelper(Temperature::kUnknown);
|
||||
ASSERT_GT(size, 0);
|
||||
size = GetSstSizeHelper(Temperature::kWarm);
|
||||
ASSERT_GT(size, 0);
|
||||
std::vector<std::string> files;
|
||||
std::vector<std::string> files_checksums;
|
||||
std::vector<std::string> files_checksum_func_names;
|
||||
|
||||
// check other non-exist temperatures
|
||||
size = GetSstSizeHelper(Temperature::kHot);
|
||||
ASSERT_EQ(size, 0);
|
||||
size = GetSstSizeHelper(Temperature::kCold);
|
||||
ASSERT_EQ(size, 0);
|
||||
std::string prop;
|
||||
ASSERT_TRUE(dbfull()->GetProperty(
|
||||
DB::Properties::kLiveSstFilesSizeAtTemperature + std::to_string(22),
|
||||
&prop));
|
||||
ASSERT_EQ(std::atoi(prop.c_str()), 0);
|
||||
files.push_back(file1);
|
||||
IngestExternalFileOptions in_opts;
|
||||
in_opts.move_files = false;
|
||||
in_opts.snapshot_consistency = true;
|
||||
in_opts.allow_global_seqno = false;
|
||||
in_opts.allow_blocking_flush = false;
|
||||
in_opts.write_global_seqno = true;
|
||||
in_opts.verify_file_checksum = false;
|
||||
in_opts.ingest_behind = (mode == "ingest_behind");
|
||||
in_opts.fail_if_not_bottommost_level = (mode == "fail_if_not");
|
||||
IngestExternalFileArg arg;
|
||||
arg.column_family = db_->DefaultColumnFamily();
|
||||
arg.external_files = files;
|
||||
arg.options = in_opts;
|
||||
arg.files_checksums = files_checksums;
|
||||
arg.files_checksum_func_names = files_checksum_func_names;
|
||||
alternate_hint = !alternate_hint;
|
||||
if (alternate_hint) {
|
||||
// Provide correct hint (for optimal file open performance)
|
||||
arg.file_temperature = Temperature::kWarm;
|
||||
} else {
|
||||
// No hint (also works because ingestion will read the temperature
|
||||
// according to storage)
|
||||
arg.file_temperature = Temperature::kUnknown;
|
||||
}
|
||||
s = db_->IngestExternalFiles({arg});
|
||||
ASSERT_OK(s);
|
||||
|
||||
// check the temperature of the file ingested (copied)
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(1, metadata.file_count);
|
||||
|
||||
if (mode != "neither") {
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[6].files[0].temperature);
|
||||
ex_cold_in_db++;
|
||||
} else {
|
||||
// Currently, we are only able to use last_level_temperature for ingestion
|
||||
// when using an ingestion option that guarantees ingestion to last level.
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[6].files[0].temperature);
|
||||
ex_hot_in_db++;
|
||||
}
|
||||
VERIFY_SST_COUNTS();
|
||||
|
||||
// non-bottommost file still has kHot temperature
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(2, metadata.file_count);
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
|
||||
|
||||
ex_hot_in_db++;
|
||||
VERIFY_SST_COUNTS();
|
||||
|
||||
// reopen and check the information is persisted
|
||||
Reopen(options);
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(2, metadata.file_count);
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
|
||||
if (mode != "neither") {
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[6].files[0].temperature);
|
||||
} else {
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[6].files[0].temperature);
|
||||
}
|
||||
|
||||
// (no change)
|
||||
VERIFY_SST_COUNTS();
|
||||
|
||||
// check invalid temperature with DB property. Not sure why the original
|
||||
// author is testing this case, but perhaps so that downgrading DB with
|
||||
// new GetProperty code using a new Temperature will report something
|
||||
// reasonable and not an error.
|
||||
std::string prop;
|
||||
ASSERT_TRUE(dbfull()->GetProperty(
|
||||
DB::Properties::kLiveSstFilesSizeAtTemperature + std::to_string(22),
|
||||
&prop));
|
||||
ASSERT_EQ(std::atoi(prop.c_str()), 0);
|
||||
#undef VERIFY_SST_COUNT
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, FailIfNotBottommostLevel) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include "db/external_sst_file_ingestion_job.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -18,12 +17,11 @@
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "logging/logging.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "table/sst_file_writer_collectors.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/udt_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -38,6 +36,8 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
// Read the information of files we are ingesting
|
||||
for (const std::string& file_path : external_files_paths) {
|
||||
IngestedFileInfo file_to_ingest;
|
||||
// For temperature, first assume it matches provided hint
|
||||
file_to_ingest.file_temperature = file_temperature;
|
||||
status =
|
||||
GetIngestedFileInfo(file_path, next_file_number++, &file_to_ingest, sv);
|
||||
if (!status.ok()) {
|
||||
@@ -91,11 +91,6 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
}
|
||||
}
|
||||
|
||||
// Hanlde the file temperature
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
files_to_ingest_[i].file_temperature = file_temperature;
|
||||
}
|
||||
|
||||
if (ingestion_options_.ingest_behind && files_overlap_) {
|
||||
return Status::NotSupported(
|
||||
"Files with overlapping ranges cannot be ingested with ingestion "
|
||||
@@ -160,10 +155,25 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
if (f.copy_file) {
|
||||
TEST_SYNC_POINT_CALLBACK("ExternalSstFileIngestionJob::Prepare:CopyFile",
|
||||
nullptr);
|
||||
// CopyFile also sync the new file.
|
||||
status =
|
||||
CopyFile(fs_.get(), path_outside_db, path_inside_db, 0,
|
||||
db_options_.use_fsync, io_tracer_, Temperature::kUnknown);
|
||||
// Always determining the destination temperature from the ingested-to
|
||||
// level would be difficult because in general we only find out the level
|
||||
// ingested to later, during Run().
|
||||
// However, we can guarantee "last level" temperature for when the user
|
||||
// requires ingestion to the last level.
|
||||
Temperature dst_temp =
|
||||
(ingestion_options_.ingest_behind ||
|
||||
ingestion_options_.fail_if_not_bottommost_level)
|
||||
? sv->mutable_cf_options.last_level_temperature
|
||||
: sv->mutable_cf_options.default_write_temperature;
|
||||
// Note: CopyFile also syncs the new file.
|
||||
status = CopyFile(fs_.get(), path_outside_db, f.file_temperature,
|
||||
path_inside_db, dst_temp, 0, db_options_.use_fsync,
|
||||
io_tracer_);
|
||||
// The destination of the copy will be ingested
|
||||
f.file_temperature = dst_temp;
|
||||
} else {
|
||||
// Note: we currently assume that linking files does not cross
|
||||
// temperatures, so no need to change f.file_temperature
|
||||
}
|
||||
TEST_SYNC_POINT("ExternalSstFileIngestionJob::Prepare:FileAdded");
|
||||
if (!status.ok()) {
|
||||
@@ -465,8 +475,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
? kReservedEpochNumberForFileIngestedBehind
|
||||
: cfd_->NewEpochNumber(),
|
||||
f.file_checksum, f.file_checksum_func_name, f.unique_id, 0, tail_size,
|
||||
static_cast<bool>(
|
||||
f.table_properties.user_defined_timestamps_persisted));
|
||||
f.user_defined_timestamps_persisted);
|
||||
f_metadata.temperature = f.file_temperature;
|
||||
edit_.AddFile(f.picked_level, f_metadata);
|
||||
}
|
||||
@@ -517,7 +526,8 @@ void ExternalSstFileIngestionJob::CreateEquivalentFileIngestingCompactions() {
|
||||
,
|
||||
LLONG_MAX /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID, not applicable */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.default_write_temperature,
|
||||
0 /* max_subcompaction, not applicable */,
|
||||
{} /* grandparents, not applicable */, false /* is manual */,
|
||||
"" /* trim_ts */, -1 /* score, not applicable */,
|
||||
@@ -644,38 +654,29 @@ void ExternalSstFileIngestionJob::DeleteInternalFiles() {
|
||||
}
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
Status ExternalSstFileIngestionJob::ResetTableReader(
|
||||
const std::string& external_file, uint64_t new_file_number,
|
||||
IngestedFileInfo* file_to_ingest, SuperVersion* sv) {
|
||||
file_to_ingest->external_file_path = external_file;
|
||||
|
||||
// Get external file size
|
||||
Status status = fs_->GetFileSize(external_file, IOOptions(),
|
||||
&file_to_ingest->file_size, nullptr);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Assign FD with number
|
||||
file_to_ingest->fd =
|
||||
FileDescriptor(new_file_number, 0, file_to_ingest->file_size);
|
||||
|
||||
// Create TableReader for external file
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
bool user_defined_timestamps_persisted, SuperVersion* sv,
|
||||
IngestedFileInfo* file_to_ingest,
|
||||
std::unique_ptr<TableReader>* table_reader) {
|
||||
std::unique_ptr<FSRandomAccessFile> sst_file;
|
||||
std::unique_ptr<RandomAccessFileReader> sst_file_reader;
|
||||
|
||||
status =
|
||||
fs_->NewRandomAccessFile(external_file, env_options_, &sst_file, nullptr);
|
||||
FileOptions fo{env_options_};
|
||||
fo.temperature = file_to_ingest->file_temperature;
|
||||
Status status =
|
||||
fs_->NewRandomAccessFile(external_file, fo, &sst_file, nullptr);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
sst_file_reader.reset(new RandomAccessFileReader(
|
||||
std::move(sst_file), external_file, nullptr /*Env*/, io_tracer_));
|
||||
|
||||
// TODO(yuzhangyu): User-defined timestamps doesn't support external sst file
|
||||
// ingestion. Pass in the correct `user_defined_timestamps_persisted` flag
|
||||
// for creating `TableReaderOptions` when the support is there.
|
||||
Temperature updated_temp = sst_file->GetTemperature();
|
||||
if (updated_temp != Temperature::kUnknown &&
|
||||
updated_temp != file_to_ingest->file_temperature) {
|
||||
// The hint was missing or wrong. Track temperature reported by storage.
|
||||
file_to_ingest->file_temperature = updated_temp;
|
||||
}
|
||||
std::unique_ptr<RandomAccessFileReader> sst_file_reader(
|
||||
new RandomAccessFileReader(std::move(sst_file), external_file,
|
||||
nullptr /*Env*/, io_tracer_));
|
||||
table_reader->reset();
|
||||
status = cfd_->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(
|
||||
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
|
||||
@@ -685,28 +686,20 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
/*force_direct_prefetch*/ false, /*level*/ -1,
|
||||
/*block_cache_tracer*/ nullptr,
|
||||
/*max_file_size_for_l0_meta_pin*/ 0, versions_->DbSessionId(),
|
||||
/*cur_file_num*/ new_file_number),
|
||||
std::move(sst_file_reader), file_to_ingest->file_size, &table_reader);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (ingestion_options_.verify_checksums_before_ingest) {
|
||||
// If customized readahead size is needed, we can pass a user option
|
||||
// all the way to here. Right now we just rely on the default readahead
|
||||
// to keep things simple.
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
ReadOptions ro;
|
||||
ro.readahead_size = ingestion_options_.verify_checksums_readahead_size;
|
||||
status = table_reader->VerifyChecksum(
|
||||
ro, TableReaderCaller::kExternalSSTIngestion);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
/*cur_file_num*/ new_file_number,
|
||||
/* unique_id */ {}, /* largest_seqno */ 0,
|
||||
/* tail_size */ 0, user_defined_timestamps_persisted),
|
||||
std::move(sst_file_reader), file_to_ingest->file_size, table_reader);
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::SanityCheckTableProperties(
|
||||
const std::string& external_file, uint64_t new_file_number,
|
||||
SuperVersion* sv, IngestedFileInfo* file_to_ingest,
|
||||
std::unique_ptr<TableReader>* table_reader) {
|
||||
// Get the external file properties
|
||||
auto props = table_reader->GetTableProperties();
|
||||
auto props = table_reader->get()->GetTableProperties();
|
||||
assert(props.get());
|
||||
const auto& uprops = props->user_collected_properties;
|
||||
|
||||
// Get table version
|
||||
@@ -744,10 +737,99 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
} else {
|
||||
return Status::InvalidArgument("External file version is not supported");
|
||||
}
|
||||
|
||||
file_to_ingest->cf_id = static_cast<uint32_t>(props->column_family_id);
|
||||
// This assignment works fine even though `table_reader` may later be reset,
|
||||
// since that will not affect how table properties are parsed, and this
|
||||
// assignment is making a copy.
|
||||
file_to_ingest->table_properties = *props;
|
||||
|
||||
// Get number of entries in table
|
||||
file_to_ingest->num_entries = props->num_entries;
|
||||
file_to_ingest->num_range_deletions = props->num_range_deletions;
|
||||
|
||||
// Validate table properties related to comparator name and user defined
|
||||
// timestamps persisted flag.
|
||||
file_to_ingest->user_defined_timestamps_persisted =
|
||||
static_cast<bool>(props->user_defined_timestamps_persisted);
|
||||
bool mark_sst_file_has_no_udt = false;
|
||||
Status s = ValidateUserDefinedTimestampsOptions(
|
||||
cfd_->user_comparator(), props->comparator_name,
|
||||
cfd_->ioptions()->persist_user_defined_timestamps,
|
||||
file_to_ingest->user_defined_timestamps_persisted,
|
||||
&mark_sst_file_has_no_udt);
|
||||
if (s.ok() && mark_sst_file_has_no_udt) {
|
||||
// A column family that enables user-defined timestamps in Memtable only
|
||||
// feature can also ingest external files created by a setting that disables
|
||||
// user-defined timestamps. In that case, we need to re-mark the
|
||||
// user_defined_timestamps_persisted flag for the file.
|
||||
file_to_ingest->user_defined_timestamps_persisted = false;
|
||||
} else if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// `TableReader` is initialized with `user_defined_timestamps_persisted` flag
|
||||
// to be true. If its value changed to false after this sanity check, we
|
||||
// need to reset the `TableReader`.
|
||||
auto ucmp = cfd_->user_comparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() > 0 &&
|
||||
!file_to_ingest->user_defined_timestamps_persisted) {
|
||||
s = ResetTableReader(external_file, new_file_number,
|
||||
file_to_ingest->user_defined_timestamps_persisted, sv,
|
||||
file_to_ingest, table_reader);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
const std::string& external_file, uint64_t new_file_number,
|
||||
IngestedFileInfo* file_to_ingest, SuperVersion* sv) {
|
||||
file_to_ingest->external_file_path = external_file;
|
||||
|
||||
// Get external file size
|
||||
Status status = fs_->GetFileSize(external_file, IOOptions(),
|
||||
&file_to_ingest->file_size, nullptr);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Assign FD with number
|
||||
file_to_ingest->fd =
|
||||
FileDescriptor(new_file_number, 0, file_to_ingest->file_size);
|
||||
|
||||
// Create TableReader for external file
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
// Initially create the `TableReader` with flag
|
||||
// `user_defined_timestamps_persisted` to be true since that's the most common
|
||||
// case
|
||||
status = ResetTableReader(external_file, new_file_number,
|
||||
/*user_defined_timestamps_persisted=*/true, sv,
|
||||
file_to_ingest, &table_reader);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = SanityCheckTableProperties(external_file, new_file_number, sv,
|
||||
file_to_ingest, &table_reader);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (ingestion_options_.verify_checksums_before_ingest) {
|
||||
// If customized readahead size is needed, we can pass a user option
|
||||
// all the way to here. Right now we just rely on the default readahead
|
||||
// to keep things simple.
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
ReadOptions ro;
|
||||
ro.readahead_size = ingestion_options_.verify_checksums_readahead_size;
|
||||
status = table_reader->VerifyChecksum(
|
||||
ro, TableReaderCaller::kExternalSSTIngestion);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
ParsedInternalKey key;
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
ReadOptions ro;
|
||||
@@ -820,7 +902,7 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
table_reader->NewRangeTombstoneIterator(ro));
|
||||
// We may need to adjust these key bounds, depending on whether any range
|
||||
// deletion tombstones extend past them.
|
||||
const Comparator* ucmp = cfd_->internal_comparator().user_comparator();
|
||||
const Comparator* ucmp = cfd_->user_comparator();
|
||||
if (range_del_iter != nullptr) {
|
||||
for (range_del_iter->SeekToFirst(); range_del_iter->Valid();
|
||||
range_del_iter->Next()) {
|
||||
@@ -848,13 +930,11 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
}
|
||||
}
|
||||
|
||||
file_to_ingest->cf_id = static_cast<uint32_t>(props->column_family_id);
|
||||
|
||||
file_to_ingest->table_properties = *props;
|
||||
|
||||
auto s = GetSstInternalUniqueId(props->db_id, props->db_session_id,
|
||||
props->orig_file_number,
|
||||
&(file_to_ingest->unique_id));
|
||||
auto s =
|
||||
GetSstInternalUniqueId(file_to_ingest->table_properties.db_id,
|
||||
file_to_ingest->table_properties.db_session_id,
|
||||
file_to_ingest->table_properties.orig_file_number,
|
||||
&(file_to_ingest->unique_id));
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Failed to get SST unique id for file %s",
|
||||
@@ -966,12 +1046,12 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
Status ExternalSstFileIngestionJob::CheckLevelForIngestedBehindFile(
|
||||
IngestedFileInfo* file_to_ingest) {
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
// First, check if new files fit in the bottommost level
|
||||
int bottom_lvl = cfd_->NumberLevels() - 1;
|
||||
if (!IngestedFileFitInLevel(file_to_ingest, bottom_lvl)) {
|
||||
// First, check if new files fit in the last level
|
||||
int last_lvl = cfd_->NumberLevels() - 1;
|
||||
if (!IngestedFileFitInLevel(file_to_ingest, last_lvl)) {
|
||||
return Status::InvalidArgument(
|
||||
"Can't ingest_behind file as it doesn't fit "
|
||||
"at the bottommost level!");
|
||||
"at the last level!");
|
||||
}
|
||||
|
||||
// Second, check if despite allow_ingest_behind=true we still have 0 seqnums
|
||||
@@ -986,7 +1066,7 @@ Status ExternalSstFileIngestionJob::CheckLevelForIngestedBehindFile(
|
||||
}
|
||||
}
|
||||
|
||||
file_to_ingest->picked_level = bottom_lvl;
|
||||
file_to_ingest->picked_level = last_lvl;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,14 @@ struct IngestedFileInfo {
|
||||
Temperature file_temperature = Temperature::kUnknown;
|
||||
// Unique id of the file to be ingested
|
||||
UniqueId64x2 unique_id{};
|
||||
// Whether the external file should be treated as if it has user-defined
|
||||
// timestamps or not. If this flag is false, and the column family enables
|
||||
// UDT feature, the file will have min-timestamp artificially padded to its
|
||||
// user keys when it's read. Since it will affect how `TableReader` reads a
|
||||
// table file, it's defaulted to optimize for the majority of the case where
|
||||
// the user key's format in the external file matches the column family's
|
||||
// setting.
|
||||
bool user_defined_timestamps_persisted = true;
|
||||
};
|
||||
|
||||
class ExternalSstFileIngestionJob {
|
||||
@@ -151,6 +159,23 @@ class ExternalSstFileIngestionJob {
|
||||
int ConsumedSequenceNumbersCount() const { return consumed_seqno_count_; }
|
||||
|
||||
private:
|
||||
Status ResetTableReader(const std::string& external_file,
|
||||
uint64_t new_file_number,
|
||||
bool user_defined_timestamps_persisted,
|
||||
SuperVersion* sv, IngestedFileInfo* file_to_ingest,
|
||||
std::unique_ptr<TableReader>* table_reader);
|
||||
|
||||
// Read the external file's table properties to do various sanity checks and
|
||||
// populates certain fields in `IngestedFileInfo` according to some table
|
||||
// properties.
|
||||
// In some cases when sanity check passes, `table_reader` could be reset with
|
||||
// different options. For example: when external file does not contain
|
||||
// timestamps while column family enables UDT in Memtables only feature.
|
||||
Status SanityCheckTableProperties(const std::string& external_file,
|
||||
uint64_t new_file_number, SuperVersion* sv,
|
||||
IngestedFileInfo* file_to_ingest,
|
||||
std::unique_ptr<TableReader>* table_reader);
|
||||
|
||||
// Open the external file and populate `file_to_ingest` with all the
|
||||
// external information we need to ingest this file.
|
||||
Status GetIngestedFileInfo(const std::string& external_file,
|
||||
|
||||
@@ -295,6 +295,25 @@ class ExternalSSTFileTest
|
||||
int last_file_id_ = 0;
|
||||
};
|
||||
|
||||
TEST_F(ExternalSSTFileTest, ComparatorMismatch) {
|
||||
Options options = CurrentOptions();
|
||||
Options options_diff_ucmp = options;
|
||||
|
||||
options.comparator = BytewiseComparator();
|
||||
options_diff_ucmp.comparator = ReverseBytewiseComparator();
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options_diff_ucmp);
|
||||
|
||||
std::string file = sst_files_dir_ + "file.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file));
|
||||
ASSERT_OK(sst_file_writer.Put("foo", "val"));
|
||||
ASSERT_OK(sst_file_writer.Put("bar", "val1"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_NOK(DeprecatedAddFile({file}));
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, Basic) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
@@ -3342,6 +3361,252 @@ TEST_F(ExternalSSTFileWithTimestampTest, SanityCheck) {
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileWithTimestampTest, UDTSettingsCompatibilityCheck) {
|
||||
Options options = CurrentOptions();
|
||||
Options disable_udt_options = options;
|
||||
Options not_persist_udt_options = options;
|
||||
Options persist_udt_options = options;
|
||||
disable_udt_options.comparator = BytewiseComparator();
|
||||
not_persist_udt_options.comparator =
|
||||
test::BytewiseComparatorWithU64TsWrapper();
|
||||
not_persist_udt_options.persist_user_defined_timestamps = false;
|
||||
not_persist_udt_options.allow_concurrent_memtable_write = false;
|
||||
persist_udt_options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
persist_udt_options.persist_user_defined_timestamps = true;
|
||||
|
||||
EnvOptions env_options = EnvOptions();
|
||||
|
||||
SstFileWriter disable_udt_sst_writer(env_options, disable_udt_options);
|
||||
SstFileWriter not_persist_udt_sst_writer(env_options,
|
||||
not_persist_udt_options);
|
||||
SstFileWriter persist_udt_sst_writer(env_options, persist_udt_options);
|
||||
|
||||
// File1: [0, 50), contains no timestamps
|
||||
// comparator name: leveldb.BytewiseComparator
|
||||
// user_defined_timestamps_persisted: true
|
||||
std::string disable_udt_sst_file = sst_files_dir_ + "file1.sst";
|
||||
ASSERT_OK(disable_udt_sst_writer.Open(disable_udt_sst_file));
|
||||
for (int k = 0; k < 50; k++) {
|
||||
ASSERT_NOK(
|
||||
disable_udt_sst_writer.Put(Key(k), EncodeAsUint64(1), Key(k) + "_val"));
|
||||
ASSERT_OK(disable_udt_sst_writer.Put(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ASSERT_OK(disable_udt_sst_writer.Finish());
|
||||
|
||||
// File2: [50, 100), contains no timestamps
|
||||
// comparator name: leveldb.BytewiseComparator.u64ts
|
||||
// user_defined_timestamps_persisted: false
|
||||
std::string not_persist_udt_sst_file = sst_files_dir_ + "file2.sst";
|
||||
ASSERT_OK(not_persist_udt_sst_writer.Open(not_persist_udt_sst_file));
|
||||
for (int k = 50; k < 100; k++) {
|
||||
ASSERT_NOK(not_persist_udt_sst_writer.Put(Key(k), Key(k) + "_val"));
|
||||
ASSERT_NOK(not_persist_udt_sst_writer.Put(Key(k), EncodeAsUint64(k),
|
||||
Key(k) + "_val"));
|
||||
ASSERT_OK(not_persist_udt_sst_writer.Put(Key(k), EncodeAsUint64(0),
|
||||
Key(k) + "_val"));
|
||||
}
|
||||
ASSERT_OK(not_persist_udt_sst_writer.Finish());
|
||||
|
||||
// File3: [100, 150), contains timestamp
|
||||
// comparator name: leveldb.BytewiseComparator.u64ts
|
||||
// user_defined_timestamps_persisted: true
|
||||
std::string persist_udt_sst_file = sst_files_dir_ + "file3.sst";
|
||||
ASSERT_OK(persist_udt_sst_writer.Open(persist_udt_sst_file));
|
||||
for (int k = 100; k < 150; k++) {
|
||||
ASSERT_NOK(persist_udt_sst_writer.Put(Key(k), Key(k) + "_val"));
|
||||
ASSERT_OK(
|
||||
persist_udt_sst_writer.Put(Key(k), EncodeAsUint64(k), Key(k) + "_val"));
|
||||
}
|
||||
ASSERT_OK(persist_udt_sst_writer.Finish());
|
||||
|
||||
DestroyAndReopen(disable_udt_options);
|
||||
ASSERT_OK(
|
||||
IngestExternalUDTFile({disable_udt_sst_file, not_persist_udt_sst_file}));
|
||||
ASSERT_NOK(IngestExternalUDTFile({persist_udt_sst_file}));
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
|
||||
DestroyAndReopen(not_persist_udt_options);
|
||||
ASSERT_OK(
|
||||
IngestExternalUDTFile({disable_udt_sst_file, not_persist_udt_sst_file}));
|
||||
ASSERT_NOK(IngestExternalUDTFile({persist_udt_sst_file}));
|
||||
for (int k = 0; k < 100; k++) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(0), Key(k) + "_val",
|
||||
EncodeAsUint64(0));
|
||||
}
|
||||
|
||||
DestroyAndReopen(persist_udt_options);
|
||||
ASSERT_NOK(
|
||||
IngestExternalUDTFile({disable_udt_sst_file, not_persist_udt_sst_file}));
|
||||
ASSERT_OK(IngestExternalUDTFile({persist_udt_sst_file}));
|
||||
for (int k = 100; k < 150; k++) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(k), Key(k) + "_val",
|
||||
EncodeAsUint64(k));
|
||||
}
|
||||
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileWithTimestampTest, TimestampsNotPersistedBasic) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
options.persist_user_defined_timestamps = false;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// file1.sst [0, 50)
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 0; k < 50; k++) {
|
||||
// Attempting to write 2 versions of values for each key, only the version
|
||||
// with timestamp 0 goes through.
|
||||
for (int version = 1; version >= 0; version--) {
|
||||
if (version == 1) {
|
||||
ASSERT_NOK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(version),
|
||||
Key(k) + "_val" + std::to_string(version)));
|
||||
} else {
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(version),
|
||||
Key(k) + "_val" + std::to_string(version)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExternalSstFileInfo file1_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file1_info));
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
ASSERT_NOK(sst_file_writer.Put(Key(100), EncodeAsUint64(0), "bad_val"));
|
||||
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 50);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(0));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(49));
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
// Add file using file path
|
||||
ASSERT_OK(IngestExternalUDTFile({file1}));
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
|
||||
// Read ingested file: all data contain minimum timestamps.
|
||||
for (int k = 0; k < 50; k++) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(0),
|
||||
Key(k) + "_val" + std::to_string(0), EncodeAsUint64(0));
|
||||
}
|
||||
|
||||
// file2.sst [50, 200)
|
||||
// Put [key=k, ts=0, value=k_val0] for k in [50, 200)
|
||||
// RangeDelete[start_key=75, end_key=125, ts=0]
|
||||
std::string file2 = sst_files_dir_ + "file2.sst";
|
||||
int range_del_begin = 75, range_del_end = 125;
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
for (int k = 50; k < 200; k++) {
|
||||
// All these timestamps will later be effectively 0
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(0), Key(k) + "_val0"));
|
||||
}
|
||||
ASSERT_OK(sst_file_writer.DeleteRange(
|
||||
Key(range_del_begin), Key(range_del_end), EncodeAsUint64(0)));
|
||||
|
||||
ExternalSstFileInfo file2_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file2_info));
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
|
||||
ASSERT_EQ(file2_info.file_path, file2);
|
||||
ASSERT_EQ(file2_info.num_entries, 150);
|
||||
ASSERT_EQ(file2_info.smallest_key, Key(50));
|
||||
ASSERT_EQ(file2_info.largest_key, Key(199));
|
||||
ASSERT_EQ(file2_info.num_range_del_entries, 1);
|
||||
ASSERT_EQ(file2_info.smallest_range_del_key, Key(range_del_begin));
|
||||
ASSERT_EQ(file2_info.largest_range_del_key, Key(range_del_end));
|
||||
// Add file using file path
|
||||
ASSERT_OK(IngestExternalUDTFile({file2}));
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
|
||||
// Range deletion covering point data in the same file is over-written.
|
||||
for (int k = 50; k < 200; k++) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(0), Key(k) + "_val0",
|
||||
EncodeAsUint64(0));
|
||||
}
|
||||
|
||||
// file3.sst [100, 200), key range overlap with db
|
||||
std::string file3 = sst_files_dir_ + "file3.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file3));
|
||||
for (int k = 100; k < 200; k++) {
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(0), Key(k) + "_val0"));
|
||||
}
|
||||
ExternalSstFileInfo file3_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file3_info));
|
||||
ASSERT_EQ(file3_info.file_path, file3);
|
||||
ASSERT_EQ(file3_info.num_entries, 100);
|
||||
ASSERT_EQ(file3_info.smallest_key, Key(100));
|
||||
ASSERT_EQ(file3_info.largest_key, Key(199));
|
||||
|
||||
// In UDT mode, file with overlapping key range cannot be ingested.
|
||||
ASSERT_NOK(IngestExternalUDTFile({file3}));
|
||||
ASSERT_NOK(IngestExternalUDTFile({file3}, /*allow_global_seqno*/ false));
|
||||
|
||||
// Write [0, 50)
|
||||
// Write to DB newer versions to cover ingested data and move sequence
|
||||
// number forward.
|
||||
for (int k = 0; k < 50; k++) {
|
||||
for (int version = 1; version < 3; version++) {
|
||||
ASSERT_OK(dbfull()->Put(WriteOptions(), Key(k), EncodeAsUint64(version),
|
||||
Key(k) + "_val" + std::to_string(version)));
|
||||
}
|
||||
}
|
||||
|
||||
// Read three versions (1 from ingested, 2 from live writes)
|
||||
for (int k = 0; k < 50; k++) {
|
||||
for (int version = 0; version < 3; version++) {
|
||||
VerifyValueAndTs(Key(k), EncodeAsUint64(version),
|
||||
Key(k) + "_val" + std::to_string(version),
|
||||
EncodeAsUint64(version));
|
||||
}
|
||||
}
|
||||
SequenceNumber seq_num_before_ingestion = db_->GetLatestSequenceNumber();
|
||||
ASSERT_GT(seq_num_before_ingestion, 0U);
|
||||
|
||||
// file4.sst [200, 250)
|
||||
std::string file4 = sst_files_dir_ + "file4.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file4));
|
||||
for (int k = 200; k < 250; k++) {
|
||||
ASSERT_OK(
|
||||
sst_file_writer.Put(Key(k), EncodeAsUint64(0), Key(k) + "_val"));
|
||||
}
|
||||
|
||||
ExternalSstFileInfo file4_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&file4_info));
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
|
||||
ASSERT_EQ(file4_info.file_path, file4);
|
||||
ASSERT_EQ(file4_info.num_entries, 50);
|
||||
ASSERT_EQ(file4_info.smallest_key, Key(200));
|
||||
ASSERT_EQ(file4_info.largest_key, Key(249));
|
||||
ASSERT_EQ(file4_info.num_range_del_entries, 0);
|
||||
ASSERT_EQ(file4_info.smallest_range_del_key, "");
|
||||
ASSERT_EQ(file4_info.largest_range_del_key, "");
|
||||
|
||||
ASSERT_OK(IngestExternalUDTFile({file4}));
|
||||
|
||||
// Ingested files do not overlap with db, they can always have global seqno
|
||||
// 0 assigned.
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), seq_num_before_ingestion);
|
||||
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipFIFOCompaction |
|
||||
kRangeDelSkipConfigs));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
|
||||
+14
-16
@@ -100,9 +100,9 @@ FlushJob::FlushJob(
|
||||
Statistics* stats, EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const SeqnoToTimeMapping& seqno_to_time_mapping, const std::string& db_id,
|
||||
const std::string& db_session_id, std::string full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback)
|
||||
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string full_history_ts_low, BlobFileCompletionCallback* blob_callback)
|
||||
: dbname_(dbname),
|
||||
db_id_(db_id),
|
||||
db_session_id_(db_session_id),
|
||||
@@ -136,7 +136,7 @@ FlushJob::FlushJob(
|
||||
clock_(db_options_.clock),
|
||||
full_history_ts_low_(std::move(full_history_ts_low)),
|
||||
blob_callback_(blob_callback),
|
||||
db_impl_seqno_to_time_mapping_(seqno_to_time_mapping) {
|
||||
seqno_to_time_mapping_(std::move(seqno_to_time_mapping)) {
|
||||
// Update the thread status to indicate flush.
|
||||
ReportStartedFlush();
|
||||
TEST_SYNC_POINT("FlushJob::FlushJob()");
|
||||
@@ -417,7 +417,8 @@ Status FlushJob::MemPurge() {
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters;
|
||||
for (MemTable* m : mems_) {
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
memtables.push_back(
|
||||
m->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena));
|
||||
auto* range_del_iter = m->NewRangeTombstoneIterator(
|
||||
ro, kMaxSequenceNumber, true /* immutable_memtable */);
|
||||
if (range_del_iter != nullptr) {
|
||||
@@ -439,7 +440,7 @@ Status FlushJob::MemPurge() {
|
||||
: earliest_seqno;
|
||||
}
|
||||
|
||||
ScopedArenaIterator iter(
|
||||
ScopedArenaPtr<InternalIterator> iter(
|
||||
NewMergingIterator(&(cfd_->internal_comparator()), memtables.data(),
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
|
||||
@@ -850,12 +851,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
const uint64_t start_cpu_micros = clock_->CPUMicros();
|
||||
Status s;
|
||||
|
||||
SequenceNumber smallest_seqno = mems_.front()->GetEarliestSequenceNumber();
|
||||
if (!db_impl_seqno_to_time_mapping_.Empty()) {
|
||||
// make a local copy to use while not holding the db_mutex.
|
||||
seqno_to_time_mapping_.CopyFromSeqnoRange(db_impl_seqno_to_time_mapping_,
|
||||
smallest_seqno);
|
||||
}
|
||||
meta_.temperature = mutable_cf_options_.default_write_temperature;
|
||||
file_options_.temperature = meta_.temperature;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
@@ -892,7 +889,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Flushing memtable with next log file: %" PRIu64 "\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, m->GetNextLogNumber());
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
memtables.push_back(
|
||||
m->NewIterator(ro, /*seqno_to_time_mapping=*/nullptr, &arena));
|
||||
auto* range_del_iter = m->NewRangeTombstoneIterator(
|
||||
ro, kMaxSequenceNumber, true /* immutable_memtable */);
|
||||
if (range_del_iter != nullptr) {
|
||||
@@ -919,7 +917,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
<< GetFlushReasonString(flush_reason_);
|
||||
|
||||
{
|
||||
ScopedArenaIterator iter(
|
||||
ScopedArenaPtr<InternalIterator> iter(
|
||||
NewMergingIterator(&cfd_->internal_comparator(), memtables.data(),
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
@@ -981,8 +979,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
earliest_write_conflict_snapshot_, job_snapshot_seq,
|
||||
snapshot_checker_, mutable_cf_options_.paranoid_file_checks,
|
||||
cfd_->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kFlush, seqno_to_time_mapping_, event_logger_,
|
||||
job_context_->job_id, &table_properties_, write_hint,
|
||||
BlobFileCreationReason::kFlush, seqno_to_time_mapping_.get(),
|
||||
event_logger_, job_context_->job_id, &table_properties_, write_hint,
|
||||
full_history_ts_low, blob_callback_, base_, &num_input_entries,
|
||||
&memtable_payload_bytes, &memtable_garbage_bytes);
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:s", &s);
|
||||
|
||||
+10
-7
@@ -39,7 +39,6 @@
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
@@ -73,7 +72,7 @@ class FlushJob {
|
||||
EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const SeqnoToTimeMapping& seq_time_mapping,
|
||||
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "",
|
||||
std::string full_history_ts_low = "",
|
||||
BlobFileCompletionCallback* blob_callback = nullptr);
|
||||
@@ -157,7 +156,7 @@ class FlushJob {
|
||||
// this job. All memtables in this column family with an ID smaller than or
|
||||
// equal to max_memtable_id_ will be selected for flush.
|
||||
uint64_t max_memtable_id_;
|
||||
const FileOptions file_options_;
|
||||
FileOptions file_options_;
|
||||
VersionSet* versions_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
std::atomic<bool>* shutting_down_;
|
||||
@@ -210,10 +209,14 @@ class FlushJob {
|
||||
const std::string full_history_ts_low_;
|
||||
BlobFileCompletionCallback* blob_callback_;
|
||||
|
||||
// reference to the seqno_to_time_mapping_ in db_impl.h, not safe to read
|
||||
// without db mutex
|
||||
const SeqnoToTimeMapping& db_impl_seqno_to_time_mapping_;
|
||||
SeqnoToTimeMapping seqno_to_time_mapping_;
|
||||
// Shared copy of DB's seqno to time mapping stored in SuperVersion. The
|
||||
// ownership is shared with this FlushJob when it's created.
|
||||
// FlushJob accesses and ref counts immutable MemTables directly via
|
||||
// `MemTableListVersion` instead of ref `SuperVersion`, so we need to give
|
||||
// the flush job shared ownership of the mapping.
|
||||
// Note this is only installed when seqno to time recording feature is
|
||||
// enables, so it could be nullptr.
|
||||
std::shared_ptr<const SeqnoToTimeMapping> seqno_to_time_mapping_;
|
||||
|
||||
// Keeps track of the newest user-defined timestamp for this flush job if
|
||||
// `persist_user_defined_timestamps` flag is false.
|
||||
|
||||
+77
-1
@@ -25,6 +25,19 @@
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
std::string ValueWithWriteTime(std::string val, uint64_t write_time = 0) {
|
||||
std::string result = val;
|
||||
PutFixed64(&result, write_time);
|
||||
return result;
|
||||
}
|
||||
std::string ValueWithPreferredSeqno(std::string val,
|
||||
SequenceNumber preferred_seqno = 0) {
|
||||
std::string result = val;
|
||||
PutFixed64(&result, preferred_seqno);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// TODO(icanadi) Mock out everything else:
|
||||
// 1. VersionSet
|
||||
@@ -156,7 +169,7 @@ class FlushJobTestBase : public testing::Test {
|
||||
bool persist_udt_ = true;
|
||||
bool paranoid_file_checks_ = false;
|
||||
|
||||
SeqnoToTimeMapping empty_seqno_to_time_mapping_;
|
||||
std::shared_ptr<SeqnoToTimeMapping> empty_seqno_to_time_mapping_;
|
||||
};
|
||||
|
||||
class FlushJobTest : public FlushJobTestBase {
|
||||
@@ -608,6 +621,69 @@ TEST_F(FlushJobTest, GetRateLimiterPriorityForWrite) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(FlushJobTest, ReplaceTimedPutWriteTimeWithPreferredSeqno) {
|
||||
JobContext job_context(0);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
auto new_mem = cfd->ConstructNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
kMaxSequenceNumber);
|
||||
new_mem->Ref();
|
||||
std::shared_ptr<SeqnoToTimeMapping> seqno_to_time_mapping =
|
||||
std::make_shared<SeqnoToTimeMapping>();
|
||||
// Seqno: 10, 11, ... 20,
|
||||
// Time: ... 500 ... 600
|
||||
// GetProximalSeqnoBeforeTime(500) -> 10
|
||||
// GetProximalSeqnoBeforeTime(600) -> 20
|
||||
seqno_to_time_mapping->Append(10, 500);
|
||||
seqno_to_time_mapping->Append(20, 600);
|
||||
|
||||
ASSERT_OK(new_mem->Add(SequenceNumber(15), kTypeValuePreferredSeqno, "bar",
|
||||
ValueWithWriteTime("bval", 500),
|
||||
nullptr /*kv_prot_info*/));
|
||||
ASSERT_OK(new_mem->Add(SequenceNumber(18), kTypeValuePreferredSeqno, "foo",
|
||||
ValueWithWriteTime("fval", 600),
|
||||
nullptr /*kv_prot_info*/));
|
||||
|
||||
auto inserted_entries = mock::MakeMockFile();
|
||||
InternalKey smallest_internal_key("bar", SequenceNumber(15),
|
||||
kTypeValuePreferredSeqno);
|
||||
inserted_entries.push_back({smallest_internal_key.Encode().ToString(),
|
||||
ValueWithPreferredSeqno("bval", 10)});
|
||||
InternalKey largest_internal_key("foo", SequenceNumber(18), kTypeValue);
|
||||
inserted_entries.push_back(
|
||||
{largest_internal_key.Encode().ToString(), "fval"});
|
||||
autovector<MemTable*> to_delete;
|
||||
new_mem->ConstructFragmentedRangeTombstones();
|
||||
cfd->imm()->Add(new_mem, &to_delete);
|
||||
for (auto& m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relevant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||
Env::Priority::USER, nullptr /*IOTracer*/, seqno_to_time_mapping);
|
||||
|
||||
FileMetaData file_meta;
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run(nullptr, &file_meta));
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_EQ(smallest_internal_key.Encode().ToString(),
|
||||
file_meta.smallest.Encode().ToString());
|
||||
ASSERT_EQ(largest_internal_key.Encode().ToString(),
|
||||
file_meta.largest.Encode().ToString());
|
||||
mock_table_factory_->AssertSingleFile(inserted_entries);
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
// Test parameters:
|
||||
// param 0): paranoid file check
|
||||
// param 1): user-defined timestamp test mode
|
||||
|
||||
+17
-4
@@ -611,6 +611,11 @@ Slice ForwardIterator::key() const {
|
||||
return current_->key();
|
||||
}
|
||||
|
||||
uint64_t ForwardIterator::write_unix_time() const {
|
||||
assert(valid_);
|
||||
return current_->write_unix_time();
|
||||
}
|
||||
|
||||
Slice ForwardIterator::value() const {
|
||||
assert(valid_);
|
||||
return current_->value();
|
||||
@@ -704,8 +709,12 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
}
|
||||
ReadRangeDelAggregator range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
mutable_iter_ = sv_->mem->NewIterator(read_options_, &arena_);
|
||||
sv_->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping =
|
||||
sv_->GetSeqnoToTimeMapping();
|
||||
mutable_iter_ =
|
||||
sv_->mem->NewIterator(read_options_, seqno_to_time_mapping, &arena_);
|
||||
sv_->imm->AddIterators(read_options_, seqno_to_time_mapping, &imm_iters_,
|
||||
&arena_);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
std::unique_ptr<FragmentedRangeTombstoneIterator> range_del_iter(
|
||||
sv_->mem->NewRangeTombstoneIterator(
|
||||
@@ -769,8 +778,12 @@ void ForwardIterator::RenewIterators() {
|
||||
}
|
||||
imm_iters_.clear();
|
||||
|
||||
mutable_iter_ = svnew->mem->NewIterator(read_options_, &arena_);
|
||||
svnew->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
UnownedPtr<const SeqnoToTimeMapping> seqno_to_time_mapping =
|
||||
svnew->GetSeqnoToTimeMapping();
|
||||
mutable_iter_ =
|
||||
svnew->mem->NewIterator(read_options_, seqno_to_time_mapping, &arena_);
|
||||
svnew->imm->AddIterators(read_options_, seqno_to_time_mapping, &imm_iters_,
|
||||
&arena_);
|
||||
ReadRangeDelAggregator range_del_agg(&cfd_->internal_comparator(),
|
||||
kMaxSequenceNumber /* upper_bound */);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
|
||||
@@ -76,6 +76,7 @@ class ForwardIterator : public InternalIterator {
|
||||
void Next() override;
|
||||
Slice key() const override;
|
||||
Slice value() const override;
|
||||
uint64_t write_unix_time() const override;
|
||||
Status status() const override;
|
||||
bool PrepareValue() override;
|
||||
Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "logging/logging.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "table/sst_file_writer_collectors.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
@@ -126,9 +125,10 @@ Status ImportColumnFamilyJob::Prepare(uint64_t next_file_number,
|
||||
}
|
||||
}
|
||||
if (!hardlink_files) {
|
||||
status =
|
||||
CopyFile(fs_.get(), path_outside_db, path_inside_db, 0,
|
||||
db_options_.use_fsync, io_tracer_, Temperature::kUnknown);
|
||||
// FIXME: temperature handling (like ExternalSstFileIngestionJob)
|
||||
status = CopyFile(fs_.get(), path_outside_db, Temperature::kUnknown,
|
||||
path_inside_db, Temperature::kUnknown, 0,
|
||||
db_options_.use_fsync, io_tracer_);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
|
||||
@@ -35,6 +35,12 @@ struct SuperVersionContext {
|
||||
std::unique_ptr<SuperVersion>
|
||||
new_superversion; // if nullptr no new superversion
|
||||
|
||||
// If not nullptr, a new seqno to time mapping is available to be installed.
|
||||
// Otherwise, make a shared copy of the one in the existing SuperVersion and
|
||||
// carry it over to the new SuperVersion. This is moved to the SuperVersion
|
||||
// during installation.
|
||||
std::shared_ptr<const SeqnoToTimeMapping> new_seqno_to_time_mapping{nullptr};
|
||||
|
||||
explicit SuperVersionContext(bool create_superversion = false)
|
||||
: new_superversion(create_superversion ? new SuperVersion() : nullptr) {}
|
||||
|
||||
|
||||
+13
-5
@@ -18,8 +18,7 @@
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace log {
|
||||
namespace ROCKSDB_NAMESPACE::log {
|
||||
|
||||
Reader::Reporter::~Reporter() = default;
|
||||
|
||||
@@ -44,7 +43,7 @@ Reader::Reader(std::shared_ptr<Logger> info_log,
|
||||
compression_type_record_read_(false),
|
||||
uncompress_(nullptr),
|
||||
hash_state_(nullptr),
|
||||
uncompress_hash_state_(nullptr){};
|
||||
uncompress_hash_state_(nullptr){}
|
||||
|
||||
Reader::~Reader() {
|
||||
delete[] backing_store_;
|
||||
@@ -259,6 +258,10 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch,
|
||||
// writing a physical record but before completing the next; don't
|
||||
// treat it as a corruption, just ignore the entire logical record.
|
||||
scratch->clear();
|
||||
} else {
|
||||
if (wal_recovery_mode == WALRecoveryMode::kPointInTimeRecovery) {
|
||||
ReportOldLogRecord(scratch->size());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -406,6 +409,12 @@ void Reader::ReportDrop(size_t bytes, const Status& reason) {
|
||||
}
|
||||
}
|
||||
|
||||
void Reader::ReportOldLogRecord(size_t bytes) {
|
||||
if (reporter_ != nullptr) {
|
||||
reporter_->OldLogRecord(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
bool Reader::ReadMore(size_t* drop_size, int* error) {
|
||||
if (!eof_ && !read_error_) {
|
||||
// Last read was a full read, so this is a trailer to skip
|
||||
@@ -937,5 +946,4 @@ bool FragmentBufferedReader::TryReadFragment(
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
} // namespace ROCKSDB_NAMESPACE::log
|
||||
@@ -45,6 +45,8 @@ class Reader {
|
||||
// Some corruption was detected. "size" is the approximate number
|
||||
// of bytes dropped due to the corruption.
|
||||
virtual void Corruption(size_t bytes, const Status& status) = 0;
|
||||
|
||||
virtual void OldLogRecord(size_t /*bytes*/) {}
|
||||
};
|
||||
|
||||
// Create a reader that will return log records from "*file".
|
||||
@@ -202,6 +204,7 @@ 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 ReportOldLogRecord(size_t bytes);
|
||||
|
||||
void InitCompression(const CompressionTypeRecord& compression_record);
|
||||
|
||||
|
||||
+2
-4
@@ -19,8 +19,7 @@
|
||||
#include "util/random.h"
|
||||
#include "utilities/memory_allocators.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace log {
|
||||
namespace ROCKSDB_NAMESPACE::log {
|
||||
|
||||
// Construct a string of the specified length made out of the supplied
|
||||
// partial string.
|
||||
@@ -1206,8 +1205,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
kBlockSize * 2),
|
||||
::testing::Values(CompressionType::kZSTD)));
|
||||
|
||||
} // namespace log
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
} // namespace ROCKSDB_NAMESPACE::log
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
|
||||
Loaded 100 of 357 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user