mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ffb20e64d | ||
|
|
629605d645 | ||
|
|
df543460d5 | ||
|
|
6628ff12d6 | ||
|
|
86634885eb | ||
|
|
64b0439bc1 | ||
|
|
2f712235ab | ||
|
|
aeda36e925 | ||
|
|
98d0f6ec08 | ||
|
|
8a7b9888d4 | ||
|
|
977aae53d2 | ||
|
|
05c3b8ecac | ||
|
|
ff0d618c7f | ||
|
|
846db9d7b1 | ||
|
|
662a1c99f6 | ||
|
|
749b179c04 | ||
|
|
b1b6f87fbe | ||
|
|
bc0db33483 | ||
|
|
c3c84b3397 | ||
|
|
854eb76a8c | ||
|
|
1a7c741977 | ||
|
|
f74526341d | ||
|
|
baf37a0e81 | ||
|
|
1f410ff95f | ||
|
|
df082c8d1d | ||
|
|
c53d604f41 | ||
|
|
25b08eb438 | ||
|
|
f4e304f987 | ||
|
|
15053f3ab4 | ||
|
|
bfdc91017c | ||
|
|
5187ac2af3 | ||
|
|
f7aa70a72f | ||
|
|
5732cf50e1 | ||
|
|
44524cf5da | ||
|
|
b14c0b0602 | ||
|
|
94c247bff8 | ||
|
|
ff1cc8a63e | ||
|
|
ca50ccc71a | ||
|
|
fbd2f563bb | ||
|
|
fb5748decf | ||
|
|
05a1d52e77 | ||
|
|
7521478b43 | ||
|
|
98c6d7fd80 | ||
|
|
2926e0718c | ||
|
|
022d89549d | ||
|
|
b3edb87341 | ||
|
|
17d5200504 | ||
|
|
1da9ac2363 | ||
|
|
66499780b2 | ||
|
|
b421a8c21b | ||
|
|
bc04ec85db | ||
|
|
253bc91953 | ||
|
|
9f774baaa8 | ||
|
|
fa878a0107 | ||
|
|
70bf5ef093 | ||
|
|
15e8a843d9 | ||
|
|
cac3240cbf | ||
|
|
e178f9e477 | ||
|
|
6041e50eba | ||
|
|
a2a90f8998 | ||
|
|
7c67aee4a0 | ||
|
|
05fcacdb42 | ||
|
|
77dda0d9d8 | ||
|
|
2b2994c8db | ||
|
|
3093d98c78 | ||
|
|
2e8cc98ab2 | ||
|
|
ddfcbea3e1 | ||
|
|
633c738a98 | ||
|
|
4dafa5b220 | ||
|
|
4aa52d89cf | ||
|
|
687a2a0d9a | ||
|
|
7a9b264f36 | ||
|
|
71ca9a1dcd | ||
|
|
e95cc1217d | ||
|
|
9f7877f246 | ||
|
|
9f1ce6d804 | ||
|
|
68a9cd21f2 | ||
|
|
87bc929db3 | ||
|
|
56ca9e3106 | ||
|
|
8848ec92dd | ||
|
|
e1c7209beb | ||
|
|
fcc358baf2 | ||
|
|
3e7fc88167 | ||
|
|
de1dd4ca19 | ||
|
|
23f4e9ad63 | ||
|
|
81aeb15988 | ||
|
|
d1ae7f6c41 | ||
|
|
dcc6fc99f9 | ||
|
|
e8710303d9 | ||
|
|
17bc27741f | ||
|
|
93e0715fad | ||
|
|
28bf7ba77d | ||
|
|
bf9e864235 | ||
|
|
68cc429be2 | ||
|
|
53e0b2fe6f | ||
|
|
11ebddb1d4 | ||
|
|
ffb5f1f445 | ||
|
|
5b945adf60 | ||
|
|
6eb3770b46 | ||
|
|
509116c53b | ||
|
|
39f5846ec7 | ||
|
|
4067acabca | ||
|
|
8d8eb0e77e | ||
|
|
7263f51d50 | ||
|
|
50046869a4 | ||
|
|
e110d713e0 | ||
|
|
586d78b31e | ||
|
|
206fdea3d9 | ||
|
|
5fc57eec2b | ||
|
|
fb636f2498 | ||
|
|
2084cdf237 | ||
|
|
113f3250f1 | ||
|
|
47235dda9e | ||
|
|
8827cd0618 | ||
|
|
7531cbda91 | ||
|
|
459969e993 | ||
|
|
f4a02f2c52 | ||
|
|
6ba4717f35 | ||
|
|
8f763bdeab | ||
|
|
e1d1c50317 | ||
|
|
736b3c4909 | ||
|
|
a5909f8864 | ||
|
|
a11f1e12ca | ||
|
|
c81d58016b | ||
|
|
50b33ebb1b | ||
|
|
a475e9f746 | ||
|
|
03a892a9fb | ||
|
|
41a7fbf758 | ||
|
|
e0e318f370 | ||
|
|
76a40286b0 | ||
|
|
925d8252e5 | ||
|
|
d3ed796855 | ||
|
|
62fc15f009 | ||
|
|
40d69b59ad | ||
|
|
3622cfa34a | ||
|
|
46dbcfd799 | ||
|
|
2898d9f593 |
No files matched your search
@@ -33,9 +33,7 @@ jobs:
|
||||
run: pip install argparse
|
||||
|
||||
- name: Download clang-format-diff.py
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
@@ -95,3 +95,5 @@ fuzz/crash-*
|
||||
|
||||
cmake-build-*
|
||||
third-party/folly/
|
||||
.cache
|
||||
*.sublime-*
|
||||
+23
-53
@@ -253,33 +253,10 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
endif(HAS_LOONGARCH64)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "loongarch64")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX "force building with AVX, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX2 "force building with AVX2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
add_definitions(-DROCKSDB_PORTABLE)
|
||||
|
||||
# MSVC does not need a separate compiler flag to enable SSE4.2; if nmmintrin.h
|
||||
# is available, it is available by default.
|
||||
if(FORCE_SSE42 AND NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2 -mpclmul")
|
||||
endif()
|
||||
if(MSVC)
|
||||
if(FORCE_AVX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX")
|
||||
endif()
|
||||
# MSVC automatically enables BMI / lzcnt with AVX2.
|
||||
if(FORCE_AVX2)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
endif()
|
||||
else()
|
||||
if(FORCE_AVX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx")
|
||||
endif()
|
||||
if(FORCE_AVX2)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2 -mbmi -mlzcnt")
|
||||
endif()
|
||||
set(PORTABLE 0 CACHE STRING "Minimum CPU arch to support, or 0 = current CPU, 1 = baseline CPU")
|
||||
if(PORTABLE STREQUAL 1)
|
||||
# Usually nothing to do; compiler default is typically the most general
|
||||
if(NOT MSVC)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=z196")
|
||||
endif()
|
||||
@@ -287,10 +264,21 @@ if(PORTABLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=loongarch64")
|
||||
endif()
|
||||
endif()
|
||||
elseif(PORTABLE MATCHES [^0]+)
|
||||
# Name of a CPU arch spec or feature set to require
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:${PORTABLE}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${PORTABLE}")
|
||||
endif()
|
||||
else()
|
||||
if(MSVC)
|
||||
# NOTE: No auto-detection of current CPU, but instead assume some useful
|
||||
# level of optimization is supported
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
# Require instruction set from current CPU (with some legacy or opt-out
|
||||
# exceptions)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x" AND NOT HAS_S390X_MARCH_NATIVE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=z196")
|
||||
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64" AND NOT HAS_ARMV8_CRC)
|
||||
@@ -305,25 +293,6 @@ if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
volatile uint32_t x = _mm_crc32_u32(0, 0);
|
||||
const auto a = _mm_set_epi64x(0, 0);
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
add_definitions(-DHAVE_PCLMUL)
|
||||
elseif(FORCE_SSE42)
|
||||
message(FATAL_ERROR "FORCE_SSE42=ON but unable to compile with SSE4.2 enabled")
|
||||
endif()
|
||||
|
||||
# Check if -latomic is required or not
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "--std=c++17")
|
||||
@@ -578,7 +547,7 @@ if(HAVE_SCHED_GETCPU)
|
||||
add_definitions(-DROCKSDB_SCHED_GETCPU_PRESENT)
|
||||
endif()
|
||||
|
||||
check_cxx_symbol_exists(getauxval auvx.h HAVE_AUXV_GETAUXVAL)
|
||||
check_cxx_symbol_exists(getauxval "sys/auxv.h" HAVE_AUXV_GETAUXVAL)
|
||||
if(HAVE_AUXV_GETAUXVAL)
|
||||
add_definitions(-DROCKSDB_AUXV_GETAUXVAL_PRESENT)
|
||||
endif()
|
||||
@@ -896,6 +865,8 @@ set(SOURCES
|
||||
util/string_util.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
util/udt_util.cc
|
||||
util/write_batch_util.cc
|
||||
util/xxhash.cc
|
||||
utilities/agg_merge/agg_merge.cc
|
||||
utilities/backup/backup_engine.cc
|
||||
@@ -1010,12 +981,6 @@ if ( ROCKSDB_PLUGINS )
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(HAVE_SSE42 AND NOT MSVC)
|
||||
set_source_files_properties(
|
||||
util/crc32c.cc
|
||||
PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
list(APPEND SOURCES
|
||||
util/crc32c_ppc.c
|
||||
@@ -1327,6 +1292,7 @@ if(WITH_TESTS)
|
||||
db/db_bloom_filter_test.cc
|
||||
db/db_compaction_filter_test.cc
|
||||
db/db_compaction_test.cc
|
||||
db/db_clip_test.cc
|
||||
db/db_dynamic_level_test.cc
|
||||
db/db_encryption_test.cc
|
||||
db/db_flush_test.cc
|
||||
@@ -1457,6 +1423,7 @@ if(WITH_TESTS)
|
||||
util/timer_test.cc
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
util/udt_util_test.cc
|
||||
util/work_queue_test.cc
|
||||
utilities/agg_merge/agg_merge_test.cc
|
||||
utilities/backup/backup_engine_test.cc
|
||||
@@ -1625,3 +1592,6 @@ option(WITH_BENCHMARK "build benchmark tests" OFF)
|
||||
if(WITH_BENCHMARK)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/microbench/)
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||
+61
-4
@@ -1,5 +1,64 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 8.4.0 (06/26/2023)
|
||||
### New Features
|
||||
* Add FSReadRequest::fs_scratch which is a data buffer allocated and provided by underlying FileSystem to RocksDB during reads, when FS wants to provide its own buffer with data instead of using RocksDB provided FSReadRequest::scratch. This can help in cpu optimization by avoiding copy from file system's buffer to RocksDB buffer. More details on how to use/enable it in file_system.h. Right now its supported only for MultiReads(async + sync) with non direct io.
|
||||
* Start logging non-zero user-defined timestamp sizes in WAL to signal user key format in subsequent records and use it during recovery. This change will break recovery from WAL files written by early versions that contain user-defined timestamps. The workaround is to ensure there are no WAL files to recover (i.e. by flushing before close) before upgrade.
|
||||
* Added new property "rocksdb.obsolete-sst-files-size-property" that reports the size of SST files that have become obsolete but have not yet been deleted or scheduled for deletion
|
||||
* Start to record the value of the flag `AdvancedColumnFamilyOptions.persist_user_defined_timestamps` in the Manifest and table properties for a SST file when it is created. And use the recorded flag when creating a table reader for the SST file. This flag is only explicitly record if it's false.
|
||||
* Add a new option OptimisticTransactionDBOptions::shared_lock_buckets that enables sharing mutexes for validating transactions between DB instances, for better balancing memory efficiency and validation contention across DB instances. Different column families and DBs also now use different hash seeds in this validation, so that the same set of key names will not contend across DBs or column families.
|
||||
* Add a new ticker `rocksdb.files.marked.trash.deleted` to track the number of trash files deleted by background thread from the trash queue.
|
||||
* Add an API NewTieredVolatileCache() in include/rocksdb/cache.h to allocate an instance of a block cache with a primary block cache tier and a compressed secondary cache tier. A cache of this type distributes memory reservations against the block cache, such as WriteBufferManager, table reader memory etc., proportionally across both the primary and compressed secondary cache.
|
||||
* Add `WaitForCompact()` to wait for all flush and compactions jobs to finish. Jobs to wait include the unscheduled (queued, but not scheduled yet).
|
||||
* Add `WriteBatch::Release()` that releases the batch's serialized data to the caller.
|
||||
|
||||
### Public API Changes
|
||||
* Add C API `rocksdb_options_add_compact_on_deletion_collector_factory_del_ratio`.
|
||||
* change the FileSystem::use_async_io() API to SupportedOps API in order to extend it to various operations supported by underlying FileSystem. Right now it contains FSSupportedOps::kAsyncIO and FSSupportedOps::kFSBuffer. More details about FSSupportedOps in filesystem.h
|
||||
* Add new tickers: `rocksdb.error.handler.bg.error.count`, `rocksdb.error.handler.bg.io.error.count`, `rocksdb.error.handler.bg.retryable.io.error.count` to replace the misspelled ones: `rocksdb.error.handler.bg.errro.count`, `rocksdb.error.handler.bg.io.errro.count`, `rocksdb.error.handler.bg.retryable.io.errro.count` ('error' instead of 'errro'). Users should switch to use the new tickers before 9.0 release as the misspelled old tickers will be completely removed then.
|
||||
* Overload the API CreateColumnFamilyWithImport() to support creating ColumnFamily by importing multiple ColumnFamilies It requires that CFs should not overlap in user key range.
|
||||
|
||||
### Behavior Changes
|
||||
* Change the default value for option `level_compaction_dynamic_level_bytes` to true. This affects users who use leveled compaction and do not set this option explicitly. These users may see additional background compactions following DB open. These compactions help to shape the LSM according to `level_compaction_dynamic_level_bytes` such that the size of each level Ln is approximately size of Ln-1 * `max_bytes_for_level_multiplier`. Turning on this option has other benefits too: see more detail in wiki: https://github.com/facebook/rocksdb/wiki/Leveled-Compaction#option-level_compaction_dynamic_level_bytes-and-levels-target-size and in option comment in advanced_options.h (#11525).
|
||||
* For Leveled Compaction users, `CompactRange()` will now always try to compact to the last non-empty level. (#11468)
|
||||
For Leveled Compaction users, `CompactRange()` with `bottommost_level_compaction = BottommostLevelCompaction::kIfHaveCompactionFilter` will behave similar to `kForceOptimized` in that it will skip files created during this manual compaction when compacting files in the bottommost level. (#11468)
|
||||
* RocksDB will try to drop range tombstones during non-bottommost compaction when it is safe to do so. (#11459)
|
||||
* When a DB is openend with `allow_ingest_behind=true` (currently only Universal compaction is supported), files in the last level, i.e. the ingested files, will not be included in any compaction. (#11489)
|
||||
* Statistics `rocksdb.sst.read.micros` scope is expanded to all SST reads except for file ingestion and column family import (some compaction reads were previously excluded).
|
||||
|
||||
### Bug Fixes
|
||||
* Reduced cases of illegally using Env::Default() during static destruction by never destroying the internal PosixEnv itself (except for builds checking for memory leaks). (#11538)
|
||||
* Fix extra prefetching during seek in async_io when BlockBasedTableOptions.num_file_reads_for_auto_readahead is 1 leading to extra reads than required.
|
||||
* Fix a bug where compactions that are qualified to be run as 2 subcompactions were only run as one subcompaction.
|
||||
* Fix a use-after-move bug in block.cc.
|
||||
|
||||
## 8.3.0 (05/19/2023)
|
||||
### New Features
|
||||
* Introduced a new option `block_protection_bytes_per_key`, which can be used to enable per key-value integrity protection for in-memory blocks in block cache (#11287).
|
||||
* Added `JemallocAllocatorOptions::num_arenas`. Setting `num_arenas > 1` may mitigate mutex contention in the allocator, particularly in scenarios where block allocations commonly bypass jemalloc tcache.
|
||||
* Improve the operational safety of publishing a DB or SST files to many hosts by using different block cache hash seeds on different hosts. The exact behavior is controlled by new option `ShardedCacheOptions::hash_seed`, which also documents the solved problem in more detail.
|
||||
* Introduced a new option `CompactionOptionsFIFO::file_temperature_age_thresholds` that allows FIFO compaction to compact files to different temperatures based on key age (#11428).
|
||||
* Added a new ticker stat to count how many times RocksDB detected a corruption while verifying a block checksum: `BLOCK_CHECKSUM_MISMATCH_COUNT`.
|
||||
* New statistics `rocksdb.file.read.db.open.micros` that measures read time of block-based SST tables or blob files during db open.
|
||||
* New statistics tickers for various iterator seek behaviors and relevant filtering, as \*`_LEVEL_SEEK_`\*. (#11460)
|
||||
|
||||
### Public API Changes
|
||||
* EXPERIMENTAL: Add new API `DB::ClipColumnFamily` to clip the key in CF to a certain range. It will physically deletes all keys outside the range including tombstones.
|
||||
* Add `MakeSharedCache()` construction functions to various cache Options objects, and deprecated the `NewWhateverCache()` functions with long parameter lists.
|
||||
* Changed the meaning of various Bloom filter stats (prefix vs. whole key), with iterator-related filtering only being tracked in the new \*`_LEVEL_SEEK_`\*. stats. (#11460)
|
||||
|
||||
### Behavior changes
|
||||
* For x86, CPU features are no longer detected at runtime nor in build scripts, but in source code using common preprocessor defines. This will likely unlock some small performance improvements on some newer hardware, but could hurt performance of the kCRC32c checksum, which is no longer the default, on some "portable" builds. See PR #11419 for details.
|
||||
|
||||
### Bug Fixes
|
||||
* Delete an empty WAL file on DB open if the log number is less than the min log number to keep
|
||||
* Delete temp OPTIONS file on DB open if there is a failure to write it out or rename it
|
||||
|
||||
### Performance Improvements
|
||||
* Improved the I/O efficiency of prefetching SST metadata by recording more information in the DB manifest. Opening files written with previous versions will still rely on heuristics for how much to prefetch (#11406).
|
||||
|
||||
## 8.2.0 (04/24/2023)
|
||||
### Public API Changes
|
||||
* `SstFileWriter::DeleteRange()` now returns `Status::InvalidArgument` if the range's end key comes before its start key according to the user comparator. Previously the behavior was undefined.
|
||||
* Add `multi_get_for_update` to C API.
|
||||
@@ -14,6 +73,7 @@
|
||||
|
||||
### Bug Fixes
|
||||
* In the DB::VerifyFileChecksums API, ensure that file system reads of SST files are equal to the readahead_size in ReadOptions, if specified. Previously, each read was 2x the readahead_size.
|
||||
* In block cache tracing, fixed some cases of bad hit/miss information (and more) with MultiGet.
|
||||
|
||||
### New Features
|
||||
* Add experimental `PerfContext` counters `iter_{next|prev|seek}_count` for db iterator, each counting the times of corresponding API being called.
|
||||
@@ -21,9 +81,6 @@
|
||||
* Added statistics tickers BYTES_COMPRESSED_FROM, BYTES_COMPRESSED_TO, BYTES_COMPRESSION_BYPASSED, BYTES_COMPRESSION_REJECTED, NUMBER_BLOCK_COMPRESSION_BYPASSED, and NUMBER_BLOCK_COMPRESSION_REJECTED. Disabled/deprecated histograms BYTES_COMPRESSED and BYTES_DECOMPRESSED, and ticker NUMBER_BLOCK_NOT_COMPRESSED. The new tickers offer more inight into compression ratios, rejected vs. disabled compression, etc. (#11388)
|
||||
* New statistics `rocksdb.file.read.{flush|compaction}.micros` that measure read time of block-based SST tables or blob files during flush or compaction.
|
||||
|
||||
### Bug Fixes
|
||||
* In block cache tracing, fixed some cases of bad hit/miss information (and more) with MultiGet.
|
||||
|
||||
## 8.1.0 (03/18/2023)
|
||||
### Behavior changes
|
||||
* Compaction output file cutting logic now considers range tombstone start keys. For example, SST partitioner now may receive ParitionRequest for range tombstone start keys.
|
||||
|
||||
+9
-6
@@ -20,12 +20,15 @@ There are few options when compiling RocksDB:
|
||||
depend on gflags. You will need to have gflags installed to run `make all`. This will compile RocksDB in debug mode. Don't
|
||||
use binaries compiled by `make all` in production.
|
||||
|
||||
* By default the binary we produce is optimized for the platform you're compiling on
|
||||
(`-march=native` or the equivalent). SSE4.2 will thus be enabled automatically if your
|
||||
CPU supports it. To print a warning if your CPU does not support SSE4.2, build with
|
||||
`USE_SSE=1 make static_lib` or, if using CMake, `cmake -DFORCE_SSE42=ON`. If you want
|
||||
to build a portable binary, add `PORTABLE=1` before your make commands, like this:
|
||||
`PORTABLE=1 make static_lib`.
|
||||
* By default the binary we produce is optimized for the CPU you're compiling on
|
||||
(`-march=native` or the equivalent). To build a binary compatible with the most
|
||||
general architecture supported by your CPU and compiler, set `PORTABLE=1` for
|
||||
the build, but performance will suffer as many operations benefit from newer
|
||||
and wider instructions. In addition to `PORTABLE=0` (default) and `PORTABLE=1`,
|
||||
it can be set to an architecture name recognized by your compiler. For example,
|
||||
on 64-bit x86, a reasonable compromise is `PORTABLE=haswell` which supports
|
||||
many or most of the available optimizations while still being compatible with
|
||||
most processors made since roughly 2013.
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@@ -337,8 +337,8 @@ ifneq ($(MACHINE), arm64)
|
||||
# linking with jemalloc (as it won't be arm64-compatible) and remove some other options
|
||||
# set during platform detection
|
||||
DISABLE_JEMALLOC=1
|
||||
PLATFORM_CCFLAGS := $(filter-out -march=native -DHAVE_SSE42 -DHAVE_AVX2, $(PLATFORM_CCFLAGS))
|
||||
PLATFORM_CXXFLAGS := $(filter-out -march=native -DHAVE_SSE42 -DHAVE_AVX2, $(PLATFORM_CXXFLAGS))
|
||||
PLATFORM_CCFLAGS := $(filter-out -march=native, $(PLATFORM_CCFLAGS))
|
||||
PLATFORM_CXXFLAGS := $(filter-out -march=native, $(PLATFORM_CXXFLAGS))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -1417,6 +1417,9 @@ thread_local_test: $(OBJ_DIR)/util/thread_local_test.o $(TEST_LIBRARY) $(LIBRARY
|
||||
work_queue_test: $(OBJ_DIR)/util/work_queue_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
udt_util_test: $(OBJ_DIR)/util/udt_util_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
corruption_test: $(OBJ_DIR)/db/corruption_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1480,6 +1483,9 @@ db_compaction_filter_test: $(OBJ_DIR)/db/db_compaction_filter_test.o $(TEST_LIBR
|
||||
db_compaction_test: $(OBJ_DIR)/db/db_compaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_clip_test: $(OBJ_DIR)/db/db_clip_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_dynamic_level_test: $(OBJ_DIR)/db/db_dynamic_level_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2435,6 +2441,8 @@ checkout_folly:
|
||||
@# NOTE: this hack is required for gcc in some cases
|
||||
perl -pi -e 's/(__has_include.<experimental.memory_resource>.)/__cpp_rtti && $$1/' third-party/folly/folly/memory/MemoryResource.h
|
||||
|
||||
CXX_M_FLAGS = $(filter -m%, $(CXXFLAGS))
|
||||
|
||||
build_folly:
|
||||
FOLLY_INST_PATH=`cd third-party/folly; $(PYTHON) build/fbcode_builder/getdeps.py show-inst-dir`; \
|
||||
if [ "$$FOLLY_INST_PATH" ]; then \
|
||||
@@ -2445,8 +2453,8 @@ build_folly:
|
||||
fi
|
||||
# Restore the original version of Invoke.h with boost dependency
|
||||
cd third-party/folly && ${GIT_COMMAND} checkout folly/functional/Invoke.h
|
||||
cd third-party/folly && MAYBE_AVX2=`echo $(CXXFLAGS) | grep -o -- -DHAVE_AVX2 | sed 's/-DHAVE_AVX2/-mavx2/g' || true` && \
|
||||
CXXFLAGS=" $$MAYBE_AVX2 -DHAVE_CXX11_ATOMIC " $(PYTHON) build/fbcode_builder/getdeps.py build --no-tests
|
||||
cd third-party/folly && \
|
||||
CXXFLAGS=" $(CXX_M_FLAGS) -DHAVE_CXX11_ATOMIC " $(PYTHON) build/fbcode_builder/getdeps.py build --no-tests
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build size testing
|
||||
|
||||
@@ -5,3 +5,4 @@ This is the list of all known third-party plugins for RocksDB. If something is m
|
||||
* [ZenFS](https://github.com/westerndigitalcorporation/zenfs): a file system for zoned block devices
|
||||
* [RADOS](https://github.com/riversand963/rocksdb-rados-env): an Env used for interacting with RADOS. Migrated from RocksDB main repo.
|
||||
* [PMEM](https://github.com/pmem/pmem-rocksdb-plugin): a collection of plugins to enable Persistent Memory on RocksDB.
|
||||
* [IPPCP](https://github.com/intel/ippcp-plugin-rocksdb): a plugin to enable encryption on RocksDB based on Intel optimized open source IPP-Crypto library.
|
||||
@@ -264,6 +264,8 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"util/string_util.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/udt_util.cc",
|
||||
"util/write_batch_util.cc",
|
||||
"util/xxhash.cc",
|
||||
"utilities/agg_merge/agg_merge.cc",
|
||||
"utilities/backup/backup_engine.cc",
|
||||
@@ -354,351 +356,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
], headers=None, link_whole=False, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"cache/cache.cc",
|
||||
"cache/cache_entry_roles.cc",
|
||||
"cache/cache_helpers.cc",
|
||||
"cache/cache_key.cc",
|
||||
"cache/cache_reservation_manager.cc",
|
||||
"cache/charged_cache.cc",
|
||||
"cache/clock_cache.cc",
|
||||
"cache/compressed_secondary_cache.cc",
|
||||
"cache/lru_cache.cc",
|
||||
"cache/secondary_cache.cc",
|
||||
"cache/secondary_cache_adapter.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_contents.cc",
|
||||
"db/blob/blob_fetcher.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_builder.cc",
|
||||
"db/blob/blob_file_cache.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/blob/blob_file_reader.cc",
|
||||
"db/blob/blob_garbage_meter.cc",
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_sequential_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
"db/blob/blob_source.cc",
|
||||
"db/blob/prefetch_buffer_collection.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
"db/column_family.cc",
|
||||
"db/compaction/compaction.cc",
|
||||
"db/compaction/compaction_iterator.cc",
|
||||
"db/compaction/compaction_job.cc",
|
||||
"db/compaction/compaction_outputs.cc",
|
||||
"db/compaction/compaction_picker.cc",
|
||||
"db/compaction/compaction_picker_fifo.cc",
|
||||
"db/compaction/compaction_picker_level.cc",
|
||||
"db/compaction/compaction_picker_universal.cc",
|
||||
"db/compaction/compaction_service_job.cc",
|
||||
"db/compaction/compaction_state.cc",
|
||||
"db/compaction/sst_partitioner.cc",
|
||||
"db/compaction/subcompaction_state.cc",
|
||||
"db/convenience.cc",
|
||||
"db/db_filesnapshot.cc",
|
||||
"db/db_impl/compacted_db_impl.cc",
|
||||
"db/db_impl/db_impl.cc",
|
||||
"db/db_impl/db_impl_compaction_flush.cc",
|
||||
"db/db_impl/db_impl_debug.cc",
|
||||
"db/db_impl/db_impl_experimental.cc",
|
||||
"db/db_impl/db_impl_files.cc",
|
||||
"db/db_impl/db_impl_open.cc",
|
||||
"db/db_impl/db_impl_readonly.cc",
|
||||
"db/db_impl/db_impl_secondary.cc",
|
||||
"db/db_impl/db_impl_write.cc",
|
||||
"db/db_info_dumper.cc",
|
||||
"db/db_iter.cc",
|
||||
"db/dbformat.cc",
|
||||
"db/error_handler.cc",
|
||||
"db/event_helpers.cc",
|
||||
"db/experimental.cc",
|
||||
"db/external_sst_file_ingestion_job.cc",
|
||||
"db/file_indexer.cc",
|
||||
"db/flush_job.cc",
|
||||
"db/flush_scheduler.cc",
|
||||
"db/forward_iterator.cc",
|
||||
"db/import_column_family_job.cc",
|
||||
"db/internal_stats.cc",
|
||||
"db/log_reader.cc",
|
||||
"db/log_writer.cc",
|
||||
"db/logs_with_prep_tracker.cc",
|
||||
"db/malloc_stats.cc",
|
||||
"db/memtable.cc",
|
||||
"db/memtable_list.cc",
|
||||
"db/merge_helper.cc",
|
||||
"db/merge_operator.cc",
|
||||
"db/output_validator.cc",
|
||||
"db/periodic_task_scheduler.cc",
|
||||
"db/range_del_aggregator.cc",
|
||||
"db/range_tombstone_fragmenter.cc",
|
||||
"db/repair.cc",
|
||||
"db/seqno_to_time_mapping.cc",
|
||||
"db/snapshot_impl.cc",
|
||||
"db/table_cache.cc",
|
||||
"db/table_properties_collector.cc",
|
||||
"db/transaction_log_impl.cc",
|
||||
"db/trim_history_scheduler.cc",
|
||||
"db/version_builder.cc",
|
||||
"db/version_edit.cc",
|
||||
"db/version_edit_handler.cc",
|
||||
"db/version_set.cc",
|
||||
"db/wal_edit.cc",
|
||||
"db/wal_manager.cc",
|
||||
"db/wide/wide_column_serialization.cc",
|
||||
"db/wide/wide_columns.cc",
|
||||
"db/write_batch.cc",
|
||||
"db/write_batch_base.cc",
|
||||
"db/write_controller.cc",
|
||||
"db/write_stall_stats.cc",
|
||||
"db/write_thread.cc",
|
||||
"env/composite_env.cc",
|
||||
"env/env.cc",
|
||||
"env/env_chroot.cc",
|
||||
"env/env_encryption.cc",
|
||||
"env/env_posix.cc",
|
||||
"env/file_system.cc",
|
||||
"env/file_system_tracer.cc",
|
||||
"env/fs_posix.cc",
|
||||
"env/fs_remap.cc",
|
||||
"env/io_posix.cc",
|
||||
"env/mock_env.cc",
|
||||
"env/unique_id_gen.cc",
|
||||
"file/delete_scheduler.cc",
|
||||
"file/file_prefetch_buffer.cc",
|
||||
"file/file_util.cc",
|
||||
"file/filename.cc",
|
||||
"file/line_file_reader.cc",
|
||||
"file/random_access_file_reader.cc",
|
||||
"file/read_write_util.cc",
|
||||
"file/readahead_raf.cc",
|
||||
"file/sequence_file_reader.cc",
|
||||
"file/sst_file_manager_impl.cc",
|
||||
"file/writable_file_writer.cc",
|
||||
"logging/auto_roll_logger.cc",
|
||||
"logging/event_logger.cc",
|
||||
"logging/log_buffer.cc",
|
||||
"memory/arena.cc",
|
||||
"memory/concurrent_arena.cc",
|
||||
"memory/jemalloc_nodump_allocator.cc",
|
||||
"memory/memkind_kmem_allocator.cc",
|
||||
"memory/memory_allocator.cc",
|
||||
"memtable/alloc_tracker.cc",
|
||||
"memtable/hash_linklist_rep.cc",
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
"memtable/skiplistrep.cc",
|
||||
"memtable/vectorrep.cc",
|
||||
"memtable/write_buffer_manager.cc",
|
||||
"monitoring/histogram.cc",
|
||||
"monitoring/histogram_windowing.cc",
|
||||
"monitoring/in_memory_stats_history.cc",
|
||||
"monitoring/instrumented_mutex.cc",
|
||||
"monitoring/iostats_context.cc",
|
||||
"monitoring/perf_context.cc",
|
||||
"monitoring/perf_level.cc",
|
||||
"monitoring/persistent_stats_history.cc",
|
||||
"monitoring/statistics.cc",
|
||||
"monitoring/thread_status_impl.cc",
|
||||
"monitoring/thread_status_updater.cc",
|
||||
"monitoring/thread_status_updater_debug.cc",
|
||||
"monitoring/thread_status_util.cc",
|
||||
"monitoring/thread_status_util_debug.cc",
|
||||
"options/cf_options.cc",
|
||||
"options/configurable.cc",
|
||||
"options/customizable.cc",
|
||||
"options/db_options.cc",
|
||||
"options/options.cc",
|
||||
"options/options_helper.cc",
|
||||
"options/options_parser.cc",
|
||||
"port/mmap.cc",
|
||||
"port/port_posix.cc",
|
||||
"port/stack_trace.cc",
|
||||
"port/win/env_default.cc",
|
||||
"port/win/env_win.cc",
|
||||
"port/win/io_win.cc",
|
||||
"port/win/port_win.cc",
|
||||
"port/win/win_logger.cc",
|
||||
"port/win/win_thread.cc",
|
||||
"table/adaptive/adaptive_table_factory.cc",
|
||||
"table/block_based/binary_search_index_reader.cc",
|
||||
"table/block_based/block.cc",
|
||||
"table/block_based/block_based_table_builder.cc",
|
||||
"table/block_based/block_based_table_factory.cc",
|
||||
"table/block_based/block_based_table_iterator.cc",
|
||||
"table/block_based/block_based_table_reader.cc",
|
||||
"table/block_based/block_builder.cc",
|
||||
"table/block_based/block_cache.cc",
|
||||
"table/block_based/block_prefetcher.cc",
|
||||
"table/block_based/block_prefix_index.cc",
|
||||
"table/block_based/data_block_footer.cc",
|
||||
"table/block_based/data_block_hash_index.cc",
|
||||
"table/block_based/filter_block_reader_common.cc",
|
||||
"table/block_based/filter_policy.cc",
|
||||
"table/block_based/flush_block_policy.cc",
|
||||
"table/block_based/full_filter_block.cc",
|
||||
"table/block_based/hash_index_reader.cc",
|
||||
"table/block_based/index_builder.cc",
|
||||
"table/block_based/index_reader_common.cc",
|
||||
"table/block_based/parsed_full_filter_block.cc",
|
||||
"table/block_based/partitioned_filter_block.cc",
|
||||
"table/block_based/partitioned_index_iterator.cc",
|
||||
"table/block_based/partitioned_index_reader.cc",
|
||||
"table/block_based/reader_common.cc",
|
||||
"table/block_based/uncompression_dict_reader.cc",
|
||||
"table/block_fetcher.cc",
|
||||
"table/compaction_merging_iterator.cc",
|
||||
"table/cuckoo/cuckoo_table_builder.cc",
|
||||
"table/cuckoo/cuckoo_table_factory.cc",
|
||||
"table/cuckoo/cuckoo_table_reader.cc",
|
||||
"table/format.cc",
|
||||
"table/get_context.cc",
|
||||
"table/iterator.cc",
|
||||
"table/merging_iterator.cc",
|
||||
"table/meta_blocks.cc",
|
||||
"table/persistent_cache_helper.cc",
|
||||
"table/plain/plain_table_bloom.cc",
|
||||
"table/plain/plain_table_builder.cc",
|
||||
"table/plain/plain_table_factory.cc",
|
||||
"table/plain/plain_table_index.cc",
|
||||
"table/plain/plain_table_key_coding.cc",
|
||||
"table/plain/plain_table_reader.cc",
|
||||
"table/sst_file_dumper.cc",
|
||||
"table/sst_file_reader.cc",
|
||||
"table/sst_file_writer.cc",
|
||||
"table/table_factory.cc",
|
||||
"table/table_properties.cc",
|
||||
"table/two_level_iterator.cc",
|
||||
"table/unique_id.cc",
|
||||
"test_util/sync_point.cc",
|
||||
"test_util/sync_point_impl.cc",
|
||||
"test_util/transaction_test_util.cc",
|
||||
"tools/dump/db_dump_tool.cc",
|
||||
"tools/io_tracer_parser_tool.cc",
|
||||
"tools/ldb_cmd.cc",
|
||||
"tools/ldb_tool.cc",
|
||||
"tools/sst_dump_tool.cc",
|
||||
"trace_replay/block_cache_tracer.cc",
|
||||
"trace_replay/io_tracer.cc",
|
||||
"trace_replay/trace_record.cc",
|
||||
"trace_replay/trace_record_handler.cc",
|
||||
"trace_replay/trace_record_result.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/async_file_reader.cc",
|
||||
"util/build_version.cc",
|
||||
"util/cleanable.cc",
|
||||
"util/coding.cc",
|
||||
"util/compaction_job_stats_impl.cc",
|
||||
"util/comparator.cc",
|
||||
"util/compression.cc",
|
||||
"util/compression_context_cache.cc",
|
||||
"util/concurrent_task_limiter_impl.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/crc32c_arm64.cc",
|
||||
"util/data_structure.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
"util/file_checksum_helper.cc",
|
||||
"util/hash.cc",
|
||||
"util/murmurhash.cc",
|
||||
"util/random.cc",
|
||||
"util/rate_limiter.cc",
|
||||
"util/ribbon_config.cc",
|
||||
"util/slice.cc",
|
||||
"util/status.cc",
|
||||
"util/stderr_logger.cc",
|
||||
"util/string_util.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/xxhash.cc",
|
||||
"utilities/agg_merge/agg_merge.cc",
|
||||
"utilities/backup/backup_engine.cc",
|
||||
"utilities/blob_db/blob_compaction_filter.cc",
|
||||
"utilities/blob_db/blob_db.cc",
|
||||
"utilities/blob_db/blob_db_impl.cc",
|
||||
"utilities/blob_db/blob_db_impl_filesnapshot.cc",
|
||||
"utilities/blob_db/blob_dump_tool.cc",
|
||||
"utilities/blob_db/blob_file.cc",
|
||||
"utilities/cache_dump_load.cc",
|
||||
"utilities/cache_dump_load_impl.cc",
|
||||
"utilities/cassandra/cassandra_compaction_filter.cc",
|
||||
"utilities/cassandra/format.cc",
|
||||
"utilities/cassandra/merge_operator.cc",
|
||||
"utilities/checkpoint/checkpoint_impl.cc",
|
||||
"utilities/compaction_filters.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
"utilities/counted_fs.cc",
|
||||
"utilities/debug.cc",
|
||||
"utilities/env_mirror.cc",
|
||||
"utilities/env_timed.cc",
|
||||
"utilities/fault_injection_env.cc",
|
||||
"utilities/fault_injection_fs.cc",
|
||||
"utilities/fault_injection_secondary_cache.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators.cc",
|
||||
"utilities/merge_operators/bytesxor.cc",
|
||||
"utilities/merge_operators/max.cc",
|
||||
"utilities/merge_operators/put.cc",
|
||||
"utilities/merge_operators/sortlist.cc",
|
||||
"utilities/merge_operators/string_append/stringappend.cc",
|
||||
"utilities/merge_operators/string_append/stringappend2.cc",
|
||||
"utilities/merge_operators/uint64add.cc",
|
||||
"utilities/object_registry.cc",
|
||||
"utilities/option_change_migration/option_change_migration.cc",
|
||||
"utilities/options/options_util.cc",
|
||||
"utilities/persistent_cache/block_cache_tier.cc",
|
||||
"utilities/persistent_cache/block_cache_tier_file.cc",
|
||||
"utilities/persistent_cache/block_cache_tier_metadata.cc",
|
||||
"utilities/persistent_cache/persistent_cache_tier.cc",
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/simulator_cache/cache_simulator.cc",
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/trace/replayer_impl.cc",
|
||||
"utilities/transactions/lock/lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_tracker.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/concurrent_tree.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/keyrange.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/lock_request.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/locktree.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/manager.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/range_buffer.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/treenode.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/txnid_set.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/wfg.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/standalone_port.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/util/dbt.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/util/memarena.cc",
|
||||
"utilities/transactions/lock/range/range_tree/range_tree_lock_manager.cc",
|
||||
"utilities/transactions/lock/range/range_tree/range_tree_lock_tracker.cc",
|
||||
"utilities/transactions/optimistic_transaction.cc",
|
||||
"utilities/transactions/optimistic_transaction_db_impl.cc",
|
||||
"utilities/transactions/pessimistic_transaction.cc",
|
||||
"utilities/transactions/pessimistic_transaction_db.cc",
|
||||
"utilities/transactions/snapshot_checker.cc",
|
||||
"utilities/transactions/transaction_base.cc",
|
||||
"utilities/transactions/transaction_db_mutex_impl.cc",
|
||||
"utilities/transactions/transaction_util.cc",
|
||||
"utilities/transactions/write_prepared_txn.cc",
|
||||
"utilities/transactions/write_prepared_txn_db.cc",
|
||||
"utilities/transactions/write_unprepared_txn.cc",
|
||||
"utilities/transactions/write_unprepared_txn_db.cc",
|
||||
"utilities/ttl/db_ttl_impl.cc",
|
||||
"utilities/wal_filter.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index.cc",
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
], deps=[
|
||||
"//folly/container:f14_hash",
|
||||
"//folly/experimental/coro:blocking_wait",
|
||||
"//folly/experimental/coro:collect",
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
], headers=None, link_whole=True, extra_test_libs=False)
|
||||
cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[], deps=[":rocksdb_lib"], headers=None, link_whole=True, extra_test_libs=False)
|
||||
|
||||
cpp_library_wrapper(name="rocksdb_test_lib", srcs=[
|
||||
"db/db_test_util.cc",
|
||||
@@ -736,6 +394,7 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
|
||||
"db_stress_tool/db_stress_test_base.cc",
|
||||
"db_stress_tool/db_stress_tool.cc",
|
||||
"db_stress_tool/expected_state.cc",
|
||||
"db_stress_tool/expected_value.cc",
|
||||
"db_stress_tool/multi_ops_txns_stress.cc",
|
||||
"db_stress_tool/no_batched_ops_stress.cc",
|
||||
"test_util/testutil.cc",
|
||||
@@ -5093,6 +4752,12 @@ cpp_unittest_wrapper(name="db_bloom_filter_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="db_clip_test",
|
||||
srcs=["db/db_clip_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="db_compaction_filter_test",
|
||||
srcs=["db/db_compaction_filter_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
@@ -5845,6 +5510,12 @@ cpp_unittest_wrapper(name="ttl_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="udt_util_test",
|
||||
srcs=["util/udt_util_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="util_merge_operators_test",
|
||||
srcs=["utilities/util_merge_operators_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
|
||||
@@ -15,6 +15,25 @@ At Facebook, we use RocksDB as storage engines in multiple data management servi
|
||||
|
||||
[2] https://code.facebook.com/posts/357056558062811/logdevice-a-distributed-data-store-for-logs/
|
||||
|
||||
## Bilibili
|
||||
[Bilibili](bilibili.com) [uses](https://www.alluxio.io/blog/when-ai-meets-alluxio-at-bilibili-building-an-efficient-ai-platform-for-data-preprocessing-and-model-training/) Alluxio to speed up its ML training workloads, and Alluxio uses RocksDB to store its filesystem metadata, so Bilibili uses RocksDB.
|
||||
|
||||
Bilibili's [real-time platform](https://www.alibabacloud.com/blog/architecture-and-practices-of-bilibilis-real-time-platform_596676) uses Flink, and uses RocksDB as Flink's state store.
|
||||
|
||||
## TikTok
|
||||
TikTok, or its parent company ByteDance, uses RocksDB as the storage engine for some storage systems, such as its distributed graph database [ByteGraph](https://vldb.org/pvldb/vol15/p3306-li.pdf).
|
||||
|
||||
Also, TikTok uses [Alluxio](alluxio.io) to [speed up Presto queries](https://www.alluxio.io/resources/videos/improving-presto-performance-with-alluxio-at-tiktok/), and Alluxio stores the files' metadata in RocksDB.
|
||||
|
||||
## FoundationDB
|
||||
[FoundationDB](https://www.foundationdb.org/) [uses](https://github.com/apple/foundationdb/blob/377f1f692da6ab2fe5bdac57035651db3e5fb66d/fdbserver/KeyValueStoreRocksDB.actor.cpp) RocksDB to implement a [key-value store interface](https://github.com/apple/foundationdb/blob/377f1f692da6ab2fe5bdac57035651db3e5fb66d/fdbserver/KeyValueStoreRocksDB.actor.cpp#L1127) in its server backend.
|
||||
|
||||
## Apple
|
||||
Apple [uses](https://opensource.apple.com/projects/foundationdb/) FoundationDB, so it also uses RocksDB.
|
||||
|
||||
## Snowflake
|
||||
Snowflake [uses](https://www.snowflake.com/blog/how-foundationdb-powers-snowflake-metadata-forward/) FoundationDB, so it also uses RocksDB.
|
||||
|
||||
## Microsoft
|
||||
The Bing search engine from Microsoft uses RocksDB as the storage engine for its web data platform: https://blogs.bing.com/Engineering-Blog/october-2021/RocksDB-in-Microsoft-Bing
|
||||
|
||||
@@ -85,9 +104,18 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
## TiKV
|
||||
[TiKV](https://github.com/pingcap/tikv) is a GEO-replicated, high-performance, distributed, transactional key-value database. TiKV is powered by Rust and Raft. TiKV uses RocksDB as its persistence layer.
|
||||
|
||||
## TiDB
|
||||
[TiDB](https://github.com/pingcap/tidb) uses the TiKV distributed key-value database, so it uses RocksDB.
|
||||
|
||||
## PingCAP
|
||||
[PingCAP](https://www.pingcap.com/) is the company behind TiDB, its cloud database service uses RocksDB.
|
||||
|
||||
## Apache Spark
|
||||
[Spark Structured Streaming](https://docs.databricks.com/structured-streaming/rocksdb-state-store.html) uses RocksDB as the local state store.
|
||||
|
||||
## Databricks
|
||||
[Databricks](https://www.databricks.com/) [replaces AWS RDS with TiDB](https://www.pingcap.com/case-study/how-databricks-tackles-the-scalability-limit-with-a-mysql-alternative/) for scalability, so it uses RocksDB.
|
||||
|
||||
## Apache Flink
|
||||
[Apache Flink](https://flink.apache.org/news/2016/03/08/release-1.0.0.html) uses RocksDB to store state locally on a machine.
|
||||
|
||||
|
||||
@@ -154,16 +154,9 @@ def generate_targets(repo_path, deps_map):
|
||||
# rocksdb_whole_archive_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_whole_archive_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
src_mk["RANGE_TREE_SOURCES"] + src_mk["TOOL_LIB_SOURCES"],
|
||||
[],
|
||||
deps=[
|
||||
"//folly/container:f14_hash",
|
||||
"//folly/experimental/coro:blocking_wait",
|
||||
"//folly/experimental/coro:collect",
|
||||
"//folly/experimental/coro:coroutine",
|
||||
"//folly/experimental/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
":rocksdb_lib",
|
||||
],
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
|
||||
@@ -419,7 +419,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_JEMALLOC; then
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -ljemalloc \
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -ljemalloc \
|
||||
2>/dev/null; then
|
||||
# This will enable some preprocessor identifiers in the Makefile
|
||||
JEMALLOC=1
|
||||
@@ -428,12 +428,19 @@ EOF
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
# check for JEMALLOC installed with HomeBrew
|
||||
if [ "$PLATFORM" == "OS_MACOSX" ]; then
|
||||
if [ "$TARGET_ARCHITECTURE" = "arm64" ]; then
|
||||
# on M1 Macs, homebrew installs here instead of /usr/local
|
||||
JEMALLOC_PREFIX="/opt/homebrew"
|
||||
else
|
||||
JEMALLOC_PREFIX="/usr/local"
|
||||
fi
|
||||
if hash brew 2>/dev/null && brew ls --versions jemalloc > /dev/null; then
|
||||
JEMALLOC_VER=$(brew ls --versions jemalloc | tail -n 1 | cut -f 2 -d ' ')
|
||||
JEMALLOC_INCLUDE="-I/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS $JEMALLOC_LIB"
|
||||
JEMALLOC_INCLUDE="-I${JEMALLOC_PREFIX}/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="${JEMALLOC_PREFIX}/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -621,7 +628,7 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "0$PORTABLE" -eq 0; then
|
||||
if [ "$PORTABLE" == "" ] || [ "$PORTABLE" == 0 ]; then
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^ppc64`"; then
|
||||
# Tune for this POWER processor, treating '+' models as base models
|
||||
POWER=`LD_SHOW_AUXV=1 /bin/true | grep AT_PLATFORM | grep -E -o power[0-9]+`
|
||||
@@ -644,27 +651,26 @@ if test "0$PORTABLE" -eq 0; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" == "AIX" ] || [ "$TARGET_OS" == "SunOS" ]; then
|
||||
# TODO: Not sure why we don't use -march=native on these OSes
|
||||
if test "$USE_SSE"; then
|
||||
TRY_SSE_ETC="1"
|
||||
fi
|
||||
else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
fi
|
||||
else
|
||||
# PORTABLE=1
|
||||
if test "$USE_SSE"; then
|
||||
TRY_SSE_ETC="1"
|
||||
fi
|
||||
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
fi
|
||||
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
# PORTABLE specified
|
||||
if [ "$PORTABLE" == 1 ]; then
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep isa | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
elif test "$USE_SSE"; then
|
||||
# USE_SSE is DEPRECATED
|
||||
# This is a rough approximation of the old USE_SSE behavior
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=haswell"
|
||||
fi
|
||||
# Other than those cases, not setting -march= here.
|
||||
else
|
||||
# Assume PORTABLE is a minimum assumed cpu type, e.g. PORTABLE=haswell
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${PORTABLE}"
|
||||
fi
|
||||
|
||||
if [[ "${PLATFORM}" == "OS_MACOSX" ]]; then
|
||||
@@ -698,101 +704,6 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TRY_SSE_ETC"; then
|
||||
# The USE_SSE flag now means "attempt to compile with widely-available
|
||||
# Intel architecture extensions utilized by specific optimizations in the
|
||||
# source code." It's a qualifier on PORTABLE=1 that means "mostly portable."
|
||||
# It doesn't even really check that your current CPU is compatible.
|
||||
#
|
||||
# SSE4.2 available since nehalem, ca. 2008-2010
|
||||
# Includes POPCNT for BitsSetToOne, BitParity
|
||||
TRY_SSE42="-msse4.2"
|
||||
# PCLMUL available since westmere, ca. 2010-2011
|
||||
TRY_PCLMUL="-mpclmul"
|
||||
# AVX2 available since haswell, ca. 2013-2015
|
||||
TRY_AVX2="-mavx2"
|
||||
# BMI available since haswell, ca. 2013-2015
|
||||
# Primarily for TZCNT for CountTrailingZeroBits
|
||||
TRY_BMI="-mbmi"
|
||||
# LZCNT available since haswell, ca. 2013-2015
|
||||
# For FloorLog2
|
||||
TRY_LZCNT="-mlzcnt"
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_SSE42 -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
int main() {
|
||||
volatile uint32_t x = _mm_crc32_u32(0, 0);
|
||||
(void)x;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_SSE42 -DHAVE_SSE42"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_PCLMUL -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
const auto a = _mm_set_epi64x(0, 0);
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
(void)d;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_PCLMUL -DHAVE_PCLMUL"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_AVX2 -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main() {
|
||||
const auto a = _mm256_setr_epi32(0, 1, 2, 3, 4, 7, 6, 5);
|
||||
const auto b = _mm256_permutevar8x32_epi32(a, a);
|
||||
(void)b;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_AVX2 -DHAVE_AVX2"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use AVX2 intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_BMI -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argv;
|
||||
return (int)_tzcnt_u64((uint64_t)argc);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_BMI -DHAVE_BMI"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use BMI intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS $TRY_LZCNT -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argv;
|
||||
return (int)_lzcnt_u64((uint64_t)argc);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS $TRY_LZCNT -DHAVE_LZCNT"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use LZCNT intrinsics, disabling" >&2
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
int main() {
|
||||
|
||||
@@ -147,7 +147,7 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DHAVE_SSE42"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
|
||||
@@ -154,7 +154,7 @@ CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DHAVE_SSE42 -DROCKSDB_IOURING_PRESENT"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_IOURING_PRESENT"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
|
||||
Vendored
+8
@@ -77,6 +77,10 @@ DEFINE_bool(lean, false,
|
||||
"If true, no additional computation is performed besides cache "
|
||||
"operations.");
|
||||
|
||||
DEFINE_bool(early_exit, false,
|
||||
"Exit before deallocating most memory. Good for malloc stats, e.g."
|
||||
"MALLOC_CONF=\"stats_print:true\"");
|
||||
|
||||
DEFINE_string(secondary_cache_uri, "",
|
||||
"Full URI for creating a custom secondary cache object");
|
||||
static class std::shared_ptr<ROCKSDB_NAMESPACE::SecondaryCache> secondary_cache;
|
||||
@@ -593,6 +597,10 @@ class CacheBench {
|
||||
}
|
||||
thread->latency_ns_hist.Add(timer.ElapsedNanos());
|
||||
}
|
||||
if (FLAGS_early_exit) {
|
||||
MutexLock l(thread->shared->GetMutex());
|
||||
exit(0);
|
||||
}
|
||||
if (handle) {
|
||||
cache_->Release(handle);
|
||||
handle = nullptr;
|
||||
|
||||
Vendored
+92
@@ -21,6 +21,7 @@
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/hash_containers.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
// HyperClockCache only supports 16-byte keys, so some of the tests
|
||||
@@ -955,6 +956,97 @@ TEST_P(CacheTest, GetChargeAndDeleter) {
|
||||
cache_->Release(h1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool AreTwoCacheKeysOrdered(Cache* cache) {
|
||||
std::vector<std::string> keys;
|
||||
const auto callback = [&](const Slice& key, Cache::ObjectPtr /*value*/,
|
||||
size_t /*charge*/,
|
||||
const Cache::CacheItemHelper* /*helper*/) {
|
||||
keys.push_back(key.ToString());
|
||||
};
|
||||
cache->ApplyToAllEntries(callback, /*opts*/ {});
|
||||
EXPECT_EQ(keys.size(), 2U);
|
||||
EXPECT_NE(keys[0], keys[1]);
|
||||
return keys[0] < keys[1];
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_P(CacheTest, CacheUniqueSeeds) {
|
||||
// kQuasiRandomHashSeed should generate unique seeds (up to 2 billion before
|
||||
// repeating)
|
||||
UnorderedSet<uint32_t> seeds_seen;
|
||||
// Roughly sqrt(number of possible values) for a decent chance at detecting
|
||||
// a random collision if it's possible (shouldn't be)
|
||||
uint16_t kSamples = 20000;
|
||||
seeds_seen.reserve(kSamples);
|
||||
|
||||
// Hash seed should affect ordering of entries in the table, so we should
|
||||
// have extremely high chance of seeing two entries ordered both ways.
|
||||
bool seen_forward_order = false;
|
||||
bool seen_reverse_order = false;
|
||||
|
||||
for (int i = 0; i < kSamples; ++i) {
|
||||
auto cache = NewCache(2, [=](ShardedCacheOptions& opts) {
|
||||
opts.hash_seed = LRUCacheOptions::kQuasiRandomHashSeed;
|
||||
opts.num_shard_bits = 0;
|
||||
opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
});
|
||||
auto val = cache->GetHashSeed();
|
||||
ASSERT_TRUE(seeds_seen.insert(val).second);
|
||||
|
||||
ASSERT_OK(cache->Insert(EncodeKey(1), nullptr, &kHelper, /*charge*/ 1));
|
||||
ASSERT_OK(cache->Insert(EncodeKey(2), nullptr, &kHelper, /*charge*/ 1));
|
||||
|
||||
if (AreTwoCacheKeysOrdered(cache.get())) {
|
||||
seen_forward_order = true;
|
||||
} else {
|
||||
seen_reverse_order = true;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_TRUE(seen_forward_order);
|
||||
ASSERT_TRUE(seen_reverse_order);
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, CacheHostSeed) {
|
||||
// kHostHashSeed should generate a consistent seed within this process
|
||||
// (and other processes on the same host, but not unit testing that).
|
||||
// And we should be able to use that chosen seed as an explicit option
|
||||
// (for debugging).
|
||||
// And we should verify consistent ordering of entries.
|
||||
uint32_t expected_seed = 0;
|
||||
bool expected_order = false;
|
||||
// 10 iterations -> chance of a random seed falsely appearing consistent
|
||||
// should be low, just 1 in 2^9.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto cache = NewCache(2, [=](ShardedCacheOptions& opts) {
|
||||
if (i != 5) {
|
||||
opts.hash_seed = LRUCacheOptions::kHostHashSeed;
|
||||
} else {
|
||||
// Can be used as explicit seed
|
||||
opts.hash_seed = static_cast<int32_t>(expected_seed);
|
||||
ASSERT_GE(opts.hash_seed, 0);
|
||||
}
|
||||
opts.num_shard_bits = 0;
|
||||
opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
});
|
||||
ASSERT_OK(cache->Insert(EncodeKey(1), nullptr, &kHelper, /*charge*/ 1));
|
||||
ASSERT_OK(cache->Insert(EncodeKey(2), nullptr, &kHelper, /*charge*/ 1));
|
||||
uint32_t val = cache->GetHashSeed();
|
||||
bool order = AreTwoCacheKeysOrdered(cache.get());
|
||||
if (i != 0) {
|
||||
ASSERT_EQ(val, expected_seed);
|
||||
ASSERT_EQ(order, expected_order);
|
||||
} else {
|
||||
expected_seed = val;
|
||||
expected_order = order;
|
||||
}
|
||||
}
|
||||
// Printed for reference in case it's needed to reproduce other unit test
|
||||
// failures on another host
|
||||
fprintf(stderr, "kHostHashSeed -> %u\n", (unsigned)expected_seed);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
secondary_cache_test_util::GetTestingCacheTypes());
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, LRUCacheTest,
|
||||
|
||||
Vendored
+454
-372
File diff suppressed because it is too large.
Load diff
Vendored
+164
-120
@@ -371,14 +371,127 @@ struct ClockHandle : public ClockHandleBasicData {
|
||||
static constexpr uint8_t kMaxCountdown = kHighCountdown;
|
||||
// TODO: make these coundown values tuning parameters for eviction?
|
||||
|
||||
// See above
|
||||
std::atomic<uint64_t> meta{};
|
||||
// See above. Mutable for read reference counting.
|
||||
mutable std::atomic<uint64_t> meta{};
|
||||
|
||||
// Anticipating use for SecondaryCache support
|
||||
void* reserved_for_future_use = nullptr;
|
||||
// Whether this is a "deteched" handle that is independently allocated
|
||||
// with `new` (so must be deleted with `delete`).
|
||||
// TODO: ideally this would be packed into some other data field, such
|
||||
// as upper bits of total_charge, but that incurs a measurable performance
|
||||
// regression.
|
||||
bool standalone = false;
|
||||
|
||||
inline bool IsStandalone() const { return standalone; }
|
||||
|
||||
inline void SetStandalone() { standalone = true; }
|
||||
}; // struct ClockHandle
|
||||
|
||||
class HyperClockTable {
|
||||
class BaseClockTable {
|
||||
public:
|
||||
BaseClockTable(CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback,
|
||||
const uint32_t* hash_seed)
|
||||
: metadata_charge_policy_(metadata_charge_policy),
|
||||
allocator_(allocator),
|
||||
eviction_callback_(*eviction_callback),
|
||||
hash_seed_(*hash_seed) {}
|
||||
|
||||
template <class Table>
|
||||
typename Table::HandleImpl* CreateStandalone(ClockHandleBasicData& proto,
|
||||
size_t capacity,
|
||||
bool strict_capacity_limit,
|
||||
bool allow_uncharged);
|
||||
|
||||
template <class Table>
|
||||
Status Insert(const ClockHandleBasicData& proto,
|
||||
typename Table::HandleImpl** handle, Cache::Priority priority,
|
||||
size_t capacity, bool strict_capacity_limit);
|
||||
|
||||
void Ref(ClockHandle& handle);
|
||||
|
||||
size_t GetOccupancy() const {
|
||||
return occupancy_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
size_t GetUsage() const { return usage_.load(std::memory_order_relaxed); }
|
||||
|
||||
size_t GetStandaloneUsage() const {
|
||||
return standalone_usage_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
uint32_t GetHashSeed() const { return hash_seed_; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Acquire N references
|
||||
void TEST_RefN(ClockHandle& handle, size_t n);
|
||||
// Helper for TEST_ReleaseN
|
||||
void TEST_ReleaseNMinus1(ClockHandle* handle, size_t n);
|
||||
#endif
|
||||
|
||||
private: // fns
|
||||
// Creates a "standalone" handle for returning from an Insert operation that
|
||||
// cannot be completed by actually inserting into the table.
|
||||
// Updates `standalone_usage_` but not `usage_` nor `occupancy_`.
|
||||
template <class HandleImpl>
|
||||
HandleImpl* StandaloneInsert(const ClockHandleBasicData& proto);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=true rules. This
|
||||
// means the operation might fail with Status::MemoryLimit. If
|
||||
// `need_evict_for_occupancy`, then eviction of at least one entry is
|
||||
// required, and the operation should fail if not possible.
|
||||
// NOTE: Otherwise, occupancy_ is not managed in this function
|
||||
template <class Table>
|
||||
Status ChargeUsageMaybeEvictStrict(size_t total_charge, size_t capacity,
|
||||
bool need_evict_for_occupancy,
|
||||
typename Table::InsertState& state);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=false rules. This
|
||||
// means that updating `usage_` always succeeds even if forced to exceed
|
||||
// capacity. If `need_evict_for_occupancy`, then eviction of at least one
|
||||
// entry is required, and the operation should return false if such eviction
|
||||
// is not possible. `usage_` is not updated in that case. Otherwise, returns
|
||||
// true, indicating success.
|
||||
// NOTE: occupancy_ is not managed in this function
|
||||
template <class Table>
|
||||
bool ChargeUsageMaybeEvictNonStrict(size_t total_charge, size_t capacity,
|
||||
bool need_evict_for_occupancy,
|
||||
typename Table::InsertState& state);
|
||||
|
||||
protected: // data
|
||||
// We partition the following members into different cache lines
|
||||
// to avoid false sharing among Lookup, Release, Erase and Insert
|
||||
// operations in ClockCacheShard.
|
||||
|
||||
// Clock algorithm sweep pointer.
|
||||
std::atomic<uint64_t> clock_pointer_{};
|
||||
|
||||
ALIGN_AS(CACHE_LINE_SIZE)
|
||||
// Number of elements in the table.
|
||||
std::atomic<size_t> occupancy_{};
|
||||
|
||||
// Memory usage by entries tracked by the cache (including standalone)
|
||||
std::atomic<size_t> usage_{};
|
||||
|
||||
// Part of usage by standalone entries (not in table)
|
||||
std::atomic<size_t> standalone_usage_{};
|
||||
|
||||
ALIGN_AS(CACHE_LINE_SIZE)
|
||||
const CacheMetadataChargePolicy metadata_charge_policy_;
|
||||
|
||||
// From Cache, for deleter
|
||||
MemoryAllocator* const allocator_;
|
||||
|
||||
// A reference to Cache::eviction_callback_
|
||||
const Cache::EvictionCallback& eviction_callback_;
|
||||
|
||||
// A reference to ShardedCacheBase::hash_seed_
|
||||
const uint32_t& hash_seed_;
|
||||
};
|
||||
|
||||
class HyperClockTable : public BaseClockTable {
|
||||
public:
|
||||
// Target size to be exactly a common cache line size (see static_assert in
|
||||
// clock_cache.cc)
|
||||
@@ -387,16 +500,6 @@ class HyperClockTable {
|
||||
// up in this slot or a higher one.
|
||||
std::atomic<uint32_t> displacements{};
|
||||
|
||||
// Whether this is a "deteched" handle that is independently allocated
|
||||
// with `new` (so must be deleted with `delete`).
|
||||
// TODO: ideally this would be packed into some other data field, such
|
||||
// as upper bits of total_charge, but that incurs a measurable performance
|
||||
// regression.
|
||||
bool standalone = false;
|
||||
|
||||
inline bool IsStandalone() const { return standalone; }
|
||||
|
||||
inline void SetStandalone() { standalone = true; }
|
||||
}; // struct HandleImpl
|
||||
|
||||
struct Opts {
|
||||
@@ -407,50 +510,49 @@ class HyperClockTable {
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback,
|
||||
const Opts& opts);
|
||||
const uint32_t* hash_seed, const Opts& opts);
|
||||
~HyperClockTable();
|
||||
|
||||
Status Insert(const ClockHandleBasicData& proto, HandleImpl** handle,
|
||||
Cache::Priority priority, size_t capacity,
|
||||
bool strict_capacity_limit);
|
||||
// For BaseClockTable::Insert
|
||||
struct InsertState {};
|
||||
|
||||
HandleImpl* CreateStandalone(ClockHandleBasicData& proto, size_t capacity,
|
||||
bool strict_capacity_limit,
|
||||
bool allow_uncharged);
|
||||
void StartInsert(InsertState& state);
|
||||
|
||||
// Returns true iff there is room for the proposed number of entries.
|
||||
bool GrowIfNeeded(size_t new_occupancy, InsertState& state);
|
||||
|
||||
HandleImpl* DoInsert(const ClockHandleBasicData& proto,
|
||||
uint64_t initial_countdown, bool take_ref,
|
||||
InsertState& state);
|
||||
|
||||
// Runs the clock eviction algorithm trying to reclaim at least
|
||||
// requested_charge. Returns how much is evicted, which could be less
|
||||
// if it appears impossible to evict the requested amount without blocking.
|
||||
void Evict(size_t requested_charge, size_t* freed_charge, size_t* freed_count,
|
||||
InsertState& state);
|
||||
|
||||
HandleImpl* Lookup(const UniqueId64x2& hashed_key);
|
||||
|
||||
bool Release(HandleImpl* handle, bool useful, bool erase_if_last_ref);
|
||||
|
||||
void Ref(HandleImpl& handle);
|
||||
|
||||
void Erase(const UniqueId64x2& hashed_key);
|
||||
|
||||
void ConstApplyToEntriesRange(std::function<void(const HandleImpl&)> func,
|
||||
size_t index_begin, size_t index_end,
|
||||
bool apply_if_will_be_deleted) const;
|
||||
|
||||
void EraseUnRefEntries();
|
||||
|
||||
size_t GetTableSize() const { return size_t{1} << length_bits_; }
|
||||
|
||||
int GetLengthBits() const { return length_bits_; }
|
||||
|
||||
size_t GetOccupancy() const {
|
||||
return occupancy_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
size_t GetOccupancyLimit() const { return occupancy_limit_; }
|
||||
|
||||
size_t GetUsage() const { return usage_.load(std::memory_order_relaxed); }
|
||||
const HandleImpl* HandlePtr(size_t idx) const { return &array_[idx]; }
|
||||
|
||||
size_t GetStandaloneUsage() const {
|
||||
return standalone_usage_.load(std::memory_order_relaxed);
|
||||
#ifndef NDEBUG
|
||||
size_t& TEST_MutableOccupancyLimit() const {
|
||||
return const_cast<size_t&>(occupancy_limit_);
|
||||
}
|
||||
|
||||
// Acquire/release N references
|
||||
void TEST_RefN(HandleImpl& handle, size_t n);
|
||||
// Release N references
|
||||
void TEST_ReleaseN(HandleImpl* handle, size_t n);
|
||||
#endif
|
||||
|
||||
private: // functions
|
||||
// Returns x mod 2^{length_bits_}.
|
||||
@@ -458,28 +560,18 @@ class HyperClockTable {
|
||||
return static_cast<size_t>(x) & length_bits_mask_;
|
||||
}
|
||||
|
||||
// Runs the clock eviction algorithm trying to reclaim at least
|
||||
// requested_charge. Returns how much is evicted, which could be less
|
||||
// if it appears impossible to evict the requested amount without blocking.
|
||||
inline void Evict(size_t requested_charge, size_t* freed_charge,
|
||||
size_t* freed_count);
|
||||
|
||||
// Returns the first slot in the probe sequence, starting from the given
|
||||
// probe number, with a handle e such that match(e) is true. At every
|
||||
// step, the function first tests whether match(e) holds. If this is false,
|
||||
// it evaluates abort(e) to decide whether the search should be aborted,
|
||||
// and in the affirmative returns -1. For every handle e probed except
|
||||
// the last one, the function runs update(e).
|
||||
// The probe parameter is modified as follows. We say a probe to a handle
|
||||
// e is aborting if match(e) is false and abort(e) is true. Then the final
|
||||
// value of probe is one more than the last non-aborting probe during the
|
||||
// call. This is so that that the variable can be used to keep track of
|
||||
// progress across consecutive calls to FindSlot.
|
||||
inline HandleImpl* FindSlot(const UniqueId64x2& hashed_key,
|
||||
std::function<bool(HandleImpl*)> match,
|
||||
std::function<bool(HandleImpl*)> stop,
|
||||
std::function<void(HandleImpl*)> update,
|
||||
size_t& probe);
|
||||
// Returns the first slot in the probe sequence with a handle e such that
|
||||
// match_fn(e) is true. At every step, the function first tests whether
|
||||
// match_fn(e) holds. If this is false, it evaluates abort_fn(e) to decide
|
||||
// whether the search should be aborted, and if so, FindSlot immediately
|
||||
// returns nullptr. For every handle e that is not a match and not aborted,
|
||||
// FindSlot runs update_fn(e, is_last) where is_last is set to true iff that
|
||||
// slot will be the last probed because the next would cycle back to the first
|
||||
// slot probed. This function uses templates instead of std::function to
|
||||
// minimize the risk of heap-allocated closures being created.
|
||||
template <typename MatchFn, typename AbortFn, typename UpdateFn>
|
||||
inline HandleImpl* FindSlot(const UniqueId64x2& hashed_key, MatchFn match_fn,
|
||||
AbortFn abort_fn, UpdateFn update_fn);
|
||||
|
||||
// Re-decrement all displacements in probe path starting from beginning
|
||||
// until (not including) the given handle
|
||||
@@ -492,33 +584,6 @@ class HyperClockTable {
|
||||
// before releasing it so that it can be provided to this function.
|
||||
inline void ReclaimEntryUsage(size_t total_charge);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=true rules. This
|
||||
// means the operation might fail with Status::MemoryLimit. If
|
||||
// `need_evict_for_occupancy`, then eviction of at least one entry is
|
||||
// required, and the operation should fail if not possible.
|
||||
// NOTE: Otherwise, occupancy_ is not managed in this function
|
||||
inline Status ChargeUsageMaybeEvictStrict(size_t total_charge,
|
||||
size_t capacity,
|
||||
bool need_evict_for_occupancy);
|
||||
|
||||
// Helper for updating `usage_` for new entry with given `total_charge`
|
||||
// and evicting if needed under strict_capacity_limit=false rules. This
|
||||
// means that updating `usage_` always succeeds even if forced to exceed
|
||||
// capacity. If `need_evict_for_occupancy`, then eviction of at least one
|
||||
// entry is required, and the operation should return false if such eviction
|
||||
// is not possible. `usage_` is not updated in that case. Otherwise, returns
|
||||
// true, indicating success.
|
||||
// NOTE: occupancy_ is not managed in this function
|
||||
inline bool ChargeUsageMaybeEvictNonStrict(size_t total_charge,
|
||||
size_t capacity,
|
||||
bool need_evict_for_occupancy);
|
||||
|
||||
// Creates a "standalone" handle for returning from an Insert operation that
|
||||
// cannot be completed by actually inserting into the table.
|
||||
// Updates `standalone_usage_` but not `usage_` nor `occupancy_`.
|
||||
inline HandleImpl* StandaloneInsert(const ClockHandleBasicData& proto);
|
||||
|
||||
MemoryAllocator* GetAllocator() const { return allocator_; }
|
||||
|
||||
// Returns the number of bits used to hash an element in the hash
|
||||
@@ -539,30 +604,6 @@ class HyperClockTable {
|
||||
|
||||
// Array of slots comprising the hash table.
|
||||
const std::unique_ptr<HandleImpl[]> array_;
|
||||
|
||||
// From Cache, for deleter
|
||||
MemoryAllocator* const allocator_;
|
||||
|
||||
// A reference to Cache::eviction_callback_
|
||||
const Cache::EvictionCallback& eviction_callback_;
|
||||
|
||||
// We partition the following members into different cache lines
|
||||
// to avoid false sharing among Lookup, Release, Erase and Insert
|
||||
// operations in ClockCacheShard.
|
||||
|
||||
ALIGN_AS(CACHE_LINE_SIZE)
|
||||
// Clock algorithm sweep pointer.
|
||||
std::atomic<uint64_t> clock_pointer_{};
|
||||
|
||||
ALIGN_AS(CACHE_LINE_SIZE)
|
||||
// Number of elements in the table.
|
||||
std::atomic<size_t> occupancy_{};
|
||||
|
||||
// Memory usage by entries tracked by the cache (including standalone)
|
||||
std::atomic<size_t> usage_{};
|
||||
|
||||
// Part of usage by standalone entries (not in table)
|
||||
std::atomic<size_t> standalone_usage_{};
|
||||
}; // class HyperClockTable
|
||||
|
||||
// A single shard of sharded cache.
|
||||
@@ -573,7 +614,7 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback,
|
||||
const typename Table::Opts& opts);
|
||||
const uint32_t* hash_seed, const typename Table::Opts& opts);
|
||||
|
||||
// For CacheShard concept
|
||||
using HandleImpl = typename Table::HandleImpl;
|
||||
@@ -583,22 +624,23 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
|
||||
static inline uint32_t HashPieceForSharding(HashCref hash) {
|
||||
return Upper32of64(hash[0]);
|
||||
}
|
||||
static inline HashVal ComputeHash(const Slice& key) {
|
||||
static inline HashVal ComputeHash(const Slice& key, uint32_t seed) {
|
||||
assert(key.size() == kCacheKeySize);
|
||||
HashVal in;
|
||||
HashVal out;
|
||||
// NOTE: endian dependence
|
||||
// TODO: use GetUnaligned?
|
||||
std::memcpy(&in, key.data(), kCacheKeySize);
|
||||
BijectiveHash2x64(in[1], in[0], &out[1], &out[0]);
|
||||
BijectiveHash2x64(in[1], in[0] ^ seed, &out[1], &out[0]);
|
||||
return out;
|
||||
}
|
||||
|
||||
// For reconstructing key from hashed_key. Requires the caller to provide
|
||||
// backing storage for the Slice in `unhashed`
|
||||
static inline Slice ReverseHash(const UniqueId64x2& hashed,
|
||||
UniqueId64x2* unhashed) {
|
||||
UniqueId64x2* unhashed, uint32_t seed) {
|
||||
BijectiveUnhash2x64(hashed[1], hashed[0], &(*unhashed)[1], &(*unhashed)[0]);
|
||||
(*unhashed)[0] ^= seed;
|
||||
// NOTE: endian dependence
|
||||
return Slice(reinterpret_cast<const char*>(unhashed), kCacheKeySize);
|
||||
}
|
||||
@@ -660,9 +702,14 @@ class ALIGN_AS(CACHE_LINE_SIZE) ClockCacheShard final : public CacheShardBase {
|
||||
return Lookup(key, hashed_key);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t& TEST_MutableOccupancyLimit() const {
|
||||
return table_.TEST_MutableOccupancyLimit();
|
||||
}
|
||||
// Acquire/release N references
|
||||
void TEST_RefN(HandleImpl* handle, size_t n);
|
||||
void TEST_ReleaseN(HandleImpl* handle, size_t n);
|
||||
#endif
|
||||
|
||||
private: // data
|
||||
Table table_;
|
||||
@@ -682,10 +729,7 @@ class HyperClockCache
|
||||
public:
|
||||
using Shard = ClockCacheShard<HyperClockTable>;
|
||||
|
||||
HyperClockCache(size_t capacity, size_t estimated_value_size,
|
||||
int num_shard_bits, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator);
|
||||
explicit HyperClockCache(const HyperClockCacheOptions& opts);
|
||||
|
||||
const char* Name() const override { return "HyperClockCache"; }
|
||||
|
||||
|
||||
Vendored
+19
-42
@@ -9,7 +9,7 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "memory/memory_allocator_impl.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -17,25 +17,16 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio, double low_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version,
|
||||
bool enable_custom_split_merge,
|
||||
const CacheEntryRoleSet& do_not_compress_roles)
|
||||
: cache_options_(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, low_pri_pool_ratio, memory_allocator,
|
||||
use_adaptive_mutex, metadata_charge_policy,
|
||||
compression_type, compress_format_version,
|
||||
enable_custom_split_merge, do_not_compress_roles) {
|
||||
cache_ =
|
||||
NewLRUCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, memory_allocator, use_adaptive_mutex,
|
||||
metadata_charge_policy, low_pri_pool_ratio);
|
||||
}
|
||||
const CompressedSecondaryCacheOptions& opts)
|
||||
: cache_(opts.LRUCacheOptions::MakeSharedCache()),
|
||||
cache_options_(opts),
|
||||
cache_res_mgr_(std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_))) {}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() { cache_.reset(); }
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() {
|
||||
assert(cache_res_mgr_->GetTotalReservedCacheSize() == 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
@@ -311,31 +302,17 @@ const Cache::CacheItemHelper* CompressedSecondaryCache::GetHelper(
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio, double low_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version,
|
||||
bool enable_custom_split_merge,
|
||||
const CacheEntryRoleSet& do_not_compress_roles) {
|
||||
return std::make_shared<CompressedSecondaryCache>(
|
||||
capacity, num_shard_bits, strict_capacity_limit, high_pri_pool_ratio,
|
||||
low_pri_pool_ratio, memory_allocator, use_adaptive_mutex,
|
||||
metadata_charge_policy, compression_type, compress_format_version,
|
||||
enable_custom_split_merge, do_not_compress_roles);
|
||||
std::shared_ptr<SecondaryCache>
|
||||
CompressedSecondaryCacheOptions::MakeSharedSecondaryCache() const {
|
||||
return std::make_shared<CompressedSecondaryCache>(*this);
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
const CompressedSecondaryCacheOptions& opts) {
|
||||
// The secondary_cache is disabled for this LRUCache instance.
|
||||
assert(opts.secondary_cache == nullptr);
|
||||
return NewCompressedSecondaryCache(
|
||||
opts.capacity, opts.num_shard_bits, opts.strict_capacity_limit,
|
||||
opts.high_pri_pool_ratio, opts.low_pri_pool_ratio, opts.memory_allocator,
|
||||
opts.use_adaptive_mutex, opts.metadata_charge_policy,
|
||||
opts.compression_type, opts.compress_format_version,
|
||||
opts.enable_custom_split_merge, opts.do_not_compress_roles);
|
||||
Status CompressedSecondaryCache::Deflate(size_t decrease) {
|
||||
return cache_res_mgr_->UpdateCacheReservation(decrease, /*increase=*/true);
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::Inflate(size_t increase) {
|
||||
return cache_res_mgr_->UpdateCacheReservation(increase, /*increase=*/false);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+11
-13
@@ -9,8 +9,9 @@
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "memory/memory_allocator_impl.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
@@ -69,18 +70,8 @@ class CompressedSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
|
||||
class CompressedSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
CompressedSecondaryCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio, double low_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDefaultCacheMetadataChargePolicy,
|
||||
CompressionType compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t compress_format_version = 2,
|
||||
bool enable_custom_split_merge = false,
|
||||
const CacheEntryRoleSet& do_not_compress_roles = {
|
||||
CacheEntryRole::kFilterBlock});
|
||||
explicit CompressedSecondaryCache(
|
||||
const CompressedSecondaryCacheOptions& opts);
|
||||
~CompressedSecondaryCache() override;
|
||||
|
||||
const char* Name() const override { return "CompressedSecondaryCache"; }
|
||||
@@ -103,8 +94,14 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
|
||||
Status GetCapacity(size_t& capacity) override;
|
||||
|
||||
Status Deflate(size_t decrease) override;
|
||||
|
||||
Status Inflate(size_t increase) override;
|
||||
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
size_t TEST_GetUsage() { return cache_->GetUsage(); }
|
||||
|
||||
private:
|
||||
friend class CompressedSecondaryCacheTestBase;
|
||||
static constexpr std::array<uint16_t, 8> malloc_bin_sizes_{
|
||||
@@ -137,6 +134,7 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
std::shared_ptr<Cache> cache_;
|
||||
CompressedSecondaryCacheOptions cache_options_;
|
||||
mutable port::Mutex capacity_mutex_;
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+101
-5
@@ -10,11 +10,13 @@
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
#include "memory/jemalloc_nodump_allocator.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -626,8 +628,9 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
|
||||
using CacheValueChunk = CompressedSecondaryCache::CacheValueChunk;
|
||||
std::unique_ptr<CompressedSecondaryCache> sec_cache =
|
||||
std::make_unique<CompressedSecondaryCache>(1000, 0, true, 0.5, 0.0,
|
||||
allocator);
|
||||
std::make_unique<CompressedSecondaryCache>(
|
||||
CompressedSecondaryCacheOptions(1000, 0, true, 0.5, 0.0,
|
||||
allocator));
|
||||
Random rnd(301);
|
||||
// 8500 = 8169 + 233 + 98, so there should be 3 chunks after split.
|
||||
size_t str_size{8500};
|
||||
@@ -678,7 +681,8 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
std::string str = str1 + str2 + str3;
|
||||
|
||||
std::unique_ptr<CompressedSecondaryCache> sec_cache =
|
||||
std::make_unique<CompressedSecondaryCache>(1000, 0, true, 0.5, 0.0);
|
||||
std::make_unique<CompressedSecondaryCache>(
|
||||
CompressedSecondaryCacheOptions(1000, 0, true, 0.5, 0.0));
|
||||
size_t charge{0};
|
||||
CacheAllocationPtr value =
|
||||
sec_cache->MergeChunksIntoValue(chunks_head, charge);
|
||||
@@ -708,8 +712,9 @@ class CompressedSecondaryCacheTestBase : public testing::Test,
|
||||
|
||||
using CacheValueChunk = CompressedSecondaryCache::CacheValueChunk;
|
||||
std::unique_ptr<CompressedSecondaryCache> sec_cache =
|
||||
std::make_unique<CompressedSecondaryCache>(1000, 0, true, 0.5, 0.0,
|
||||
allocator);
|
||||
std::make_unique<CompressedSecondaryCache>(
|
||||
CompressedSecondaryCacheOptions(1000, 0, true, 0.5, 0.0,
|
||||
allocator));
|
||||
Random rnd(301);
|
||||
// 8500 = 8169 + 233 + 98, so there should be 3 chunks after split.
|
||||
size_t str_size{8500};
|
||||
@@ -971,6 +976,97 @@ TEST_P(CompressedSecondaryCacheTest, SplictValueAndMergeChunksTest) {
|
||||
SplictValueAndMergeChunksTest();
|
||||
}
|
||||
|
||||
class CompressedSecCacheTestWithTiered : public ::testing::Test {
|
||||
public:
|
||||
CompressedSecCacheTestWithTiered() {
|
||||
LRUCacheOptions lru_opts;
|
||||
TieredVolatileCacheOptions opts;
|
||||
lru_opts.capacity = 70 << 20;
|
||||
opts.cache_opts = &lru_opts;
|
||||
opts.cache_type = PrimaryCacheType::kCacheTypeLRU;
|
||||
opts.comp_cache_opts.capacity = 30 << 20;
|
||||
cache_ = NewTieredVolatileCache(opts);
|
||||
cache_res_mgr_ =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_);
|
||||
}
|
||||
|
||||
protected:
|
||||
CacheReservationManager* cache_res_mgr() { return cache_res_mgr_.get(); }
|
||||
|
||||
Cache* GetCache() {
|
||||
return static_cast_with_check<CacheWithSecondaryAdapter, Cache>(
|
||||
cache_.get())
|
||||
->TEST_GetCache();
|
||||
}
|
||||
|
||||
SecondaryCache* GetSecondaryCache() {
|
||||
return static_cast_with_check<CacheWithSecondaryAdapter, Cache>(
|
||||
cache_.get())
|
||||
->TEST_GetSecondaryCache();
|
||||
}
|
||||
|
||||
size_t GetPercent(size_t val, unsigned int percent) {
|
||||
return static_cast<size_t>(val * percent / 100);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr_;
|
||||
};
|
||||
|
||||
bool CacheUsageWithinBounds(size_t val1, size_t val2, size_t error) {
|
||||
return ((val1 < (val2 + error)) && (val1 > (val2 - error)));
|
||||
}
|
||||
|
||||
TEST_F(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
// double explicit casts
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(10 << 20));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
|
||||
GetPercent(37 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(0));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
}
|
||||
|
||||
TEST_F(CompressedSecCacheTestWithTiered,
|
||||
CacheReservationManagerMultipleUpdate) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation((1 + i) << 20));
|
||||
}
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
|
||||
GetPercent(37 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
|
||||
for (i = 10; i > 0; --i) {
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(((i - 1) << 20)));
|
||||
}
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
+21
-47
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "port/lang.h"
|
||||
#include "util/distributed_mutex.h"
|
||||
|
||||
@@ -646,23 +646,15 @@ void LRUCacheShard::AppendPrintableOptions(std::string& str) const {
|
||||
str.append(buffer);
|
||||
}
|
||||
|
||||
LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit, double high_pri_pool_ratio,
|
||||
double low_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> allocator,
|
||||
bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
std::move(allocator)) {
|
||||
LRUCache::LRUCache(const LRUCacheOptions& opts) : ShardedCache(opts) {
|
||||
size_t per_shard = GetPerShardCapacity();
|
||||
MemoryAllocator* alloc = memory_allocator();
|
||||
const EvictionCallback* eviction_callback = &eviction_callback_;
|
||||
InitShards([=](LRUCacheShard* cs) {
|
||||
new (cs) LRUCacheShard(per_shard, strict_capacity_limit,
|
||||
high_pri_pool_ratio, low_pri_pool_ratio,
|
||||
use_adaptive_mutex, metadata_charge_policy,
|
||||
/* max_upper_hash_bits */ 32 - num_shard_bits, alloc,
|
||||
eviction_callback);
|
||||
InitShards([&](LRUCacheShard* cs) {
|
||||
new (cs) LRUCacheShard(per_shard, opts.strict_capacity_limit,
|
||||
opts.high_pri_pool_ratio, opts.low_pri_pool_ratio,
|
||||
opts.use_adaptive_mutex, opts.metadata_charge_policy,
|
||||
/* max_upper_hash_bits */ 32 - opts.num_shard_bits,
|
||||
alloc, &eviction_callback_);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -692,13 +684,7 @@ double LRUCache::GetHighPriPoolRatio() {
|
||||
|
||||
} // namespace lru_cache
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
const std::shared_ptr<SecondaryCache>& secondary_cache,
|
||||
double low_pri_pool_ratio) {
|
||||
std::shared_ptr<Cache> LRUCacheOptions::MakeSharedCache() const {
|
||||
if (num_shard_bits >= 20) {
|
||||
return nullptr; // The cache cannot be sharded into too many fine pieces.
|
||||
}
|
||||
@@ -714,36 +700,24 @@ std::shared_ptr<Cache> NewLRUCache(
|
||||
// Invalid high_pri_pool_ratio and low_pri_pool_ratio combination
|
||||
return nullptr;
|
||||
}
|
||||
if (num_shard_bits < 0) {
|
||||
num_shard_bits = GetDefaultCacheShardBits(capacity);
|
||||
// For sanitized options
|
||||
LRUCacheOptions opts = *this;
|
||||
if (opts.num_shard_bits < 0) {
|
||||
opts.num_shard_bits = GetDefaultCacheShardBits(capacity);
|
||||
}
|
||||
std::shared_ptr<Cache> cache = std::make_shared<LRUCache>(
|
||||
capacity, num_shard_bits, strict_capacity_limit, high_pri_pool_ratio,
|
||||
low_pri_pool_ratio, std::move(memory_allocator), use_adaptive_mutex,
|
||||
metadata_charge_policy);
|
||||
std::shared_ptr<Cache> cache = std::make_shared<LRUCache>(opts);
|
||||
if (secondary_cache) {
|
||||
cache = std::make_shared<CacheWithSecondaryAdapter>(cache, secondary_cache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(const LRUCacheOptions& cache_opts) {
|
||||
return NewLRUCache(cache_opts.capacity, cache_opts.num_shard_bits,
|
||||
cache_opts.strict_capacity_limit,
|
||||
cache_opts.high_pri_pool_ratio,
|
||||
cache_opts.memory_allocator, cache_opts.use_adaptive_mutex,
|
||||
cache_opts.metadata_charge_policy,
|
||||
cache_opts.secondary_cache, cache_opts.low_pri_pool_ratio);
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
double low_pri_pool_ratio) {
|
||||
return NewLRUCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, memory_allocator, use_adaptive_mutex,
|
||||
metadata_charge_policy, nullptr, low_pri_pool_ratio);
|
||||
std::shared_ptr<RowCache> LRUCacheOptions::MakeSharedRowCache() const {
|
||||
if (secondary_cache) {
|
||||
// Not allowed for a RowCache
|
||||
return nullptr;
|
||||
}
|
||||
// Works while RowCache is an alias for Cache
|
||||
return MakeSharedCache();
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+3
-8
@@ -279,8 +279,8 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShardBase {
|
||||
using HashCref = uint32_t;
|
||||
|
||||
public: // Function definitions expected as parameter to ShardedCache
|
||||
static inline HashVal ComputeHash(const Slice& key) {
|
||||
return Lower32of64(GetSliceNPHash64(key));
|
||||
static inline HashVal ComputeHash(const Slice& key, uint32_t seed) {
|
||||
return Lower32of64(GetSliceNPHash64(key, seed));
|
||||
}
|
||||
|
||||
// Separate from constructor so caller can easily make an array of LRUCache
|
||||
@@ -446,12 +446,7 @@ class LRUCache
|
||||
#endif
|
||||
: public ShardedCache<LRUCacheShard> {
|
||||
public:
|
||||
LRUCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio, double low_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDontChargeCacheMetadata);
|
||||
explicit LRUCache(const LRUCacheOptions& opts);
|
||||
const char* Name() const override { return "LRUCache"; }
|
||||
ObjectPtr Value(Handle* handle) override;
|
||||
size_t GetCharge(Handle* handle) const override;
|
||||
|
||||
Vendored
+58
-1
@@ -397,7 +397,7 @@ class ClockCacheTest : public testing::Test {
|
||||
opts.estimated_value_size = 1;
|
||||
new (shard_)
|
||||
Shard(capacity, strict_capacity_limit, kDontChargeCacheMetadata,
|
||||
/*allocator*/ nullptr, &eviction_callback_, opts);
|
||||
/*allocator*/ nullptr, &eviction_callback_, &hash_seed_, opts);
|
||||
}
|
||||
|
||||
Status Insert(const UniqueId64x2& hashed_key,
|
||||
@@ -455,6 +455,7 @@ class ClockCacheTest : public testing::Test {
|
||||
|
||||
private:
|
||||
Cache::EvictionCallback eviction_callback_;
|
||||
uint32_t hash_seed_ = 0;
|
||||
};
|
||||
|
||||
TEST_F(ClockCacheTest, Misc) {
|
||||
@@ -714,6 +715,62 @@ TEST_F(ClockCacheTest, ClockCounterOverflowTest) {
|
||||
ASSERT_EQ(val.deleted, 1);
|
||||
}
|
||||
|
||||
TEST_F(ClockCacheTest, ClockTableFull) {
|
||||
// Force clock cache table to fill up (not usually allowed) in order
|
||||
// to test full probe sequence that is theoretically possible due to
|
||||
// parallel operations
|
||||
NewShard(6, /*strict_capacity_limit*/ false);
|
||||
size_t size = shard_->GetTableAddressCount();
|
||||
ASSERT_LE(size + 3, 256); // for using char keys
|
||||
// Modify occupancy and capacity limits to attempt insert on full
|
||||
shard_->TEST_MutableOccupancyLimit() = size + 100;
|
||||
shard_->SetCapacity(size + 100);
|
||||
|
||||
DeleteCounter val;
|
||||
std::vector<HandleImpl*> handles;
|
||||
// NOTE: the three extra insertions should create standalone entries
|
||||
for (size_t i = 0; i < size + 3; ++i) {
|
||||
UniqueId64x2 hkey = TestHashedKey(static_cast<char>(i));
|
||||
ASSERT_OK(shard_->Insert(TestKey(hkey), hkey, &val, &kDeleteCounterHelper,
|
||||
1, &handles.emplace_back(),
|
||||
Cache::Priority::HIGH));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size + 3; ++i) {
|
||||
UniqueId64x2 hkey = TestHashedKey(static_cast<char>(i));
|
||||
HandleImpl* h = shard_->Lookup(TestKey(hkey), hkey);
|
||||
if (i < size) {
|
||||
ASSERT_NE(h, nullptr);
|
||||
shard_->Release(h);
|
||||
} else {
|
||||
// Standalone entries not visible by lookup
|
||||
ASSERT_EQ(h, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size + 3; ++i) {
|
||||
ASSERT_NE(handles[i], nullptr);
|
||||
shard_->Release(handles[i]);
|
||||
if (i < size) {
|
||||
// Everything still in cache
|
||||
ASSERT_EQ(val.deleted, 0);
|
||||
} else {
|
||||
// Standalone entries freed on release
|
||||
ASSERT_EQ(val.deleted, i + 1 - size);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = size + 3; i > 0; --i) {
|
||||
UniqueId64x2 hkey = TestHashedKey(static_cast<char>(i - 1));
|
||||
shard_->Erase(TestKey(hkey), hkey);
|
||||
if (i - 1 > size) {
|
||||
ASSERT_EQ(val.deleted, 3);
|
||||
} else {
|
||||
ASSERT_EQ(val.deleted, 3 + size - (i - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test is mostly to exercise some corner case logic, by forcing two
|
||||
// keys to have the same hash, and more
|
||||
TEST_F(ClockCacheTest, CollidingInsertEraseTest) {
|
||||
|
||||
Vendored
+141
-3
@@ -6,6 +6,7 @@
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -18,20 +19,98 @@ const Dummy kDummy{};
|
||||
Cache::ObjectPtr const kDummyObj = const_cast<Dummy*>(&kDummy);
|
||||
} // namespace
|
||||
|
||||
// When CacheWithSecondaryAdapter is constructed with the distribute_cache_res
|
||||
// parameter set to true, it manages the entire memory budget across the
|
||||
// primary and secondary cache. The secondary cache is assumed to be in
|
||||
// memory, such as the CompressedSecondaryCache. When a placeholder entry
|
||||
// is inserted by a CacheReservationManager instance to reserve memory,
|
||||
// the CacheWithSecondaryAdapter ensures that the reservation is distributed
|
||||
// proportionally across the primary/secondary caches.
|
||||
//
|
||||
// The primary block cache is initially sized to the sum of the primary cache
|
||||
// budget + teh secondary cache budget, as follows -
|
||||
// |--------- Primary Cache Configured Capacity -----------|
|
||||
// |---Secondary Cache Budget----|----Primary Cache Budget-----|
|
||||
//
|
||||
// A ConcurrentCacheReservationManager member in the CacheWithSecondaryAdapter,
|
||||
// pri_cache_res_,
|
||||
// is used to help with tracking the distribution of memory reservations.
|
||||
// Initially, it accounts for the entire secondary cache budget as a
|
||||
// reservation against the primary cache. This shrinks the usable capacity of
|
||||
// the primary cache to the budget that the user originally desired.
|
||||
//
|
||||
// |--Reservation for Sec Cache--|-Pri Cache Usable Capacity---|
|
||||
//
|
||||
// When a reservation placeholder is inserted into the adapter, it is inserted
|
||||
// directly into the primary cache. This means the entire charge of the
|
||||
// placeholder is counted against the primary cache. To compensate and count
|
||||
// a portion of it against the secondary cache, the secondary cache Deflate()
|
||||
// method is called to shrink it. Since the Deflate() causes the secondary
|
||||
// actual usage to shrink, it is refelcted here by releasing an equal amount
|
||||
// from the pri_cache_res_ reservation. The Deflate() in the secondary cache
|
||||
// can be, but is not required to be, implemented using its own cache
|
||||
// reservation manager.
|
||||
//
|
||||
// For example, if the pri/sec ratio is 70/30, and the combined capacity is
|
||||
// 100MB, the intermediate and final state after inserting a reservation
|
||||
// placeholder for 10MB would be as follows -
|
||||
//
|
||||
// |-Reservation for Sec Cache-|-Pri Cache Usable Capacity-|---R---|
|
||||
// 1. After inserting the placeholder in primary
|
||||
// |------- 30MB -------------|------- 60MB -------------|-10MB--|
|
||||
// 2. After deflating the secondary and adjusting the reservation for
|
||||
// secondary against the primary
|
||||
// |------- 27MB -------------|------- 63MB -------------|-10MB--|
|
||||
//
|
||||
// Likewise, when the user inserted placeholder is released, the secondary
|
||||
// cache Inflate() method is called to grow it, and the pri_cache_res_
|
||||
// reservation is increased by an equal amount.
|
||||
//
|
||||
// Another way of implementing this would have been to simply split the user
|
||||
// reservation into primary and seconary components. However, this would
|
||||
// require allocating a structure to track the associated secondary cache
|
||||
// reservation, which adds some complexity and overhead.
|
||||
//
|
||||
CacheWithSecondaryAdapter::CacheWithSecondaryAdapter(
|
||||
std::shared_ptr<Cache> target,
|
||||
std::shared_ptr<SecondaryCache> secondary_cache)
|
||||
std::shared_ptr<SecondaryCache> secondary_cache, bool distribute_cache_res)
|
||||
: CacheWrapper(std::move(target)),
|
||||
secondary_cache_(std::move(secondary_cache)) {
|
||||
secondary_cache_(std::move(secondary_cache)),
|
||||
distribute_cache_res_(distribute_cache_res) {
|
||||
target_->SetEvictionCallback([this](const Slice& key, Handle* handle) {
|
||||
return EvictionHandler(key, handle);
|
||||
});
|
||||
if (distribute_cache_res_) {
|
||||
size_t sec_capacity = 0;
|
||||
pri_cache_res_ = std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
target_));
|
||||
Status s = secondary_cache_->GetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
// Initially, the primary cache is sized to uncompressed cache budget plsu
|
||||
// compressed secondary cache budget. The secondary cache budget is then
|
||||
// taken away from the primary cache through cache reservations. Later,
|
||||
// when a placeholder entry is inserted by the caller, its inserted
|
||||
// into the primary cache and the portion that should be assigned to the
|
||||
// secondary cache is freed from the reservation.
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_capacity);
|
||||
assert(s.ok());
|
||||
sec_cache_res_ratio_ = (double)sec_capacity / target_->GetCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
CacheWithSecondaryAdapter::~CacheWithSecondaryAdapter() {
|
||||
// `*this` will be destroyed before `*target_`, so we have to prevent
|
||||
// use after free
|
||||
target_->SetEvictionCallback({});
|
||||
#ifndef NDEBUG
|
||||
if (distribute_cache_res_) {
|
||||
size_t sec_capacity = 0;
|
||||
Status s = secondary_cache_->GetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
assert(pri_cache_res_->GetTotalReservedCacheSize() == sec_capacity);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
bool CacheWithSecondaryAdapter::EvictionHandler(const Slice& key,
|
||||
@@ -136,6 +215,22 @@ Cache::Handle* CacheWithSecondaryAdapter::Promote(
|
||||
return result;
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle,
|
||||
Priority priority) {
|
||||
Status s = target_->Insert(key, value, helper, charge, handle, priority);
|
||||
if (s.ok() && value == nullptr && distribute_cache_res_) {
|
||||
size_t sec_charge = static_cast<size_t>(charge * (sec_cache_res_ratio_));
|
||||
s = secondary_cache_->Deflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge, /*increase=*/false);
|
||||
assert(s.ok());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Cache::Handle* CacheWithSecondaryAdapter::Lookup(const Slice& key,
|
||||
const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
@@ -162,6 +257,22 @@ Cache::Handle* CacheWithSecondaryAdapter::Lookup(const Slice& key,
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CacheWithSecondaryAdapter::Release(Handle* handle,
|
||||
bool erase_if_last_ref) {
|
||||
if (erase_if_last_ref) {
|
||||
ObjectPtr v = target_->Value(handle);
|
||||
if (v == nullptr && distribute_cache_res_) {
|
||||
size_t charge = target_->GetCharge(handle);
|
||||
size_t sec_charge = static_cast<size_t>(charge * (sec_cache_res_ratio_));
|
||||
Status s = secondary_cache_->Inflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge, /*increase=*/true);
|
||||
assert(s.ok());
|
||||
}
|
||||
}
|
||||
return target_->Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
Cache::ObjectPtr CacheWithSecondaryAdapter::Value(Handle* handle) {
|
||||
ObjectPtr v = target_->Value(handle);
|
||||
// TODO with stacked secondaries: might fail in EvictionHandler
|
||||
@@ -265,7 +376,7 @@ void CacheWithSecondaryAdapter::WaitAll(AsyncLookupHandle* async_handles,
|
||||
for (AsyncLookupHandle* cur : my_pending) {
|
||||
my_secondary_handles.push_back(cur->pending_handle);
|
||||
}
|
||||
secondary_cache_->WaitAll(my_secondary_handles);
|
||||
secondary_cache_->WaitAll(std::move(my_secondary_handles));
|
||||
}
|
||||
|
||||
// Process results
|
||||
@@ -292,4 +403,31 @@ const char* CacheWithSecondaryAdapter::Name() const {
|
||||
// a secondary cache. So we pretend to be that cache
|
||||
return target_->Name();
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewTieredVolatileCache(
|
||||
TieredVolatileCacheOptions& opts) {
|
||||
if (!opts.cache_opts) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> cache;
|
||||
if (opts.cache_type == PrimaryCacheType::kCacheTypeLRU) {
|
||||
LRUCacheOptions cache_opts =
|
||||
*(static_cast_with_check<LRUCacheOptions, ShardedCacheOptions>(
|
||||
opts.cache_opts));
|
||||
cache_opts.capacity += opts.comp_cache_opts.capacity;
|
||||
cache = cache_opts.MakeSharedCache();
|
||||
} else if (opts.cache_type == PrimaryCacheType::kCacheTypeHCC) {
|
||||
HyperClockCacheOptions cache_opts =
|
||||
*(static_cast_with_check<HyperClockCacheOptions, ShardedCacheOptions>(
|
||||
opts.cache_opts));
|
||||
cache = cache_opts.MakeSharedCache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<SecondaryCache> sec_cache;
|
||||
sec_cache = NewCompressedSecondaryCache(opts.comp_cache_opts);
|
||||
|
||||
return std::make_shared<CacheWithSecondaryAdapter>(cache, sec_cache, true);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+25
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -13,15 +14,24 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
public:
|
||||
explicit CacheWithSecondaryAdapter(
|
||||
std::shared_ptr<Cache> target,
|
||||
std::shared_ptr<SecondaryCache> secondary_cache);
|
||||
std::shared_ptr<SecondaryCache> secondary_cache,
|
||||
bool distribute_cache_res = false);
|
||||
|
||||
~CacheWithSecondaryAdapter() override;
|
||||
|
||||
Status Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override;
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
Priority priority = Priority::LOW,
|
||||
Statistics* stats = nullptr) override;
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override;
|
||||
|
||||
ObjectPtr Value(Handle* handle) override;
|
||||
|
||||
void StartAsyncLookup(AsyncLookupHandle& async_handle) override;
|
||||
@@ -32,6 +42,10 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
|
||||
const char* Name() const override;
|
||||
|
||||
Cache* TEST_GetCache() { return target_.get(); }
|
||||
|
||||
SecondaryCache* TEST_GetSecondaryCache() { return secondary_cache_.get(); }
|
||||
|
||||
private:
|
||||
bool EvictionHandler(const Slice& key, Handle* handle);
|
||||
|
||||
@@ -47,6 +61,16 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
void CleanupCacheObject(ObjectPtr obj, const CacheItemHelper* helper);
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
// Whether to proportionally distribute cache memory reservations, i.e
|
||||
// placeholder entries with null value and a non-zero charge, across
|
||||
// the primary and secondary caches.
|
||||
bool distribute_cache_res_;
|
||||
// A cache reservation manager to keep track of secondary cache memory
|
||||
// usage by reserving equivalent capacity against the primary cache
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> pri_cache_res_;
|
||||
// Fraction of a cache memory reservation to be assigned to the secondary
|
||||
// cache
|
||||
double sec_cache_res_ratio_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+44
-7
@@ -13,20 +13,57 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "env/unique_id_gen.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/math.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
// The generated seeds must fit in 31 bits so that
|
||||
// ShardedCacheOptions::hash_seed can be set to it explicitly, for
|
||||
// diagnostic/debugging purposes.
|
||||
constexpr uint32_t kSeedMask = 0x7fffffff;
|
||||
uint32_t DetermineSeed(int32_t hash_seed_option) {
|
||||
if (hash_seed_option >= 0) {
|
||||
// User-specified exact seed
|
||||
return static_cast<uint32_t>(hash_seed_option);
|
||||
}
|
||||
static SemiStructuredUniqueIdGen gen;
|
||||
if (hash_seed_option == ShardedCacheOptions::kHostHashSeed) {
|
||||
std::string hostname;
|
||||
Status s = Env::Default()->GetHostNameString(&hostname);
|
||||
if (s.ok()) {
|
||||
return GetSliceHash(hostname) & kSeedMask;
|
||||
} else {
|
||||
// Fall back on something stable within the process.
|
||||
return static_cast<uint32_t>(gen.GetBaseUpper()) & kSeedMask;
|
||||
}
|
||||
} else {
|
||||
// for kQuasiRandomHashSeed and fallback
|
||||
uint32_t val = gen.GenerateNext<uint32_t>() & kSeedMask;
|
||||
// Perform some 31-bit bijective transformations so that we get
|
||||
// quasirandom, not just incrementing. (An incrementing seed from a
|
||||
// random starting point would be fine, but hard to describe in a name.)
|
||||
// See https://en.wikipedia.org/wiki/Quasirandom and using a murmur-like
|
||||
// transformation here for our bijection in the lower 31 bits.
|
||||
// See https://en.wikipedia.org/wiki/MurmurHash
|
||||
val *= /*31-bit prime*/ 1150630961;
|
||||
val ^= (val & kSeedMask) >> 17;
|
||||
val *= /*31-bit prime*/ 1320603883;
|
||||
return val & kSeedMask;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ShardedCacheBase::ShardedCacheBase(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
std::shared_ptr<MemoryAllocator> allocator)
|
||||
: Cache(std::move(allocator)),
|
||||
ShardedCacheBase::ShardedCacheBase(const ShardedCacheOptions& opts)
|
||||
: Cache(opts.memory_allocator),
|
||||
last_id_(1),
|
||||
shard_mask_((uint32_t{1} << num_shard_bits) - 1),
|
||||
strict_capacity_limit_(strict_capacity_limit),
|
||||
capacity_(capacity) {}
|
||||
shard_mask_((uint32_t{1} << opts.num_shard_bits) - 1),
|
||||
hash_seed_(DetermineSeed(opts.hash_seed)),
|
||||
strict_capacity_limit_(opts.strict_capacity_limit),
|
||||
capacity_(opts.capacity) {}
|
||||
|
||||
size_t ShardedCacheBase::ComputePerShardCapacity(size_t capacity) const {
|
||||
uint32_t num_shards = GetNumShards();
|
||||
|
||||
Vendored
+12
-13
@@ -34,8 +34,8 @@ class CacheShardBase {
|
||||
std::string GetPrintableOptions() const { return ""; }
|
||||
using HashVal = uint64_t;
|
||||
using HashCref = uint64_t;
|
||||
static inline HashVal ComputeHash(const Slice& key) {
|
||||
return GetSliceNPHash64(key);
|
||||
static inline HashVal ComputeHash(const Slice& key, uint32_t seed) {
|
||||
return GetSliceNPHash64(key, seed);
|
||||
}
|
||||
static inline uint32_t HashPieceForSharding(HashCref hash) {
|
||||
return Lower32of64(hash);
|
||||
@@ -89,9 +89,7 @@ class CacheShardBase {
|
||||
// Portions of ShardedCache that do not depend on the template parameter
|
||||
class ShardedCacheBase : public Cache {
|
||||
public:
|
||||
ShardedCacheBase(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator);
|
||||
explicit ShardedCacheBase(const ShardedCacheOptions& opts);
|
||||
virtual ~ShardedCacheBase() = default;
|
||||
|
||||
int GetNumShardBits() const;
|
||||
@@ -106,6 +104,8 @@ class ShardedCacheBase : public Cache {
|
||||
size_t GetUsage(Handle* handle) const override;
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
uint32_t GetHashSeed() const override { return hash_seed_; }
|
||||
|
||||
protected: // fns
|
||||
virtual void AppendPrintableOptions(std::string& str) const = 0;
|
||||
size_t GetPerShardCapacity() const;
|
||||
@@ -114,6 +114,7 @@ class ShardedCacheBase : public Cache {
|
||||
protected: // data
|
||||
std::atomic<uint64_t> last_id_; // For NewId
|
||||
const uint32_t shard_mask_;
|
||||
const uint32_t hash_seed_;
|
||||
|
||||
// Dynamic configuration parameters, guarded by config_mutex_
|
||||
bool strict_capacity_limit_;
|
||||
@@ -134,10 +135,8 @@ class ShardedCache : public ShardedCacheBase {
|
||||
using HashCref = typename CacheShard::HashCref;
|
||||
using HandleImpl = typename CacheShard::HandleImpl;
|
||||
|
||||
ShardedCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
std::shared_ptr<MemoryAllocator> allocator)
|
||||
: ShardedCacheBase(capacity, num_shard_bits, strict_capacity_limit,
|
||||
allocator),
|
||||
explicit ShardedCache(const ShardedCacheOptions& opts)
|
||||
: ShardedCacheBase(opts),
|
||||
shards_(reinterpret_cast<CacheShard*>(port::cacheline_aligned_alloc(
|
||||
sizeof(CacheShard) * GetNumShards()))),
|
||||
destroy_shards_in_dtor_(false) {}
|
||||
@@ -175,7 +174,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
size_t charge, Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
assert(helper);
|
||||
HashVal hash = CacheShard::ComputeHash(key);
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
auto h_out = reinterpret_cast<HandleImpl**>(handle);
|
||||
return GetShard(hash).Insert(key, hash, obj, helper, charge, h_out,
|
||||
priority);
|
||||
@@ -185,7 +184,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
bool allow_uncharged) override {
|
||||
assert(helper);
|
||||
HashVal hash = CacheShard::ComputeHash(key);
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).CreateStandalone(
|
||||
key, hash, obj, helper, charge, allow_uncharged);
|
||||
return reinterpret_cast<Handle*>(result);
|
||||
@@ -195,14 +194,14 @@ class ShardedCache : public ShardedCacheBase {
|
||||
CreateContext* create_context = nullptr,
|
||||
Priority priority = Priority::LOW,
|
||||
Statistics* stats = nullptr) override {
|
||||
HashVal hash = CacheShard::ComputeHash(key);
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
HandleImpl* result = GetShard(hash).Lookup(key, hash, helper,
|
||||
create_context, priority, stats);
|
||||
return reinterpret_cast<Handle*>(result);
|
||||
}
|
||||
|
||||
void Erase(const Slice& key) override {
|
||||
HashVal hash = CacheShard::ComputeHash(key);
|
||||
HashVal hash = CacheShard::ComputeHash(key, hash_seed_);
|
||||
GetShard(hash).Erase(key, hash);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --
|
||||
blackbox_crash_test_with_multiops_wp_txn \
|
||||
crash_test_with_tiered_storage blackbox_crash_test_with_tiered_storage \
|
||||
whitebox_crash_test_with_tiered_storage \
|
||||
whitebox_crash_test_with_optimistic_txn \
|
||||
blackbox_crash_test_with_optimistic_txn \
|
||||
|
||||
crash_test: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
@@ -37,6 +39,11 @@ crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_optimistic_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_optimistic_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
@@ -80,6 +87,9 @@ blackbox_crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
|
||||
blackbox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_tiered_storage blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --optimistic_txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
@@ -105,3 +115,7 @@ whitebox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
whitebox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_tiered_storage whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --optimistic_txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -47,7 +47,9 @@ void ArenaWrappedDBIter::Init(
|
||||
read_options_ = read_options;
|
||||
allow_refresh_ = allow_refresh;
|
||||
memtable_range_tombstone_iter_ = nullptr;
|
||||
if (!env->GetFileSystem()->use_async_io()) {
|
||||
|
||||
if (!CheckFSFeatureSupport(env->GetFileSystem().get(),
|
||||
FSSupportedOps::kAsyncIO)) {
|
||||
read_options_.async_io = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "memory/memory_allocator_impl.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
@@ -25,7 +25,7 @@ BlobFileCache::BlobFileCache(Cache* cache,
|
||||
HistogramImpl* blob_file_read_hist,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: cache_(cache),
|
||||
mutex_(kNumberOfMutexStripes, kGetSliceNPHash64UnseededFnPtr),
|
||||
mutex_(kNumberOfMutexStripes),
|
||||
immutable_options_(immutable_options),
|
||||
file_options_(file_options),
|
||||
column_family_id_(column_family_id),
|
||||
@@ -55,7 +55,7 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
TEST_SYNC_POINT("BlobFileCache::GetBlobFileReader:DoubleCheck");
|
||||
|
||||
// Check again while holding mutex
|
||||
MutexLock lock(mutex_.get(key));
|
||||
MutexLock lock(&mutex_.Get(key));
|
||||
|
||||
handle = cache_.Lookup(key);
|
||||
if (handle) {
|
||||
|
||||
@@ -43,7 +43,7 @@ class BlobFileCache {
|
||||
CacheInterface cache_;
|
||||
// Note: mutex_ below is used to guard against multiple threads racing to open
|
||||
// the same file.
|
||||
Striped<port::Mutex, Slice> mutex_;
|
||||
Striped<CacheAlignedWrapper<port::Mutex>> mutex_;
|
||||
const ImmutableOptions* immutable_options_;
|
||||
const FileOptions* file_options_;
|
||||
uint32_t column_family_id_;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/file_prefetch_buffer.h"
|
||||
#include "file/filename.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/slice.h"
|
||||
@@ -439,11 +439,11 @@ void BlobFileReader::MultiGetBlob(
|
||||
assert(req->offset >= adjustment);
|
||||
adjustments.push_back(adjustment);
|
||||
|
||||
FSReadRequest read_req = {};
|
||||
FSReadRequest read_req;
|
||||
read_req.offset = req->offset - adjustment;
|
||||
read_req.len = req->len + adjustment;
|
||||
read_reqs.emplace_back(read_req);
|
||||
total_len += read_req.len;
|
||||
read_reqs.emplace_back(std::move(read_req));
|
||||
}
|
||||
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_READ, total_len);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "db/blob/blob_log_sequential_reader.h"
|
||||
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "db/blob/blob_contents.h"
|
||||
#include "db/blob/blob_file_reader.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "table/get_context.h"
|
||||
#include "table/multiget_context.h"
|
||||
|
||||
+32
-7
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "db/blob/blob_file_builder.h"
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/merge_helper.h"
|
||||
@@ -205,21 +206,38 @@ Status BuildTable(
|
||||
/*compaction=*/nullptr, compaction_filter.get(),
|
||||
/*shutting_down=*/nullptr, db_options.info_log, full_history_ts_low);
|
||||
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool strip_timestamp =
|
||||
ts_sz > 0 && !ioptions.persist_user_defined_timestamps;
|
||||
|
||||
std::string key_after_flush_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();
|
||||
// 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, value);
|
||||
Slice key_after_flush = key;
|
||||
// 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
|
||||
// output validator, block builder all see the effect of the stripping.
|
||||
if (strip_timestamp) {
|
||||
key_after_flush_buf.clear();
|
||||
ReplaceInternalKeyWithMinTimestamp(&key_after_flush_buf, key, ts_sz);
|
||||
key_after_flush = key_after_flush_buf;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
builder->Add(key, value);
|
||||
builder->Add(key_after_flush, value);
|
||||
|
||||
s = meta->UpdateBoundaries(key, value, ikey.sequence, ikey.type);
|
||||
s = meta->UpdateBoundaries(key_after_flush, value, ikey.sequence,
|
||||
ikey.type);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -244,6 +262,7 @@ Status BuildTable(
|
||||
range_del_it->Next()) {
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
// TODO(yuzhangyu): handle range deletion for UDT in memtables only.
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone_end, tombstone.seq_,
|
||||
@@ -291,7 +310,10 @@ Status BuildTable(
|
||||
if (s.ok() && !empty) {
|
||||
uint64_t file_size = builder->FileSize();
|
||||
meta->fd.file_size = file_size;
|
||||
meta->tail_size = builder->GetTailSize();
|
||||
meta->marked_for_compaction = builder->NeedCompact();
|
||||
meta->user_defined_timestamps_persisted =
|
||||
ioptions.persist_user_defined_timestamps;
|
||||
assert(meta->fd.GetFileSize() > 0);
|
||||
tp = builder
|
||||
->GetTableProperties(); // refresh now that builder is finished
|
||||
@@ -351,6 +373,8 @@ Status BuildTable(
|
||||
s = *io_status;
|
||||
}
|
||||
|
||||
// TODO(yuzhangyu): handle the key copy in the blob when ts should be
|
||||
// stripped.
|
||||
if (blob_file_builder) {
|
||||
if (s.ok()) {
|
||||
s = blob_file_builder->Finish();
|
||||
@@ -380,7 +404,8 @@ Status BuildTable(
|
||||
MaxFileSizeForL0MetaPin(mutable_cf_options),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr,
|
||||
/*allow_unprepared_value*/ false));
|
||||
/*allow_unprepared_value*/ false,
|
||||
mutable_cf_options.block_protection_bytes_per_key));
|
||||
s = it->status();
|
||||
if (s.ok() && paranoid_file_checks) {
|
||||
OutputValidator file_validator(tboptions.internal_comparator,
|
||||
|
||||
@@ -1057,6 +1057,36 @@ rocksdb_column_family_handle_t* rocksdb_create_column_family(
|
||||
return handle;
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t** rocksdb_create_column_families(
|
||||
rocksdb_t* db, const rocksdb_options_t* column_family_options,
|
||||
int num_column_families, const char* const* column_family_names,
|
||||
size_t* lencfs, char** errptr) {
|
||||
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]));
|
||||
}
|
||||
SaveError(errptr, db->rep->CreateColumnFamilies(
|
||||
ColumnFamilyOptions(column_family_options->rep), names,
|
||||
&handles));
|
||||
|
||||
*lencfs = handles.size();
|
||||
rocksdb_column_family_handle_t** c_handles =
|
||||
static_cast<rocksdb_column_family_handle_t**>(
|
||||
malloc(sizeof(rocksdb_column_family_handle_t*) * handles.size()));
|
||||
for (size_t i = 0; i != handles.size(); ++i) {
|
||||
c_handles[i] = new rocksdb_column_family_handle_t;
|
||||
c_handles[i]->rep = handles[i];
|
||||
}
|
||||
|
||||
return c_handles;
|
||||
}
|
||||
|
||||
void rocksdb_create_column_families_destroy(
|
||||
rocksdb_column_family_handle_t** list) {
|
||||
free(list);
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_create_column_family_with_ttl(
|
||||
rocksdb_t* db, const rocksdb_options_t* column_family_options,
|
||||
const char* column_family_name, int ttl, char** errptr) {
|
||||
@@ -3740,16 +3770,21 @@ void rocksdb_options_set_hash_link_list_rep(rocksdb_options_t* opt,
|
||||
ROCKSDB_NAMESPACE::NewHashLinkListRepFactory(bucket_count));
|
||||
}
|
||||
|
||||
void rocksdb_options_set_plain_table_factory(rocksdb_options_t* opt,
|
||||
uint32_t user_key_len,
|
||||
int bloom_bits_per_key,
|
||||
double hash_table_ratio,
|
||||
size_t index_sparseness) {
|
||||
void rocksdb_options_set_plain_table_factory(
|
||||
rocksdb_options_t* opt, uint32_t user_key_len, int bloom_bits_per_key,
|
||||
double hash_table_ratio, size_t index_sparseness, size_t huge_page_tlb_size,
|
||||
char encoding_type, unsigned char full_scan_mode,
|
||||
unsigned char store_index_in_file) {
|
||||
ROCKSDB_NAMESPACE::PlainTableOptions options;
|
||||
options.user_key_len = user_key_len;
|
||||
options.bloom_bits_per_key = bloom_bits_per_key;
|
||||
options.hash_table_ratio = hash_table_ratio;
|
||||
options.index_sparseness = index_sparseness;
|
||||
options.huge_page_tlb_size = huge_page_tlb_size;
|
||||
options.encoding_type =
|
||||
static_cast<ROCKSDB_NAMESPACE::EncodingType>(encoding_type);
|
||||
options.full_scan_mode = full_scan_mode;
|
||||
options.store_index_in_file = store_index_in_file;
|
||||
|
||||
ROCKSDB_NAMESPACE::TableFactory* factory =
|
||||
ROCKSDB_NAMESPACE::NewPlainTableFactory(options);
|
||||
@@ -3888,6 +3923,15 @@ void rocksdb_options_add_compact_on_deletion_collector_factory(
|
||||
opt->rep.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
}
|
||||
|
||||
void rocksdb_options_add_compact_on_deletion_collector_factory_del_ratio(
|
||||
rocksdb_options_t* opt, size_t window_size, size_t num_dels_trigger,
|
||||
double deletion_ratio) {
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::TablePropertiesCollectorFactory>
|
||||
compact_on_del = NewCompactOnDeletionCollectorFactory(
|
||||
window_size, num_dels_trigger, deletion_ratio);
|
||||
opt->rep.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
}
|
||||
|
||||
void rocksdb_set_perf_level(int v) {
|
||||
PerfLevel level = static_cast<PerfLevel>(v);
|
||||
SetPerfLevel(level);
|
||||
@@ -5579,6 +5623,23 @@ int rocksdb_transactiondb_property_int(rocksdb_transactiondb_t* db,
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_transactiondb_get_base_db(
|
||||
rocksdb_transactiondb_t* txn_db) {
|
||||
DB* base_db = txn_db->rep->GetBaseDB();
|
||||
|
||||
if (base_db != nullptr) {
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = base_db;
|
||||
return result;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_close_base_db(rocksdb_t* base_db) {
|
||||
delete base_db;
|
||||
}
|
||||
|
||||
rocksdb_transaction_t* rocksdb_transaction_begin(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* write_options,
|
||||
|
||||
+28
-3
@@ -721,6 +721,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
rocksdb_options_add_compact_on_deletion_collector_factory(options, 10000,
|
||||
10001);
|
||||
rocksdb_options_add_compact_on_deletion_collector_factory_del_ratio(
|
||||
options, 10000, 10001, 0.0);
|
||||
|
||||
StartPhase("destroy");
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
@@ -1672,7 +1674,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_prefix_extractor(
|
||||
options, rocksdb_slicetransform_create_fixed_prefix(3));
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
|
||||
rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
|
||||
rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16, 0, 0, 0,
|
||||
0);
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
|
||||
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
@@ -3119,6 +3122,17 @@ int main(int argc, char** argv) {
|
||||
CheckTxnDBGetCF(txn_db, roptions, cfh, "cf_foo", NULL);
|
||||
CheckTxnDBPinGetCF(txn_db, roptions, cfh, "cf_foo", NULL);
|
||||
|
||||
|
||||
//memory usage
|
||||
rocksdb_t* base_db = rocksdb_transactiondb_get_base_db(txn_db);
|
||||
rocksdb_memory_consumers_t* consumers = rocksdb_memory_consumers_create();
|
||||
rocksdb_memory_consumers_add_db(consumers, base_db);
|
||||
rocksdb_memory_usage_t* usage = rocksdb_approximate_memory_usage_create(consumers, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_approximate_memory_usage_destroy(usage);
|
||||
rocksdb_memory_consumers_destroy(consumers);
|
||||
rocksdb_transactiondb_close_base_db(base_db);
|
||||
|
||||
// flush
|
||||
rocksdb_flushoptions_t* flush_options = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_options, 1);
|
||||
@@ -3374,8 +3388,19 @@ int main(int argc, char** argv) {
|
||||
rocksdb_put(db, woptions, "key", 3, "value", 5, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_column_family_handle_t *cfh1, *cfh2;
|
||||
cfh1 = rocksdb_create_column_family(db, db_options, "txn_db_cf1", &err);
|
||||
cfh2 = rocksdb_create_column_family(db, db_options, "txn_db_cf2", &err);
|
||||
char** list_const_cf_names = (char**)malloc(2 * sizeof(char*));
|
||||
list_const_cf_names[0] = "txn_db_cf1";
|
||||
list_const_cf_names[1] = "txn_db_cf2";
|
||||
size_t cflen;
|
||||
rocksdb_column_family_handle_t** list_cfh = rocksdb_create_column_families(
|
||||
db, db_options, 2, (const char* const*)list_const_cf_names, &cflen,
|
||||
&err);
|
||||
free(list_const_cf_names);
|
||||
CheckNoError(err);
|
||||
assert(cflen == 2);
|
||||
cfh1 = list_cfh[0];
|
||||
cfh2 = list_cfh[1];
|
||||
rocksdb_create_column_families_destroy(list_cfh);
|
||||
txn = rocksdb_optimistictransaction_begin(otxn_db, woptions, otxn_options,
|
||||
NULL);
|
||||
rocksdb_transaction_put_cf(txn, cfh1, "key_cf1", 7, "val_cf1", 7, &err);
|
||||
|
||||
+52
-34
@@ -382,8 +382,8 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
|
||||
const uint64_t kAdjustedTtl = 30 * 24 * 60 * 60;
|
||||
if (result.ttl == kDefaultTtl) {
|
||||
if (is_block_based_table &&
|
||||
result.compaction_style != kCompactionStyleFIFO) {
|
||||
if (is_block_based_table) {
|
||||
// For FIFO, max_open_files is checked in ValidateOptions().
|
||||
result.ttl = kAdjustedTtl;
|
||||
} else {
|
||||
result.ttl = 0;
|
||||
@@ -403,16 +403,12 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
result.periodic_compaction_seconds = kAdjustedPeriodicCompSecs;
|
||||
}
|
||||
} else {
|
||||
// result.compaction_style == kCompactionStyleFIFO
|
||||
if (result.ttl == 0) {
|
||||
if (is_block_based_table) {
|
||||
if (result.periodic_compaction_seconds == kDefaultPeriodicCompSecs) {
|
||||
result.periodic_compaction_seconds = kAdjustedPeriodicCompSecs;
|
||||
}
|
||||
result.ttl = result.periodic_compaction_seconds;
|
||||
}
|
||||
} else if (result.periodic_compaction_seconds != 0) {
|
||||
result.ttl = std::min(result.ttl, result.periodic_compaction_seconds);
|
||||
if (result.periodic_compaction_seconds != kDefaultPeriodicCompSecs &&
|
||||
result.periodic_compaction_seconds > 0) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options.info_log.get(),
|
||||
"periodic_compaction_seconds does not support FIFO compaction. You"
|
||||
"may want to set option TTL instead.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1245,30 +1241,11 @@ SuperVersion* ColumnFamilyData::GetThreadLocalSuperVersion(DBImpl* db) {
|
||||
// (if no Scrape happens).
|
||||
assert(ptr != SuperVersion::kSVInUse);
|
||||
SuperVersion* sv = static_cast<SuperVersion*>(ptr);
|
||||
if (sv == SuperVersion::kSVObsolete ||
|
||||
sv->version_number != super_version_number_.load()) {
|
||||
if (sv == SuperVersion::kSVObsolete) {
|
||||
RecordTick(ioptions_.stats, NUMBER_SUPERVERSION_ACQUIRES);
|
||||
SuperVersion* sv_to_delete = nullptr;
|
||||
|
||||
if (sv && sv->Unref()) {
|
||||
RecordTick(ioptions_.stats, NUMBER_SUPERVERSION_CLEANUPS);
|
||||
db->mutex()->Lock();
|
||||
// NOTE: underlying resources held by superversion (sst files) might
|
||||
// not be released until the next background job.
|
||||
sv->Cleanup();
|
||||
if (db->immutable_db_options().avoid_unnecessary_blocking_io) {
|
||||
db->AddSuperVersionsToFreeQueue(sv);
|
||||
db->SchedulePurge();
|
||||
} else {
|
||||
sv_to_delete = sv;
|
||||
}
|
||||
} else {
|
||||
db->mutex()->Lock();
|
||||
}
|
||||
db->mutex()->Lock();
|
||||
sv = super_version_->Ref();
|
||||
db->mutex()->Unlock();
|
||||
|
||||
delete sv_to_delete;
|
||||
}
|
||||
assert(sv != nullptr);
|
||||
return sv;
|
||||
@@ -1428,6 +1405,37 @@ Status ColumnFamilyData::ValidateOptions(
|
||||
"Memtable per key-value checksum protection only supports 0, 1, 2, 4 "
|
||||
"or 8 bytes per key.");
|
||||
}
|
||||
if (std::find(supported.begin(), supported.end(),
|
||||
cf_options.block_protection_bytes_per_key) == supported.end()) {
|
||||
return Status::NotSupported(
|
||||
"Block per key-value checksum protection only supports 0, 1, 2, 4 "
|
||||
"or 8 bytes per key.");
|
||||
}
|
||||
|
||||
if (!cf_options.compaction_options_fifo.file_temperature_age_thresholds
|
||||
.empty()) {
|
||||
if (cf_options.compaction_style != kCompactionStyleFIFO) {
|
||||
return Status::NotSupported(
|
||||
"Option file_temperature_age_thresholds only supports FIFO "
|
||||
"compaction.");
|
||||
} else if (cf_options.num_levels > 1) {
|
||||
return Status::NotSupported(
|
||||
"Option file_temperature_age_thresholds is only supported when "
|
||||
"num_levels = 1.");
|
||||
} else {
|
||||
const auto& ages =
|
||||
cf_options.compaction_options_fifo.file_temperature_age_thresholds;
|
||||
assert(ages.size() >= 1);
|
||||
// check that age is sorted
|
||||
for (size_t i = 0; i < ages.size() - 1; ++i) {
|
||||
if (ages[i].age >= ages[i + 1].age) {
|
||||
return Status::NotSupported(
|
||||
"Option file_temperature_age_thresholds requires elements to be "
|
||||
"sorted in increasing order with respect to `age` field.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1609,6 +1617,13 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
db_id_, db_session_id_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
auto ucmp = new_cfd->user_comparator();
|
||||
assert(ucmp);
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
running_ts_sz_.insert({id, ts_sz});
|
||||
if (ts_sz > 0) {
|
||||
ts_sz_for_record_.insert({id, ts_sz});
|
||||
}
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
// add to linked list
|
||||
new_cfd->next_ = dummy_cfd_;
|
||||
@@ -1624,10 +1639,13 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
|
||||
// under a DB mutex AND from a write thread
|
||||
void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
|
||||
auto cfd_iter = column_family_data_.find(cfd->GetID());
|
||||
uint32_t cf_id = cfd->GetID();
|
||||
auto cfd_iter = column_family_data_.find(cf_id);
|
||||
assert(cfd_iter != column_family_data_.end());
|
||||
column_family_data_.erase(cfd_iter);
|
||||
column_families_.erase(cfd->GetName());
|
||||
running_ts_sz_.erase(cf_id);
|
||||
ts_sz_for_record_.erase(cf_id);
|
||||
}
|
||||
|
||||
// under a DB mutex OR from a write thread
|
||||
|
||||
@@ -705,6 +705,16 @@ class ColumnFamilySet {
|
||||
Version* dummy_version,
|
||||
const ColumnFamilyOptions& options);
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& GetRunningColumnFamiliesTimestampSize()
|
||||
const {
|
||||
return running_ts_sz_;
|
||||
}
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>&
|
||||
GetColumnFamiliesTimestampSizeForRecord() const {
|
||||
return ts_sz_for_record_;
|
||||
}
|
||||
|
||||
iterator begin() { return iterator(dummy_cfd_->next_); }
|
||||
iterator end() { return iterator(dummy_cfd_); }
|
||||
|
||||
@@ -730,6 +740,15 @@ class ColumnFamilySet {
|
||||
UnorderedMap<std::string, uint32_t> column_families_;
|
||||
UnorderedMap<uint32_t, ColumnFamilyData*> column_family_data_;
|
||||
|
||||
// Mutating / reading `running_ts_sz_` and `ts_sz_for_record_` follow
|
||||
// the same requirements as `column_families_` and `column_family_data_`.
|
||||
// Mapping from column family id to user-defined timestamp size for all
|
||||
// running column families.
|
||||
UnorderedMap<uint32_t, size_t> running_ts_sz_;
|
||||
// Mapping from column family id to user-defined timestamp size for
|
||||
// column families with non-zero user-defined timestamp size.
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_for_record_;
|
||||
|
||||
uint32_t max_column_family_;
|
||||
const FileOptions file_options_;
|
||||
|
||||
|
||||
@@ -1255,6 +1255,7 @@ TEST_P(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
@@ -1272,6 +1273,7 @@ TEST_P(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
two.level_compaction_dynamic_level_bytes = false;
|
||||
two.compaction_style = kCompactionStyleLevel;
|
||||
two.num_levels = 4;
|
||||
two.level0_file_num_compaction_trigger = 3;
|
||||
@@ -1326,6 +1328,7 @@ TEST_P(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
@@ -1342,6 +1345,7 @@ TEST_P(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
two.level_compaction_dynamic_level_bytes = false;
|
||||
two.compaction_style = kCompactionStyleLevel;
|
||||
two.num_levels = 4;
|
||||
two.level0_file_num_compaction_trigger = 3;
|
||||
@@ -1424,13 +1428,14 @@ TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.max_compaction_bytes = default_cf.target_file_size_base * 1100;
|
||||
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||
;
|
||||
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
@@ -1441,6 +1446,7 @@ TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
two.level_compaction_dynamic_level_bytes = false;
|
||||
two.compaction_style = kCompactionStyleLevel;
|
||||
two.num_levels = 4;
|
||||
two.level0_file_num_compaction_trigger = 3;
|
||||
@@ -1519,13 +1525,14 @@ TEST_P(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.max_compaction_bytes = default_cf.target_file_size_base * 1100;
|
||||
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||
;
|
||||
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
@@ -1536,6 +1543,7 @@ TEST_P(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
two.level_compaction_dynamic_level_bytes = false;
|
||||
two.compaction_style = kCompactionStyleLevel;
|
||||
two.num_levels = 4;
|
||||
two.level0_file_num_compaction_trigger = 3;
|
||||
|
||||
@@ -66,6 +66,7 @@ TEST_F(CompactFilesTest, L0ConflictsFiles) {
|
||||
const int kWriteBufferSize = 10000;
|
||||
const int kLevel0Trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
// Small slowdown and stop trigger for experimental purpose.
|
||||
options.level0_slowdown_writes_trigger = 20;
|
||||
@@ -359,6 +360,7 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
std::shared_ptr<FilterWithGet> cf(new FilterWithGet());
|
||||
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_filter = cf.get();
|
||||
|
||||
@@ -401,6 +403,7 @@ TEST_F(CompactFilesTest, SentinelCompressionType) {
|
||||
CompactionStyle::kCompactionStyleNone}) {
|
||||
ASSERT_OK(DestroyDB(db_name_, Options()));
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.compaction_style = compaction_style;
|
||||
// L0: Snappy, L1: ZSTD, L2: Snappy
|
||||
options.compression_per_level = {CompressionType::kSnappyCompression,
|
||||
|
||||
+69
-23
@@ -23,14 +23,13 @@ namespace ROCKSDB_NAMESPACE {
|
||||
const uint64_t kRangeTombstoneSentinel =
|
||||
PackSequenceAndType(kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b) {
|
||||
auto c = user_cmp->CompareWithoutTimestamp(a.user_key(), b.user_key());
|
||||
int sstableKeyCompare(const Comparator* uc, const Slice& a, const Slice& b) {
|
||||
auto c = uc->CompareWithoutTimestamp(ExtractUserKey(a), ExtractUserKey(b));
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
auto a_footer = ExtractInternalKeyFooter(a.Encode());
|
||||
auto b_footer = ExtractInternalKeyFooter(b.Encode());
|
||||
auto a_footer = ExtractInternalKeyFooter(a);
|
||||
auto b_footer = ExtractInternalKeyFooter(b);
|
||||
if (a_footer == kRangeTombstoneSentinel) {
|
||||
if (b_footer != kRangeTombstoneSentinel) {
|
||||
return -1;
|
||||
@@ -465,6 +464,11 @@ bool Compaction::IsTrivialMove() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (compaction_reason_ == CompactionReason::kChangeTemperature) {
|
||||
// Changing temperature usually requires rewriting the file.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Used in universal compaction, where trivial move can be done if the
|
||||
// input files are non overlapping
|
||||
if ((mutable_cf_options_.compaction_options_universal.allow_trivial_move) &&
|
||||
@@ -481,26 +485,25 @@ bool Compaction::IsTrivialMove() const {
|
||||
|
||||
// assert inputs_.size() == 1
|
||||
|
||||
std::unique_ptr<SstPartitioner> partitioner = CreateSstPartitioner();
|
||||
|
||||
for (const auto& file : inputs_.front().files) {
|
||||
std::vector<FileMetaData*> file_grand_parents;
|
||||
if (output_level_ + 1 >= number_levels_) {
|
||||
continue;
|
||||
}
|
||||
input_vstorage_->GetOverlappingInputs(output_level_ + 1, &file->smallest,
|
||||
&file->largest, &file_grand_parents);
|
||||
const auto compaction_size =
|
||||
file->fd.GetFileSize() + TotalFileSize(file_grand_parents);
|
||||
if (compaction_size > max_compaction_bytes_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (partitioner.get() != nullptr) {
|
||||
if (!partitioner->CanDoTrivialMove(file->smallest.user_key(),
|
||||
file->largest.user_key())) {
|
||||
if (output_level_ + 1 < number_levels_) {
|
||||
std::unique_ptr<SstPartitioner> partitioner = CreateSstPartitioner();
|
||||
for (const auto& file : inputs_.front().files) {
|
||||
std::vector<FileMetaData*> file_grand_parents;
|
||||
input_vstorage_->GetOverlappingInputs(output_level_ + 1, &file->smallest,
|
||||
&file->largest,
|
||||
&file_grand_parents);
|
||||
const auto compaction_size =
|
||||
file->fd.GetFileSize() + TotalFileSize(file_grand_parents);
|
||||
if (compaction_size > max_compaction_bytes_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (partitioner.get() != nullptr) {
|
||||
if (!partitioner->CanDoTrivialMove(file->smallest.user_key(),
|
||||
file->largest.user_key())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,6 +561,49 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Compaction::KeyRangeNotExistsBeyondOutputLevel(
|
||||
const Slice& begin_key, const Slice& end_key,
|
||||
std::vector<size_t>* level_ptrs) const {
|
||||
assert(input_version_ != nullptr);
|
||||
assert(level_ptrs != nullptr);
|
||||
assert(level_ptrs->size() == static_cast<size_t>(number_levels_));
|
||||
assert(cfd_->user_comparator()->CompareWithoutTimestamp(begin_key, end_key) <
|
||||
0);
|
||||
if (bottommost_level_) {
|
||||
return true /* does not overlap */;
|
||||
} else if (output_level_ != 0 &&
|
||||
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
const std::vector<FileMetaData*>& files =
|
||||
input_vstorage_->LevelFiles(lvl);
|
||||
for (; level_ptrs->at(lvl) < files.size(); level_ptrs->at(lvl)++) {
|
||||
auto* f = files[level_ptrs->at(lvl)];
|
||||
// Advance until the first file with begin_key <= f->largest.user_key()
|
||||
if (user_cmp->CompareWithoutTimestamp(begin_key,
|
||||
f->largest.user_key()) > 0) {
|
||||
continue;
|
||||
}
|
||||
// We know that the previous file prev_f, if exists, has
|
||||
// prev_f->largest.user_key() < begin_key.
|
||||
if (user_cmp->CompareWithoutTimestamp(end_key,
|
||||
f->smallest.user_key()) <= 0) {
|
||||
// not overlapping with this level
|
||||
break;
|
||||
} else {
|
||||
// We have:
|
||||
// - begin_key < end_key,
|
||||
// - begin_key <= f->largest.user_key(), and
|
||||
// - end_key > f->smallest.user_key()
|
||||
return false /* overlap */;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true /* does not overlap */;
|
||||
}
|
||||
return false /* overlaps */;
|
||||
};
|
||||
|
||||
// Mark (or clear) each file that is being compacted
|
||||
void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) {
|
||||
for (size_t i = 0; i < num_input_levels(); i++) {
|
||||
|
||||
@@ -31,8 +31,19 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// that key never appears in the database. We don't want adjacent sstables to
|
||||
// be considered overlapping if they are separated by the range tombstone
|
||||
// sentinel.
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b);
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const Slice&, const Slice&);
|
||||
inline int sstableKeyCompare(const Comparator* user_cmp, const Slice& a,
|
||||
const InternalKey& b) {
|
||||
return sstableKeyCompare(user_cmp, a, b.Encode());
|
||||
}
|
||||
inline int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const Slice& b) {
|
||||
return sstableKeyCompare(user_cmp, a.Encode(), b);
|
||||
}
|
||||
inline int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
const InternalKey& b) {
|
||||
return sstableKeyCompare(user_cmp, a.Encode(), b.Encode());
|
||||
}
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey* a,
|
||||
const InternalKey& b);
|
||||
int sstableKeyCompare(const Comparator* user_cmp, const InternalKey& a,
|
||||
@@ -203,10 +214,18 @@ class Compaction {
|
||||
void AddInputDeletions(VersionEdit* edit);
|
||||
|
||||
// Returns true if the available information we have guarantees that
|
||||
// the input "user_key" does not exist in any level beyond "output_level()".
|
||||
// the input "user_key" does not exist in any level beyond `output_level()`.
|
||||
bool KeyNotExistsBeyondOutputLevel(const Slice& user_key,
|
||||
std::vector<size_t>* level_ptrs) const;
|
||||
|
||||
// Returns true if the user key range [begin_key, end_key) does not exist
|
||||
// in any level beyond `output_level()`.
|
||||
// Used for checking range tombstones, so we assume begin_key < end_key.
|
||||
// begin_key and end_key should include timestamp if enabled.
|
||||
bool KeyRangeNotExistsBeyondOutputLevel(
|
||||
const Slice& begin_key, const Slice& end_key,
|
||||
std::vector<size_t>* level_ptrs) const;
|
||||
|
||||
// Clear all files to indicate that they are not being compacted
|
||||
// Delete this compaction from the list of running compactions.
|
||||
//
|
||||
|
||||
@@ -260,7 +260,7 @@ void CompactionJob::Prepare() {
|
||||
StopWatch sw(db_options_.clock, stats_, SUBCOMPACTION_SETUP_TIME);
|
||||
GenSubcompactionBoundaries();
|
||||
}
|
||||
if (boundaries_.size() > 1) {
|
||||
if (boundaries_.size() >= 1) {
|
||||
for (size_t i = 0; i <= boundaries_.size(); i++) {
|
||||
compact_->sub_compact_states.emplace_back(
|
||||
c, (i != 0) ? std::optional<Slice>(boundaries_[i - 1]) : std::nullopt,
|
||||
@@ -504,7 +504,9 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
FileMetaData* f = flevel->files[i].file_metadata;
|
||||
std::vector<TableReader::Anchor> my_anchors;
|
||||
Status s = cfd->table_cache()->ApproximateKeyAnchors(
|
||||
read_options, icomp, *f, my_anchors);
|
||||
read_options, icomp, *f,
|
||||
c->mutable_cf_options()->block_protection_bytes_per_key,
|
||||
my_anchors);
|
||||
if (!s.ok() || my_anchors.empty()) {
|
||||
my_anchors.emplace_back(f->largest.user_key(), f->fd.GetFileSize());
|
||||
}
|
||||
@@ -735,7 +737,9 @@ Status CompactionJob::Run() {
|
||||
*compact_->compaction->mutable_cf_options()),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
/*allow_unprepared_value=*/false,
|
||||
compact_->compaction->mutable_cf_options()
|
||||
->block_protection_bytes_per_key);
|
||||
auto s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
@@ -804,8 +808,8 @@ Status CompactionJob::Run() {
|
||||
RecordCompactionIOStats();
|
||||
LogFlush(db_options_.info_log);
|
||||
TEST_SYNC_POINT("CompactionJob::Run():End");
|
||||
|
||||
compact_->status = status;
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():EndStatusSet", &status);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1092,17 +1096,17 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
// GenSubcompactionBoundaries doesn't strip away the timestamp.
|
||||
size_t ts_sz = cfd->user_comparator()->timestamp_size();
|
||||
if (start.has_value()) {
|
||||
read_options.iterate_lower_bound = &start.value();
|
||||
read_options.iterate_lower_bound = &(*start);
|
||||
if (ts_sz > 0) {
|
||||
start_without_ts = StripTimestampFromUserKey(start.value(), ts_sz);
|
||||
read_options.iterate_lower_bound = &start_without_ts.value();
|
||||
start_without_ts = StripTimestampFromUserKey(*start, ts_sz);
|
||||
read_options.iterate_lower_bound = &(*start_without_ts);
|
||||
}
|
||||
}
|
||||
if (end.has_value()) {
|
||||
read_options.iterate_upper_bound = &end.value();
|
||||
read_options.iterate_upper_bound = &(*end);
|
||||
if (ts_sz > 0) {
|
||||
end_without_ts = StripTimestampFromUserKey(end.value(), ts_sz);
|
||||
read_options.iterate_upper_bound = &end_without_ts.value();
|
||||
end_without_ts = StripTimestampFromUserKey(*end, ts_sz);
|
||||
read_options.iterate_upper_bound = &(*end_without_ts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1134,8 +1138,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
if (start.has_value()) {
|
||||
start_ikey.SetInternalKey(start.value(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
start_ikey.SetInternalKey(*start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
if (ts_sz > 0) {
|
||||
start_ikey.UpdateInternalKey(kMaxSequenceNumber, kValueTypeForSeek,
|
||||
&ts_slice);
|
||||
@@ -1144,7 +1147,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
start_user_key = start_ikey.GetUserKey();
|
||||
}
|
||||
if (end.has_value()) {
|
||||
end_ikey.SetInternalKey(end.value(), kMaxSequenceNumber, kValueTypeForSeek);
|
||||
end_ikey.SetInternalKey(*end, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
if (ts_sz > 0) {
|
||||
end_ikey.UpdateInternalKey(kMaxSequenceNumber, kValueTypeForSeek,
|
||||
&ts_slice);
|
||||
@@ -1285,8 +1288,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
|
||||
// Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
|
||||
// returns true.
|
||||
assert(!end.has_value() || cfd->user_comparator()->Compare(
|
||||
c_iter->user_key(), end.value()) < 0);
|
||||
assert(!end.has_value() ||
|
||||
cfd->user_comparator()->Compare(c_iter->user_key(), *end) < 0);
|
||||
|
||||
if (c_iter_stats.num_input_records % kRecordStatsEvery ==
|
||||
kRecordStatsEvery - 1) {
|
||||
@@ -1814,10 +1817,10 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
|
||||
uint64_t current_time = static_cast<uint64_t>(temp_current_time);
|
||||
InternalKey tmp_start, tmp_end;
|
||||
if (sub_compact->start.has_value()) {
|
||||
tmp_start.SetMinPossibleForUserKey(sub_compact->start.value());
|
||||
tmp_start.SetMinPossibleForUserKey(*(sub_compact->start));
|
||||
}
|
||||
if (sub_compact->end.has_value()) {
|
||||
tmp_end.SetMinPossibleForUserKey(sub_compact->end.value());
|
||||
tmp_end.SetMinPossibleForUserKey(*(sub_compact->end));
|
||||
}
|
||||
uint64_t oldest_ancester_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime(
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
@@ -54,7 +54,7 @@
|
||||
#include "util/compression.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/rate_limiter_impl.h"
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
@@ -616,6 +616,7 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
// via AddExpectedStats().
|
||||
auto* stats_checker = new CompactionJobStatsChecker();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.listeners.emplace_back(stats_checker);
|
||||
options.create_if_missing = true;
|
||||
// just enough setting to hold off auto-compaction.
|
||||
@@ -815,6 +816,7 @@ TEST_P(CompactionJobStatsTest, DeletionStatsTest) {
|
||||
// what we expect.
|
||||
auto* stats_checker = new CompactionJobDeletionStatsChecker();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.listeners.emplace_back(stats_checker);
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = kTestScale + 1;
|
||||
|
||||
@@ -373,7 +373,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
} else if (table_type_ == TableTypeForTest::kMockTable) {
|
||||
file_size = 10;
|
||||
EXPECT_OK(mock_table_factory_->CreateMockTable(
|
||||
env_, GenerateFileName(file_number), std::move(contents)));
|
||||
env_, GenerateFileName(file_number), contents));
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
@@ -386,7 +386,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
kUnknownFileCreationTime,
|
||||
versions_->GetColumnFamilySet()->GetDefault()->NewEpochNumber(),
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2,
|
||||
0);
|
||||
/*compensated_range_deletion_size=*/0, /*tail_size=*/0,
|
||||
/*user_defined_timestamps_persisted=*/true);
|
||||
|
||||
mutex_.Lock();
|
||||
EXPECT_OK(versions_->LogAndApply(
|
||||
@@ -454,7 +455,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
Status s = cf_options_.table_factory->NewTableReader(
|
||||
read_opts,
|
||||
TableReaderOptions(*cfd->ioptions(), nullptr, FileOptions(),
|
||||
cfd_->internal_comparator()),
|
||||
cfd_->internal_comparator(),
|
||||
0 /* block_protection_bytes_per_key */),
|
||||
std::move(freader), file_size, &table_reader, false);
|
||||
ASSERT_OK(s);
|
||||
assert(table_reader);
|
||||
|
||||
@@ -43,7 +43,10 @@ Status CompactionOutputs::Finish(const Status& intput_status,
|
||||
const uint64_t current_bytes = builder_->FileSize();
|
||||
if (s.ok()) {
|
||||
meta->fd.file_size = current_bytes;
|
||||
meta->tail_size = builder_->GetTailSize();
|
||||
meta->marked_for_compaction = builder_->NeedCompact();
|
||||
meta->user_defined_timestamps_persisted = static_cast<bool>(
|
||||
builder_->GetTableProperties().user_defined_timestamps_persisted);
|
||||
}
|
||||
current_output().finished = true;
|
||||
stats_.bytes_written += current_bytes;
|
||||
@@ -124,11 +127,6 @@ size_t CompactionOutputs::UpdateGrandparentBoundaryInfo(
|
||||
if (grandparents.empty()) {
|
||||
return curr_key_boundary_switched_num;
|
||||
}
|
||||
assert(!internal_key.empty());
|
||||
InternalKey ikey;
|
||||
ikey.DecodeFrom(internal_key);
|
||||
assert(ikey.Valid());
|
||||
|
||||
const Comparator* ucmp = compaction_->column_family_data()->user_comparator();
|
||||
|
||||
// Move the grandparent_index_ to the file containing the current user_key.
|
||||
@@ -136,7 +134,7 @@ size_t CompactionOutputs::UpdateGrandparentBoundaryInfo(
|
||||
// index points to the last file containing the key.
|
||||
while (grandparent_index_ < grandparents.size()) {
|
||||
if (being_grandparent_gap_) {
|
||||
if (sstableKeyCompare(ucmp, ikey,
|
||||
if (sstableKeyCompare(ucmp, internal_key,
|
||||
grandparents[grandparent_index_]->smallest) < 0) {
|
||||
break;
|
||||
}
|
||||
@@ -149,13 +147,13 @@ size_t CompactionOutputs::UpdateGrandparentBoundaryInfo(
|
||||
being_grandparent_gap_ = false;
|
||||
} else {
|
||||
int cmp_result = sstableKeyCompare(
|
||||
ucmp, ikey, grandparents[grandparent_index_]->largest);
|
||||
ucmp, internal_key, grandparents[grandparent_index_]->largest);
|
||||
// If it's same key, make sure grandparent_index_ is pointing to the last
|
||||
// one.
|
||||
if (cmp_result < 0 ||
|
||||
(cmp_result == 0 &&
|
||||
(grandparent_index_ == grandparents.size() - 1 ||
|
||||
sstableKeyCompare(ucmp, ikey,
|
||||
sstableKeyCompare(ucmp, internal_key,
|
||||
grandparents[grandparent_index_ + 1]->smallest) <
|
||||
0))) {
|
||||
break;
|
||||
@@ -597,10 +595,12 @@ Status CompactionOutputs::AddRangeDels(
|
||||
// in any snapshot. trim_ts_ is passed to the constructor for
|
||||
// range_del_agg_, and range_del_agg_ internally drops tombstones above
|
||||
// trim_ts_.
|
||||
if (bottommost_level && tombstone.seq_ <= earliest_snapshot &&
|
||||
bool consider_drop =
|
||||
tombstone.seq_ <= earliest_snapshot &&
|
||||
(ts_sz == 0 ||
|
||||
(!full_history_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(tombstone.ts_, full_history_ts_low) < 0))) {
|
||||
ucmp->CompareTimestamp(tombstone.ts_, full_history_ts_low) < 0));
|
||||
if (consider_drop && bottommost_level) {
|
||||
// TODO(andrewkr): tombstones that span multiple output files are
|
||||
// counted for each compaction output file, so lots of double
|
||||
// counting.
|
||||
@@ -634,6 +634,20 @@ Status CompactionOutputs::AddRangeDels(
|
||||
icmp.Compare(*upper_bound, tombstone_start.Encode()) < 0) {
|
||||
break;
|
||||
}
|
||||
if (lower_bound &&
|
||||
icmp.Compare(tombstone_start.Encode(), *lower_bound) < 0) {
|
||||
tombstone_start.DecodeFrom(*lower_bound);
|
||||
}
|
||||
if (upper_bound && icmp.Compare(*upper_bound, tombstone_end.Encode()) < 0) {
|
||||
tombstone_end.DecodeFrom(*upper_bound);
|
||||
}
|
||||
if (consider_drop && compaction_->KeyRangeNotExistsBeyondOutputLevel(
|
||||
tombstone_start.user_key(),
|
||||
tombstone_end.user_key(), &level_ptrs_)) {
|
||||
range_del_out_stats.num_range_del_drop_obsolete++;
|
||||
range_del_out_stats.num_record_drop_obsolete++;
|
||||
continue;
|
||||
}
|
||||
// Here we show that *only* range tombstones that overlap with
|
||||
// [lower_bound, upper_bound] are added to the current file, and
|
||||
// sanity checking invariants that should hold:
|
||||
@@ -687,13 +701,6 @@ Status CompactionOutputs::AddRangeDels(
|
||||
|
||||
// Range tombstone is not supported by output validator yet.
|
||||
builder_->Add(kv.first.Encode(), kv.second);
|
||||
if (lower_bound &&
|
||||
icmp.Compare(tombstone_start.Encode(), *lower_bound) < 0) {
|
||||
tombstone_start.DecodeFrom(*lower_bound);
|
||||
}
|
||||
if (upper_bound && icmp.Compare(*upper_bound, tombstone_end.Encode()) < 0) {
|
||||
tombstone_end.DecodeFrom(*upper_bound);
|
||||
}
|
||||
assert(icmp.Compare(tombstone_start, tombstone_end) <= 0);
|
||||
meta.UpdateBoundariesForRange(tombstone_start, tombstone_end,
|
||||
tombstone.seq_, icmp);
|
||||
@@ -778,6 +785,8 @@ CompactionOutputs::CompactionOutputs(const Compaction* compaction,
|
||||
if (compaction->output_level() != 0) {
|
||||
FillFilesToCutForTtl();
|
||||
}
|
||||
|
||||
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -206,10 +206,10 @@ class CompactionOutputs {
|
||||
// We may only split the output when the cursor is in the range. Split
|
||||
if ((!end.has_value() ||
|
||||
icmp->user_comparator()->Compare(
|
||||
ExtractUserKey(output_split_key->Encode()), end.value()) < 0) &&
|
||||
(!start.has_value() || icmp->user_comparator()->Compare(
|
||||
ExtractUserKey(output_split_key->Encode()),
|
||||
start.value()) > 0)) {
|
||||
ExtractUserKey(output_split_key->Encode()), *end) < 0) &&
|
||||
(!start.has_value() ||
|
||||
icmp->user_comparator()->Compare(
|
||||
ExtractUserKey(output_split_key->Encode()), *start) > 0)) {
|
||||
local_output_split_key_ = output_split_key;
|
||||
}
|
||||
}
|
||||
@@ -356,6 +356,15 @@ class CompactionOutputs {
|
||||
// The smallest key of the current output file, this is set when current
|
||||
// output file's smallest key is a range tombstone start key.
|
||||
InternalKey range_tombstone_lower_bound_;
|
||||
|
||||
// Used for calls to compaction->KeyRangeNotExistsBeyondOutputLevel() in
|
||||
// CompactionOutputs::AddRangeDels().
|
||||
// level_ptrs_[i] holds index of the file that was checked during the last
|
||||
// call to compaction->KeyRangeNotExistsBeyondOutputLevel(). This allows
|
||||
// future calls to the function to pick up where it left off, since each
|
||||
// range tombstone added to output file within each subcompaction is in
|
||||
// increasing key range.
|
||||
std::vector<size_t> level_ptrs_;
|
||||
};
|
||||
|
||||
// helper struct to concatenate the last level and penultimate level outputs
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "file/filename.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -611,23 +611,21 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// Universal compaction with more than one level always compacts all the
|
||||
// files together to the last level.
|
||||
assert(vstorage->num_levels() > 1);
|
||||
int max_output_level =
|
||||
vstorage->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
// DBImpl::CompactRange() set output level to be the last level
|
||||
if (ioptions_.allow_ingest_behind) {
|
||||
assert(output_level == vstorage->num_levels() - 2);
|
||||
} else {
|
||||
assert(output_level == vstorage->num_levels() - 1);
|
||||
}
|
||||
assert(output_level == max_output_level);
|
||||
// DBImpl::RunManualCompaction will make full range for universal compaction
|
||||
assert(begin == nullptr);
|
||||
assert(end == nullptr);
|
||||
*compaction_end = nullptr;
|
||||
|
||||
int start_level = 0;
|
||||
for (; start_level < vstorage->num_levels() &&
|
||||
for (; start_level <= max_output_level &&
|
||||
vstorage->NumLevelFiles(start_level) == 0;
|
||||
start_level++) {
|
||||
}
|
||||
if (start_level == vstorage->num_levels()) {
|
||||
if (start_level > max_output_level) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -637,9 +635,9 @@ Compaction* CompactionPicker::CompactRange(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage->num_levels() -
|
||||
std::vector<CompactionInputFiles> inputs(max_output_level + 1 -
|
||||
start_level);
|
||||
for (int level = start_level; level < vstorage->num_levels(); level++) {
|
||||
for (int level = start_level; level <= max_output_level; level++) {
|
||||
inputs[level - start_level].level = level;
|
||||
auto& files = inputs[level - start_level].files;
|
||||
for (FileMetaData* f : vstorage->LevelFiles(level)) {
|
||||
@@ -753,8 +751,10 @@ Compaction* CompactionPicker::CompactRange(
|
||||
|
||||
// for BOTTOM LEVEL compaction only, use max_file_num_to_ignore to filter out
|
||||
// files that are created during the current compaction.
|
||||
if (compact_range_options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForceOptimized &&
|
||||
if ((compact_range_options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForceOptimized ||
|
||||
compact_range_options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter) &&
|
||||
max_file_num_to_ignore != std::numeric_limits<uint64_t>::max()) {
|
||||
assert(input_level == output_level);
|
||||
// inputs_shrunk holds a continuous subset of input files which were all
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "db/column_family.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -284,31 +285,36 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompactionToWarm(
|
||||
Compaction* FIFOCompactionPicker::PickTemperatureChangeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
if (mutable_cf_options.compaction_options_fifo.age_for_warm == 0) {
|
||||
const std::vector<FileTemperatureAge>& ages =
|
||||
mutable_cf_options.compaction_options_fifo
|
||||
.file_temperature_age_thresholds;
|
||||
if (ages.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// PickCompactionToWarm is only triggered if there is no non-L0 files.
|
||||
for (int level = 1; level < vstorage->num_levels(); ++level) {
|
||||
if (GetTotalFilesSize(vstorage->LevelFiles(level)) > 0) {
|
||||
return nullptr;
|
||||
}
|
||||
// Does not apply to multi-level FIFO.
|
||||
if (vstorage->num_levels() > 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
if (level_files.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions_.clock->GetCurrentTime(&_current_time);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: Couldn't get current time: %s. "
|
||||
"Not doing compactions based on warm threshold. ",
|
||||
cf_name.c_str(), status.ToString().c_str());
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: Couldn't get current time: %s. "
|
||||
"Not doing compactions based on file temperature-age threshold. ",
|
||||
cf_name.c_str(), status.ToString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
@@ -327,56 +333,77 @@ Compaction* FIFOCompactionPicker::PickCompactionToWarm(
|
||||
inputs[0].level = 0;
|
||||
|
||||
// avoid underflow
|
||||
if (current_time > mutable_cf_options.compaction_options_fifo.age_for_warm) {
|
||||
uint64_t create_time_threshold =
|
||||
current_time - mutable_cf_options.compaction_options_fifo.age_for_warm;
|
||||
uint64_t min_age = ages[0].age;
|
||||
// kLastTemperature means target temperature is to be determined.
|
||||
Temperature compaction_target_temp = Temperature::kLastTemperature;
|
||||
if (current_time > min_age) {
|
||||
uint64_t create_time_threshold = current_time - min_age;
|
||||
uint64_t compaction_size = 0;
|
||||
// We will ideally identify a file qualifying for warm tier by knowing
|
||||
// the timestamp for the youngest entry in the file. However, right now
|
||||
// we don't have the information. We infer it by looking at timestamp
|
||||
// of the next file's (which is just younger) oldest entry's timestamp.
|
||||
FileMetaData* prev_file = nullptr;
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
FileMetaData* f = *ritr;
|
||||
assert(f);
|
||||
if (f->being_compacted) {
|
||||
// Right now this probably won't happen as we never try to schedule
|
||||
// two compactions in parallel, so here we just simply don't schedule
|
||||
// anything.
|
||||
// We will ideally identify a file qualifying for temperature change by
|
||||
// knowing the timestamp for the youngest entry in the file. However, right
|
||||
// now we don't have the information. We infer it by looking at timestamp of
|
||||
// the previous file's (which is just younger) oldest entry's timestamp.
|
||||
Temperature cur_target_temp;
|
||||
// avoid index underflow
|
||||
assert(level_files.size() >= 1);
|
||||
for (size_t index = level_files.size() - 1; index >= 1; --index) {
|
||||
// Try to add cur_file to compaction inputs.
|
||||
FileMetaData* cur_file = level_files[index];
|
||||
// prev_file is just younger than cur_file
|
||||
FileMetaData* prev_file = level_files[index - 1];
|
||||
if (cur_file->being_compacted) {
|
||||
// Should not happen since we check for
|
||||
// `level0_compactions_in_progress_` above. Here we simply just don't
|
||||
// schedule anything.
|
||||
return nullptr;
|
||||
}
|
||||
uint64_t oldest_ancester_time = f->TryGetOldestAncesterTime();
|
||||
if (oldest_ancester_time == kUnknownOldestAncesterTime) {
|
||||
uint64_t oldest_ancestor_time = prev_file->TryGetOldestAncesterTime();
|
||||
if (oldest_ancestor_time == kUnknownOldestAncesterTime) {
|
||||
// Older files might not have enough information. It is possible to
|
||||
// handle these files by looking at newer files, but maintaining the
|
||||
// logic isn't worth it.
|
||||
break;
|
||||
}
|
||||
if (oldest_ancester_time > create_time_threshold) {
|
||||
// The previous file (which has slightly older data) doesn't qualify
|
||||
// for warm tier.
|
||||
if (oldest_ancestor_time > create_time_threshold) {
|
||||
// cur_file is too fresh
|
||||
break;
|
||||
}
|
||||
if (prev_file != nullptr) {
|
||||
compaction_size += prev_file->fd.GetFileSize();
|
||||
if (compaction_size > mutable_cf_options.max_compaction_bytes) {
|
||||
cur_target_temp = ages[0].temperature;
|
||||
for (size_t i = 1; i < ages.size(); ++i) {
|
||||
if (current_time >= ages[i].age &&
|
||||
oldest_ancestor_time <= current_time - ages[i].age) {
|
||||
cur_target_temp = ages[i].temperature;
|
||||
}
|
||||
}
|
||||
if (cur_file->temperature == cur_target_temp) {
|
||||
if (inputs[0].empty()) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
inputs[0].files.push_back(prev_file);
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with next file's oldest time %" PRIu64 " for warm",
|
||||
cf_name.c_str(), prev_file->fd.GetNumber(),
|
||||
oldest_ancester_time);
|
||||
}
|
||||
if (f->temperature == Temperature::kUnknown ||
|
||||
f->temperature == Temperature::kHot) {
|
||||
prev_file = f;
|
||||
} else if (!inputs[0].files.empty()) {
|
||||
// A warm file newer than files picked.
|
||||
|
||||
// cur_file needs to change temperature
|
||||
if (compaction_target_temp == Temperature::kLastTemperature) {
|
||||
assert(inputs[0].empty());
|
||||
compaction_target_temp = cur_target_temp;
|
||||
} else if (cur_target_temp != compaction_target_temp) {
|
||||
assert(!inputs[0].empty());
|
||||
break;
|
||||
}
|
||||
if (inputs[0].empty() || compaction_size + cur_file->fd.GetFileSize() <=
|
||||
mutable_cf_options.max_compaction_bytes) {
|
||||
inputs[0].files.push_back(cur_file);
|
||||
compaction_size += cur_file->fd.GetFileSize();
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with next file's oldest time %" PRIu64 " for temperature %s.",
|
||||
cf_name.c_str(), cur_file->fd.GetNumber(), oldest_ancestor_time,
|
||||
temperature_to_string[cur_target_temp].c_str());
|
||||
}
|
||||
if (compaction_size > mutable_cf_options.max_compaction_bytes) {
|
||||
break;
|
||||
} else {
|
||||
assert(prev_file == nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,7 +417,7 @@ Compaction* FIFOCompactionPicker::PickCompactionToWarm(
|
||||
std::move(inputs), 0, 0 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */, 0 /* output path ID */,
|
||||
mutable_cf_options.compression, mutable_cf_options.compression_opts,
|
||||
Temperature::kWarm,
|
||||
compaction_target_temp,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "",
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false, /* l0_files_might_overlap */ true,
|
||||
@@ -412,8 +439,8 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
vstorage, log_buffer);
|
||||
}
|
||||
if (c == nullptr) {
|
||||
c = PickCompactionToWarm(cf_name, mutable_cf_options, mutable_db_options,
|
||||
vstorage, log_buffer);
|
||||
c = PickTemperatureChangeCompaction(
|
||||
cf_name, mutable_cf_options, mutable_db_options, vstorage, log_buffer);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
|
||||
@@ -52,10 +52,9 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
Compaction* PickCompactionToWarm(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
Compaction* PickTemperatureChangeCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer);
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -70,6 +70,11 @@ class CompactionPickerTestBase : public testing::Test {
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
ioptions_.cf_paths.emplace_back("dummy",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
// When the default value of this option is true, universal compaction
|
||||
// tests can encounter assertion failure since SanitizeOption() is
|
||||
// not run to set this option to false. So we do the sanitization
|
||||
// here. Tests that test this option set this option to true explicitly.
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
}
|
||||
|
||||
~CompactionPickerTestBase() override {}
|
||||
@@ -148,7 +153,8 @@ class CompactionPickerTestBase : public testing::Test {
|
||||
smallest_seq, largest_seq, marked_for_compact, temperature,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, epoch_number, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
|
||||
true /* user_defined_timestamps_persisted */);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->oldest_ancester_time = oldest_ancestor_time;
|
||||
@@ -505,7 +511,7 @@ TEST_F(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionUniversalIngestBehindReservedLevel) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
NewVersionStorage(3 /* num_levels */, kCompactionStyleUniversal);
|
||||
ioptions_.allow_ingest_behind = true;
|
||||
ioptions_.num_levels = 3;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
@@ -532,6 +538,14 @@ TEST_F(CompactionPickerTest, CompactionUniversalIngestBehindReservedLevel) {
|
||||
|
||||
// output level should be the one above the bottom-most
|
||||
ASSERT_EQ(1, compaction->output_level());
|
||||
|
||||
// input should not include the reserved level
|
||||
const std::vector<CompactionInputFiles>* inputs = compaction->inputs();
|
||||
for (const auto& compaction_input : *inputs) {
|
||||
if (!compaction_input.empty()) {
|
||||
ASSERT_LT(compaction_input.level, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tests if the files can be trivially moved in multi level
|
||||
// universal compaction when allow_trivial_move option is set
|
||||
@@ -1005,29 +1019,28 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarm1) {
|
||||
TEST_F(CompactionPickerTest, FIFOToCold1) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
uint64_t kColdThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
fifo_options_.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, kColdThreshold}};
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0 /* level */, 4U /* file_number */, "260", "300", 1 * kFileSize, 0, 2500,
|
||||
2600, 0, true, Temperature::kUnknown,
|
||||
threshold_time - 2000 /* oldest_ancestor_time */);
|
||||
// Qualifies for compaction to kCold.
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
UpdateVersionStorageInfo();
|
||||
@@ -1037,33 +1050,36 @@ TEST_F(CompactionPickerTest, FIFOToWarm1) {
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(compaction->compaction_reason(),
|
||||
CompactionReason::kChangeTemperature);
|
||||
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(3U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarm2) {
|
||||
TEST_F(CompactionPickerTest, FIFOToCold2) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
uint64_t kColdThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
fifo_options_.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, kColdThreshold}};
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Temperature::kUnknown, threshold_time);
|
||||
// The following two files qualify for compaction to kCold.
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
@@ -1075,34 +1091,40 @@ TEST_F(CompactionPickerTest, FIFOToWarm2) {
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(compaction->compaction_reason(),
|
||||
CompactionReason::kChangeTemperature);
|
||||
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarmMaxSize) {
|
||||
TEST_F(CompactionPickerTest, FIFOToColdMaxCompactionSize) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
uint64_t kColdThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
fifo_options_.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, kColdThreshold}};
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 9;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
// The following two files qualify for compaction to kCold.
|
||||
// But only the last two should be included to respect `max_compaction_bytes`.
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
@@ -1116,40 +1138,45 @@ TEST_F(CompactionPickerTest, FIFOToWarmMaxSize) {
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(compaction->compaction_reason(),
|
||||
CompactionReason::kChangeTemperature);
|
||||
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarmWithExistingWarm) {
|
||||
TEST_F(CompactionPickerTest, FIFOToColdWithExistingCold) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
uint64_t kColdThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
fifo_options_.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, kColdThreshold}};
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
// The following two files qualify for compaction to kCold.
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 4000);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kWarm, threshold_time - 5000);
|
||||
Temperature::kCold, threshold_time - 5000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
@@ -1157,28 +1184,32 @@ TEST_F(CompactionPickerTest, FIFOToWarmWithExistingWarm) {
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(compaction->compaction_reason(),
|
||||
CompactionReason::kChangeTemperature);
|
||||
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(3U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarmWithOngoing) {
|
||||
TEST_F(CompactionPickerTest, FIFOToColdWithHotBetweenCold) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
uint64_t kColdThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
fifo_options_.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, kColdThreshold}};
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
static_cast<uint64_t>(current_time) - kColdThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
@@ -1186,63 +1217,77 @@ TEST_F(CompactionPickerTest, FIFOToWarmWithOngoing) {
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
Temperature::kCold, threshold_time - 3000);
|
||||
// Qualifies for compaction to kCold.
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 4000);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kWarm, threshold_time - 5000);
|
||||
file_map_[2].first->being_compacted = true;
|
||||
Temperature::kCold, threshold_time - 5000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
// Stop if a file is being compacted
|
||||
ASSERT_TRUE(compaction.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarmWithHotBetweenWarms) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kWarm, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 4000);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kWarm, threshold_time - 5000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
// Stop if a file is being compacted
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(compaction->compaction_reason(),
|
||||
CompactionReason::kChangeTemperature);
|
||||
ASSERT_EQ(compaction->output_temperature(), Temperature::kCold);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToColdAndWarm) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 10000;
|
||||
uint64_t kHotThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
// Test that multiple threshold works.
|
||||
fifo_options_.file_temperature_age_thresholds = {
|
||||
{Temperature::kHot, kHotThreshold}, {Temperature::kWarm, kWarmThreshold}};
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t hot_threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kHotThreshold;
|
||||
uint64_t warm_threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, hot_threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, hot_threshold_time - 200);
|
||||
// Qualifies for Hot
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, warm_threshold_time - 100);
|
||||
// Qualifies for Warm
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, warm_threshold_time - 4000);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kUnknown, warm_threshold_time - 5000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(compaction->compaction_reason(),
|
||||
CompactionReason::kChangeTemperature);
|
||||
// Assumes compaction picker picks older files first.
|
||||
ASSERT_EQ(compaction->output_temperature(), Temperature::kWarm);
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "file/filename.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -133,8 +133,8 @@ class UniversalCompactionBuilder {
|
||||
UniversalCompactionPicker* picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage);
|
||||
static std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -339,13 +339,13 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage) {
|
||||
const VersionStorageInfo& vstorage, int last_level) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
}
|
||||
for (int level = 1; level < vstorage.num_levels(); level++) {
|
||||
for (int level = 1; level <= last_level; level++) {
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
@@ -374,7 +374,9 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
const int kLevel0 = 0;
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_);
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_, max_output_level);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
@@ -471,6 +473,8 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
assert(c->output_level() <=
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind));
|
||||
|
||||
if (mutable_cf_options_.compaction_options_universal.allow_trivial_move ==
|
||||
true &&
|
||||
@@ -698,22 +702,18 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
GetPathId(ioptions_, mutable_cf_options_, estimated_total_size);
|
||||
int start_level = sorted_runs_[start_index].level;
|
||||
int output_level;
|
||||
// last level is reserved for the files ingested behind
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
if (first_index_after == sorted_runs_.size()) {
|
||||
output_level = vstorage_->num_levels() - 1;
|
||||
output_level = max_output_level;
|
||||
} else if (sorted_runs_[first_index_after].level == 0) {
|
||||
output_level = 0;
|
||||
} else {
|
||||
output_level = sorted_runs_[first_index_after].level - 1;
|
||||
}
|
||||
|
||||
// last level is reserved for the files ingested behind
|
||||
if (ioptions_.allow_ingest_behind &&
|
||||
(output_level == vstorage_->num_levels() - 1)) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage_->num_levels());
|
||||
std::vector<CompactionInputFiles> inputs(max_output_level + 1);
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
inputs[i].level = start_level + static_cast<int>(i);
|
||||
}
|
||||
@@ -1192,8 +1192,10 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
// Pick the first non-empty level after the start_level
|
||||
for (output_level = start_level + 1; output_level < vstorage_->num_levels();
|
||||
for (output_level = start_level + 1; output_level <= max_output_level;
|
||||
output_level++) {
|
||||
if (vstorage_->NumLevelFiles(output_level) != 0) {
|
||||
break;
|
||||
@@ -1201,9 +1203,9 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
}
|
||||
|
||||
// If all higher levels are empty, pick the highest level as output level
|
||||
if (output_level == vstorage_->num_levels()) {
|
||||
if (output_level > max_output_level) {
|
||||
if (start_level == 0) {
|
||||
output_level = vstorage_->num_levels() - 1;
|
||||
output_level = max_output_level;
|
||||
} else {
|
||||
// If start level is non-zero and all higher levels are empty, this
|
||||
// compaction will translate into a trivial move. Since the idea is
|
||||
@@ -1212,11 +1214,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (ioptions_.allow_ingest_behind &&
|
||||
output_level == vstorage_->num_levels() - 1) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
assert(output_level <= max_output_level);
|
||||
|
||||
if (output_level != 0) {
|
||||
if (start_level == 0) {
|
||||
@@ -1293,8 +1291,9 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
uint32_t path_id =
|
||||
GetPathId(ioptions_, mutable_cf_options_, estimated_total_size);
|
||||
int start_level = sorted_runs_[start_index].level;
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage_->num_levels());
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
std::vector<CompactionInputFiles> inputs(max_output_level + 1);
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
inputs[i].level = start_level + static_cast<int>(i);
|
||||
}
|
||||
@@ -1331,13 +1330,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
|
||||
int output_level;
|
||||
if (end_index == sorted_runs_.size() - 1) {
|
||||
// output files at the last level, unless it's reserved
|
||||
output_level = vstorage_->num_levels() - 1;
|
||||
// last level is reserved for the files ingested behind
|
||||
if (ioptions_.allow_ingest_behind) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
output_level = max_output_level;
|
||||
} else {
|
||||
// if it's not including all sorted_runs, it can only output to the level
|
||||
// above the `end_index + 1` sorted_run.
|
||||
|
||||
@@ -928,7 +928,7 @@ TEST_F(CompactionServiceTest, TablePropertiesCollector) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&fname_to_props));
|
||||
|
||||
@@ -952,4 +952,3 @@ int main(int argc, char** argv) {
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
seq_history.emplace_back(dbfull()->GetLatestSequenceNumber());
|
||||
expect_stats[0].Add(kBasicFlushStats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// the penultimate level file temperature is not cold, all data are output to
|
||||
// the penultimate level.
|
||||
@@ -374,7 +374,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
ASSERT_OK(Flush());
|
||||
expect_stats[0].Add(kBasicFlushStats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -445,8 +445,8 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(
|
||||
true)); // make sure the compaction is able to finish
|
||||
// make sure the compaction is able to finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -911,7 +911,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
ASSERT_OK(Flush());
|
||||
expect_stats[0].Add(kBasicFlushStats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// non last level is hot
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
@@ -954,7 +954,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
ASSERT_OK(Flush());
|
||||
seq_history.emplace_back(dbfull()->GetLatestSequenceNumber());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,1,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -1005,7 +1005,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
ASSERT_OK(Flush());
|
||||
seq_history.emplace_back(dbfull()->GetLatestSequenceNumber());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
latest_cold_seq = seq_history[0];
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
@@ -1135,7 +1135,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -1265,7 +1265,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeManualCompaction) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1327,7 +1327,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1360,7 +1360,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
});
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
// all data is moved up to the penultimate level
|
||||
@@ -1403,7 +1403,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimePartial) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1530,7 +1530,7 @@ TEST_F(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1609,7 +1609,7 @@ TEST_P(PrecludeLastLevelTestWithParms, LastLevelOnlyCompactionNoPreclude) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1705,7 +1705,7 @@ TEST_P(PrecludeLastLevelTestWithParms, LastLevelOnlyCompactionNoPreclude) {
|
||||
|
||||
manual_compaction_thread.join();
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
if (enable_preclude_last_level) {
|
||||
ASSERT_NE("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
@@ -1841,7 +1841,7 @@ TEST_P(PrecludeLastLevelTestWithParms, PeriodicCompactionToPenultimateLevel) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
stop_token.reset();
|
||||
|
||||
@@ -1940,7 +1940,7 @@ TEST_F(PrecludeLastLevelTest, PartialPenultimateLevelCompaction) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// L5: [0,19] [20,39] [40,299]
|
||||
// L6: [0, 299]
|
||||
@@ -2106,7 +2106,7 @@ TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
Slice begin_key(begin_key_buf), end_key(end_key_buf);
|
||||
ASSERT_OK(db_->SuggestCompactRange(db_->DefaultColumnFamily(), &begin_key,
|
||||
&end_key));
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,3,3", FilesPerLevel());
|
||||
ASSERT_EQ(1, per_key_comp_num);
|
||||
verify_db();
|
||||
@@ -2116,7 +2116,7 @@ TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
db_->ReleaseSnapshot(snap2);
|
||||
ASSERT_OK(db_->SuggestCompactRange(db_->DefaultColumnFamily(), &begin_key,
|
||||
&end_key));
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,3,3", FilesPerLevel());
|
||||
ASSERT_EQ(2, per_key_comp_num);
|
||||
verify_db();
|
||||
@@ -2126,7 +2126,7 @@ TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
db_->ReleaseSnapshot(snap1);
|
||||
ASSERT_OK(db_->SuggestCompactRange(db_->DefaultColumnFamily(), &begin_key,
|
||||
&end_key));
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,2,3", FilesPerLevel());
|
||||
ASSERT_EQ(3, per_key_comp_num);
|
||||
verify_db();
|
||||
|
||||
+2
-2
@@ -64,8 +64,8 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
const bool kImmortal = true;
|
||||
auto reader_options = TableReaderOptions(
|
||||
ioptions, options.prefix_extractor, env_options, internal_comparator,
|
||||
false /* skip_filters */, !kImmortal, false /* force_direct_prefetch */,
|
||||
-1 /* level */);
|
||||
options.block_protection_bytes_per_key, false /* skip_filters */,
|
||||
!kImmortal, false /* force_direct_prefetch */, -1 /* level */);
|
||||
reader_options.largest_seqno = largest_seqno;
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
reader_options, std::move(file_reader), file_size, &table_reader,
|
||||
|
||||
@@ -450,6 +450,7 @@ TEST_F(CorruptionTest, TableFile) {
|
||||
|
||||
TEST_F(CorruptionTest, VerifyChecksumReadahead) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
SpecialEnv senv(base_env_);
|
||||
options.env = &senv;
|
||||
// Disable block cache as we are going to check checksum for
|
||||
@@ -503,6 +504,7 @@ TEST_F(CorruptionTest, VerifyChecksumReadahead) {
|
||||
|
||||
TEST_F(CorruptionTest, TableFileIndexData) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
// very big, we'll trigger flushes manually
|
||||
options.write_buffer_size = 100 * 1024 * 1024;
|
||||
Reopen(&options);
|
||||
@@ -659,6 +661,7 @@ TEST_F(CorruptionTest, CorruptedDescriptor) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionInputError) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
Reopen(&options);
|
||||
Build(10);
|
||||
@@ -680,6 +683,7 @@ TEST_F(CorruptionTest, CompactionInputError) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_checks = true;
|
||||
options.write_buffer_size = 131072;
|
||||
@@ -777,6 +781,7 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_checks = true;
|
||||
options.create_if_missing = true;
|
||||
@@ -816,6 +821,7 @@ static const auto& corruption_modes = {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
@@ -844,6 +850,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
@@ -877,6 +884,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
@@ -913,6 +921,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
@@ -952,6 +961,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
@@ -988,6 +998,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
|
||||
TEST_F(CorruptionTest, LogCorruptionErrorsInCompactionIterator) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.allow_data_in_errors = true;
|
||||
@@ -1017,6 +1028,7 @@ TEST_F(CorruptionTest, LogCorruptionErrorsInCompactionIterator) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
@@ -1044,6 +1056,7 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
|
||||
TEST_F(CorruptionTest, FlushKeyOrderCheck) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
@@ -1097,6 +1110,7 @@ TEST_F(CorruptionTest, DisableKeyOrderCheck) {
|
||||
TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
options.create_if_missing = true;
|
||||
@@ -1182,6 +1196,7 @@ INSTANTIATE_TEST_CASE_P(CorruptionTest, CrashDuringRecoveryWithCorruptionTest,
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest_;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
@@ -1354,6 +1369,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
options.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest_;
|
||||
@@ -1551,6 +1567,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecoveryWithFlush) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
options.env = env_.get();
|
||||
|
||||
@@ -39,6 +39,7 @@ class CuckooTableDBTest : public testing::Test {
|
||||
|
||||
Options CurrentOptions() {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.table_factory.reset(NewCuckooTableFactory());
|
||||
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true));
|
||||
options.allow_mmap_reads = true;
|
||||
|
||||
@@ -620,9 +620,9 @@ class MockCache : public LRUCache {
|
||||
static uint32_t low_pri_insert_count;
|
||||
|
||||
MockCache()
|
||||
: LRUCache((size_t)1 << 25 /*capacity*/, 0 /*num_shard_bits*/,
|
||||
false /*strict_capacity_limit*/, 0.0 /*high_pri_pool_ratio*/,
|
||||
0.0 /*low_pri_pool_ratio*/) {}
|
||||
: LRUCache(LRUCacheOptions(
|
||||
size_t{1} << 25 /*capacity*/, 0 /*num_shard_bits*/,
|
||||
false /*strict_capacity_limit*/, 0.0 /*high_pri_pool_ratio*/)) {}
|
||||
|
||||
using ShardedCache::Insert;
|
||||
|
||||
@@ -979,12 +979,14 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
const size_t capacity = size_t{1} << 25;
|
||||
int iterations_tested = 0;
|
||||
for (bool partition : {false, true}) {
|
||||
SCOPED_TRACE("Partition? " + std::to_string(partition));
|
||||
for (std::shared_ptr<Cache> cache :
|
||||
{NewLRUCache(capacity),
|
||||
HyperClockCacheOptions(
|
||||
capacity,
|
||||
BlockBasedTableOptions().block_size /*estimated_value_size*/)
|
||||
.MakeSharedCache()}) {
|
||||
SCOPED_TRACE(std::string("Cache: ") + cache->Name());
|
||||
++iterations_tested;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -1278,6 +1280,7 @@ TEST_F(DBBlockCacheTest, HyperClockCacheReportProblems) {
|
||||
HyperClockCacheOptions hcc_opts{capacity, value_size_est};
|
||||
hcc_opts.num_shard_bits = 2; // 4 shards
|
||||
hcc_opts.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
hcc_opts.hash_seed = 0; // deterministic hashing
|
||||
std::shared_ptr<Cache> cache = hcc_opts.MakeSharedCache();
|
||||
std::shared_ptr<CountingLogger> logger = std::make_shared<CountingLogger>();
|
||||
|
||||
|
||||
+250
-217
@@ -43,6 +43,16 @@ const std::string kStandard128Ribbon =
|
||||
test::Standard128RibbonFilterPolicy::kClassName();
|
||||
const std::string kAutoBloom = BloomFilterPolicy::kClassName();
|
||||
const std::string kAutoRibbon = RibbonFilterPolicy::kClassName();
|
||||
|
||||
template <typename T>
|
||||
T Pop(T& var) {
|
||||
auto rv = var;
|
||||
var = 0;
|
||||
return rv;
|
||||
}
|
||||
PerfContextByLevel& GetLevelPerfContext(uint32_t level) {
|
||||
return (*(get_perf_context()->level_to_perf_context))[level];
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
// DB tests related to bloom filter.
|
||||
@@ -209,57 +219,43 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloomCustomPrefixExtractor) {
|
||||
ASSERT_OK(dbfull()->Flush(fo));
|
||||
|
||||
ASSERT_EQ("foo", Get("barbarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 0);
|
||||
|
||||
ASSERT_EQ("foo2", Get("barbarbar2"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 0);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("barbarbar3"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 0);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("barfoofoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ(
|
||||
1,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 1);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
ASSERT_EQ(
|
||||
2,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 1);
|
||||
|
||||
ro.total_order_seek = true;
|
||||
// NOTE: total_order_seek no longer affects Get()
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
ASSERT_EQ(
|
||||
3,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 1);
|
||||
|
||||
// No bloom on extractor changed
|
||||
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "capped:10"}}));
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
ASSERT_EQ(
|
||||
3,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 0);
|
||||
|
||||
// No bloom on extractor changed, after re-open
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(10));
|
||||
Reopen(options);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
ASSERT_EQ(
|
||||
3,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 0);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
@@ -294,33 +290,32 @@ TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
|
||||
ASSERT_OK(dbfull()->Flush(fo));
|
||||
|
||||
ASSERT_EQ("foo", Get("barbarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ("foo2", Get("barbarbar2"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("barbarbar3"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 0);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("barfoofoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 1);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 1);
|
||||
|
||||
ro.total_order_seek = true;
|
||||
// NOTE: total_order_seek no longer affects Get()
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
ASSERT_EQ(
|
||||
3,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 1);
|
||||
|
||||
// No bloom on extractor changed
|
||||
ASSERT_OK(db_->SetOptions({{"prefix_extractor", "capped:10"}}));
|
||||
ASSERT_EQ("NOT_FOUND", Get("foobarbar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
ASSERT_EQ(
|
||||
3,
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(Pop(GetLevelPerfContext(0).bloom_filter_useful), 0);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
@@ -357,12 +352,17 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
ASSERT_OK(dbfull()->Flush(fo));
|
||||
|
||||
Reopen(options);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
|
||||
// Reopen with whole key filtering enabled and prefix extractor
|
||||
// NULL. Bloom filter should be off for both of whole key and
|
||||
@@ -372,13 +372,17 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
options.prefix_extractor.reset();
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
// Write DB with only full key filtering.
|
||||
ASSERT_OK(dbfull()->Put(wo, "foobar", "foo"));
|
||||
// Needs insert some keys to make sure files are not filtered out by key
|
||||
@@ -394,13 +398,17 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
|
||||
// Try to create a DB with mixed files:
|
||||
ASSERT_OK(dbfull()->Put(wo, "foobar", "foo"));
|
||||
@@ -424,61 +432,81 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Now we have two files:
|
||||
// File 1: An older file with prefix bloom.
|
||||
// File 1: An older file with prefix bloom (disabled)
|
||||
// File 2: A newer file with whole bloom filter.
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 2);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 3);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
|
||||
// Reopen with the same setting: only whole key is used
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 4);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 5);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 6);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
|
||||
// Restart with both filters are allowed
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 7);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
// File 1 will has it filtered out.
|
||||
// File 2 will not, as prefix `foo` exists in the file.
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 8);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 10);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
|
||||
// Restart with only prefix bloom is allowed.
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 11);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("NOT_FOUND", Get("bar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("foo", Get("foobar"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
ASSERT_EQ("bar", Get("barfoo"));
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 0);
|
||||
uint64_t bloom_filter_useful_all_levels = 0;
|
||||
for (auto& kv : (*(get_perf_context()->level_to_perf_context))) {
|
||||
if (kv.second.bloom_filter_useful > 0) {
|
||||
@@ -696,11 +724,8 @@ TEST_P(DBBloomFilterTestWithParam, SkipFilterOnEssentiallyZeroBpk) {
|
||||
PutAndGetFn();
|
||||
|
||||
// Verify filter is accessed (and constructed)
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_POSITIVE),
|
||||
maxKey * 2);
|
||||
EXPECT_EQ(
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
maxKey);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_FULL_POSITIVE), maxKey * 2);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_FULL_TRUE_POSITIVE), maxKey);
|
||||
props.clear();
|
||||
ASSERT_TRUE(db_->GetMapProperty(kAggTableProps, &props));
|
||||
EXPECT_NE(props["filter_size"], "0");
|
||||
@@ -715,11 +740,8 @@ TEST_P(DBBloomFilterTestWithParam, SkipFilterOnEssentiallyZeroBpk) {
|
||||
GetFn();
|
||||
|
||||
// Verify filter is accessed
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_POSITIVE),
|
||||
maxKey * 2);
|
||||
EXPECT_EQ(
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
maxKey);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_FULL_POSITIVE), maxKey * 2);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_FULL_TRUE_POSITIVE), maxKey);
|
||||
|
||||
// But new filters are not generated (configuration details unknown)
|
||||
DestroyAndReopen(options);
|
||||
@@ -783,9 +805,7 @@ TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, Key(i + 33333)));
|
||||
}
|
||||
ASSERT_GE(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), maxKey * 0.98);
|
||||
ASSERT_GE(
|
||||
(*(get_perf_context()->level_to_perf_context))[0].bloom_filter_useful,
|
||||
maxKey * 0.98);
|
||||
ASSERT_GE(GetLevelPerfContext(0).bloom_filter_useful, maxKey * 0.98);
|
||||
get_perf_context()->Reset();
|
||||
}
|
||||
}
|
||||
@@ -864,9 +884,8 @@ TEST_F(DBBloomFilterTest, BloomFilterCompatibility) {
|
||||
ASSERT_EQ("val", Get(prefix + "Z")); // Filter positive
|
||||
// Filter negative, with high probability
|
||||
ASSERT_EQ("NOT_FOUND", Get(prefix + "Q"));
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_POSITIVE),
|
||||
2);
|
||||
EXPECT_EQ(TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_FULL_POSITIVE), 2);
|
||||
EXPECT_EQ(PopTicker(options, BLOOM_FILTER_USEFUL), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1705,7 +1724,7 @@ TEST_F(DBBloomFilterTest, ContextCustomFilterPolicy) {
|
||||
}
|
||||
// Since we have two tables / two filters, we might have Bloom checks on
|
||||
// our queries, but no more than one "useful" per query on a found key.
|
||||
EXPECT_LE(TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL), maxKey);
|
||||
EXPECT_LE(PopTicker(options, BLOOM_FILTER_USEFUL), maxKey);
|
||||
|
||||
// Check that we have two filters, each about
|
||||
// fifo: 0.12% FP rate (15 bits per key)
|
||||
@@ -1714,8 +1733,7 @@ TEST_F(DBBloomFilterTest, ContextCustomFilterPolicy) {
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, Key(i + 33333)));
|
||||
}
|
||||
{
|
||||
auto useful_count =
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL);
|
||||
auto useful_count = PopTicker(options, BLOOM_FILTER_USEFUL);
|
||||
EXPECT_GE(useful_count, maxKey * 2 * (fifo ? 0.9980 : 0.975));
|
||||
EXPECT_LE(useful_count, maxKey * 2 * (fifo ? 0.9995 : 0.98));
|
||||
}
|
||||
@@ -1732,8 +1750,7 @@ TEST_F(DBBloomFilterTest, ContextCustomFilterPolicy) {
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, Key(i + 33333)));
|
||||
}
|
||||
{
|
||||
auto useful_count =
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL);
|
||||
auto useful_count = PopTicker(options, BLOOM_FILTER_USEFUL);
|
||||
EXPECT_GE(useful_count, maxKey * 0.90);
|
||||
EXPECT_LE(useful_count, maxKey * 0.91);
|
||||
}
|
||||
@@ -2027,13 +2044,14 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
std::array<Status, Q> statuses;
|
||||
std::array<PinnableSlice, Q> values;
|
||||
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_USEFUL);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_POSITIVE);
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE);
|
||||
PopTicker(options, BLOCK_CACHE_FILTER_HIT);
|
||||
PopTicker(options, BLOCK_CACHE_FILTER_MISS);
|
||||
PopTicker(options, BLOOM_FILTER_PREFIX_USEFUL);
|
||||
PopTicker(options, BLOOM_FILTER_USEFUL);
|
||||
PopTicker(options, BLOOM_FILTER_PREFIX_CHECKED);
|
||||
PopTicker(options, BLOOM_FILTER_FULL_POSITIVE);
|
||||
PopTicker(options, BLOOM_FILTER_FULL_TRUE_POSITIVE);
|
||||
PopTicker(options, BLOOM_FILTER_PREFIX_TRUE_POSITIVE);
|
||||
|
||||
// Check that initial clump of keys only loads one partition filter from
|
||||
// block cache.
|
||||
@@ -2063,26 +2081,22 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
}
|
||||
|
||||
// Confirm correct Bloom stats (no FPs)
|
||||
uint64_t filter_useful = TestGetAndResetTickerCount(
|
||||
options,
|
||||
use_prefix_ ? BLOOM_FILTER_PREFIX_USEFUL : BLOOM_FILTER_USEFUL);
|
||||
uint64_t filter_useful =
|
||||
PopTicker(options, use_prefix_ ? BLOOM_FILTER_PREFIX_USEFUL
|
||||
: BLOOM_FILTER_USEFUL);
|
||||
uint64_t filter_checked =
|
||||
TestGetAndResetTickerCount(options, use_prefix_
|
||||
? BLOOM_FILTER_PREFIX_CHECKED
|
||||
: BLOOM_FILTER_FULL_POSITIVE) +
|
||||
PopTicker(options, use_prefix_ ? BLOOM_FILTER_PREFIX_CHECKED
|
||||
: BLOOM_FILTER_FULL_POSITIVE) +
|
||||
(use_prefix_ ? 0 : filter_useful);
|
||||
EXPECT_EQ(filter_useful, number_not_found);
|
||||
EXPECT_EQ(filter_checked, Q);
|
||||
if (!use_prefix_) {
|
||||
EXPECT_EQ(
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
Q - number_not_found);
|
||||
}
|
||||
EXPECT_EQ(PopTicker(options, use_prefix_ ? BLOOM_FILTER_PREFIX_TRUE_POSITIVE
|
||||
: BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
Q - number_not_found);
|
||||
|
||||
// Confirm no duplicate loading same filter partition
|
||||
uint64_t filter_accesses =
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT) +
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
uint64_t filter_accesses = PopTicker(options, BLOCK_CACHE_FILTER_HIT) +
|
||||
PopTicker(options, BLOCK_CACHE_FILTER_MISS);
|
||||
if (stride == 1) {
|
||||
EXPECT_EQ(filter_accesses, 1);
|
||||
} else {
|
||||
@@ -2118,26 +2132,22 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
}
|
||||
|
||||
// Confirm correct Bloom stats (might see some FPs)
|
||||
uint64_t filter_useful = TestGetAndResetTickerCount(
|
||||
options,
|
||||
use_prefix_ ? BLOOM_FILTER_PREFIX_USEFUL : BLOOM_FILTER_USEFUL);
|
||||
uint64_t filter_useful =
|
||||
PopTicker(options, use_prefix_ ? BLOOM_FILTER_PREFIX_USEFUL
|
||||
: BLOOM_FILTER_USEFUL);
|
||||
uint64_t filter_checked =
|
||||
TestGetAndResetTickerCount(options, use_prefix_
|
||||
? BLOOM_FILTER_PREFIX_CHECKED
|
||||
: BLOOM_FILTER_FULL_POSITIVE) +
|
||||
PopTicker(options, use_prefix_ ? BLOOM_FILTER_PREFIX_CHECKED
|
||||
: BLOOM_FILTER_FULL_POSITIVE) +
|
||||
(use_prefix_ ? 0 : filter_useful);
|
||||
EXPECT_GE(filter_useful, number_not_found - 2); // possible FP
|
||||
EXPECT_EQ(filter_checked, Q);
|
||||
if (!use_prefix_) {
|
||||
EXPECT_EQ(
|
||||
TestGetAndResetTickerCount(options, BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
Q - number_not_found);
|
||||
}
|
||||
EXPECT_EQ(PopTicker(options, use_prefix_ ? BLOOM_FILTER_PREFIX_TRUE_POSITIVE
|
||||
: BLOOM_FILTER_FULL_TRUE_POSITIVE),
|
||||
Q - number_not_found);
|
||||
|
||||
// Confirm no duplicate loading of same filter partition
|
||||
uint64_t filter_accesses =
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_HIT) +
|
||||
TestGetAndResetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
uint64_t filter_accesses = PopTicker(options, BLOCK_CACHE_FILTER_HIT) +
|
||||
PopTicker(options, BLOCK_CACHE_FILTER_MISS);
|
||||
if (filter_accesses == 2) {
|
||||
// Spanned across partitions.
|
||||
++found_spanning;
|
||||
@@ -2604,7 +2614,7 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
compact_options.target_level = 7;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
|
||||
|
||||
ASSERT_EQ(trivial_move, 1);
|
||||
ASSERT_GE(trivial_move, 1);
|
||||
ASSERT_EQ(non_trivial_move, 0);
|
||||
|
||||
prev_cache_filter_hits = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
@@ -2636,6 +2646,9 @@ int CountIter(std::unique_ptr<Iterator>& iter, const Slice& key) {
|
||||
int count = 0;
|
||||
for (iter->Seek(key); iter->Valid(); iter->Next()) {
|
||||
count++;
|
||||
// Access key & value as if we were using them
|
||||
(void)iter->key();
|
||||
(void)iter->value();
|
||||
}
|
||||
EXPECT_OK(iter->status());
|
||||
return count;
|
||||
@@ -2675,6 +2688,12 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abcd0000"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
1);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(
|
||||
options, NON_LAST_LEVEL_SEEK_DATA_USEFUL_FILTER_MATCH),
|
||||
1);
|
||||
}
|
||||
{
|
||||
Slice upper_bound("abcdzzzz");
|
||||
@@ -2683,8 +2702,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abcd0000"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
}
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:5"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().prefix_extractor->AsString(),
|
||||
@@ -2698,8 +2718,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abcdxx00"), 4);
|
||||
// should check bloom filter since upper bound meets requirement
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
}
|
||||
{
|
||||
// [abcdxx01, abcey) is not valid bound since upper bound is too long for
|
||||
@@ -2711,8 +2732,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abcdxx01"), 4);
|
||||
// should skip bloom filter since upper bound is too long
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
}
|
||||
{
|
||||
// [abcdxx02, abcdy) is a valid bound since the prefix is the same
|
||||
@@ -2724,8 +2746,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
ASSERT_EQ(CountIter(iter, "abcdxx02"), 4);
|
||||
// should check bloom filter since upper bound matches transformed seek
|
||||
// key
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
}
|
||||
{
|
||||
// [aaaaaaaa, abce) is not a valid bound since 1) they don't share the
|
||||
@@ -2737,8 +2760,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "aaaaaaaa"), 0);
|
||||
// should skip bloom filter since mismatch is found
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
}
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:3"}}));
|
||||
{
|
||||
@@ -2750,8 +2774,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abc"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
}
|
||||
// Same with re-open
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
@@ -2763,8 +2788,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abc"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
}
|
||||
// Set back to capped:4 and verify BF is always read
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(4));
|
||||
@@ -2776,8 +2802,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abc"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 5);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 1);
|
||||
}
|
||||
// Same if there's a problem initally loading prefix transform
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
@@ -2792,8 +2819,9 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "abc"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 6);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NON_LAST_LEVEL_SEEK_FILTERED), 2);
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
@@ -2828,7 +2856,8 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
dbfull()->Flush(FlushOptions());
|
||||
std::unique_ptr<Iterator> iter_old(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 1);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 1);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "capped:3"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().prefix_extractor->AsString(),
|
||||
@@ -2836,10 +2865,11 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 2);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 1);
|
||||
ASSERT_EQ(CountIter(iter, "gpk"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 0);
|
||||
|
||||
// second SST with capped:3 BF
|
||||
ASSERT_OK(Put("foo3", "bar3"));
|
||||
@@ -2851,13 +2881,13 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
// BF is cappped:3 now
|
||||
std::unique_ptr<Iterator> iter_tmp(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter_tmp, "foo"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 2);
|
||||
ASSERT_EQ(CountIter(iter_tmp, "gpk"), 0);
|
||||
// both counters are incremented because BF is "not changed" for 1 of the
|
||||
// 2 SST files, so filter is checked once and found no match.
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 5);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 1);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 0);
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "fixed:2"}}));
|
||||
@@ -2874,33 +2904,34 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
std::unique_ptr<Iterator> iter_tmp(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter_tmp, "foo"), 9);
|
||||
// the first and last BF are checked
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 7);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 1);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 2);
|
||||
ASSERT_EQ(CountIter(iter_tmp, "gpk"), 0);
|
||||
// only last BF is checked and not found
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 8);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 2);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 1);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 0);
|
||||
}
|
||||
|
||||
// iter_old can only see the first SST, so checked plus 1
|
||||
// iter_old can only see the first SST
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 4);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 9);
|
||||
// iter was created after the first setoptions call so only full filter
|
||||
// will check the filter
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 1);
|
||||
// same with iter, but different prefix extractor
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 2);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 10);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 1);
|
||||
|
||||
{
|
||||
// keys in all three SSTs are visible to iterator
|
||||
// The range of [foo, foz90000] is compatible with (fixed:1) and (fixed:2)
|
||||
// so +2 for checked counter
|
||||
std::unique_ptr<Iterator> iter_all(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter_all, "foo"), 9);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 12);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 2);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 2);
|
||||
ASSERT_EQ(CountIter(iter_all, "gpk"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 13);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 3);
|
||||
// FIXME? isn't seek key out of SST range?
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 1);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 0);
|
||||
}
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "capped:3"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().prefix_extractor->AsString(),
|
||||
@@ -2910,11 +2941,12 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
ASSERT_EQ(CountIter(iter_all, "foo"), 6);
|
||||
// all three SST are checked because the current options has the same as
|
||||
// the remaining SST (capped:3)
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 16);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 3);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 3);
|
||||
ASSERT_EQ(CountIter(iter_all, "gpk"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 17);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 4);
|
||||
// FIXME? isn't seek key out of SST range?
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 1);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 0);
|
||||
}
|
||||
// TODO(Zhongyi): Maybe also need to add Get calls to test point look up?
|
||||
}
|
||||
@@ -3016,13 +3048,13 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterOptions) {
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 12);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 3);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 3);
|
||||
}
|
||||
std::unique_ptr<Iterator> iter_old(db_->NewIterator(read_options));
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 12);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 6);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 3);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"prefix_extractor", "capped:3"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().prefix_extractor->AsString(),
|
||||
@@ -3031,17 +3063,18 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterOptions) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
// "fp*" should be skipped
|
||||
ASSERT_EQ(CountIter(iter, "foo"), 9);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 6);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 0);
|
||||
}
|
||||
|
||||
// iterator created before should not be affected and see all keys
|
||||
ASSERT_EQ(CountIter(iter_old, "foo"), 12);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 9);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 0);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 3);
|
||||
ASSERT_EQ(CountIter(iter_old, "abc"), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED), 12);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_PREFIX_USEFUL), 3);
|
||||
// FIXME? isn't seek key out of SST range?
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTERED), 3);
|
||||
EXPECT_EQ(PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3136,19 +3169,19 @@ class FixedSuffix4Transform : public SliceTransform {
|
||||
|
||||
std::pair<uint64_t, uint64_t> GetBloomStat(const Options& options, bool sst) {
|
||||
if (sst) {
|
||||
return {
|
||||
options.statistics->getAndResetTickerCount(BLOOM_FILTER_PREFIX_CHECKED),
|
||||
options.statistics->getAndResetTickerCount(BLOOM_FILTER_PREFIX_USEFUL)};
|
||||
return {options.statistics->getAndResetTickerCount(
|
||||
NON_LAST_LEVEL_SEEK_FILTER_MATCH),
|
||||
options.statistics->getAndResetTickerCount(
|
||||
NON_LAST_LEVEL_SEEK_FILTERED)};
|
||||
} else {
|
||||
auto hit = std::exchange(get_perf_context()->bloom_memtable_hit_count, 0);
|
||||
auto miss = std::exchange(get_perf_context()->bloom_memtable_miss_count, 0);
|
||||
return {hit + miss, miss};
|
||||
return {hit, miss};
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<uint64_t, uint64_t> CheckedAndUseful(uint64_t checked,
|
||||
uint64_t useful) {
|
||||
return {checked, useful};
|
||||
std::pair<uint64_t, uint64_t> HitAndMiss(uint64_t hits, uint64_t misses) {
|
||||
return {hits, misses};
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
@@ -3186,27 +3219,27 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter1) {
|
||||
if (flushed) { // TODO: support auto_prefix_mode in memtable?
|
||||
read_options.auto_prefix_mode = true;
|
||||
}
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
{
|
||||
Slice ub("999aaaa");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaaa"), 3);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
Slice ub("999abaa");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "abaa"), 1);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
Slice ub("999acaa");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "acaa"), 0);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 1));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 1));
|
||||
}
|
||||
{
|
||||
Slice ub("zzzz");
|
||||
@@ -3214,7 +3247,7 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter1) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "baa"), 3);
|
||||
if (flushed) { // TODO: fix memtable case
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3260,13 +3293,13 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter2) {
|
||||
get_perf_context()->bloom_memtable_hit_count = 0;
|
||||
get_perf_context()->bloom_memtable_miss_count = 0;
|
||||
}
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
{
|
||||
Slice ub("aaaa000");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaaa999"), 3);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
// Note: prefix does work as upper bound
|
||||
@@ -3274,7 +3307,7 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter2) {
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaaa999"), 3);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
// Note: prefix does not work here as seek key
|
||||
@@ -3282,28 +3315,28 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter2) {
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaaa"), 0);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
Slice ub("aaba000");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaba999"), 1);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
Slice ub("aaca000");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaca999"), 0);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 1));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 1));
|
||||
}
|
||||
{
|
||||
Slice ub("aaaz");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "zzz"), 5);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
}
|
||||
{
|
||||
// Note: prefix does work here as seek key, but only finds key equal
|
||||
@@ -3313,7 +3346,7 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter2) {
|
||||
read_options.prefix_same_as_start = true;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "qqqq"), 1);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3404,13 +3437,13 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter3) {
|
||||
get_perf_context()->bloom_memtable_hit_count = 0;
|
||||
get_perf_context()->bloom_memtable_miss_count = 0;
|
||||
}
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
{
|
||||
Slice ub("aaaa999");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaaa000"), 3);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
// Note: prefix as seek key is not bloom-optimized
|
||||
@@ -3420,28 +3453,28 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter3) {
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaaa"), 3);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
}
|
||||
{
|
||||
Slice ub("aaba9");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaba0"), 1);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
Slice ub("aaca9");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aaca0"), 0);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 1));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 1));
|
||||
}
|
||||
{
|
||||
Slice ub("qqqq9");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "qqqq0"), 1);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(1, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(1, 0));
|
||||
}
|
||||
{
|
||||
// Note: prefix as seek key is not bloom-optimized
|
||||
@@ -3449,7 +3482,7 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter3) {
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "qqqq"), weird_comparator ? 7 : 2);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
}
|
||||
{
|
||||
// Note: prefix as seek key is not bloom-optimized
|
||||
@@ -3457,14 +3490,14 @@ TEST_F(DBBloomFilterTest, WeirdPrefixExtractorWithFilter3) {
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "zzzz"), weird_comparator ? 8 : 1);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
}
|
||||
{
|
||||
Slice ub("zzzz9");
|
||||
read_options.iterate_upper_bound = &ub;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
EXPECT_EQ(CountIter(iter, "aab"), weird_comparator ? 6 : 5);
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), CheckedAndUseful(0, 0));
|
||||
EXPECT_EQ(GetBloomStat(options, flushed), HitAndMiss(0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBClipTest : public DBTestBase {
|
||||
public:
|
||||
DBClipTest() : DBTestBase("db_clip_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBClipTest, TestClipRange) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1024 * 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 3;
|
||||
options.max_background_compactions = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int32_t value_size = 10 * 1024; // 10 KB
|
||||
|
||||
Random rnd(301);
|
||||
std::map<int32_t, std::string> values;
|
||||
|
||||
// file [0 => 100), [100 => 200), ... [900, 1000)
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
values[k] = rnd.RandomString(value_size);
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("10", FilesPerLevel(0));
|
||||
auto begin_key = Key(251), end_key = Key(751);
|
||||
ASSERT_OK(
|
||||
db_->ClipColumnFamily(db_->DefaultColumnFamily(), begin_key, end_key));
|
||||
|
||||
for (auto i = 0; i < 251; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 251; i < 751; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
for (auto i = 751; i < 1000; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
|
||||
std::vector<LiveFileMetaData> all_metadata;
|
||||
db_->GetLiveFilesMetaData(&all_metadata);
|
||||
for (auto& md : all_metadata) {
|
||||
// make sure clip_begin_key <= file_smallestkey <= file_largestkey <=
|
||||
// clip_end_key
|
||||
bool in_range = false;
|
||||
|
||||
if (options.comparator->Compare(begin_key, md.smallestkey) <= 0 &&
|
||||
options.comparator->Compare(end_key, md.largestkey) > 0) {
|
||||
in_range = true;
|
||||
}
|
||||
ASSERT_TRUE(in_range);
|
||||
}
|
||||
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = 2;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,3", FilesPerLevel(0));
|
||||
|
||||
for (auto i = 0; i < 10; i += 2) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("5,0,3", FilesPerLevel(0));
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
|
||||
ASSERT_EQ("0,5,3", FilesPerLevel(0));
|
||||
|
||||
for (auto i = 1; i < 10; i += 2) {
|
||||
for (auto j = 0; j < 100; j++) {
|
||||
auto k = i * 100 + j;
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("5,5,3", FilesPerLevel(0));
|
||||
|
||||
auto begin_key_2 = Key(222), end_key_2 = Key(888);
|
||||
|
||||
ASSERT_OK(db_->ClipColumnFamily(db_->DefaultColumnFamily(), begin_key_2,
|
||||
end_key_2));
|
||||
|
||||
for (auto i = 0; i < 222; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
for (auto i = 222; i < 888; i++) {
|
||||
ASSERT_EQ(Get(Key(i)), values[i]);
|
||||
}
|
||||
for (auto i = 888; i < 1000; i++) {
|
||||
ReadOptions ropts;
|
||||
std::string result;
|
||||
auto s = db_->Get(ropts, Key(i), &result);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
|
||||
std::vector<LiveFileMetaData> all_metadata_2;
|
||||
db_->GetLiveFilesMetaData(&all_metadata_2);
|
||||
for (auto& md : all_metadata_2) {
|
||||
// make sure clip_begin_key <= file_smallestkey <= file_largestkey <=
|
||||
// clip_end_key
|
||||
bool in_range = false;
|
||||
if (begin_key_2.compare(md.smallestkey) <= 0 &&
|
||||
end_key_2.compare(md.largestkey) > 0) {
|
||||
in_range = true;
|
||||
}
|
||||
ASSERT_TRUE(in_range);
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+435
-47
@@ -153,6 +153,48 @@ class DBCompactionDirectIOTest : public DBCompactionTest,
|
||||
DBCompactionDirectIOTest() : DBCompactionTest() {}
|
||||
};
|
||||
|
||||
class DBCompactionWaitForCompactTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
DBCompactionWaitForCompactTest()
|
||||
: DBTestBase("db_compaction_test", /*env_do_fsync=*/true) {
|
||||
abort_on_pause_ = std::get<0>(GetParam());
|
||||
flush_ = std::get<1>(GetParam());
|
||||
}
|
||||
bool abort_on_pause_;
|
||||
bool flush_;
|
||||
Options options_;
|
||||
WaitForCompactOptions wait_for_compact_options_;
|
||||
|
||||
void SetUp() override {
|
||||
// This test sets up a scenario that one more L0 file will trigger a
|
||||
// compaction
|
||||
const int kNumKeysPerFile = 4;
|
||||
const int kNumFiles = 2;
|
||||
|
||||
options_ = CurrentOptions();
|
||||
options_.level0_file_num_compaction_trigger = kNumFiles + 1;
|
||||
|
||||
wait_for_compact_options_ = WaitForCompactOptions();
|
||||
wait_for_compact_options_.abort_on_pause = abort_on_pause_;
|
||||
wait_for_compact_options_.flush = flush_;
|
||||
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(100 /* len */)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
}
|
||||
};
|
||||
|
||||
// Param = true : target level is non-empty
|
||||
// Param = false: level between target level and source level
|
||||
// is not empty.
|
||||
@@ -1105,7 +1147,7 @@ TEST_F(DBCompactionTest, CompactionSstPartitionerNonTrivial) {
|
||||
ASSERT_OK(Put("bbbb1", "B"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
std::vector<LiveFileMetaData> files;
|
||||
dbfull()->GetLiveFilesMetaData(&files);
|
||||
@@ -3285,6 +3327,186 @@ TEST_F(DBCompactionTest, SuggestCompactRangeNoTwoLevel0Compactions) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::SuggestCompactRangeNoTwoLevel0Compactions:2");
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionWaitForCompactTest,
|
||||
DBCompactionWaitForCompactTest,
|
||||
::testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
std::make_tuple(true, false),
|
||||
std::make_tuple(true, true)));
|
||||
|
||||
TEST_P(DBCompactionWaitForCompactTest,
|
||||
WaitForCompactWaitsOnCompactionToFinish) {
|
||||
// Triggers a compaction. Before the compaction finishes, test
|
||||
// closes the DB Upon reopen, wait for the compaction to finish and checks for
|
||||
// the number of compaction finished
|
||||
|
||||
int compaction_finished = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():EndStatusSet", [&](void* arg) {
|
||||
auto status = static_cast<Status*>(arg);
|
||||
if (status->ok()) {
|
||||
compaction_finished++;
|
||||
}
|
||||
});
|
||||
// To make sure there's a flush/compaction debt
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:BeforeSchedule", [&](void* arg) {
|
||||
auto unscheduled_flushes = *static_cast<int*>(arg);
|
||||
ASSERT_GT(unscheduled_flushes, 0);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBCompactionTest::WaitForCompactWaitsOnCompactionToFinish",
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:BeforeSchedule"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// create compaction debt by adding one more L0 file then closing
|
||||
Random rnd(123);
|
||||
GenerateNewRandomFile(&rnd, /* nowait */ true);
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
Close();
|
||||
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactWaitsOnCompactionToFinish");
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
|
||||
// Reopen the db and we expect the compaction to be triggered.
|
||||
Reopen(options_);
|
||||
|
||||
// Wait for compaction to finish
|
||||
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
|
||||
ASSERT_GT(compaction_finished, 0);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactAbortOnPause) {
|
||||
// Triggers a compaction. Before the compaction finishes, test
|
||||
// pauses the compaction. Calling WaitForCompact() with option
|
||||
// abort_on_pause=true should return Status::Aborted Or
|
||||
// ContinueBackgroundWork() must be called
|
||||
|
||||
// Now trigger L0 compaction by adding a file
|
||||
Random rnd(123);
|
||||
GenerateNewRandomFile(&rnd, /* nowait */ true);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Pause the background jobs.
|
||||
ASSERT_OK(dbfull()->PauseBackgroundWork());
|
||||
|
||||
// If not abort_on_pause_ continue the background jobs.
|
||||
if (!abort_on_pause_) {
|
||||
ASSERT_OK(dbfull()->ContinueBackgroundWork());
|
||||
}
|
||||
|
||||
Status s = dbfull()->WaitForCompact(wait_for_compact_options_);
|
||||
if (abort_on_pause_) {
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsAborted());
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactShutdownWhileWaiting) {
|
||||
// Triggers a compaction. Before the compaction finishes, db
|
||||
// shuts down (by calling CancelAllBackgroundWork()). Calling WaitForCompact()
|
||||
// should return Status::IsShutdownInProgress()
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"CompactionJob::Run():Start",
|
||||
"DBCompactionTest::WaitForCompactShutdownWhileWaiting:0"},
|
||||
{"DBImpl::WaitForCompact:StartWaiting",
|
||||
"DBCompactionTest::WaitForCompactShutdownWhileWaiting:1"},
|
||||
{"DBImpl::~DBImpl:WaitJob", "CompactionJob::Run():End"},
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Now trigger L0 compaction by adding a file
|
||||
Random rnd(123);
|
||||
GenerateNewRandomFile(&rnd, /* nowait */ true);
|
||||
ASSERT_OK(Flush());
|
||||
// Wait for compaction to start
|
||||
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactShutdownWhileWaiting:0");
|
||||
|
||||
// Wait for Compaction in another thread
|
||||
auto waiting_for_compaction_thread = port::Thread([this]() {
|
||||
Status s = dbfull()->WaitForCompact(wait_for_compact_options_);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsShutdownInProgress());
|
||||
});
|
||||
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactShutdownWhileWaiting:1");
|
||||
// Shutdown after wait started, but before the compaction finishes
|
||||
auto closing_thread = port::Thread([this]() { ASSERT_OK(db_->Close()); });
|
||||
|
||||
waiting_for_compaction_thread.join();
|
||||
closing_thread.join();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactWithOptionToFlush) {
|
||||
// After creating enough L0 files that one more file will trigger the
|
||||
// compaction, write some data in memtable. Calls WaitForCompact with option
|
||||
// to flush. This will flush the memtable to a new L0 file which will trigger
|
||||
// compaction. Lastly check for expected number of files, closing + reopening
|
||||
// DB won't trigger any flush or compaction
|
||||
|
||||
int compaction_finished = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
[&](void*) { compaction_finished++; });
|
||||
|
||||
int flush_finished = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::End", [&](void*) { flush_finished++; });
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// write to memtable (overlapping key with first L0 file), but no flush is
|
||||
// needed at this point.
|
||||
ASSERT_OK(Put(Key(0), "some random string"));
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
ASSERT_EQ(0, flush_finished);
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
|
||||
if (flush_) {
|
||||
ASSERT_EQ("1,2", FilesPerLevel());
|
||||
ASSERT_EQ(1, compaction_finished);
|
||||
ASSERT_EQ(1, flush_finished);
|
||||
} else {
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
ASSERT_EQ(0, flush_finished);
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
}
|
||||
|
||||
compaction_finished = 0;
|
||||
flush_finished = 0;
|
||||
Close();
|
||||
Reopen(options_);
|
||||
|
||||
ASSERT_EQ(0, flush_finished);
|
||||
if (flush_) {
|
||||
// if flushed already prior to close and reopen, expect there's no
|
||||
// additional compaction needed
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
} else {
|
||||
// if not flushed prior to close and reopen, expect L0 file creation from
|
||||
// WAL when reopening which will trigger the compaction.
|
||||
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
|
||||
ASSERT_EQ(1, compaction_finished);
|
||||
}
|
||||
|
||||
ASSERT_EQ("1,2", FilesPerLevel());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
static std::string ShortKey(int i) {
|
||||
@@ -4378,6 +4600,7 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
PutVarint64(encoded_fieled, 0);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options.env = env_;
|
||||
|
||||
@@ -4394,7 +4617,6 @@ TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
|
||||
ttl_compactions++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Add two L6 files with key ranges: [1 .. 100], [101 .. 200].
|
||||
Random rnd(301);
|
||||
@@ -4526,6 +4748,7 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
PutVarint64(encoded_fieled, 0);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options.env = env_;
|
||||
|
||||
@@ -4542,7 +4765,6 @@ TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
@@ -5075,7 +5297,11 @@ TEST_F(DBCompactionTest, CompactRangeShutdownWhileDelayed) {
|
||||
manual_compaction_thread.join();
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::CompactRangeShutdownWhileDelayed:PostManual");
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
if (i == 0) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
} else {
|
||||
ASSERT_NOK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
@@ -5818,7 +6044,7 @@ TEST_P(RoundRobinSubcompactionsAgainstPressureToken, PressureTokenTest) {
|
||||
}
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstPressureToken:2");
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(num_planned_subcompactions_verified);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -5893,7 +6119,7 @@ TEST_P(RoundRobinSubcompactionsAgainstResources, SubcompactionsUsingResources) {
|
||||
"CompactionJob::ReleaseSubcompactionResources:1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:0");
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:1");
|
||||
@@ -5909,7 +6135,7 @@ TEST_P(RoundRobinSubcompactionsAgainstResources, SubcompactionsUsingResources) {
|
||||
total_low_pri_threads_ - 1,
|
||||
env_->ReleaseThreads(total_low_pri_threads_ - 1, Env::Priority::LOW));
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:4");
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(num_planned_subcompactions_verified);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -5981,11 +6207,11 @@ TEST_P(DBCompactionTestWithParam, RoundRobinWithoutAdditionalResources) {
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
|
||||
TEST_SYNC_POINT("DBCompactionTest::RoundRobinWithoutAdditionalResources:0");
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(num_planned_subcompactions_verified);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -6740,10 +6966,8 @@ class DBCompactionTestL0FilesMisorderCorruption : public DBCompactionTest {
|
||||
if (compaction_path_to_test == "FindIntraL0Compaction" ||
|
||||
compaction_path_to_test == "CompactRange") {
|
||||
fifo_options.allow_compaction = true;
|
||||
fifo_options.age_for_warm = 0;
|
||||
} else if (compaction_path_to_test == "CompactFile") {
|
||||
fifo_options.allow_compaction = false;
|
||||
fifo_options.age_for_warm = 0;
|
||||
}
|
||||
options_.compaction_options_fifo = fifo_options;
|
||||
}
|
||||
@@ -7742,26 +7966,14 @@ TEST_F(DBCompactionTest, ChangeLevelErrorPathTest) {
|
||||
auto start_idx = key_idx;
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
auto end_idx = key_idx - 1;
|
||||
ASSERT_EQ("1,1,2", FilesPerLevel(0));
|
||||
|
||||
// Next two CompactRange() calls are used to test exercise error paths within
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ("0,2,2", FilesPerLevel(0));
|
||||
|
||||
// The next CompactRange() call is used to test exercise error paths within
|
||||
// RefitLevel() before triggering a valid RefitLevel() call
|
||||
|
||||
// Trigger a refit to L1 first
|
||||
{
|
||||
std::string begin_string = Key(start_idx);
|
||||
std::string end_string = Key(end_idx);
|
||||
Slice begin(begin_string);
|
||||
Slice end(end_string);
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 1;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, &begin, &end));
|
||||
}
|
||||
ASSERT_EQ("0,3,2", FilesPerLevel(0));
|
||||
|
||||
//
|
||||
// Try a refit from L2->L1 - this should fail and exercise error paths in
|
||||
// RefitLevel()
|
||||
{
|
||||
@@ -7776,7 +7988,7 @@ TEST_F(DBCompactionTest, ChangeLevelErrorPathTest) {
|
||||
cro.target_level = 1;
|
||||
ASSERT_NOK(dbfull()->CompactRange(cro, &begin, &end));
|
||||
}
|
||||
ASSERT_EQ("0,3,2", FilesPerLevel(0));
|
||||
ASSERT_EQ("0,2,2", FilesPerLevel(0));
|
||||
|
||||
// Try a valid Refit request to ensure, the path is still working
|
||||
{
|
||||
@@ -7789,10 +8001,8 @@ TEST_F(DBCompactionTest, ChangeLevelErrorPathTest) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionWithBlob) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
@@ -7884,10 +8094,8 @@ INSTANTIATE_TEST_CASE_P(DBCompactionTestBlobError, DBCompactionTestBlobError,
|
||||
"BlobFileBuilder::WriteBlobToFile:AppendFooter"}));
|
||||
|
||||
TEST_P(DBCompactionTestBlobError, CompactionError) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
@@ -8053,8 +8261,7 @@ TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGCOverrides) {
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGC) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = 32; // one blob per file
|
||||
@@ -8593,7 +8800,7 @@ TEST_F(DBCompactionTest, CompactionWithChecksumHandoffManifest2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, FIFOWarm) {
|
||||
TEST_F(DBCompactionTest, FIFOChangeTemperature) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
@@ -8601,18 +8808,18 @@ TEST_F(DBCompactionTest, FIFOWarm) {
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.age_for_warm = 1000;
|
||||
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);
|
||||
|
||||
int total_warm = 0;
|
||||
int total_cold = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kWarm) {
|
||||
total_warm++;
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -8649,9 +8856,9 @@ TEST_F(DBCompactionTest, FIFOWarm) {
|
||||
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::kWarm, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_warm);
|
||||
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);
|
||||
}
|
||||
@@ -8716,7 +8923,7 @@ TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
|
||||
@@ -8848,7 +9055,7 @@ TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFull) {
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
}
|
||||
|
||||
@@ -9413,6 +9620,187 @@ TEST_F(DBCompactionTest, DrainUnnecessaryLevelsAfterDBBecomesSmall) {
|
||||
ASSERT_GT(after_delete_range_nonempty, final_num_nonempty);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ManualCompactionCompactAllKeysInRange) {
|
||||
// CompactRange() used to pre-compute target level to compact to
|
||||
// before running compactions. However, the files at target level
|
||||
// could be trivially moved down by some background compaction. This means
|
||||
// some keys in the manual compaction key range may not be compacted
|
||||
// during the manual compaction. This unit test tests this scenario.
|
||||
// A fix has been applied for this scenario to always compact
|
||||
// to the bottommost level.
|
||||
const int kBaseLevelBytes = 8 << 20; // 8MB
|
||||
const int kMultiplier = 2;
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 7;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.max_bytes_for_level_base = kBaseLevelBytes;
|
||||
options.max_bytes_for_level_multiplier = kMultiplier;
|
||||
options.compression = kNoCompression;
|
||||
options.target_file_size_base = 2 * kBaseLevelBytes;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
// Populate L2 so that manual compaction will compact to at least L2.
|
||||
// Otherwise, there is still a possibility of race condition where
|
||||
// the manual compaction thread believes that max non-empty level is L1
|
||||
// while there is some auto compaction that moves some files from L1 to L2.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1000), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(2));
|
||||
|
||||
// one file in L1: [Key(5), Key(6)]
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(5), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(6), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(1), rnd.RandomString(kBaseLevelBytes / 2)));
|
||||
// We now do manual compaction for key range [Key(1), Key(6)].
|
||||
// First it compacts file [Key(1)] to L1.
|
||||
// L1 will have two files [Key(1)], and [Key(5), Key(6)].
|
||||
// After L0 -> L1 manual compaction, an automatic compaction will trivially
|
||||
// move both files from L1 to L2. Here the dependency makes manual compaction
|
||||
// wait for auto-compaction to pick a compaction before proceeding. Manual
|
||||
// compaction should not stop at L1 and keep compacting L2. With kForce
|
||||
// specified, expected output is that manual compaction compacts to L2 and L2
|
||||
// will contain 2 files: one for Key(1000) and one for Key(1), Key(5) and
|
||||
// Key(6).
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction():AfterPickCompaction",
|
||||
"DBImpl::RunManualCompaction()::1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::string begin_str = Key(1);
|
||||
std::string end_str = Key(6);
|
||||
Slice begin_slice = begin_str;
|
||||
Slice end_slice = end_str;
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, &begin_slice, &end_slice));
|
||||
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 2);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest,
|
||||
ManualCompactionCompactAllKeysInRangeDynamicLevelBytes) {
|
||||
// Similar to the test above (ManualCompactionCompactAllKeysInRange), but with
|
||||
// level_compaction_dynamic_level_bytes = true.
|
||||
const int kBaseLevelBytes = 8 << 20; // 8MB
|
||||
const int kMultiplier = 2;
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 7;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.max_bytes_for_level_base = kBaseLevelBytes;
|
||||
options.max_bytes_for_level_multiplier = kMultiplier;
|
||||
options.compression = kNoCompression;
|
||||
options.target_file_size_base = 2 * kBaseLevelBytes;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(5),
|
||||
rnd.RandomString(3 * kBaseLevelBytes / 2)));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(6));
|
||||
// L6 now has one file with size ~ 3/2 * kBaseLevelBytes.
|
||||
// L5 is the new base level, with target size ~ 3/4 * kBaseLevelBytes.
|
||||
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(3), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(4), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
MoveFilesToLevel(5);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(5));
|
||||
// L5 now has one file with size ~ 2/3 * kBaseLevelBytes, which is below its
|
||||
// target size.
|
||||
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(1), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(2), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction():AfterPickCompaction",
|
||||
"DBImpl::RunManualCompaction()::1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
// After compacting the file with [Key(1), Key(2)] to L5,
|
||||
// L5 has size ~ 4/3 * kBaseLevelBytes > its target size.
|
||||
// We let manual compaction wait for an auto-compaction to pick
|
||||
// a compaction before proceeding. The auto-compaction would
|
||||
// trivially move both files in L5 down to L6. If manual compaction
|
||||
// works correctly with kForce specified, it should rewrite the two files in
|
||||
// L6 into a single file.
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
std::string begin_str = Key(1);
|
||||
std::string end_str = Key(4);
|
||||
Slice begin_slice = begin_str;
|
||||
Slice end_slice = end_str;
|
||||
ASSERT_OK(db_->CompactRange(cro, &begin_slice, &end_slice));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(6));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(5));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, NumberOfSubcompactions) {
|
||||
// Tests that expected number of subcompactions are created.
|
||||
class SubCompactionEventListener : public EventListener {
|
||||
public:
|
||||
void OnSubcompactionCompleted(const SubcompactionJobInfo&) override {
|
||||
sub_compaction_finished_++;
|
||||
}
|
||||
void OnCompactionCompleted(DB*, const CompactionJobInfo&) override {
|
||||
compaction_finished_++;
|
||||
}
|
||||
std::atomic<int> sub_compaction_finished_{0};
|
||||
std::atomic<int> compaction_finished_{0};
|
||||
};
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.compression = kNoCompression;
|
||||
const int kFileSize = 100 << 10; // 100KB
|
||||
options.target_file_size_base = kFileSize;
|
||||
const int kLevel0CompactTrigger = 2;
|
||||
options.level0_file_num_compaction_trigger = kLevel0CompactTrigger;
|
||||
Destroy(options);
|
||||
Random rnd(301);
|
||||
|
||||
// Exposing internal implementation detail here where the
|
||||
// number of subcompactions depends on the size of data
|
||||
// being compacted. In particular, to enable x subcompactions,
|
||||
// we need to compact at least x * target file size amount
|
||||
// of data.
|
||||
//
|
||||
// Will write two files below to avoid trivial move.
|
||||
// Size written in total: 500 * 1000 * 2 ~ 10MB ~ 100 * target file size.
|
||||
const int kValueSize = 500;
|
||||
const int kNumKeyPerFile = 1000;
|
||||
for (int i = 1; i <= 8; ++i) {
|
||||
options.max_subcompactions = i;
|
||||
SubCompactionEventListener* listener = new SubCompactionEventListener();
|
||||
options.listeners.clear();
|
||||
options.listeners.emplace_back(listener);
|
||||
TryReopen(options);
|
||||
|
||||
for (int file = 0; file < kLevel0CompactTrigger; ++file) {
|
||||
for (int key = file; key < 2 * kNumKeyPerFile; key += 2) {
|
||||
ASSERT_OK(Put(Key(key), rnd.RandomString(kValueSize)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(listener->compaction_finished_, 1);
|
||||
EXPECT_EQ(listener->sub_compaction_finished_, i);
|
||||
Destroy(options);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+2
-29
@@ -29,35 +29,8 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status DBImpl::FlushForGetLiveFiles() {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
// flush all dirty data to disk.
|
||||
Status status;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
return DBImpl::FlushAllColumnFamilies(FlushOptions(),
|
||||
FlushReason::kGetLiveFiles);
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
@@ -118,16 +118,24 @@ class CompactedDBImpl : public DBImpl {
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
const std::vector<const ExportImportFilesMetaData*>& /*metadatas*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
// FIXME: some missing overrides for more "write" functions
|
||||
// Share with DBImplReadOnly?
|
||||
|
||||
|
||||
+102
-7
@@ -258,6 +258,9 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
co.capacity = table_cache_size;
|
||||
co.num_shard_bits = immutable_db_options_.table_cache_numshardbits;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
// TODO: Consider a non-fixed seed once test fallout (prefetch_test) is
|
||||
// dealt with
|
||||
co.hash_seed = 0;
|
||||
table_cache_ = NewLRUCache(co);
|
||||
SetDbSessionId();
|
||||
assert(!db_session_id_.empty());
|
||||
@@ -2475,7 +2478,6 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
|
||||
// Post processing (decrement reference counts and record statistics)
|
||||
PERF_TIMER_GUARD(get_post_process_time);
|
||||
autovector<SuperVersion*> superversions_to_delete;
|
||||
|
||||
for (auto mgd_iter : multiget_cf_data) {
|
||||
auto mgd = mgd_iter.second;
|
||||
@@ -4520,7 +4522,6 @@ void DBImpl::GetAllColumnFamilyMetaData(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Status DBImpl::CheckConsistency() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
@@ -4911,6 +4912,15 @@ Status DBImpl::WriteOptionsFile(bool need_mutex_lock,
|
||||
if (s.ok()) {
|
||||
s = RenameTempFileToOptionsFile(file_name);
|
||||
}
|
||||
|
||||
if (!s.ok() && GetEnv()->FileExists(file_name).ok()) {
|
||||
if (!GetEnv()->DeleteFile(file_name).ok()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"Unable to delete temp options file %s",
|
||||
file_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// restore lock
|
||||
if (!need_mutex_lock) {
|
||||
mutex_.Lock();
|
||||
@@ -5565,14 +5575,24 @@ Status DBImpl::IngestExternalFiles(
|
||||
Status DBImpl::CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const ExportImportFilesMetaData& metadata, ColumnFamilyHandle** handle) {
|
||||
const std::vector<const ExportImportFilesMetaData*>& metadatas,
|
||||
ColumnFamilyHandle** handle) {
|
||||
assert(handle != nullptr);
|
||||
assert(*handle == nullptr);
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
std::string cf_comparator_name = options.comparator->Name();
|
||||
if (cf_comparator_name != metadata.db_comparator_name) {
|
||||
return Status::InvalidArgument("Comparator name mismatch");
|
||||
|
||||
size_t total_file_num = 0;
|
||||
std::vector<std::vector<LiveFileMetaData*>> metadata_files(metadatas.size());
|
||||
for (size_t i = 0; i < metadatas.size(); i++) {
|
||||
if (cf_comparator_name != metadatas[i]->db_comparator_name) {
|
||||
return Status::InvalidArgument("Comparator name mismatch");
|
||||
}
|
||||
for (auto& file : metadatas[i]->files) {
|
||||
metadata_files[i].push_back((LiveFileMetaData*)&file);
|
||||
}
|
||||
total_file_num += metadatas[i]->files.size();
|
||||
}
|
||||
|
||||
// Create column family.
|
||||
@@ -5586,7 +5606,7 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
auto cfd = cfh->cfd();
|
||||
ImportColumnFamilyJob import_job(versions_.get(), cfd, immutable_db_options_,
|
||||
file_options_, import_options,
|
||||
metadata.files, io_tracer_);
|
||||
metadata_files, io_tracer_);
|
||||
|
||||
SuperVersionContext dummy_sv_ctx(/* create_superversion */ true);
|
||||
VersionEdit dummy_edit;
|
||||
@@ -5609,7 +5629,7 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
// reuse the file number that has already assigned to the internal file,
|
||||
// and this will overwrite the external file. To protect the external
|
||||
// file, we have to make sure the file number will never being reused.
|
||||
next_file_number = versions_->FetchAddFileNumber(metadata.files.size());
|
||||
next_file_number = versions_->FetchAddFileNumber(total_file_num);
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status =
|
||||
versions_->LogAndApply(cfd, *cf_options, read_options, &dummy_edit,
|
||||
@@ -5694,6 +5714,81 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::ClipColumnFamily(ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key, const Slice& end_key) {
|
||||
assert(column_family);
|
||||
Status status;
|
||||
// Flush memtable
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
auto* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(column_family)->cfd();
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
status = AtomicFlushMemTables(flush_opts, FlushReason::kDeleteFiles,
|
||||
{} /* provided_candidate_cfds */,
|
||||
false /* entered_write_thread */);
|
||||
} else {
|
||||
status = FlushMemTable(cfd, flush_opts, FlushReason::kDeleteFiles,
|
||||
false /* entered_write_thread */);
|
||||
}
|
||||
|
||||
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));
|
||||
status = DeleteFilesInRanges(column_family, ranges.data(), ranges.size());
|
||||
}
|
||||
|
||||
// DeleteRange the remaining overlapping keys
|
||||
bool empty_after_delete = false;
|
||||
if (status.ok()) {
|
||||
Slice smallest_user_key, largest_user_key;
|
||||
{
|
||||
// Lock db mutex
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
cfd->current()->GetSstFilesBoundaryKeys(&smallest_user_key,
|
||||
&largest_user_key);
|
||||
}
|
||||
// all the files has been deleted after DeleteFilesInRanges;
|
||||
if (smallest_user_key.empty() && largest_user_key.empty()) {
|
||||
empty_after_delete = true;
|
||||
} else {
|
||||
const Comparator* const ucmp = column_family->GetComparator();
|
||||
WriteOptions wo;
|
||||
// Delete [smallest_user_key, clip_begin_key)
|
||||
if (ucmp->Compare(smallest_user_key, begin_key) < 0) {
|
||||
status = DeleteRange(wo, column_family, smallest_user_key, begin_key);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
// Delete [clip_end_key, largest_use_key]
|
||||
if (ucmp->Compare(end_key, largest_user_key) < 0) {
|
||||
status = DeleteRange(wo, column_family, end_key, largest_user_key);
|
||||
if (status.ok()) {
|
||||
status = Delete(wo, column_family, largest_user_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status.ok() && !empty_after_delete) {
|
||||
// CompactRange delete all the tombstones
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = true;
|
||||
compact_options.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
// We could just compact the ranges [null, clip_begin_key] and
|
||||
// [clip_end_key, null]. But due to how manual compaction calculates the
|
||||
// last level to compact to and that range tombstones are not dropped
|
||||
// during non-bottommost compactions, calling CompactRange() on these two
|
||||
// ranges may not clear all range tombstones.
|
||||
status = CompactRange(compact_options, nullptr, nullptr);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifyFileChecksums(const ReadOptions& read_options) {
|
||||
return VerifyChecksumInternal(read_options, /*use_file_checksum=*/true);
|
||||
}
|
||||
|
||||
+17
-10
@@ -531,9 +531,14 @@ class DBImpl : public DB {
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const ExportImportFilesMetaData& metadata,
|
||||
const std::vector<const ExportImportFilesMetaData*>& metadatas,
|
||||
ColumnFamilyHandle** handle) override;
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* column_family,
|
||||
const Slice& begin_key,
|
||||
const Slice& end_key) override;
|
||||
|
||||
using DB::VerifyFileChecksums;
|
||||
Status VerifyFileChecksums(const ReadOptions& read_options) override;
|
||||
|
||||
@@ -806,6 +811,8 @@ class DBImpl : public DB {
|
||||
// being deleted.
|
||||
uint64_t MinObsoleteSstNumberToKeep();
|
||||
|
||||
uint64_t GetObsoleteSstFilesSize();
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
// of all files in the filesystem in 'candidate_files'.
|
||||
// If force == false and the last call was less than
|
||||
@@ -1053,10 +1060,8 @@ class DBImpl : public DB {
|
||||
|
||||
VersionSet* GetVersionSet() const { return versions_.get(); }
|
||||
|
||||
// Wait for any compaction
|
||||
// We add a bool parameter to wait for unscheduledCompactions_ == 0, but this
|
||||
// is only for the special test of CancelledCompactions
|
||||
Status WaitForCompact(bool waitUnscheduled = false);
|
||||
Status WaitForCompact(
|
||||
const WaitForCompactOptions& wait_for_compact_options) override;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Compact any files in the named level that overlap [*begin, *end]
|
||||
@@ -1095,10 +1100,9 @@ class DBImpl : public DB {
|
||||
// Wait for memtable compaction
|
||||
Status TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
// Wait for any compaction
|
||||
// We add a bool parameter to wait for unscheduledCompactions_ == 0, but this
|
||||
// is only for the special test of CancelledCompactions
|
||||
Status TEST_WaitForCompact(bool waitUnscheduled = false);
|
||||
Status TEST_WaitForCompact();
|
||||
Status TEST_WaitForCompact(
|
||||
const WaitForCompactOptions& wait_for_compact_options);
|
||||
|
||||
// Wait for any background purge
|
||||
Status TEST_WaitForPurge();
|
||||
@@ -1408,6 +1412,9 @@ class DBImpl : public DB {
|
||||
void NotifyOnExternalFileIngested(
|
||||
ColumnFamilyData* cfd, const ExternalSstFileIngestionJob& ingestion_job);
|
||||
|
||||
Status FlushAllColumnFamilies(const FlushOptions& flush_options,
|
||||
FlushReason flush_reason);
|
||||
|
||||
virtual Status FlushForGetLiveFiles();
|
||||
|
||||
void NewThreadStatusCfInfo(ColumnFamilyData* cfd) const;
|
||||
@@ -2310,7 +2317,7 @@ class DBImpl : public DB {
|
||||
// logfile_number_. With two_write_queues it also protects alive_log_files_,
|
||||
// and log_empty_. Refer to the definition of each variable below for more
|
||||
// details.
|
||||
// Note: to avoid dealock, if needed to acquire both log_write_mutex_ and
|
||||
// Note: to avoid deadlock, if needed to acquire both log_write_mutex_ and
|
||||
// mutex_, the order should be first mutex_ and then log_write_mutex_.
|
||||
InstrumentedMutex log_write_mutex_;
|
||||
|
||||
|
||||
@@ -852,8 +852,8 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
// no need to signal bg_cv_ as it will be signaled at the end of the
|
||||
// flush process.
|
||||
// no need to signal bg_cv_ as it will be signaled at the end of the
|
||||
// flush process.
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnFlushCompleted(
|
||||
@@ -1066,7 +1066,6 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
std::numeric_limits<uint64_t>::max(), trim_ts);
|
||||
} else {
|
||||
int first_overlapped_level = kInvalidLevel;
|
||||
int max_overlapped_level = kInvalidLevel;
|
||||
{
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
Version* current_version = super_version->current;
|
||||
@@ -1142,10 +1141,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
begin, end);
|
||||
}
|
||||
if (overlap) {
|
||||
if (first_overlapped_level == kInvalidLevel) {
|
||||
first_overlapped_level = level;
|
||||
}
|
||||
max_overlapped_level = level;
|
||||
first_overlapped_level = level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CleanupSuperVersion(super_version);
|
||||
@@ -1159,7 +1156,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
end, exclusive, true /* disallow_trivial_move */,
|
||||
std::numeric_limits<uint64_t>::max() /* max_file_num_to_ignore */,
|
||||
trim_ts);
|
||||
final_output_level = max_overlapped_level;
|
||||
final_output_level = first_overlapped_level;
|
||||
} else {
|
||||
assert(cfd->ioptions()->compaction_style == kCompactionStyleLevel);
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
@@ -1170,8 +1167,30 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
// at L1 (or LBase), if applicable.
|
||||
int level = first_overlapped_level;
|
||||
final_output_level = level;
|
||||
int output_level, base_level;
|
||||
while (level < max_overlapped_level || level == 0) {
|
||||
int output_level = 0, base_level = 0;
|
||||
for (;;) {
|
||||
// Always allow L0 -> L1 compaction
|
||||
if (level > 0) {
|
||||
if (cfd->ioptions()->level_compaction_dynamic_level_bytes) {
|
||||
assert(final_output_level < cfd->ioptions()->num_levels);
|
||||
if (final_output_level + 1 == cfd->ioptions()->num_levels) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// TODO(cbi): there is still a race condition here where
|
||||
// if a background compaction compacts some file beyond
|
||||
// current()->storage_info()->num_non_empty_levels() right after
|
||||
// the check here.This should happen very infrequently and should
|
||||
// not happen once a user populates the last level of the LSM.
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// num_non_empty_levels may be lower after a compaction, so
|
||||
// we check for >= here.
|
||||
if (final_output_level + 1 >=
|
||||
cfd->current()->storage_info()->num_non_empty_levels()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
output_level = level + 1;
|
||||
if (cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
level == 0) {
|
||||
@@ -1203,17 +1222,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
if (s.ok()) {
|
||||
assert(final_output_level > 0);
|
||||
// bottommost level intra-level compaction
|
||||
// TODO(cbi): this preserves earlier behavior where if
|
||||
// max_overlapped_level = 0 and bottommost_level_compaction is
|
||||
// kIfHaveCompactionFilter, we only do a L0 -> LBase compaction
|
||||
// and do not do intra-LBase compaction even when user configures
|
||||
// compaction filter. We may want to still do a LBase -> LBase
|
||||
// compaction in case there is some file in LBase that did not go
|
||||
// through L0 -> LBase compaction, and hence did not go through
|
||||
// compaction filter.
|
||||
if ((options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter &&
|
||||
max_overlapped_level != 0 &&
|
||||
(cfd->ioptions()->compaction_filter != nullptr ||
|
||||
cfd->ioptions()->compaction_filter_factory != nullptr)) ||
|
||||
options.bottommost_level_compaction ==
|
||||
@@ -1221,10 +1231,11 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForce) {
|
||||
// Use `next_file_number` as `max_file_num_to_ignore` to avoid
|
||||
// rewriting newly compacted files when it is kForceOptimized.
|
||||
// rewriting newly compacted files when it is kForceOptimized
|
||||
// or kIfHaveCompactionFilter with compaction filter set.
|
||||
s = RunManualCompaction(
|
||||
cfd, final_output_level, final_output_level, options, begin,
|
||||
end, exclusive, !trim_ts.empty() /* disallow_trivial_move */,
|
||||
end, exclusive, true /* disallow_trivial_move */,
|
||||
next_file_number /* max_file_num_to_ignore */, trim_ts);
|
||||
}
|
||||
}
|
||||
@@ -1375,6 +1386,14 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
}
|
||||
|
||||
if (cfd->ioptions()->allow_ingest_behind &&
|
||||
output_level >= cfd->ioptions()->num_levels - 1) {
|
||||
return Status::InvalidArgument(
|
||||
"Exceed the maximum output level defined by "
|
||||
"the current compaction algorithm with ingest_behind --- " +
|
||||
std::to_string(cfd->ioptions()->num_levels - 1));
|
||||
}
|
||||
|
||||
Status s = cfd->compaction_picker()->SanitizeCompactionInputFiles(
|
||||
&input_set, cf_meta, output_level);
|
||||
TEST_SYNC_POINT("DBImpl::CompactFilesImpl::PostSanitizeCompactionInputFiles");
|
||||
@@ -1758,7 +1777,8 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
f->marked_for_compaction, f->temperature, f->oldest_blob_file_number,
|
||||
f->oldest_ancester_time, f->file_creation_time, f->epoch_number,
|
||||
f->file_checksum, f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size);
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
f->user_defined_timestamps_persisted);
|
||||
}
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] Apply version edit:\n%s", cfd->GetName().c_str(),
|
||||
@@ -1808,6 +1828,37 @@ int DBImpl::Level0StopWriteTrigger(ColumnFamilyHandle* column_family) {
|
||||
->mutable_cf_options.level0_stop_writes_trigger;
|
||||
}
|
||||
|
||||
Status DBImpl::FlushAllColumnFamilies(const FlushOptions& flush_options,
|
||||
FlushReason flush_reason) {
|
||||
mutex_.AssertHeld();
|
||||
Status status;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(flush_options, flush_reason);
|
||||
if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, flush_options, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::FlushAllColumnFamilies:1");
|
||||
TEST_SYNC_POINT("DBImpl::FlushAllColumnFamilies:2");
|
||||
mutex_.Lock();
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::Flush(const FlushOptions& flush_options,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
@@ -2612,6 +2663,9 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
env_->GetBackgroundThreads(Env::Priority::HIGH) == 0;
|
||||
while (!is_flush_pool_empty && unscheduled_flushes_ > 0 &&
|
||||
bg_flush_scheduled_ < bg_job_limits.max_flushes) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:BeforeSchedule",
|
||||
&unscheduled_flushes_);
|
||||
bg_flush_scheduled_++;
|
||||
FlushThreadArg* fta = new FlushThreadArg;
|
||||
fta->db_ = this;
|
||||
@@ -2721,7 +2775,7 @@ ColumnFamilyData* DBImpl::PopFirstFromCompactionQueue() {
|
||||
|
||||
DBImpl::FlushRequest DBImpl::PopFirstFromFlushQueue() {
|
||||
assert(!flush_queue_.empty());
|
||||
FlushRequest flush_req = flush_queue_.front();
|
||||
FlushRequest flush_req = std::move(flush_queue_.front());
|
||||
flush_queue_.pop_front();
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
assert(flush_req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||
@@ -2916,14 +2970,12 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
autovector<ColumnFamilyData*> column_families_not_to_flush;
|
||||
while (!flush_queue_.empty()) {
|
||||
// This cfd is already referenced
|
||||
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
||||
FlushReason flush_reason = flush_req.flush_reason;
|
||||
auto [flush_reason, cfd_to_max_mem_id_to_persist] =
|
||||
PopFirstFromFlushQueue();
|
||||
superversion_contexts.clear();
|
||||
superversion_contexts.reserve(
|
||||
flush_req.cfd_to_max_mem_id_to_persist.size());
|
||||
superversion_contexts.reserve(cfd_to_max_mem_id_to_persist.size());
|
||||
|
||||
for (const auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
for (const auto& [cfd, max_memtable_id] : cfd_to_max_mem_id_to_persist) {
|
||||
if (cfd->GetMempurgeUsed()) {
|
||||
// If imm() contains silent memtables (e.g.: because
|
||||
// MemPurge was activated), requesting a flush will
|
||||
@@ -2937,7 +2989,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
continue;
|
||||
}
|
||||
superversion_contexts.emplace_back(SuperVersionContext(true));
|
||||
bg_flush_args.emplace_back(cfd, iter.second,
|
||||
bg_flush_args.emplace_back(cfd, max_memtable_id,
|
||||
&(superversion_contexts.back()), flush_reason);
|
||||
}
|
||||
if (!bg_flush_args.empty()) {
|
||||
@@ -3457,7 +3509,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number, f->file_checksum,
|
||||
f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size);
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
f->user_defined_timestamps_persisted);
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
@@ -3569,7 +3622,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun", nullptr);
|
||||
// Should handle erorr?
|
||||
// Should handle error?
|
||||
compaction_job.Run().PermitUncheckedError();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:NonTrivial:AfterRun");
|
||||
mutex_.Lock();
|
||||
@@ -3957,16 +4010,33 @@ void DBImpl::GetSnapshotContext(
|
||||
*snapshot_seqs = snapshots_.GetAll(earliest_write_conflict_snapshot);
|
||||
}
|
||||
|
||||
Status DBImpl::WaitForCompact(bool wait_unscheduled) {
|
||||
// Wait until the compaction completes
|
||||
Status DBImpl::WaitForCompact(
|
||||
const WaitForCompactOptions& wait_for_compact_options) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while ((bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ ||
|
||||
(wait_unscheduled && unscheduled_compactions_)) &&
|
||||
(error_handler_.GetBGError().ok())) {
|
||||
bg_cv_.Wait();
|
||||
if (wait_for_compact_options.flush) {
|
||||
Status s = DBImpl::FlushAllColumnFamilies(FlushOptions(),
|
||||
FlushReason::kManualFlush);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::WaitForCompact:StartWaiting");
|
||||
for (;;) {
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
if (bg_work_paused_ && wait_for_compact_options.abort_on_pause) {
|
||||
return Status::Aborted();
|
||||
}
|
||||
if ((bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ || unscheduled_compactions_ ||
|
||||
unscheduled_flushes_) &&
|
||||
(error_handler_.GetBGError().ok())) {
|
||||
bg_cv_.Wait();
|
||||
} else {
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
}
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -178,9 +178,12 @@ Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
return WaitForFlushMemTable(cfd, nullptr, false);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForCompact(bool wait_unscheduled) {
|
||||
// Wait until the compaction completes
|
||||
return WaitForCompact(wait_unscheduled);
|
||||
Status DBImpl::TEST_WaitForCompact() {
|
||||
return WaitForCompact(WaitForCompactOptions());
|
||||
}
|
||||
Status DBImpl::TEST_WaitForCompact(
|
||||
const WaitForCompactOptions& wait_for_compact_options) {
|
||||
return WaitForCompact(wait_for_compact_options);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForPurge() {
|
||||
|
||||
@@ -138,7 +138,8 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number, f->file_checksum,
|
||||
f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size);
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
f->user_defined_timestamps_persisted);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
|
||||
@@ -35,6 +35,11 @@ uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::GetObsoleteSstFilesSize() {
|
||||
mutex_.AssertHeld();
|
||||
return versions_->GetObsoleteSstFilesSize();
|
||||
}
|
||||
|
||||
Status DBImpl::DisableFileDeletions() {
|
||||
Status s;
|
||||
int my_disable_delete_obsolete_files;
|
||||
@@ -286,6 +291,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
return;
|
||||
}
|
||||
|
||||
bool mutex_unlocked = false;
|
||||
if (!alive_log_files_.empty() && !logs_.empty()) {
|
||||
uint64_t min_log_number = job_context->log_number;
|
||||
size_t num_alive_log_files = alive_log_files_.size();
|
||||
@@ -315,6 +321,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
mutex_.Unlock();
|
||||
mutex_unlocked = true;
|
||||
TEST_SYNC_POINT_CALLBACK("FindObsoleteFiles::PostMutexUnlock", nullptr);
|
||||
log_write_mutex_.Lock();
|
||||
while (!logs_.empty() && logs_.front().number < min_log_number) {
|
||||
@@ -337,7 +344,9 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
|
||||
logs_to_free_.clear();
|
||||
log_write_mutex_.Unlock();
|
||||
mutex_.Lock();
|
||||
if (mutex_unlocked) {
|
||||
mutex_.Lock();
|
||||
}
|
||||
job_context->log_recycle_files.assign(log_recycle_files_.begin(),
|
||||
log_recycle_files_.end());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/rate_limiter_impl.h"
|
||||
#include "util/udt_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
Options SanitizeOptions(const std::string& dbname, const Options& src,
|
||||
@@ -616,7 +617,8 @@ Status DBImpl::Recover(
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number,
|
||||
f->file_checksum, f->file_checksum_func_name,
|
||||
f->unique_id, f->compensated_range_deletion_size);
|
||||
f->unique_id, f->compensated_range_deletion_size,
|
||||
f->tail_size, f->user_defined_timestamps_persisted);
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] Moving #%" PRIu64
|
||||
" from from_level-%d to from_level-%d %" PRIu64
|
||||
@@ -1185,6 +1187,9 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& running_ts_sz =
|
||||
versions_->GetRunningColumnFamiliesTimestampSize();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::RecoverLogFiles:BeforeReadWal",
|
||||
/*arg=*/nullptr);
|
||||
uint64_t record_checksum;
|
||||
@@ -1207,6 +1212,17 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& record_ts_sz =
|
||||
reader.GetRecordedTimestampSize();
|
||||
// TODO(yuzhangyu): update mode to kReconcileInconsistency when user
|
||||
// comparator can be changed.
|
||||
status = HandleWriteBatchTimestampSizeDifference(
|
||||
&batch, running_ts_sz, record_ts_sz,
|
||||
TimestampSizeConsistencyMode::kVerifyConsistency);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::RecoverLogFiles:BeforeUpdateProtectionInfo:batch", &batch);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
@@ -1453,7 +1469,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
recovery_ctx->UpdateVersionEdits(cfd, iter->second);
|
||||
}
|
||||
|
||||
if (flushed) {
|
||||
if (flushed || !data_seen) {
|
||||
VersionEdit wal_deletion;
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion.DeleteWalsBefore(max_wal_number + 1);
|
||||
@@ -1639,6 +1655,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
Version* version = cfd->current();
|
||||
version->Ref();
|
||||
const ReadOptions read_option(Env::IOActivity::kDBOpen);
|
||||
uint64_t num_input_entries = 0;
|
||||
s = BuildTable(
|
||||
dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, read_option, cfd->table_cache(),
|
||||
@@ -1648,7 +1665,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
io_tracer_, BlobFileCreationReason::kRecovery,
|
||||
empty_seqno_time_mapping, &event_logger_, job_id, Env::IO_HIGH,
|
||||
nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version);
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
&num_input_entries);
|
||||
version->Unref();
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
@@ -1662,6 +1680,19 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
if (!io_s.ok() && s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
|
||||
uint64_t total_num_entries = mem->num_entries();
|
||||
if (s.ok() && total_num_entries != num_input_entries) {
|
||||
std::string msg = "Expected " + std::to_string(total_num_entries) +
|
||||
" entries in memtable, but read " +
|
||||
std::to_string(num_input_entries);
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush during recover: %s",
|
||||
cfd->GetName().c_str(), job_id, msg.c_str());
|
||||
if (immutable_db_options_.flush_verify_memtable_count) {
|
||||
s = Status::Corruption(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
@@ -1680,7 +1711,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
meta.oldest_blob_file_number, meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.epoch_number,
|
||||
meta.file_checksum, meta.file_checksum_func_name,
|
||||
meta.unique_id, meta.compensated_range_deletion_size);
|
||||
meta.unique_id, meta.compensated_range_deletion_size,
|
||||
meta.tail_size, meta.user_defined_timestamps_persisted);
|
||||
|
||||
for (const auto& blob : blob_file_additions) {
|
||||
edit->AddBlobFile(blob);
|
||||
|
||||
@@ -46,7 +46,8 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
assert(pinnable_val != nullptr);
|
||||
// TODO: stopwatch DB_GET needed?, perf timer needed?
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
assert(column_family);
|
||||
|
||||
@@ -142,6 +142,22 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const std::vector<const ExportImportFilesMetaData*>& /*metadatas*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
// FIXME: some missing overrides for more "write" functions
|
||||
|
||||
protected:
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "rocksdb/configurable.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/write_batch_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -197,6 +198,9 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
}
|
||||
assert(reader != nullptr);
|
||||
}
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& running_ts_sz =
|
||||
versions_->GetRunningColumnFamiliesTimestampSize();
|
||||
for (auto log_number : log_numbers) {
|
||||
auto it = log_readers_.find(log_number);
|
||||
assert(it != log_readers_.end());
|
||||
@@ -224,6 +228,14 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
const UnorderedMap<uint32_t, size_t>& record_ts_sz =
|
||||
reader->GetRecordedTimestampSize();
|
||||
status = HandleWriteBatchTimestampSizeDifference(
|
||||
&batch, running_ts_sz, record_ts_sz,
|
||||
TimestampSizeConsistencyMode::kVerifyConsistency);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
SequenceNumber seq_of_batch = WriteBatchInternal::Sequence(&batch);
|
||||
std::vector<uint32_t> column_family_ids;
|
||||
status = CollectColumnFamilyIdsFromWriteBatch(batch, &column_family_ids);
|
||||
|
||||
@@ -273,85 +273,6 @@ class DBImplSecondary : public DBImpl {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// ColumnFamilyCollector is a write batch handler which does nothing
|
||||
// except recording unique column family IDs
|
||||
class ColumnFamilyCollector : public WriteBatch::Handler {
|
||||
std::unordered_set<uint32_t> column_family_ids_;
|
||||
|
||||
Status AddColumnFamilyId(uint32_t column_family_id) {
|
||||
if (column_family_ids_.find(column_family_id) ==
|
||||
column_family_ids_.end()) {
|
||||
column_family_ids_.insert(column_family_id);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ColumnFamilyCollector() {}
|
||||
|
||||
~ColumnFamilyCollector() override {}
|
||||
|
||||
Status PutCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status DeleteCF(uint32_t column_family_id, const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status SingleDeleteCF(uint32_t column_family_id, const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status DeleteRangeCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status MergeCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status PutBlobIndexCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status MarkBeginPrepare(bool) override { return Status::OK(); }
|
||||
|
||||
Status MarkEndPrepare(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkRollback(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkCommit(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkCommitWithTimestamp(const Slice&, const Slice&) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status MarkNoop(bool) override { return Status::OK(); }
|
||||
|
||||
const std::unordered_set<uint32_t>& column_families() const {
|
||||
return column_family_ids_;
|
||||
}
|
||||
};
|
||||
|
||||
Status CollectColumnFamilyIdsFromWriteBatch(
|
||||
const WriteBatch& batch, std::vector<uint32_t>* column_family_ids) {
|
||||
assert(column_family_ids != nullptr);
|
||||
column_family_ids->clear();
|
||||
ColumnFamilyCollector handler;
|
||||
Status s = batch.Iterate(&handler);
|
||||
if (s.ok()) {
|
||||
for (const auto& cf : handler.column_families()) {
|
||||
column_family_ids->push_back(cf);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool OwnTablesAndLogs() const override {
|
||||
// Currently, the secondary instance does not own the database files. It
|
||||
// simply opens the files of the primary instance and tracks their file
|
||||
|
||||
@@ -1328,7 +1328,13 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
IOStatus io_s = log_writer->AddRecord(log_entry, rate_limiter_priority);
|
||||
IOStatus io_s = log_writer->MaybeAddUserDefinedTimestampSizeRecord(
|
||||
versions_->GetColumnFamiliesTimestampSizeForRecord(),
|
||||
rate_limiter_priority);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
io_s = log_writer->AddRecord(log_entry, rate_limiter_priority);
|
||||
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
|
||||
@@ -964,9 +964,6 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
assert(last_key_entry_type == ikey_.type);
|
||||
}
|
||||
|
||||
Status s;
|
||||
s.PermitUncheckedError();
|
||||
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeDeletionWithTimestamp:
|
||||
@@ -1043,11 +1040,6 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
std::to_string(static_cast<unsigned int>(last_key_entry_type)));
|
||||
return false;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "table/block_based/flush_block_policy.h"
|
||||
#include "table/block_based/flush_block_policy_impl.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend2.h"
|
||||
|
||||
|
||||
@@ -49,10 +49,8 @@ TEST_F(DBMergeOperandTest, CacheEvictedMergeOperandReadAfterFreeBug) {
|
||||
// There was a bug of reading merge operands after they are mistakely freed
|
||||
// in DB::GetMergeOperands, which is surfaced by cache full.
|
||||
// See PR#9507 for more.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.env = env_;
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
// Small cache to simulate cache full
|
||||
@@ -121,11 +119,9 @@ TEST_F(DBMergeOperandTest, FlushedMergeOperandReadAfterFreeBug) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Options options = CurrentOptions();
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
@@ -309,13 +305,11 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, BlobDBGetMergeOperandsBasic) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Options options = CurrentOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
@@ -401,8 +395,7 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsLargeResultOptimization) {
|
||||
const int kNumOperands = 1024;
|
||||
const int kOperandLen = 1024;
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
size_t limit_ = 0;
|
||||
};
|
||||
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
@@ -134,7 +134,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnRead) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
@@ -147,7 +147,7 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnRead) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnWrite) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.max_successive_merges = 3;
|
||||
@@ -163,7 +163,7 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnWrite) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnIteration) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
@@ -221,7 +221,7 @@ TEST_F(DBMergeOperatorTest, MergeOperatorFailsWithMustMerge) {
|
||||
// expect "k0" and "k2" to always be readable. "k1" is expected to be readable
|
||||
// only by APIs that do not require merging, such as `GetMergeOperands()`.
|
||||
const int kNumOperands = 3;
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
@@ -361,7 +361,7 @@ TEST_F(DBMergeOperatorTest, MergeOperatorFailsWithMustMerge) {
|
||||
TEST_F(DBMergeOperatorTest, DataBlockBinaryAndHash) {
|
||||
// Basic test to check that merge operator works with data block index type
|
||||
// DataBlockBinaryAndHash.
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
|
||||
+125
-8
@@ -22,6 +22,7 @@
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -581,6 +582,7 @@ TEST_F(DBOptionsTest, EnableAutoCompactionAndTriggerStall) {
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsMayTriggerCompaction) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 1000;
|
||||
options.env = env_;
|
||||
@@ -878,9 +880,13 @@ TEST_F(DBOptionsTest, SanitizeFIFOPeriodicCompaction) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.env = CurrentOptions().env;
|
||||
// Default value allows RocksDB to set ttl to 30 days.
|
||||
ASSERT_EQ(30 * 24 * 60 * 60, dbfull()->GetOptions().ttl);
|
||||
|
||||
// Disable
|
||||
options.ttl = 0;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(30 * 24 * 60 * 60, dbfull()->GetOptions().ttl);
|
||||
ASSERT_EQ(0, dbfull()->GetOptions().ttl);
|
||||
|
||||
options.ttl = 100;
|
||||
Reopen(options);
|
||||
@@ -890,15 +896,12 @@ TEST_F(DBOptionsTest, SanitizeFIFOPeriodicCompaction) {
|
||||
Reopen(options);
|
||||
ASSERT_EQ(100 * 24 * 60 * 60, dbfull()->GetOptions().ttl);
|
||||
|
||||
options.ttl = 200;
|
||||
options.periodic_compaction_seconds = 300;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(200, dbfull()->GetOptions().ttl);
|
||||
|
||||
// periodic_compaction_seconds should have no effect
|
||||
// on FIFO compaction.
|
||||
options.ttl = 500;
|
||||
options.periodic_compaction_seconds = 300;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(300, dbfull()->GetOptions().ttl);
|
||||
ASSERT_EQ(500, dbfull()->GetOptions().ttl);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
@@ -910,6 +913,7 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.num_levels = 1;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
@@ -1009,6 +1013,24 @@ TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_GE(NumTableFilesAtLevel(0), 1);
|
||||
ASSERT_LE(NumTableFilesAtLevel(0), 5);
|
||||
|
||||
// Test dynamically setting `file_temperature_age_thresholds`
|
||||
ASSERT_TRUE(
|
||||
dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_fifo.file_temperature_age_thresholds.empty());
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compaction_options_fifo",
|
||||
"{file_temperature_age_thresholds={{age=10;"
|
||||
"temperature=kWarm}:{age=30000;"
|
||||
"temperature=kCold}}}"}}));
|
||||
auto opts = dbfull()->GetOptions();
|
||||
const auto& fifo_temp_opt =
|
||||
opts.compaction_options_fifo.file_temperature_age_thresholds;
|
||||
ASSERT_EQ(fifo_temp_opt.size(), 2);
|
||||
ASSERT_EQ(fifo_temp_opt[0].temperature, Temperature::kWarm);
|
||||
ASSERT_EQ(fifo_temp_opt[0].age, 10);
|
||||
ASSERT_EQ(fifo_temp_opt[1].temperature, Temperature::kCold);
|
||||
ASSERT_EQ(fifo_temp_opt[1].age, 30000);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, CompactionReadaheadSizeChange) {
|
||||
@@ -1043,6 +1065,7 @@ TEST_F(DBOptionsTest, FIFOTtlBackwardCompatible) {
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.create_if_missing = true;
|
||||
options.env = CurrentOptions().env;
|
||||
options.num_levels = 1;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
@@ -1063,12 +1086,19 @@ TEST_F(DBOptionsTest, FIFOTtlBackwardCompatible) {
|
||||
// ttl under compaction_options_fifo.
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo",
|
||||
"{allow_compaction=true;max_table_files_size=1024;ttl=731;}"},
|
||||
"{allow_compaction=true;max_table_files_size=1024;ttl=731;file_"
|
||||
"temperature_age_thresholds={temperature=kCold;age=12345}}"},
|
||||
{"ttl", "60"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
1024);
|
||||
auto opts = dbfull()->GetOptions();
|
||||
const auto& file_temp_age =
|
||||
opts.compaction_options_fifo.file_temperature_age_thresholds;
|
||||
ASSERT_EQ(file_temp_age.size(), 1);
|
||||
ASSERT_EQ(file_temp_age[0].temperature, Temperature::kCold);
|
||||
ASSERT_EQ(file_temp_age[0].age, 12345);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 60);
|
||||
|
||||
// Put ttl as the first option inside compaction_options_fifo. That works as
|
||||
@@ -1081,6 +1111,9 @@ TEST_F(DBOptionsTest, FIFOTtlBackwardCompatible) {
|
||||
true);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
1024);
|
||||
ASSERT_EQ(file_temp_age.size(), 1);
|
||||
ASSERT_EQ(file_temp_age[0].temperature, Temperature::kCold);
|
||||
ASSERT_EQ(file_temp_age[0].age, 12345);
|
||||
ASSERT_EQ(dbfull()->GetOptions().ttl, 191);
|
||||
}
|
||||
|
||||
@@ -1206,6 +1239,90 @@ TEST_F(DBOptionsTest, BottommostCompressionOptsWithFallbackType) {
|
||||
ASSERT_EQ(kBottommostCompressionLevel, compression_opt_used.level);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, FIFOTemperatureAgeThresholdValidation) {
|
||||
Options options = CurrentOptions();
|
||||
Destroy(options);
|
||||
|
||||
options.num_levels = 1;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.max_open_files = -1;
|
||||
// elements are not sorted
|
||||
// During DB open
|
||||
options.compaction_options_fifo.file_temperature_age_thresholds.push_back(
|
||||
{Temperature::kCold, 1000});
|
||||
options.compaction_options_fifo.file_temperature_age_thresholds.push_back(
|
||||
{Temperature::kWarm, 500});
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsNotSupported());
|
||||
ASSERT_TRUE(std::strstr(
|
||||
s.getState(),
|
||||
"Option file_temperature_age_thresholds requires elements to be sorted "
|
||||
"in increasing order with respect to `age` field."));
|
||||
// Dynamically set option
|
||||
options.compaction_options_fifo.file_temperature_age_thresholds.pop_back();
|
||||
ASSERT_OK(TryReopen(options));
|
||||
s = db_->SetOptions({{"compaction_options_fifo",
|
||||
"{file_temperature_age_thresholds={{temperature=kCold;"
|
||||
"age=1000000}:{temperature=kWarm;age=1}}}"}});
|
||||
ASSERT_TRUE(s.IsNotSupported());
|
||||
ASSERT_TRUE(std::strstr(
|
||||
s.getState(),
|
||||
"Option file_temperature_age_thresholds requires elements to be sorted "
|
||||
"in increasing order with respect to `age` field."));
|
||||
|
||||
// not single level
|
||||
// During DB open
|
||||
options.num_levels = 2;
|
||||
s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsNotSupported());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Option file_temperature_age_thresholds is only "
|
||||
"supported when num_levels = 1."));
|
||||
// Dynamically set option
|
||||
options.compaction_options_fifo.file_temperature_age_thresholds.clear();
|
||||
DestroyAndReopen(options);
|
||||
s = db_->SetOptions(
|
||||
{{"compaction_options_fifo",
|
||||
"{file_temperature_age_thresholds={temperature=kCold;age=1000}}"}});
|
||||
ASSERT_TRUE(s.IsNotSupported());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Option file_temperature_age_thresholds is only "
|
||||
"supported when num_levels = 1."));
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, TempOptionsFailTest) {
|
||||
std::shared_ptr<FaultInjectionTestFS> fs;
|
||||
std::unique_ptr<Env> env;
|
||||
|
||||
fs.reset(new FaultInjectionTestFS(env_->GetFileSystem()));
|
||||
env = NewCompositeEnv(fs);
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"PersistRocksDBOptions:create",
|
||||
[&](void* /*arg*/) { fs->SetFilesystemActive(false); });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"PersistRocksDBOptions:written",
|
||||
[&](void* /*arg*/) { fs->SetFilesystemActive(true); });
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
TryReopen(options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &filenames));
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
bool found_temp_file = false;
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == kTempFile) {
|
||||
found_temp_file = true;
|
||||
}
|
||||
}
|
||||
ASSERT_FALSE(found_temp_file);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+23
-36
@@ -188,40 +188,6 @@ TEST_F(DBPropertiesTest, GetAggregatedIntPropertyTest) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
void ResetTableProperties(TableProperties* tp) {
|
||||
tp->data_size = 0;
|
||||
tp->index_size = 0;
|
||||
tp->filter_size = 0;
|
||||
tp->raw_key_size = 0;
|
||||
tp->raw_value_size = 0;
|
||||
tp->num_data_blocks = 0;
|
||||
tp->num_entries = 0;
|
||||
tp->num_deletions = 0;
|
||||
tp->num_merge_operands = 0;
|
||||
tp->num_range_deletions = 0;
|
||||
}
|
||||
|
||||
void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
|
||||
double dummy_double;
|
||||
std::replace(tp_string.begin(), tp_string.end(), ';', ' ');
|
||||
std::replace(tp_string.begin(), tp_string.end(), '=', ' ');
|
||||
ResetTableProperties(tp);
|
||||
sscanf(tp_string.c_str(),
|
||||
"# data blocks %" SCNu64 " # entries %" SCNu64 " # deletions %" SCNu64
|
||||
" # merge operands %" SCNu64 " # range deletions %" SCNu64
|
||||
" raw key size %" SCNu64
|
||||
" raw average key size %lf "
|
||||
" raw value size %" SCNu64
|
||||
" raw average value size %lf "
|
||||
" data block size %" SCNu64 " index block size (user-key? %" SCNu64
|
||||
", delta-value? %" SCNu64 ") %" SCNu64 " filter block size %" SCNu64,
|
||||
&tp->num_data_blocks, &tp->num_entries, &tp->num_deletions,
|
||||
&tp->num_merge_operands, &tp->num_range_deletions, &tp->raw_key_size,
|
||||
&dummy_double, &tp->raw_value_size, &dummy_double, &tp->data_size,
|
||||
&tp->index_key_is_user_key, &tp->index_value_is_delta_encoded,
|
||||
&tp->index_size, &tp->filter_size);
|
||||
}
|
||||
|
||||
void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
ASSERT_EQ(a == 0U, b == 0U);
|
||||
if (a == 0) {
|
||||
@@ -1749,14 +1715,35 @@ TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
ASSERT_OK(Delete("key" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
uint64_t sst_size;
|
||||
bool ok = db_->GetIntProperty(DB::Properties::kTotalSstFilesSize, &sst_size);
|
||||
ASSERT_TRUE(ok);
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kTotalSstFilesSize, &sst_size));
|
||||
ASSERT_GT(sst_size, 0);
|
||||
listener->size_before_compaction = sst_size;
|
||||
|
||||
uint64_t obsolete_sst_size;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kObsoleteSstFilesSize,
|
||||
&obsolete_sst_size));
|
||||
ASSERT_EQ(obsolete_sst_size, 0);
|
||||
|
||||
// Hold files from being deleted so we can test property for size of obsolete
|
||||
// SST files.
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
|
||||
// Compact to clean all keys and trigger listener.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_TRUE(listener->callback_triggered);
|
||||
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kObsoleteSstFilesSize,
|
||||
&obsolete_sst_size));
|
||||
ASSERT_EQ(obsolete_sst_size, sst_size);
|
||||
|
||||
// Let the obsolete files be deleted.
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kObsoleteSstFilesSize,
|
||||
&obsolete_sst_size));
|
||||
ASSERT_EQ(obsolete_sst_size, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, MinObsoleteSstNumberToKeep) {
|
||||
|
||||
@@ -1690,6 +1690,8 @@ TEST_F(DBRangeDelTest, LevelCompactOutputCutAtRangeTombstoneForTtlFiles) {
|
||||
ASSERT_EQ("0,1,0,1", FilesPerLevel());
|
||||
|
||||
env_->MockSleepForSeconds(20 * 60 * 60);
|
||||
// Prevent range tombstone from being dropped during compaction.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(11), Key(12)));
|
||||
ASSERT_OK(Put(Key(0), rnd.RandomString(1 << 10)));
|
||||
@@ -1703,6 +1705,7 @@ TEST_F(DBRangeDelTest, LevelCompactOutputCutAtRangeTombstoneForTtlFiles) {
|
||||
// File 1: (qualified for TTL): Key(5) - Key(10)
|
||||
// File 1: DeleteRange [11, 12)
|
||||
ASSERT_EQ("0,3,0,1", FilesPerLevel());
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
// Test SST partitioner cut after every single key
|
||||
@@ -3405,6 +3408,73 @@ TEST_F(DBRangeDelTest, AddRangeDelsSingleUserKeyTombstoneOnlyFile) {
|
||||
db_->ReleaseSnapshot(snapshot1);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, NonBottommostCompactionDropRangetombstone) {
|
||||
// L0: file 1: [DeleteRange[4, 5)], file 2: [3, 6, DeleteRange[8, 9)]
|
||||
// L6 file 1: [2, 3], file 2: [7, 8]
|
||||
// When compacting the two L0 files to L1, the compaction is non-bottommost
|
||||
// since the compaction key range overlaps with L6 file 1. The range tombstone
|
||||
// [4, 5) should be dropped since it does not overlap with any file in lower
|
||||
// levels. The range tombstone [8, 9) should not be dropped.
|
||||
Options opts = CurrentOptions();
|
||||
opts.level_compaction_dynamic_level_bytes = false;
|
||||
opts.num_levels = 7;
|
||||
opts.level0_file_num_compaction_trigger = 3;
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
Random rnd(301);
|
||||
// L6 file 1
|
||||
ASSERT_OK(Put(Key(2), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(3), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
// L6 file 2
|
||||
ASSERT_OK(Put(Key(7), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(8), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(6), 2);
|
||||
// L0 file 1
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(4),
|
||||
Key(5)));
|
||||
ASSERT_OK(Flush());
|
||||
// L0 file 2
|
||||
ASSERT_OK(Put(Key(3), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(6), rnd.RandomString(100)));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(8),
|
||||
Key(9)));
|
||||
ASSERT_OK(Flush());
|
||||
// nothing is dropped during flush
|
||||
std::string property;
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
|
||||
TableProperties output_tp;
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
ASSERT_EQ(output_tp.num_range_deletions, 2);
|
||||
// Add one more L0 file to trigger L0->L1 compaction
|
||||
ASSERT_OK(Put(Key(1), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(9), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 1);
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
ASSERT_EQ(output_tp.num_range_deletions, 1);
|
||||
|
||||
// Now create a snapshot protected range tombstone [4, 5), it should not
|
||||
// be dropped.
|
||||
ASSERT_OK(Put(Key(4), rnd.RandomString(100)));
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(4),
|
||||
Key(5)));
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
// All compacted to L6
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel(0));
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
ASSERT_EQ(output_tp.num_range_deletions, 1);
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+6
-6
@@ -743,7 +743,7 @@ TEST_P(DBSSTTestRateLimit, RateLimitedDelete) {
|
||||
|
||||
// Compaction will move the 4 files in L0 to trash and create 1 L1 file
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
|
||||
uint64_t delete_start_time = env_->NowMicros();
|
||||
@@ -815,7 +815,7 @@ TEST_F(DBSSTTest, RateLimitedWALDelete) {
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
|
||||
sfm->WaitForEmptyTrash();
|
||||
@@ -1224,7 +1224,7 @@ TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// Because we set a callback in CancelledCompaction, we actually
|
||||
// let the compaction run
|
||||
@@ -1281,7 +1281,7 @@ TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
|
||||
.IsCompactionTooLarge());
|
||||
|
||||
// Wait for manual compaction to get scheduled and finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
// Make sure the stat is bumped
|
||||
@@ -1297,7 +1297,7 @@ TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
|
||||
.IsCompactionTooLarge());
|
||||
|
||||
// Wait for manual compaction to get scheduled and finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
COMPACTION_CANCELLED),
|
||||
@@ -1314,7 +1314,7 @@ TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(),
|
||||
l0_files, 0));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
ASSERT_GT(completed_compactions, 0);
|
||||
|
||||
@@ -242,6 +242,46 @@ TEST_F(DBStatisticsTest, VerifyChecksumReadStat) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBStatisticsTest, BlockChecksumStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
Reopen(options);
|
||||
|
||||
// Scenario 0: only WAL data. Not verified so require ticker to be zero.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(db_->VerifyChecksum());
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getTickerCount(BLOCK_CHECKSUM_COMPUTE_COUNT));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getTickerCount(BLOCK_CHECKSUM_MISMATCH_COUNT));
|
||||
|
||||
// Scenario 1: Flushed table verified in `VerifyChecksum()`. This opens a
|
||||
// `TableReader` to verify each of the four blocks (meta-index, table
|
||||
// properties, index, and data block).
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(options.statistics->Reset());
|
||||
ASSERT_OK(db_->VerifyChecksum());
|
||||
ASSERT_EQ(4,
|
||||
options.statistics->getTickerCount(BLOCK_CHECKSUM_COMPUTE_COUNT));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getTickerCount(BLOCK_CHECKSUM_MISMATCH_COUNT));
|
||||
|
||||
// Scenario 2: Corrupted table verified in `VerifyChecksum()`. The corruption
|
||||
// is in the fourth and final verified block, i.e., the data block.
|
||||
std::unordered_map<std::string, uint64_t> table_files;
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &table_files));
|
||||
ASSERT_EQ(1, table_files.size());
|
||||
std::string table_name = table_files.begin()->first;
|
||||
// Assumes the data block starts at offset zero.
|
||||
ASSERT_OK(test::CorruptFile(options.env, table_name, 0 /* offset */,
|
||||
3 /* bytes_to_corrupt */));
|
||||
ASSERT_OK(options.statistics->Reset());
|
||||
ASSERT_NOK(db_->VerifyChecksum());
|
||||
ASSERT_EQ(4,
|
||||
options.statistics->getTickerCount(BLOCK_CHECKSUM_COMPUTE_COUNT));
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getTickerCount(BLOCK_CHECKSUM_MISMATCH_COUNT));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -282,6 +282,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
Random rnd(301);
|
||||
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 4096;
|
||||
options.max_write_buffer_number = 2;
|
||||
|
||||
+275
-162
@@ -15,6 +15,11 @@
|
||||
#include "db/forward_iterator.h"
|
||||
#include "port/stack_trace.h"
|
||||
|
||||
namespace {
|
||||
static bool enable_io_uring = true;
|
||||
extern "C" bool RocksDbIOUringEnable() { return enable_io_uring; }
|
||||
} // namespace
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBTestTailingIterator : public DBTestBase,
|
||||
@@ -50,94 +55,125 @@ TEST_P(DBTestTailingIterator, TailingIteratorSingle) {
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorKeepAdding) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
read_options.async_io = true;
|
||||
}
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::string value(1024, 'a');
|
||||
|
||||
const int num_records = 10000;
|
||||
for (int i = 0; i < num_records; ++i) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%016d", i);
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::string value(1024, 'a');
|
||||
|
||||
Slice key(buf, 16);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
const int num_records = 10000;
|
||||
for (int i = 0; i < num_records; ++i) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%016d", i);
|
||||
|
||||
iter->Seek(key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
Slice key(buf, 16);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
|
||||
iter->Seek(key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorSeekToNext) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
read_options.async_io = true;
|
||||
}
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::unique_ptr<Iterator> itern(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(itern->status());
|
||||
std::string value(1024, 'a');
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::unique_ptr<Iterator> itern(
|
||||
db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(itern->status());
|
||||
std::string value(1024, 'a');
|
||||
|
||||
const int num_records = 1000;
|
||||
for (int i = 1; i < num_records; ++i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
||||
const int num_records = 1000;
|
||||
for (int i = 1; i < num_records; ++i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
||||
|
||||
Slice key(buf1, 20);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
Slice key(buf1, 20);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush(1));
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
||||
Slice target(buf2, 20);
|
||||
iter->Seek(target);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
if (i == 1) {
|
||||
itern->SeekToFirst();
|
||||
} else {
|
||||
itern->Next();
|
||||
}
|
||||
ASSERT_TRUE(itern->Valid());
|
||||
ASSERT_EQ(itern->key().compare(key), 0);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
for (int i = 2 * num_records; i > 0; --i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
||||
Slice target(buf2, 20);
|
||||
iter->Seek(target);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
if (i == 1) {
|
||||
itern->SeekToFirst();
|
||||
} else {
|
||||
itern->Next();
|
||||
Slice key(buf1, 20);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
||||
Slice target(buf2, 20);
|
||||
iter->Seek(target);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
}
|
||||
ASSERT_TRUE(itern->Valid());
|
||||
ASSERT_EQ(itern->key().compare(key), 0);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
for (int i = 2 * num_records; i > 0; --i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
||||
|
||||
Slice key(buf1, 20);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
||||
Slice target(buf2, 20);
|
||||
iter->Seek(target);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
const uint64_t k150KB = 150 * 1024;
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options;
|
||||
options.env = env.get();
|
||||
options.write_buffer_size = k150KB;
|
||||
options.max_write_buffer_number = 3;
|
||||
options.min_write_buffer_number_to_merge = 2;
|
||||
@@ -278,56 +314,73 @@ TEST_P(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorDeletes) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
read_options.async_io = true;
|
||||
}
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
// write a single record, read it using the iterator, then delete it
|
||||
ASSERT_OK(Put(1, "0test", "test"));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "0test");
|
||||
ASSERT_OK(Delete(1, "0test"));
|
||||
|
||||
// write a single record, read it using the iterator, then delete it
|
||||
ASSERT_OK(Put(1, "0test", "test"));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "0test");
|
||||
ASSERT_OK(Delete(1, "0test"));
|
||||
// write many more records
|
||||
const int num_records = 10000;
|
||||
std::string value(1024, 'A');
|
||||
|
||||
// write many more records
|
||||
const int num_records = 10000;
|
||||
std::string value(1024, 'A');
|
||||
for (int i = 0; i < num_records; ++i) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "1%015d", i);
|
||||
|
||||
for (int i = 0; i < num_records; ++i) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "1%015d", i);
|
||||
Slice key(buf, 16);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
}
|
||||
|
||||
Slice key(buf, 16);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
// force a flush to make sure that no records are read from memtable
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
// skip "0test"
|
||||
iter->Next();
|
||||
|
||||
// make sure we can read all new records using the existing iterator
|
||||
int count = 0;
|
||||
for (; iter->Valid(); iter->Next(), ++count)
|
||||
;
|
||||
|
||||
ASSERT_EQ(count, num_records);
|
||||
}
|
||||
|
||||
// force a flush to make sure that no records are read from memtable
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
// skip "0test"
|
||||
iter->Next();
|
||||
|
||||
// make sure we can read all new records using the existing iterator
|
||||
int count = 0;
|
||||
for (; iter->Valid(); iter->Next(), ++count)
|
||||
;
|
||||
|
||||
ASSERT_EQ(count, num_records);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorPrefixSeek) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
read_options.async_io = true;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
@@ -336,28 +389,39 @@ TEST_P(DBTestTailingIterator, TailingIteratorPrefixSeek) {
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_OK(Put(1, "0101", "test"));
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_OK(Put(1, "0101", "test"));
|
||||
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
ASSERT_OK(Put(1, "0202", "test"));
|
||||
ASSERT_OK(Put(1, "0202", "test"));
|
||||
|
||||
// Seek(0102) shouldn't find any records since 0202 has a different prefix
|
||||
iter->Seek("0102");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
// Seek(0102) shouldn't find any records since 0202 has a different prefix
|
||||
iter->Seek("0102");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
|
||||
iter->Seek("0202");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "0202");
|
||||
iter->Seek("0202");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "0202");
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorIncomplete) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
@@ -370,20 +434,30 @@ TEST_P(DBTestTailingIterator, TailingIteratorIncomplete) {
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
// we either see the entry or it's not in cache
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
// we either see the entry or it's not in cache
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
iter->SeekToFirst();
|
||||
// should still be true after compaction
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
iter->SeekToFirst();
|
||||
// should still be true after compaction
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorSeekToSame) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.write_buffer_size = 1000;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -403,28 +477,39 @@ TEST_P(DBTestTailingIterator, TailingIteratorSeekToSame) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
// Seek to 00001. We expect to find 00002.
|
||||
std::string start_key = "00001";
|
||||
iter->Seek(start_key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
// Seek to 00001. We expect to find 00002.
|
||||
std::string start_key = "00001";
|
||||
iter->Seek(start_key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
std::string found = iter->key().ToString();
|
||||
ASSERT_EQ("00002", found);
|
||||
std::string found = iter->key().ToString();
|
||||
ASSERT_EQ("00002", found);
|
||||
|
||||
// Now seek to the same key. The iterator should remain in the same
|
||||
// position.
|
||||
iter->Seek(found);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(found, iter->key().ToString());
|
||||
// Now seek to the same key. The iterator should remain in the same
|
||||
// position.
|
||||
iter->Seek(found);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(found, iter->key().ToString());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
// Sets iterate_upper_bound and verifies that ForwardIterator doesn't call
|
||||
// Seek() on immutable iterators when target key is >= prev_key and all
|
||||
// iterators, including the memtable iterator, are over the upper bound.
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorUpperBound) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
const Slice upper_bound("20", 3);
|
||||
ReadOptions read_options;
|
||||
@@ -441,34 +526,51 @@ TEST_P(DBTestTailingIterator, TailingIteratorUpperBound) {
|
||||
// Add another key to the memtable.
|
||||
ASSERT_OK(Put(1, "21", "21"));
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(it->status());
|
||||
it->Seek("12");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("12", it->key().ToString());
|
||||
{
|
||||
bool read_async_called = false;
|
||||
|
||||
it->Next();
|
||||
// Not valid since "21" is over the upper bound.
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
// This keeps track of the number of times NeedToSeekImmutable() was true.
|
||||
int immutable_seeks = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::SeekInternal:Immutable",
|
||||
[&](void* /*arg*/) { ++immutable_seeks; });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"UpdateResults::io_uring_result",
|
||||
[&](void* /*arg*/) { read_async_called = true; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Seek to 13. This should not require any immutable seeks.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
it->Seek("13");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
auto it =
|
||||
std::unique_ptr<Iterator>(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(it->status());
|
||||
it->Seek("12");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("12", it->key().ToString());
|
||||
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
if (GetParam()) {
|
||||
ASSERT_EQ(1, immutable_seeks);
|
||||
} else {
|
||||
ASSERT_EQ(0, immutable_seeks);
|
||||
it->Next();
|
||||
// Not valid since "21" is over the upper bound.
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
// This keeps track of the number of times NeedToSeekImmutable() was true.
|
||||
int immutable_seeks = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::SeekInternal:Immutable",
|
||||
[&](void* /*arg*/) { ++immutable_seeks; });
|
||||
|
||||
// Seek to 13. This should not require any immutable seeks.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
it->Seek("13");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"UpdateResults::io_uring_result",
|
||||
[&](void* /*arg*/) { read_async_called = true; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
if (GetParam() && read_async_called) {
|
||||
ASSERT_EQ(1, immutable_seeks);
|
||||
} else {
|
||||
ASSERT_EQ(0, immutable_seeks);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorGap) {
|
||||
@@ -480,7 +582,15 @@ TEST_P(DBTestTailingIterator, TailingIteratorGap) {
|
||||
// the largest key of index n file and the smallest key of index n+1 file
|
||||
// if both file fit in that gap. In this example, 25 < key < 35
|
||||
// https://github.com/facebook/rocksdb/issues/1372
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
@@ -512,20 +622,23 @@ TEST_P(DBTestTailingIterator, TailingIteratorGap) {
|
||||
ColumnFamilyMetaData meta;
|
||||
db_->GetColumnFamilyMetaData(handles_[1], &meta);
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
||||
it->Seek("30");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("30", it->key().ToString());
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
||||
it->Seek("30");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("30", it->key().ToString());
|
||||
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("35", it->key().ToString());
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("35", it->key().ToString());
|
||||
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("40", it->key().ToString());
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("40", it->key().ToString());
|
||||
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_OK(it->status());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
||||
|
||||
+33
-6
@@ -66,7 +66,7 @@
|
||||
#include "util/compression.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/rate_limiter_impl.h"
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
@@ -583,7 +583,7 @@ TEST_F(DBTest, LevelReopenWithFIFO) {
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, fifo_options));
|
||||
// For FIFO to pick a compaction
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(false));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
for (int g = 0; g < kKeyCount; ++g) {
|
||||
std::string get_key = std::string(1, char('a' + g));
|
||||
int status_index = i / kKeyCount;
|
||||
@@ -3097,7 +3097,7 @@ class ModelDB : public DB {
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
const std::vector<const ExportImportFilesMetaData*>& /*metadatas*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
@@ -3107,6 +3107,13 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
const Slice& /*end*/) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
using DB::GetPropertiesOfAllTables;
|
||||
Status GetPropertiesOfAllTables(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
@@ -3257,6 +3264,11 @@ class ModelDB : public DB {
|
||||
|
||||
void DisableManualCompaction() override { return; }
|
||||
|
||||
virtual Status WaitForCompact(
|
||||
const WaitForCompactOptions& /* wait_for_compact_options */) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
using DB::NumberLevels;
|
||||
int NumberLevels(ColumnFamilyHandle* /*column_family*/) override { return 1; }
|
||||
|
||||
@@ -3460,6 +3472,7 @@ static bool CompareIterators(int step, DB* model, DB* db,
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
(void)count;
|
||||
delete miter;
|
||||
delete dbiter;
|
||||
return ok;
|
||||
@@ -4936,7 +4949,7 @@ TEST_P(DBTestWithParam, PreShutdownMultipleCompaction) {
|
||||
ASSERT_GE(operation_count[ThreadStatus::OP_COMPACTION], 1);
|
||||
CancelAllBackgroundWork(db_);
|
||||
TEST_SYNC_POINT("DBTest::PreShutdownMultipleCompaction:VerifyPreshutdown");
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
// Record the number of compactions at a time.
|
||||
for (int i = 0; i < ThreadStatus::NUM_OP_TYPES; ++i) {
|
||||
operation_count[i] = 0;
|
||||
@@ -5023,7 +5036,7 @@ TEST_P(DBTestWithParam, PreShutdownCompactionMiddle) {
|
||||
CancelAllBackgroundWork(db_);
|
||||
TEST_SYNC_POINT("DBTest::PreShutdownCompactionMiddle:Preshutdown");
|
||||
TEST_SYNC_POINT("DBTest::PreShutdownCompactionMiddle:VerifyPreshutdown");
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
// Record the number of compactions at a time.
|
||||
for (int i = 0; i < ThreadStatus::NUM_OP_TYPES; ++i) {
|
||||
operation_count[i] = 0;
|
||||
@@ -5259,6 +5272,7 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
const uint64_t k1MB = 1 << 20;
|
||||
const uint64_t k4KB = 1 << 12;
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.compression = kNoCompression;
|
||||
@@ -6886,7 +6900,20 @@ TEST_F(DBTest, CreateColumnFamilyShouldFailOnIncompatibleOptions) {
|
||||
TEST_F(DBTest, RowCache) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.row_cache = NewLRUCache(8192);
|
||||
LRUCacheOptions cache_options;
|
||||
cache_options.capacity = 8192;
|
||||
options.row_cache = cache_options.MakeSharedRowCache();
|
||||
// BEGIN check that Cache classes as aliases of each other.
|
||||
// Currently, RowCache and BlockCache are aliases for Cache.
|
||||
// This is expected to change (carefully, intentionally)
|
||||
std::shared_ptr<RowCache> row_cache = options.row_cache;
|
||||
std::shared_ptr<Cache> cache = row_cache;
|
||||
std::shared_ptr<BlockCache> block_cache = row_cache;
|
||||
row_cache = cache;
|
||||
block_cache = cache;
|
||||
row_cache = block_cache;
|
||||
cache = block_cache;
|
||||
// END check that Cache classes as aliases of each other.
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
|
||||
+77
-136
@@ -2063,6 +2063,7 @@ class PinL0IndexAndFilterBlocksTest
|
||||
ASSERT_OK(Flush(1));
|
||||
// move this table to L1
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1, 1));
|
||||
|
||||
// reset block cache
|
||||
table_options.block_cache = NewLRUCache(64 * 1024);
|
||||
@@ -2220,7 +2221,7 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
// this should be read from L1
|
||||
value = Get(1, "a");
|
||||
if (!disallow_preload_) {
|
||||
// In inifinite max files case, there's a cache miss in executing Get()
|
||||
// In infinite max files case, there's a cache miss in executing Get()
|
||||
// because index and filter are not prefetched before.
|
||||
ASSERT_EQ(fm + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
@@ -2248,12 +2249,12 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
ASSERT_EQ(ih + 2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 4, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
ASSERT_EQ(ih + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
|
||||
// Bloom and index hit will happen when a Get() happens.
|
||||
@@ -2262,12 +2263,12 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 4, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
ASSERT_EQ(ih + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 5, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
ASSERT_EQ(ih + 4, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3044,7 +3045,7 @@ TEST_F(DBTest2, PausingManualCompaction1) {
|
||||
.IsManualCompactionPaused());
|
||||
|
||||
// Wait for compactions to get scheduled and stopped
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// Get file names after compaction is stopped
|
||||
files_meta.clear();
|
||||
@@ -3064,7 +3065,7 @@ TEST_F(DBTest2, PausingManualCompaction1) {
|
||||
files_before_compact, 0)
|
||||
.IsManualCompactionPaused());
|
||||
// Wait for manual compaction to get scheduled and finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
files_meta.clear();
|
||||
files_after_compact.clear();
|
||||
@@ -3097,7 +3098,7 @@ TEST_F(DBTest2, PausingManualCompaction2) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
std::vector<LiveFileMetaData> files_meta;
|
||||
dbfull()->GetLiveFilesMetaData(&files_meta);
|
||||
@@ -3139,7 +3140,7 @@ TEST_F(DBTest2, PausingManualCompaction3) {
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
// As manual compaction disabled, not even reach sync point
|
||||
ASSERT_EQ(run_manual_compactions, 0);
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
@@ -3148,7 +3149,7 @@ TEST_F(DBTest2, PausingManualCompaction3) {
|
||||
"CompactionJob::Run():PausingManualCompaction:1");
|
||||
dbfull()->EnableManualCompaction();
|
||||
ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -3203,14 +3204,14 @@ TEST_F(DBTest2, PausingManualCompaction4) {
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(run_manual_compactions, 1);
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:2");
|
||||
ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -3262,7 +3263,7 @@ TEST_F(DBTest2, CancelManualCompaction1) {
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// Since compactions are disabled, we shouldn't start compacting.
|
||||
// E.g. we should call the compaction function exactly one time.
|
||||
@@ -3286,7 +3287,7 @@ TEST_F(DBTest2, CancelManualCompaction1) {
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(compactions_run, 3);
|
||||
|
||||
@@ -3298,7 +3299,7 @@ TEST_F(DBTest2, CancelManualCompaction1) {
|
||||
// Compactions should work again if we re-enable them..
|
||||
compact_options.canceled->store(false, std::memory_order_relaxed);
|
||||
ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -3362,7 +3363,7 @@ TEST_F(DBTest2, CancelManualCompaction2) {
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// NOTE: as we set compact_options.max_subcompacitons = 1, and store true to
|
||||
// the canceled variable from the single compacting thread (via callback),
|
||||
@@ -3380,7 +3381,7 @@ TEST_F(DBTest2, CancelManualCompaction2) {
|
||||
// Compactions should work again if we re-enable them..
|
||||
compact_options.canceled->store(false, std::memory_order_relaxed);
|
||||
ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -3455,7 +3456,7 @@ TEST_F(DBTest2, CancelManualCompactionWithListener) {
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_GT(listener->num_compaction_started_, 0);
|
||||
ASSERT_EQ(listener->num_compaction_started_, listener->num_compaction_ended_);
|
||||
@@ -3470,7 +3471,7 @@ TEST_F(DBTest2, CancelManualCompactionWithListener) {
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(compact_options, nullptr, nullptr)
|
||||
.IsManualCompactionPaused());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ(listener->num_compaction_started_, 0);
|
||||
ASSERT_EQ(listener->num_compaction_started_, listener->num_compaction_ended_);
|
||||
@@ -3492,7 +3493,7 @@ TEST_F(DBTest2, CancelManualCompactionWithListener) {
|
||||
|
||||
compact_options.canceled->store(false, std::memory_order_release);
|
||||
ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_GT(listener->num_compaction_started_, 0);
|
||||
ASSERT_EQ(listener->num_compaction_started_, listener->num_compaction_ended_);
|
||||
@@ -5298,88 +5299,6 @@ TEST_F(DBTest2, DISABLED_IteratorPinnedMemory) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TestBBTTailPrefetch) {
|
||||
std::atomic<bool> called(false);
|
||||
size_t expected_lower_bound = 512 * 1024;
|
||||
size_t expected_higher_bound = 512 * 1024;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::Open::TailPrefetchLen", [&](void* arg) {
|
||||
size_t* prefetch_size = static_cast<size_t*>(arg);
|
||||
EXPECT_LE(expected_lower_bound, *prefetch_size);
|
||||
EXPECT_GE(expected_higher_bound, *prefetch_size);
|
||||
called = true;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("1", "1"));
|
||||
ASSERT_OK(Put("9", "1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
expected_lower_bound = 0;
|
||||
expected_higher_bound = 8 * 1024;
|
||||
|
||||
ASSERT_OK(Put("1", "1"));
|
||||
ASSERT_OK(Put("9", "1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("1", "1"));
|
||||
ASSERT_OK(Put("9", "1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Full compaction to make sure there is no L0 file after the open.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_TRUE(called.load());
|
||||
called = false;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
std::atomic<bool> first_call(true);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::Open::TailPrefetchLen", [&](void* arg) {
|
||||
size_t* prefetch_size = static_cast<size_t*>(arg);
|
||||
if (first_call) {
|
||||
EXPECT_EQ(4 * 1024, *prefetch_size);
|
||||
first_call = false;
|
||||
} else {
|
||||
EXPECT_GE(4 * 1024, *prefetch_size);
|
||||
}
|
||||
called = true;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.max_file_opening_threads = 1; // one thread
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.max_open_files = -1;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("1", "1"));
|
||||
ASSERT_OK(Put("9", "1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("1", "1"));
|
||||
ASSERT_OK(Put("9", "1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_TRUE(called.load());
|
||||
called = false;
|
||||
|
||||
// Parallel loading SST files
|
||||
options.max_file_opening_threads = 16;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_TRUE(called.load());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, TestGetColumnFamilyHandleUnlocked) {
|
||||
// Setup sync point dependency to reproduce the race condition of
|
||||
// DBImpl::GetColumnFamilyHandleUnlocked
|
||||
@@ -5905,7 +5824,7 @@ TEST_F(DBTest2, SameSmallestInSameLevel) {
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "key", "8"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,4,1", FilesPerLevel());
|
||||
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", Get("key"));
|
||||
@@ -6074,17 +5993,15 @@ TEST_F(DBTest2, ChangePrefixExtractor) {
|
||||
iterator->Seek("xa");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xb", iterator->key().ToString());
|
||||
// It's a bug that the counter BLOOM_FILTER_PREFIX_CHECKED is not
|
||||
// correct in this case. So don't check counters in this case.
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(0, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
|
||||
iterator->Seek("xz");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xz1", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(0, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6102,7 +6019,7 @@ TEST_F(DBTest2, ChangePrefixExtractor) {
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xb", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(0, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6116,14 +6033,14 @@ TEST_F(DBTest2, ChangePrefixExtractor) {
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xb", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(0, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
|
||||
iterator->Seek("xx0");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xx1", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(1, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6141,21 +6058,21 @@ TEST_F(DBTest2, ChangePrefixExtractor) {
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xb", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(1, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
|
||||
iterator->Seek("xg");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xx1", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(3, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(1, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
|
||||
iterator->Seek("xz");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xz1", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(4, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(1, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
|
||||
ASSERT_OK(iterator->status());
|
||||
@@ -6167,14 +6084,14 @@ TEST_F(DBTest2, ChangePrefixExtractor) {
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xb", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(5, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(1, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
|
||||
iterator->Seek("xx0");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xx1", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(6, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(1, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
|
||||
ASSERT_OK(iterator->status());
|
||||
@@ -6188,7 +6105,7 @@ TEST_F(DBTest2, ChangePrefixExtractor) {
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("xb", iterator->key().ToString());
|
||||
if (expect_filter_check) {
|
||||
ASSERT_EQ(7, TestGetTickerCount(options, BLOOM_FILTER_PREFIX_CHECKED));
|
||||
EXPECT_EQ(1, PopTicker(options, NON_LAST_LEVEL_SEEK_FILTER_MATCH));
|
||||
}
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
@@ -6262,13 +6179,19 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
ro.total_order_seek = false;
|
||||
ro.auto_prefix_mode = true;
|
||||
|
||||
const auto stat = BLOOM_FILTER_PREFIX_CHECKED;
|
||||
const auto hit_stat = options.num_levels == 1
|
||||
? LAST_LEVEL_SEEK_FILTER_MATCH
|
||||
: NON_LAST_LEVEL_SEEK_FILTER_MATCH;
|
||||
const auto miss_stat = options.num_levels == 1
|
||||
? LAST_LEVEL_SEEK_FILTERED
|
||||
: NON_LAST_LEVEL_SEEK_FILTERED;
|
||||
{
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
@@ -6280,7 +6203,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
@@ -6290,7 +6214,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
@@ -6299,7 +6224,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
@@ -6308,7 +6234,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
|
||||
@@ -6319,25 +6246,29 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
ub = "b9";
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
|
||||
ub = "z";
|
||||
iterator->Seek("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "c";
|
||||
iterator->Seek("b1");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "b9";
|
||||
iterator->SeekForPrev("b1");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "zz";
|
||||
iterator->SeekToLast();
|
||||
@@ -6369,26 +6300,30 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
ub = "b1";
|
||||
iterator->Seek("b9");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
|
||||
ub = "b1";
|
||||
iterator->Seek("z");
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("y1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "b1";
|
||||
iterator->Seek("c");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "b";
|
||||
iterator->Seek("c9");
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
// Fails if ReverseBytewiseComparator::IsSameLengthImmediateSuccessor
|
||||
// is "correctly" implemented.
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "a";
|
||||
iterator->Seek("b9");
|
||||
@@ -6396,7 +6331,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
// is "correctly" implemented.
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("a1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "b";
|
||||
iterator->Seek("a");
|
||||
@@ -6404,7 +6340,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
// Fails if ReverseBytewiseComparator::IsSameLengthImmediateSuccessor
|
||||
// matches BytewiseComparator::IsSameLengthImmediateSuccessor. Upper
|
||||
// comparing before seek key prevents a real bug from surfacing.
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "b1";
|
||||
iterator->SeekForPrev("b9");
|
||||
@@ -6412,7 +6349,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
// Fails if ReverseBytewiseComparator::IsSameLengthImmediateSuccessor
|
||||
// is "correctly" implemented.
|
||||
ASSERT_EQ("x1", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
|
||||
ub = "a";
|
||||
iterator->SeekToLast();
|
||||
@@ -6454,7 +6392,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
std::unique_ptr<Iterator> iterator(db_->NewIterator(ro));
|
||||
iterator->Seek(Slice(a_end_stuff, 2));
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
|
||||
// test, cannot be validly optimized with auto_prefix_mode
|
||||
@@ -6464,7 +6403,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
iterator->Seek(Slice(a_end_stuff, 2));
|
||||
// !!! BUG !!! See "BUG" section of auto_prefix_mode.
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(1, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
|
||||
// To prove that is the wrong result, now use total order seek
|
||||
@@ -6475,7 +6415,8 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
iterator->Seek(Slice(a_end_stuff, 2));
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ("b", iterator->key().ToString());
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, hit_stat));
|
||||
EXPECT_EQ(0, TestGetAndResetTickerCount(options, miss_stat));
|
||||
ASSERT_OK(iterator->status());
|
||||
}
|
||||
} while (ChangeOptions(kSkipPlainTable));
|
||||
|
||||
@@ -324,6 +324,12 @@ Options DBTestBase::GetDefaultOptions() const {
|
||||
options.max_open_files = 5000;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
options.compaction_pri = CompactionPri::kByCompensatedSize;
|
||||
// The original default value for this option is false,
|
||||
// and many unit tests assume this value. It also makes
|
||||
// it easier to create desired LSM shape in unit tests.
|
||||
// Unit tests for this option sets level_compaction_dynamic_level_bytes=true
|
||||
// explicitly.
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_;
|
||||
if (!env_->skip_fsync_) {
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
@@ -569,6 +575,8 @@ Options DBTestBase::GetOptions(
|
||||
if (set_block_based_table_factory) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
}
|
||||
options.level_compaction_dynamic_level_bytes =
|
||||
options_override.level_compaction_dynamic_level_bytes;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.fail_if_options_file_error = true;
|
||||
@@ -1074,6 +1082,24 @@ size_t DBTestBase::TotalLiveFiles(int cf) {
|
||||
return num_files;
|
||||
}
|
||||
|
||||
size_t DBTestBase::TotalLiveFilesAtPath(int cf, const std::string& path) {
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
if (cf == 0) {
|
||||
db_->GetColumnFamilyMetaData(&cf_meta);
|
||||
} else {
|
||||
db_->GetColumnFamilyMetaData(handles_[cf], &cf_meta);
|
||||
}
|
||||
size_t num_files = 0;
|
||||
for (auto& level : cf_meta.levels) {
|
||||
for (auto& f : level.files) {
|
||||
if (f.directory == path) {
|
||||
num_files++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_files;
|
||||
}
|
||||
|
||||
size_t DBTestBase::CountLiveFiles() {
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
|
||||
@@ -114,6 +114,12 @@ struct OptionsOverride {
|
||||
|
||||
// Used as a bit mask of individual enums in which to skip an XF test point
|
||||
int skip_policy = 0;
|
||||
|
||||
// The default value for this option is changed from false to true.
|
||||
// Keeping the default to false for unit tests as old unit tests assume
|
||||
// this behavior. Tests for level_compaction_dynamic_level_bytes
|
||||
// will set the option to true explicitly.
|
||||
bool level_compaction_dynamic_level_bytes = false;
|
||||
};
|
||||
|
||||
} // namespace anon
|
||||
@@ -1192,6 +1198,8 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
size_t TotalLiveFiles(int cf = 0);
|
||||
|
||||
size_t TotalLiveFilesAtPath(int cf, const std::string& path);
|
||||
|
||||
size_t CountLiveFiles();
|
||||
|
||||
int NumTableFilesAtLevel(int level, int cf = 0);
|
||||
@@ -1316,11 +1324,49 @@ class DBTestBase : public testing::Test {
|
||||
Tickers ticker_type) {
|
||||
return options.statistics->getAndResetTickerCount(ticker_type);
|
||||
}
|
||||
// Short name for TestGetAndResetTickerCount
|
||||
uint64_t PopTicker(const Options& options, Tickers ticker_type) {
|
||||
return options.statistics->getAndResetTickerCount(ticker_type);
|
||||
}
|
||||
|
||||
// Note: reverting this setting within the same test run is not yet
|
||||
// supported
|
||||
void SetTimeElapseOnlySleepOnReopen(DBOptions* options);
|
||||
|
||||
void ResetTableProperties(TableProperties* tp) {
|
||||
tp->data_size = 0;
|
||||
tp->index_size = 0;
|
||||
tp->filter_size = 0;
|
||||
tp->raw_key_size = 0;
|
||||
tp->raw_value_size = 0;
|
||||
tp->num_data_blocks = 0;
|
||||
tp->num_entries = 0;
|
||||
tp->num_deletions = 0;
|
||||
tp->num_merge_operands = 0;
|
||||
tp->num_range_deletions = 0;
|
||||
}
|
||||
|
||||
void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
|
||||
double dummy_double;
|
||||
std::replace(tp_string.begin(), tp_string.end(), ';', ' ');
|
||||
std::replace(tp_string.begin(), tp_string.end(), '=', ' ');
|
||||
ResetTableProperties(tp);
|
||||
sscanf(tp_string.c_str(),
|
||||
"# data blocks %" SCNu64 " # entries %" SCNu64
|
||||
" # deletions %" SCNu64 " # merge operands %" SCNu64
|
||||
" # range deletions %" SCNu64 " raw key size %" SCNu64
|
||||
" raw average key size %lf "
|
||||
" raw value size %" SCNu64
|
||||
" raw average value size %lf "
|
||||
" data block size %" SCNu64 " index block size (user-key? %" SCNu64
|
||||
", delta-value? %" SCNu64 ") %" SCNu64 " filter block size %" SCNu64,
|
||||
&tp->num_data_blocks, &tp->num_entries, &tp->num_deletions,
|
||||
&tp->num_merge_operands, &tp->num_range_deletions, &tp->raw_key_size,
|
||||
&dummy_double, &tp->raw_value_size, &dummy_double, &tp->data_size,
|
||||
&tp->index_key_is_user_key, &tp->index_value_is_delta_encoded,
|
||||
&tp->index_size, &tp->filter_size);
|
||||
}
|
||||
|
||||
private: // Prone to error on direct use
|
||||
void MaybeInstallTimeElapseOnlySleep(const DBOptions& options);
|
||||
|
||||
|
||||
@@ -1850,31 +1850,31 @@ TEST_P(DBTestUniversalManualCompactionOutputPathId,
|
||||
compact_options.exclusive_manual_compaction = exclusive_manual_compaction_;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
|
||||
ASSERT_EQ(1, TotalLiveFiles(1));
|
||||
ASSERT_EQ(0, GetSstFileCount(options.db_paths[0].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, TotalLiveFilesAtPath(1, options.db_paths[0].path));
|
||||
ASSERT_EQ(1, TotalLiveFilesAtPath(1, options.db_paths[1].path));
|
||||
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
ASSERT_EQ(1, TotalLiveFiles(1));
|
||||
ASSERT_EQ(0, GetSstFileCount(options.db_paths[0].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, TotalLiveFilesAtPath(1, options.db_paths[0].path));
|
||||
ASSERT_EQ(1, TotalLiveFilesAtPath(1, options.db_paths[1].path));
|
||||
|
||||
MakeTables(1, "p", "q", 1);
|
||||
ASSERT_EQ(2, TotalLiveFiles(1));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[0].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, TotalLiveFilesAtPath(1, options.db_paths[0].path));
|
||||
ASSERT_EQ(1, TotalLiveFilesAtPath(1, options.db_paths[1].path));
|
||||
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
ASSERT_EQ(2, TotalLiveFiles(1));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[0].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, TotalLiveFilesAtPath(1, options.db_paths[0].path));
|
||||
ASSERT_EQ(1, TotalLiveFilesAtPath(1, options.db_paths[1].path));
|
||||
|
||||
// Full compaction to DB path 0
|
||||
compact_options.target_path_id = 0;
|
||||
compact_options.exclusive_manual_compaction = exclusive_manual_compaction_;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
|
||||
ASSERT_EQ(1, TotalLiveFiles(1));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[0].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, TotalLiveFilesAtPath(1, options.db_paths[0].path));
|
||||
ASSERT_EQ(0, TotalLiveFilesAtPath(1, options.db_paths[1].path));
|
||||
|
||||
// Fail when compacting to an invalid path ID
|
||||
compact_options.target_path_id = 2;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/db_with_timestamp_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -145,6 +146,11 @@ TEST_F(DBWALTestWithEnrichedEnv, SkipDeletedWALs) {
|
||||
options.write_buffer_size = 128;
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::PurgeObsoleteFiles:End",
|
||||
"DBWALTestWithEnrichedEnv.SkipDeletedWALs:AfterFlush"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions writeOpt = WriteOptions();
|
||||
for (int i = 0; i < 128 * 5; i++) {
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, "foo", "v1"));
|
||||
@@ -153,6 +159,8 @@ TEST_F(DBWALTestWithEnrichedEnv, SkipDeletedWALs) {
|
||||
fo.wait = true;
|
||||
ASSERT_OK(db_->Flush(fo));
|
||||
|
||||
TEST_SYNC_POINT("DBWALTestWithEnrichedEnv.SkipDeletedWALs:AfterFlush");
|
||||
|
||||
// some wals are deleted
|
||||
ASSERT_NE(0, enriched_env_->deleted_wal_cnt);
|
||||
// but not the first one
|
||||
@@ -163,6 +171,8 @@ TEST_F(DBWALTestWithEnrichedEnv, SkipDeletedWALs) {
|
||||
Reopen(options);
|
||||
ASSERT_FALSE(enriched_env_->deleted_wal_reopened);
|
||||
ASSERT_FALSE(enriched_env_->gap_in_wals);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, WAL) {
|
||||
@@ -301,6 +311,204 @@ TEST_F(DBWALTest, Recover) {
|
||||
} while (ChangeWalOptions());
|
||||
}
|
||||
|
||||
class DBWALTestWithTimestamp
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<test::UserDefinedTimestampTestMode> {
|
||||
public:
|
||||
DBWALTestWithTimestamp()
|
||||
: DBBasicTestWithTimestampBase("db_wal_test_with_timestamp") {}
|
||||
|
||||
void SetUp() override {
|
||||
persist_udt_ = test::ShouldPersistUDT(GetParam());
|
||||
DBBasicTestWithTimestampBase::SetUp();
|
||||
}
|
||||
|
||||
Status CreateAndReopenWithCFWithTs(const std::vector<std::string>& cfs,
|
||||
const Options& options,
|
||||
bool avoid_flush_during_recovery = false) {
|
||||
CreateColumnFamilies(cfs, options);
|
||||
return ReopenColumnFamiliesWithTs(cfs, options,
|
||||
avoid_flush_during_recovery);
|
||||
}
|
||||
|
||||
Status ReopenColumnFamiliesWithTs(const std::vector<std::string>& cfs,
|
||||
Options ts_options,
|
||||
bool avoid_flush_during_recovery = false) {
|
||||
Options default_options = CurrentOptions();
|
||||
default_options.create_if_missing = false;
|
||||
default_options.avoid_flush_during_recovery = avoid_flush_during_recovery;
|
||||
ts_options.create_if_missing = false;
|
||||
|
||||
std::vector<Options> cf_options(cfs.size(), ts_options);
|
||||
std::vector<std::string> cfs_plus_default = cfs;
|
||||
cfs_plus_default.insert(cfs_plus_default.begin(), kDefaultColumnFamilyName);
|
||||
cf_options.insert(cf_options.begin(), default_options);
|
||||
Close();
|
||||
return TryReopenWithColumnFamilies(cfs_plus_default, cf_options);
|
||||
}
|
||||
|
||||
Status Put(uint32_t cf, const Slice& key, const Slice& ts,
|
||||
const Slice& value) {
|
||||
WriteOptions write_opts;
|
||||
return db_->Put(write_opts, handles_[cf], key, ts, value);
|
||||
}
|
||||
|
||||
void CheckGet(const ReadOptions& read_opts, uint32_t cf, const Slice& key,
|
||||
const std::string& expected_value,
|
||||
const std::string& expected_ts) {
|
||||
std::string actual_value;
|
||||
std::string actual_ts;
|
||||
ASSERT_OK(
|
||||
db_->Get(read_opts, handles_[cf], key, &actual_value, &actual_ts));
|
||||
ASSERT_EQ(expected_value, actual_value);
|
||||
ASSERT_EQ(expected_ts, actual_ts);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool persist_udt_;
|
||||
};
|
||||
|
||||
TEST_P(DBWALTestWithTimestamp, RecoverAndNoFlush) {
|
||||
// Set up the option that enables user defined timestmp size.
|
||||
std::string ts1 = Timestamp(1, 0);
|
||||
const size_t kTimestampSize = ts1.size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
Options ts_options;
|
||||
ts_options.create_if_missing = true;
|
||||
ts_options.comparator = &test_cmp;
|
||||
// Test that user-defined timestamps are recovered from WAL regardless of
|
||||
// the value of this flag because UDTs are saved in WAL nonetheless.
|
||||
// We however need to explicitly disable flush during recovery by setting
|
||||
// `avoid_flush_during_recovery=true` so that we can avoid timestamps getting
|
||||
// stripped when the `persist_user_defined_timestamps` flag is false, so that
|
||||
// all written timestamps are available for testing user-defined time travel
|
||||
// read.
|
||||
ts_options.persist_user_defined_timestamps = persist_udt_;
|
||||
bool avoid_flush_during_recovery = true;
|
||||
|
||||
ReadOptions read_opts;
|
||||
do {
|
||||
Slice ts_slice = ts1;
|
||||
read_opts.timestamp = &ts_slice;
|
||||
ASSERT_OK(CreateAndReopenWithCFWithTs({"pikachu"}, ts_options,
|
||||
avoid_flush_during_recovery));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"), 0U);
|
||||
ASSERT_OK(Put(1, "foo", ts1, "v1"));
|
||||
ASSERT_OK(Put(1, "baz", ts1, "v5"));
|
||||
|
||||
ASSERT_OK(ReopenColumnFamiliesWithTs({"pikachu"}, ts_options,
|
||||
avoid_flush_during_recovery));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"), 0U);
|
||||
// Do a timestamped read with ts1 after second reopen.
|
||||
CheckGet(read_opts, 1, "foo", "v1", ts1);
|
||||
CheckGet(read_opts, 1, "baz", "v5", ts1);
|
||||
|
||||
// Write more value versions for key "foo" and "bar" before and after second
|
||||
// reopen.
|
||||
std::string ts2 = Timestamp(2, 0);
|
||||
ASSERT_OK(Put(1, "bar", ts2, "v2"));
|
||||
ASSERT_OK(Put(1, "foo", ts2, "v3"));
|
||||
|
||||
ASSERT_OK(ReopenColumnFamiliesWithTs({"pikachu"}, ts_options,
|
||||
avoid_flush_during_recovery));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"), 0U);
|
||||
std::string ts3 = Timestamp(3, 0);
|
||||
ASSERT_OK(Put(1, "foo", ts3, "v4"));
|
||||
|
||||
// Do a timestamped read with ts1 after third reopen.
|
||||
CheckGet(read_opts, 1, "foo", "v1", ts1);
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(read_opts, handles_[1], "bar", &value).IsNotFound());
|
||||
CheckGet(read_opts, 1, "baz", "v5", ts1);
|
||||
|
||||
// Do a timestamped read with ts2 after third reopen.
|
||||
ts_slice = ts2;
|
||||
CheckGet(read_opts, 1, "foo", "v3", ts2);
|
||||
CheckGet(read_opts, 1, "bar", "v2", ts2);
|
||||
CheckGet(read_opts, 1, "baz", "v5", ts1);
|
||||
|
||||
// Do a timestamped read with ts3 after third reopen.
|
||||
ts_slice = ts3;
|
||||
CheckGet(read_opts, 1, "foo", "v4", ts3);
|
||||
CheckGet(read_opts, 1, "bar", "v2", ts2);
|
||||
CheckGet(read_opts, 1, "baz", "v5", ts1);
|
||||
} while (ChangeWalOptions());
|
||||
}
|
||||
|
||||
TEST_P(DBWALTestWithTimestamp, RecoverInconsistentTimestamp) {
|
||||
// Set up the option that enables user defined timestmp size.
|
||||
std::string ts = Timestamp(1, 0);
|
||||
const size_t kTimestampSize = ts.size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
Options ts_options;
|
||||
ts_options.create_if_missing = true;
|
||||
ts_options.comparator = &test_cmp;
|
||||
ts_options.persist_user_defined_timestamps = persist_udt_;
|
||||
|
||||
ASSERT_OK(CreateAndReopenWithCFWithTs({"pikachu"}, ts_options));
|
||||
ASSERT_OK(Put(1, "foo", ts, "v1"));
|
||||
ASSERT_OK(Put(1, "baz", ts, "v5"));
|
||||
|
||||
// In real use cases, switching to a different user comparator is prohibited
|
||||
// by a sanity check during DB open that does a user comparator name
|
||||
// comparison. This test mocked and bypassed that sanity check because the
|
||||
// before and after user comparator are both named "TestComparator". This is
|
||||
// to test the user-defined timestamp recovery logic for WAL files have
|
||||
// the intended consistency check.
|
||||
// `HandleWriteBatchTimestampSizeDifference` in udt_util.h has more details.
|
||||
TestComparator diff_test_cmp(kTimestampSize + 1);
|
||||
ts_options.comparator = &diff_test_cmp;
|
||||
ASSERT_TRUE(
|
||||
ReopenColumnFamiliesWithTs({"pikachu"}, ts_options).IsInvalidArgument());
|
||||
}
|
||||
|
||||
TEST_P(DBWALTestWithTimestamp, RecoverAndFlush) {
|
||||
// Set up the option that enables user defined timestamp size.
|
||||
std::string min_ts = Timestamp(0, 0);
|
||||
std::string write_ts = Timestamp(1, 0);
|
||||
const size_t kTimestampSize = write_ts.size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
Options ts_options;
|
||||
ts_options.create_if_missing = true;
|
||||
ts_options.comparator = &test_cmp;
|
||||
ts_options.persist_user_defined_timestamps = persist_udt_;
|
||||
|
||||
std::string smallest_ukey_without_ts = "baz";
|
||||
std::string largest_ukey_without_ts = "foo";
|
||||
|
||||
ASSERT_OK(CreateAndReopenWithCFWithTs({"pikachu"}, ts_options));
|
||||
// No flush, no sst files, because of no data.
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"), 0U);
|
||||
ASSERT_OK(Put(1, largest_ukey_without_ts, write_ts, "v1"));
|
||||
ASSERT_OK(Put(1, smallest_ukey_without_ts, write_ts, "v5"));
|
||||
|
||||
ASSERT_OK(ReopenColumnFamiliesWithTs({"pikachu"}, ts_options));
|
||||
// Memtable recovered from WAL flushed because `avoid_flush_during_recovery`
|
||||
// defaults to false, created one L0 file.
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"), 1U);
|
||||
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
dbfull()->TEST_GetFilesMetaData(handles_[1], &level_to_files);
|
||||
ASSERT_GT(level_to_files.size(), 1);
|
||||
// L0 only has one SST file.
|
||||
ASSERT_EQ(level_to_files[0].size(), 1);
|
||||
auto meta = level_to_files[0][0];
|
||||
if (persist_udt_) {
|
||||
ASSERT_EQ(smallest_ukey_without_ts + write_ts, meta.smallest.user_key());
|
||||
ASSERT_EQ(largest_ukey_without_ts + write_ts, meta.largest.user_key());
|
||||
} else {
|
||||
ASSERT_EQ(smallest_ukey_without_ts + min_ts, meta.smallest.user_key());
|
||||
ASSERT_EQ(largest_ukey_without_ts + min_ts, meta.largest.user_key());
|
||||
}
|
||||
}
|
||||
|
||||
// Param 0: test mode for the user-defined timestamp feature
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBWALTestWithTimestamp, DBWALTestWithTimestamp,
|
||||
::testing::Values(
|
||||
test::UserDefinedTimestampTestMode::kStripUserDefinedTimestamp,
|
||||
test::UserDefinedTimestampTestMode::kNormal));
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithTableHandle) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
@@ -2399,6 +2607,31 @@ TEST_F(DBWALTest, GetCompressedWalsAfterSync) {
|
||||
Status s = dbfull()->GetSortedWalFiles(wals);
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, EmptyWalReopenTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// make sure we can re-open it.
|
||||
ASSERT_OK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
|
||||
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
int num_wal_files = 0;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &files));
|
||||
for (const auto& file : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type = kWalFile;
|
||||
if (ParseFileName(file, &number, &type) && type == kWalFile) {
|
||||
num_wal_files++;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(num_wal_files, 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -3273,6 +3273,76 @@ TEST_F(UpdateFullHistoryTsLowTest, ConcurrentUpdate) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// Tests the effect of flag `persist_user_defined_timestamps` on the file
|
||||
// boundaries contained in the Manifest, a.k.a FileMetaData.smallest,
|
||||
// FileMetaData.largest.
|
||||
class HandleFileBoundariesTest
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<test::UserDefinedTimestampTestMode> {
|
||||
public:
|
||||
HandleFileBoundariesTest()
|
||||
: DBBasicTestWithTimestampBase("/handle_file_boundaries") {}
|
||||
};
|
||||
|
||||
TEST_P(HandleFileBoundariesTest, ConfigurePersistUdt) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
// Write a timestamp that is not the min timestamp to help test the behavior
|
||||
// of flag `persist_user_defined_timestamps`.
|
||||
std::string write_ts = Timestamp(1, 0);
|
||||
std::string min_ts = Timestamp(0, 0);
|
||||
std::string smallest_ukey_without_ts = "bar";
|
||||
std::string largest_ukey_without_ts = "foo";
|
||||
const size_t kTimestampSize = write_ts.size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
bool persist_udt = test::ShouldPersistUDT(GetParam());
|
||||
options.persist_user_defined_timestamps = persist_udt;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), smallest_ukey_without_ts, write_ts, "val1"));
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), largest_ukey_without_ts, write_ts, "val2"));
|
||||
|
||||
// Create a L0 SST file and its record is added to the Manfiest.
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
|
||||
options.create_if_missing = false;
|
||||
// Reopen the DB and process manifest file.
|
||||
Reopen(options);
|
||||
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
|
||||
&level_to_files);
|
||||
ASSERT_GT(level_to_files.size(), 1);
|
||||
// L0 only has one SST file.
|
||||
ASSERT_EQ(level_to_files[0].size(), 1);
|
||||
auto file_meta = level_to_files[0][0];
|
||||
if (persist_udt) {
|
||||
ASSERT_EQ(smallest_ukey_without_ts + write_ts,
|
||||
file_meta.smallest.user_key());
|
||||
ASSERT_EQ(largest_ukey_without_ts + write_ts, file_meta.largest.user_key());
|
||||
} else {
|
||||
// If `persist_user_defined_timestamps` is false, the file boundaries should
|
||||
// have the min timestamp. Behind the scenes, when file boundaries in
|
||||
// FileMetaData is persisted to Manifest, the original user-defined
|
||||
// timestamps in user key are stripped. When manifest is read and processed
|
||||
// during DB open, a min timestamp is padded to the file boundaries. This
|
||||
// test's writes contain non min timestamp to verify this logic end-to-end.
|
||||
ASSERT_EQ(smallest_ukey_without_ts + min_ts, file_meta.smallest.user_key());
|
||||
ASSERT_EQ(largest_ukey_without_ts + min_ts, file_meta.largest.user_key());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ConfigurePersistUdt, HandleFileBoundariesTest,
|
||||
::testing::Values(
|
||||
test::UserDefinedTimestampTestMode::kStripUserDefinedTimestamp,
|
||||
test::UserDefinedTimestampTestMode::kNormal));
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp,
|
||||
GCPreserveRangeTombstoneWhenNoOrSmallFullHistoryLow) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -101,6 +101,35 @@ void AppendUserKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
}
|
||||
}
|
||||
|
||||
void PadInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(ts_sz > 0);
|
||||
size_t user_key_size = key.size() - kNumInternalBytes;
|
||||
result->reserve(key.size() + ts_sz);
|
||||
result->append(key.data(), user_key_size);
|
||||
result->append(ts_sz, static_cast<unsigned char>(0));
|
||||
result->append(key.data() + user_key_size, kNumInternalBytes);
|
||||
}
|
||||
|
||||
void StripTimestampFromInternalKey(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(key.size() >= ts_sz + kNumInternalBytes);
|
||||
result->reserve(key.size() - ts_sz);
|
||||
result->append(key.data(), key.size() - kNumInternalBytes - ts_sz);
|
||||
result->append(key.data() + key.size() - kNumInternalBytes,
|
||||
kNumInternalBytes);
|
||||
}
|
||||
|
||||
void ReplaceInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
const size_t key_sz = key.size();
|
||||
assert(key_sz >= ts_sz + kNumInternalBytes);
|
||||
result->reserve(key_sz);
|
||||
result->append(key.data(), key_sz - kNumInternalBytes - ts_sz);
|
||||
result->append(ts_sz, static_cast<unsigned char>(0));
|
||||
result->append(key.data() + key_sz - kNumInternalBytes, kNumInternalBytes);
|
||||
}
|
||||
|
||||
std::string ParsedInternalKey::DebugString(bool log_err_key, bool hex) const {
|
||||
std::string result = "'";
|
||||
if (log_err_key) {
|
||||
|
||||
+110
-20
@@ -168,39 +168,57 @@ inline void UnPackSequenceAndType(uint64_t packed, uint64_t* seq,
|
||||
EntryType GetEntryType(ValueType value_type);
|
||||
|
||||
// Append the serialization of "key" to *result.
|
||||
extern void AppendInternalKey(std::string* result,
|
||||
const ParsedInternalKey& key);
|
||||
void AppendInternalKey(std::string* result, const ParsedInternalKey& key);
|
||||
|
||||
// Append the serialization of "key" to *result, replacing the original
|
||||
// timestamp with argument ts.
|
||||
extern void AppendInternalKeyWithDifferentTimestamp(
|
||||
std::string* result, const ParsedInternalKey& key, const Slice& ts);
|
||||
void AppendInternalKeyWithDifferentTimestamp(std::string* result,
|
||||
const ParsedInternalKey& key,
|
||||
const Slice& ts);
|
||||
|
||||
// Serialized internal key consists of user key followed by footer.
|
||||
// This function appends the footer to *result, assuming that *result already
|
||||
// contains the user key at the end.
|
||||
extern void AppendInternalKeyFooter(std::string* result, SequenceNumber s,
|
||||
ValueType t);
|
||||
void AppendInternalKeyFooter(std::string* result, SequenceNumber s,
|
||||
ValueType t);
|
||||
|
||||
// Append the key and a minimal timestamp to *result
|
||||
extern void AppendKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
void AppendKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// Append the key and a maximal timestamp to *result
|
||||
extern void AppendKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
void AppendKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// `key` is a user key with timestamp. Append the user key without timestamp
|
||||
// and the maximal timestamp to *result.
|
||||
extern void AppendUserKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
void AppendUserKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// `key` is an internal key containing a user key without timestamp. Create a
|
||||
// new key in *result by padding a min timestamp of size `ts_sz` to the user key
|
||||
// and copying the remaining internal key bytes.
|
||||
void PadInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// `key` is an internal key containing a user key with timestamp of size
|
||||
// `ts_sz`. Create a new internal key in *result by stripping the timestamp from
|
||||
// the user key and copying the remaining internal key bytes.
|
||||
void StripTimestampFromInternalKey(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// `key` is an internal key containing a user key with timestamp of size
|
||||
// `ts_sz`. Create a new internal key in *result while replace the original
|
||||
// timestamp with min timestamp.
|
||||
void ReplaceInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// Attempt to parse an internal key from "internal_key". On success,
|
||||
// stores the parsed data in "*result", and returns true.
|
||||
//
|
||||
// On error, returns false, leaves "*result" in an undefined state.
|
||||
extern Status ParseInternalKey(const Slice& internal_key,
|
||||
ParsedInternalKey* result, bool log_err_key);
|
||||
Status ParseInternalKey(const Slice& internal_key, ParsedInternalKey* result,
|
||||
bool log_err_key);
|
||||
|
||||
// Returns the user key portion of an internal key.
|
||||
inline Slice ExtractUserKey(const Slice& internal_key) {
|
||||
@@ -504,6 +522,62 @@ class IterKey {
|
||||
key_size_ = total_size;
|
||||
}
|
||||
|
||||
// A version of `TrimAppend` assuming the last bytes of length `ts_sz` in the
|
||||
// user key part of `key_` is not counted towards shared bytes. And the
|
||||
// decoded key needed a min timestamp of length `ts_sz` pad to the user key.
|
||||
void TrimAppendWithTimestamp(const size_t shared_len,
|
||||
const char* non_shared_data,
|
||||
const size_t non_shared_len,
|
||||
const size_t ts_sz) {
|
||||
std::string kTsMin(ts_sz, static_cast<unsigned char>(0));
|
||||
std::string key_with_ts;
|
||||
std::vector<Slice> key_parts_with_ts;
|
||||
if (IsUserKey()) {
|
||||
key_parts_with_ts = {Slice(key_, shared_len),
|
||||
Slice(non_shared_data, non_shared_len),
|
||||
Slice(kTsMin)};
|
||||
} else {
|
||||
assert(shared_len + non_shared_len >= kNumInternalBytes);
|
||||
// Invaraint: shared_user_key_len + shared_internal_bytes_len = shared_len
|
||||
// In naming below `*_len` variables, keyword `user_key` refers to the
|
||||
// user key part of the existing key in `key_` as apposed to the new key.
|
||||
// Similary, `internal_bytes` refers to the footer part of the existing
|
||||
// key. These bytes potentially will move between user key part and the
|
||||
// footer part in the new key.
|
||||
const size_t user_key_len = key_size_ - kNumInternalBytes;
|
||||
const size_t sharable_user_key_len = user_key_len - ts_sz;
|
||||
const size_t shared_user_key_len =
|
||||
std::min(shared_len, sharable_user_key_len);
|
||||
const size_t shared_internal_bytes_len = shared_len - shared_user_key_len;
|
||||
|
||||
// One Slice among the three Slices will get split into two Slices, plus
|
||||
// a timestamp slice.
|
||||
key_parts_with_ts.reserve(5);
|
||||
bool ts_added = false;
|
||||
// Add slice parts and find the right location to add the min timestamp.
|
||||
MaybeAddKeyPartsWithTimestamp(
|
||||
key_, shared_user_key_len,
|
||||
shared_internal_bytes_len + non_shared_len < kNumInternalBytes,
|
||||
shared_len + non_shared_len - kNumInternalBytes, kTsMin,
|
||||
key_parts_with_ts, &ts_added);
|
||||
MaybeAddKeyPartsWithTimestamp(
|
||||
key_ + user_key_len, shared_internal_bytes_len,
|
||||
non_shared_len < kNumInternalBytes,
|
||||
shared_internal_bytes_len + non_shared_len - kNumInternalBytes,
|
||||
kTsMin, key_parts_with_ts, &ts_added);
|
||||
MaybeAddKeyPartsWithTimestamp(non_shared_data, non_shared_len,
|
||||
non_shared_len >= kNumInternalBytes,
|
||||
non_shared_len - kNumInternalBytes, kTsMin,
|
||||
key_parts_with_ts, &ts_added);
|
||||
assert(ts_added);
|
||||
}
|
||||
|
||||
Slice new_key(SliceParts(&key_parts_with_ts.front(),
|
||||
static_cast<int>(key_parts_with_ts.size())),
|
||||
&key_with_ts);
|
||||
SetKey(new_key);
|
||||
}
|
||||
|
||||
Slice SetKey(const Slice& key, bool copy = true) {
|
||||
// is_user_key_ expected to be set already via SetIsUserKey
|
||||
return SetKeyImpl(key, copy);
|
||||
@@ -620,7 +694,7 @@ class IterKey {
|
||||
const char* key_;
|
||||
size_t key_size_;
|
||||
size_t buf_size_;
|
||||
char space_[32]; // Avoid allocation for short keys
|
||||
char space_[39]; // Avoid allocation for short keys
|
||||
bool is_user_key_;
|
||||
|
||||
Slice SetKeyImpl(const Slice& key, bool copy) {
|
||||
@@ -661,6 +735,23 @@ class IterKey {
|
||||
}
|
||||
|
||||
void EnlargeBuffer(size_t key_size);
|
||||
|
||||
void MaybeAddKeyPartsWithTimestamp(const char* slice_data,
|
||||
const size_t slice_sz, bool add_timestamp,
|
||||
const size_t left_sz,
|
||||
const std::string& min_timestamp,
|
||||
std::vector<Slice>& key_parts,
|
||||
bool* ts_added) {
|
||||
if (add_timestamp && !*ts_added) {
|
||||
assert(slice_sz >= left_sz);
|
||||
key_parts.emplace_back(slice_data, left_sz);
|
||||
key_parts.emplace_back(min_timestamp);
|
||||
key_parts.emplace_back(slice_data + left_sz, slice_sz - left_sz);
|
||||
*ts_added = true;
|
||||
} else {
|
||||
key_parts.emplace_back(slice_data, slice_sz);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Convert from a SliceTransform of user keys, to a SliceTransform of
|
||||
@@ -698,8 +789,7 @@ class InternalKeySliceTransform : public SliceTransform {
|
||||
// Read the key of a record from a write batch.
|
||||
// if this record represent the default column family then cf_record
|
||||
// must be passed as false, otherwise it must be passed as true.
|
||||
extern bool ReadKeyFromWriteBatchEntry(Slice* input, Slice* key,
|
||||
bool cf_record);
|
||||
bool ReadKeyFromWriteBatchEntry(Slice* input, Slice* key, bool cf_record);
|
||||
|
||||
// Read record from a write batch piece from input.
|
||||
// tag, column_family, key, value and blob are return values. Callers own the
|
||||
@@ -708,9 +798,9 @@ extern bool ReadKeyFromWriteBatchEntry(Slice* input, Slice* key,
|
||||
// input will be advanced to after the record.
|
||||
// If user-defined timestamp is enabled for a column family, then the `key`
|
||||
// resulting from this call will include timestamp.
|
||||
extern Status ReadRecordFromWriteBatch(Slice* input, char* tag,
|
||||
uint32_t* column_family, Slice* key,
|
||||
Slice* value, Slice* blob, Slice* xid);
|
||||
Status ReadRecordFromWriteBatch(Slice* input, char* tag,
|
||||
uint32_t* column_family, Slice* key,
|
||||
Slice* value, Slice* blob, Slice* xid);
|
||||
|
||||
// When user call DeleteRange() to delete a range of keys,
|
||||
// we will store a serialized RangeTombstone in MemTable and SST.
|
||||
|
||||
@@ -178,6 +178,79 @@ TEST_F(FormatTest, IterKeyOperation) {
|
||||
"abcdefghijklmnopqrstuvwxyz"));
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, IterKeyWithTimestampOperation) {
|
||||
IterKey k;
|
||||
k.SetUserKey("");
|
||||
const char p[] = "abcdefghijklmnopqrstuvwxyz";
|
||||
const char q[] = "0123456789";
|
||||
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string(""));
|
||||
|
||||
size_t ts_sz = 8;
|
||||
std::string min_timestamp(ts_sz, static_cast<unsigned char>(0));
|
||||
k.TrimAppendWithTimestamp(0, p, 3, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abc" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(1, p, 3, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"aabc" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(0, p, 26, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(26, q, 10, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(36, q, 1, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz01234567890" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(26, q, 1, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz0" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(27, p, 26, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz0"
|
||||
"abcdefghijklmnopqrstuvwxyz" +
|
||||
min_timestamp);
|
||||
// IterKey holds an internal key, the last 8 bytes hold the key footer, the
|
||||
// timestamp is expected to be added before the key footer.
|
||||
std::string key_without_ts = "keywithoutts";
|
||||
k.SetInternalKey(key_without_ts + min_timestamp + "internal");
|
||||
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
key_without_ts + min_timestamp + "internal");
|
||||
k.TrimAppendWithTimestamp(0, p, 10, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"ab" + min_timestamp + "cdefghij");
|
||||
|
||||
k.TrimAppendWithTimestamp(1, p, 8, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"a" + min_timestamp + "abcdefgh");
|
||||
|
||||
k.TrimAppendWithTimestamp(9, p, 3, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"aabc" + min_timestamp + "defghabc");
|
||||
|
||||
k.TrimAppendWithTimestamp(10, q, 10, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"aabcdefgha01" + min_timestamp + "23456789");
|
||||
|
||||
k.TrimAppendWithTimestamp(20, q, 1, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"aabcdefgha012" + min_timestamp + "34567890");
|
||||
|
||||
k.TrimAppendWithTimestamp(21, p, 26, ts_sz);
|
||||
ASSERT_EQ(
|
||||
std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"aabcdefgha01234567890abcdefghijklmnopqr" + min_timestamp + "stuvwxyz");
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, UpdateInternalKey) {
|
||||
std::string user_key("abcdefghijklmnopqrstuvwxyz");
|
||||
uint64_t new_seq = 0x123456;
|
||||
@@ -204,6 +277,62 @@ TEST_F(FormatTest, RangeTombstoneSerializeEndKey) {
|
||||
ASSERT_LT(cmp.Compare(t.SerializeEndKey(), k), 0);
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, PadInternalKeyWithMinTimestamp) {
|
||||
std::string orig_user_key = "foo";
|
||||
std::string orig_internal_key = IKey(orig_user_key, 100, kTypeValue);
|
||||
size_t ts_sz = 8;
|
||||
|
||||
std::string key_buf;
|
||||
PadInternalKeyWithMinTimestamp(&key_buf, orig_internal_key, ts_sz);
|
||||
ParsedInternalKey key_with_timestamp;
|
||||
Slice in(key_buf);
|
||||
ASSERT_OK(ParseInternalKey(in, &key_with_timestamp, true /*log_err_key*/));
|
||||
|
||||
std::string min_timestamp(ts_sz, static_cast<unsigned char>(0));
|
||||
ASSERT_EQ(orig_user_key + min_timestamp, key_with_timestamp.user_key);
|
||||
ASSERT_EQ(100, key_with_timestamp.sequence);
|
||||
ASSERT_EQ(kTypeValue, key_with_timestamp.type);
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, StripTimestampFromInternalKey) {
|
||||
std::string orig_user_key = "foo";
|
||||
size_t ts_sz = 8;
|
||||
std::string timestamp(ts_sz, static_cast<unsigned char>(0));
|
||||
orig_user_key.append(timestamp.data(), timestamp.size());
|
||||
std::string orig_internal_key = IKey(orig_user_key, 100, kTypeValue);
|
||||
|
||||
std::string key_buf;
|
||||
StripTimestampFromInternalKey(&key_buf, orig_internal_key, ts_sz);
|
||||
ParsedInternalKey key_without_timestamp;
|
||||
Slice in(key_buf);
|
||||
ASSERT_OK(ParseInternalKey(in, &key_without_timestamp, true /*log_err_key*/));
|
||||
|
||||
ASSERT_EQ("foo", key_without_timestamp.user_key);
|
||||
ASSERT_EQ(100, key_without_timestamp.sequence);
|
||||
ASSERT_EQ(kTypeValue, key_without_timestamp.type);
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, ReplaceInternalKeyWithMinTimestamp) {
|
||||
std::string orig_user_key = "foo";
|
||||
size_t ts_sz = 8;
|
||||
orig_user_key.append(ts_sz, static_cast<unsigned char>(1));
|
||||
std::string orig_internal_key = IKey(orig_user_key, 100, kTypeValue);
|
||||
|
||||
std::string key_buf;
|
||||
ReplaceInternalKeyWithMinTimestamp(&key_buf, orig_internal_key, ts_sz);
|
||||
ParsedInternalKey new_key;
|
||||
Slice in(key_buf);
|
||||
ASSERT_OK(ParseInternalKey(in, &new_key, true /*log_err_key*/));
|
||||
|
||||
std::string min_timestamp(ts_sz, static_cast<unsigned char>(0));
|
||||
size_t ukey_diff_offset = new_key.user_key.difference_offset(orig_user_key);
|
||||
ASSERT_EQ(min_timestamp,
|
||||
Slice(new_key.user_key.data() + ukey_diff_offset, ts_sz));
|
||||
ASSERT_EQ(orig_user_key.size(), new_key.user_key.size());
|
||||
ASSERT_EQ(100, new_key.sequence);
|
||||
ASSERT_EQ(kTypeValue, new_key.type);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -279,6 +279,7 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set regular background error\n");
|
||||
@@ -416,7 +417,11 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
@@ -443,9 +448,15 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
&auto_recovery);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set background retryable IO error\n");
|
||||
@@ -486,6 +497,8 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
} else {
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
// HandleKnownErrors() will use recovery_error_, so ignore
|
||||
// recovery_io_error_.
|
||||
|
||||
Loaded 100 of 505 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user