mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
791a7fe402 | ||
|
|
ecfb8779c2 | ||
|
|
869801945b | ||
|
|
a89bd81df4 | ||
|
|
3f7c92b975 | ||
|
|
e978dccd7a | ||
|
|
32c6de7bc3 | ||
|
|
b36f691e58 | ||
|
|
0f6d363db9 | ||
|
|
031714aa83 | ||
|
|
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 | ||
|
|
a2c1f57358 | ||
|
|
fb63d9b4ee | ||
|
|
d79be3dca2 | ||
|
|
adc9001f20 | ||
|
|
151242ce46 | ||
|
|
0a774a102f | ||
|
|
6cac4c79d4 | ||
|
|
43e9a60bb2 | ||
|
|
bd80433c73 | ||
|
|
9b698cda51 | ||
|
|
f3818948e8 | ||
|
|
b8555ba470 | ||
|
|
226ee25d30 | ||
|
|
4d72f48e57 | ||
|
|
ba16e8eee7 | ||
|
|
9500d90d1b | ||
|
|
6b67b561bc | ||
|
|
647cd73674 | ||
|
|
760b773f58 | ||
|
|
d5a9c0c937 | ||
|
|
c8552d8c63 | ||
|
|
d30bb3d14a | ||
|
|
b2c4bc5f73 | ||
|
|
140dd93b57 | ||
|
|
64cead919f | ||
|
|
f9db0c6e9c | ||
|
|
f631138e1c | ||
|
|
0578d9f951 | ||
|
|
b3c43a5b99 | ||
|
|
0623c5b903 | ||
|
|
7f5b9f40cb | ||
|
|
b45738622a | ||
|
|
03ccb1cd42 | ||
|
|
3c17930ede | ||
|
|
e5a560ec98 | ||
|
|
b4d78189b3 | ||
|
|
891ced8b15 | ||
|
|
39c29372bf | ||
|
|
0efd7b4ba1 | ||
|
|
c14eb134ed | ||
|
|
601320164b | ||
|
|
40c2ec6d08 | ||
|
|
9f8cdc8ad6 | ||
|
|
57abdea389 | ||
|
|
8f6c2a2cc0 | ||
|
|
b92bc04ab0 | ||
|
|
cea81cad66 | ||
|
|
8c445407b7 | ||
|
|
87de4fee6b |
No files matched your search
@@ -95,3 +95,4 @@ fuzz/crash-*
|
||||
|
||||
cmake-build-*
|
||||
third-party/folly/
|
||||
.cache
|
||||
+16
-52
@@ -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")
|
||||
@@ -1010,12 +979,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 +1290,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
|
||||
|
||||
+59
-1
@@ -1,7 +1,65 @@
|
||||
# Rocksdb Change Log
|
||||
## 8.1.1 (04/06/2023)
|
||||
## 8.3.3 (09/01/2023)
|
||||
### Bug Fixes
|
||||
* Fix a bug where if there is an error reading from offset 0 of a file from L1+ and that the file is not the first file in the sorted run, data can be lost in compaction and read/scan can return incorrect results.
|
||||
* Fix a bug where iterator may return incorrect result for DeleteRange() users if there was an error reading from a file.
|
||||
* Fixed a race condition in `GenericRateLimiter` that could cause it to stop granting requests
|
||||
|
||||
## 8.3.2 (06/14/2023)
|
||||
### 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)
|
||||
|
||||
## 8.3.1 (06/07/2023)
|
||||
### Performance Improvements
|
||||
* Fixed higher read QPS during DB::Open() reading files created prior to #11406, especially when reading many small file (size < 52 MB) during DB::Open() and partitioned filter or index is used.
|
||||
|
||||
## 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.
|
||||
* Remove unnecessary constructor for CompressionOptions.
|
||||
|
||||
### Behavior changes
|
||||
* Changed default block cache size from an 8MB to 32MB LRUCache, which increases the default number of cache shards from 16 to 64. This change is intended to minimize cache mutex contention under stress conditions. See https://github.com/facebook/rocksdb/wiki/Block-Cache for more information.
|
||||
* For level compaction with `level_compaction_dynamic_level_bytes=true`, RocksDB now trivially moves levels down to fill LSM starting from bottommost level during DB open. See more in comments for option `level_compaction_dynamic_level_bytes` (#11321).
|
||||
* User-provided `ReadOptions` take effect for more reads of non-`CacheEntryRole::kDataBlock` blocks.
|
||||
* For level compaction with `level_compaction_dynamic_level_bytes=true`, RocksDB now drains unnecessary levels through background compaction automatically (#11340). This together with #11321 makes it automatic to migrate other compaction settings to level compaction with `level_compaction_dynamic_level_bytes=true`. In addition, a live DB that becomes smaller will now have unnecessary levels drained which can help to reduce read and space amp.
|
||||
* If `CompactRange()` is called with `CompactRangeOptions::bottommost_level_compaction=kForce*` to compact from L0 to L1, RocksDB now will try to do trivial move from L0 to L1 and then do an intra L1 compaction, instead of a L0 to L1 compaction with trivial move disabled (#11375)).
|
||||
|
||||
### 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.
|
||||
* Allow runtime changes to whether `WriteBufferManager` allows stall or not by calling `SetAllowStall()`
|
||||
* 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.
|
||||
|
||||
## 8.1.0 (03/18/2023)
|
||||
### Behavior changes
|
||||
|
||||
+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_CFLAGS := $(filter-out -march=native -DHAVE_SSE42 -DHAVE_AVX2, $(PLATFORM_CFLAGS))
|
||||
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
|
||||
@@ -539,7 +539,7 @@ endif
|
||||
|
||||
ifdef USE_CLANG
|
||||
# Used by some teams in Facebook
|
||||
WARNING_FLAGS += -Wshift-sign-overflow
|
||||
WARNING_FLAGS += -Wshift-sign-overflow -Wambiguous-reversed-operator
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
@@ -1480,6 +1480,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 +2438,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 +2450,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.
|
||||
@@ -1,8 +1,6 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://circleci.com/gh/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/main)
|
||||
[](http://140-211-168-68-openstack.osuosl.org:8080/job/rocksdb)
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
It is built on earlier work on [LevelDB](https://github.com/google/leveldb) by Sanjay Ghemawat (sanjay@google.com)
|
||||
|
||||
@@ -392,6 +392,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",
|
||||
@@ -4749,6 +4750,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"],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,6 +48,9 @@ Learn more about those use cases in a Tech Talk by Ankit Gupta and Naveen Somasu
|
||||
## Yahoo
|
||||
Yahoo is using RocksDB as a storage engine for their biggest distributed data store Sherpa. Learn more about it here: http://yahooeng.tumblr.com/post/120730204806/sherpa-scales-new-heights
|
||||
|
||||
## Tencent
|
||||
[PaxosStore](https://github.com/Tencent/paxosstore) is a distributed database supporting WeChat. It uses RocksDB as its storage engine.
|
||||
|
||||
## Baidu
|
||||
[Apache Doris](http://doris.apache.org/master/en/) is a MPP analytical database engine released by Baidu. It [uses RocksDB](http://doris.apache.org/master/en/administrator-guide/operation/tablet-meta-tool.html) to manage its tablet's metadata.
|
||||
|
||||
@@ -82,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.
|
||||
|
||||
|
||||
@@ -621,7 +621,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 +644,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 +697,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
+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
+32
-32
@@ -16,7 +16,7 @@
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/statistics_impl.h"
|
||||
#include "port/lang.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/hash.h"
|
||||
@@ -130,7 +130,8 @@ HyperClockTable::HyperClockTable(
|
||||
size_t capacity, bool /*strict_capacity_limit*/,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback, const Opts& opts)
|
||||
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
|
||||
const Opts& opts)
|
||||
: length_bits_(CalcHashBits(capacity, opts.estimated_value_size,
|
||||
metadata_charge_policy)),
|
||||
length_bits_mask_((size_t{1} << length_bits_) - 1),
|
||||
@@ -138,7 +139,8 @@ HyperClockTable::HyperClockTable(
|
||||
kStrictLoadFactor)),
|
||||
array_(new HandleImpl[size_t{1} << length_bits_]),
|
||||
allocator_(allocator),
|
||||
eviction_callback_(*eviction_callback) {
|
||||
eviction_callback_(*eviction_callback),
|
||||
hash_seed_(*hash_seed) {
|
||||
if (metadata_charge_policy ==
|
||||
CacheMetadataChargePolicy::kFullChargeCacheMetadata) {
|
||||
usage_ += size_t{GetTableSize()} * sizeof(HandleImpl);
|
||||
@@ -1010,7 +1012,7 @@ inline void HyperClockTable::Evict(size_t requested_charge,
|
||||
if (eviction_callback_) {
|
||||
took_ownership =
|
||||
eviction_callback_(ClockCacheShard<HyperClockTable>::ReverseHash(
|
||||
h.GetHash(), &unhashed),
|
||||
h.GetHash(), &unhashed, hash_seed_),
|
||||
reinterpret_cast<Cache::Handle*>(&h));
|
||||
}
|
||||
if (!took_ownership) {
|
||||
@@ -1039,11 +1041,11 @@ ClockCacheShard<Table>::ClockCacheShard(
|
||||
size_t capacity, bool strict_capacity_limit,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
MemoryAllocator* allocator,
|
||||
const Cache::EvictionCallback* eviction_callback,
|
||||
const Cache::EvictionCallback* eviction_callback, const uint32_t* hash_seed,
|
||||
const typename Table::Opts& opts)
|
||||
: CacheShardBase(metadata_charge_policy),
|
||||
table_(capacity, strict_capacity_limit, metadata_charge_policy, allocator,
|
||||
eviction_callback, opts),
|
||||
eviction_callback, hash_seed, opts),
|
||||
capacity_(capacity),
|
||||
strict_capacity_limit_(strict_capacity_limit) {
|
||||
// Initial charge metadata should not exceed capacity
|
||||
@@ -1082,10 +1084,11 @@ void ClockCacheShard<Table>::ApplyToSomeEntries(
|
||||
*state = index_end << (sizeof(size_t) * 8u - length_bits);
|
||||
}
|
||||
|
||||
auto hash_seed = table_.GetHashSeed();
|
||||
table_.ConstApplyToEntriesRange(
|
||||
[callback](const HandleImpl& h) {
|
||||
[callback, hash_seed](const HandleImpl& h) {
|
||||
UniqueId64x2 unhashed;
|
||||
callback(ReverseHash(h.hashed_key, &unhashed), h.value,
|
||||
callback(ReverseHash(h.hashed_key, &unhashed, hash_seed), h.value,
|
||||
h.GetTotalCharge(), h.helper);
|
||||
},
|
||||
index_begin, index_end, false);
|
||||
@@ -1282,25 +1285,20 @@ size_t ClockCacheShard<Table>::GetTableAddressCount() const {
|
||||
// Explicit instantiation
|
||||
template class ClockCacheShard<HyperClockTable>;
|
||||
|
||||
HyperClockCache::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)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
std::move(memory_allocator)) {
|
||||
assert(estimated_value_size > 0 ||
|
||||
metadata_charge_policy != kDontChargeCacheMetadata);
|
||||
HyperClockCache::HyperClockCache(const HyperClockCacheOptions& opts)
|
||||
: ShardedCache(opts) {
|
||||
assert(opts.estimated_entry_charge > 0 ||
|
||||
opts.metadata_charge_policy != kDontChargeCacheMetadata);
|
||||
// TODO: should not need to go through two levels of pointer indirection to
|
||||
// get to table entries
|
||||
size_t per_shard = GetPerShardCapacity();
|
||||
MemoryAllocator* alloc = this->memory_allocator();
|
||||
const Cache::EvictionCallback* eviction_callback = &eviction_callback_;
|
||||
InitShards([=](Shard* cs) {
|
||||
HyperClockTable::Opts opts;
|
||||
opts.estimated_value_size = estimated_value_size;
|
||||
new (cs) Shard(per_shard, strict_capacity_limit, metadata_charge_policy,
|
||||
alloc, eviction_callback, opts);
|
||||
InitShards([&](Shard* cs) {
|
||||
HyperClockTable::Opts table_opts;
|
||||
table_opts.estimated_value_size = opts.estimated_entry_charge;
|
||||
new (cs) Shard(per_shard, opts.strict_capacity_limit,
|
||||
opts.metadata_charge_policy, alloc, &eviction_callback_,
|
||||
&hash_seed_, table_opts);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1460,21 +1458,23 @@ std::shared_ptr<Cache> NewClockCache(
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> HyperClockCacheOptions::MakeSharedCache() const {
|
||||
auto my_num_shard_bits = num_shard_bits;
|
||||
if (my_num_shard_bits >= 20) {
|
||||
// For sanitized options
|
||||
HyperClockCacheOptions opts = *this;
|
||||
if (opts.num_shard_bits >= 20) {
|
||||
return nullptr; // The cache cannot be sharded into too many fine pieces.
|
||||
}
|
||||
if (my_num_shard_bits < 0) {
|
||||
if (opts.num_shard_bits < 0) {
|
||||
// Use larger shard size to reduce risk of large entries clustering
|
||||
// or skewing individual shards.
|
||||
constexpr size_t min_shard_size = 32U * 1024U * 1024U;
|
||||
my_num_shard_bits = GetDefaultCacheShardBits(capacity, min_shard_size);
|
||||
opts.num_shard_bits =
|
||||
GetDefaultCacheShardBits(opts.capacity, min_shard_size);
|
||||
}
|
||||
std::shared_ptr<Cache> cache = std::make_shared<clock_cache::HyperClockCache>(
|
||||
capacity, estimated_entry_charge, my_num_shard_bits,
|
||||
strict_capacity_limit, metadata_charge_policy, memory_allocator);
|
||||
if (secondary_cache) {
|
||||
cache = std::make_shared<CacheWithSecondaryAdapter>(cache, secondary_cache);
|
||||
std::shared_ptr<Cache> cache =
|
||||
std::make_shared<clock_cache::HyperClockCache>(opts);
|
||||
if (opts.secondary_cache) {
|
||||
cache = std::make_shared<CacheWithSecondaryAdapter>(cache,
|
||||
opts.secondary_cache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
Vendored
+12
-9
@@ -407,7 +407,7 @@ 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,
|
||||
@@ -448,6 +448,8 @@ class HyperClockTable {
|
||||
return standalone_usage_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
uint32_t GetHashSeed() const { return hash_seed_; }
|
||||
|
||||
// Acquire/release N references
|
||||
void TEST_RefN(HandleImpl& handle, size_t n);
|
||||
void TEST_ReleaseN(HandleImpl* handle, size_t n);
|
||||
@@ -546,6 +548,9 @@ class HyperClockTable {
|
||||
// A reference to Cache::eviction_callback_
|
||||
const Cache::EvictionCallback& eviction_callback_;
|
||||
|
||||
// A reference to ShardedCacheBase::hash_seed_
|
||||
const uint32_t& hash_seed_;
|
||||
|
||||
// We partition the following members into different cache lines
|
||||
// to avoid false sharing among Lookup, Release, Erase and Insert
|
||||
// operations in ClockCacheShard.
|
||||
@@ -573,7 +578,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 +588,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);
|
||||
}
|
||||
@@ -682,10 +688,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
+6
-43
@@ -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,23 +17,8 @@
|
||||
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) {}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() { cache_.reset(); }
|
||||
|
||||
@@ -311,31 +296,9 @@ 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> 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);
|
||||
std::shared_ptr<SecondaryCache>
|
||||
CompressedSecondaryCacheOptions::MakeSharedSecondaryCache() const {
|
||||
return std::make_shared<CompressedSecondaryCache>(*this);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+3
-13
@@ -10,7 +10,7 @@
|
||||
#include <memory>
|
||||
|
||||
#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 +69,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"; }
|
||||
|
||||
+8
-5
@@ -626,8 +626,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 +679,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 +710,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};
|
||||
|
||||
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<GeneralCache> LRUCacheOptions::MakeSharedGeneralCache() const {
|
||||
if (secondary_cache) {
|
||||
// Not allowed for a GeneralCache
|
||||
return nullptr;
|
||||
}
|
||||
// Works while GeneralCache 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
+2
-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) {
|
||||
|
||||
Vendored
+1
-2
@@ -95,8 +95,7 @@ Cache::Handle* CacheWithSecondaryAdapter::Promote(
|
||||
PERF_COUNTER_ADD(secondary_cache_hit_count, 1);
|
||||
RecordTick(stats, SECONDARY_CACHE_HITS);
|
||||
|
||||
// FIXME? using charge=Size() is suspicious but inherited from older
|
||||
// implementation
|
||||
// Note: SecondaryCache::Size() is really charge (from the CreateCallback)
|
||||
size_t charge = secondary_handle->Size();
|
||||
Handle* result = nullptr;
|
||||
// Insert into primary cache, possibly as a standalone+dummy entries.
|
||||
|
||||
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
+13
-14
@@ -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);
|
||||
}
|
||||
|
||||
@@ -226,7 +225,7 @@ class ShardedCache : public ShardedCacheBase {
|
||||
return SumOverShards2(&CacheShard::GetPinnedUsage);
|
||||
}
|
||||
size_t GetOccupancyCount() const override {
|
||||
return SumOverShards2(&CacheShard::GetPinnedUsage);
|
||||
return SumOverShards2(&CacheShard::GetOccupancyCount);
|
||||
}
|
||||
size_t GetTableAddressCount() const override {
|
||||
return SumOverShards2(&CacheShard::GetTableAddressCount);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -259,6 +259,7 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// TODO: allow user CompressionOptions, including max_compressed_bytes_per_kb
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(blob_compression_type_);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
@@ -37,7 +37,7 @@ BlobFileCache::BlobFileCache(Cache* cache,
|
||||
}
|
||||
|
||||
Status BlobFileCache::GetBlobFileReader(
|
||||
uint64_t blob_file_number,
|
||||
const ReadOptions& read_options, uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader) {
|
||||
assert(blob_file_reader);
|
||||
assert(blob_file_reader->IsEmpty());
|
||||
@@ -73,7 +73,7 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
{
|
||||
assert(file_options_);
|
||||
const Status s = BlobFileReader::Create(
|
||||
*immutable_options_, *file_options_, column_family_id_,
|
||||
*immutable_options_, read_options, *file_options_, column_family_id_,
|
||||
blob_file_read_hist_, blob_file_number, io_tracer_, &reader);
|
||||
if (!s.ok()) {
|
||||
RecordTick(statistics, NO_FILE_ERRORS);
|
||||
|
||||
@@ -32,7 +32,8 @@ class BlobFileCache {
|
||||
BlobFileCache(const BlobFileCache&) = delete;
|
||||
BlobFileCache& operator=(const BlobFileCache&) = delete;
|
||||
|
||||
Status GetBlobFileReader(uint64_t blob_file_number,
|
||||
Status GetBlobFileReader(const ReadOptions& read_options,
|
||||
uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader);
|
||||
|
||||
private:
|
||||
|
||||
@@ -118,7 +118,9 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
|
||||
// First try: reader should be opened and put in cache
|
||||
CacheHandleGuard<BlobFileReader> first;
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(blob_file_number, &first));
|
||||
const ReadOptions read_options;
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
|
||||
&first));
|
||||
ASSERT_NE(first.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
@@ -126,7 +128,8 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
|
||||
// Second try: reader should be served from cache
|
||||
CacheHandleGuard<BlobFileReader> second;
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(blob_file_number, &second));
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
|
||||
&second));
|
||||
ASSERT_NE(second.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
@@ -163,19 +166,21 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
|
||||
CacheHandleGuard<BlobFileReader> first;
|
||||
CacheHandleGuard<BlobFileReader> second;
|
||||
|
||||
const ReadOptions read_options;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileCache::GetBlobFileReader:DoubleCheck", [&](void* /* arg */) {
|
||||
// Disabling sync points to prevent infinite recursion
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(blob_file_number, &second));
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options,
|
||||
blob_file_number, &second));
|
||||
ASSERT_NE(second.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(blob_file_number, &first));
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(read_options, blob_file_number,
|
||||
&first));
|
||||
ASSERT_NE(first.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
@@ -213,8 +218,10 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_IOError) {
|
||||
|
||||
CacheHandleGuard<BlobFileReader> reader;
|
||||
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(
|
||||
blob_file_cache.GetBlobFileReader(blob_file_number, &reader).IsIOError());
|
||||
blob_file_cache.GetBlobFileReader(read_options, blob_file_number, &reader)
|
||||
.IsIOError());
|
||||
ASSERT_EQ(reader.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 1);
|
||||
@@ -253,8 +260,10 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
|
||||
// strict_capacity_limit is set
|
||||
CacheHandleGuard<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(blob_file_cache.GetBlobFileReader(blob_file_number, &reader)
|
||||
.IsMemoryLimit());
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(
|
||||
blob_file_cache.GetBlobFileReader(read_options, blob_file_number, &reader)
|
||||
.IsMemoryLimit());
|
||||
ASSERT_EQ(reader.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 1);
|
||||
|
||||
+40
-21
@@ -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"
|
||||
@@ -26,9 +26,10 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status BlobFileReader::Create(
|
||||
const ImmutableOptions& immutable_options, const FileOptions& file_options,
|
||||
uint32_t column_family_id, HistogramImpl* blob_file_read_hist,
|
||||
uint64_t blob_file_number, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const ImmutableOptions& immutable_options, const ReadOptions& read_options,
|
||||
const FileOptions& file_options, uint32_t column_family_id,
|
||||
HistogramImpl* blob_file_read_hist, uint64_t blob_file_number,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader) {
|
||||
assert(blob_file_reader);
|
||||
assert(!*blob_file_reader);
|
||||
@@ -52,15 +53,17 @@ Status BlobFileReader::Create(
|
||||
CompressionType compression_type = kNoCompression;
|
||||
|
||||
{
|
||||
const Status s = ReadHeader(file_reader.get(), column_family_id, statistics,
|
||||
&compression_type);
|
||||
const Status s =
|
||||
ReadHeader(file_reader.get(), read_options, column_family_id,
|
||||
statistics, &compression_type);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Status s = ReadFooter(file_reader.get(), file_size, statistics);
|
||||
const Status s =
|
||||
ReadFooter(file_reader.get(), read_options, file_size, statistics);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -134,6 +137,7 @@ Status BlobFileReader::OpenFile(
|
||||
}
|
||||
|
||||
Status BlobFileReader::ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
const ReadOptions& read_options,
|
||||
uint32_t column_family_id,
|
||||
Statistics* statistics,
|
||||
CompressionType* compression_type) {
|
||||
@@ -151,9 +155,10 @@ Status BlobFileReader::ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
constexpr size_t read_size = BlobLogHeader::kSize;
|
||||
|
||||
// TODO: rate limit reading headers from blob files.
|
||||
const Status s = ReadFromFile(file_reader, read_offset, read_size,
|
||||
statistics, &header_slice, &buf, &aligned_buf,
|
||||
Env::IO_TOTAL /* rate_limiter_priority */);
|
||||
const Status s =
|
||||
ReadFromFile(file_reader, read_options, read_offset, read_size,
|
||||
statistics, &header_slice, &buf, &aligned_buf,
|
||||
Env::IO_TOTAL /* rate_limiter_priority */);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -187,6 +192,7 @@ Status BlobFileReader::ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
}
|
||||
|
||||
Status BlobFileReader::ReadFooter(const RandomAccessFileReader* file_reader,
|
||||
const ReadOptions& read_options,
|
||||
uint64_t file_size, Statistics* statistics) {
|
||||
assert(file_size >= BlobLogHeader::kSize + BlobLogFooter::kSize);
|
||||
assert(file_reader);
|
||||
@@ -202,9 +208,10 @@ Status BlobFileReader::ReadFooter(const RandomAccessFileReader* file_reader,
|
||||
constexpr size_t read_size = BlobLogFooter::kSize;
|
||||
|
||||
// TODO: rate limit reading footers from blob files.
|
||||
const Status s = ReadFromFile(file_reader, read_offset, read_size,
|
||||
statistics, &footer_slice, &buf, &aligned_buf,
|
||||
Env::IO_TOTAL /* rate_limiter_priority */);
|
||||
const Status s =
|
||||
ReadFromFile(file_reader, read_options, read_offset, read_size,
|
||||
statistics, &footer_slice, &buf, &aligned_buf,
|
||||
Env::IO_TOTAL /* rate_limiter_priority */);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -232,6 +239,7 @@ Status BlobFileReader::ReadFooter(const RandomAccessFileReader* file_reader,
|
||||
}
|
||||
|
||||
Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
const ReadOptions& read_options,
|
||||
uint64_t read_offset, size_t read_size,
|
||||
Statistics* statistics, Slice* slice,
|
||||
Buffer* buf, AlignedBuf* aligned_buf,
|
||||
@@ -246,17 +254,23 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
|
||||
Status s;
|
||||
|
||||
IOOptions io_options;
|
||||
s = file_reader->PrepareIOOptions(read_options, io_options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (file_reader->use_direct_io()) {
|
||||
constexpr char* scratch = nullptr;
|
||||
|
||||
s = file_reader->Read(IOOptions(), read_offset, read_size, slice, scratch,
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, scratch,
|
||||
aligned_buf, rate_limiter_priority);
|
||||
} else {
|
||||
buf->reset(new char[read_size]);
|
||||
constexpr AlignedBuf* aligned_scratch = nullptr;
|
||||
|
||||
s = file_reader->Read(IOOptions(), read_offset, read_size, slice,
|
||||
buf->get(), aligned_scratch, rate_limiter_priority);
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, buf->get(),
|
||||
aligned_scratch, rate_limiter_priority);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
@@ -324,8 +338,13 @@ Status BlobFileReader::GetBlob(
|
||||
Status s;
|
||||
constexpr bool for_compaction = true;
|
||||
|
||||
IOOptions io_options;
|
||||
s = file_reader_->PrepareIOOptions(read_options, io_options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
prefetched = prefetch_buffer->TryReadFromCache(
|
||||
IOOptions(), file_reader_.get(), record_offset,
|
||||
io_options, file_reader_.get(), record_offset,
|
||||
static_cast<size_t>(record_size), &record_slice, &s,
|
||||
read_options.rate_limiter_priority, for_compaction);
|
||||
if (!s.ok()) {
|
||||
@@ -338,10 +357,10 @@ Status BlobFileReader::GetBlob(
|
||||
PERF_COUNTER_ADD(blob_read_count, 1);
|
||||
PERF_COUNTER_ADD(blob_read_byte, record_size);
|
||||
PERF_TIMER_GUARD(blob_read_time);
|
||||
const Status s = ReadFromFile(file_reader_.get(), record_offset,
|
||||
static_cast<size_t>(record_size), statistics_,
|
||||
&record_slice, &buf, &aligned_buf,
|
||||
read_options.rate_limiter_priority);
|
||||
const Status s = ReadFromFile(
|
||||
file_reader_.get(), read_options, record_offset,
|
||||
static_cast<size_t>(record_size), statistics_, &record_slice, &buf,
|
||||
&aligned_buf, read_options.rate_limiter_priority);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ class Statistics;
|
||||
class BlobFileReader {
|
||||
public:
|
||||
static Status Create(const ImmutableOptions& immutable_options,
|
||||
const ReadOptions& read_options,
|
||||
const FileOptions& file_options,
|
||||
uint32_t column_family_id,
|
||||
HistogramImpl* blob_file_read_hist,
|
||||
@@ -74,15 +75,18 @@ class BlobFileReader {
|
||||
std::unique_ptr<RandomAccessFileReader>* file_reader);
|
||||
|
||||
static Status ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
const ReadOptions& read_options,
|
||||
uint32_t column_family_id, Statistics* statistics,
|
||||
CompressionType* compression_type);
|
||||
|
||||
static Status ReadFooter(const RandomAccessFileReader* file_reader,
|
||||
uint64_t file_size, Statistics* statistics);
|
||||
const ReadOptions& read_options, uint64_t file_size,
|
||||
Statistics* statistics);
|
||||
|
||||
using Buffer = std::unique_ptr<char[]>;
|
||||
|
||||
static Status ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
const ReadOptions& read_options,
|
||||
uint64_t read_offset, size_t read_size,
|
||||
Statistics* statistics, Slice* slice, Buffer* buf,
|
||||
AlignedBuf* aligned_buf,
|
||||
|
||||
@@ -172,12 +172,12 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ReadOptions read_options;
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
immutable_options, read_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
// Make sure the blob can be retrieved with and without checksum verification
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = false;
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
@@ -479,11 +479,11 @@ TEST_F(BlobFileReaderTest, Malformed) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, read_options,
|
||||
FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number,
|
||||
nullptr /*IOTracer*/, &reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -513,11 +513,11 @@ TEST_F(BlobFileReaderTest, TTL) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, read_options,
|
||||
FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number,
|
||||
nullptr /*IOTracer*/, &reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -552,11 +552,11 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInHeader) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, read_options,
|
||||
FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number,
|
||||
nullptr /*IOTracer*/, &reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -591,11 +591,11 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInFooter) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, read_options,
|
||||
FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number,
|
||||
nullptr /*IOTracer*/, &reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -629,9 +629,9 @@ TEST_F(BlobFileReaderTest, IncorrectColumnFamily) {
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
constexpr uint32_t incorrect_column_family_id = 2;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
incorrect_column_family_id,
|
||||
const ReadOptions read_options;
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, read_options,
|
||||
FileOptions(), incorrect_column_family_id,
|
||||
blob_file_read_hist, blob_file_number,
|
||||
nullptr /*IOTracer*/, &reader)
|
||||
.IsCorruption());
|
||||
@@ -664,10 +664,10 @@ TEST_F(BlobFileReaderTest, BlobCRCError) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
const ReadOptions read_options;
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
immutable_options, read_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::VerifyBlob:CheckBlobCRC", [](void* arg) {
|
||||
@@ -728,13 +728,12 @@ TEST_F(BlobFileReaderTest, Compression) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ReadOptions read_options;
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
immutable_options, read_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
// Make sure the blob can be retrieved with and without checksum verification
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = false;
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
@@ -803,10 +802,10 @@ TEST_F(BlobFileReaderTest, UncompressionError) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
const ReadOptions read_options;
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
immutable_options, read_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::UncompressBlobIfNeeded:TamperWithResult", [](void* arg) {
|
||||
@@ -895,10 +894,10 @@ TEST_P(BlobFileReaderIOErrorTest, IOError) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
const ReadOptions read_options;
|
||||
const Status s = BlobFileReader::Create(
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
immutable_options, read_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
|
||||
const bool fail_during_create =
|
||||
(sync_point_ != "BlobFileReader::GetBlob:ReadFromFile");
|
||||
@@ -983,10 +982,10 @@ TEST_P(BlobFileReaderDecodingErrorTest, DecodingError) {
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
const ReadOptions read_options;
|
||||
const Status s = BlobFileReader::Create(
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
immutable_options, read_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
|
||||
const bool fail_during_create =
|
||||
sync_point_ != "BlobFileReader::GetBlob:TamperWithResult";
|
||||
|
||||
@@ -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"
|
||||
@@ -209,7 +209,8 @@ Status BlobSource::GetBlob(const ReadOptions& read_options,
|
||||
|
||||
{
|
||||
CacheHandleGuard<BlobFileReader> blob_file_reader;
|
||||
s = blob_file_cache_->GetBlobFileReader(file_number, &blob_file_reader);
|
||||
s = blob_file_cache_->GetBlobFileReader(read_options, file_number,
|
||||
&blob_file_reader);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -372,8 +373,8 @@ void BlobSource::MultiGetBlobFromOneFile(const ReadOptions& read_options,
|
||||
}
|
||||
|
||||
CacheHandleGuard<BlobFileReader> blob_file_reader;
|
||||
Status s =
|
||||
blob_file_cache_->GetBlobFileReader(file_number, &blob_file_reader);
|
||||
Status s = blob_file_cache_->GetBlobFileReader(read_options, file_number,
|
||||
&blob_file_reader);
|
||||
if (!s.ok()) {
|
||||
for (size_t i = 0; i < _blob_reqs.size(); ++i) {
|
||||
BlobReadRequest* const req = _blob_reqs[i].first;
|
||||
|
||||
@@ -95,9 +95,9 @@ class BlobSource {
|
||||
uint64_t* bytes_read);
|
||||
|
||||
inline Status GetBlobFileReader(
|
||||
uint64_t blob_file_number,
|
||||
const ReadOptions& read_options, uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader) {
|
||||
return blob_file_cache_->GetBlobFileReader(blob_file_number,
|
||||
return blob_file_cache_->GetBlobFileReader(read_options, blob_file_number,
|
||||
blob_file_reader);
|
||||
}
|
||||
|
||||
|
||||
@@ -517,7 +517,8 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
|
||||
compression, blob_offsets, blob_sizes);
|
||||
|
||||
CacheHandleGuard<BlobFileReader> blob_file_reader;
|
||||
ASSERT_OK(blob_source.GetBlobFileReader(file_number, &blob_file_reader));
|
||||
ASSERT_OK(blob_source.GetBlobFileReader(read_options, file_number,
|
||||
&blob_file_reader));
|
||||
ASSERT_NE(blob_file_reader.GetValue(), nullptr);
|
||||
|
||||
const uint64_t file_size = blob_file_reader.GetValue()->GetFileSize();
|
||||
@@ -1139,12 +1140,13 @@ TEST_F(BlobSecondaryCacheTest, GetBlobsFromSecondaryCache) {
|
||||
blob_file_cache.get());
|
||||
|
||||
CacheHandleGuard<BlobFileReader> file_reader;
|
||||
ASSERT_OK(blob_source.GetBlobFileReader(file_number, &file_reader));
|
||||
ReadOptions read_options;
|
||||
ASSERT_OK(
|
||||
blob_source.GetBlobFileReader(read_options, file_number, &file_reader));
|
||||
ASSERT_NE(file_reader.GetValue(), nullptr);
|
||||
const uint64_t file_size = file_reader.GetValue()->GetFileSize();
|
||||
ASSERT_EQ(file_reader.GetValue()->GetCompressionType(), kNoCompression);
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
|
||||
auto blob_cache = options_.blob_cache;
|
||||
|
||||
+7
-6
@@ -56,8 +56,8 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
const FileOptions& file_options, TableCache* table_cache,
|
||||
InternalIterator* iter,
|
||||
const FileOptions& file_options, const ReadOptions& read_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
@@ -255,8 +255,8 @@ Status BuildTable(
|
||||
SizeApproximationOptions approx_opts;
|
||||
approx_opts.files_size_error_margin = 0.1;
|
||||
meta->compensated_range_deletion_size += versions->ApproximateSize(
|
||||
approx_opts, version, kv.first.Encode(), tombstone_end.Encode(),
|
||||
0 /* start_level */, -1 /* end_level */,
|
||||
approx_opts, read_options, version, kv.first.Encode(),
|
||||
tombstone_end.Encode(), 0 /* start_level */, -1 /* end_level */,
|
||||
TableReaderCaller::kFlush);
|
||||
}
|
||||
last_tombstone_start_user_key = range_del_it->start_key();
|
||||
@@ -291,6 +291,7 @@ 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();
|
||||
assert(meta->fd.GetFileSize() > 0);
|
||||
tp = builder
|
||||
@@ -369,7 +370,6 @@ Status BuildTable(
|
||||
// here because this is a special case after we finish the table building.
|
||||
// No matter whether use_direct_io_for_flush_and_compaction is true,
|
||||
// the goal is to cache it here for further user reads.
|
||||
ReadOptions read_options;
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
read_options, file_options, tboptions.internal_comparator, *meta,
|
||||
nullptr /* range_del_agg */, mutable_cf_options.prefix_extractor,
|
||||
@@ -381,7 +381,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,
|
||||
|
||||
+2
-2
@@ -53,8 +53,8 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
extern Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
const FileOptions& file_options, TableCache* table_cache,
|
||||
InternalIterator* iter,
|
||||
const FileOptions& file_options, const ReadOptions& read_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters,
|
||||
FileMetaData* meta, std::vector<BlobFileAddition>* blob_file_additions,
|
||||
|
||||
@@ -4064,6 +4064,8 @@ uint64_t rocksdb_perfcontext_metric(rocksdb_perfcontext_t* context,
|
||||
return rep->blob_decompress_time;
|
||||
case rocksdb_internal_range_del_reseek_count:
|
||||
return rep->internal_range_del_reseek_count;
|
||||
case rocksdb_block_read_cpu_time:
|
||||
return rep->block_read_cpu_time;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -4679,7 +4681,7 @@ rocksdb_cache_t* rocksdb_cache_create_lru_with_strict_capacity_limit(
|
||||
}
|
||||
|
||||
rocksdb_cache_t* rocksdb_cache_create_lru_opts(
|
||||
rocksdb_lru_cache_options_t* opt) {
|
||||
const rocksdb_lru_cache_options_t* opt) {
|
||||
rocksdb_cache_t* c = new rocksdb_cache_t;
|
||||
c->rep = NewLRUCache(opt->rep);
|
||||
return c;
|
||||
@@ -4726,7 +4728,7 @@ rocksdb_cache_t* rocksdb_cache_create_hyper_clock(
|
||||
}
|
||||
|
||||
rocksdb_cache_t* rocksdb_cache_create_hyper_clock_opts(
|
||||
rocksdb_hyper_clock_cache_options_t* opts) {
|
||||
const rocksdb_hyper_clock_cache_options_t* opts) {
|
||||
rocksdb_cache_t* c = new rocksdb_cache_t;
|
||||
c->rep = opts->rep.MakeSharedCache();
|
||||
return c;
|
||||
@@ -4742,18 +4744,26 @@ void rocksdb_cache_set_capacity(rocksdb_cache_t* cache, size_t capacity) {
|
||||
cache->rep->SetCapacity(capacity);
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_capacity(rocksdb_cache_t* cache) {
|
||||
size_t rocksdb_cache_get_capacity(const rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetCapacity();
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_usage(rocksdb_cache_t* cache) {
|
||||
size_t rocksdb_cache_get_usage(const rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetUsage();
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_pinned_usage(rocksdb_cache_t* cache) {
|
||||
size_t rocksdb_cache_get_pinned_usage(const rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetPinnedUsage();
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_table_address_count(const rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetTableAddressCount();
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_occupancy_count(const rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetOccupancyCount();
|
||||
}
|
||||
|
||||
rocksdb_dbpath_t* rocksdb_dbpath_create(const char* path,
|
||||
uint64_t target_size) {
|
||||
rocksdb_dbpath_t* result = new rocksdb_dbpath_t;
|
||||
@@ -5853,6 +5863,35 @@ void rocksdb_transaction_multi_get(rocksdb_transaction_t* txn,
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_transaction_multi_get_for_update(
|
||||
rocksdb_transaction_t* txn, const rocksdb_readoptions_t* options,
|
||||
size_t num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes, char** values_list,
|
||||
size_t* values_list_sizes, char** errs) {
|
||||
std::vector<Slice> keys(num_keys);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
keys[i] = Slice(keys_list[i], keys_list_sizes[i]);
|
||||
}
|
||||
std::vector<std::string> values(num_keys);
|
||||
std::vector<Status> statuses =
|
||||
txn->rep->MultiGetForUpdate(options->rep, keys, &values);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
if (statuses[i].ok()) {
|
||||
values_list[i] = CopyString(values[i]);
|
||||
values_list_sizes[i] = values[i].size();
|
||||
errs[i] = nullptr;
|
||||
} else {
|
||||
values_list[i] = nullptr;
|
||||
values_list_sizes[i] = 0;
|
||||
if (!statuses[i].IsNotFound()) {
|
||||
errs[i] = strdup(statuses[i].ToString().c_str());
|
||||
} else {
|
||||
errs[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_transaction_multi_get_cf(
|
||||
rocksdb_transaction_t* txn, const rocksdb_readoptions_t* options,
|
||||
const rocksdb_column_family_handle_t* const* column_families,
|
||||
@@ -5885,6 +5924,38 @@ void rocksdb_transaction_multi_get_cf(
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_transaction_multi_get_for_update_cf(
|
||||
rocksdb_transaction_t* txn, const rocksdb_readoptions_t* options,
|
||||
const rocksdb_column_family_handle_t* const* column_families,
|
||||
size_t num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes, char** values_list,
|
||||
size_t* values_list_sizes, char** errs) {
|
||||
std::vector<Slice> keys(num_keys);
|
||||
std::vector<ColumnFamilyHandle*> cfs(num_keys);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
keys[i] = Slice(keys_list[i], keys_list_sizes[i]);
|
||||
cfs[i] = column_families[i]->rep;
|
||||
}
|
||||
std::vector<std::string> values(num_keys);
|
||||
std::vector<Status> statuses =
|
||||
txn->rep->MultiGetForUpdate(options->rep, cfs, keys, &values);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
if (statuses[i].ok()) {
|
||||
values_list[i] = CopyString(values[i]);
|
||||
values_list_sizes[i] = values[i].size();
|
||||
errs[i] = nullptr;
|
||||
} else {
|
||||
values_list[i] = nullptr;
|
||||
values_list_sizes[i] = 0;
|
||||
if (!statuses[i].IsNotFound()) {
|
||||
errs[i] = strdup(statuses[i].ToString().c_str());
|
||||
} else {
|
||||
errs[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read a key outside a transaction
|
||||
char* rocksdb_transactiondb_get(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
|
||||
+141
@@ -488,6 +488,19 @@ static void CheckTxnPinGetCF(rocksdb_transaction_t* txn,
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
}
|
||||
|
||||
static void CheckTxnGetForUpdate(rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key, const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transaction_get_for_update(txn, options, key, strlen(key),
|
||||
&val_len, true, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnDBGet(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options, const char* key,
|
||||
const char* expected) {
|
||||
@@ -515,6 +528,20 @@ static void CheckTxnDBGetCF(rocksdb_transactiondb_t* txn_db,
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnGetForUpdateCF(
|
||||
rocksdb_transaction_t* txn, const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transaction_get_for_update_cf(
|
||||
txn, options, column_family, key, strlen(key), &val_len, true, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnDBPinGet(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
const char* key, const char* expected) {
|
||||
@@ -3204,6 +3231,120 @@ int main(int argc, char** argv) {
|
||||
rocksdb_transactiondb_options_destroy(txn_db_options);
|
||||
}
|
||||
|
||||
StartPhase("transactions_multi_get_for_update");
|
||||
{
|
||||
// open a TransactionDB
|
||||
txn_db_options = rocksdb_transactiondb_options_create();
|
||||
rocksdb_transactiondb_options_set_transaction_lock_timeout(txn_db_options,
|
||||
0);
|
||||
txn_options = rocksdb_transaction_options_create();
|
||||
rocksdb_options_set_create_if_missing(options, 1);
|
||||
txn_db = rocksdb_transactiondb_open(options, txn_db_options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hey", 3, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "bar", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// begin transactions
|
||||
txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
rocksdb_transaction_t* txn2 =
|
||||
rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
|
||||
// multi get
|
||||
{
|
||||
const char* keys[2] = {"foo", "bar"};
|
||||
const size_t keys_sizes[2] = {3, 3};
|
||||
char* vals[2];
|
||||
size_t vals_sizes[2];
|
||||
char* errs[2];
|
||||
const char* expected[2] = {"hey", "hello"};
|
||||
rocksdb_transaction_multi_get_for_update(
|
||||
txn, roptions, 2, keys, keys_sizes, vals, vals_sizes, errs);
|
||||
CheckMultiGetValues(2, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
|
||||
char* conflict_err = NULL;
|
||||
size_t val_len;
|
||||
rocksdb_transaction_get_for_update(txn2, roptions, "foo", 3, &val_len, true,
|
||||
&conflict_err);
|
||||
// get-for-update conflict
|
||||
CheckCondition(conflict_err != NULL);
|
||||
Free(&conflict_err);
|
||||
|
||||
// commit
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// should work after first tx is commited
|
||||
CheckTxnGetForUpdate(txn2, roptions, "foo", "hey");
|
||||
|
||||
// commit the second one
|
||||
rocksdb_transaction_commit(txn2, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// destroy txns
|
||||
rocksdb_transaction_destroy(txn);
|
||||
rocksdb_transaction_destroy(txn2);
|
||||
|
||||
// same for column families
|
||||
|
||||
rocksdb_column_family_handle_t* cfh;
|
||||
cfh = rocksdb_transactiondb_create_column_family(txn_db, options,
|
||||
"txn_db_cf", &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_transactiondb_put_cf(txn_db, woptions, cfh, "cf_foo", 6, "cf_hello",
|
||||
8, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transactiondb_put_cf(txn_db, woptions, cfh, "cf_bar", 6, "cf_hey",
|
||||
6, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
txn = rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
txn2 = rocksdb_transaction_begin(txn_db, woptions, txn_options, NULL);
|
||||
|
||||
{
|
||||
const rocksdb_column_family_handle_t* get_handles[2] = {cfh, cfh};
|
||||
const char* keys[2] = {"cf_foo", "cf_bar"};
|
||||
const size_t keys_sizes[2] = {6, 6};
|
||||
char* vals[2];
|
||||
size_t vals_sizes[2];
|
||||
char* errs[2];
|
||||
const char* expected[2] = {"cf_hello", "cf_hey"};
|
||||
rocksdb_transaction_multi_get_for_update_cf(txn, roptions, get_handles, 2,
|
||||
keys, keys_sizes, vals,
|
||||
vals_sizes, errs);
|
||||
CheckMultiGetValues(2, vals, vals_sizes, errs, expected);
|
||||
}
|
||||
|
||||
char* conflict_err_cf = NULL;
|
||||
size_t val_len_cf;
|
||||
rocksdb_transaction_get_for_update_cf(txn2, roptions, cfh, "cf_foo", 6,
|
||||
&val_len_cf, true, &conflict_err_cf);
|
||||
CheckCondition(conflict_err_cf != NULL);
|
||||
Free(&conflict_err_cf);
|
||||
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
CheckTxnGetForUpdateCF(txn2, roptions, cfh, "cf_foo", "cf_hello");
|
||||
|
||||
rocksdb_transaction_commit(txn2, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// close and destroy
|
||||
rocksdb_column_family_handle_destroy(cfh);
|
||||
rocksdb_transaction_destroy(txn);
|
||||
rocksdb_transaction_destroy(txn2);
|
||||
rocksdb_transactiondb_close(txn_db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_options_destroy(txn_options);
|
||||
rocksdb_transactiondb_options_destroy(txn_db_options);
|
||||
}
|
||||
|
||||
StartPhase("optimistic_transactions");
|
||||
{
|
||||
rocksdb_options_t* db_options = rocksdb_options_create();
|
||||
|
||||
@@ -1141,6 +1141,7 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
*overlap = false;
|
||||
// Create an InternalIterator over all unflushed memtables
|
||||
Arena arena;
|
||||
// TODO: plumb Env::IOActivity
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&internal_comparator_, &arena);
|
||||
@@ -1427,6 +1428,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,9 @@ TEST_F(CompactFilesTest, L0ConflictsFiles) {
|
||||
TEST_F(CompactFilesTest, MultipleLevel) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
// Otherwise background compaction can happen to
|
||||
// drain unnecessary level
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.num_levels = 6;
|
||||
// Add listener
|
||||
FlushedFileCollector* collector = new FlushedFileCollector();
|
||||
@@ -181,7 +183,6 @@ TEST_F(CompactFilesTest, MultipleLevel) {
|
||||
for (int invalid_output_level = 0; invalid_output_level < 5;
|
||||
invalid_output_level++) {
|
||||
s = db->CompactFiles(CompactionOptions(), files, invalid_output_level);
|
||||
std::cout << s.ToString() << std::endl;
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
}
|
||||
|
||||
|
||||
@@ -465,6 +465,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) &&
|
||||
|
||||
@@ -546,13 +546,16 @@ struct PerKeyPlacementContext {
|
||||
const Slice value;
|
||||
const SequenceNumber seq_num;
|
||||
|
||||
bool output_to_penultimate_level;
|
||||
bool& output_to_penultimate_level;
|
||||
|
||||
PerKeyPlacementContext(int _level, Slice _key, Slice _value,
|
||||
SequenceNumber _seq_num)
|
||||
: level(_level), key(_key), value(_value), seq_num(_seq_num) {
|
||||
output_to_penultimate_level = false;
|
||||
}
|
||||
SequenceNumber _seq_num,
|
||||
bool& _output_to_penultimate_level)
|
||||
: level(_level),
|
||||
key(_key),
|
||||
value(_value),
|
||||
seq_num(_seq_num),
|
||||
output_to_penultimate_level(_output_to_penultimate_level) {}
|
||||
};
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
|
||||
@@ -1201,17 +1201,7 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
|
||||
void CompactionIterator::DecideOutputLevel() {
|
||||
assert(compaction_->SupportsPerKeyPlacement());
|
||||
#ifndef NDEBUG
|
||||
// Could be overridden by unittest
|
||||
PerKeyPlacementContext context(level_, ikey_.user_key, value_,
|
||||
ikey_.sequence);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator::PrepareOutput.context",
|
||||
&context);
|
||||
output_to_penultimate_level_ = context.output_to_penultimate_level;
|
||||
#else
|
||||
output_to_penultimate_level_ = false;
|
||||
#endif // NDEBUG
|
||||
|
||||
// if the key is newer than the cutoff sequence or within the earliest
|
||||
// snapshot, it should output to the penultimate level.
|
||||
if (ikey_.sequence > preclude_last_level_min_seqno_ ||
|
||||
@@ -1219,6 +1209,17 @@ void CompactionIterator::DecideOutputLevel() {
|
||||
output_to_penultimate_level_ = true;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Could be overridden by unittest
|
||||
PerKeyPlacementContext context(level_, ikey_.user_key, value_, ikey_.sequence,
|
||||
output_to_penultimate_level_);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator::PrepareOutput.context",
|
||||
&context);
|
||||
if (ikey_.sequence > earliest_snapshot_) {
|
||||
output_to_penultimate_level_ = true;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
if (output_to_penultimate_level_) {
|
||||
// If it's decided to output to the penultimate level, but unsafe to do so,
|
||||
// still output to the last level. For example, moving the data from a lower
|
||||
@@ -1411,6 +1412,7 @@ std::unique_ptr<BlobFetcher> CompactionIterator::CreateBlobFetcherIfNeeded(
|
||||
}
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.io_activity = Env::IOActivity::kCompaction;
|
||||
read_options.fill_cache = false;
|
||||
|
||||
return std::unique_ptr<BlobFetcher>(new BlobFetcher(version, read_options));
|
||||
|
||||
@@ -192,8 +192,8 @@ CompactionJob::CompactionJob(
|
||||
assert(log_buffer_ != nullptr);
|
||||
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
db_options_.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetEnableTracking(db_options_.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetColumnFamily(cfd);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
|
||||
ReportStartedCompaction(compaction);
|
||||
}
|
||||
@@ -204,10 +204,6 @@ CompactionJob::~CompactionJob() {
|
||||
}
|
||||
|
||||
void CompactionJob::ReportStartedCompaction(Compaction* compaction) {
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
db_options_.enable_thread_tracking);
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(ThreadStatus::COMPACTION_JOB_ID,
|
||||
job_id_);
|
||||
|
||||
@@ -291,12 +287,14 @@ void CompactionJob::Prepare() {
|
||||
c->immutable_options()->preclude_last_level_data_seconds);
|
||||
|
||||
if (preserve_time_duration > 0) {
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
// setup seqno_time_mapping_
|
||||
seqno_time_mapping_.SetMaxTimeDuration(preserve_time_duration);
|
||||
for (const auto& each_level : *c->inputs()) {
|
||||
for (const auto& fmd : each_level.files) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
Status s = cfd->current()->GetTableProperties(&tp, fmd, nullptr);
|
||||
Status s =
|
||||
cfd->current()->GetTableProperties(read_options, &tp, fmd, nullptr);
|
||||
if (s.ok()) {
|
||||
seqno_time_mapping_.Add(tp->seqno_to_time_mapping)
|
||||
.PermitUncheckedError();
|
||||
@@ -472,7 +470,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// overlap with N-1 other ranges. Since we requested a relatively large number
|
||||
// (128) of ranges from each input files, even N range overlapping would
|
||||
// cause relatively small inaccuracy.
|
||||
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
auto* c = compact_->compaction;
|
||||
if (c->max_subcompactions() <= 1 &&
|
||||
!(c->immutable_options()->compaction_pri == kRoundRobin &&
|
||||
@@ -506,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(
|
||||
ReadOptions(), 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());
|
||||
}
|
||||
@@ -722,11 +722,12 @@ Status CompactionJob::Run() {
|
||||
// use_direct_io_for_flush_and_compaction is true, we will regard this
|
||||
// verification as user reads since the goal is to cache it here for
|
||||
// further user reads
|
||||
ReadOptions read_options;
|
||||
const ReadOptions verify_table_read_options(
|
||||
Env::IOActivity::kCompaction);
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
read_options, file_options_, cfd->internal_comparator(),
|
||||
files_output[file_idx]->meta, /*range_del_agg=*/nullptr,
|
||||
prefix_extractor,
|
||||
verify_table_read_options, file_options_,
|
||||
cfd->internal_comparator(), files_output[file_idx]->meta,
|
||||
/*range_del_agg=*/nullptr, prefix_extractor,
|
||||
/*table_reader_ptr=*/nullptr,
|
||||
cfd->internal_stats()->GetFileReadHist(
|
||||
compact_->compaction->output_level()),
|
||||
@@ -736,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_) {
|
||||
@@ -1032,7 +1035,6 @@ void CompactionJob::NotifyOnSubcompactionCompleted(
|
||||
void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact);
|
||||
assert(sub_compact->compaction);
|
||||
|
||||
if (db_options_.compaction_service) {
|
||||
CompactionServiceJobStatus comp_status =
|
||||
ProcessKeyValueCompactionWithCompactionService(sub_compact);
|
||||
@@ -1083,6 +1085,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
read_options.verify_checksums = true;
|
||||
read_options.fill_cache = false;
|
||||
read_options.rate_limiter_priority = GetRateLimiterPriority();
|
||||
read_options.io_activity = Env::IOActivity::kCompaction;
|
||||
// Compaction iterators shouldn't be confined to a single prefix.
|
||||
// Compactions use Seek() for
|
||||
// (a) concurrent compactions,
|
||||
@@ -1640,6 +1643,7 @@ Status CompactionJob::InstallCompactionResults(
|
||||
|
||||
db_mutex_->AssertHeld();
|
||||
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
auto* compaction = compact_->compaction;
|
||||
assert(compaction);
|
||||
|
||||
@@ -1717,8 +1721,8 @@ Status CompactionJob::InstallCompactionResults(
|
||||
}
|
||||
|
||||
return versions_->LogAndApply(compaction->column_family_data(),
|
||||
mutable_cf_options, edit, db_mutex_,
|
||||
db_directory_);
|
||||
mutable_cf_options, read_options, edit,
|
||||
db_mutex_, db_directory_);
|
||||
}
|
||||
|
||||
void CompactionJob::RecordCompactionIOStats() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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,12 +386,12 @@ class CompactionJobTestBase : public testing::Test {
|
||||
kUnknownFileCreationTime,
|
||||
versions_->GetColumnFamilySet()->GetDefault()->NewEpochNumber(),
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2,
|
||||
0);
|
||||
0, 0);
|
||||
|
||||
mutex_.Lock();
|
||||
EXPECT_OK(
|
||||
versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options_, &edit, &mutex_, nullptr));
|
||||
EXPECT_OK(versions_->LogAndApply(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), mutable_cf_options_,
|
||||
read_options_, &edit, &mutex_, nullptr));
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
@@ -454,7 +454,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);
|
||||
@@ -727,6 +728,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
ColumnFamilyOptions cf_options_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
MutableDBOptions mutable_db_options_;
|
||||
const ReadOptions read_options_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
WriteController write_controller_;
|
||||
WriteBufferManager write_buffer_manager_;
|
||||
@@ -2440,4 +2442,3 @@ int main(int argc, char** argv) {
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ 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();
|
||||
}
|
||||
current_output().finished = true;
|
||||
@@ -574,6 +575,7 @@ Status CompactionOutputs::AddRangeDels(
|
||||
auto it = range_del_agg_->NewIterator(lower_bound, upper_bound);
|
||||
Slice last_tombstone_start_user_key{};
|
||||
bool reached_lower_bound = false;
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
auto tombstone = it->Tombstone();
|
||||
auto kv = tombstone.Serialize();
|
||||
@@ -713,7 +715,7 @@ Status CompactionOutputs::AddRangeDels(
|
||||
approx_opts.files_size_error_margin = 0.1;
|
||||
auto approximate_covered_size =
|
||||
compaction_->input_version()->version_set()->ApproximateSize(
|
||||
approx_opts, compaction_->input_version(),
|
||||
approx_opts, read_options, compaction_->input_version(),
|
||||
tombstone_start.Encode(), tombstone_end.Encode(),
|
||||
compaction_->output_level() + 1 /* start_level */,
|
||||
-1 /* end_level */, kCompaction);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -83,7 +83,7 @@ class LevelCompactionBuilder {
|
||||
|
||||
Compaction* GetCompaction();
|
||||
|
||||
// For the specfied level, pick a file that we want to compact.
|
||||
// From `start_level_`, pick files to compact to `output_level_`.
|
||||
// Returns false if there is no file to compact.
|
||||
// If it returns true, inputs->files.size() will be exactly one for
|
||||
// all compaction priorities except round-robin. For round-robin,
|
||||
@@ -107,8 +107,9 @@ class LevelCompactionBuilder {
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
// Return true if TrivialMove is extended. `start_index` is the index of
|
||||
// the intiial file picked, which should already be in `start_level_inputs_`.
|
||||
bool TryExtendNonL0TrivialMove(int start_index);
|
||||
// the initial file picked, which should already be in `start_level_inputs_`.
|
||||
bool TryExtendNonL0TrivialMove(int start_index,
|
||||
bool only_expand_right = false);
|
||||
|
||||
// Picks a file from level_files to compact.
|
||||
// level_files is a vector of (level, file metadata) in ascending order of
|
||||
@@ -355,7 +356,8 @@ void LevelCompactionBuilder::SetupOtherFilesWithRoundRobinExpansion() {
|
||||
vstorage_->GetOverlappingInputs(output_level_, &smallest, &largest,
|
||||
&output_level_inputs.files);
|
||||
if (output_level_inputs.empty()) {
|
||||
if (TryExtendNonL0TrivialMove((int)start_index)) {
|
||||
if (TryExtendNonL0TrivialMove((int)start_index,
|
||||
true /* only_expand_right */)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -501,6 +503,16 @@ Compaction* LevelCompactionBuilder::PickCompaction() {
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
// TryPickL0TrivialMove() does not apply to the case when compacting L0 to an
|
||||
// empty output level. So L0 files is picked in PickFileToCompact() by
|
||||
// compaction score. We may still be able to do trivial move when this file
|
||||
// does not overlap with other L0s. This happens when
|
||||
// compaction_inputs_[0].size() == 1 since SetupOtherL0FilesIfNeeded() did not
|
||||
// pull in more L0s.
|
||||
assert(!compaction_inputs_.empty());
|
||||
bool l0_files_might_overlap =
|
||||
start_level_ == 0 && !is_l0_trivial_move_ &&
|
||||
(compaction_inputs_.size() > 1 || compaction_inputs_[0].size() > 1);
|
||||
auto c = new Compaction(
|
||||
vstorage_, ioptions_, mutable_cf_options_, mutable_db_options_,
|
||||
std::move(compaction_inputs_), output_level_,
|
||||
@@ -515,8 +527,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
|
||||
/* l0_files_might_overlap */ start_level_ == 0 && !is_l0_trivial_move_,
|
||||
compaction_reason_);
|
||||
l0_files_might_overlap, compaction_reason_);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
@@ -653,7 +664,8 @@ bool LevelCompactionBuilder::TryPickL0TrivialMove() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::TryExtendNonL0TrivialMove(int start_index) {
|
||||
bool LevelCompactionBuilder::TryExtendNonL0TrivialMove(int start_index,
|
||||
bool only_expand_right) {
|
||||
if (start_level_inputs_.size() == 1 &&
|
||||
(ioptions_.db_paths.empty() || ioptions_.db_paths.size() == 1) &&
|
||||
(mutable_cf_options_.compression_per_level.empty())) {
|
||||
@@ -670,6 +682,7 @@ bool LevelCompactionBuilder::TryExtendNonL0TrivialMove(int start_index) {
|
||||
size_t total_size = initial_file->fd.GetFileSize();
|
||||
CompactionInputFiles output_level_inputs;
|
||||
output_level_inputs.level = output_level_;
|
||||
// Expand towards right
|
||||
for (int i = start_index + 1;
|
||||
i < static_cast<int>(level_files.size()) &&
|
||||
start_level_inputs_.size() < kMaxMultiTrivialMove;
|
||||
@@ -702,6 +715,37 @@ bool LevelCompactionBuilder::TryExtendNonL0TrivialMove(int start_index) {
|
||||
}
|
||||
start_level_inputs_.files.push_back(next_file);
|
||||
}
|
||||
// Expand towards left
|
||||
if (!only_expand_right) {
|
||||
for (int i = start_index - 1;
|
||||
i >= 0 && start_level_inputs_.size() < kMaxMultiTrivialMove; i--) {
|
||||
FileMetaData* next_file = level_files[i];
|
||||
if (next_file->being_compacted) {
|
||||
break;
|
||||
}
|
||||
vstorage_->GetOverlappingInputs(output_level_, &(next_file->smallest),
|
||||
&(initial_file->largest),
|
||||
&output_level_inputs.files);
|
||||
if (!output_level_inputs.empty()) {
|
||||
break;
|
||||
}
|
||||
if (i > 0 && compaction_picker_->icmp()
|
||||
->user_comparator()
|
||||
->CompareWithoutTimestamp(
|
||||
next_file->smallest.user_key(),
|
||||
level_files[i - 1]->largest.user_key()) == 0) {
|
||||
// Not a clean up after adding the next file. Skip.
|
||||
break;
|
||||
}
|
||||
total_size += next_file->fd.GetFileSize();
|
||||
if (total_size > mutable_cf_options_.max_compaction_bytes) {
|
||||
break;
|
||||
}
|
||||
// keep `files` sorted in increasing order by key range
|
||||
start_level_inputs_.files.insert(start_level_inputs_.files.begin(),
|
||||
next_file);
|
||||
}
|
||||
}
|
||||
return start_level_inputs_.size() > 1;
|
||||
}
|
||||
return false;
|
||||
@@ -785,7 +829,10 @@ bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
vstorage_->GetOverlappingInputs(output_level_, &smallest, &largest,
|
||||
&output_level_inputs.files);
|
||||
if (output_level_inputs.empty()) {
|
||||
if (TryExtendNonL0TrivialMove(index)) {
|
||||
if (start_level_ > 0 &&
|
||||
TryExtendNonL0TrivialMove(index,
|
||||
ioptions_.compaction_pri ==
|
||||
kRoundRobin /* only_expand_right */)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -148,7 +148,7 @@ 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);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->oldest_ancester_time = oldest_ancestor_time;
|
||||
@@ -1005,29 +1005,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 +1036,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 +1077,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 +1124,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 +1170,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 +1203,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);
|
||||
@@ -2520,6 +2551,61 @@ TEST_F(CompactionPickerTest, L0TrivialMoveWholeL0) {
|
||||
ASSERT_TRUE(compaction->IsTrivialMove());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NonL0TrivialMoveExtendBothDirection) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 5000;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
|
||||
mutable_cf_options_.max_compaction_bytes = 10000000u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
|
||||
Add(1, 1U, "300", "350", 3000U, 0, 710, 800, 3000U);
|
||||
Add(1, 2U, "600", "651", 3001U, 0, 610, 700, 3001U);
|
||||
Add(1, 3U, "700", "750", 3000U, 0, 500, 550, 3000U);
|
||||
Add(2, 4U, "800", "850", 4000U, 0, 150, 200, 4000U);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
// File #2 should be picked first, and expand both directions to include
|
||||
// files #1 and #3.
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1, compaction->num_input_levels());
|
||||
ASSERT_EQ(3, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3, compaction->input(0, 2)->fd.GetNumber());
|
||||
ASSERT_TRUE(compaction->IsTrivialMove());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, L0TrivialMoveToEmptyLevel) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 5000;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
|
||||
mutable_cf_options_.max_compaction_bytes = 10000000u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
|
||||
// File 2 will be picked first, which by itself is trivial movable.
|
||||
// There was a bug before where compaction also picks file 3 and 4,
|
||||
// (and then file 1 since it overlaps with the key range),
|
||||
// which makes the compaction not trivial movable.
|
||||
Add(0, 1U, "450", "599", 3000U, 0, 710, 800, 3000U);
|
||||
Add(0, 2U, "600", "651", 3001U, 0, 610, 700, 3001U);
|
||||
Add(0, 3U, "300", "350", 3000U, 0, 500, 550, 3000U);
|
||||
Add(0, 4U, "500", "550", 2999U, 0, 300, 350, 2999U);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1, compaction->num_input_levels());
|
||||
ASSERT_EQ(1, compaction->num_input_files(0));
|
||||
ASSERT_EQ(2, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_TRUE(compaction->IsTrivialMove());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IsTrivialMoveOffSstPartitioned) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 10001u;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()->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()->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()->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()->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()->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()->WaitForCompact());
|
||||
|
||||
latest_cold_seq = seq_history[0];
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
@@ -1111,6 +1111,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
options.num_levels = kNumLevels;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.max_subcompactions = 10;
|
||||
options.preclude_last_level_data_seconds = 10000;
|
||||
DestroyAndReopen(options);
|
||||
auto cmp = options.comparator;
|
||||
|
||||
@@ -1134,7 +1135,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -1264,7 +1265,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeManualCompaction) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1326,7 +1327,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1359,7 +1360,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
});
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
}
|
||||
|
||||
// all data is moved up to the penultimate level
|
||||
@@ -1402,7 +1403,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimePartial) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1529,7 +1530,7 @@ TEST_F(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1608,7 +1609,7 @@ TEST_P(PrecludeLastLevelTestWithParms, LastLevelOnlyCompactionNoPreclude) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1704,7 +1705,7 @@ TEST_P(PrecludeLastLevelTestWithParms, LastLevelOnlyCompactionNoPreclude) {
|
||||
|
||||
manual_compaction_thread.join();
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
|
||||
if (enable_preclude_last_level) {
|
||||
ASSERT_NE("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
@@ -1840,7 +1841,7 @@ TEST_P(PrecludeLastLevelTestWithParms, PeriodicCompactionToPenultimateLevel) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
|
||||
stop_token.reset();
|
||||
|
||||
@@ -1939,7 +1940,7 @@ TEST_F(PrecludeLastLevelTest, PartialPenultimateLevelCompaction) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
|
||||
// L5: [0,19] [20,39] [40,299]
|
||||
// L6: [0, 299]
|
||||
@@ -2105,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()->WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,3,3", FilesPerLevel());
|
||||
ASSERT_EQ(1, per_key_comp_num);
|
||||
verify_db();
|
||||
@@ -2115,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()->WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,3,3", FilesPerLevel());
|
||||
ASSERT_EQ(2, per_key_comp_num);
|
||||
verify_db();
|
||||
@@ -2125,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()->WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,2,3", FilesPerLevel());
|
||||
ASSERT_EQ(3, per_key_comp_num);
|
||||
verify_db();
|
||||
|
||||
+8
-6
@@ -33,7 +33,9 @@ Status DeleteFilesInRanges(DB* db, ColumnFamilyHandle* column_family,
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
const EnvOptions& env_options,
|
||||
const std::string& file_path) {
|
||||
return VerifySstFileChecksum(options, env_options, ReadOptions(), file_path);
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
return VerifySstFileChecksum(options, env_options, read_options, file_path);
|
||||
}
|
||||
Status VerifySstFileChecksum(const Options& options,
|
||||
const EnvOptions& env_options,
|
||||
@@ -56,13 +58,14 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(
|
||||
std::move(file), file_path, ioptions.clock, nullptr /* io_tracer */,
|
||||
nullptr /* stats */, 0 /* hist_type */, nullptr /* file_read_hist */,
|
||||
ioptions.rate_limiter.get()));
|
||||
ioptions.stats /* stats */,
|
||||
Histograms::SST_READ_MICROS /* hist_type */,
|
||||
nullptr /* file_read_hist */, ioptions.rate_limiter.get()));
|
||||
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,
|
||||
@@ -76,4 +79,3 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -762,9 +762,11 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
fs->GetFileSize(filename, file_opts.io_options, &file_size, nullptr));
|
||||
|
||||
BlockHandle range_del_handle;
|
||||
ASSERT_OK(FindMetaBlockInFile(
|
||||
file_reader.get(), file_size, kBlockBasedTableMagicNumber,
|
||||
ImmutableOptions(options_), kRangeDelBlockName, &range_del_handle));
|
||||
const ReadOptions read_options;
|
||||
ASSERT_OK(FindMetaBlockInFile(file_reader.get(), file_size,
|
||||
kBlockBasedTableMagicNumber,
|
||||
ImmutableOptions(options_), read_options,
|
||||
kRangeDelBlockName, &range_del_handle));
|
||||
|
||||
ASSERT_OK(TryReopen());
|
||||
ASSERT_OK(test::CorruptFile(env_.get(), filename,
|
||||
@@ -1666,4 +1668,3 @@ int main(int argc, char** argv) {
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
+260
-220
@@ -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;
|
||||
@@ -2437,9 +2447,11 @@ TEST_F(DBBloomFilterTest, PrefixScan) {
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
const int kNumKeysPerFlush = 1000;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 64 * 1024;
|
||||
options.arena_block_size = 4 * 1024;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysPerFlush));
|
||||
options.target_file_size_base = 64 * 1024;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
@@ -2475,8 +2487,13 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
int num_inserted = 0;
|
||||
for (int key : keys) {
|
||||
ASSERT_OK(Put(1, Key(key), "val"));
|
||||
if (++num_inserted % 1000 == 0) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
num_inserted++;
|
||||
// The write after each `kNumKeysPerFlush` keys triggers a flush. Always
|
||||
// wait for that flush and any follow-on compactions for deterministic LSM
|
||||
// shape.
|
||||
if (num_inserted > kNumKeysPerFlush &&
|
||||
num_inserted % kNumKeysPerFlush == 1) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
}
|
||||
@@ -2629,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;
|
||||
@@ -2668,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");
|
||||
@@ -2676,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(),
|
||||
@@ -2691,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
|
||||
@@ -2704,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
|
||||
@@ -2717,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
|
||||
@@ -2730,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"}}));
|
||||
{
|
||||
@@ -2743,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));
|
||||
@@ -2756,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));
|
||||
@@ -2769,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(
|
||||
@@ -2785,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();
|
||||
}
|
||||
@@ -2821,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(),
|
||||
@@ -2829,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"));
|
||||
@@ -2844,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"}}));
|
||||
@@ -2867,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(),
|
||||
@@ -2903,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?
|
||||
}
|
||||
@@ -3009,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(),
|
||||
@@ -3024,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3129,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
|
||||
|
||||
@@ -3179,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");
|
||||
@@ -3207,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3253,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
|
||||
@@ -3267,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
|
||||
@@ -3275,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
|
||||
@@ -3306,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3397,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
|
||||
@@ -3413,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
|
||||
@@ -3442,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
|
||||
@@ -3450,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();
|
||||
}
|
||||
@@ -742,7 +742,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextCfId) {
|
||||
ASSERT_TRUE(filter->compaction_filter_created());
|
||||
}
|
||||
|
||||
// Compaction filters aplies to all records, regardless snapshots.
|
||||
// Compaction filters applies to all records, regardless snapshots.
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
std::string five = std::to_string(5);
|
||||
Options options = CurrentOptions();
|
||||
|
||||
+333
-27
@@ -136,11 +136,12 @@ class DBCompactionTestWithParam
|
||||
|
||||
class DBCompactionTestWithBottommostParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<BottommostLevelCompaction> {
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<BottommostLevelCompaction, bool>> {
|
||||
public:
|
||||
DBCompactionTestWithBottommostParam()
|
||||
: DBTestBase("db_compaction_test", /*env_do_fsync=*/true) {
|
||||
bottommost_level_compaction_ = GetParam();
|
||||
bottommost_level_compaction_ = std::get<0>(GetParam());
|
||||
}
|
||||
|
||||
BottommostLevelCompaction bottommost_level_compaction_;
|
||||
@@ -1104,7 +1105,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);
|
||||
@@ -3613,7 +3614,10 @@ TEST_F(DBCompactionTest, CancelCompactionWaitingOnConflict) {
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumSortedRuns; ++i) {
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx, true /* nowait */);
|
||||
// We hold the compaction from happening, so when generating the last SST
|
||||
// file, we cannot wait. Otherwise, we'll hit a deadlock.
|
||||
GenerateNewFile(&rnd, &key_idx,
|
||||
(i == kNumSortedRuns - 1) ? true : false /* nowait */);
|
||||
}
|
||||
auto_compaction_sleeping_task.WaitUntilSleeping();
|
||||
|
||||
@@ -5071,7 +5075,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();
|
||||
}
|
||||
}
|
||||
@@ -5506,8 +5514,9 @@ TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
|
||||
for (int n = 0; n < options.level0_file_num_compaction_trigger; n++) {
|
||||
for (unsigned int cf = 0; cf < cf_count; cf++) {
|
||||
// All L0s should overlap with each other
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(cf, Key(keyIndex++), ""));
|
||||
ASSERT_OK(Put(cf, Key(i), ""));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put(cf, "", ""));
|
||||
@@ -6735,10 +6744,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;
|
||||
}
|
||||
@@ -7335,10 +7342,63 @@ TEST_P(DBCompactionTestL0FilesMisorderCorruptionWithParam,
|
||||
Destroy(options_);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, SingleLevelUniveresal) {
|
||||
// Tests that manual compaction works with single level universal compaction.
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 1;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(31);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
for (int j = 0; j < 50; ++j) {
|
||||
ASSERT_OK(Put(Key(i * 100 + j), rnd.RandomString(50)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, SingleOverlappingNonL0BottommostManualCompaction) {
|
||||
// Tests that manual compact will rewrite bottommost level
|
||||
// when there is only a single non-L0 level that overlaps with
|
||||
// manual compaction range.
|
||||
constexpr int kSstNum = 10;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
for (auto b : {BottommostLevelCompaction::kForce,
|
||||
BottommostLevelCompaction::kForceOptimized}) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Generate some sst files on level 0 with sequence keys (no overlap)
|
||||
for (int i = 0; i < kSstNum; i++) {
|
||||
for (int j = 1; j < UCHAR_MAX; j++) {
|
||||
auto key = std::string(kSstNum, '\0');
|
||||
key[kSstNum - i] += static_cast<char>(j);
|
||||
ASSERT_OK(Put(key, std::string(i % 1000, 'A')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(4);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(4), kSstNum);
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = b;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(4), 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithBottommostParam, SequenceKeysManualCompaction) {
|
||||
constexpr int kSstNum = 10;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
const bool dynamic_level = std::get<1>(GetParam());
|
||||
options.level_compaction_dynamic_level_bytes = dynamic_level;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Generate some sst files on level 0 with sequence keys (no overlap)
|
||||
@@ -7356,25 +7416,42 @@ TEST_P(DBCompactionTestWithBottommostParam, SequenceKeysManualCompaction) {
|
||||
|
||||
auto cro = CompactRangeOptions();
|
||||
cro.bottommost_level_compaction = bottommost_level_compaction_;
|
||||
bool trivial_moved = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TrivialMove",
|
||||
[&](void* /*arg*/) { trivial_moved = true; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
// All bottommost_level_compaction options should allow l0 -> l1 trivial move.
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_TRUE(trivial_moved);
|
||||
if (bottommost_level_compaction_ == BottommostLevelCompaction::kForce ||
|
||||
bottommost_level_compaction_ ==
|
||||
BottommostLevelCompaction::kForceOptimized) {
|
||||
// Real compaction to compact all sst files from level 0 to 1 file on level
|
||||
// 1
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
// bottommost level should go through intra-level compaction
|
||||
// and has only 1 file
|
||||
if (dynamic_level) {
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel(0));
|
||||
} else {
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
}
|
||||
} else {
|
||||
// Just trivial move from level 0 -> 1
|
||||
ASSERT_EQ("0," + std::to_string(kSstNum), FilesPerLevel(0));
|
||||
// Just trivial move from level 0 -> 1/base
|
||||
if (dynamic_level) {
|
||||
ASSERT_EQ("0,0,0,0,0,0," + std::to_string(kSstNum), FilesPerLevel(0));
|
||||
} else {
|
||||
ASSERT_EQ("0," + std::to_string(kSstNum), FilesPerLevel(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBCompactionTestWithBottommostParam, DBCompactionTestWithBottommostParam,
|
||||
::testing::Values(BottommostLevelCompaction::kSkip,
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter,
|
||||
BottommostLevelCompaction::kForce,
|
||||
BottommostLevelCompaction::kForceOptimized));
|
||||
::testing::Combine(
|
||||
::testing::Values(BottommostLevelCompaction::kSkip,
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter,
|
||||
BottommostLevelCompaction::kForce,
|
||||
BottommostLevelCompaction::kForceOptimized),
|
||||
::testing::Bool()));
|
||||
|
||||
TEST_F(DBCompactionTest, UpdateLevelSubCompactionTest) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -8518,7 +8595,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;
|
||||
@@ -8526,18 +8603,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();
|
||||
@@ -8574,9 +8651,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);
|
||||
}
|
||||
@@ -8641,7 +8718,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) {
|
||||
@@ -8773,7 +8850,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));
|
||||
}
|
||||
|
||||
@@ -9108,6 +9185,235 @@ TEST_F(DBCompactionTest, BottommostFileCompactionAllowIngestBehind) {
|
||||
// ASSERT_OK(dbfull()->TEST_WaitForCompact(true /* wait_unscheduled */));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, TurnOnLevelCompactionDynamicLevelBytes) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.allow_ingest_behind = false;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.num_levels = 6;
|
||||
options.compression = kNoCompression;
|
||||
options.max_bytes_for_level_base = 1 << 20;
|
||||
options.max_bytes_for_level_multiplier = 10;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// put files in L0, L1 and L2
|
||||
WriteOptions write_opts;
|
||||
ASSERT_OK(db_->Put(write_opts, Key(1), "val1"));
|
||||
Random rnd(33);
|
||||
// Fill L2 with size larger than max_bytes_for_level_base,
|
||||
// so the level above it won't be drained.
|
||||
for (int i = 2; i <= (1 << 10); ++i) {
|
||||
ASSERT_OK(db_->Put(write_opts, Key(i), rnd.RandomString(2 << 10)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
ASSERT_OK(db_->Put(write_opts, Key(2), "val2"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
ASSERT_OK(db_->Put(write_opts, Key(1), "new_val1"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_OK(db_->Put(write_opts, Key(3), "val3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("1,1,2", FilesPerLevel());
|
||||
auto verify_db = [&]() {
|
||||
ASSERT_EQ(Get(Key(1)), "new_val1");
|
||||
ASSERT_EQ(Get(Key(2)), "val2");
|
||||
ASSERT_EQ(Get(Key(3)), "val3");
|
||||
};
|
||||
verify_db();
|
||||
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
Reopen(options);
|
||||
// except for L0, files should be pushed down as much as possible
|
||||
ASSERT_EQ("1,0,0,0,1,2", FilesPerLevel());
|
||||
verify_db();
|
||||
|
||||
// turning the options on and off should be safe
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
Reopen(options);
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ("0,1,0,0,1,2", FilesPerLevel());
|
||||
verify_db();
|
||||
|
||||
// newly flushed file is also pushed down
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
Reopen(options);
|
||||
// Files in L1 should be trivially moved down during DB opening.
|
||||
// The file should be moved to L3, and then may be drained and compacted to
|
||||
// L4. So we just check L1 and L2 here.
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(2));
|
||||
verify_db();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, TurnOnLevelCompactionDynamicLevelBytesUCToLC) {
|
||||
// Basic test for migrating from UC to LC.
|
||||
// DB has non-empty L1 that should be pushed down to last level (L49).
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
|
||||
options.allow_ingest_behind = false;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.num_levels = 50;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
Random rnd(33);
|
||||
for (int f = 0; f < 10; ++f) {
|
||||
ASSERT_OK(Put(1, Key(f), rnd.RandomString(1000)));
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = 1;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
|
||||
ASSERT_EQ("0,1", FilesPerLevel(1));
|
||||
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
std::string expected_lsm = "";
|
||||
for (int i = 0; i < 49; ++i) {
|
||||
expected_lsm += "0,";
|
||||
}
|
||||
expected_lsm += "1";
|
||||
ASSERT_EQ(expected_lsm, FilesPerLevel(1));
|
||||
|
||||
// Tests that entries for trial move in MANIFEST should be valid
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ(expected_lsm, FilesPerLevel(1));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DrainUnnecessaryLevelsAfterMultiplierChanged) {
|
||||
// When the level size multiplier increases such that fewer levels become
|
||||
// necessary, unnecessary levels should to be drained.
|
||||
const int kBaseLevelBytes = 256 << 10; // 256KB
|
||||
const int kFileBytes = 64 << 10; // 64KB
|
||||
const int kInitMultiplier = 2, kChangedMultiplier = 10;
|
||||
const int kNumFiles = 32;
|
||||
const int kNumLevels = 5;
|
||||
const int kValueBytes = 1 << 10; // 1KB
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.max_bytes_for_level_base = kBaseLevelBytes;
|
||||
options.max_bytes_for_level_multiplier = kInitMultiplier;
|
||||
options.num_levels = kNumLevels;
|
||||
Reopen(options);
|
||||
|
||||
// Initially we setup the LSM to look roughly as follows:
|
||||
//
|
||||
// L0: empty
|
||||
// L1: 256KB
|
||||
// ...
|
||||
// L4: 1MB
|
||||
Random rnd(301);
|
||||
for (int file = 0; file < kNumFiles; ++file) {
|
||||
for (int i = 0; i < kFileBytes / kValueBytes; ++i) {
|
||||
ASSERT_OK(Put(Key(file * kFileBytes / kValueBytes + i),
|
||||
rnd.RandomString(kValueBytes)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
int init_num_nonempty = 0;
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
for (int level = 1; level < kNumLevels; ++level) {
|
||||
if (NumTableFilesAtLevel(level) > 0) {
|
||||
++init_num_nonempty;
|
||||
}
|
||||
}
|
||||
|
||||
// After increasing the multiplier and running compaction fewer levels are
|
||||
// needed to hold all the data. Unnecessary levels should be drained.
|
||||
ASSERT_OK(db_->SetOptions({{"max_bytes_for_level_multiplier",
|
||||
std::to_string(kChangedMultiplier)}}));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
int final_num_nonempty = 0;
|
||||
for (int level = 1; level < kNumLevels; ++level) {
|
||||
if (NumTableFilesAtLevel(level) > 0) {
|
||||
++final_num_nonempty;
|
||||
}
|
||||
}
|
||||
ASSERT_GT(init_num_nonempty, final_num_nonempty);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DrainUnnecessaryLevelsAfterDBBecomesSmall) {
|
||||
// When the DB size is smaller, e.g., large chunk of data deleted by
|
||||
// DeleteRange(), unnecessary levels should to be drained.
|
||||
const int kBaseLevelBytes = 256 << 10; // 256KB
|
||||
const int kFileBytes = 64 << 10; // 64KB
|
||||
const int kMultiplier = 2;
|
||||
const int kNumFiles = 32;
|
||||
const int kNumLevels = 5;
|
||||
const int kValueBytes = 1 << 10; // 1KB
|
||||
const int kDeleteFileNum = 8;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.max_bytes_for_level_base = kBaseLevelBytes;
|
||||
options.max_bytes_for_level_multiplier = kMultiplier;
|
||||
options.num_levels = kNumLevels;
|
||||
Reopen(options);
|
||||
|
||||
// Initially we setup the LSM to look roughly as follows:
|
||||
//
|
||||
// L0: empty
|
||||
// L1: 256KB
|
||||
// ...
|
||||
// L4: 1MB
|
||||
Random rnd(301);
|
||||
for (int file = 0; file < kNumFiles; ++file) {
|
||||
for (int i = 0; i < kFileBytes / kValueBytes; ++i) {
|
||||
ASSERT_OK(Put(Key(file * kFileBytes / kValueBytes + i),
|
||||
rnd.RandomString(kValueBytes)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
if (file == kDeleteFileNum) {
|
||||
// Ensure the DeleteRange() call below only delete data from last level
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(kNumLevels - 1), kDeleteFileNum + 1);
|
||||
}
|
||||
}
|
||||
|
||||
int init_num_nonempty = 0;
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
for (int level = 1; level < kNumLevels; ++level) {
|
||||
if (NumTableFilesAtLevel(level) > 0) {
|
||||
++init_num_nonempty;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable auto compaction CompactRange() below
|
||||
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "true"}}));
|
||||
// Delete keys within first (kDeleteFileNum + 1) files' key ranges.
|
||||
// This should reduce DB size enough such that there is now
|
||||
// an unneeded level.
|
||||
std::string begin = Key(0);
|
||||
std::string end = Key(kDeleteFileNum * kFileBytes / kValueBytes);
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), begin, end));
|
||||
Slice begin_slice = begin;
|
||||
Slice end_slice = end;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &begin_slice, &end_slice));
|
||||
int after_delete_range_nonempty = 0;
|
||||
for (int level = 1; level < kNumLevels; ++level) {
|
||||
if (NumTableFilesAtLevel(level) > 0) {
|
||||
++after_delete_range_nonempty;
|
||||
}
|
||||
}
|
||||
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
int final_num_nonempty = 0;
|
||||
for (int level = 1; level < kNumLevels; ++level) {
|
||||
if (NumTableFilesAtLevel(level) > 0) {
|
||||
++final_num_nonempty;
|
||||
}
|
||||
}
|
||||
ASSERT_GE(init_num_nonempty, after_delete_range_nonempty);
|
||||
ASSERT_GT(after_delete_range_nonempty, final_num_nonempty);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
@@ -46,6 +46,11 @@ Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
Status CompactedDBImpl::Get(const ReadOptions& options, ColumnFamilyHandle*,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
std::string* timestamp) {
|
||||
if (options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call Get with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
assert(user_comparator_);
|
||||
if (options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(
|
||||
|
||||
@@ -128,6 +128,13 @@ class CompactedDBImpl : public DBImpl {
|
||||
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?
|
||||
|
||||
|
||||
+189
-27
@@ -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());
|
||||
@@ -327,6 +330,9 @@ Status DBImpl::Resume() {
|
||||
// means a new super version wouldn't have been installed
|
||||
Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
WaitForBackgroundWork();
|
||||
|
||||
Status s;
|
||||
@@ -368,7 +374,7 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
assert(cfh);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
const MutableCFOptions& cf_opts = *cfd->GetLatestMutableCFOptions();
|
||||
s = versions_->LogAndApply(cfd, cf_opts, &edit, &mutex_,
|
||||
s = versions_->LogAndApply(cfd, cf_opts, read_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (!s.ok()) {
|
||||
io_s = versions_->io_status();
|
||||
@@ -1147,6 +1153,8 @@ FSDirectory* DBImpl::GetDataDir(ColumnFamilyData* cfd, size_t path_id) const {
|
||||
Status DBImpl::SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
auto* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(column_family)->cfd();
|
||||
if (options_map.empty()) {
|
||||
@@ -1168,8 +1176,8 @@ Status DBImpl::SetOptions(
|
||||
new_options = *cfd->GetLatestMutableCFOptions();
|
||||
// Append new version to recompute compaction score.
|
||||
VersionEdit dummy_edit;
|
||||
s = versions_->LogAndApply(cfd, new_options, &dummy_edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
s = versions_->LogAndApply(cfd, new_options, read_options, &dummy_edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
// Trigger possible flush/compactions. This has to be before we persist
|
||||
// options to file, otherwise there will be a deadlock with writer
|
||||
// thread.
|
||||
@@ -1507,7 +1515,9 @@ Status DBImpl::SyncWAL() {
|
||||
}
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
status = ApplyWALToManifest(&synced_wals);
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
status = ApplyWALToManifest(read_options, &synced_wals);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
|
||||
@@ -1515,11 +1525,13 @@ Status DBImpl::SyncWAL() {
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::ApplyWALToManifest(VersionEdit* synced_wals) {
|
||||
Status DBImpl::ApplyWALToManifest(const ReadOptions& read_options,
|
||||
VersionEdit* synced_wals) {
|
||||
// not empty, write to MANIFEST.
|
||||
mutex_.AssertHeld();
|
||||
|
||||
Status status = versions_->LogAndApplyToDefaultColumnFamily(
|
||||
synced_wals, &mutex_, directories_.GetDbDir());
|
||||
read_options, synced_wals, &mutex_, directories_.GetDbDir());
|
||||
if (!status.ok() && versions_->io_status().IsIOError()) {
|
||||
status = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
@@ -1936,6 +1948,12 @@ Status DBImpl::GetEntity(const ReadOptions& read_options,
|
||||
"Cannot call GetEntity without a PinnableWideColumns object");
|
||||
}
|
||||
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call GetEntity with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
|
||||
columns->Reset();
|
||||
|
||||
GetImplOptions get_impl_options;
|
||||
@@ -1981,6 +1999,12 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
|
||||
assert(get_impl_options.column_family);
|
||||
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call Get with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
|
||||
if (read_options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(get_impl_options.column_family,
|
||||
*(read_options.timestamp),
|
||||
@@ -2454,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;
|
||||
@@ -2931,6 +2954,11 @@ Status DBImpl::MultiGetImpl(
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys,
|
||||
SuperVersion* super_version, SequenceNumber snapshot,
|
||||
ReadCallback* callback) {
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call MultiGet with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_MULTIGET);
|
||||
|
||||
@@ -3129,6 +3157,8 @@ Status DBImpl::CreateColumnFamilies(
|
||||
Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family_name,
|
||||
ColumnFamilyHandle** handle) {
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
Status s;
|
||||
*handle = nullptr;
|
||||
|
||||
@@ -3169,9 +3199,9 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
// LogAndApply will both write the creation in MANIFEST and create
|
||||
// ColumnFamilyData object
|
||||
s = versions_->LogAndApply(nullptr, MutableCFOptions(cf_options), &edit,
|
||||
&mutex_, directories_.GetDbDir(), false,
|
||||
&cf_options);
|
||||
s = versions_->LogAndApply(nullptr, MutableCFOptions(cf_options),
|
||||
read_options, &edit, &mutex_,
|
||||
directories_.GetDbDir(), false, &cf_options);
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
if (s.ok()) {
|
||||
@@ -3250,6 +3280,8 @@ Status DBImpl::DropColumnFamilies(
|
||||
}
|
||||
|
||||
Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
if (cfd->GetID() == 0) {
|
||||
@@ -3272,8 +3304,9 @@ Status DBImpl::DropColumnFamilyImpl(ColumnFamilyHandle* column_family) {
|
||||
// we drop column family from a single write thread
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(), &edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
read_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
if (s.ok()) {
|
||||
@@ -3324,10 +3357,13 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
|
||||
std::string* value, std::string* timestamp,
|
||||
bool* value_found) {
|
||||
assert(value != nullptr);
|
||||
assert(read_options.io_activity == Env::IOActivity::kUnknown);
|
||||
|
||||
if (value_found != nullptr) {
|
||||
// falsify later if key-may-exist but can't fetch value
|
||||
*value_found = true;
|
||||
}
|
||||
// TODO: plumb Env::IOActivity
|
||||
ReadOptions roptions = read_options;
|
||||
roptions.read_tier = kBlockCacheTier; // read from block cache only
|
||||
PinnableSlice pinnable_val;
|
||||
@@ -3356,6 +3392,11 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
return NewErrorIterator(Status::NotSupported(
|
||||
"ReadTier::kPersistedData is not yet supported in iterators."));
|
||||
}
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return NewErrorIterator(Status::InvalidArgument(
|
||||
"Cannot call NewIterator with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`"));
|
||||
}
|
||||
|
||||
assert(column_family);
|
||||
|
||||
@@ -3491,6 +3532,11 @@ Status DBImpl::NewIterators(
|
||||
return Status::NotSupported(
|
||||
"ReadTier::kPersistedData is not yet supported in iterators.");
|
||||
}
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call NewIterators with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
|
||||
if (read_options.timestamp) {
|
||||
for (auto* cf : column_families) {
|
||||
@@ -3808,7 +3854,9 @@ Status DBImpl::GetPropertiesOfAllTables(ColumnFamilyHandle* column_family,
|
||||
version->Ref();
|
||||
mutex_.Unlock();
|
||||
|
||||
auto s = version->GetPropertiesOfAllTables(props);
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
auto s = version->GetPropertiesOfAllTables(read_options, props);
|
||||
|
||||
// Decrement the ref count
|
||||
mutex_.Lock();
|
||||
@@ -3830,7 +3878,9 @@ Status DBImpl::GetPropertiesOfTablesInRange(ColumnFamilyHandle* column_family,
|
||||
version->Ref();
|
||||
mutex_.Unlock();
|
||||
|
||||
auto s = version->GetPropertiesOfTablesInRange(range, n, props);
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
auto s = version->GetPropertiesOfTablesInRange(read_options, range, n, props);
|
||||
|
||||
// Decrement the ref count
|
||||
mutex_.Lock();
|
||||
@@ -4163,6 +4213,8 @@ Status DBImpl::GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
SuperVersion* sv = GetAndRefSuperVersion(cfd);
|
||||
v = sv->current;
|
||||
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
for (int i = 0; i < n; i++) {
|
||||
Slice start = range[i].start;
|
||||
Slice limit = range[i].limit;
|
||||
@@ -4184,7 +4236,7 @@ Status DBImpl::GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
sizes[i] = 0;
|
||||
if (options.include_files) {
|
||||
sizes[i] += versions_->ApproximateSize(
|
||||
options, v, k1.Encode(), k2.Encode(), /*start_level=*/0,
|
||||
options, read_options, v, k1.Encode(), k2.Encode(), /*start_level=*/0,
|
||||
/*end_level=*/-1, TableReaderCaller::kUserApproximateSize);
|
||||
}
|
||||
if (options.include_memtables) {
|
||||
@@ -4232,6 +4284,8 @@ Status DBImpl::GetUpdatesSince(
|
||||
}
|
||||
|
||||
Status DBImpl::DeleteFile(std::string name) {
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
WalFileType log_type;
|
||||
@@ -4311,7 +4365,8 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.DeleteFile(level, number);
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
read_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
@@ -4333,6 +4388,8 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end) {
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
Status status = Status::OK();
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
@@ -4398,7 +4455,8 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
}
|
||||
input_version->Ref();
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
read_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
@@ -4464,7 +4522,6 @@ void DBImpl::GetAllColumnFamilyMetaData(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Status DBImpl::CheckConsistency() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
@@ -4855,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();
|
||||
@@ -5034,6 +5100,7 @@ Status DBImpl::GetLatestSequenceForKey(
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
|
||||
// TODO: plumb Env::IOActivity
|
||||
ReadOptions read_options;
|
||||
SequenceNumber current_seq = versions_->LastSequence();
|
||||
|
||||
@@ -5189,6 +5256,8 @@ Status DBImpl::IngestExternalFile(
|
||||
|
||||
Status DBImpl::IngestExternalFiles(
|
||||
const std::vector<IngestExternalFileArg>& args) {
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
if (args.empty()) {
|
||||
return Status::InvalidArgument("ingestion arg list is empty");
|
||||
}
|
||||
@@ -5406,9 +5475,9 @@ Status DBImpl::IngestExternalFiles(
|
||||
}
|
||||
assert(0 == num_entries);
|
||||
}
|
||||
status =
|
||||
versions_->LogAndApply(cfds_to_commit, mutable_cf_options_list,
|
||||
edit_lists, &mutex_, directories_.GetDbDir());
|
||||
status = versions_->LogAndApply(cfds_to_commit, mutable_cf_options_list,
|
||||
read_options, edit_lists, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
// It is safe to update VersionSet last seqno here after LogAndApply since
|
||||
// LogAndApply persists last sequence number from VersionEdits,
|
||||
// which are from file's largest seqno and not from VersionSet.
|
||||
@@ -5509,6 +5578,8 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
const ExportImportFilesMetaData& metadata, 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");
|
||||
@@ -5550,8 +5621,9 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
// file, we have to make sure the file number will never being reused.
|
||||
next_file_number = versions_->FetchAddFileNumber(metadata.files.size());
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status = versions_->LogAndApply(cfd, *cf_options, &dummy_edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
status =
|
||||
versions_->LogAndApply(cfd, *cf_options, read_options, &dummy_edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx, *cf_options);
|
||||
}
|
||||
@@ -5587,8 +5659,9 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
// Install job edit [Mutex will be unlocked here]
|
||||
if (status.ok()) {
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status = versions_->LogAndApply(cfd, *cf_options, import_job.edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
status = versions_->LogAndApply(cfd, *cf_options, read_options,
|
||||
import_job.edit(), &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, *cf_options);
|
||||
}
|
||||
@@ -5631,6 +5704,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);
|
||||
}
|
||||
@@ -5648,6 +5796,12 @@ Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
|
||||
Status s;
|
||||
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
s = Status::InvalidArgument(
|
||||
"Cannot verify file checksum with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
return s;
|
||||
}
|
||||
if (use_file_checksum) {
|
||||
FileChecksumGenFactory* const file_checksum_gen_factory =
|
||||
immutable_db_options_.file_checksum_gen_factory.get();
|
||||
@@ -5761,6 +5915,12 @@ Status DBImpl::VerifyFullFileChecksum(const std::string& file_checksum_expected,
|
||||
const std::string& func_name_expected,
|
||||
const std::string& fname,
|
||||
const ReadOptions& read_options) {
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call VerifyChecksum with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
|
||||
Status s;
|
||||
if (file_checksum_expected == kUnknownFileChecksum) {
|
||||
return s;
|
||||
@@ -5893,6 +6053,8 @@ Status DBImpl::ReserveFileNumbersBeforeIngestion(
|
||||
ColumnFamilyData* cfd, uint64_t num,
|
||||
std::unique_ptr<std::list<uint64_t>::iterator>& pending_output_elem,
|
||||
uint64_t* next_file_number) {
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
Status s;
|
||||
SuperVersionContext dummy_sv_ctx(true /* create_superversion */);
|
||||
assert(nullptr != next_file_number);
|
||||
@@ -5910,8 +6072,8 @@ Status DBImpl::ReserveFileNumbersBeforeIngestion(
|
||||
// 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.
|
||||
s = versions_->LogAndApply(cfd, *cf_options, &dummy_edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
s = versions_->LogAndApply(cfd, *cf_options, read_options, &dummy_edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &dummy_sv_ctx, *cf_options);
|
||||
}
|
||||
|
||||
+23
-10
@@ -534,6 +534,11 @@ class DBImpl : public DB {
|
||||
const ExportImportFilesMetaData& metadata,
|
||||
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;
|
||||
|
||||
@@ -734,13 +739,17 @@ class DBImpl : public DB {
|
||||
// max_file_num_to_ignore allows bottom level compaction to filter out newly
|
||||
// compacted SST files. Setting max_file_num_to_ignore to kMaxUint64 will
|
||||
// disable the filtering
|
||||
// If `final_output_level` is not nullptr, it is set to manual compaction's
|
||||
// output level if returned status is OK, and it may or may not be set to
|
||||
// manual compaction's output level if returned status is not OK.
|
||||
Status RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
int output_level,
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const Slice* begin, const Slice* end,
|
||||
bool exclusive, bool disallow_trivial_move,
|
||||
uint64_t max_file_num_to_ignore,
|
||||
const std::string& trim_ts);
|
||||
const std::string& trim_ts,
|
||||
int* final_output_level = nullptr);
|
||||
|
||||
// Return an internal iterator over the current state of the database.
|
||||
// The keys of this iterator are internal keys (see format.h).
|
||||
@@ -1049,10 +1058,16 @@ 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);
|
||||
// Wait for all flush and compactions jobs to finish. Jobs to wait include the
|
||||
// unscheduled (queued, but not scheduled yet). If the db is shutting down,
|
||||
// Status::ShutdownInProgress will be returned. If PauseBackgroundWork() was
|
||||
// called prior to this, this may potentially wait for unscheduled jobs
|
||||
// indefinitely. abort_on_pause can be set to true to abort, and
|
||||
// Status::Aborted will be returned immediately. This may also never return if
|
||||
// there's sufficient ongoing writes that keeps flush and compaction going
|
||||
// without stopping. The user would have to cease all the writes to DB to make
|
||||
// this eventually return in a stable state.
|
||||
Status WaitForCompact(bool abort_on_pause = false);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Compact any files in the named level that overlap [*begin, *end]
|
||||
@@ -1092,9 +1107,7 @@ class DBImpl : public DB {
|
||||
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(bool abort_on_pause = false);
|
||||
|
||||
// Wait for any background purge
|
||||
Status TEST_WaitForPurge();
|
||||
@@ -2123,7 +2136,7 @@ class DBImpl : public DB {
|
||||
|
||||
// helper function to call after some of the logs_ were synced
|
||||
void MarkLogsSynced(uint64_t up_to, bool synced_dir, VersionEdit* edit);
|
||||
Status ApplyWALToManifest(VersionEdit* edit);
|
||||
Status ApplyWALToManifest(const ReadOptions& read_options, VersionEdit* edit);
|
||||
// WALs with log number up to up_to are not synced successfully.
|
||||
void MarkLogsNotSynced(uint64_t up_to);
|
||||
|
||||
@@ -2306,7 +2319,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_;
|
||||
|
||||
|
||||
@@ -229,7 +229,9 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
log_io_s = SyncClosedLogs(job_context, &synced_wals);
|
||||
mutex_.Lock();
|
||||
if (log_io_s.ok() && synced_wals.IsWalAddition()) {
|
||||
log_io_s = status_to_io_status(ApplyWALToManifest(&synced_wals));
|
||||
const ReadOptions read_options(Env::IOActivity::kFlush);
|
||||
log_io_s =
|
||||
status_to_io_status(ApplyWALToManifest(read_options, &synced_wals));
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::FlushMemTableToOutputFile:CommitWal:1",
|
||||
nullptr);
|
||||
}
|
||||
@@ -492,7 +494,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
log_io_s = SyncClosedLogs(job_context, &synced_wals);
|
||||
mutex_.Lock();
|
||||
if (log_io_s.ok() && synced_wals.IsWalAddition()) {
|
||||
log_io_s = status_to_io_status(ApplyWALToManifest(&synced_wals));
|
||||
const ReadOptions read_options(Env::IOActivity::kFlush);
|
||||
log_io_s =
|
||||
status_to_io_status(ApplyWALToManifest(read_options, &synced_wals));
|
||||
}
|
||||
|
||||
if (!log_io_s.ok() && !log_io_s.IsShutdownInProgress() &&
|
||||
@@ -956,6 +960,9 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.SetFullHistoryTsLow(ts_low);
|
||||
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::IncreaseFullHistoryTsLowImpl:BeforeEdit",
|
||||
&edit);
|
||||
|
||||
@@ -969,7 +976,8 @@ Status DBImpl::IncreaseFullHistoryTsLowImpl(ColumnFamilyData* cfd,
|
||||
}
|
||||
|
||||
Status s = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
read_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -1054,8 +1062,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
}
|
||||
s = RunManualCompaction(cfd, ColumnFamilyData::kCompactAllLevels,
|
||||
final_output_level, options, begin, end, exclusive,
|
||||
false, std::numeric_limits<uint64_t>::max(),
|
||||
trim_ts);
|
||||
false /* disable_trivial_move */,
|
||||
std::numeric_limits<uint64_t>::max(), trim_ts);
|
||||
} else {
|
||||
int first_overlapped_level = kInvalidLevel;
|
||||
int max_overlapped_level = kInvalidLevel;
|
||||
@@ -1080,6 +1088,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
ro.io_activity = Env::IOActivity::kCompaction;
|
||||
bool overlap;
|
||||
for (int level = 0;
|
||||
level < current_version->storage_info()->num_non_empty_levels();
|
||||
@@ -1142,74 +1151,83 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
if (s.ok() && first_overlapped_level != kInvalidLevel) {
|
||||
// max_file_num_to_ignore can be used to filter out newly created SST
|
||||
// files, useful for bottom level compaction in a manual compaction
|
||||
uint64_t max_file_num_to_ignore = std::numeric_limits<uint64_t>::max();
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
final_output_level = max_overlapped_level;
|
||||
int output_level;
|
||||
for (int level = first_overlapped_level; level <= max_overlapped_level;
|
||||
level++) {
|
||||
bool disallow_trivial_move = false;
|
||||
// in case the compaction is universal or if we're compacting the
|
||||
// bottom-most level, the output level will be the same as input one.
|
||||
// level 0 can never be the bottommost level (i.e. if all files are in
|
||||
// level 0, we will compact to level 1)
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
output_level = level;
|
||||
} else if (level == max_overlapped_level && level > 0) {
|
||||
if (options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kSkip) {
|
||||
// Skip bottommost level compaction
|
||||
continue;
|
||||
} else if (options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter &&
|
||||
cfd->ioptions()->compaction_filter == nullptr &&
|
||||
cfd->ioptions()->compaction_filter_factory == nullptr) {
|
||||
// Skip bottommost level compaction since we don't have a compaction
|
||||
// filter
|
||||
continue;
|
||||
}
|
||||
output_level = level;
|
||||
// update max_file_num_to_ignore only for bottom level compaction
|
||||
// because data in newly compacted files in middle levels may still
|
||||
// need to be pushed down
|
||||
max_file_num_to_ignore = next_file_number;
|
||||
} else {
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleUniversal ||
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
assert(first_overlapped_level == 0);
|
||||
s = RunManualCompaction(
|
||||
cfd, first_overlapped_level, first_overlapped_level, options, begin,
|
||||
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;
|
||||
} else {
|
||||
assert(cfd->ioptions()->compaction_style == kCompactionStyleLevel);
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
// Start compaction from `first_overlapped_level`, one level down at a
|
||||
// time, until output level >= max_overlapped_level.
|
||||
// When max_overlapped_level == 0, we will still compact from L0 -> L1
|
||||
// (or LBase), and followed by a bottommost level intra-level compaction
|
||||
// at L1 (or LBase), if applicable.
|
||||
int level = first_overlapped_level;
|
||||
final_output_level = level;
|
||||
int output_level = 0, base_level = 0;
|
||||
while (level < max_overlapped_level || level == 0) {
|
||||
output_level = level + 1;
|
||||
if (cfd->ioptions()->compaction_style == kCompactionStyleLevel &&
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
if (cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
level == 0) {
|
||||
output_level = ColumnFamilyData::kCompactToBaseLevel;
|
||||
}
|
||||
// if it's a BottommostLevel compaction and `kForce*` compaction is
|
||||
// set, disallow trivial move
|
||||
if (level == max_overlapped_level &&
|
||||
(options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForce ||
|
||||
options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForceOptimized)) {
|
||||
disallow_trivial_move = true;
|
||||
// Use max value for `max_file_num_to_ignore` to always compact
|
||||
// files down.
|
||||
s = RunManualCompaction(
|
||||
cfd, level, output_level, options, begin, end, exclusive,
|
||||
!trim_ts.empty() /* disallow_trivial_move */,
|
||||
std::numeric_limits<uint64_t>::max() /* max_file_num_to_ignore */,
|
||||
trim_ts,
|
||||
output_level == ColumnFamilyData::kCompactToBaseLevel
|
||||
? &base_level
|
||||
: nullptr);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
if (output_level == ColumnFamilyData::kCompactToBaseLevel) {
|
||||
assert(base_level > 0);
|
||||
level = base_level;
|
||||
} else {
|
||||
++level;
|
||||
}
|
||||
final_output_level = level;
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::1");
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::2");
|
||||
}
|
||||
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 ==
|
||||
BottommostLevelCompaction::kForceOptimized ||
|
||||
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.
|
||||
s = RunManualCompaction(
|
||||
cfd, final_output_level, final_output_level, options, begin,
|
||||
end, exclusive, !trim_ts.empty() /* disallow_trivial_move */,
|
||||
next_file_number /* max_file_num_to_ignore */, trim_ts);
|
||||
}
|
||||
}
|
||||
// trim_ts need real compaction to remove latest record
|
||||
if (!trim_ts.empty()) {
|
||||
disallow_trivial_move = true;
|
||||
}
|
||||
s = RunManualCompaction(cfd, level, output_level, options, begin, end,
|
||||
exclusive, disallow_trivial_move,
|
||||
max_file_num_to_ignore, trim_ts);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
if (output_level == ColumnFamilyData::kCompactToBaseLevel) {
|
||||
final_output_level = cfd->NumberLevels() - 1;
|
||||
} else if (output_level > final_output_level) {
|
||||
final_output_level = output_level;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::1");
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction()::2");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1630,6 +1648,8 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
return Status::InvalidArgument("Target level exceeds number of levels");
|
||||
}
|
||||
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
@@ -1738,14 +1758,15 @@ 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);
|
||||
}
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] Apply version edit:\n%s", cfd->GetName().c_str(),
|
||||
edit.DebugString().data());
|
||||
|
||||
Status status = versions_->LogAndApply(cfd, mutable_cf_options, &edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
Status status =
|
||||
versions_->LogAndApply(cfd, mutable_cf_options, read_options, &edit,
|
||||
&mutex_, directories_.GetDbDir());
|
||||
|
||||
cfd->compaction_picker()->UnregisterCompaction(c.get());
|
||||
c.reset();
|
||||
@@ -1853,7 +1874,8 @@ Status DBImpl::RunManualCompaction(
|
||||
ColumnFamilyData* cfd, int input_level, int output_level,
|
||||
const CompactRangeOptions& compact_range_options, const Slice* begin,
|
||||
const Slice* end, bool exclusive, bool disallow_trivial_move,
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts) {
|
||||
uint64_t max_file_num_to_ignore, const std::string& trim_ts,
|
||||
int* final_output_level) {
|
||||
assert(input_level == ColumnFamilyData::kCompactAllLevels ||
|
||||
input_level >= 0);
|
||||
|
||||
@@ -2004,6 +2026,15 @@ Status DBImpl::RunManualCompaction(
|
||||
} else if (!scheduled) {
|
||||
if (compaction == nullptr) {
|
||||
manual.done = true;
|
||||
if (final_output_level) {
|
||||
// No compaction needed or there is a conflicting compaction.
|
||||
// Still set `final_output_level` to the level where we would
|
||||
// have compacted to.
|
||||
*final_output_level = output_level;
|
||||
if (output_level == ColumnFamilyData::kCompactToBaseLevel) {
|
||||
*final_output_level = cfd->current()->storage_info()->base_level();
|
||||
}
|
||||
}
|
||||
bg_cv_.SignalAll();
|
||||
continue;
|
||||
}
|
||||
@@ -2037,6 +2068,9 @@ Status DBImpl::RunManualCompaction(
|
||||
}
|
||||
scheduled = true;
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:Scheduled");
|
||||
if (final_output_level) {
|
||||
*final_output_level = compaction->output_level();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3167,6 +3201,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
mutex_.AssertHeld();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:Start");
|
||||
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
|
||||
bool is_manual = (manual_compaction != nullptr);
|
||||
std::unique_ptr<Compaction> c;
|
||||
if (prepicked_compaction != nullptr &&
|
||||
@@ -3377,9 +3413,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
for (const auto& f : *c->inputs(0)) {
|
||||
c->edit()->DeleteFile(c->level(), f->fd.GetNumber());
|
||||
}
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
status = versions_->LogAndApply(
|
||||
c->column_family_data(), *c->mutable_cf_options(), read_options,
|
||||
c->edit(), &mutex_, directories_.GetDbDir());
|
||||
io_s = versions_->io_status();
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
&job_context->superversion_contexts[0],
|
||||
@@ -3396,9 +3432,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->column_family_data());
|
||||
// Instrument for event update
|
||||
// TODO(yhchiang): add op details for showing trivial-move.
|
||||
ThreadStatusUtil::SetColumnFamily(
|
||||
c->column_family_data(), c->column_family_data()->ioptions()->env,
|
||||
immutable_db_options_.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetColumnFamily(c->column_family_data());
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
|
||||
|
||||
compaction_job_stats.num_input_files = c->num_input_files(0);
|
||||
@@ -3423,7 +3457,7 @@ 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);
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
@@ -3444,9 +3478,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
vstorage->GetNextCompactCursor(start_level, c->num_input_files(0)));
|
||||
}
|
||||
}
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
status = versions_->LogAndApply(
|
||||
c->column_family_data(), *c->mutable_cf_options(), read_options,
|
||||
c->edit(), &mutex_, directories_.GetDbDir());
|
||||
io_s = versions_->io_status();
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWork(c->column_family_data(),
|
||||
@@ -3535,7 +3569,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();
|
||||
@@ -3760,6 +3794,8 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
compaction_job_info->table_properties = c->GetOutputTableProperties();
|
||||
compaction_job_info->compaction_reason = c->compaction_reason();
|
||||
compaction_job_info->compression = c->output_compression();
|
||||
|
||||
const ReadOptions read_options(Env::IOActivity::kCompaction);
|
||||
for (size_t i = 0; i < c->num_input_levels(); ++i) {
|
||||
for (const auto fmd : *c->inputs(i)) {
|
||||
const FileDescriptor& desc = fmd->fd;
|
||||
@@ -3771,7 +3807,7 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
static_cast<int>(i), file_number, fmd->oldest_blob_file_number});
|
||||
if (compaction_job_info->table_properties.count(fn) == 0) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
auto s = current->GetTableProperties(&tp, fmd, &fn);
|
||||
auto s = current->GetTableProperties(read_options, &tp, fmd, &fn);
|
||||
if (s.ok()) {
|
||||
compaction_job_info->table_properties[fn] = tp;
|
||||
}
|
||||
@@ -3921,16 +3957,24 @@ 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(bool abort_on_pause) {
|
||||
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();
|
||||
for (;;) {
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
if (bg_work_paused_ && 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,9 @@ Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
return WaitForFlushMemTable(cfd, nullptr, false);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForCompact(bool wait_unscheduled) {
|
||||
Status DBImpl::TEST_WaitForCompact(bool abort_on_pause) {
|
||||
// Wait until the compaction completes
|
||||
return WaitForCompact(wait_unscheduled);
|
||||
return WaitForCompact(abort_on_pause);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForPurge() {
|
||||
|
||||
@@ -61,7 +61,8 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
"PromoteL0 FAILED. Invalid target level %d\n", target_level);
|
||||
return Status::InvalidArgument("Invalid target level");
|
||||
}
|
||||
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
Status status;
|
||||
VersionEdit edit;
|
||||
JobContext job_context(next_job_id_.fetch_add(1), true);
|
||||
@@ -137,11 +138,12 @@ 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);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
read_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWork(cfd,
|
||||
&job_context.superversion_contexts[0],
|
||||
|
||||
@@ -286,6 +286,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 +316,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 +339,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());
|
||||
}
|
||||
|
||||
+124
-16
@@ -19,11 +19,12 @@
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/persistent_stats_history.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#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"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
Options SanitizeOptions(const std::string& dbname, const Options& src,
|
||||
@@ -533,6 +534,104 @@ Status DBImpl::Recover(
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (s.ok() && !read_only) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
// Try to trivially move files down the LSM tree to start from bottommost
|
||||
// level when level_compaction_dynamic_level_bytes is enabled. This should
|
||||
// only be useful when user is migrating to turning on this option.
|
||||
// If a user is migrating from Level Compaction with a smaller level
|
||||
// multiplier or from Universal Compaction, there may be too many
|
||||
// non-empty levels and the trivial moves here are not sufficed for
|
||||
// migration. Additional compactions are needed to drain unnecessary
|
||||
// levels.
|
||||
//
|
||||
// Note that this step moves files down LSM without consulting
|
||||
// SSTPartitioner. Further compactions are still needed if
|
||||
// the user wants to partition SST files.
|
||||
// Note that files moved in this step may not respect the compression
|
||||
// option in target level.
|
||||
if (cfd->ioptions()->compaction_style ==
|
||||
CompactionStyle::kCompactionStyleLevel &&
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
!cfd->GetLatestMutableCFOptions()->disable_auto_compactions) {
|
||||
int to_level = cfd->ioptions()->num_levels - 1;
|
||||
// last level is reserved
|
||||
// allow_ingest_behind does not support Level Compaction,
|
||||
// and per_key_placement can have infinite compaction loop for Level
|
||||
// Compaction. Adjust to_level here just to be safe.
|
||||
if (cfd->ioptions()->allow_ingest_behind ||
|
||||
cfd->ioptions()->preclude_last_level_data_seconds > 0) {
|
||||
to_level -= 1;
|
||||
}
|
||||
// Whether this column family has a level trivially moved
|
||||
bool moved = false;
|
||||
// Fill the LSM starting from to_level and going up one level at a time.
|
||||
// Some loop invariants (when last level is not reserved):
|
||||
// - levels in (from_level, to_level] are empty, and
|
||||
// - levels in (to_level, last_level] are non-empty.
|
||||
for (int from_level = to_level; from_level >= 0; --from_level) {
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
cfd->current()->storage_info()->LevelFiles(from_level);
|
||||
if (level_files.empty() || from_level == 0) {
|
||||
continue;
|
||||
}
|
||||
assert(from_level <= to_level);
|
||||
// Trivial move files from `from_level` to `to_level`
|
||||
if (from_level < to_level) {
|
||||
if (!moved) {
|
||||
// lsm_state will look like "[1,2,3,4,5,6,0]" for an LSM with
|
||||
// 7 levels
|
||||
std::string lsm_state = "[";
|
||||
for (int i = 0; i < cfd->ioptions()->num_levels; ++i) {
|
||||
lsm_state += std::to_string(
|
||||
cfd->current()->storage_info()->NumLevelFiles(i));
|
||||
if (i < cfd->ioptions()->num_levels - 1) {
|
||||
lsm_state += ",";
|
||||
}
|
||||
}
|
||||
lsm_state += "]";
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] Trivially move files down the LSM when open "
|
||||
"with level_compaction_dynamic_level_bytes=true,"
|
||||
" lsm_state: %s (Files are moved only if DB "
|
||||
"Recovery is successful).",
|
||||
cfd->GetName().c_str(), lsm_state.c_str());
|
||||
moved = true;
|
||||
}
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"[%s] Moving %zu files from from_level-%d to from_level-%d",
|
||||
cfd->GetName().c_str(), level_files.size(), from_level,
|
||||
to_level);
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
for (const FileMetaData* f : level_files) {
|
||||
edit.DeleteFile(from_level, f->fd.GetNumber());
|
||||
edit.AddFile(to_level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction,
|
||||
f->temperature, // this can be different from
|
||||
// `last_level_temperature`
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number,
|
||||
f->file_checksum, f->file_checksum_func_name,
|
||||
f->unique_id, f->compensated_range_deletion_size,
|
||||
f->tail_size);
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] Moving #%" PRIu64
|
||||
" from from_level-%d to from_level-%d %" PRIu64
|
||||
" bytes\n",
|
||||
cfd->GetName().c_str(), f->fd.GetNumber(),
|
||||
from_level, to_level, f->fd.GetFileSize());
|
||||
}
|
||||
recovery_ctx->UpdateVersionEdits(cfd, edit);
|
||||
}
|
||||
--to_level;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s = SetupDBId(read_only, recovery_ctx);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "DB ID: %s\n", db_id_.c_str());
|
||||
if (s.ok() && !read_only) {
|
||||
@@ -826,8 +925,9 @@ Status DBImpl::InitPersistStatsColumnFamily() {
|
||||
Status DBImpl::LogAndApplyForRecovery(const RecoveryContext& recovery_ctx) {
|
||||
mutex_.AssertHeld();
|
||||
assert(versions_->descriptor_log_ == nullptr);
|
||||
const ReadOptions read_options(Env::IOActivity::kDBOpen);
|
||||
Status s = versions_->LogAndApply(
|
||||
recovery_ctx.cfds_, recovery_ctx.mutable_cf_opts_,
|
||||
recovery_ctx.cfds_, recovery_ctx.mutable_cf_opts_, read_options,
|
||||
recovery_ctx.edit_lists_, &mutex_, directories_.GetDbDir());
|
||||
if (s.ok() && !(recovery_ctx.files_to_delete_.empty())) {
|
||||
mutex_.Unlock();
|
||||
@@ -1354,7 +1454,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);
|
||||
@@ -1480,6 +1580,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
meta.fd = FileDescriptor(versions_->NewFileNumber(), 0, 0);
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
ro.io_activity = Env::IOActivity::kDBOpen;
|
||||
Arena arena;
|
||||
Status s;
|
||||
TableProperties table_properties;
|
||||
@@ -1538,10 +1639,11 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
SeqnoToTimeMapping empty_seqno_time_mapping;
|
||||
Version* version = cfd->current();
|
||||
version->Ref();
|
||||
const ReadOptions read_option(Env::IOActivity::kDBOpen);
|
||||
s = BuildTable(
|
||||
dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::move(range_del_iters), &meta, &blob_file_additions,
|
||||
file_options_for_compaction_, read_option, cfd->table_cache(),
|
||||
iter.get(), std::move(range_del_iters), &meta, &blob_file_additions,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks, cfd->internal_stats(), &io_s,
|
||||
io_tracer_, BlobFileCreationReason::kRecovery,
|
||||
@@ -1572,14 +1674,14 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
constexpr int level = 0;
|
||||
|
||||
if (s.ok() && has_output) {
|
||||
edit->AddFile(level, meta.fd.GetNumber(), meta.fd.GetPathId(),
|
||||
meta.fd.GetFileSize(), meta.smallest, meta.largest,
|
||||
meta.fd.smallest_seqno, meta.fd.largest_seqno,
|
||||
meta.marked_for_compaction, meta.temperature,
|
||||
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);
|
||||
edit->AddFile(
|
||||
level, meta.fd.GetNumber(), meta.fd.GetPathId(), meta.fd.GetFileSize(),
|
||||
meta.smallest, meta.largest, meta.fd.smallest_seqno,
|
||||
meta.fd.largest_seqno, meta.marked_for_compaction, meta.temperature,
|
||||
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.tail_size);
|
||||
|
||||
for (const auto& blob : blob_file_additions) {
|
||||
edit->AddBlobFile(blob);
|
||||
@@ -1642,8 +1744,12 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr) {
|
||||
const bool kSeqPerBatch = true;
|
||||
const bool kBatchPerTxn = true;
|
||||
return DBImpl::Open(db_options, dbname, column_families, handles, dbptr,
|
||||
!kSeqPerBatch, kBatchPerTxn);
|
||||
ThreadStatusUtil::SetEnableTracking(db_options.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OperationType::OP_DBOPEN);
|
||||
Status s = DBImpl::Open(db_options, dbname, column_families, handles, dbptr,
|
||||
!kSeqPerBatch, kBatchPerTxn);
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
return s;
|
||||
}
|
||||
|
||||
// TODO: Implement the trimming in flush code path.
|
||||
@@ -1828,7 +1934,9 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
|
||||
// Handles create_if_missing, error_if_exists
|
||||
uint64_t recovered_seq(kMaxSequenceNumber);
|
||||
s = impl->Recover(column_families, false, false, false, &recovered_seq,
|
||||
s = impl->Recover(column_families, false /* read_only */,
|
||||
false /* error_if_wal_file_exists */,
|
||||
false /* error_if_data_exists_in_wals */, &recovered_seq,
|
||||
&recovery_ctx);
|
||||
if (s.ok()) {
|
||||
uint64_t new_log_number = impl->versions_->NewFileNumber();
|
||||
|
||||
@@ -40,6 +40,11 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* pinnable_val,
|
||||
std::string* timestamp) {
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call Get with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
assert(pinnable_val != nullptr);
|
||||
// TODO: stopwatch DB_GET needed?, perf timer needed?
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
@@ -112,6 +117,11 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
|
||||
Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return NewErrorIterator(Status::InvalidArgument(
|
||||
"Cannot call NewIterator with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`"));
|
||||
}
|
||||
assert(column_family);
|
||||
if (read_options.timestamp) {
|
||||
const Status s = FailIfTsMismatchCf(
|
||||
|
||||
@@ -142,6 +142,13 @@ class DBImplReadOnly : public DBImpl {
|
||||
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:
|
||||
|
||||
@@ -345,6 +345,11 @@ Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* pinnable_val,
|
||||
std::string* timestamp) {
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call Get with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
assert(pinnable_val != nullptr);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_GET);
|
||||
@@ -445,6 +450,11 @@ Iterator* DBImplSecondary::NewIterator(const ReadOptions& read_options,
|
||||
return NewErrorIterator(Status::NotSupported(
|
||||
"ReadTier::kPersistedData is not yet supported in iterators."));
|
||||
}
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return NewErrorIterator(Status::InvalidArgument(
|
||||
"Cannot call NewIterator with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`"));
|
||||
}
|
||||
|
||||
assert(column_family);
|
||||
if (read_options.timestamp) {
|
||||
@@ -511,6 +521,11 @@ Status DBImplSecondary::NewIterators(
|
||||
return Status::NotSupported(
|
||||
"ReadTier::kPersistedData is not yet supported in iterators.");
|
||||
}
|
||||
if (read_options.io_activity != Env::IOActivity::kUnknown) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot call NewIterators with `ReadOptions::io_activity` != "
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (iterators == nullptr) {
|
||||
return Status::InvalidArgument("iterators not allowed to be nullptr");
|
||||
|
||||
@@ -606,7 +606,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
log_write_mutex_.Unlock();
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
status = ApplyWALToManifest(&synced_wals);
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
status = ApplyWALToManifest(read_options, &synced_wals);
|
||||
}
|
||||
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
@@ -767,7 +769,9 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
if (w.status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
w.status = ApplyWALToManifest(&synced_wals);
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
w.status = ApplyWALToManifest(read_options, &synced_wals);
|
||||
}
|
||||
write_thread_.ExitAsBatchGroupLeader(wal_write_group, w.status);
|
||||
}
|
||||
@@ -1805,7 +1809,7 @@ Status DBImpl::DelayWrite(uint64_t num_bytes, WriteThread& write_thread,
|
||||
bool delayed = false;
|
||||
{
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, WRITE_STALL,
|
||||
&time_delayed);
|
||||
Histograms::HISTOGRAM_ENUM_MAX, &time_delayed);
|
||||
// To avoid parallel timed delays (bad throttling), only support them
|
||||
// on the primary write queue.
|
||||
uint64_t delay;
|
||||
@@ -2086,6 +2090,8 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* /*cfd*/,
|
||||
// two_write_queues_ is true (This is to simplify the reasoning.)
|
||||
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
mutex_.AssertHeld();
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
log::Writer* new_log = nullptr;
|
||||
MemTable* new_mem = nullptr;
|
||||
IOStatus io_s;
|
||||
@@ -2237,8 +2243,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
|
||||
VersionEdit wal_deletion;
|
||||
wal_deletion.DeleteWalsBefore(min_wal_number_to_keep);
|
||||
s = versions_->LogAndApplyToDefaultColumnFamily(&wal_deletion, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
s = versions_->LogAndApplyToDefaultColumnFamily(
|
||||
read_options, &wal_deletion, &mutex_, directories_.GetDbDir());
|
||||
if (!s.ok() && versions_->io_status().IsIOError()) {
|
||||
s = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
|
||||
+7
-8
@@ -131,6 +131,7 @@ void DBIter::Next() {
|
||||
assert(valid_);
|
||||
assert(status_.ok());
|
||||
|
||||
PERF_COUNTER_ADD(iter_next_count, 1);
|
||||
PERF_CPU_TIMER_GUARD(iter_next_cpu_nanos, clock_);
|
||||
// Release temporarily pinned blocks from last operation
|
||||
ReleaseTempPinnedData();
|
||||
@@ -195,6 +196,7 @@ bool DBIter::SetBlobValueIfNeeded(const Slice& user_key,
|
||||
|
||||
// TODO: consider moving ReadOptions from ArenaWrappedDBIter to DBIter to
|
||||
// avoid having to copy options back and forth.
|
||||
// TODO: plumb Env::IOActivity
|
||||
ReadOptions read_options;
|
||||
read_options.read_tier = read_tier_;
|
||||
read_options.fill_cache = fill_cache_;
|
||||
@@ -637,6 +639,7 @@ void DBIter::Prev() {
|
||||
assert(valid_);
|
||||
assert(status_.ok());
|
||||
|
||||
PERF_COUNTER_ADD(iter_prev_count, 1);
|
||||
PERF_CPU_TIMER_GUARD(iter_prev_cpu_nanos, clock_);
|
||||
ReleaseTempPinnedData();
|
||||
ResetBlobValue();
|
||||
@@ -961,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:
|
||||
@@ -1040,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;
|
||||
}
|
||||
@@ -1444,6 +1439,7 @@ void DBIter::SetSavedKeyToSeekForPrevTarget(const Slice& target) {
|
||||
}
|
||||
|
||||
void DBIter::Seek(const Slice& target) {
|
||||
PERF_COUNTER_ADD(iter_seek_count, 1);
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
StopWatch sw(clock_, statistics_, DB_SEEK);
|
||||
|
||||
@@ -1517,6 +1513,7 @@ void DBIter::Seek(const Slice& target) {
|
||||
}
|
||||
|
||||
void DBIter::SeekForPrev(const Slice& target) {
|
||||
PERF_COUNTER_ADD(iter_seek_count, 1);
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
StopWatch sw(clock_, statistics_, DB_SEEK);
|
||||
|
||||
@@ -1590,6 +1587,7 @@ void DBIter::SeekToFirst() {
|
||||
Seek(*iterate_lower_bound_);
|
||||
return;
|
||||
}
|
||||
PERF_COUNTER_ADD(iter_seek_count, 1);
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
// Don't use iter_::Seek() if we set a prefix extractor
|
||||
// because prefix seek will be used.
|
||||
@@ -1652,6 +1650,7 @@ void DBIter::SeekToLast() {
|
||||
return;
|
||||
}
|
||||
|
||||
PERF_COUNTER_ADD(iter_seek_count, 1);
|
||||
PERF_CPU_TIMER_GUARD(iter_seek_cpu_nanos, clock_);
|
||||
// Don't use iter_::Seek() if we set a prefix extractor
|
||||
// because prefix seek will be used.
|
||||
|
||||
+54
-3
@@ -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"
|
||||
|
||||
@@ -30,12 +30,63 @@ class DummyReadCallback : public ReadCallback {
|
||||
void SetSnapshot(SequenceNumber seq) { max_visible_seq_ = seq; }
|
||||
};
|
||||
|
||||
class DBIteratorBaseTest : public DBTestBase {
|
||||
public:
|
||||
DBIteratorBaseTest()
|
||||
: DBTestBase("db_iterator_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBIteratorBaseTest, APICallsWithPerfContext) {
|
||||
// Set up the DB
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
ASSERT_OK(Put(std::to_string(i), std::to_string(i)));
|
||||
}
|
||||
|
||||
// Setup iterator and PerfContext
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
std::string key_str = std::to_string(2);
|
||||
Slice key(key_str);
|
||||
SetPerfLevel(kEnableCount);
|
||||
get_perf_context()->Reset();
|
||||
|
||||
// Initial PerfContext counters
|
||||
ASSERT_EQ(0, get_perf_context()->iter_seek_count);
|
||||
ASSERT_EQ(0, get_perf_context()->iter_next_count);
|
||||
ASSERT_EQ(0, get_perf_context()->iter_prev_count);
|
||||
|
||||
// Test Seek-related API calls PerfContext counter
|
||||
iter->Seek(key);
|
||||
iter->SeekToFirst();
|
||||
iter->SeekToLast();
|
||||
iter->SeekForPrev(key);
|
||||
ASSERT_EQ(4, get_perf_context()->iter_seek_count);
|
||||
ASSERT_EQ(0, get_perf_context()->iter_next_count);
|
||||
ASSERT_EQ(0, get_perf_context()->iter_prev_count);
|
||||
|
||||
// Test Next() calls PerfContext counter
|
||||
iter->Next();
|
||||
ASSERT_EQ(4, get_perf_context()->iter_seek_count);
|
||||
ASSERT_EQ(1, get_perf_context()->iter_next_count);
|
||||
ASSERT_EQ(0, get_perf_context()->iter_prev_count);
|
||||
|
||||
// Test Prev() calls PerfContext counter
|
||||
iter->Prev();
|
||||
ASSERT_EQ(4, get_perf_context()->iter_seek_count);
|
||||
ASSERT_EQ(1, get_perf_context()->iter_next_count);
|
||||
ASSERT_EQ(1, get_perf_context()->iter_prev_count);
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
// Test param:
|
||||
// bool: whether to pass read_callback to NewIterator().
|
||||
class DBIteratorTest : public DBTestBase,
|
||||
class DBIteratorTest : public DBIteratorBaseTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBIteratorTest() : DBTestBase("db_iterator_test", /*env_do_fsync=*/true) {}
|
||||
DBIteratorTest() {}
|
||||
|
||||
Iterator* NewIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family = nullptr) {
|
||||
|
||||
@@ -358,6 +358,48 @@ 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.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_restart_interval = 16;
|
||||
table_options.data_block_index_type =
|
||||
BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinaryAndHash;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumKeys = 100;
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), Key(i), std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
std::string value;
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(db_->Get(ReadOptions(), Key(i), &value));
|
||||
ASSERT_EQ(std::to_string(i), value);
|
||||
}
|
||||
|
||||
std::vector<const Snapshot*> snapshots;
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), Key(i)));
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), Key(i), std::to_string(i * 3 + j)));
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
}
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(db_->Get(ReadOptions(), Key(i), &value));
|
||||
ASSERT_EQ(std::to_string(i * 3 + 2), value);
|
||||
}
|
||||
for (auto snapshot : snapshots) {
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
class MergeOperatorPinningTest : public DBMergeOperatorTest,
|
||||
public testing::WithParamInterface<bool> {
|
||||
|
||||
+116
-1
@@ -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 {
|
||||
|
||||
@@ -910,6 +911,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 +1011,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 +1063,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 +1084,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 +1109,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 +1237,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) {
|
||||
|
||||
@@ -2333,8 +2333,9 @@ TEST_F(DBPropertiesTest, TableMetaIndexKeys) {
|
||||
|
||||
// Read metaindex
|
||||
BlockContents bc;
|
||||
ASSERT_OK(ReadMetaIndexBlockInFile(r.get(), file_size, 0U,
|
||||
ImmutableOptions(options), &bc));
|
||||
const ReadOptions read_options;
|
||||
ASSERT_OK(ReadMetaIndexBlockInFile(
|
||||
r.get(), file_size, 0U, ImmutableOptions(options), read_options, &bc));
|
||||
Block metaindex_block(std::move(bc));
|
||||
std::unique_ptr<InternalIterator> meta_iter;
|
||||
meta_iter.reset(metaindex_block.NewMetaIterator());
|
||||
|
||||
@@ -235,8 +235,9 @@ TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
|
||||
ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
|
||||
ASSERT_OK(db_->VerifyChecksum(GetReadOptions()));
|
||||
// The files are tiny so there should have just been one read per file.
|
||||
int expected = kNumFiles;
|
||||
// There are 3 reads per file: ReadMetaIndexBlock,
|
||||
// VerifyChecksumInMetaBlocks, VerifyChecksumInBlocks
|
||||
int expected = kNumFiles * 3;
|
||||
ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
|
||||
}
|
||||
|
||||
|
||||
+9
-8
@@ -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();
|
||||
@@ -810,11 +810,12 @@ TEST_F(DBSSTTest, RateLimitedWALDelete) {
|
||||
// We created 4 sst files in L0
|
||||
ASSERT_EQ("4", FilesPerLevel(0));
|
||||
|
||||
// Compaction will move the 4 files in L0 to trash and create 1 L1 file
|
||||
// Compaction will move the 4 files in L0 to trash and create 1 L1 file.
|
||||
// Use kForceOptimized to not rewrite the new L1 file.
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
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();
|
||||
@@ -1223,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
|
||||
@@ -1280,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
|
||||
@@ -1296,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),
|
||||
@@ -1313,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);
|
||||
|
||||
+147
-68
@@ -20,76 +20,115 @@ class DBStatisticsTest : public DBTestBase {
|
||||
};
|
||||
|
||||
TEST_F(DBStatisticsTest, CompressionStatsTest) {
|
||||
CompressionType type;
|
||||
for (CompressionType type : GetSupportedCompressions()) {
|
||||
if (type == kNoCompression) {
|
||||
continue;
|
||||
}
|
||||
if (type == kBZip2Compression) {
|
||||
// Weird behavior in this test
|
||||
continue;
|
||||
}
|
||||
SCOPED_TRACE("Compression type: " + std::to_string(type));
|
||||
|
||||
if (Snappy_Supported()) {
|
||||
type = kSnappyCompression;
|
||||
fprintf(stderr, "using snappy\n");
|
||||
} else if (Zlib_Supported()) {
|
||||
type = kZlibCompression;
|
||||
fprintf(stderr, "using zlib\n");
|
||||
} else if (BZip2_Supported()) {
|
||||
type = kBZip2Compression;
|
||||
fprintf(stderr, "using bzip2\n");
|
||||
} else if (LZ4_Supported()) {
|
||||
type = kLZ4Compression;
|
||||
fprintf(stderr, "using lz4\n");
|
||||
} else if (XPRESS_Supported()) {
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTD;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
return;
|
||||
Options options = CurrentOptions();
|
||||
options.compression = type;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kExceptTimeForMutex);
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.enable_index_compression = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
auto PopStat = [&](Tickers t) -> uint64_t {
|
||||
return options.statistics->getAndResetTickerCount(t);
|
||||
};
|
||||
|
||||
int kNumKeysWritten = 100;
|
||||
double compress_to = 0.5;
|
||||
// About three KVs per block
|
||||
int len = static_cast<int>(BlockBasedTableOptions().block_size / 3);
|
||||
int uncomp_est = kNumKeysWritten * (len + 20);
|
||||
|
||||
Random rnd(301);
|
||||
std::string buf;
|
||||
|
||||
// Check that compressions occur and are counted when compression is turned
|
||||
// on
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i), test::CompressibleString(&rnd, compress_to, len, &buf)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
EXPECT_EQ(34, PopStat(NUMBER_BLOCK_COMPRESSED));
|
||||
EXPECT_NEAR2(uncomp_est, PopStat(BYTES_COMPRESSED_FROM), uncomp_est / 10);
|
||||
EXPECT_NEAR2(uncomp_est * compress_to, PopStat(BYTES_COMPRESSED_TO),
|
||||
uncomp_est / 10);
|
||||
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_DECOMPRESSED));
|
||||
EXPECT_EQ(0, PopStat(BYTES_DECOMPRESSED_FROM));
|
||||
EXPECT_EQ(0, PopStat(BYTES_DECOMPRESSED_TO));
|
||||
|
||||
// And decompressions
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
EXPECT_EQ(34, PopStat(NUMBER_BLOCK_DECOMPRESSED));
|
||||
EXPECT_NEAR2(uncomp_est, PopStat(BYTES_DECOMPRESSED_TO), uncomp_est / 10);
|
||||
EXPECT_NEAR2(uncomp_est * compress_to, PopStat(BYTES_DECOMPRESSED_FROM),
|
||||
uncomp_est / 10);
|
||||
|
||||
EXPECT_EQ(0, PopStat(BYTES_COMPRESSION_BYPASSED));
|
||||
EXPECT_EQ(0, PopStat(BYTES_COMPRESSION_REJECTED));
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_COMPRESSION_BYPASSED));
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_COMPRESSION_REJECTED));
|
||||
|
||||
// Check when compression is rejected.
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomBinaryString(len)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
EXPECT_EQ(34, PopStat(NUMBER_BLOCK_COMPRESSION_REJECTED));
|
||||
EXPECT_NEAR2(uncomp_est, PopStat(BYTES_COMPRESSION_REJECTED),
|
||||
uncomp_est / 10);
|
||||
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_COMPRESSED));
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_COMPRESSION_BYPASSED));
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_DECOMPRESSED));
|
||||
EXPECT_EQ(0, PopStat(BYTES_COMPRESSED_FROM));
|
||||
EXPECT_EQ(0, PopStat(BYTES_COMPRESSED_TO));
|
||||
EXPECT_EQ(0, PopStat(BYTES_COMPRESSION_BYPASSED));
|
||||
EXPECT_EQ(0, PopStat(BYTES_DECOMPRESSED_FROM));
|
||||
EXPECT_EQ(0, PopStat(BYTES_DECOMPRESSED_TO));
|
||||
|
||||
// Check when compression is disabled.
|
||||
options.compression = kNoCompression;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomBinaryString(len)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
EXPECT_EQ(34, PopStat(NUMBER_BLOCK_COMPRESSION_BYPASSED));
|
||||
EXPECT_NEAR2(uncomp_est, PopStat(BYTES_COMPRESSION_BYPASSED),
|
||||
uncomp_est / 10);
|
||||
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_COMPRESSED));
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_COMPRESSION_REJECTED));
|
||||
EXPECT_EQ(0, PopStat(NUMBER_BLOCK_DECOMPRESSED));
|
||||
EXPECT_EQ(0, PopStat(BYTES_COMPRESSED_FROM));
|
||||
EXPECT_EQ(0, PopStat(BYTES_COMPRESSED_TO));
|
||||
EXPECT_EQ(0, PopStat(BYTES_COMPRESSION_REJECTED));
|
||||
EXPECT_EQ(0, PopStat(BYTES_DECOMPRESSED_FROM));
|
||||
EXPECT_EQ(0, PopStat(BYTES_DECOMPRESSED_TO));
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = type;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.statistics->set_stats_level(StatsLevel::kExceptTimeForMutex);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int kNumKeysWritten = 100000;
|
||||
|
||||
// Check that compressions occur and are counted when compression is turned on
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED), 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED), 0);
|
||||
|
||||
options.compression = kNoCompression;
|
||||
DestroyAndReopen(options);
|
||||
uint64_t currentCompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
uint64_t currentDecompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED);
|
||||
|
||||
// Check that compressions do not occur when turned off
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), rnd.RandomString(128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED) -
|
||||
currentCompressions,
|
||||
0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED) -
|
||||
currentDecompressions,
|
||||
0);
|
||||
}
|
||||
|
||||
TEST_F(DBStatisticsTest, MutexWaitStatsDisabledByDefault) {
|
||||
@@ -203,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
|
||||
|
||||
|
||||
+28
-7
@@ -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"
|
||||
|
||||
@@ -353,7 +353,7 @@ TEST_F(DBTest, MixedSlowdownOptionsInQueue) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
threads.emplace_back(write_no_slowdown_func);
|
||||
}
|
||||
// Sleep for 2s to allow the threads to insert themselves into the
|
||||
// Sleep for 3s to allow the threads to insert themselves into the
|
||||
// write queue
|
||||
env_->SleepForMicroseconds(3000000ULL);
|
||||
}
|
||||
@@ -424,7 +424,7 @@ TEST_F(DBTest, MixedSlowdownOptionsStop) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
threads.emplace_back(write_no_slowdown_func);
|
||||
}
|
||||
// Sleep for 2s to allow the threads to insert themselves into the
|
||||
// Sleep for 3s to allow the threads to insert themselves into the
|
||||
// write queue
|
||||
env_->SleepForMicroseconds(3000000ULL);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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*/,
|
||||
@@ -3460,6 +3467,7 @@ static bool CompareIterators(int step, DB* model, DB* db,
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
(void)count;
|
||||
delete miter;
|
||||
delete dbiter;
|
||||
return ok;
|
||||
@@ -4936,7 +4944,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 +5031,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;
|
||||
@@ -6886,7 +6894,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.MakeSharedGeneralCache();
|
||||
// BEGIN check that Cache classes as aliases of each other.
|
||||
// Currently, GeneralCache and BlockCache are aliases for Cache.
|
||||
// This is expected to change (carefully, intentionally)
|
||||
std::shared_ptr<GeneralCache> general_cache = options.row_cache;
|
||||
std::shared_ptr<Cache> cache = general_cache;
|
||||
std::shared_ptr<BlockCache> block_cache = general_cache;
|
||||
general_cache = cache;
|
||||
block_cache = cache;
|
||||
general_cache = block_cache;
|
||||
cache = block_cache;
|
||||
// END check that Cache classes as aliases of each other.
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
|
||||
+71
-131
@@ -3044,7 +3044,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 +3064,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 +3097,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 +3139,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 +3148,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 +3203,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 +3262,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 +3286,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 +3298,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 +3362,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 +3380,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 +3455,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 +3470,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 +3492,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 +5298,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 +5823,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 +5992,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 +6018,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 +6032,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 +6057,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 +6083,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 +6104,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 +6178,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 +6202,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 +6213,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 +6223,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 +6233,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 +6245,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 +6299,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 +6330,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 +6339,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 +6348,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 +6391,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 +6402,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 +6414,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));
|
||||
|
||||
@@ -1074,6 +1074,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);
|
||||
|
||||
@@ -1192,6 +1192,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,6 +1318,10 @@ 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2399,6 +2399,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) {
|
||||
|
||||
@@ -3918,6 +3918,94 @@ TEST_F(DBBasicTestWithTimestamp, IterSeekToLastWithIterateUpperbound) {
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, TimestampFilterTableReadOnGet) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = 100;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Put
|
||||
// Create two SST files
|
||||
// file1: key => [1, 3], timestamp => [10, 20]
|
||||
// file2, key => [2, 4], timestamp => [30, 40]
|
||||
{
|
||||
WriteOptions write_opts;
|
||||
std::string write_ts = Timestamp(10, 0);
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(1), write_ts, "value1"));
|
||||
write_ts = Timestamp(20, 0);
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(3), write_ts, "value3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
write_ts = Timestamp(30, 0);
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(2), write_ts, "value2"));
|
||||
write_ts = Timestamp(40, 0);
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(4), write_ts, "value4"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
// Get with timestamp
|
||||
{
|
||||
auto prev_checked_events = options.statistics->getTickerCount(
|
||||
Tickers::TIMESTAMP_FILTER_TABLE_CHECKED);
|
||||
auto prev_filtered_events = options.statistics->getTickerCount(
|
||||
Tickers::TIMESTAMP_FILTER_TABLE_FILTERED);
|
||||
|
||||
// key=3 (ts=20) does not exist at timestamp=1
|
||||
std::string read_ts_str = Timestamp(1, 0);
|
||||
Slice read_ts_slice = Slice(read_ts_str);
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &read_ts_slice;
|
||||
std::string value_from_get = "";
|
||||
std::string timestamp_from_get = "";
|
||||
auto status =
|
||||
db_->Get(read_opts, Key1(3), &value_from_get, ×tamp_from_get);
|
||||
ASSERT_TRUE(status.IsNotFound());
|
||||
ASSERT_EQ(value_from_get, std::string(""));
|
||||
ASSERT_EQ(timestamp_from_get, std::string(""));
|
||||
|
||||
// key=3 is in the key ranges for both files, so both files will be queried.
|
||||
// The table read was skipped because the timestamp is out of the table
|
||||
// range, i.e.., 1 < [10,20], [30,40].
|
||||
// The tickers increase by 2 due to 2 files.
|
||||
ASSERT_EQ(prev_checked_events + 2,
|
||||
options.statistics->getTickerCount(
|
||||
Tickers::TIMESTAMP_FILTER_TABLE_CHECKED));
|
||||
ASSERT_EQ(prev_filtered_events + 2,
|
||||
options.statistics->getTickerCount(
|
||||
Tickers::TIMESTAMP_FILTER_TABLE_FILTERED));
|
||||
|
||||
// key=3 (ts=20) exists at timestamp = 25
|
||||
read_ts_str = Timestamp(25, 0);
|
||||
read_ts_slice = Slice(read_ts_str);
|
||||
read_opts.timestamp = &read_ts_slice;
|
||||
ASSERT_OK(
|
||||
db_->Get(read_opts, Key1(3), &value_from_get, ×tamp_from_get));
|
||||
ASSERT_EQ("value3", value_from_get);
|
||||
ASSERT_EQ(Timestamp(20, 0), timestamp_from_get);
|
||||
|
||||
// file1 was not skipped, because the timestamp is in range, [10,20] < 25.
|
||||
// file2 was skipped, because the timestamp is not in range, 25 < [30,40].
|
||||
// So the checked ticker increase by 2 due to 2 files;
|
||||
// filtered ticker increase by 1 because file2 was skipped
|
||||
ASSERT_EQ(prev_checked_events + 4,
|
||||
options.statistics->getTickerCount(
|
||||
Tickers::TIMESTAMP_FILTER_TABLE_CHECKED));
|
||||
ASSERT_EQ(prev_filtered_events + 3,
|
||||
options.statistics->getTickerCount(
|
||||
Tickers::TIMESTAMP_FILTER_TABLE_FILTERED));
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -846,6 +846,73 @@ TEST_P(DBWriteBufferManagerTest, StopSwitchingMemTablesOnceFlushing) {
|
||||
delete shared_wbm_db;
|
||||
}
|
||||
|
||||
TEST_F(DBWriteBufferManagerTest, RuntimeChangeableAllowStall) {
|
||||
constexpr int kBigValue = 10000;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_manager.reset(
|
||||
new WriteBufferManager(1, nullptr /* cache */, true /* allow_stall */));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Pause flush thread so that
|
||||
// (a) the only way to exist write stall below is to change the `allow_stall`
|
||||
// (b) the write stall is "stable" without being interfered by flushes so that
|
||||
// we can check it without flakiness
|
||||
std::unique_ptr<test::SleepingBackgroundTask> sleeping_task(
|
||||
new test::SleepingBackgroundTask());
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
sleeping_task.get(), Env::Priority::HIGH);
|
||||
sleeping_task->WaitUntilSleeping();
|
||||
|
||||
// Test 1: test setting `allow_stall` from true to false
|
||||
//
|
||||
// Assert existence of a write stall
|
||||
WriteOptions wo_no_slowdown;
|
||||
wo_no_slowdown.no_slowdown = true;
|
||||
Status s = Put(Key(0), DummyString(kBigValue), wo_no_slowdown);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
ASSERT_TRUE(s.ToString().find("Write stall") != std::string::npos);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"WBMStallInterface::BlockDB",
|
||||
"DBWriteBufferManagerTest::RuntimeChangeableThreadSafeParameters::"
|
||||
"ChangeParameter"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Test `SetAllowStall()`
|
||||
port::Thread thread1([&] { ASSERT_OK(Put(Key(0), DummyString(kBigValue))); });
|
||||
port::Thread thread2([&] {
|
||||
TEST_SYNC_POINT(
|
||||
"DBWriteBufferManagerTest::RuntimeChangeableThreadSafeParameters::"
|
||||
"ChangeParameter");
|
||||
options.write_buffer_manager->SetAllowStall(false);
|
||||
});
|
||||
|
||||
// Verify `allow_stall` is successfully set to false in thread2.
|
||||
// Othwerwise, thread1's write will be stalled and this test will hang
|
||||
// forever.
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Test 2: test setting `allow_stall` from false to true
|
||||
//
|
||||
// Assert no write stall
|
||||
ASSERT_OK(Put(Key(0), DummyString(kBigValue), wo_no_slowdown));
|
||||
|
||||
// Test `SetAllowStall()`
|
||||
options.write_buffer_manager->SetAllowStall(true);
|
||||
|
||||
// Verify `allow_stall` is successfully set to true.
|
||||
// Otherwise the following write will not be stalled and therefore succeed.
|
||||
s = Put(Key(0), DummyString(kBigValue), wo_no_slowdown);
|
||||
ASSERT_TRUE(s.IsIncomplete());
|
||||
ASSERT_TRUE(s.ToString().find("Write stall") != std::string::npos);
|
||||
sleeping_task->WakeUp();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBWriteBufferManagerTest, DBWriteBufferManagerTest,
|
||||
testing::Bool());
|
||||
|
||||
@@ -636,6 +636,11 @@ TEST_P(DBWriteTest, LockWALInEffect) {
|
||||
ASSERT_TRUE(dbfull()->WALBufferIsEmpty());
|
||||
ASSERT_OK(db_->UnlockWAL());
|
||||
|
||||
// The above `TEST_SwitchWAL()` triggered a flush. That flush needs to finish
|
||||
// before we make the filesystem inactive, otherwise the flush might hit an
|
||||
// unrecoverable error (e.g., failed MANIFEST update).
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(nullptr));
|
||||
|
||||
// Fail the WAL flush if applicable
|
||||
fault_fs->SetFilesystemActive(false);
|
||||
Status s = Put("key2", "value");
|
||||
|
||||
@@ -1578,7 +1578,7 @@ TEST_F(DBErrorHandlingFSTest, MultiDBCompactionError) {
|
||||
}
|
||||
|
||||
for (auto i = 0; i < kNumDbInstances; ++i) {
|
||||
Status s = static_cast<DBImpl*>(db[i])->TEST_WaitForCompact(true);
|
||||
Status s = static_cast<DBImpl*>(db[i])->TEST_WaitForCompact();
|
||||
ASSERT_EQ(s.severity(), Status::Severity::kSoftError);
|
||||
fault_fs[i]->SetFilesystemActive(true);
|
||||
}
|
||||
@@ -1587,7 +1587,7 @@ TEST_F(DBErrorHandlingFSTest, MultiDBCompactionError) {
|
||||
for (auto i = 0; i < kNumDbInstances; ++i) {
|
||||
std::string prop;
|
||||
ASSERT_EQ(listener[i]->WaitForRecovery(5000000), true);
|
||||
ASSERT_OK(static_cast<DBImpl*>(db[i])->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(static_cast<DBImpl*>(db[i])->TEST_WaitForCompact());
|
||||
EXPECT_TRUE(db[i]->GetProperty(
|
||||
"rocksdb.num-files-at-level" + std::to_string(0), &prop));
|
||||
EXPECT_EQ(atoi(prop.c_str()), 0);
|
||||
@@ -1701,7 +1701,7 @@ TEST_F(DBErrorHandlingFSTest, MultiDBVariousErrors) {
|
||||
}
|
||||
|
||||
for (auto i = 0; i < kNumDbInstances; ++i) {
|
||||
Status s = static_cast<DBImpl*>(db[i])->TEST_WaitForCompact(true);
|
||||
Status s = static_cast<DBImpl*>(db[i])->TEST_WaitForCompact();
|
||||
switch (i) {
|
||||
case 0:
|
||||
ASSERT_EQ(s.severity(), Status::Severity::kSoftError);
|
||||
@@ -1723,7 +1723,7 @@ TEST_F(DBErrorHandlingFSTest, MultiDBVariousErrors) {
|
||||
ASSERT_EQ(listener[i]->WaitForRecovery(5000000), true);
|
||||
}
|
||||
if (i == 1) {
|
||||
ASSERT_OK(static_cast<DBImpl*>(db[i])->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(static_cast<DBImpl*>(db[i])->TEST_WaitForCompact());
|
||||
}
|
||||
EXPECT_TRUE(db[i]->GetProperty(
|
||||
"rocksdb.num-files-at-level" + std::to_string(0), &prop));
|
||||
|
||||
+4
-2
@@ -38,6 +38,8 @@ Status UpdateManifestForFilesState(
|
||||
const DBOptions& db_opts, const std::string& db_name,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
const UpdateManifestForFilesStateOptions& opts) {
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
OfflineManifestWriter w(db_opts, db_name);
|
||||
Status s = w.Recover(column_families);
|
||||
|
||||
@@ -100,7 +102,7 @@ Status UpdateManifestForFilesState(
|
||||
lf->oldest_blob_file_number, lf->oldest_ancester_time,
|
||||
lf->file_creation_time, lf->epoch_number, lf->file_checksum,
|
||||
lf->file_checksum_func_name, lf->unique_id,
|
||||
lf->compensated_range_deletion_size);
|
||||
lf->compensated_range_deletion_size, lf->tail_size);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -114,7 +116,7 @@ Status UpdateManifestForFilesState(
|
||||
std::unique_ptr<FSDirectory> db_dir;
|
||||
s = fs->NewDirectory(db_name, IOOptions(), &db_dir, nullptr);
|
||||
if (s.ok()) {
|
||||
s = w.LogAndApply(cfd, &edit, db_dir.get());
|
||||
s = w.LogAndApply(read_options, cfd, &edit, db_dir.get());
|
||||
}
|
||||
if (s.ok()) {
|
||||
++cfs_updated;
|
||||
|
||||
@@ -102,7 +102,8 @@ class ExternalSSTFileBasicTest
|
||||
// all point operators, even though sst_file_writer.DeleteRange
|
||||
// must be called before other sst_file_writer methods. This is
|
||||
// because point writes take precedence over range deletions
|
||||
// in the same ingested sst.
|
||||
// in the same ingested sst. This precedence is part of
|
||||
// `SstFileWriter::DeleteRange()`'s API contract.
|
||||
std::string start_key = Key(range_deletions[i].first);
|
||||
std::string end_key = Key(range_deletions[i].second);
|
||||
s = sst_file_writer.DeleteRange(start_key, end_key);
|
||||
@@ -1418,6 +1419,7 @@ TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(options.num_levels - 1));
|
||||
VerifyDBFromMap(true_data);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, AdjacentRangeDeletionTombstones) {
|
||||
@@ -1462,6 +1464,89 @@ TEST_F(ExternalSSTFileBasicTest, AdjacentRangeDeletionTombstones) {
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, UnorderedRangeDeletions) {
|
||||
int kNumLevels = 7;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = kNumLevels;
|
||||
Reopen(options);
|
||||
|
||||
std::map<std::string, std::string> true_data;
|
||||
int file_id = 1;
|
||||
|
||||
// prevent range deletions from being dropped due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// Range del [0, 50) in memtable
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(0),
|
||||
Key(50)));
|
||||
|
||||
// Out of order range del overlaps memtable, so flush is required before file
|
||||
// is ingested into L0
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 90}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
{{65, 70}, {45, 50}}, file_id++, true /* write_global_seqno */,
|
||||
true /* verify_checksums_before_ingest */, &true_data));
|
||||
ASSERT_EQ(2, true_data.size());
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 1));
|
||||
VerifyDBFromMap(true_data);
|
||||
|
||||
// Compact to L6
|
||||
MoveFilesToLevel(kNumLevels - 1);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 1));
|
||||
VerifyDBFromMap(true_data);
|
||||
|
||||
// Ingest a file containing out of order range dels that cover nothing
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {151, 175}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
{{160, 200}, {120, 180}}, file_id++, true /* write_global_seqno */,
|
||||
true /* verify_checksums_before_ingest */, &true_data));
|
||||
ASSERT_EQ(4, true_data.size());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(kNumLevels - 1));
|
||||
VerifyDBFromMap(true_data);
|
||||
|
||||
// Ingest a file containing out of order range dels that cover keys in L6
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {}, {}, {{190, 200}, {170, 180}, {55, 65}}, file_id++,
|
||||
true /* write_global_seqno */, true /* verify_checksums_before_ingest */,
|
||||
&true_data));
|
||||
ASSERT_EQ(2, true_data.size());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(kNumLevels - 1));
|
||||
VerifyDBFromMap(true_data);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, RangeDeletionEndComesBeforeStart) {
|
||||
Options options = CurrentOptions();
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// "file.sst"
|
||||
// Verify attempt to delete 300 => 200 fails.
|
||||
// Then, verify attempt to delete 300 => 300 succeeds but writes nothing.
|
||||
// Afterwards, verify attempt to delete 300 => 400 works normally.
|
||||
std::string file = sst_files_dir_ + "file.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file));
|
||||
ASSERT_TRUE(
|
||||
sst_file_writer.DeleteRange(Key(300), Key(200)).IsInvalidArgument());
|
||||
ASSERT_OK(sst_file_writer.DeleteRange(Key(300), Key(300)));
|
||||
ASSERT_OK(sst_file_writer.DeleteRange(Key(300), Key(400)));
|
||||
ExternalSstFileInfo file_info;
|
||||
Status s = sst_file_writer.Finish(&file_info);
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file_info.file_path, file);
|
||||
ASSERT_EQ(file_info.num_entries, 0);
|
||||
ASSERT_EQ(file_info.smallest_key, "");
|
||||
ASSERT_EQ(file_info.largest_key, "");
|
||||
ASSERT_EQ(file_info.num_range_del_entries, 1);
|
||||
ASSERT_EQ(file_info.smallest_range_del_key, Key(300));
|
||||
ASSERT_EQ(file_info.largest_range_del_key, Key(400));
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileBasicTest, IngestFileWithBadBlockChecksum) {
|
||||
bool change_checksum_called = false;
|
||||
const auto& change_checksum = [&](void* arg) {
|
||||
|
||||
@@ -464,6 +464,16 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
current_time = oldest_ancester_time =
|
||||
static_cast<uint64_t>(temp_current_time);
|
||||
}
|
||||
uint64_t tail_size = 0;
|
||||
bool contain_no_data_blocks = f.table_properties.num_entries > 0 &&
|
||||
(f.table_properties.num_entries ==
|
||||
f.table_properties.num_range_deletions);
|
||||
if (f.table_properties.tail_start_offset > 0 || contain_no_data_blocks) {
|
||||
uint64_t file_size = f.fd.GetFileSize();
|
||||
assert(f.table_properties.tail_start_offset <= file_size);
|
||||
tail_size = file_size - f.table_properties.tail_start_offset;
|
||||
}
|
||||
|
||||
FileMetaData f_metadata(
|
||||
f.fd.GetNumber(), f.fd.GetPathId(), f.fd.GetFileSize(),
|
||||
f.smallest_internal_key, f.largest_internal_key, f.assigned_seqno,
|
||||
@@ -472,7 +482,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
ingestion_options_.ingest_behind
|
||||
? kReservedEpochNumberForFileIngestedBehind
|
||||
: cfd_->NewEpochNumber(),
|
||||
f.file_checksum, f.file_checksum_func_name, f.unique_id, 0);
|
||||
f.file_checksum, f.file_checksum_func_name, f.unique_id, 0, tail_size);
|
||||
f_metadata.temperature = f.file_temperature;
|
||||
edit_.AddFile(f.picked_level, f_metadata);
|
||||
}
|
||||
@@ -678,6 +688,7 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
TableReaderOptions(
|
||||
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
|
||||
env_options_, cfd_->internal_comparator(),
|
||||
sv->mutable_cf_options.block_protection_bytes_per_key,
|
||||
/*skip_filters*/ false, /*immortal*/ false,
|
||||
/*force_direct_prefetch*/ false, /*level*/ -1,
|
||||
/*block_cache_tracer*/ nullptr,
|
||||
@@ -692,6 +703,7 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
// If customized readahead size is needed, we can pass a user option
|
||||
// all the way to here. Right now we just rely on the default readahead
|
||||
// to keep things simple.
|
||||
// TODO: plumb Env::IOActivity
|
||||
ReadOptions ro;
|
||||
ro.readahead_size = ingestion_options_.verify_checksums_readahead_size;
|
||||
status = table_reader->VerifyChecksum(
|
||||
@@ -745,6 +757,7 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
file_to_ingest->num_range_deletions = props->num_range_deletions;
|
||||
|
||||
ParsedInternalKey key;
|
||||
// TODO: plumb Env::IOActivity
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<InternalIterator> iter(table_reader->NewIterator(
|
||||
ro, sv->mutable_cf_options.prefix_extractor.get(), /*arena=*/nullptr,
|
||||
@@ -855,6 +868,7 @@ Status ExternalSstFileIngestionJob::AssignLevelAndSeqnoForIngestedFile(
|
||||
|
||||
bool overlap_with_db = false;
|
||||
Arena arena;
|
||||
// TODO: plumb Env::IOActivity
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
int target_level = 0;
|
||||
@@ -1088,4 +1102,3 @@ Status ExternalSstFileIngestionJob::SyncIngestedFile(TWritableFile* file) {
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -1289,7 +1289,7 @@ TEST_F(ExternalSSTFileTest, IngestNonExistingFile) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// After full compaction, there should be only 1 file.
|
||||
std::vector<std::string> files;
|
||||
@@ -2857,4 +2857,3 @@ int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
Loaded 100 of 498 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user