mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffcfbaa584 | ||
|
|
f2cbed0cc4 | ||
|
|
e283b751a6 | ||
|
|
c15ee5a758 | ||
|
|
5f981e1145 | ||
|
|
732ab9a131 | ||
|
|
4bef82ad85 | ||
|
|
935ddf6469 | ||
|
|
2aed669e36 | ||
|
|
30a98ee159 | ||
|
|
4db32f3df9 | ||
|
|
a5f08293d8 | ||
|
|
ff1cc8a63e | ||
|
|
ca50ccc71a | ||
|
|
fbd2f563bb | ||
|
|
fb5748decf | ||
|
|
05a1d52e77 | ||
|
|
7521478b43 | ||
|
|
98c6d7fd80 | ||
|
|
2926e0718c | ||
|
|
022d89549d | ||
|
|
b3edb87341 | ||
|
|
17d5200504 | ||
|
|
1da9ac2363 | ||
|
|
66499780b2 | ||
|
|
b421a8c21b | ||
|
|
bc04ec85db | ||
|
|
253bc91953 | ||
|
|
9f774baaa8 | ||
|
|
fa878a0107 | ||
|
|
70bf5ef093 | ||
|
|
15e8a843d9 | ||
|
|
cac3240cbf | ||
|
|
e178f9e477 | ||
|
|
6041e50eba | ||
|
|
a2a90f8998 | ||
|
|
7c67aee4a0 | ||
|
|
05fcacdb42 | ||
|
|
77dda0d9d8 | ||
|
|
2b2994c8db | ||
|
|
3093d98c78 | ||
|
|
2e8cc98ab2 | ||
|
|
ddfcbea3e1 | ||
|
|
633c738a98 | ||
|
|
4dafa5b220 | ||
|
|
4aa52d89cf | ||
|
|
687a2a0d9a | ||
|
|
7a9b264f36 | ||
|
|
71ca9a1dcd | ||
|
|
e95cc1217d | ||
|
|
9f7877f246 | ||
|
|
9f1ce6d804 | ||
|
|
68a9cd21f2 | ||
|
|
87bc929db3 | ||
|
|
56ca9e3106 | ||
|
|
8848ec92dd | ||
|
|
e1c7209beb | ||
|
|
fcc358baf2 | ||
|
|
3e7fc88167 | ||
|
|
de1dd4ca19 | ||
|
|
23f4e9ad63 | ||
|
|
81aeb15988 | ||
|
|
d1ae7f6c41 | ||
|
|
dcc6fc99f9 | ||
|
|
e8710303d9 | ||
|
|
17bc27741f | ||
|
|
93e0715fad | ||
|
|
28bf7ba77d | ||
|
|
bf9e864235 | ||
|
|
68cc429be2 | ||
|
|
53e0b2fe6f | ||
|
|
11ebddb1d4 | ||
|
|
ffb5f1f445 | ||
|
|
5b945adf60 | ||
|
|
6eb3770b46 | ||
|
|
509116c53b |
No files matched your search
@@ -33,9 +33,7 @@ jobs:
|
||||
run: pip install argparse
|
||||
|
||||
- name: Download clang-format-diff.py
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
run: wget https://raw.githubusercontent.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
+2
-1
@@ -95,4 +95,5 @@ fuzz/crash-*
|
||||
|
||||
cmake-build-*
|
||||
third-party/folly/
|
||||
.cache
|
||||
.cache
|
||||
*.sublime-*
|
||||
+4
-1
@@ -547,7 +547,7 @@ if(HAVE_SCHED_GETCPU)
|
||||
add_definitions(-DROCKSDB_SCHED_GETCPU_PRESENT)
|
||||
endif()
|
||||
|
||||
check_cxx_symbol_exists(getauxval auvx.h HAVE_AUXV_GETAUXVAL)
|
||||
check_cxx_symbol_exists(getauxval "sys/auxv.h" HAVE_AUXV_GETAUXVAL)
|
||||
if(HAVE_AUXV_GETAUXVAL)
|
||||
add_definitions(-DROCKSDB_AUXV_GETAUXVAL_PRESENT)
|
||||
endif()
|
||||
@@ -865,6 +865,8 @@ set(SOURCES
|
||||
util/string_util.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
util/udt_util.cc
|
||||
util/write_batch_util.cc
|
||||
util/xxhash.cc
|
||||
utilities/agg_merge/agg_merge.cc
|
||||
utilities/backup/backup_engine.cc
|
||||
@@ -1421,6 +1423,7 @@ if(WITH_TESTS)
|
||||
util/timer_test.cc
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
util/udt_util_test.cc
|
||||
util/work_queue_test.cc
|
||||
utilities/agg_merge/agg_merge_test.cc
|
||||
utilities/backup/backup_engine_test.cc
|
||||
|
||||
+45
-1
@@ -1,5 +1,49 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 8.4.4 (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.4.3 (07/27/2023)
|
||||
### Bug Fixes
|
||||
* Fix use_after_free bug in async_io MultiReads when underlying FS enabled kFSBuffer. kFSBuffer is when underlying FS pass their own buffer instead of using RocksDB scratch in FSReadRequest.
|
||||
|
||||
## 8.4.0 (06/26/2023)
|
||||
### New Features
|
||||
* Add FSReadRequest::fs_scratch which is a data buffer allocated and provided by underlying FileSystem to RocksDB during reads, when FS wants to provide its own buffer with data instead of using RocksDB provided FSReadRequest::scratch. This can help in cpu optimization by avoiding copy from file system's buffer to RocksDB buffer. More details on how to use/enable it in file_system.h. Right now its supported only for MultiReads(async + sync) with non direct io.
|
||||
* Start logging non-zero user-defined timestamp sizes in WAL to signal user key format in subsequent records and use it during recovery. This change will break recovery from WAL files written by early versions that contain user-defined timestamps. The workaround is to ensure there are no WAL files to recover (i.e. by flushing before close) before upgrade.
|
||||
* Added new property "rocksdb.obsolete-sst-files-size-property" that reports the size of SST files that have become obsolete but have not yet been deleted or scheduled for deletion
|
||||
* Start to record the value of the flag `AdvancedColumnFamilyOptions.persist_user_defined_timestamps` in the Manifest and table properties for a SST file when it is created. And use the recorded flag when creating a table reader for the SST file. This flag is only explicitly record if it's false.
|
||||
* Add a new option OptimisticTransactionDBOptions::shared_lock_buckets that enables sharing mutexes for validating transactions between DB instances, for better balancing memory efficiency and validation contention across DB instances. Different column families and DBs also now use different hash seeds in this validation, so that the same set of key names will not contend across DBs or column families.
|
||||
* Add a new ticker `rocksdb.files.marked.trash.deleted` to track the number of trash files deleted by background thread from the trash queue.
|
||||
* Add an API NewTieredVolatileCache() in include/rocksdb/cache.h to allocate an instance of a block cache with a primary block cache tier and a compressed secondary cache tier. A cache of this type distributes memory reservations against the block cache, such as WriteBufferManager, table reader memory etc., proportionally across both the primary and compressed secondary cache.
|
||||
* Add `WaitForCompact()` to wait for all flush and compactions jobs to finish. Jobs to wait include the unscheduled (queued, but not scheduled yet).
|
||||
* Add `WriteBatch::Release()` that releases the batch's serialized data to the caller.
|
||||
|
||||
### Public API Changes
|
||||
* Add C API `rocksdb_options_add_compact_on_deletion_collector_factory_del_ratio`.
|
||||
* change the FileSystem::use_async_io() API to SupportedOps API in order to extend it to various operations supported by underlying FileSystem. Right now it contains FSSupportedOps::kAsyncIO and FSSupportedOps::kFSBuffer. More details about FSSupportedOps in filesystem.h
|
||||
* Add new tickers: `rocksdb.error.handler.bg.error.count`, `rocksdb.error.handler.bg.io.error.count`, `rocksdb.error.handler.bg.retryable.io.error.count` to replace the misspelled ones: `rocksdb.error.handler.bg.errro.count`, `rocksdb.error.handler.bg.io.errro.count`, `rocksdb.error.handler.bg.retryable.io.errro.count` ('error' instead of 'errro'). Users should switch to use the new tickers before 9.0 release as the misspelled old tickers will be completely removed then.
|
||||
* Overload the API CreateColumnFamilyWithImport() to support creating ColumnFamily by importing multiple ColumnFamilies It requires that CFs should not overlap in user key range.
|
||||
|
||||
### Behavior Changes
|
||||
* Change the default value for option `level_compaction_dynamic_level_bytes` to true. This affects users who use leveled compaction and do not set this option explicitly. These users may see additional background compactions following DB open. These compactions help to shape the LSM according to `level_compaction_dynamic_level_bytes` such that the size of each level Ln is approximately size of Ln-1 * `max_bytes_for_level_multiplier`. Turning on this option has other benefits too: see more detail in wiki: https://github.com/facebook/rocksdb/wiki/Leveled-Compaction#option-level_compaction_dynamic_level_bytes-and-levels-target-size and in option comment in advanced_options.h (#11525).
|
||||
* For Leveled Compaction users, `CompactRange()` will now always try to compact to the last non-empty level. (#11468)
|
||||
For Leveled Compaction users, `CompactRange()` with `bottommost_level_compaction = BottommostLevelCompaction::kIfHaveCompactionFilter` will behave similar to `kForceOptimized` in that it will skip files created during this manual compaction when compacting files in the bottommost level. (#11468)
|
||||
* RocksDB will try to drop range tombstones during non-bottommost compaction when it is safe to do so. (#11459)
|
||||
* When a DB is openend with `allow_ingest_behind=true` (currently only Universal compaction is supported), files in the last level, i.e. the ingested files, will not be included in any compaction. (#11489)
|
||||
* Statistics `rocksdb.sst.read.micros` scope is expanded to all SST reads except for file ingestion and column family import (some compaction reads were previously excluded).
|
||||
|
||||
### Bug Fixes
|
||||
* Reduced cases of illegally using Env::Default() during static destruction by never destroying the internal PosixEnv itself (except for builds checking for memory leaks). (#11538)
|
||||
* Fix extra prefetching during seek in async_io when BlockBasedTableOptions.num_file_reads_for_auto_readahead is 1 leading to extra reads than required.
|
||||
* Fix a bug where compactions that are qualified to be run as 2 subcompactions were only run as one subcompaction.
|
||||
* Fix a use-after-move bug in block.cc.
|
||||
|
||||
## 8.3.0 (05/19/2023)
|
||||
### New Features
|
||||
* Introduced a new option `block_protection_bytes_per_key`, which can be used to enable per key-value integrity protection for in-memory blocks in block cache (#11287).
|
||||
* Added `JemallocAllocatorOptions::num_arenas`. Setting `num_arenas > 1` may mitigate mutex contention in the allocator, particularly in scenarios where block allocations commonly bypass jemalloc tcache.
|
||||
|
||||
@@ -1417,6 +1417,9 @@ thread_local_test: $(OBJ_DIR)/util/thread_local_test.o $(TEST_LIBRARY) $(LIBRARY
|
||||
work_queue_test: $(OBJ_DIR)/util/work_queue_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
udt_util_test: $(OBJ_DIR)/util/udt_util_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
corruption_test: $(OBJ_DIR)/db/corruption_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
|
||||
@@ -264,6 +264,8 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"util/string_util.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/udt_util.cc",
|
||||
"util/write_batch_util.cc",
|
||||
"util/xxhash.cc",
|
||||
"utilities/agg_merge/agg_merge.cc",
|
||||
"utilities/backup/backup_engine.cc",
|
||||
@@ -5508,6 +5510,12 @@ cpp_unittest_wrapper(name="ttl_test",
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="udt_util_test",
|
||||
srcs=["util/udt_util_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_compiler_flags=[])
|
||||
|
||||
|
||||
cpp_unittest_wrapper(name="util_merge_operators_test",
|
||||
srcs=["utilities/util_merge_operators_test.cc"],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
|
||||
@@ -419,7 +419,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_JEMALLOC; then
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -ljemalloc \
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -ljemalloc \
|
||||
2>/dev/null; then
|
||||
# This will enable some preprocessor identifiers in the Makefile
|
||||
JEMALLOC=1
|
||||
@@ -428,12 +428,19 @@ EOF
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
# check for JEMALLOC installed with HomeBrew
|
||||
if [ "$PLATFORM" == "OS_MACOSX" ]; then
|
||||
if [ "$TARGET_ARCHITECTURE" = "arm64" ]; then
|
||||
# on M1 Macs, homebrew installs here instead of /usr/local
|
||||
JEMALLOC_PREFIX="/opt/homebrew"
|
||||
else
|
||||
JEMALLOC_PREFIX="/usr/local"
|
||||
fi
|
||||
if hash brew 2>/dev/null && brew ls --versions jemalloc > /dev/null; then
|
||||
JEMALLOC_VER=$(brew ls --versions jemalloc | tail -n 1 | cut -f 2 -d ' ')
|
||||
JEMALLOC_INCLUDE="-I/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS $JEMALLOC_LIB"
|
||||
JEMALLOC_INCLUDE="-I${JEMALLOC_PREFIX}/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="${JEMALLOC_PREFIX}/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
Vendored
+16
-2
@@ -18,9 +18,15 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
const CompressedSecondaryCacheOptions& opts)
|
||||
: cache_(opts.LRUCacheOptions::MakeSharedCache()), cache_options_(opts) {}
|
||||
: cache_(opts.LRUCacheOptions::MakeSharedCache()),
|
||||
cache_options_(opts),
|
||||
cache_res_mgr_(std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_))) {}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() { cache_.reset(); }
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() {
|
||||
assert(cache_res_mgr_->GetTotalReservedCacheSize() == 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
const Slice& key, const Cache::CacheItemHelper* helper,
|
||||
@@ -301,4 +307,12 @@ CompressedSecondaryCacheOptions::MakeSharedSecondaryCache() const {
|
||||
return std::make_shared<CompressedSecondaryCache>(*this);
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::Deflate(size_t decrease) {
|
||||
return cache_res_mgr_->UpdateCacheReservation(decrease, /*increase=*/true);
|
||||
}
|
||||
|
||||
Status CompressedSecondaryCache::Inflate(size_t increase) {
|
||||
return cache_res_mgr_->UpdateCacheReservation(increase, /*increase=*/false);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+8
@@ -9,6 +9,7 @@
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "memory/memory_allocator_impl.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
@@ -93,8 +94,14 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
|
||||
Status GetCapacity(size_t& capacity) override;
|
||||
|
||||
Status Deflate(size_t decrease) override;
|
||||
|
||||
Status Inflate(size_t increase) override;
|
||||
|
||||
std::string GetPrintableOptions() const override;
|
||||
|
||||
size_t TEST_GetUsage() { return cache_->GetUsage(); }
|
||||
|
||||
private:
|
||||
friend class CompressedSecondaryCacheTestBase;
|
||||
static constexpr std::array<uint16_t, 8> malloc_bin_sizes_{
|
||||
@@ -127,6 +134,7 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
std::shared_ptr<Cache> cache_;
|
||||
CompressedSecondaryCacheOptions cache_options_;
|
||||
mutable port::Mutex capacity_mutex_;
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+93
@@ -10,11 +10,13 @@
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
#include "memory/jemalloc_nodump_allocator.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "test_util/secondary_cache_test_util.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -974,6 +976,97 @@ TEST_P(CompressedSecondaryCacheTest, SplictValueAndMergeChunksTest) {
|
||||
SplictValueAndMergeChunksTest();
|
||||
}
|
||||
|
||||
class CompressedSecCacheTestWithTiered : public ::testing::Test {
|
||||
public:
|
||||
CompressedSecCacheTestWithTiered() {
|
||||
LRUCacheOptions lru_opts;
|
||||
TieredVolatileCacheOptions opts;
|
||||
lru_opts.capacity = 70 << 20;
|
||||
opts.cache_opts = &lru_opts;
|
||||
opts.cache_type = PrimaryCacheType::kCacheTypeLRU;
|
||||
opts.comp_cache_opts.capacity = 30 << 20;
|
||||
cache_ = NewTieredVolatileCache(opts);
|
||||
cache_res_mgr_ =
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_);
|
||||
}
|
||||
|
||||
protected:
|
||||
CacheReservationManager* cache_res_mgr() { return cache_res_mgr_.get(); }
|
||||
|
||||
Cache* GetCache() {
|
||||
return static_cast_with_check<CacheWithSecondaryAdapter, Cache>(
|
||||
cache_.get())
|
||||
->TEST_GetCache();
|
||||
}
|
||||
|
||||
SecondaryCache* GetSecondaryCache() {
|
||||
return static_cast_with_check<CacheWithSecondaryAdapter, Cache>(
|
||||
cache_.get())
|
||||
->TEST_GetSecondaryCache();
|
||||
}
|
||||
|
||||
size_t GetPercent(size_t val, unsigned int percent) {
|
||||
return static_cast<size_t>(val * percent / 100);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr_;
|
||||
};
|
||||
|
||||
bool CacheUsageWithinBounds(size_t val1, size_t val2, size_t error) {
|
||||
return ((val1 < (val2 + error)) && (val1 > (val2 - error)));
|
||||
}
|
||||
|
||||
TEST_F(CompressedSecCacheTestWithTiered, CacheReservationManager) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
// Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
|
||||
// double explicit casts
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(10 << 20));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
|
||||
GetPercent(37 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(0));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
}
|
||||
|
||||
TEST_F(CompressedSecCacheTestWithTiered,
|
||||
CacheReservationManagerMultipleUpdate) {
|
||||
CompressedSecondaryCache* sec_cache =
|
||||
reinterpret_cast<CompressedSecondaryCache*>(GetSecondaryCache());
|
||||
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation((1 + i) << 20));
|
||||
}
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
|
||||
GetPercent(37 << 20, 1));
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
|
||||
GetPercent(3 << 20, 1));
|
||||
|
||||
for (i = 10; i > 0; --i) {
|
||||
ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(((i - 1) << 20)));
|
||||
}
|
||||
EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
|
||||
GetPercent(30 << 20, 1));
|
||||
EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
+140
-2
@@ -6,6 +6,7 @@
|
||||
#include "cache/secondary_cache_adapter.h"
|
||||
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -18,20 +19,98 @@ const Dummy kDummy{};
|
||||
Cache::ObjectPtr const kDummyObj = const_cast<Dummy*>(&kDummy);
|
||||
} // namespace
|
||||
|
||||
// When CacheWithSecondaryAdapter is constructed with the distribute_cache_res
|
||||
// parameter set to true, it manages the entire memory budget across the
|
||||
// primary and secondary cache. The secondary cache is assumed to be in
|
||||
// memory, such as the CompressedSecondaryCache. When a placeholder entry
|
||||
// is inserted by a CacheReservationManager instance to reserve memory,
|
||||
// the CacheWithSecondaryAdapter ensures that the reservation is distributed
|
||||
// proportionally across the primary/secondary caches.
|
||||
//
|
||||
// The primary block cache is initially sized to the sum of the primary cache
|
||||
// budget + teh secondary cache budget, as follows -
|
||||
// |--------- Primary Cache Configured Capacity -----------|
|
||||
// |---Secondary Cache Budget----|----Primary Cache Budget-----|
|
||||
//
|
||||
// A ConcurrentCacheReservationManager member in the CacheWithSecondaryAdapter,
|
||||
// pri_cache_res_,
|
||||
// is used to help with tracking the distribution of memory reservations.
|
||||
// Initially, it accounts for the entire secondary cache budget as a
|
||||
// reservation against the primary cache. This shrinks the usable capacity of
|
||||
// the primary cache to the budget that the user originally desired.
|
||||
//
|
||||
// |--Reservation for Sec Cache--|-Pri Cache Usable Capacity---|
|
||||
//
|
||||
// When a reservation placeholder is inserted into the adapter, it is inserted
|
||||
// directly into the primary cache. This means the entire charge of the
|
||||
// placeholder is counted against the primary cache. To compensate and count
|
||||
// a portion of it against the secondary cache, the secondary cache Deflate()
|
||||
// method is called to shrink it. Since the Deflate() causes the secondary
|
||||
// actual usage to shrink, it is refelcted here by releasing an equal amount
|
||||
// from the pri_cache_res_ reservation. The Deflate() in the secondary cache
|
||||
// can be, but is not required to be, implemented using its own cache
|
||||
// reservation manager.
|
||||
//
|
||||
// For example, if the pri/sec ratio is 70/30, and the combined capacity is
|
||||
// 100MB, the intermediate and final state after inserting a reservation
|
||||
// placeholder for 10MB would be as follows -
|
||||
//
|
||||
// |-Reservation for Sec Cache-|-Pri Cache Usable Capacity-|---R---|
|
||||
// 1. After inserting the placeholder in primary
|
||||
// |------- 30MB -------------|------- 60MB -------------|-10MB--|
|
||||
// 2. After deflating the secondary and adjusting the reservation for
|
||||
// secondary against the primary
|
||||
// |------- 27MB -------------|------- 63MB -------------|-10MB--|
|
||||
//
|
||||
// Likewise, when the user inserted placeholder is released, the secondary
|
||||
// cache Inflate() method is called to grow it, and the pri_cache_res_
|
||||
// reservation is increased by an equal amount.
|
||||
//
|
||||
// Another way of implementing this would have been to simply split the user
|
||||
// reservation into primary and seconary components. However, this would
|
||||
// require allocating a structure to track the associated secondary cache
|
||||
// reservation, which adds some complexity and overhead.
|
||||
//
|
||||
CacheWithSecondaryAdapter::CacheWithSecondaryAdapter(
|
||||
std::shared_ptr<Cache> target,
|
||||
std::shared_ptr<SecondaryCache> secondary_cache)
|
||||
std::shared_ptr<SecondaryCache> secondary_cache, bool distribute_cache_res)
|
||||
: CacheWrapper(std::move(target)),
|
||||
secondary_cache_(std::move(secondary_cache)) {
|
||||
secondary_cache_(std::move(secondary_cache)),
|
||||
distribute_cache_res_(distribute_cache_res) {
|
||||
target_->SetEvictionCallback([this](const Slice& key, Handle* handle) {
|
||||
return EvictionHandler(key, handle);
|
||||
});
|
||||
if (distribute_cache_res_) {
|
||||
size_t sec_capacity = 0;
|
||||
pri_cache_res_ = std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
target_));
|
||||
Status s = secondary_cache_->GetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
// Initially, the primary cache is sized to uncompressed cache budget plsu
|
||||
// compressed secondary cache budget. The secondary cache budget is then
|
||||
// taken away from the primary cache through cache reservations. Later,
|
||||
// when a placeholder entry is inserted by the caller, its inserted
|
||||
// into the primary cache and the portion that should be assigned to the
|
||||
// secondary cache is freed from the reservation.
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_capacity);
|
||||
assert(s.ok());
|
||||
sec_cache_res_ratio_ = (double)sec_capacity / target_->GetCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
CacheWithSecondaryAdapter::~CacheWithSecondaryAdapter() {
|
||||
// `*this` will be destroyed before `*target_`, so we have to prevent
|
||||
// use after free
|
||||
target_->SetEvictionCallback({});
|
||||
#ifndef NDEBUG
|
||||
if (distribute_cache_res_) {
|
||||
size_t sec_capacity = 0;
|
||||
Status s = secondary_cache_->GetCapacity(sec_capacity);
|
||||
assert(s.ok());
|
||||
assert(pri_cache_res_->GetTotalReservedCacheSize() == sec_capacity);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
bool CacheWithSecondaryAdapter::EvictionHandler(const Slice& key,
|
||||
@@ -136,6 +215,22 @@ Cache::Handle* CacheWithSecondaryAdapter::Promote(
|
||||
return result;
|
||||
}
|
||||
|
||||
Status CacheWithSecondaryAdapter::Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper,
|
||||
size_t charge, Handle** handle,
|
||||
Priority priority) {
|
||||
Status s = target_->Insert(key, value, helper, charge, handle, priority);
|
||||
if (s.ok() && value == nullptr && distribute_cache_res_) {
|
||||
size_t sec_charge = static_cast<size_t>(charge * (sec_cache_res_ratio_));
|
||||
s = secondary_cache_->Deflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge, /*increase=*/false);
|
||||
assert(s.ok());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Cache::Handle* CacheWithSecondaryAdapter::Lookup(const Slice& key,
|
||||
const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
@@ -162,6 +257,22 @@ Cache::Handle* CacheWithSecondaryAdapter::Lookup(const Slice& key,
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CacheWithSecondaryAdapter::Release(Handle* handle,
|
||||
bool erase_if_last_ref) {
|
||||
if (erase_if_last_ref) {
|
||||
ObjectPtr v = target_->Value(handle);
|
||||
if (v == nullptr && distribute_cache_res_) {
|
||||
size_t charge = target_->GetCharge(handle);
|
||||
size_t sec_charge = static_cast<size_t>(charge * (sec_cache_res_ratio_));
|
||||
Status s = secondary_cache_->Inflate(sec_charge);
|
||||
assert(s.ok());
|
||||
s = pri_cache_res_->UpdateCacheReservation(sec_charge, /*increase=*/true);
|
||||
assert(s.ok());
|
||||
}
|
||||
}
|
||||
return target_->Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
Cache::ObjectPtr CacheWithSecondaryAdapter::Value(Handle* handle) {
|
||||
ObjectPtr v = target_->Value(handle);
|
||||
// TODO with stacked secondaries: might fail in EvictionHandler
|
||||
@@ -292,4 +403,31 @@ const char* CacheWithSecondaryAdapter::Name() const {
|
||||
// a secondary cache. So we pretend to be that cache
|
||||
return target_->Name();
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewTieredVolatileCache(
|
||||
TieredVolatileCacheOptions& opts) {
|
||||
if (!opts.cache_opts) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> cache;
|
||||
if (opts.cache_type == PrimaryCacheType::kCacheTypeLRU) {
|
||||
LRUCacheOptions cache_opts =
|
||||
*(static_cast_with_check<LRUCacheOptions, ShardedCacheOptions>(
|
||||
opts.cache_opts));
|
||||
cache_opts.capacity += opts.comp_cache_opts.capacity;
|
||||
cache = cache_opts.MakeSharedCache();
|
||||
} else if (opts.cache_type == PrimaryCacheType::kCacheTypeHCC) {
|
||||
HyperClockCacheOptions cache_opts =
|
||||
*(static_cast_with_check<HyperClockCacheOptions, ShardedCacheOptions>(
|
||||
opts.cache_opts));
|
||||
cache = cache_opts.MakeSharedCache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<SecondaryCache> sec_cache;
|
||||
sec_cache = NewCompressedSecondaryCache(opts.comp_cache_opts);
|
||||
|
||||
return std::make_shared<CacheWithSecondaryAdapter>(cache, sec_cache, true);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+25
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -13,15 +14,24 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
public:
|
||||
explicit CacheWithSecondaryAdapter(
|
||||
std::shared_ptr<Cache> target,
|
||||
std::shared_ptr<SecondaryCache> secondary_cache);
|
||||
std::shared_ptr<SecondaryCache> secondary_cache,
|
||||
bool distribute_cache_res = false);
|
||||
|
||||
~CacheWithSecondaryAdapter() override;
|
||||
|
||||
Status Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override;
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
Priority priority = Priority::LOW,
|
||||
Statistics* stats = nullptr) override;
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override;
|
||||
|
||||
ObjectPtr Value(Handle* handle) override;
|
||||
|
||||
void StartAsyncLookup(AsyncLookupHandle& async_handle) override;
|
||||
@@ -32,6 +42,10 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
|
||||
const char* Name() const override;
|
||||
|
||||
Cache* TEST_GetCache() { return target_.get(); }
|
||||
|
||||
SecondaryCache* TEST_GetSecondaryCache() { return secondary_cache_.get(); }
|
||||
|
||||
private:
|
||||
bool EvictionHandler(const Slice& key, Handle* handle);
|
||||
|
||||
@@ -47,6 +61,16 @@ class CacheWithSecondaryAdapter : public CacheWrapper {
|
||||
void CleanupCacheObject(ObjectPtr obj, const CacheItemHelper* helper);
|
||||
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
// Whether to proportionally distribute cache memory reservations, i.e
|
||||
// placeholder entries with null value and a non-zero charge, across
|
||||
// the primary and secondary caches.
|
||||
bool distribute_cache_res_;
|
||||
// A cache reservation manager to keep track of secondary cache memory
|
||||
// usage by reserving equivalent capacity against the primary cache
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> pri_cache_res_;
|
||||
// Fraction of a cache memory reservation to be assigned to the secondary
|
||||
// cache
|
||||
double sec_cache_res_ratio_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -21,6 +21,8 @@ CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --
|
||||
blackbox_crash_test_with_multiops_wp_txn \
|
||||
crash_test_with_tiered_storage blackbox_crash_test_with_tiered_storage \
|
||||
whitebox_crash_test_with_tiered_storage \
|
||||
whitebox_crash_test_with_optimistic_txn \
|
||||
blackbox_crash_test_with_optimistic_txn \
|
||||
|
||||
crash_test: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
@@ -37,6 +39,11 @@ crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_optimistic_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_optimistic_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
@@ -80,6 +87,9 @@ blackbox_crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
|
||||
blackbox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_tiered_storage blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --optimistic_txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
@@ -105,3 +115,7 @@ whitebox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
whitebox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_tiered_storage whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --optimistic_txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -47,7 +47,9 @@ void ArenaWrappedDBIter::Init(
|
||||
read_options_ = read_options;
|
||||
allow_refresh_ = allow_refresh;
|
||||
memtable_range_tombstone_iter_ = nullptr;
|
||||
if (!env->GetFileSystem()->use_async_io()) {
|
||||
|
||||
if (!CheckFSFeatureSupport(env->GetFileSystem().get(),
|
||||
FSSupportedOps::kAsyncIO)) {
|
||||
read_options_.async_io = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ BlobFileCache::BlobFileCache(Cache* cache,
|
||||
HistogramImpl* blob_file_read_hist,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: cache_(cache),
|
||||
mutex_(kNumberOfMutexStripes, kGetSliceNPHash64UnseededFnPtr),
|
||||
mutex_(kNumberOfMutexStripes),
|
||||
immutable_options_(immutable_options),
|
||||
file_options_(file_options),
|
||||
column_family_id_(column_family_id),
|
||||
@@ -55,7 +55,7 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
TEST_SYNC_POINT("BlobFileCache::GetBlobFileReader:DoubleCheck");
|
||||
|
||||
// Check again while holding mutex
|
||||
MutexLock lock(mutex_.get(key));
|
||||
MutexLock lock(&mutex_.Get(key));
|
||||
|
||||
handle = cache_.Lookup(key);
|
||||
if (handle) {
|
||||
|
||||
@@ -43,7 +43,7 @@ class BlobFileCache {
|
||||
CacheInterface cache_;
|
||||
// Note: mutex_ below is used to guard against multiple threads racing to open
|
||||
// the same file.
|
||||
Striped<port::Mutex, Slice> mutex_;
|
||||
Striped<CacheAlignedWrapper<port::Mutex>> mutex_;
|
||||
const ImmutableOptions* immutable_options_;
|
||||
const FileOptions* file_options_;
|
||||
uint32_t column_family_id_;
|
||||
|
||||
@@ -439,11 +439,11 @@ void BlobFileReader::MultiGetBlob(
|
||||
assert(req->offset >= adjustment);
|
||||
adjustments.push_back(adjustment);
|
||||
|
||||
FSReadRequest read_req = {};
|
||||
FSReadRequest read_req;
|
||||
read_req.offset = req->offset - adjustment;
|
||||
read_req.len = req->len + adjustment;
|
||||
read_reqs.emplace_back(read_req);
|
||||
total_len += read_req.len;
|
||||
read_reqs.emplace_back(std::move(read_req));
|
||||
}
|
||||
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_READ, total_len);
|
||||
|
||||
@@ -293,6 +293,8 @@ Status BuildTable(
|
||||
meta->fd.file_size = file_size;
|
||||
meta->tail_size = builder->GetTailSize();
|
||||
meta->marked_for_compaction = builder->NeedCompact();
|
||||
meta->user_defined_timestamps_persisted =
|
||||
ioptions.persist_user_defined_timestamps;
|
||||
assert(meta->fd.GetFileSize() > 0);
|
||||
tp = builder
|
||||
->GetTableProperties(); // refresh now that builder is finished
|
||||
|
||||
@@ -3888,6 +3888,15 @@ void rocksdb_options_add_compact_on_deletion_collector_factory(
|
||||
opt->rep.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
}
|
||||
|
||||
void rocksdb_options_add_compact_on_deletion_collector_factory_del_ratio(
|
||||
rocksdb_options_t* opt, size_t window_size, size_t num_dels_trigger,
|
||||
double deletion_ratio) {
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::TablePropertiesCollectorFactory>
|
||||
compact_on_del = NewCompactOnDeletionCollectorFactory(
|
||||
window_size, num_dels_trigger, deletion_ratio);
|
||||
opt->rep.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
}
|
||||
|
||||
void rocksdb_set_perf_level(int v) {
|
||||
PerfLevel level = static_cast<PerfLevel>(v);
|
||||
SetPerfLevel(level);
|
||||
|
||||
@@ -721,6 +721,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
rocksdb_options_add_compact_on_deletion_collector_factory(options, 10000,
|
||||
10001);
|
||||
rocksdb_options_add_compact_on_deletion_collector_factory_del_ratio(
|
||||
options, 10000, 10001, 0.0);
|
||||
|
||||
StartPhase("destroy");
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
|
||||
+13
-22
@@ -1245,30 +1245,11 @@ SuperVersion* ColumnFamilyData::GetThreadLocalSuperVersion(DBImpl* db) {
|
||||
// (if no Scrape happens).
|
||||
assert(ptr != SuperVersion::kSVInUse);
|
||||
SuperVersion* sv = static_cast<SuperVersion*>(ptr);
|
||||
if (sv == SuperVersion::kSVObsolete ||
|
||||
sv->version_number != super_version_number_.load()) {
|
||||
if (sv == SuperVersion::kSVObsolete) {
|
||||
RecordTick(ioptions_.stats, NUMBER_SUPERVERSION_ACQUIRES);
|
||||
SuperVersion* sv_to_delete = nullptr;
|
||||
|
||||
if (sv && sv->Unref()) {
|
||||
RecordTick(ioptions_.stats, NUMBER_SUPERVERSION_CLEANUPS);
|
||||
db->mutex()->Lock();
|
||||
// NOTE: underlying resources held by superversion (sst files) might
|
||||
// not be released until the next background job.
|
||||
sv->Cleanup();
|
||||
if (db->immutable_db_options().avoid_unnecessary_blocking_io) {
|
||||
db->AddSuperVersionsToFreeQueue(sv);
|
||||
db->SchedulePurge();
|
||||
} else {
|
||||
sv_to_delete = sv;
|
||||
}
|
||||
} else {
|
||||
db->mutex()->Lock();
|
||||
}
|
||||
db->mutex()->Lock();
|
||||
sv = super_version_->Ref();
|
||||
db->mutex()->Unlock();
|
||||
|
||||
delete sv_to_delete;
|
||||
}
|
||||
assert(sv != nullptr);
|
||||
return sv;
|
||||
@@ -1640,6 +1621,13 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
db_id_, db_session_id_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
auto ucmp = new_cfd->user_comparator();
|
||||
assert(ucmp);
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
running_ts_sz_.insert({id, ts_sz});
|
||||
if (ts_sz > 0) {
|
||||
ts_sz_for_record_.insert({id, ts_sz});
|
||||
}
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
// add to linked list
|
||||
new_cfd->next_ = dummy_cfd_;
|
||||
@@ -1655,10 +1643,13 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
|
||||
// under a DB mutex AND from a write thread
|
||||
void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
|
||||
auto cfd_iter = column_family_data_.find(cfd->GetID());
|
||||
uint32_t cf_id = cfd->GetID();
|
||||
auto cfd_iter = column_family_data_.find(cf_id);
|
||||
assert(cfd_iter != column_family_data_.end());
|
||||
column_family_data_.erase(cfd_iter);
|
||||
column_families_.erase(cfd->GetName());
|
||||
running_ts_sz_.erase(cf_id);
|
||||
ts_sz_for_record_.erase(cf_id);
|
||||
}
|
||||
|
||||
// under a DB mutex OR from a write thread
|
||||
|
||||
@@ -705,6 +705,16 @@ class ColumnFamilySet {
|
||||
Version* dummy_version,
|
||||
const ColumnFamilyOptions& options);
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& GetRunningColumnFamiliesTimestampSize()
|
||||
const {
|
||||
return running_ts_sz_;
|
||||
}
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>&
|
||||
GetColumnFamiliesTimestampSizeForRecord() const {
|
||||
return ts_sz_for_record_;
|
||||
}
|
||||
|
||||
iterator begin() { return iterator(dummy_cfd_->next_); }
|
||||
iterator end() { return iterator(dummy_cfd_); }
|
||||
|
||||
@@ -730,6 +740,15 @@ class ColumnFamilySet {
|
||||
UnorderedMap<std::string, uint32_t> column_families_;
|
||||
UnorderedMap<uint32_t, ColumnFamilyData*> column_family_data_;
|
||||
|
||||
// Mutating / reading `running_ts_sz_` and `ts_sz_for_record_` follow
|
||||
// the same requirements as `column_families_` and `column_family_data_`.
|
||||
// Mapping from column family id to user-defined timestamp size for all
|
||||
// running column families.
|
||||
UnorderedMap<uint32_t, size_t> running_ts_sz_;
|
||||
// Mapping from column family id to user-defined timestamp size for
|
||||
// column families with non-zero user-defined timestamp size.
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_for_record_;
|
||||
|
||||
uint32_t max_column_family_;
|
||||
const FileOptions file_options_;
|
||||
|
||||
|
||||
@@ -1255,6 +1255,7 @@ TEST_P(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
@@ -1272,6 +1273,7 @@ TEST_P(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
two.level_compaction_dynamic_level_bytes = false;
|
||||
two.compaction_style = kCompactionStyleLevel;
|
||||
two.num_levels = 4;
|
||||
two.level0_file_num_compaction_trigger = 3;
|
||||
@@ -1326,6 +1328,7 @@ TEST_P(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
@@ -1342,6 +1345,7 @@ TEST_P(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
two.level_compaction_dynamic_level_bytes = false;
|
||||
two.compaction_style = kCompactionStyleLevel;
|
||||
two.num_levels = 4;
|
||||
two.level0_file_num_compaction_trigger = 3;
|
||||
@@ -1424,13 +1428,14 @@ TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.max_compaction_bytes = default_cf.target_file_size_base * 1100;
|
||||
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||
;
|
||||
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
@@ -1441,6 +1446,7 @@ TEST_P(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
two.level_compaction_dynamic_level_bytes = false;
|
||||
two.compaction_style = kCompactionStyleLevel;
|
||||
two.num_levels = 4;
|
||||
two.level0_file_num_compaction_trigger = 3;
|
||||
@@ -1519,13 +1525,14 @@ TEST_P(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.level_compaction_dynamic_level_bytes = false;
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.max_compaction_bytes = default_cf.target_file_size_base * 1100;
|
||||
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||
;
|
||||
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
@@ -1536,6 +1543,7 @@ TEST_P(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
two.level_compaction_dynamic_level_bytes = false;
|
||||
two.compaction_style = kCompactionStyleLevel;
|
||||
two.num_levels = 4;
|
||||
two.level0_file_num_compaction_trigger = 3;
|
||||
|
||||
@@ -66,6 +66,7 @@ TEST_F(CompactFilesTest, L0ConflictsFiles) {
|
||||
const int kWriteBufferSize = 10000;
|
||||
const int kLevel0Trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
// Small slowdown and stop trigger for experimental purpose.
|
||||
options.level0_slowdown_writes_trigger = 20;
|
||||
@@ -359,6 +360,7 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
std::shared_ptr<FilterWithGet> cf(new FilterWithGet());
|
||||
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_filter = cf.get();
|
||||
|
||||
@@ -401,6 +403,7 @@ TEST_F(CompactFilesTest, SentinelCompressionType) {
|
||||
CompactionStyle::kCompactionStyleNone}) {
|
||||
ASSERT_OK(DestroyDB(db_name_, Options()));
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.compaction_style = compaction_style;
|
||||
// L0: Snappy, L1: ZSTD, L2: Snappy
|
||||
options.compression_per_level = {CompressionType::kSnappyCompression,
|
||||
|
||||
+60
-18
@@ -486,26 +486,25 @@ bool Compaction::IsTrivialMove() const {
|
||||
|
||||
// assert inputs_.size() == 1
|
||||
|
||||
std::unique_ptr<SstPartitioner> partitioner = CreateSstPartitioner();
|
||||
|
||||
for (const auto& file : inputs_.front().files) {
|
||||
std::vector<FileMetaData*> file_grand_parents;
|
||||
if (output_level_ + 1 >= number_levels_) {
|
||||
continue;
|
||||
}
|
||||
input_vstorage_->GetOverlappingInputs(output_level_ + 1, &file->smallest,
|
||||
&file->largest, &file_grand_parents);
|
||||
const auto compaction_size =
|
||||
file->fd.GetFileSize() + TotalFileSize(file_grand_parents);
|
||||
if (compaction_size > max_compaction_bytes_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (partitioner.get() != nullptr) {
|
||||
if (!partitioner->CanDoTrivialMove(file->smallest.user_key(),
|
||||
file->largest.user_key())) {
|
||||
if (output_level_ + 1 < number_levels_) {
|
||||
std::unique_ptr<SstPartitioner> partitioner = CreateSstPartitioner();
|
||||
for (const auto& file : inputs_.front().files) {
|
||||
std::vector<FileMetaData*> file_grand_parents;
|
||||
input_vstorage_->GetOverlappingInputs(output_level_ + 1, &file->smallest,
|
||||
&file->largest,
|
||||
&file_grand_parents);
|
||||
const auto compaction_size =
|
||||
file->fd.GetFileSize() + TotalFileSize(file_grand_parents);
|
||||
if (compaction_size > max_compaction_bytes_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (partitioner.get() != nullptr) {
|
||||
if (!partitioner->CanDoTrivialMove(file->smallest.user_key(),
|
||||
file->largest.user_key())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,6 +562,49 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Compaction::KeyRangeNotExistsBeyondOutputLevel(
|
||||
const Slice& begin_key, const Slice& end_key,
|
||||
std::vector<size_t>* level_ptrs) const {
|
||||
assert(input_version_ != nullptr);
|
||||
assert(level_ptrs != nullptr);
|
||||
assert(level_ptrs->size() == static_cast<size_t>(number_levels_));
|
||||
assert(cfd_->user_comparator()->CompareWithoutTimestamp(begin_key, end_key) <
|
||||
0);
|
||||
if (bottommost_level_) {
|
||||
return true /* does not overlap */;
|
||||
} else if (output_level_ != 0 &&
|
||||
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
const std::vector<FileMetaData*>& files =
|
||||
input_vstorage_->LevelFiles(lvl);
|
||||
for (; level_ptrs->at(lvl) < files.size(); level_ptrs->at(lvl)++) {
|
||||
auto* f = files[level_ptrs->at(lvl)];
|
||||
// Advance until the first file with begin_key <= f->largest.user_key()
|
||||
if (user_cmp->CompareWithoutTimestamp(begin_key,
|
||||
f->largest.user_key()) > 0) {
|
||||
continue;
|
||||
}
|
||||
// We know that the previous file prev_f, if exists, has
|
||||
// prev_f->largest.user_key() < begin_key.
|
||||
if (user_cmp->CompareWithoutTimestamp(end_key,
|
||||
f->smallest.user_key()) <= 0) {
|
||||
// not overlapping with this level
|
||||
break;
|
||||
} else {
|
||||
// We have:
|
||||
// - begin_key < end_key,
|
||||
// - begin_key <= f->largest.user_key(), and
|
||||
// - end_key > f->smallest.user_key()
|
||||
return false /* overlap */;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true /* does not overlap */;
|
||||
}
|
||||
return false /* overlaps */;
|
||||
};
|
||||
|
||||
// Mark (or clear) each file that is being compacted
|
||||
void Compaction::MarkFilesBeingCompacted(bool mark_as_compacted) {
|
||||
for (size_t i = 0; i < num_input_levels(); i++) {
|
||||
|
||||
@@ -203,10 +203,18 @@ class Compaction {
|
||||
void AddInputDeletions(VersionEdit* edit);
|
||||
|
||||
// Returns true if the available information we have guarantees that
|
||||
// the input "user_key" does not exist in any level beyond "output_level()".
|
||||
// the input "user_key" does not exist in any level beyond `output_level()`.
|
||||
bool KeyNotExistsBeyondOutputLevel(const Slice& user_key,
|
||||
std::vector<size_t>* level_ptrs) const;
|
||||
|
||||
// Returns true if the user key range [begin_key, end_key) does not exist
|
||||
// in any level beyond `output_level()`.
|
||||
// Used for checking range tombstones, so we assume begin_key < end_key.
|
||||
// begin_key and end_key should include timestamp if enabled.
|
||||
bool KeyRangeNotExistsBeyondOutputLevel(
|
||||
const Slice& begin_key, const Slice& end_key,
|
||||
std::vector<size_t>* level_ptrs) const;
|
||||
|
||||
// Clear all files to indicate that they are not being compacted
|
||||
// Delete this compaction from the list of running compactions.
|
||||
//
|
||||
|
||||
@@ -260,7 +260,7 @@ void CompactionJob::Prepare() {
|
||||
StopWatch sw(db_options_.clock, stats_, SUBCOMPACTION_SETUP_TIME);
|
||||
GenSubcompactionBoundaries();
|
||||
}
|
||||
if (boundaries_.size() > 1) {
|
||||
if (boundaries_.size() >= 1) {
|
||||
for (size_t i = 0; i <= boundaries_.size(); i++) {
|
||||
compact_->sub_compact_states.emplace_back(
|
||||
c, (i != 0) ? std::optional<Slice>(boundaries_[i - 1]) : std::nullopt,
|
||||
@@ -808,8 +808,8 @@ Status CompactionJob::Run() {
|
||||
RecordCompactionIOStats();
|
||||
LogFlush(db_options_.info_log);
|
||||
TEST_SYNC_POINT("CompactionJob::Run():End");
|
||||
|
||||
compact_->status = status;
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::Run():EndStatusSet", &status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -616,6 +616,7 @@ TEST_P(CompactionJobStatsTest, CompactionJobStatsTest) {
|
||||
// via AddExpectedStats().
|
||||
auto* stats_checker = new CompactionJobStatsChecker();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.listeners.emplace_back(stats_checker);
|
||||
options.create_if_missing = true;
|
||||
// just enough setting to hold off auto-compaction.
|
||||
@@ -815,6 +816,7 @@ TEST_P(CompactionJobStatsTest, DeletionStatsTest) {
|
||||
// what we expect.
|
||||
auto* stats_checker = new CompactionJobDeletionStatsChecker();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.listeners.emplace_back(stats_checker);
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = kTestScale + 1;
|
||||
|
||||
@@ -386,7 +386,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
kUnknownFileCreationTime,
|
||||
versions_->GetColumnFamilySet()->GetDefault()->NewEpochNumber(),
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2,
|
||||
0, 0);
|
||||
/*compensated_range_deletion_size=*/0, /*tail_size=*/0,
|
||||
/*user_defined_timestamps_persisted=*/true);
|
||||
|
||||
mutex_.Lock();
|
||||
EXPECT_OK(versions_->LogAndApply(
|
||||
|
||||
@@ -45,6 +45,8 @@ Status CompactionOutputs::Finish(const Status& intput_status,
|
||||
meta->fd.file_size = current_bytes;
|
||||
meta->tail_size = builder_->GetTailSize();
|
||||
meta->marked_for_compaction = builder_->NeedCompact();
|
||||
meta->user_defined_timestamps_persisted = static_cast<bool>(
|
||||
builder_->GetTableProperties().user_defined_timestamps_persisted);
|
||||
}
|
||||
current_output().finished = true;
|
||||
stats_.bytes_written += current_bytes;
|
||||
@@ -598,10 +600,12 @@ Status CompactionOutputs::AddRangeDels(
|
||||
// in any snapshot. trim_ts_ is passed to the constructor for
|
||||
// range_del_agg_, and range_del_agg_ internally drops tombstones above
|
||||
// trim_ts_.
|
||||
if (bottommost_level && tombstone.seq_ <= earliest_snapshot &&
|
||||
bool consider_drop =
|
||||
tombstone.seq_ <= earliest_snapshot &&
|
||||
(ts_sz == 0 ||
|
||||
(!full_history_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(tombstone.ts_, full_history_ts_low) < 0))) {
|
||||
ucmp->CompareTimestamp(tombstone.ts_, full_history_ts_low) < 0));
|
||||
if (consider_drop && bottommost_level) {
|
||||
// TODO(andrewkr): tombstones that span multiple output files are
|
||||
// counted for each compaction output file, so lots of double
|
||||
// counting.
|
||||
@@ -635,6 +639,20 @@ Status CompactionOutputs::AddRangeDels(
|
||||
icmp.Compare(*upper_bound, tombstone_start.Encode()) < 0) {
|
||||
break;
|
||||
}
|
||||
if (lower_bound &&
|
||||
icmp.Compare(tombstone_start.Encode(), *lower_bound) < 0) {
|
||||
tombstone_start.DecodeFrom(*lower_bound);
|
||||
}
|
||||
if (upper_bound && icmp.Compare(*upper_bound, tombstone_end.Encode()) < 0) {
|
||||
tombstone_end.DecodeFrom(*upper_bound);
|
||||
}
|
||||
if (consider_drop && compaction_->KeyRangeNotExistsBeyondOutputLevel(
|
||||
tombstone_start.user_key(),
|
||||
tombstone_end.user_key(), &level_ptrs_)) {
|
||||
range_del_out_stats.num_range_del_drop_obsolete++;
|
||||
range_del_out_stats.num_record_drop_obsolete++;
|
||||
continue;
|
||||
}
|
||||
// Here we show that *only* range tombstones that overlap with
|
||||
// [lower_bound, upper_bound] are added to the current file, and
|
||||
// sanity checking invariants that should hold:
|
||||
@@ -688,13 +706,6 @@ Status CompactionOutputs::AddRangeDels(
|
||||
|
||||
// Range tombstone is not supported by output validator yet.
|
||||
builder_->Add(kv.first.Encode(), kv.second);
|
||||
if (lower_bound &&
|
||||
icmp.Compare(tombstone_start.Encode(), *lower_bound) < 0) {
|
||||
tombstone_start.DecodeFrom(*lower_bound);
|
||||
}
|
||||
if (upper_bound && icmp.Compare(*upper_bound, tombstone_end.Encode()) < 0) {
|
||||
tombstone_end.DecodeFrom(*upper_bound);
|
||||
}
|
||||
assert(icmp.Compare(tombstone_start, tombstone_end) <= 0);
|
||||
meta.UpdateBoundariesForRange(tombstone_start, tombstone_end,
|
||||
tombstone.seq_, icmp);
|
||||
@@ -779,6 +790,8 @@ CompactionOutputs::CompactionOutputs(const Compaction* compaction,
|
||||
if (compaction->output_level() != 0) {
|
||||
FillFilesToCutForTtl();
|
||||
}
|
||||
|
||||
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -356,6 +356,15 @@ class CompactionOutputs {
|
||||
// The smallest key of the current output file, this is set when current
|
||||
// output file's smallest key is a range tombstone start key.
|
||||
InternalKey range_tombstone_lower_bound_;
|
||||
|
||||
// Used for calls to compaction->KeyRangeNotExistsBeyondOutputLevel() in
|
||||
// CompactionOutputs::AddRangeDels().
|
||||
// level_ptrs_[i] holds index of the file that was checked during the last
|
||||
// call to compaction->KeyRangeNotExistsBeyondOutputLevel(). This allows
|
||||
// future calls to the function to pick up where it left off, since each
|
||||
// range tombstone added to output file within each subcompaction is in
|
||||
// increasing key range.
|
||||
std::vector<size_t> level_ptrs_;
|
||||
};
|
||||
|
||||
// helper struct to concatenate the last level and penultimate level outputs
|
||||
|
||||
@@ -611,23 +611,21 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// Universal compaction with more than one level always compacts all the
|
||||
// files together to the last level.
|
||||
assert(vstorage->num_levels() > 1);
|
||||
int max_output_level =
|
||||
vstorage->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
// DBImpl::CompactRange() set output level to be the last level
|
||||
if (ioptions_.allow_ingest_behind) {
|
||||
assert(output_level == vstorage->num_levels() - 2);
|
||||
} else {
|
||||
assert(output_level == vstorage->num_levels() - 1);
|
||||
}
|
||||
assert(output_level == max_output_level);
|
||||
// DBImpl::RunManualCompaction will make full range for universal compaction
|
||||
assert(begin == nullptr);
|
||||
assert(end == nullptr);
|
||||
*compaction_end = nullptr;
|
||||
|
||||
int start_level = 0;
|
||||
for (; start_level < vstorage->num_levels() &&
|
||||
for (; start_level <= max_output_level &&
|
||||
vstorage->NumLevelFiles(start_level) == 0;
|
||||
start_level++) {
|
||||
}
|
||||
if (start_level == vstorage->num_levels()) {
|
||||
if (start_level > max_output_level) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -637,9 +635,9 @@ Compaction* CompactionPicker::CompactRange(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage->num_levels() -
|
||||
std::vector<CompactionInputFiles> inputs(max_output_level + 1 -
|
||||
start_level);
|
||||
for (int level = start_level; level < vstorage->num_levels(); level++) {
|
||||
for (int level = start_level; level <= max_output_level; level++) {
|
||||
inputs[level - start_level].level = level;
|
||||
auto& files = inputs[level - start_level].files;
|
||||
for (FileMetaData* f : vstorage->LevelFiles(level)) {
|
||||
@@ -753,8 +751,10 @@ Compaction* CompactionPicker::CompactRange(
|
||||
|
||||
// for BOTTOM LEVEL compaction only, use max_file_num_to_ignore to filter out
|
||||
// files that are created during the current compaction.
|
||||
if (compact_range_options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForceOptimized &&
|
||||
if ((compact_range_options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForceOptimized ||
|
||||
compact_range_options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter) &&
|
||||
max_file_num_to_ignore != std::numeric_limits<uint64_t>::max()) {
|
||||
assert(input_level == output_level);
|
||||
// inputs_shrunk holds a continuous subset of input files which were all
|
||||
|
||||
@@ -70,6 +70,11 @@ class CompactionPickerTestBase : public testing::Test {
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
ioptions_.cf_paths.emplace_back("dummy",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
// When the default value of this option is true, universal compaction
|
||||
// tests can encounter assertion failure since SanitizeOption() is
|
||||
// not run to set this option to false. So we do the sanitization
|
||||
// here. Tests that test this option set this option to true explicitly.
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
}
|
||||
|
||||
~CompactionPickerTestBase() override {}
|
||||
@@ -148,7 +153,8 @@ class CompactionPickerTestBase : public testing::Test {
|
||||
smallest_seq, largest_seq, marked_for_compact, temperature,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, epoch_number, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
|
||||
true /* user_defined_timestamps_persisted */);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->oldest_ancester_time = oldest_ancestor_time;
|
||||
@@ -505,7 +511,7 @@ TEST_F(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionUniversalIngestBehindReservedLevel) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
NewVersionStorage(3 /* num_levels */, kCompactionStyleUniversal);
|
||||
ioptions_.allow_ingest_behind = true;
|
||||
ioptions_.num_levels = 3;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
@@ -532,6 +538,14 @@ TEST_F(CompactionPickerTest, CompactionUniversalIngestBehindReservedLevel) {
|
||||
|
||||
// output level should be the one above the bottom-most
|
||||
ASSERT_EQ(1, compaction->output_level());
|
||||
|
||||
// input should not include the reserved level
|
||||
const std::vector<CompactionInputFiles>* inputs = compaction->inputs();
|
||||
for (const auto& compaction_input : *inputs) {
|
||||
if (!compaction_input.empty()) {
|
||||
ASSERT_LT(compaction_input.level, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tests if the files can be trivially moved in multi level
|
||||
// universal compaction when allow_trivial_move option is set
|
||||
|
||||
@@ -133,8 +133,8 @@ class UniversalCompactionBuilder {
|
||||
UniversalCompactionPicker* picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage);
|
||||
static std::vector<UniversalCompactionBuilder::SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, int last_level);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -339,13 +339,13 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage) {
|
||||
const VersionStorageInfo& vstorage, int last_level) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
f->being_compacted);
|
||||
}
|
||||
for (int level = 1; level < vstorage.num_levels(); level++) {
|
||||
for (int level = 1; level <= last_level; level++) {
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
@@ -374,7 +374,9 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
const int kLevel0 = 0;
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_);
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_, max_output_level);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
@@ -471,6 +473,8 @@ Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
"UniversalCompactionBuilder::PickCompaction:Return", nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
assert(c->output_level() <=
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind));
|
||||
|
||||
if (mutable_cf_options_.compaction_options_universal.allow_trivial_move ==
|
||||
true &&
|
||||
@@ -698,22 +702,18 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
GetPathId(ioptions_, mutable_cf_options_, estimated_total_size);
|
||||
int start_level = sorted_runs_[start_index].level;
|
||||
int output_level;
|
||||
// last level is reserved for the files ingested behind
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
if (first_index_after == sorted_runs_.size()) {
|
||||
output_level = vstorage_->num_levels() - 1;
|
||||
output_level = max_output_level;
|
||||
} else if (sorted_runs_[first_index_after].level == 0) {
|
||||
output_level = 0;
|
||||
} else {
|
||||
output_level = sorted_runs_[first_index_after].level - 1;
|
||||
}
|
||||
|
||||
// last level is reserved for the files ingested behind
|
||||
if (ioptions_.allow_ingest_behind &&
|
||||
(output_level == vstorage_->num_levels() - 1)) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage_->num_levels());
|
||||
std::vector<CompactionInputFiles> inputs(max_output_level + 1);
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
inputs[i].level = start_level + static_cast<int>(i);
|
||||
}
|
||||
@@ -1192,8 +1192,10 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
// Pick the first non-empty level after the start_level
|
||||
for (output_level = start_level + 1; output_level < vstorage_->num_levels();
|
||||
for (output_level = start_level + 1; output_level <= max_output_level;
|
||||
output_level++) {
|
||||
if (vstorage_->NumLevelFiles(output_level) != 0) {
|
||||
break;
|
||||
@@ -1201,9 +1203,9 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
}
|
||||
|
||||
// If all higher levels are empty, pick the highest level as output level
|
||||
if (output_level == vstorage_->num_levels()) {
|
||||
if (output_level > max_output_level) {
|
||||
if (start_level == 0) {
|
||||
output_level = vstorage_->num_levels() - 1;
|
||||
output_level = max_output_level;
|
||||
} else {
|
||||
// If start level is non-zero and all higher levels are empty, this
|
||||
// compaction will translate into a trivial move. Since the idea is
|
||||
@@ -1212,11 +1214,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (ioptions_.allow_ingest_behind &&
|
||||
output_level == vstorage_->num_levels() - 1) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
assert(output_level <= max_output_level);
|
||||
|
||||
if (output_level != 0) {
|
||||
if (start_level == 0) {
|
||||
@@ -1293,8 +1291,9 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
uint32_t path_id =
|
||||
GetPathId(ioptions_, mutable_cf_options_, estimated_total_size);
|
||||
int start_level = sorted_runs_[start_index].level;
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage_->num_levels());
|
||||
int max_output_level =
|
||||
vstorage_->MaxOutputLevel(ioptions_.allow_ingest_behind);
|
||||
std::vector<CompactionInputFiles> inputs(max_output_level + 1);
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
inputs[i].level = start_level + static_cast<int>(i);
|
||||
}
|
||||
@@ -1331,13 +1330,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionWithSortedRunRange(
|
||||
|
||||
int output_level;
|
||||
if (end_index == sorted_runs_.size() - 1) {
|
||||
// output files at the last level, unless it's reserved
|
||||
output_level = vstorage_->num_levels() - 1;
|
||||
// last level is reserved for the files ingested behind
|
||||
if (ioptions_.allow_ingest_behind) {
|
||||
assert(output_level > 1);
|
||||
output_level--;
|
||||
}
|
||||
output_level = max_output_level;
|
||||
} else {
|
||||
// if it's not including all sorted_runs, it can only output to the level
|
||||
// above the `end_index + 1` sorted_run.
|
||||
|
||||
@@ -209,7 +209,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
seq_history.emplace_back(dbfull()->GetLatestSequenceNumber());
|
||||
expect_stats[0].Add(kBasicFlushStats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// the penultimate level file temperature is not cold, all data are output to
|
||||
// the penultimate level.
|
||||
@@ -374,7 +374,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
ASSERT_OK(Flush());
|
||||
expect_stats[0].Add(kBasicFlushStats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -446,7 +446,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageUniversal) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
// make sure the compaction is able to finish
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -911,7 +911,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
ASSERT_OK(Flush());
|
||||
expect_stats[0].Add(kBasicFlushStats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// non last level is hot
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
@@ -954,7 +954,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
ASSERT_OK(Flush());
|
||||
seq_history.emplace_back(dbfull()->GetLatestSequenceNumber());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,1,0,0,0,0,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -1005,7 +1005,7 @@ TEST_P(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
ASSERT_OK(Flush());
|
||||
seq_history.emplace_back(dbfull()->GetLatestSequenceNumber());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
latest_cold_seq = seq_history[0];
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
@@ -1135,7 +1135,7 @@ TEST_P(TieredCompactionTest, RangeBasedTieredStorageLevel) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
@@ -1265,7 +1265,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeManualCompaction) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1327,7 +1327,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1360,7 +1360,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimeAutoCompaction) {
|
||||
});
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
// all data is moved up to the penultimate level
|
||||
@@ -1403,7 +1403,7 @@ TEST_F(PrecludeLastLevelTest, MigrationFromPreserveTimePartial) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1530,7 +1530,7 @@ TEST_F(PrecludeLastLevelTest, LastLevelOnlyCompactionPartial) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1609,7 +1609,7 @@ TEST_P(PrecludeLastLevelTestWithParms, LastLevelOnlyCompactionNoPreclude) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// all data is pushed to the last level
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -1705,7 +1705,7 @@ TEST_P(PrecludeLastLevelTestWithParms, LastLevelOnlyCompactionNoPreclude) {
|
||||
|
||||
manual_compaction_thread.join();
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
if (enable_preclude_last_level) {
|
||||
ASSERT_NE("0,0,0,0,0,1,1", FilesPerLevel());
|
||||
@@ -1841,7 +1841,7 @@ TEST_P(PrecludeLastLevelTestWithParms, PeriodicCompactionToPenultimateLevel) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
stop_token.reset();
|
||||
|
||||
@@ -1940,7 +1940,7 @@ TEST_F(PrecludeLastLevelTest, PartialPenultimateLevelCompaction) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// L5: [0,19] [20,39] [40,299]
|
||||
// L6: [0, 299]
|
||||
@@ -2106,7 +2106,7 @@ TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
Slice begin_key(begin_key_buf), end_key(end_key_buf);
|
||||
ASSERT_OK(db_->SuggestCompactRange(db_->DefaultColumnFamily(), &begin_key,
|
||||
&end_key));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,3,3", FilesPerLevel());
|
||||
ASSERT_EQ(1, per_key_comp_num);
|
||||
verify_db();
|
||||
@@ -2116,7 +2116,7 @@ TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
db_->ReleaseSnapshot(snap2);
|
||||
ASSERT_OK(db_->SuggestCompactRange(db_->DefaultColumnFamily(), &begin_key,
|
||||
&end_key));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,3,3", FilesPerLevel());
|
||||
ASSERT_EQ(2, per_key_comp_num);
|
||||
verify_db();
|
||||
@@ -2126,7 +2126,7 @@ TEST_F(PrecludeLastLevelTest, RangeDelsCauseFileEndpointsToOverlap) {
|
||||
db_->ReleaseSnapshot(snap1);
|
||||
ASSERT_OK(db_->SuggestCompactRange(db_->DefaultColumnFamily(), &begin_key,
|
||||
&end_key));
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("0,0,0,0,0,2,3", FilesPerLevel());
|
||||
ASSERT_EQ(3, per_key_comp_num);
|
||||
verify_db();
|
||||
|
||||
@@ -450,6 +450,7 @@ TEST_F(CorruptionTest, TableFile) {
|
||||
|
||||
TEST_F(CorruptionTest, VerifyChecksumReadahead) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
SpecialEnv senv(base_env_);
|
||||
options.env = &senv;
|
||||
// Disable block cache as we are going to check checksum for
|
||||
@@ -503,6 +504,7 @@ TEST_F(CorruptionTest, VerifyChecksumReadahead) {
|
||||
|
||||
TEST_F(CorruptionTest, TableFileIndexData) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
// very big, we'll trigger flushes manually
|
||||
options.write_buffer_size = 100 * 1024 * 1024;
|
||||
Reopen(&options);
|
||||
@@ -659,6 +661,7 @@ TEST_F(CorruptionTest, CorruptedDescriptor) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionInputError) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
Reopen(&options);
|
||||
Build(10);
|
||||
@@ -680,6 +683,7 @@ TEST_F(CorruptionTest, CompactionInputError) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_checks = true;
|
||||
options.write_buffer_size = 131072;
|
||||
@@ -777,6 +781,7 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_checks = true;
|
||||
options.create_if_missing = true;
|
||||
@@ -816,6 +821,7 @@ static const auto& corruption_modes = {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
@@ -844,6 +850,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
@@ -877,6 +884,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
@@ -913,6 +921,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
@@ -952,6 +961,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
@@ -988,6 +998,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
|
||||
TEST_F(CorruptionTest, LogCorruptionErrorsInCompactionIterator) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.allow_data_in_errors = true;
|
||||
@@ -1017,6 +1028,7 @@ TEST_F(CorruptionTest, LogCorruptionErrorsInCompactionIterator) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
@@ -1044,6 +1056,7 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
|
||||
TEST_F(CorruptionTest, FlushKeyOrderCheck) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
@@ -1097,6 +1110,7 @@ TEST_F(CorruptionTest, DisableKeyOrderCheck) {
|
||||
TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_.get();
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
options.create_if_missing = true;
|
||||
@@ -1182,6 +1196,7 @@ INSTANTIATE_TEST_CASE_P(CorruptionTest, CrashDuringRecoveryWithCorruptionTest,
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest_;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
@@ -1354,6 +1369,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecovery) {
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
options.track_and_verify_wals_in_manifest =
|
||||
track_and_verify_wals_in_manifest_;
|
||||
@@ -1551,6 +1567,7 @@ TEST_P(CrashDuringRecoveryWithCorruptionTest, TxnDbCrashDuringRecovery) {
|
||||
TEST_P(CrashDuringRecoveryWithCorruptionTest, CrashDuringRecoveryWithFlush) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
options.env = env_.get();
|
||||
|
||||
@@ -39,6 +39,7 @@ class CuckooTableDBTest : public testing::Test {
|
||||
|
||||
Options CurrentOptions() {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.table_factory.reset(NewCuckooTableFactory());
|
||||
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true));
|
||||
options.allow_mmap_reads = true;
|
||||
|
||||
@@ -2614,7 +2614,7 @@ TEST_F(DBBloomFilterTest, OptimizeFiltersForHits) {
|
||||
compact_options.target_level = 7;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
|
||||
|
||||
ASSERT_EQ(trivial_move, 1);
|
||||
ASSERT_GE(trivial_move, 1);
|
||||
ASSERT_EQ(non_trivial_move, 0);
|
||||
|
||||
prev_cache_filter_hits = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
|
||||
+417
-31
@@ -153,6 +153,48 @@ class DBCompactionDirectIOTest : public DBCompactionTest,
|
||||
DBCompactionDirectIOTest() : DBCompactionTest() {}
|
||||
};
|
||||
|
||||
class DBCompactionWaitForCompactTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
DBCompactionWaitForCompactTest()
|
||||
: DBTestBase("db_compaction_test", /*env_do_fsync=*/true) {
|
||||
abort_on_pause_ = std::get<0>(GetParam());
|
||||
flush_ = std::get<1>(GetParam());
|
||||
}
|
||||
bool abort_on_pause_;
|
||||
bool flush_;
|
||||
Options options_;
|
||||
WaitForCompactOptions wait_for_compact_options_;
|
||||
|
||||
void SetUp() override {
|
||||
// This test sets up a scenario that one more L0 file will trigger a
|
||||
// compaction
|
||||
const int kNumKeysPerFile = 4;
|
||||
const int kNumFiles = 2;
|
||||
|
||||
options_ = CurrentOptions();
|
||||
options_.level0_file_num_compaction_trigger = kNumFiles + 1;
|
||||
|
||||
wait_for_compact_options_ = WaitForCompactOptions();
|
||||
wait_for_compact_options_.abort_on_pause = abort_on_pause_;
|
||||
wait_for_compact_options_.flush = flush_;
|
||||
|
||||
DestroyAndReopen(options_);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(100 /* len */)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
}
|
||||
};
|
||||
|
||||
// Param = true : target level is non-empty
|
||||
// Param = false: level between target level and source level
|
||||
// is not empty.
|
||||
@@ -3285,6 +3327,186 @@ TEST_F(DBCompactionTest, SuggestCompactRangeNoTwoLevel0Compactions) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::SuggestCompactRangeNoTwoLevel0Compactions:2");
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionWaitForCompactTest,
|
||||
DBCompactionWaitForCompactTest,
|
||||
::testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
std::make_tuple(true, false),
|
||||
std::make_tuple(true, true)));
|
||||
|
||||
TEST_P(DBCompactionWaitForCompactTest,
|
||||
WaitForCompactWaitsOnCompactionToFinish) {
|
||||
// Triggers a compaction. Before the compaction finishes, test
|
||||
// closes the DB Upon reopen, wait for the compaction to finish and checks for
|
||||
// the number of compaction finished
|
||||
|
||||
int compaction_finished = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():EndStatusSet", [&](void* arg) {
|
||||
auto status = static_cast<Status*>(arg);
|
||||
if (status->ok()) {
|
||||
compaction_finished++;
|
||||
}
|
||||
});
|
||||
// To make sure there's a flush/compaction debt
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:BeforeSchedule", [&](void* arg) {
|
||||
auto unscheduled_flushes = *static_cast<int*>(arg);
|
||||
ASSERT_GT(unscheduled_flushes, 0);
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBCompactionTest::WaitForCompactWaitsOnCompactionToFinish",
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:BeforeSchedule"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// create compaction debt by adding one more L0 file then closing
|
||||
Random rnd(123);
|
||||
GenerateNewRandomFile(&rnd, /* nowait */ true);
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
Close();
|
||||
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactWaitsOnCompactionToFinish");
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
|
||||
// Reopen the db and we expect the compaction to be triggered.
|
||||
Reopen(options_);
|
||||
|
||||
// Wait for compaction to finish
|
||||
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
|
||||
ASSERT_GT(compaction_finished, 0);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactAbortOnPause) {
|
||||
// Triggers a compaction. Before the compaction finishes, test
|
||||
// pauses the compaction. Calling WaitForCompact() with option
|
||||
// abort_on_pause=true should return Status::Aborted Or
|
||||
// ContinueBackgroundWork() must be called
|
||||
|
||||
// Now trigger L0 compaction by adding a file
|
||||
Random rnd(123);
|
||||
GenerateNewRandomFile(&rnd, /* nowait */ true);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Pause the background jobs.
|
||||
ASSERT_OK(dbfull()->PauseBackgroundWork());
|
||||
|
||||
// If not abort_on_pause_ continue the background jobs.
|
||||
if (!abort_on_pause_) {
|
||||
ASSERT_OK(dbfull()->ContinueBackgroundWork());
|
||||
}
|
||||
|
||||
Status s = dbfull()->WaitForCompact(wait_for_compact_options_);
|
||||
if (abort_on_pause_) {
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsAborted());
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactShutdownWhileWaiting) {
|
||||
// Triggers a compaction. Before the compaction finishes, db
|
||||
// shuts down (by calling CancelAllBackgroundWork()). Calling WaitForCompact()
|
||||
// should return Status::IsShutdownInProgress()
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"CompactionJob::Run():Start",
|
||||
"DBCompactionTest::WaitForCompactShutdownWhileWaiting:0"},
|
||||
{"DBImpl::WaitForCompact:StartWaiting",
|
||||
"DBCompactionTest::WaitForCompactShutdownWhileWaiting:1"},
|
||||
{"DBImpl::~DBImpl:WaitJob", "CompactionJob::Run():End"},
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Now trigger L0 compaction by adding a file
|
||||
Random rnd(123);
|
||||
GenerateNewRandomFile(&rnd, /* nowait */ true);
|
||||
ASSERT_OK(Flush());
|
||||
// Wait for compaction to start
|
||||
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactShutdownWhileWaiting:0");
|
||||
|
||||
// Wait for Compaction in another thread
|
||||
auto waiting_for_compaction_thread = port::Thread([this]() {
|
||||
Status s = dbfull()->WaitForCompact(wait_for_compact_options_);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsShutdownInProgress());
|
||||
});
|
||||
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactShutdownWhileWaiting:1");
|
||||
// Shutdown after wait started, but before the compaction finishes
|
||||
auto closing_thread = port::Thread([this]() { ASSERT_OK(db_->Close()); });
|
||||
|
||||
waiting_for_compaction_thread.join();
|
||||
closing_thread.join();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactWithOptionToFlush) {
|
||||
// After creating enough L0 files that one more file will trigger the
|
||||
// compaction, write some data in memtable. Calls WaitForCompact with option
|
||||
// to flush. This will flush the memtable to a new L0 file which will trigger
|
||||
// compaction. Lastly check for expected number of files, closing + reopening
|
||||
// DB won't trigger any flush or compaction
|
||||
|
||||
int compaction_finished = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
[&](void*) { compaction_finished++; });
|
||||
|
||||
int flush_finished = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FlushJob::End", [&](void*) { flush_finished++; });
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// write to memtable (overlapping key with first L0 file), but no flush is
|
||||
// needed at this point.
|
||||
ASSERT_OK(Put(Key(0), "some random string"));
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
ASSERT_EQ(0, flush_finished);
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
|
||||
if (flush_) {
|
||||
ASSERT_EQ("1,2", FilesPerLevel());
|
||||
ASSERT_EQ(1, compaction_finished);
|
||||
ASSERT_EQ(1, flush_finished);
|
||||
} else {
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
ASSERT_EQ(0, flush_finished);
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
}
|
||||
|
||||
compaction_finished = 0;
|
||||
flush_finished = 0;
|
||||
Close();
|
||||
Reopen(options_);
|
||||
|
||||
ASSERT_EQ(0, flush_finished);
|
||||
if (flush_) {
|
||||
// if flushed already prior to close and reopen, expect there's no
|
||||
// additional compaction needed
|
||||
ASSERT_EQ(0, compaction_finished);
|
||||
} else {
|
||||
// if not flushed prior to close and reopen, expect L0 file creation from
|
||||
// WAL when reopening which will trigger the compaction.
|
||||
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
|
||||
ASSERT_EQ(1, compaction_finished);
|
||||
}
|
||||
|
||||
ASSERT_EQ("1,2", FilesPerLevel());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
static std::string ShortKey(int i) {
|
||||
@@ -5822,7 +6044,7 @@ TEST_P(RoundRobinSubcompactionsAgainstPressureToken, PressureTokenTest) {
|
||||
}
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstPressureToken:2");
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(num_planned_subcompactions_verified);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -5897,7 +6119,7 @@ TEST_P(RoundRobinSubcompactionsAgainstResources, SubcompactionsUsingResources) {
|
||||
"CompactionJob::ReleaseSubcompactionResources:1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:0");
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:1");
|
||||
@@ -5913,7 +6135,7 @@ TEST_P(RoundRobinSubcompactionsAgainstResources, SubcompactionsUsingResources) {
|
||||
total_low_pri_threads_ - 1,
|
||||
env_->ReleaseThreads(total_low_pri_threads_ - 1, Env::Priority::LOW));
|
||||
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstResources:4");
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(num_planned_subcompactions_verified);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -5985,11 +6207,11 @@ TEST_P(DBCompactionTestWithParam, RoundRobinWithoutAdditionalResources) {
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
|
||||
TEST_SYNC_POINT("DBCompactionTest::RoundRobinWithoutAdditionalResources:0");
|
||||
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(num_planned_subcompactions_verified);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -7744,26 +7966,14 @@ TEST_F(DBCompactionTest, ChangeLevelErrorPathTest) {
|
||||
auto start_idx = key_idx;
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
auto end_idx = key_idx - 1;
|
||||
ASSERT_EQ("1,1,2", FilesPerLevel(0));
|
||||
|
||||
// Next two CompactRange() calls are used to test exercise error paths within
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ("0,2,2", FilesPerLevel(0));
|
||||
|
||||
// The next CompactRange() call is used to test exercise error paths within
|
||||
// RefitLevel() before triggering a valid RefitLevel() call
|
||||
|
||||
// Trigger a refit to L1 first
|
||||
{
|
||||
std::string begin_string = Key(start_idx);
|
||||
std::string end_string = Key(end_idx);
|
||||
Slice begin(begin_string);
|
||||
Slice end(end_string);
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 1;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, &begin, &end));
|
||||
}
|
||||
ASSERT_EQ("0,3,2", FilesPerLevel(0));
|
||||
|
||||
//
|
||||
// Try a refit from L2->L1 - this should fail and exercise error paths in
|
||||
// RefitLevel()
|
||||
{
|
||||
@@ -7778,7 +7988,7 @@ TEST_F(DBCompactionTest, ChangeLevelErrorPathTest) {
|
||||
cro.target_level = 1;
|
||||
ASSERT_NOK(dbfull()->CompactRange(cro, &begin, &end));
|
||||
}
|
||||
ASSERT_EQ("0,3,2", FilesPerLevel(0));
|
||||
ASSERT_EQ("0,2,2", FilesPerLevel(0));
|
||||
|
||||
// Try a valid Refit request to ensure, the path is still working
|
||||
{
|
||||
@@ -7791,10 +8001,8 @@ TEST_F(DBCompactionTest, ChangeLevelErrorPathTest) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionWithBlob) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
@@ -7886,10 +8094,8 @@ INSTANTIATE_TEST_CASE_P(DBCompactionTestBlobError, DBCompactionTestBlobError,
|
||||
"BlobFileBuilder::WriteBlobToFile:AppendFooter"}));
|
||||
|
||||
TEST_P(DBCompactionTestBlobError, CompactionError) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
@@ -8055,8 +8261,7 @@ TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGCOverrides) {
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGC) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = 32; // one blob per file
|
||||
@@ -9415,6 +9620,187 @@ TEST_F(DBCompactionTest, DrainUnnecessaryLevelsAfterDBBecomesSmall) {
|
||||
ASSERT_GT(after_delete_range_nonempty, final_num_nonempty);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ManualCompactionCompactAllKeysInRange) {
|
||||
// CompactRange() used to pre-compute target level to compact to
|
||||
// before running compactions. However, the files at target level
|
||||
// could be trivially moved down by some background compaction. This means
|
||||
// some keys in the manual compaction key range may not be compacted
|
||||
// during the manual compaction. This unit test tests this scenario.
|
||||
// A fix has been applied for this scenario to always compact
|
||||
// to the bottommost level.
|
||||
const int kBaseLevelBytes = 8 << 20; // 8MB
|
||||
const int kMultiplier = 2;
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 7;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.max_bytes_for_level_base = kBaseLevelBytes;
|
||||
options.max_bytes_for_level_multiplier = kMultiplier;
|
||||
options.compression = kNoCompression;
|
||||
options.target_file_size_base = 2 * kBaseLevelBytes;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
// Populate L2 so that manual compaction will compact to at least L2.
|
||||
// Otherwise, there is still a possibility of race condition where
|
||||
// the manual compaction thread believes that max non-empty level is L1
|
||||
// while there is some auto compaction that moves some files from L1 to L2.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1000), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(2));
|
||||
|
||||
// one file in L1: [Key(5), Key(6)]
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(5), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(6), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(1), rnd.RandomString(kBaseLevelBytes / 2)));
|
||||
// We now do manual compaction for key range [Key(1), Key(6)].
|
||||
// First it compacts file [Key(1)] to L1.
|
||||
// L1 will have two files [Key(1)], and [Key(5), Key(6)].
|
||||
// After L0 -> L1 manual compaction, an automatic compaction will trivially
|
||||
// move both files from L1 to L2. Here the dependency makes manual compaction
|
||||
// wait for auto-compaction to pick a compaction before proceeding. Manual
|
||||
// compaction should not stop at L1 and keep compacting L2. With kForce
|
||||
// specified, expected output is that manual compaction compacts to L2 and L2
|
||||
// will contain 2 files: one for Key(1000) and one for Key(1), Key(5) and
|
||||
// Key(6).
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction():AfterPickCompaction",
|
||||
"DBImpl::RunManualCompaction()::1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::string begin_str = Key(1);
|
||||
std::string end_str = Key(6);
|
||||
Slice begin_slice = begin_str;
|
||||
Slice end_slice = end_str;
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, &begin_slice, &end_slice));
|
||||
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 2);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest,
|
||||
ManualCompactionCompactAllKeysInRangeDynamicLevelBytes) {
|
||||
// Similar to the test above (ManualCompactionCompactAllKeysInRange), but with
|
||||
// level_compaction_dynamic_level_bytes = true.
|
||||
const int kBaseLevelBytes = 8 << 20; // 8MB
|
||||
const int kMultiplier = 2;
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 7;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.max_bytes_for_level_base = kBaseLevelBytes;
|
||||
options.max_bytes_for_level_multiplier = kMultiplier;
|
||||
options.compression = kNoCompression;
|
||||
options.target_file_size_base = 2 * kBaseLevelBytes;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(5),
|
||||
rnd.RandomString(3 * kBaseLevelBytes / 2)));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(6));
|
||||
// L6 now has one file with size ~ 3/2 * kBaseLevelBytes.
|
||||
// L5 is the new base level, with target size ~ 3/4 * kBaseLevelBytes.
|
||||
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(3), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(4), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
MoveFilesToLevel(5);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(5));
|
||||
// L5 now has one file with size ~ 2/3 * kBaseLevelBytes, which is below its
|
||||
// target size.
|
||||
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(1), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
ASSERT_OK(
|
||||
db_->Put(WriteOptions(), Key(2), rnd.RandomString(kBaseLevelBytes / 3)));
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction():AfterPickCompaction",
|
||||
"DBImpl::RunManualCompaction()::1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
// After compacting the file with [Key(1), Key(2)] to L5,
|
||||
// L5 has size ~ 4/3 * kBaseLevelBytes > its target size.
|
||||
// We let manual compaction wait for an auto-compaction to pick
|
||||
// a compaction before proceeding. The auto-compaction would
|
||||
// trivially move both files in L5 down to L6. If manual compaction
|
||||
// works correctly with kForce specified, it should rewrite the two files in
|
||||
// L6 into a single file.
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
std::string begin_str = Key(1);
|
||||
std::string end_str = Key(4);
|
||||
Slice begin_slice = begin_str;
|
||||
Slice end_slice = end_str;
|
||||
ASSERT_OK(db_->CompactRange(cro, &begin_slice, &end_slice));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(6));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(5));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, NumberOfSubcompactions) {
|
||||
// Tests that expected number of subcompactions are created.
|
||||
class SubCompactionEventListener : public EventListener {
|
||||
public:
|
||||
void OnSubcompactionCompleted(const SubcompactionJobInfo&) override {
|
||||
sub_compaction_finished_++;
|
||||
}
|
||||
void OnCompactionCompleted(DB*, const CompactionJobInfo&) override {
|
||||
compaction_finished_++;
|
||||
}
|
||||
std::atomic<int> sub_compaction_finished_{0};
|
||||
std::atomic<int> compaction_finished_{0};
|
||||
};
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.compression = kNoCompression;
|
||||
const int kFileSize = 100 << 10; // 100KB
|
||||
options.target_file_size_base = kFileSize;
|
||||
const int kLevel0CompactTrigger = 2;
|
||||
options.level0_file_num_compaction_trigger = kLevel0CompactTrigger;
|
||||
Destroy(options);
|
||||
Random rnd(301);
|
||||
|
||||
// Exposing internal implementation detail here where the
|
||||
// number of subcompactions depends on the size of data
|
||||
// being compacted. In particular, to enable x subcompactions,
|
||||
// we need to compact at least x * target file size amount
|
||||
// of data.
|
||||
//
|
||||
// Will write two files below to avoid trivial move.
|
||||
// Size written in total: 500 * 1000 * 2 ~ 10MB ~ 100 * target file size.
|
||||
const int kValueSize = 500;
|
||||
const int kNumKeyPerFile = 1000;
|
||||
for (int i = 1; i <= 8; ++i) {
|
||||
options.max_subcompactions = i;
|
||||
SubCompactionEventListener* listener = new SubCompactionEventListener();
|
||||
options.listeners.clear();
|
||||
options.listeners.emplace_back(listener);
|
||||
TryReopen(options);
|
||||
|
||||
for (int file = 0; file < kLevel0CompactTrigger; ++file) {
|
||||
for (int key = file; key < 2 * kNumKeyPerFile; key += 2) {
|
||||
ASSERT_OK(Put(Key(key), rnd.RandomString(kValueSize)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(listener->compaction_finished_, 1);
|
||||
EXPECT_EQ(listener->sub_compaction_finished_, i);
|
||||
Destroy(options);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+2
-29
@@ -29,35 +29,8 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status DBImpl::FlushForGetLiveFiles() {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
// flush all dirty data to disk.
|
||||
Status status;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions(), FlushReason::kGetLiveFiles);
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:1");
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
return DBImpl::FlushAllColumnFamilies(FlushOptions(),
|
||||
FlushReason::kGetLiveFiles);
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
@@ -118,12 +118,13 @@ class CompactedDBImpl : public DBImpl {
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
using DB::CreateColumnFamilyWithImport;
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
const std::vector<const ExportImportFilesMetaData*>& /*metadatas*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
+15
-5
@@ -5575,14 +5575,24 @@ Status DBImpl::IngestExternalFiles(
|
||||
Status DBImpl::CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const ExportImportFilesMetaData& metadata, ColumnFamilyHandle** handle) {
|
||||
const std::vector<const ExportImportFilesMetaData*>& metadatas,
|
||||
ColumnFamilyHandle** handle) {
|
||||
assert(handle != nullptr);
|
||||
assert(*handle == nullptr);
|
||||
// TODO: plumb Env::IOActivity
|
||||
const ReadOptions read_options;
|
||||
std::string cf_comparator_name = options.comparator->Name();
|
||||
if (cf_comparator_name != metadata.db_comparator_name) {
|
||||
return Status::InvalidArgument("Comparator name mismatch");
|
||||
|
||||
size_t total_file_num = 0;
|
||||
std::vector<std::vector<LiveFileMetaData*>> metadata_files(metadatas.size());
|
||||
for (size_t i = 0; i < metadatas.size(); i++) {
|
||||
if (cf_comparator_name != metadatas[i]->db_comparator_name) {
|
||||
return Status::InvalidArgument("Comparator name mismatch");
|
||||
}
|
||||
for (auto& file : metadatas[i]->files) {
|
||||
metadata_files[i].push_back((LiveFileMetaData*)&file);
|
||||
}
|
||||
total_file_num += metadatas[i]->files.size();
|
||||
}
|
||||
|
||||
// Create column family.
|
||||
@@ -5596,7 +5606,7 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
auto cfd = cfh->cfd();
|
||||
ImportColumnFamilyJob import_job(versions_.get(), cfd, immutable_db_options_,
|
||||
file_options_, import_options,
|
||||
metadata.files, io_tracer_);
|
||||
metadata_files, io_tracer_);
|
||||
|
||||
SuperVersionContext dummy_sv_ctx(/* create_superversion */ true);
|
||||
VersionEdit dummy_edit;
|
||||
@@ -5619,7 +5629,7 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
// reuse the file number that has already assigned to the internal file,
|
||||
// and this will overwrite the external file. To protect the external
|
||||
// file, we have to make sure the file number will never being reused.
|
||||
next_file_number = versions_->FetchAddFileNumber(metadata.files.size());
|
||||
next_file_number = versions_->FetchAddFileNumber(total_file_num);
|
||||
auto cf_options = cfd->GetLatestMutableCFOptions();
|
||||
status =
|
||||
versions_->LogAndApply(cfd, *cf_options, read_options, &dummy_edit,
|
||||
|
||||
+11
-13
@@ -531,7 +531,7 @@ class DBImpl : public DB {
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& options, const std::string& column_family_name,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const ExportImportFilesMetaData& metadata,
|
||||
const std::vector<const ExportImportFilesMetaData*>& metadatas,
|
||||
ColumnFamilyHandle** handle) override;
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
@@ -811,6 +811,8 @@ class DBImpl : public DB {
|
||||
// being deleted.
|
||||
uint64_t MinObsoleteSstNumberToKeep();
|
||||
|
||||
uint64_t GetObsoleteSstFilesSize();
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
// of all files in the filesystem in 'candidate_files'.
|
||||
// If force == false and the last call was less than
|
||||
@@ -1058,16 +1060,8 @@ class DBImpl : public DB {
|
||||
|
||||
VersionSet* GetVersionSet() const { return versions_.get(); }
|
||||
|
||||
// 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);
|
||||
Status WaitForCompact(
|
||||
const WaitForCompactOptions& wait_for_compact_options) override;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Compact any files in the named level that overlap [*begin, *end]
|
||||
@@ -1106,8 +1100,9 @@ class DBImpl : public DB {
|
||||
// Wait for memtable compaction
|
||||
Status TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
// Wait for any compaction
|
||||
Status TEST_WaitForCompact(bool abort_on_pause = false);
|
||||
Status TEST_WaitForCompact();
|
||||
Status TEST_WaitForCompact(
|
||||
const WaitForCompactOptions& wait_for_compact_options);
|
||||
|
||||
// Wait for any background purge
|
||||
Status TEST_WaitForPurge();
|
||||
@@ -1417,6 +1412,9 @@ class DBImpl : public DB {
|
||||
void NotifyOnExternalFileIngested(
|
||||
ColumnFamilyData* cfd, const ExternalSstFileIngestionJob& ingestion_job);
|
||||
|
||||
Status FlushAllColumnFamilies(const FlushOptions& flush_options,
|
||||
FlushReason flush_reason);
|
||||
|
||||
virtual Status FlushForGetLiveFiles();
|
||||
|
||||
void NewThreadStatusCfInfo(ColumnFamilyData* cfd) const;
|
||||
|
||||
@@ -1066,7 +1066,6 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
std::numeric_limits<uint64_t>::max(), trim_ts);
|
||||
} else {
|
||||
int first_overlapped_level = kInvalidLevel;
|
||||
int max_overlapped_level = kInvalidLevel;
|
||||
{
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
Version* current_version = super_version->current;
|
||||
@@ -1142,10 +1141,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
begin, end);
|
||||
}
|
||||
if (overlap) {
|
||||
if (first_overlapped_level == kInvalidLevel) {
|
||||
first_overlapped_level = level;
|
||||
}
|
||||
max_overlapped_level = level;
|
||||
first_overlapped_level = level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CleanupSuperVersion(super_version);
|
||||
@@ -1159,7 +1156,7 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
end, exclusive, true /* disallow_trivial_move */,
|
||||
std::numeric_limits<uint64_t>::max() /* max_file_num_to_ignore */,
|
||||
trim_ts);
|
||||
final_output_level = max_overlapped_level;
|
||||
final_output_level = first_overlapped_level;
|
||||
} else {
|
||||
assert(cfd->ioptions()->compaction_style == kCompactionStyleLevel);
|
||||
uint64_t next_file_number = versions_->current_next_file_number();
|
||||
@@ -1171,7 +1168,29 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
int level = first_overlapped_level;
|
||||
final_output_level = level;
|
||||
int output_level = 0, base_level = 0;
|
||||
while (level < max_overlapped_level || level == 0) {
|
||||
for (;;) {
|
||||
// Always allow L0 -> L1 compaction
|
||||
if (level > 0) {
|
||||
if (cfd->ioptions()->level_compaction_dynamic_level_bytes) {
|
||||
assert(final_output_level < cfd->ioptions()->num_levels);
|
||||
if (final_output_level + 1 == cfd->ioptions()->num_levels) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// TODO(cbi): there is still a race condition here where
|
||||
// if a background compaction compacts some file beyond
|
||||
// current()->storage_info()->num_non_empty_levels() right after
|
||||
// the check here.This should happen very infrequently and should
|
||||
// not happen once a user populates the last level of the LSM.
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// num_non_empty_levels may be lower after a compaction, so
|
||||
// we check for >= here.
|
||||
if (final_output_level + 1 >=
|
||||
cfd->current()->storage_info()->num_non_empty_levels()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
output_level = level + 1;
|
||||
if (cfd->ioptions()->level_compaction_dynamic_level_bytes &&
|
||||
level == 0) {
|
||||
@@ -1203,17 +1222,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
if (s.ok()) {
|
||||
assert(final_output_level > 0);
|
||||
// bottommost level intra-level compaction
|
||||
// TODO(cbi): this preserves earlier behavior where if
|
||||
// max_overlapped_level = 0 and bottommost_level_compaction is
|
||||
// kIfHaveCompactionFilter, we only do a L0 -> LBase compaction
|
||||
// and do not do intra-LBase compaction even when user configures
|
||||
// compaction filter. We may want to still do a LBase -> LBase
|
||||
// compaction in case there is some file in LBase that did not go
|
||||
// through L0 -> LBase compaction, and hence did not go through
|
||||
// compaction filter.
|
||||
if ((options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kIfHaveCompactionFilter &&
|
||||
max_overlapped_level != 0 &&
|
||||
(cfd->ioptions()->compaction_filter != nullptr ||
|
||||
cfd->ioptions()->compaction_filter_factory != nullptr)) ||
|
||||
options.bottommost_level_compaction ==
|
||||
@@ -1221,10 +1231,11 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForce) {
|
||||
// Use `next_file_number` as `max_file_num_to_ignore` to avoid
|
||||
// rewriting newly compacted files when it is kForceOptimized.
|
||||
// rewriting newly compacted files when it is kForceOptimized
|
||||
// or kIfHaveCompactionFilter with compaction filter set.
|
||||
s = RunManualCompaction(
|
||||
cfd, final_output_level, final_output_level, options, begin,
|
||||
end, exclusive, !trim_ts.empty() /* disallow_trivial_move */,
|
||||
end, exclusive, true /* disallow_trivial_move */,
|
||||
next_file_number /* max_file_num_to_ignore */, trim_ts);
|
||||
}
|
||||
}
|
||||
@@ -1375,6 +1386,14 @@ Status DBImpl::CompactFilesImpl(
|
||||
}
|
||||
}
|
||||
|
||||
if (cfd->ioptions()->allow_ingest_behind &&
|
||||
output_level >= cfd->ioptions()->num_levels - 1) {
|
||||
return Status::InvalidArgument(
|
||||
"Exceed the maximum output level defined by "
|
||||
"the current compaction algorithm with ingest_behind --- " +
|
||||
std::to_string(cfd->ioptions()->num_levels - 1));
|
||||
}
|
||||
|
||||
Status s = cfd->compaction_picker()->SanitizeCompactionInputFiles(
|
||||
&input_set, cf_meta, output_level);
|
||||
TEST_SYNC_POINT("DBImpl::CompactFilesImpl::PostSanitizeCompactionInputFiles");
|
||||
@@ -1758,7 +1777,8 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
f->marked_for_compaction, f->temperature, f->oldest_blob_file_number,
|
||||
f->oldest_ancester_time, f->file_creation_time, f->epoch_number,
|
||||
f->file_checksum, f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size, f->tail_size);
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
f->user_defined_timestamps_persisted);
|
||||
}
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] Apply version edit:\n%s", cfd->GetName().c_str(),
|
||||
@@ -1808,6 +1828,37 @@ int DBImpl::Level0StopWriteTrigger(ColumnFamilyHandle* column_family) {
|
||||
->mutable_cf_options.level0_stop_writes_trigger;
|
||||
}
|
||||
|
||||
Status DBImpl::FlushAllColumnFamilies(const FlushOptions& flush_options,
|
||||
FlushReason flush_reason) {
|
||||
mutex_.AssertHeld();
|
||||
Status status;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(flush_options, flush_reason);
|
||||
if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : versions_->GetRefedColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, flush_options, flush_reason);
|
||||
TEST_SYNC_POINT("DBImpl::FlushAllColumnFamilies:1");
|
||||
TEST_SYNC_POINT("DBImpl::FlushAllColumnFamilies:2");
|
||||
mutex_.Lock();
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::Flush(const FlushOptions& flush_options,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
@@ -2612,6 +2663,9 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
env_->GetBackgroundThreads(Env::Priority::HIGH) == 0;
|
||||
while (!is_flush_pool_empty && unscheduled_flushes_ > 0 &&
|
||||
bg_flush_scheduled_ < bg_job_limits.max_flushes) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:BeforeSchedule",
|
||||
&unscheduled_flushes_);
|
||||
bg_flush_scheduled_++;
|
||||
FlushThreadArg* fta = new FlushThreadArg;
|
||||
fta->db_ = this;
|
||||
@@ -3457,7 +3511,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number, f->file_checksum,
|
||||
f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size, f->tail_size);
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
f->user_defined_timestamps_persisted);
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
@@ -3957,13 +4012,22 @@ void DBImpl::GetSnapshotContext(
|
||||
*snapshot_seqs = snapshots_.GetAll(earliest_write_conflict_snapshot);
|
||||
}
|
||||
|
||||
Status DBImpl::WaitForCompact(bool abort_on_pause) {
|
||||
Status DBImpl::WaitForCompact(
|
||||
const WaitForCompactOptions& wait_for_compact_options) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (wait_for_compact_options.flush) {
|
||||
Status s = DBImpl::FlushAllColumnFamilies(FlushOptions(),
|
||||
FlushReason::kManualFlush);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::WaitForCompact:StartWaiting");
|
||||
for (;;) {
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
if (bg_work_paused_ && abort_on_pause) {
|
||||
if (bg_work_paused_ && wait_for_compact_options.abort_on_pause) {
|
||||
return Status::Aborted();
|
||||
}
|
||||
if ((bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
|
||||
@@ -178,9 +178,12 @@ Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
return WaitForFlushMemTable(cfd, nullptr, false);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForCompact(bool abort_on_pause) {
|
||||
// Wait until the compaction completes
|
||||
return WaitForCompact(abort_on_pause);
|
||||
Status DBImpl::TEST_WaitForCompact() {
|
||||
return WaitForCompact(WaitForCompactOptions());
|
||||
}
|
||||
Status DBImpl::TEST_WaitForCompact(
|
||||
const WaitForCompactOptions& wait_for_compact_options) {
|
||||
return WaitForCompact(wait_for_compact_options);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForPurge() {
|
||||
|
||||
@@ -138,7 +138,8 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->epoch_number, f->file_checksum,
|
||||
f->file_checksum_func_name, f->unique_id,
|
||||
f->compensated_range_deletion_size, f->tail_size);
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
f->user_defined_timestamps_persisted);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
|
||||
@@ -35,6 +35,11 @@ uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::GetObsoleteSstFilesSize() {
|
||||
mutex_.AssertHeld();
|
||||
return versions_->GetObsoleteSstFilesSize();
|
||||
}
|
||||
|
||||
Status DBImpl::DisableFileDeletions() {
|
||||
Status s;
|
||||
int my_disable_delete_obsolete_files;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rocksdb/wal_filter.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/rate_limiter_impl.h"
|
||||
#include "util/udt_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
Options SanitizeOptions(const std::string& dbname, const Options& src,
|
||||
@@ -617,7 +618,7 @@ Status DBImpl::Recover(
|
||||
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);
|
||||
f->tail_size, f->user_defined_timestamps_persisted);
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] Moving #%" PRIu64
|
||||
" from from_level-%d to from_level-%d %" PRIu64
|
||||
@@ -1186,6 +1187,9 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& running_ts_sz =
|
||||
versions_->GetRunningColumnFamiliesTimestampSize();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::RecoverLogFiles:BeforeReadWal",
|
||||
/*arg=*/nullptr);
|
||||
uint64_t record_checksum;
|
||||
@@ -1208,6 +1212,17 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& record_ts_sz =
|
||||
reader.GetRecordedTimestampSize();
|
||||
// TODO(yuzhangyu): update mode to kReconcileInconsistency when user
|
||||
// comparator can be changed.
|
||||
status = HandleWriteBatchTimestampSizeDifference(
|
||||
&batch, running_ts_sz, record_ts_sz,
|
||||
TimestampSizeConsistencyMode::kVerifyConsistency);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::RecoverLogFiles:BeforeUpdateProtectionInfo:batch", &batch);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
@@ -1674,14 +1689,15 @@ 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, meta.tail_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, meta.user_defined_timestamps_persisted);
|
||||
|
||||
for (const auto& blob : blob_file_additions) {
|
||||
edit->AddBlobFile(blob);
|
||||
|
||||
@@ -46,7 +46,8 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
"`Env::IOActivity::kUnknown`");
|
||||
}
|
||||
assert(pinnable_val != nullptr);
|
||||
// TODO: stopwatch DB_GET needed?, perf timer needed?
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, immutable_db_options_.clock);
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
assert(column_family);
|
||||
|
||||
@@ -142,6 +142,15 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
virtual Status CreateColumnFamilyWithImport(
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const std::vector<const ExportImportFilesMetaData*>& /*metadatas*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::ClipColumnFamily;
|
||||
virtual Status ClipColumnFamily(ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*begin*/,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "rocksdb/configurable.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/write_batch_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -197,6 +198,9 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
}
|
||||
assert(reader != nullptr);
|
||||
}
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& running_ts_sz =
|
||||
versions_->GetRunningColumnFamiliesTimestampSize();
|
||||
for (auto log_number : log_numbers) {
|
||||
auto it = log_readers_.find(log_number);
|
||||
assert(it != log_readers_.end());
|
||||
@@ -224,6 +228,14 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
const UnorderedMap<uint32_t, size_t>& record_ts_sz =
|
||||
reader->GetRecordedTimestampSize();
|
||||
status = HandleWriteBatchTimestampSizeDifference(
|
||||
&batch, running_ts_sz, record_ts_sz,
|
||||
TimestampSizeConsistencyMode::kVerifyConsistency);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
SequenceNumber seq_of_batch = WriteBatchInternal::Sequence(&batch);
|
||||
std::vector<uint32_t> column_family_ids;
|
||||
status = CollectColumnFamilyIdsFromWriteBatch(batch, &column_family_ids);
|
||||
|
||||
@@ -273,85 +273,6 @@ class DBImplSecondary : public DBImpl {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// ColumnFamilyCollector is a write batch handler which does nothing
|
||||
// except recording unique column family IDs
|
||||
class ColumnFamilyCollector : public WriteBatch::Handler {
|
||||
std::unordered_set<uint32_t> column_family_ids_;
|
||||
|
||||
Status AddColumnFamilyId(uint32_t column_family_id) {
|
||||
if (column_family_ids_.find(column_family_id) ==
|
||||
column_family_ids_.end()) {
|
||||
column_family_ids_.insert(column_family_id);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ColumnFamilyCollector() {}
|
||||
|
||||
~ColumnFamilyCollector() override {}
|
||||
|
||||
Status PutCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status DeleteCF(uint32_t column_family_id, const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status SingleDeleteCF(uint32_t column_family_id, const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status DeleteRangeCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status MergeCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status PutBlobIndexCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status MarkBeginPrepare(bool) override { return Status::OK(); }
|
||||
|
||||
Status MarkEndPrepare(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkRollback(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkCommit(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkCommitWithTimestamp(const Slice&, const Slice&) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status MarkNoop(bool) override { return Status::OK(); }
|
||||
|
||||
const std::unordered_set<uint32_t>& column_families() const {
|
||||
return column_family_ids_;
|
||||
}
|
||||
};
|
||||
|
||||
Status CollectColumnFamilyIdsFromWriteBatch(
|
||||
const WriteBatch& batch, std::vector<uint32_t>* column_family_ids) {
|
||||
assert(column_family_ids != nullptr);
|
||||
column_family_ids->clear();
|
||||
ColumnFamilyCollector handler;
|
||||
Status s = batch.Iterate(&handler);
|
||||
if (s.ok()) {
|
||||
for (const auto& cf : handler.column_families()) {
|
||||
column_family_ids->push_back(cf);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool OwnTablesAndLogs() const override {
|
||||
// Currently, the secondary instance does not own the database files. It
|
||||
// simply opens the files of the primary instance and tracks their file
|
||||
|
||||
@@ -1328,7 +1328,13 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
IOStatus io_s = log_writer->AddRecord(log_entry, rate_limiter_priority);
|
||||
IOStatus io_s = log_writer->MaybeAddUserDefinedTimestampSizeRecord(
|
||||
versions_->GetColumnFamiliesTimestampSizeForRecord(),
|
||||
rate_limiter_priority);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
io_s = log_writer->AddRecord(log_entry, rate_limiter_priority);
|
||||
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
|
||||
@@ -49,10 +49,8 @@ TEST_F(DBMergeOperandTest, CacheEvictedMergeOperandReadAfterFreeBug) {
|
||||
// There was a bug of reading merge operands after they are mistakely freed
|
||||
// in DB::GetMergeOperands, which is surfaced by cache full.
|
||||
// See PR#9507 for more.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.env = env_;
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
// Small cache to simulate cache full
|
||||
@@ -121,11 +119,9 @@ TEST_F(DBMergeOperandTest, FlushedMergeOperandReadAfterFreeBug) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Options options = CurrentOptions();
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
@@ -309,13 +305,11 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, BlobDBGetMergeOperandsBasic) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Options options = CurrentOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
@@ -401,8 +395,7 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsLargeResultOptimization) {
|
||||
const int kNumOperands = 1024;
|
||||
const int kOperandLen = 1024;
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
size_t limit_ = 0;
|
||||
};
|
||||
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
@@ -134,7 +134,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnRead) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
@@ -147,7 +147,7 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnRead) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnWrite) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.max_successive_merges = 3;
|
||||
@@ -163,7 +163,7 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnWrite) {
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnIteration) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
@@ -221,7 +221,7 @@ TEST_F(DBMergeOperatorTest, MergeOperatorFailsWithMustMerge) {
|
||||
// expect "k0" and "k2" to always be readable. "k1" is expected to be readable
|
||||
// only by APIs that do not require merging, such as `GetMergeOperands()`.
|
||||
const int kNumOperands = 3;
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
@@ -361,7 +361,7 @@ TEST_F(DBMergeOperatorTest, MergeOperatorFailsWithMustMerge) {
|
||||
TEST_F(DBMergeOperatorTest, DataBlockBinaryAndHash) {
|
||||
// Basic test to check that merge operator works with data block index type
|
||||
// DataBlockBinaryAndHash.
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.env = env_;
|
||||
|
||||
@@ -582,6 +582,7 @@ TEST_F(DBOptionsTest, EnableAutoCompactionAndTriggerStall) {
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsMayTriggerCompaction) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 1000;
|
||||
options.env = env_;
|
||||
|
||||
+23
-36
@@ -188,40 +188,6 @@ TEST_F(DBPropertiesTest, GetAggregatedIntPropertyTest) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
void ResetTableProperties(TableProperties* tp) {
|
||||
tp->data_size = 0;
|
||||
tp->index_size = 0;
|
||||
tp->filter_size = 0;
|
||||
tp->raw_key_size = 0;
|
||||
tp->raw_value_size = 0;
|
||||
tp->num_data_blocks = 0;
|
||||
tp->num_entries = 0;
|
||||
tp->num_deletions = 0;
|
||||
tp->num_merge_operands = 0;
|
||||
tp->num_range_deletions = 0;
|
||||
}
|
||||
|
||||
void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
|
||||
double dummy_double;
|
||||
std::replace(tp_string.begin(), tp_string.end(), ';', ' ');
|
||||
std::replace(tp_string.begin(), tp_string.end(), '=', ' ');
|
||||
ResetTableProperties(tp);
|
||||
sscanf(tp_string.c_str(),
|
||||
"# data blocks %" SCNu64 " # entries %" SCNu64 " # deletions %" SCNu64
|
||||
" # merge operands %" SCNu64 " # range deletions %" SCNu64
|
||||
" raw key size %" SCNu64
|
||||
" raw average key size %lf "
|
||||
" raw value size %" SCNu64
|
||||
" raw average value size %lf "
|
||||
" data block size %" SCNu64 " index block size (user-key? %" SCNu64
|
||||
", delta-value? %" SCNu64 ") %" SCNu64 " filter block size %" SCNu64,
|
||||
&tp->num_data_blocks, &tp->num_entries, &tp->num_deletions,
|
||||
&tp->num_merge_operands, &tp->num_range_deletions, &tp->raw_key_size,
|
||||
&dummy_double, &tp->raw_value_size, &dummy_double, &tp->data_size,
|
||||
&tp->index_key_is_user_key, &tp->index_value_is_delta_encoded,
|
||||
&tp->index_size, &tp->filter_size);
|
||||
}
|
||||
|
||||
void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
ASSERT_EQ(a == 0U, b == 0U);
|
||||
if (a == 0) {
|
||||
@@ -1749,14 +1715,35 @@ TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
ASSERT_OK(Delete("key" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
uint64_t sst_size;
|
||||
bool ok = db_->GetIntProperty(DB::Properties::kTotalSstFilesSize, &sst_size);
|
||||
ASSERT_TRUE(ok);
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty(DB::Properties::kTotalSstFilesSize, &sst_size));
|
||||
ASSERT_GT(sst_size, 0);
|
||||
listener->size_before_compaction = sst_size;
|
||||
|
||||
uint64_t obsolete_sst_size;
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kObsoleteSstFilesSize,
|
||||
&obsolete_sst_size));
|
||||
ASSERT_EQ(obsolete_sst_size, 0);
|
||||
|
||||
// Hold files from being deleted so we can test property for size of obsolete
|
||||
// SST files.
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
|
||||
// Compact to clean all keys and trigger listener.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_TRUE(listener->callback_triggered);
|
||||
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kObsoleteSstFilesSize,
|
||||
&obsolete_sst_size));
|
||||
ASSERT_EQ(obsolete_sst_size, sst_size);
|
||||
|
||||
// Let the obsolete files be deleted.
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kObsoleteSstFilesSize,
|
||||
&obsolete_sst_size));
|
||||
ASSERT_EQ(obsolete_sst_size, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, MinObsoleteSstNumberToKeep) {
|
||||
|
||||
@@ -1690,6 +1690,8 @@ TEST_F(DBRangeDelTest, LevelCompactOutputCutAtRangeTombstoneForTtlFiles) {
|
||||
ASSERT_EQ("0,1,0,1", FilesPerLevel());
|
||||
|
||||
env_->MockSleepForSeconds(20 * 60 * 60);
|
||||
// Prevent range tombstone from being dropped during compaction.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(11), Key(12)));
|
||||
ASSERT_OK(Put(Key(0), rnd.RandomString(1 << 10)));
|
||||
@@ -1703,6 +1705,7 @@ TEST_F(DBRangeDelTest, LevelCompactOutputCutAtRangeTombstoneForTtlFiles) {
|
||||
// File 1: (qualified for TTL): Key(5) - Key(10)
|
||||
// File 1: DeleteRange [11, 12)
|
||||
ASSERT_EQ("0,3,0,1", FilesPerLevel());
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
// Test SST partitioner cut after every single key
|
||||
@@ -3405,6 +3408,73 @@ TEST_F(DBRangeDelTest, AddRangeDelsSingleUserKeyTombstoneOnlyFile) {
|
||||
db_->ReleaseSnapshot(snapshot1);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, NonBottommostCompactionDropRangetombstone) {
|
||||
// L0: file 1: [DeleteRange[4, 5)], file 2: [3, 6, DeleteRange[8, 9)]
|
||||
// L6 file 1: [2, 3], file 2: [7, 8]
|
||||
// When compacting the two L0 files to L1, the compaction is non-bottommost
|
||||
// since the compaction key range overlaps with L6 file 1. The range tombstone
|
||||
// [4, 5) should be dropped since it does not overlap with any file in lower
|
||||
// levels. The range tombstone [8, 9) should not be dropped.
|
||||
Options opts = CurrentOptions();
|
||||
opts.level_compaction_dynamic_level_bytes = false;
|
||||
opts.num_levels = 7;
|
||||
opts.level0_file_num_compaction_trigger = 3;
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
Random rnd(301);
|
||||
// L6 file 1
|
||||
ASSERT_OK(Put(Key(2), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(3), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
// L6 file 2
|
||||
ASSERT_OK(Put(Key(7), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(8), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(6), 2);
|
||||
// L0 file 1
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(4),
|
||||
Key(5)));
|
||||
ASSERT_OK(Flush());
|
||||
// L0 file 2
|
||||
ASSERT_OK(Put(Key(3), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(6), rnd.RandomString(100)));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(8),
|
||||
Key(9)));
|
||||
ASSERT_OK(Flush());
|
||||
// nothing is dropped during flush
|
||||
std::string property;
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
|
||||
TableProperties output_tp;
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
ASSERT_EQ(output_tp.num_range_deletions, 2);
|
||||
// Add one more L0 file to trigger L0->L1 compaction
|
||||
ASSERT_OK(Put(Key(1), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(9), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 1);
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
ASSERT_EQ(output_tp.num_range_deletions, 1);
|
||||
|
||||
// Now create a snapshot protected range tombstone [4, 5), it should not
|
||||
// be dropped.
|
||||
ASSERT_OK(Put(Key(4), rnd.RandomString(100)));
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(4),
|
||||
Key(5)));
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
// All compacted to L6
|
||||
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel(0));
|
||||
db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
|
||||
ParseTablePropertiesString(property, &output_tp);
|
||||
ASSERT_EQ(output_tp.num_range_deletions, 1);
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -282,6 +282,7 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
Random rnd(301);
|
||||
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 4096;
|
||||
options.max_write_buffer_number = 2;
|
||||
|
||||
+275
-162
@@ -15,6 +15,11 @@
|
||||
#include "db/forward_iterator.h"
|
||||
#include "port/stack_trace.h"
|
||||
|
||||
namespace {
|
||||
static bool enable_io_uring = true;
|
||||
extern "C" bool RocksDbIOUringEnable() { return enable_io_uring; }
|
||||
} // namespace
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBTestTailingIterator : public DBTestBase,
|
||||
@@ -50,94 +55,125 @@ TEST_P(DBTestTailingIterator, TailingIteratorSingle) {
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorKeepAdding) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
read_options.async_io = true;
|
||||
}
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::string value(1024, 'a');
|
||||
|
||||
const int num_records = 10000;
|
||||
for (int i = 0; i < num_records; ++i) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%016d", i);
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::string value(1024, 'a');
|
||||
|
||||
Slice key(buf, 16);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
const int num_records = 10000;
|
||||
for (int i = 0; i < num_records; ++i) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%016d", i);
|
||||
|
||||
iter->Seek(key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
Slice key(buf, 16);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
|
||||
iter->Seek(key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorSeekToNext) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
read_options.async_io = true;
|
||||
}
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::unique_ptr<Iterator> itern(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(itern->status());
|
||||
std::string value(1024, 'a');
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::unique_ptr<Iterator> itern(
|
||||
db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(itern->status());
|
||||
std::string value(1024, 'a');
|
||||
|
||||
const int num_records = 1000;
|
||||
for (int i = 1; i < num_records; ++i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
||||
const int num_records = 1000;
|
||||
for (int i = 1; i < num_records; ++i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
||||
|
||||
Slice key(buf1, 20);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
Slice key(buf1, 20);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush(1));
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
||||
Slice target(buf2, 20);
|
||||
iter->Seek(target);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
if (i == 1) {
|
||||
itern->SeekToFirst();
|
||||
} else {
|
||||
itern->Next();
|
||||
}
|
||||
ASSERT_TRUE(itern->Valid());
|
||||
ASSERT_EQ(itern->key().compare(key), 0);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
for (int i = 2 * num_records; i > 0; --i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
||||
Slice target(buf2, 20);
|
||||
iter->Seek(target);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
if (i == 1) {
|
||||
itern->SeekToFirst();
|
||||
} else {
|
||||
itern->Next();
|
||||
Slice key(buf1, 20);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
||||
Slice target(buf2, 20);
|
||||
iter->Seek(target);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
}
|
||||
ASSERT_TRUE(itern->Valid());
|
||||
ASSERT_EQ(itern->key().compare(key), 0);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
for (int i = 2 * num_records; i > 0; --i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
snprintf(buf1, sizeof(buf1), "00a0%016d", i * 5);
|
||||
|
||||
Slice key(buf1, 20);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
|
||||
if (i % 100 == 99) {
|
||||
ASSERT_OK(Flush(1));
|
||||
}
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "00a0%016d", i * 5 - 2);
|
||||
Slice target(buf2, 20);
|
||||
iter->Seek(target);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(key), 0);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
const uint64_t k150KB = 150 * 1024;
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options;
|
||||
options.env = env.get();
|
||||
options.write_buffer_size = k150KB;
|
||||
options.max_write_buffer_number = 3;
|
||||
options.min_write_buffer_number_to_merge = 2;
|
||||
@@ -278,56 +314,73 @@ TEST_P(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorDeletes) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
read_options.async_io = true;
|
||||
}
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
// write a single record, read it using the iterator, then delete it
|
||||
ASSERT_OK(Put(1, "0test", "test"));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "0test");
|
||||
ASSERT_OK(Delete(1, "0test"));
|
||||
|
||||
// write a single record, read it using the iterator, then delete it
|
||||
ASSERT_OK(Put(1, "0test", "test"));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "0test");
|
||||
ASSERT_OK(Delete(1, "0test"));
|
||||
// write many more records
|
||||
const int num_records = 10000;
|
||||
std::string value(1024, 'A');
|
||||
|
||||
// write many more records
|
||||
const int num_records = 10000;
|
||||
std::string value(1024, 'A');
|
||||
for (int i = 0; i < num_records; ++i) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "1%015d", i);
|
||||
|
||||
for (int i = 0; i < num_records; ++i) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "1%015d", i);
|
||||
Slice key(buf, 16);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
}
|
||||
|
||||
Slice key(buf, 16);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
// force a flush to make sure that no records are read from memtable
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
// skip "0test"
|
||||
iter->Next();
|
||||
|
||||
// make sure we can read all new records using the existing iterator
|
||||
int count = 0;
|
||||
for (; iter->Valid(); iter->Next(), ++count)
|
||||
;
|
||||
|
||||
ASSERT_EQ(count, num_records);
|
||||
}
|
||||
|
||||
// force a flush to make sure that no records are read from memtable
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
// skip "0test"
|
||||
iter->Next();
|
||||
|
||||
// make sure we can read all new records using the existing iterator
|
||||
int count = 0;
|
||||
for (; iter->Valid(); iter->Next(), ++count)
|
||||
;
|
||||
|
||||
ASSERT_EQ(count, num_records);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorPrefixSeek) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
read_options.async_io = true;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
@@ -336,28 +389,39 @@ TEST_P(DBTestTailingIterator, TailingIteratorPrefixSeek) {
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_OK(Put(1, "0101", "test"));
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_OK(Put(1, "0101", "test"));
|
||||
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
ASSERT_OK(Put(1, "0202", "test"));
|
||||
ASSERT_OK(Put(1, "0202", "test"));
|
||||
|
||||
// Seek(0102) shouldn't find any records since 0202 has a different prefix
|
||||
iter->Seek("0102");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
// Seek(0102) shouldn't find any records since 0202 has a different prefix
|
||||
iter->Seek("0102");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
|
||||
iter->Seek("0202");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "0202");
|
||||
iter->Seek("0202");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "0202");
|
||||
|
||||
iter->Next();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorIncomplete) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
if (GetParam()) {
|
||||
@@ -370,20 +434,30 @@ TEST_P(DBTestTailingIterator, TailingIteratorIncomplete) {
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
// we either see the entry or it's not in cache
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
// we either see the entry or it's not in cache
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
iter->SeekToFirst();
|
||||
// should still be true after compaction
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
iter->SeekToFirst();
|
||||
// should still be true after compaction
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorSeekToSame) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.write_buffer_size = 1000;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -403,28 +477,39 @@ TEST_P(DBTestTailingIterator, TailingIteratorSeekToSame) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
// Seek to 00001. We expect to find 00002.
|
||||
std::string start_key = "00001";
|
||||
iter->Seek(start_key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
// Seek to 00001. We expect to find 00002.
|
||||
std::string start_key = "00001";
|
||||
iter->Seek(start_key);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
std::string found = iter->key().ToString();
|
||||
ASSERT_EQ("00002", found);
|
||||
std::string found = iter->key().ToString();
|
||||
ASSERT_EQ("00002", found);
|
||||
|
||||
// Now seek to the same key. The iterator should remain in the same
|
||||
// position.
|
||||
iter->Seek(found);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(found, iter->key().ToString());
|
||||
// Now seek to the same key. The iterator should remain in the same
|
||||
// position.
|
||||
iter->Seek(found);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(found, iter->key().ToString());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
// Sets iterate_upper_bound and verifies that ForwardIterator doesn't call
|
||||
// Seek() on immutable iterators when target key is >= prev_key and all
|
||||
// iterators, including the memtable iterator, are over the upper bound.
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorUpperBound) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
const Slice upper_bound("20", 3);
|
||||
ReadOptions read_options;
|
||||
@@ -441,34 +526,51 @@ TEST_P(DBTestTailingIterator, TailingIteratorUpperBound) {
|
||||
// Add another key to the memtable.
|
||||
ASSERT_OK(Put(1, "21", "21"));
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(it->status());
|
||||
it->Seek("12");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("12", it->key().ToString());
|
||||
{
|
||||
bool read_async_called = false;
|
||||
|
||||
it->Next();
|
||||
// Not valid since "21" is over the upper bound.
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
// This keeps track of the number of times NeedToSeekImmutable() was true.
|
||||
int immutable_seeks = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::SeekInternal:Immutable",
|
||||
[&](void* /*arg*/) { ++immutable_seeks; });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"UpdateResults::io_uring_result",
|
||||
[&](void* /*arg*/) { read_async_called = true; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Seek to 13. This should not require any immutable seeks.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
it->Seek("13");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
auto it =
|
||||
std::unique_ptr<Iterator>(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(it->status());
|
||||
it->Seek("12");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("12", it->key().ToString());
|
||||
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
if (GetParam()) {
|
||||
ASSERT_EQ(1, immutable_seeks);
|
||||
} else {
|
||||
ASSERT_EQ(0, immutable_seeks);
|
||||
it->Next();
|
||||
// Not valid since "21" is over the upper bound.
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
// This keeps track of the number of times NeedToSeekImmutable() was true.
|
||||
int immutable_seeks = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"ForwardIterator::SeekInternal:Immutable",
|
||||
[&](void* /*arg*/) { ++immutable_seeks; });
|
||||
|
||||
// Seek to 13. This should not require any immutable seeks.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
it->Seek("13");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"UpdateResults::io_uring_result",
|
||||
[&](void* /*arg*/) { read_async_called = true; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_FALSE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
if (GetParam() && read_async_called) {
|
||||
ASSERT_EQ(1, immutable_seeks);
|
||||
} else {
|
||||
ASSERT_EQ(0, immutable_seeks);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, TailingIteratorGap) {
|
||||
@@ -480,7 +582,15 @@ TEST_P(DBTestTailingIterator, TailingIteratorGap) {
|
||||
// the largest key of index n file and the smallest key of index n+1 file
|
||||
// if both file fit in that gap. In this example, 25 < key < 35
|
||||
// https://github.com/facebook/rocksdb/issues/1372
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_BYPASS("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<Env> env(
|
||||
new CompositeEnvWrapper(env_, FileSystem::Default()));
|
||||
Options options = CurrentOptions();
|
||||
options.env = env.get();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
@@ -512,20 +622,23 @@ TEST_P(DBTestTailingIterator, TailingIteratorGap) {
|
||||
ColumnFamilyMetaData meta;
|
||||
db_->GetColumnFamilyMetaData(handles_[1], &meta);
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
||||
it->Seek("30");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("30", it->key().ToString());
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
||||
it->Seek("30");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("30", it->key().ToString());
|
||||
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("35", it->key().ToString());
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("35", it->key().ToString());
|
||||
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("40", it->key().ToString());
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("40", it->key().ToString());
|
||||
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_OK(it->status());
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
||||
|
||||
+7
-1
@@ -3097,7 +3097,7 @@ class ModelDB : public DB {
|
||||
const ColumnFamilyOptions& /*options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
const ImportColumnFamilyOptions& /*import_options*/,
|
||||
const ExportImportFilesMetaData& /*metadata*/,
|
||||
const std::vector<const ExportImportFilesMetaData*>& /*metadatas*/,
|
||||
ColumnFamilyHandle** /*handle*/) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
@@ -3264,6 +3264,11 @@ class ModelDB : public DB {
|
||||
|
||||
void DisableManualCompaction() override { return; }
|
||||
|
||||
virtual Status WaitForCompact(
|
||||
const WaitForCompactOptions& /* wait_for_compact_options */) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
using DB::NumberLevels;
|
||||
int NumberLevels(ColumnFamilyHandle* /*column_family*/) override { return 1; }
|
||||
|
||||
@@ -5267,6 +5272,7 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
const uint64_t k1MB = 1 << 20;
|
||||
const uint64_t k4KB = 1 << 12;
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.compression = kNoCompression;
|
||||
|
||||
+6
-5
@@ -2063,6 +2063,7 @@ class PinL0IndexAndFilterBlocksTest
|
||||
ASSERT_OK(Flush(1));
|
||||
// move this table to L1
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1, 1));
|
||||
|
||||
// reset block cache
|
||||
table_options.block_cache = NewLRUCache(64 * 1024);
|
||||
@@ -2220,7 +2221,7 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
// this should be read from L1
|
||||
value = Get(1, "a");
|
||||
if (!disallow_preload_) {
|
||||
// In inifinite max files case, there's a cache miss in executing Get()
|
||||
// In infinite max files case, there's a cache miss in executing Get()
|
||||
// because index and filter are not prefetched before.
|
||||
ASSERT_EQ(fm + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
@@ -2248,12 +2249,12 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
ASSERT_EQ(ih + 2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 4, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
ASSERT_EQ(ih + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
|
||||
// Bloom and index hit will happen when a Get() happens.
|
||||
@@ -2262,12 +2263,12 @@ TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 4, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
ASSERT_EQ(ih + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(fm + 3, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 3, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 5, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
ASSERT_EQ(ih + 4, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,12 @@ Options DBTestBase::GetDefaultOptions() const {
|
||||
options.max_open_files = 5000;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
options.compaction_pri = CompactionPri::kByCompensatedSize;
|
||||
// The original default value for this option is false,
|
||||
// and many unit tests assume this value. It also makes
|
||||
// it easier to create desired LSM shape in unit tests.
|
||||
// Unit tests for this option sets level_compaction_dynamic_level_bytes=true
|
||||
// explicitly.
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = env_;
|
||||
if (!env_->skip_fsync_) {
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
@@ -569,6 +575,8 @@ Options DBTestBase::GetOptions(
|
||||
if (set_block_based_table_factory) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
}
|
||||
options.level_compaction_dynamic_level_bytes =
|
||||
options_override.level_compaction_dynamic_level_bytes;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.fail_if_options_file_error = true;
|
||||
|
||||
@@ -114,6 +114,12 @@ struct OptionsOverride {
|
||||
|
||||
// Used as a bit mask of individual enums in which to skip an XF test point
|
||||
int skip_policy = 0;
|
||||
|
||||
// The default value for this option is changed from false to true.
|
||||
// Keeping the default to false for unit tests as old unit tests assume
|
||||
// this behavior. Tests for level_compaction_dynamic_level_bytes
|
||||
// will set the option to true explicitly.
|
||||
bool level_compaction_dynamic_level_bytes = false;
|
||||
};
|
||||
|
||||
} // namespace anon
|
||||
@@ -1327,6 +1333,40 @@ class DBTestBase : public testing::Test {
|
||||
// supported
|
||||
void SetTimeElapseOnlySleepOnReopen(DBOptions* options);
|
||||
|
||||
void ResetTableProperties(TableProperties* tp) {
|
||||
tp->data_size = 0;
|
||||
tp->index_size = 0;
|
||||
tp->filter_size = 0;
|
||||
tp->raw_key_size = 0;
|
||||
tp->raw_value_size = 0;
|
||||
tp->num_data_blocks = 0;
|
||||
tp->num_entries = 0;
|
||||
tp->num_deletions = 0;
|
||||
tp->num_merge_operands = 0;
|
||||
tp->num_range_deletions = 0;
|
||||
}
|
||||
|
||||
void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
|
||||
double dummy_double;
|
||||
std::replace(tp_string.begin(), tp_string.end(), ';', ' ');
|
||||
std::replace(tp_string.begin(), tp_string.end(), '=', ' ');
|
||||
ResetTableProperties(tp);
|
||||
sscanf(tp_string.c_str(),
|
||||
"# data blocks %" SCNu64 " # entries %" SCNu64
|
||||
" # deletions %" SCNu64 " # merge operands %" SCNu64
|
||||
" # range deletions %" SCNu64 " raw key size %" SCNu64
|
||||
" raw average key size %lf "
|
||||
" raw value size %" SCNu64
|
||||
" raw average value size %lf "
|
||||
" data block size %" SCNu64 " index block size (user-key? %" SCNu64
|
||||
", delta-value? %" SCNu64 ") %" SCNu64 " filter block size %" SCNu64,
|
||||
&tp->num_data_blocks, &tp->num_entries, &tp->num_deletions,
|
||||
&tp->num_merge_operands, &tp->num_range_deletions, &tp->raw_key_size,
|
||||
&dummy_double, &tp->raw_value_size, &dummy_double, &tp->data_size,
|
||||
&tp->index_key_is_user_key, &tp->index_value_is_delta_encoded,
|
||||
&tp->index_size, &tp->filter_size);
|
||||
}
|
||||
|
||||
private: // Prone to error on direct use
|
||||
void MaybeInstallTimeElapseOnlySleep(const DBOptions& options);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/db_with_timestamp_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -145,6 +146,11 @@ TEST_F(DBWALTestWithEnrichedEnv, SkipDeletedWALs) {
|
||||
options.write_buffer_size = 128;
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::PurgeObsoleteFiles:End",
|
||||
"DBWALTestWithEnrichedEnv.SkipDeletedWALs:AfterFlush"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions writeOpt = WriteOptions();
|
||||
for (int i = 0; i < 128 * 5; i++) {
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, "foo", "v1"));
|
||||
@@ -153,6 +159,8 @@ TEST_F(DBWALTestWithEnrichedEnv, SkipDeletedWALs) {
|
||||
fo.wait = true;
|
||||
ASSERT_OK(db_->Flush(fo));
|
||||
|
||||
TEST_SYNC_POINT("DBWALTestWithEnrichedEnv.SkipDeletedWALs:AfterFlush");
|
||||
|
||||
// some wals are deleted
|
||||
ASSERT_NE(0, enriched_env_->deleted_wal_cnt);
|
||||
// but not the first one
|
||||
@@ -163,6 +171,8 @@ TEST_F(DBWALTestWithEnrichedEnv, SkipDeletedWALs) {
|
||||
Reopen(options);
|
||||
ASSERT_FALSE(enriched_env_->deleted_wal_reopened);
|
||||
ASSERT_FALSE(enriched_env_->gap_in_wals);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, WAL) {
|
||||
@@ -301,6 +311,96 @@ TEST_F(DBWALTest, Recover) {
|
||||
} while (ChangeWalOptions());
|
||||
}
|
||||
|
||||
class DBWALTestWithTimestamp : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
DBWALTestWithTimestamp()
|
||||
: DBBasicTestWithTimestampBase("db_wal_test_with_timestamp") {}
|
||||
|
||||
Status CreateAndReopenWithCFWithTs(const std::vector<std::string>& cfs,
|
||||
const Options& options) {
|
||||
CreateColumnFamilies(cfs, options);
|
||||
return ReopenColumnFamiliesWithTs(cfs, options);
|
||||
}
|
||||
|
||||
Status ReopenColumnFamiliesWithTs(const std::vector<std::string>& cfs,
|
||||
Options ts_options) {
|
||||
Options default_options = CurrentOptions();
|
||||
default_options.create_if_missing = false;
|
||||
ts_options.create_if_missing = false;
|
||||
|
||||
std::vector<Options> cf_options(cfs.size(), ts_options);
|
||||
std::vector<std::string> cfs_plus_default = cfs;
|
||||
cfs_plus_default.insert(cfs_plus_default.begin(), kDefaultColumnFamilyName);
|
||||
cf_options.insert(cf_options.begin(), default_options);
|
||||
Close();
|
||||
return TryReopenWithColumnFamilies(cfs_plus_default, cf_options);
|
||||
}
|
||||
|
||||
Status Put(uint32_t cf, const Slice& key, const Slice& ts,
|
||||
const Slice& value) {
|
||||
WriteOptions write_opts;
|
||||
return db_->Put(write_opts, handles_[cf], key, ts, value);
|
||||
}
|
||||
|
||||
void CheckGet(const ReadOptions& read_opts, uint32_t cf, const Slice& key,
|
||||
const std::string& expected_value) {
|
||||
std::string actual_value;
|
||||
ASSERT_OK(db_->Get(read_opts, handles_[cf], key, &actual_value));
|
||||
ASSERT_EQ(expected_value, actual_value);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBWALTestWithTimestamp, Recover) {
|
||||
// Set up the option that enables user defined timestmp size.
|
||||
std::string ts = Timestamp(1, 0);
|
||||
const size_t kTimestampSize = ts.size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
Options ts_options;
|
||||
ts_options.create_if_missing = true;
|
||||
ts_options.comparator = &test_cmp;
|
||||
|
||||
ReadOptions read_opts;
|
||||
Slice ts_slice = ts;
|
||||
read_opts.timestamp = &ts_slice;
|
||||
do {
|
||||
ASSERT_OK(CreateAndReopenWithCFWithTs({"pikachu"}, ts_options));
|
||||
ASSERT_OK(Put(1, "foo", ts, "v1"));
|
||||
ASSERT_OK(Put(1, "baz", ts, "v5"));
|
||||
|
||||
ASSERT_OK(ReopenColumnFamiliesWithTs({"pikachu"}, ts_options));
|
||||
CheckGet(read_opts, 1, "foo", "v1");
|
||||
CheckGet(read_opts, 1, "baz", "v5");
|
||||
ASSERT_OK(Put(1, "bar", ts, "v2"));
|
||||
ASSERT_OK(Put(1, "foo", ts, "v3"));
|
||||
|
||||
ASSERT_OK(ReopenColumnFamiliesWithTs({"pikachu"}, ts_options));
|
||||
CheckGet(read_opts, 1, "foo", "v3");
|
||||
ASSERT_OK(Put(1, "foo", ts, "v4"));
|
||||
CheckGet(read_opts, 1, "foo", "v4");
|
||||
CheckGet(read_opts, 1, "bar", "v2");
|
||||
CheckGet(read_opts, 1, "baz", "v5");
|
||||
} while (ChangeWalOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTestWithTimestamp, RecoverInconsistentTimestamp) {
|
||||
// Set up the option that enables user defined timestmp size.
|
||||
std::string ts = Timestamp(1, 0);
|
||||
const size_t kTimestampSize = ts.size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
Options ts_options;
|
||||
ts_options.create_if_missing = true;
|
||||
ts_options.comparator = &test_cmp;
|
||||
|
||||
ASSERT_OK(CreateAndReopenWithCFWithTs({"pikachu"}, ts_options));
|
||||
ASSERT_OK(Put(1, "foo", ts, "v1"));
|
||||
ASSERT_OK(Put(1, "baz", ts, "v5"));
|
||||
|
||||
TestComparator diff_test_cmp(kTimestampSize + 1);
|
||||
ts_options.comparator = &diff_test_cmp;
|
||||
ASSERT_TRUE(
|
||||
ReopenColumnFamiliesWithTs({"pikachu"}, ts_options).IsInvalidArgument());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithTableHandle) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -101,6 +101,25 @@ void AppendUserKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
}
|
||||
}
|
||||
|
||||
void PadInternalKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(ts_sz > 0);
|
||||
size_t user_key_size = key.size() - kNumInternalBytes;
|
||||
result->reserve(key.size() + ts_sz);
|
||||
result->append(key.data(), user_key_size);
|
||||
result->append(ts_sz, static_cast<unsigned char>(0));
|
||||
result->append(key.data() + user_key_size, kNumInternalBytes);
|
||||
}
|
||||
|
||||
void StripTimestampFromInternalKey(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(key.size() >= ts_sz + kNumInternalBytes);
|
||||
result->reserve(key.size() - ts_sz);
|
||||
result->append(key.data(), key.size() - kNumInternalBytes - ts_sz);
|
||||
result->append(key.data() + key.size() - kNumInternalBytes,
|
||||
kNumInternalBytes);
|
||||
}
|
||||
|
||||
std::string ParsedInternalKey::DebugString(bool log_err_key, bool hex) const {
|
||||
std::string result = "'";
|
||||
if (log_err_key) {
|
||||
|
||||
+86
-1
@@ -195,6 +195,18 @@ extern void AppendKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
extern void AppendUserKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// `key` is an internal key containing a user key without timestamp. Create a
|
||||
// new key in *result by padding a min timestamp of size `ts_sz` to the user key
|
||||
// and copying the remaining internal key bytes.
|
||||
extern void PadInternalKeyWithMinTimestamp(std::string* result,
|
||||
const Slice& key, size_t ts_sz);
|
||||
|
||||
// `key` is an internal key containing a user key with timestamp of size
|
||||
// `ts_sz`. Create a new internal key in *result by stripping the timestamp from
|
||||
// the user key and copying the remaining internal key bytes.
|
||||
extern void StripTimestampFromInternalKey(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// Attempt to parse an internal key from "internal_key". On success,
|
||||
// stores the parsed data in "*result", and returns true.
|
||||
//
|
||||
@@ -504,6 +516,62 @@ class IterKey {
|
||||
key_size_ = total_size;
|
||||
}
|
||||
|
||||
// A version of `TrimAppend` assuming the last bytes of length `ts_sz` in the
|
||||
// user key part of `key_` is not counted towards shared bytes. And the
|
||||
// decoded key needed a min timestamp of length `ts_sz` pad to the user key.
|
||||
void TrimAppendWithTimestamp(const size_t shared_len,
|
||||
const char* non_shared_data,
|
||||
const size_t non_shared_len,
|
||||
const size_t ts_sz) {
|
||||
std::string kTsMin(ts_sz, static_cast<unsigned char>(0));
|
||||
std::string key_with_ts;
|
||||
std::vector<Slice> key_parts_with_ts;
|
||||
if (IsUserKey()) {
|
||||
key_parts_with_ts = {Slice(key_, shared_len),
|
||||
Slice(non_shared_data, non_shared_len),
|
||||
Slice(kTsMin)};
|
||||
} else {
|
||||
assert(shared_len + non_shared_len >= kNumInternalBytes);
|
||||
// Invaraint: shared_user_key_len + shared_internal_bytes_len = shared_len
|
||||
// In naming below `*_len` variables, keyword `user_key` refers to the
|
||||
// user key part of the existing key in `key_` as apposed to the new key.
|
||||
// Similary, `internal_bytes` refers to the footer part of the existing
|
||||
// key. These bytes potentially will move between user key part and the
|
||||
// footer part in the new key.
|
||||
const size_t user_key_len = key_size_ - kNumInternalBytes;
|
||||
const size_t sharable_user_key_len = user_key_len - ts_sz;
|
||||
const size_t shared_user_key_len =
|
||||
std::min(shared_len, sharable_user_key_len);
|
||||
const size_t shared_internal_bytes_len = shared_len - shared_user_key_len;
|
||||
|
||||
// One Slice among the three Slices will get split into two Slices, plus
|
||||
// a timestamp slice.
|
||||
key_parts_with_ts.reserve(5);
|
||||
bool ts_added = false;
|
||||
// Add slice parts and find the right location to add the min timestamp.
|
||||
MaybeAddKeyPartsWithTimestamp(
|
||||
key_, shared_user_key_len,
|
||||
shared_internal_bytes_len + non_shared_len < kNumInternalBytes,
|
||||
shared_len + non_shared_len - kNumInternalBytes, kTsMin,
|
||||
key_parts_with_ts, &ts_added);
|
||||
MaybeAddKeyPartsWithTimestamp(
|
||||
key_ + user_key_len, shared_internal_bytes_len,
|
||||
non_shared_len < kNumInternalBytes,
|
||||
shared_internal_bytes_len + non_shared_len - kNumInternalBytes,
|
||||
kTsMin, key_parts_with_ts, &ts_added);
|
||||
MaybeAddKeyPartsWithTimestamp(non_shared_data, non_shared_len,
|
||||
non_shared_len >= kNumInternalBytes,
|
||||
non_shared_len - kNumInternalBytes, kTsMin,
|
||||
key_parts_with_ts, &ts_added);
|
||||
assert(ts_added);
|
||||
}
|
||||
|
||||
Slice new_key(SliceParts(&key_parts_with_ts.front(),
|
||||
static_cast<int>(key_parts_with_ts.size())),
|
||||
&key_with_ts);
|
||||
SetKey(new_key);
|
||||
}
|
||||
|
||||
Slice SetKey(const Slice& key, bool copy = true) {
|
||||
// is_user_key_ expected to be set already via SetIsUserKey
|
||||
return SetKeyImpl(key, copy);
|
||||
@@ -620,7 +688,7 @@ class IterKey {
|
||||
const char* key_;
|
||||
size_t key_size_;
|
||||
size_t buf_size_;
|
||||
char space_[32]; // Avoid allocation for short keys
|
||||
char space_[39]; // Avoid allocation for short keys
|
||||
bool is_user_key_;
|
||||
|
||||
Slice SetKeyImpl(const Slice& key, bool copy) {
|
||||
@@ -661,6 +729,23 @@ class IterKey {
|
||||
}
|
||||
|
||||
void EnlargeBuffer(size_t key_size);
|
||||
|
||||
void MaybeAddKeyPartsWithTimestamp(const char* slice_data,
|
||||
const size_t slice_sz, bool add_timestamp,
|
||||
const size_t left_sz,
|
||||
const std::string& min_timestamp,
|
||||
std::vector<Slice>& key_parts,
|
||||
bool* ts_added) {
|
||||
if (add_timestamp && !*ts_added) {
|
||||
assert(slice_sz >= left_sz);
|
||||
key_parts.emplace_back(slice_data, left_sz);
|
||||
key_parts.emplace_back(min_timestamp);
|
||||
key_parts.emplace_back(slice_data + left_sz, slice_sz - left_sz);
|
||||
*ts_added = true;
|
||||
} else {
|
||||
key_parts.emplace_back(slice_data, slice_sz);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Convert from a SliceTransform of user keys, to a SliceTransform of
|
||||
|
||||
@@ -178,6 +178,79 @@ TEST_F(FormatTest, IterKeyOperation) {
|
||||
"abcdefghijklmnopqrstuvwxyz"));
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, IterKeyWithTimestampOperation) {
|
||||
IterKey k;
|
||||
k.SetUserKey("");
|
||||
const char p[] = "abcdefghijklmnopqrstuvwxyz";
|
||||
const char q[] = "0123456789";
|
||||
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
std::string(""));
|
||||
|
||||
size_t ts_sz = 8;
|
||||
std::string min_timestamp(ts_sz, static_cast<unsigned char>(0));
|
||||
k.TrimAppendWithTimestamp(0, p, 3, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abc" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(1, p, 3, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"aabc" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(0, p, 26, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(26, q, 10, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(36, q, 1, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz01234567890" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(26, q, 1, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz0" + min_timestamp);
|
||||
|
||||
k.TrimAppendWithTimestamp(27, p, 26, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetUserKey().data(), k.GetUserKey().size()),
|
||||
"abcdefghijklmnopqrstuvwxyz0"
|
||||
"abcdefghijklmnopqrstuvwxyz" +
|
||||
min_timestamp);
|
||||
// IterKey holds an internal key, the last 8 bytes hold the key footer, the
|
||||
// timestamp is expected to be added before the key footer.
|
||||
std::string key_without_ts = "keywithoutts";
|
||||
k.SetInternalKey(key_without_ts + min_timestamp + "internal");
|
||||
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
key_without_ts + min_timestamp + "internal");
|
||||
k.TrimAppendWithTimestamp(0, p, 10, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"ab" + min_timestamp + "cdefghij");
|
||||
|
||||
k.TrimAppendWithTimestamp(1, p, 8, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"a" + min_timestamp + "abcdefgh");
|
||||
|
||||
k.TrimAppendWithTimestamp(9, p, 3, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"aabc" + min_timestamp + "defghabc");
|
||||
|
||||
k.TrimAppendWithTimestamp(10, q, 10, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"aabcdefgha01" + min_timestamp + "23456789");
|
||||
|
||||
k.TrimAppendWithTimestamp(20, q, 1, ts_sz);
|
||||
ASSERT_EQ(std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"aabcdefgha012" + min_timestamp + "34567890");
|
||||
|
||||
k.TrimAppendWithTimestamp(21, p, 26, ts_sz);
|
||||
ASSERT_EQ(
|
||||
std::string(k.GetInternalKey().data(), k.GetInternalKey().size()),
|
||||
"aabcdefgha01234567890abcdefghijklmnopqr" + min_timestamp + "stuvwxyz");
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, UpdateInternalKey) {
|
||||
std::string user_key("abcdefghijklmnopqrstuvwxyz");
|
||||
uint64_t new_seq = 0x123456;
|
||||
@@ -204,6 +277,41 @@ TEST_F(FormatTest, RangeTombstoneSerializeEndKey) {
|
||||
ASSERT_LT(cmp.Compare(t.SerializeEndKey(), k), 0);
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, PadInternalKeyWithMinTimestamp) {
|
||||
std::string orig_user_key = "foo";
|
||||
std::string orig_internal_key = IKey(orig_user_key, 100, kTypeValue);
|
||||
size_t ts_sz = 8;
|
||||
|
||||
std::string key_buf;
|
||||
PadInternalKeyWithMinTimestamp(&key_buf, orig_internal_key, ts_sz);
|
||||
ParsedInternalKey key_with_timestamp;
|
||||
Slice in(key_buf);
|
||||
ASSERT_OK(ParseInternalKey(in, &key_with_timestamp, true /*log_err_key*/));
|
||||
|
||||
std::string min_timestamp(ts_sz, static_cast<unsigned char>(0));
|
||||
ASSERT_EQ(orig_user_key + min_timestamp, key_with_timestamp.user_key);
|
||||
ASSERT_EQ(100, key_with_timestamp.sequence);
|
||||
ASSERT_EQ(kTypeValue, key_with_timestamp.type);
|
||||
}
|
||||
|
||||
TEST_F(FormatTest, StripTimestampFromInternalKey) {
|
||||
std::string orig_user_key = "foo";
|
||||
size_t ts_sz = 8;
|
||||
std::string timestamp(ts_sz, static_cast<unsigned char>(0));
|
||||
orig_user_key.append(timestamp.data(), timestamp.size());
|
||||
std::string orig_internal_key = IKey(orig_user_key, 100, kTypeValue);
|
||||
|
||||
std::string key_buf;
|
||||
StripTimestampFromInternalKey(&key_buf, orig_internal_key, ts_sz);
|
||||
ParsedInternalKey key_without_timestamp;
|
||||
Slice in(key_buf);
|
||||
ASSERT_OK(ParseInternalKey(in, &key_without_timestamp, true /*log_err_key*/));
|
||||
|
||||
ASSERT_EQ("foo", key_without_timestamp.user_key);
|
||||
ASSERT_EQ(100, key_without_timestamp.sequence);
|
||||
ASSERT_EQ(kTypeValue, key_without_timestamp.type);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -279,6 +279,7 @@ const Status& ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set regular background error\n");
|
||||
@@ -416,7 +417,11 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
@@ -443,9 +448,15 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
&auto_recovery);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set background retryable IO error\n");
|
||||
@@ -486,6 +497,8 @@ const Status& ErrorHandler::SetBGError(const Status& bg_status,
|
||||
} else {
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT_MISSPELLED);
|
||||
}
|
||||
// HandleKnownErrors() will use recovery_error_, so ignore
|
||||
// recovery_io_error_.
|
||||
|
||||
+2
-1
@@ -102,7 +102,8 @@ 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->tail_size);
|
||||
lf->compensated_range_deletion_size, lf->tail_size,
|
||||
lf->user_defined_timestamps_persisted);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -482,7 +482,9 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
ingestion_options_.ingest_behind
|
||||
? kReservedEpochNumberForFileIngestedBehind
|
||||
: cfd_->NewEpochNumber(),
|
||||
f.file_checksum, f.file_checksum_func_name, f.unique_id, 0, tail_size);
|
||||
f.file_checksum, f.file_checksum_func_name, f.unique_id, 0, tail_size,
|
||||
static_cast<bool>(
|
||||
f.table_properties.user_defined_timestamps_persisted));
|
||||
f_metadata.temperature = f.file_temperature;
|
||||
edit_.AddFile(f.picked_level, f_metadata);
|
||||
}
|
||||
@@ -684,6 +686,9 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
sst_file_reader.reset(new RandomAccessFileReader(
|
||||
std::move(sst_file), external_file, nullptr /*Env*/, io_tracer_));
|
||||
|
||||
// TODO(yuzhangyu): User-defined timestamps doesn't support external sst file
|
||||
// ingestion. Pass in the correct `user_defined_timestamps_persisted` flag
|
||||
// for creating `TableReaderOptions` when the support is there.
|
||||
status = cfd_->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(
|
||||
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
|
||||
@@ -708,9 +713,9 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
ro.readahead_size = ingestion_options_.verify_checksums_readahead_size;
|
||||
status = table_reader->VerifyChecksum(
|
||||
ro, TableReaderCaller::kExternalSSTIngestion);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the external file properties
|
||||
|
||||
@@ -2160,13 +2160,13 @@ TEST_P(ExternalSSTFileTest, IngestBehind) {
|
||||
// Insert 100 -> 200 into the memtable
|
||||
for (int i = 100; i <= 200; i++) {
|
||||
ASSERT_OK(Put(Key(i), "memtable"));
|
||||
true_data[Key(i)] = "memtable";
|
||||
}
|
||||
|
||||
// Insert 100 -> 200 using IngestExternalFile
|
||||
file_data.clear();
|
||||
for (int i = 0; i <= 20; i++) {
|
||||
file_data.emplace_back(Key(i), "ingest_behind");
|
||||
true_data[Key(i)] = "ingest_behind";
|
||||
}
|
||||
|
||||
bool allow_global_seqno = true;
|
||||
@@ -2188,6 +2188,7 @@ TEST_P(ExternalSSTFileTest, IngestBehind) {
|
||||
|
||||
options.num_levels = 3;
|
||||
DestroyAndReopen(options);
|
||||
true_data.clear();
|
||||
// Insert 100 -> 200 into the memtable
|
||||
for (int i = 100; i <= 200; i++) {
|
||||
ASSERT_OK(Put(Key(i), "memtable"));
|
||||
@@ -2207,12 +2208,43 @@ TEST_P(ExternalSSTFileTest, IngestBehind) {
|
||||
verify_checksums_before_ingest, true /*ingest_behind*/,
|
||||
false /*sort_data*/, &true_data));
|
||||
ASSERT_EQ("0,1,1", FilesPerLevel());
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &level_to_files);
|
||||
uint64_t ingested_file_number = level_to_files[2][0].fd.GetNumber();
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
// bottom level should be empty
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
|
||||
// Last level should not be compacted
|
||||
ASSERT_EQ("0,1,1", FilesPerLevel());
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &level_to_files);
|
||||
ASSERT_EQ(ingested_file_number, level_to_files[2][0].fd.GetNumber());
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
|
||||
// Auto-compaction should not include the last level.
|
||||
// Trigger compaction if size amplification exceeds 110%.
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
TryReopen(options);
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
true_data[Key(j)] = rnd.RandomString(1000);
|
||||
ASSERT_OK(Put(Key(j), true_data[Key(j)]));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &level_to_files);
|
||||
ASSERT_EQ(1, level_to_files[2].size());
|
||||
ASSERT_EQ(ingested_file_number, level_to_files[2][0].fd.GetNumber());
|
||||
|
||||
// Turning off the option allows DB to compact ingested files.
|
||||
options.allow_ingest_behind = false;
|
||||
TryReopen(options);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &level_to_files);
|
||||
ASSERT_EQ(1, level_to_files[2].size());
|
||||
ASSERT_NE(ingested_file_number, level_to_files[2][0].fd.GetNumber());
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, SkipBloomFilter) {
|
||||
|
||||
+4
-1
@@ -75,6 +75,8 @@ const char* GetFlushReasonString(FlushReason flush_reason) {
|
||||
return "Manual Flush";
|
||||
case FlushReason::kErrorRecovery:
|
||||
return "Error Recovery";
|
||||
case FlushReason::kErrorRecoveryRetryFlush:
|
||||
return "Error Recovery Retry Flush";
|
||||
case FlushReason::kWalFull:
|
||||
return "WAL Full";
|
||||
default:
|
||||
@@ -352,6 +354,7 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker, FileMetaData* file_meta,
|
||||
<< (IOSTATS(cpu_read_nanos) - prev_cpu_read_nanos);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("FlushJob::End");
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1008,7 +1011,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
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);
|
||||
meta_.tail_size, meta_.user_defined_timestamps_persisted);
|
||||
edit_->SetBlobFileAdditions(std::move(blob_file_additions));
|
||||
}
|
||||
// Piggyback FlushJobInfo on the first first flushed memtable.
|
||||
|
||||
@@ -241,10 +241,10 @@ ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
if (sv_) {
|
||||
RebuildIterators(false);
|
||||
}
|
||||
if (!cfd_->ioptions()->env->GetFileSystem()->use_async_io()) {
|
||||
if (!CheckFSFeatureSupport(cfd_->ioptions()->env->GetFileSystem().get(),
|
||||
FSSupportedOps::kAsyncIO)) {
|
||||
read_options_.async_io = false;
|
||||
}
|
||||
|
||||
// immutable_status_ is a local aggregation of the
|
||||
// status of the immutable Iterators.
|
||||
// We have to PermitUncheckedError in case it is never
|
||||
@@ -1067,4 +1067,3 @@ void ForwardIterator::DeleteIterator(InternalIterator* iter, bool is_arena) {
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+156
-89
@@ -29,79 +29,132 @@ namespace ROCKSDB_NAMESPACE {
|
||||
Status ImportColumnFamilyJob::Prepare(uint64_t next_file_number,
|
||||
SuperVersion* sv) {
|
||||
Status status;
|
||||
std::vector<ColumnFamilyIngestFileInfo> cf_ingest_infos;
|
||||
for (const auto& metadata_per_cf : metadatas_) {
|
||||
// Read the information of files we are importing
|
||||
ColumnFamilyIngestFileInfo cf_file_info;
|
||||
InternalKey smallest, largest;
|
||||
int num_files = 0;
|
||||
std::vector<IngestedFileInfo> files_to_import_per_cf;
|
||||
for (size_t i = 0; i < metadata_per_cf.size(); i++) {
|
||||
auto file_metadata = *metadata_per_cf[i];
|
||||
const auto file_path = file_metadata.db_path + "/" + file_metadata.name;
|
||||
IngestedFileInfo file_to_import;
|
||||
status = GetIngestedFileInfo(file_path, next_file_number++, sv,
|
||||
file_metadata, &file_to_import);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Read the information of files we are importing
|
||||
for (const auto& file_metadata : metadata_) {
|
||||
const auto file_path = file_metadata.db_path + "/" + file_metadata.name;
|
||||
IngestedFileInfo file_to_import;
|
||||
status = GetIngestedFileInfo(file_path, next_file_number++, sv,
|
||||
file_metadata, &file_to_import);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
files_to_import_.push_back(file_to_import);
|
||||
}
|
||||
if (file_to_import.num_entries == 0) {
|
||||
status = Status::InvalidArgument("File contain no entries");
|
||||
return status;
|
||||
}
|
||||
|
||||
auto num_files = files_to_import_.size();
|
||||
if (num_files == 0) {
|
||||
status = Status::InvalidArgument("The list of files is empty");
|
||||
return status;
|
||||
}
|
||||
if (!file_to_import.smallest_internal_key.Valid() ||
|
||||
!file_to_import.largest_internal_key.Valid()) {
|
||||
status = Status::Corruption("File has corrupted keys");
|
||||
return status;
|
||||
}
|
||||
|
||||
for (const auto& f : files_to_import_) {
|
||||
if (f.num_entries == 0) {
|
||||
status = Status::InvalidArgument("File contain no entries");
|
||||
return status;
|
||||
files_to_import_per_cf.push_back(file_to_import);
|
||||
num_files++;
|
||||
|
||||
// Calculate the smallest and largest keys of all files in this CF
|
||||
if (i == 0) {
|
||||
smallest = file_to_import.smallest_internal_key;
|
||||
largest = file_to_import.largest_internal_key;
|
||||
} else {
|
||||
if (cfd_->internal_comparator().Compare(
|
||||
smallest, file_to_import.smallest_internal_key) < 0) {
|
||||
smallest = file_to_import.smallest_internal_key;
|
||||
}
|
||||
if (cfd_->internal_comparator().Compare(
|
||||
largest, file_to_import.largest_internal_key) > 0) {
|
||||
largest = file_to_import.largest_internal_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!f.smallest_internal_key.Valid() || !f.largest_internal_key.Valid()) {
|
||||
status = Status::Corruption("File has corrupted keys");
|
||||
if (num_files == 0) {
|
||||
status = Status::InvalidArgument("The list of files is empty");
|
||||
return status;
|
||||
}
|
||||
files_to_import_.push_back(files_to_import_per_cf);
|
||||
cf_file_info.smallest_internal_key = smallest;
|
||||
cf_file_info.largest_internal_key = largest;
|
||||
cf_ingest_infos.push_back(cf_file_info);
|
||||
}
|
||||
|
||||
std::sort(cf_ingest_infos.begin(), cf_ingest_infos.end(),
|
||||
[this](const ColumnFamilyIngestFileInfo& info1,
|
||||
const ColumnFamilyIngestFileInfo& info2) {
|
||||
return cfd_->user_comparator()->Compare(
|
||||
info1.smallest_internal_key.user_key(),
|
||||
info2.smallest_internal_key.user_key()) < 0;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i + 1 < cf_ingest_infos.size(); i++) {
|
||||
if (cfd_->user_comparator()->Compare(
|
||||
cf_ingest_infos[i].largest_internal_key.user_key(),
|
||||
cf_ingest_infos[i + 1].smallest_internal_key.user_key()) >= 0) {
|
||||
status = Status::InvalidArgument("CFs have overlapping ranges");
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy/Move external files into DB
|
||||
auto hardlink_files = import_options_.move_files;
|
||||
for (auto& f : files_to_import_) {
|
||||
const auto path_outside_db = f.external_file_path;
|
||||
const auto path_inside_db = TableFileName(
|
||||
cfd_->ioptions()->cf_paths, f.fd.GetNumber(), f.fd.GetPathId());
|
||||
|
||||
if (hardlink_files) {
|
||||
status =
|
||||
fs_->LinkFile(path_outside_db, path_inside_db, IOOptions(), nullptr);
|
||||
if (status.IsNotSupported()) {
|
||||
// Original file is on a different FS, use copy instead of hard linking
|
||||
hardlink_files = false;
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"Try to link file %s but it's not supported : %s",
|
||||
f.internal_file_path.c_str(), status.ToString().c_str());
|
||||
for (auto& files_to_import_per_cf : files_to_import_) {
|
||||
for (auto& f : files_to_import_per_cf) {
|
||||
const auto path_outside_db = f.external_file_path;
|
||||
const auto path_inside_db = TableFileName(
|
||||
cfd_->ioptions()->cf_paths, f.fd.GetNumber(), f.fd.GetPathId());
|
||||
|
||||
if (hardlink_files) {
|
||||
status = fs_->LinkFile(path_outside_db, path_inside_db, IOOptions(),
|
||||
nullptr);
|
||||
if (status.IsNotSupported()) {
|
||||
// Original file is on a different FS, use copy instead of hard
|
||||
// linking
|
||||
hardlink_files = false;
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"Try to link file %s but it's not supported : %s",
|
||||
f.internal_file_path.c_str(),
|
||||
status.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hardlink_files) {
|
||||
status =
|
||||
CopyFile(fs_.get(), path_outside_db, path_inside_db, 0,
|
||||
db_options_.use_fsync, io_tracer_, Temperature::kUnknown);
|
||||
if (!hardlink_files) {
|
||||
status =
|
||||
CopyFile(fs_.get(), path_outside_db, path_inside_db, 0,
|
||||
db_options_.use_fsync, io_tracer_, Temperature::kUnknown);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
f.copy_file = !hardlink_files;
|
||||
f.internal_file_path = path_inside_db;
|
||||
}
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
f.copy_file = !hardlink_files;
|
||||
f.internal_file_path = path_inside_db;
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
// We failed, remove all files that we copied into the db
|
||||
for (const auto& f : files_to_import_) {
|
||||
if (f.internal_file_path.empty()) {
|
||||
break;
|
||||
}
|
||||
const auto s =
|
||||
fs_->DeleteFile(f.internal_file_path, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
for (auto& files_to_import_per_cf : files_to_import_) {
|
||||
for (auto& f : files_to_import_per_cf) {
|
||||
if (f.internal_file_path.empty()) {
|
||||
break;
|
||||
}
|
||||
const auto s =
|
||||
fs_->DeleteFile(f.internal_file_path, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,30 +187,37 @@ Status ImportColumnFamilyJob::Run() {
|
||||
nullptr /* src_vstorage */, cfd_->ioptions()->force_consistency_checks,
|
||||
EpochNumberRequirement::kMightMissing);
|
||||
Status s;
|
||||
|
||||
for (size_t i = 0; s.ok() && i < files_to_import_.size(); ++i) {
|
||||
const auto& f = files_to_import_[i];
|
||||
const auto& file_metadata = metadata_[i];
|
||||
for (size_t j = 0; s.ok() && j < files_to_import_[i].size(); ++j) {
|
||||
const auto& f = files_to_import_[i][j];
|
||||
const auto& file_metadata = *metadatas_[i][j];
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
VersionEdit dummy_version_edit;
|
||||
dummy_version_edit.AddFile(
|
||||
file_metadata.level, f.fd.GetNumber(), f.fd.GetPathId(),
|
||||
f.fd.GetFileSize(), f.smallest_internal_key, f.largest_internal_key,
|
||||
file_metadata.smallest_seqno, file_metadata.largest_seqno, false,
|
||||
file_metadata.temperature, kInvalidBlobFileNumber,
|
||||
oldest_ancester_time, current_time, file_metadata.epoch_number,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, f.unique_id, 0,
|
||||
tail_size,
|
||||
static_cast<bool>(
|
||||
f.table_properties.user_defined_timestamps_persisted));
|
||||
s = dummy_version_builder.Apply(&dummy_version_edit);
|
||||
}
|
||||
|
||||
VersionEdit dummy_version_edit;
|
||||
dummy_version_edit.AddFile(
|
||||
file_metadata.level, f.fd.GetNumber(), f.fd.GetPathId(),
|
||||
f.fd.GetFileSize(), f.smallest_internal_key, f.largest_internal_key,
|
||||
file_metadata.smallest_seqno, file_metadata.largest_seqno, false,
|
||||
file_metadata.temperature, kInvalidBlobFileNumber, oldest_ancester_time,
|
||||
current_time, file_metadata.epoch_number, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, f.unique_id, 0, tail_size);
|
||||
s = dummy_version_builder.Apply(&dummy_version_edit);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
s = dummy_version_builder.SaveTo(&dummy_vstorage);
|
||||
}
|
||||
@@ -198,26 +258,30 @@ Status ImportColumnFamilyJob::Run() {
|
||||
void ImportColumnFamilyJob::Cleanup(const Status& status) {
|
||||
if (!status.ok()) {
|
||||
// We failed to add files to the database remove all the files we copied.
|
||||
for (const auto& f : files_to_import_) {
|
||||
const auto s =
|
||||
fs_->DeleteFile(f.internal_file_path, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
for (auto& files_to_import_per_cf : files_to_import_) {
|
||||
for (auto& f : files_to_import_per_cf) {
|
||||
const auto s =
|
||||
fs_->DeleteFile(f.internal_file_path, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (status.ok() && import_options_.move_files) {
|
||||
// The files were moved and added successfully, remove original file links
|
||||
for (IngestedFileInfo& f : files_to_import_) {
|
||||
const auto s =
|
||||
fs_->DeleteFile(f.external_file_path, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"%s was added to DB successfully but failed to remove original "
|
||||
"file link : %s",
|
||||
f.external_file_path.c_str(), s.ToString().c_str());
|
||||
for (auto& files_to_import_per_cf : files_to_import_) {
|
||||
for (auto& f : files_to_import_per_cf) {
|
||||
const auto s =
|
||||
fs_->DeleteFile(f.external_file_path, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"%s was added to DB successfully but failed to remove original "
|
||||
"file link : %s",
|
||||
f.external_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,6 +320,9 @@ Status ImportColumnFamilyJob::GetIngestedFileInfo(
|
||||
sst_file_reader.reset(new RandomAccessFileReader(
|
||||
std::move(sst_file), external_file, nullptr /*Env*/, io_tracer_));
|
||||
|
||||
// TODO(yuzhangyu): User-defined timestamps doesn't support importing column
|
||||
// family. Pass in the correct `user_defined_timestamps_persisted` flag for
|
||||
// creating `TableReaderOptions` when the support is there.
|
||||
status = cfd_->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(
|
||||
*cfd_->ioptions(), sv->mutable_cf_options.prefix_extractor,
|
||||
@@ -361,4 +428,4 @@ Status ImportColumnFamilyJob::GetIngestedFileInfo(
|
||||
|
||||
return status;
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -25,13 +25,22 @@ class SystemClock;
|
||||
// Imports a set of sst files as is into a new column family. Logic is similar
|
||||
// to ExternalSstFileIngestionJob.
|
||||
class ImportColumnFamilyJob {
|
||||
// All file information of an imported CF, mainly used to
|
||||
// calculate whether there is overlap between CFs
|
||||
struct ColumnFamilyIngestFileInfo {
|
||||
// Smallest internal key in cf
|
||||
InternalKey smallest_internal_key;
|
||||
// Largest internal key in cf
|
||||
InternalKey largest_internal_key;
|
||||
};
|
||||
|
||||
public:
|
||||
ImportColumnFamilyJob(VersionSet* versions, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const std::vector<LiveFileMetaData>& metadata,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
ImportColumnFamilyJob(
|
||||
VersionSet* versions, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options, const EnvOptions& env_options,
|
||||
const ImportColumnFamilyOptions& import_options,
|
||||
const std::vector<std::vector<LiveFileMetaData*>>& metadatas,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: clock_(db_options.clock),
|
||||
versions_(versions),
|
||||
cfd_(cfd),
|
||||
@@ -39,7 +48,7 @@ class ImportColumnFamilyJob {
|
||||
fs_(db_options_.fs, io_tracer),
|
||||
env_options_(env_options),
|
||||
import_options_(import_options),
|
||||
metadata_(metadata),
|
||||
metadatas_(metadatas),
|
||||
io_tracer_(io_tracer) {}
|
||||
|
||||
// Prepare the job by copying external files into the DB.
|
||||
@@ -54,7 +63,7 @@ class ImportColumnFamilyJob {
|
||||
|
||||
VersionEdit* edit() { return &edit_; }
|
||||
|
||||
const autovector<IngestedFileInfo>& files_to_import() const {
|
||||
const std::vector<std::vector<IngestedFileInfo>>& files_to_import() const {
|
||||
return files_to_import_;
|
||||
}
|
||||
|
||||
@@ -72,10 +81,10 @@ class ImportColumnFamilyJob {
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const FileSystemPtr fs_;
|
||||
const EnvOptions& env_options_;
|
||||
autovector<IngestedFileInfo> files_to_import_;
|
||||
std::vector<std::vector<IngestedFileInfo>> files_to_import_;
|
||||
VersionEdit edit_;
|
||||
const ImportColumnFamilyOptions& import_options_;
|
||||
std::vector<LiveFileMetaData> metadata_;
|
||||
const std::vector<std::vector<LiveFileMetaData*>> metadatas_;
|
||||
const std::shared_ptr<IOTracer> io_tracer_;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,10 +22,13 @@ class ImportColumnFamilyTest : public DBTestBase {
|
||||
: DBTestBase("import_column_family_test", /*env_do_fsync=*/true) {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
export_files_dir_ = test::PerThreadDBPath(env_, "export");
|
||||
export_files_dir2_ = test::PerThreadDBPath(env_, "export2");
|
||||
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
import_cfh_ = nullptr;
|
||||
import_cfh2_ = nullptr;
|
||||
metadata_ptr_ = nullptr;
|
||||
metadata_ptr2_ = nullptr;
|
||||
}
|
||||
|
||||
~ImportColumnFamilyTest() {
|
||||
@@ -43,14 +46,21 @@ class ImportColumnFamilyTest : public DBTestBase {
|
||||
delete metadata_ptr_;
|
||||
metadata_ptr_ = nullptr;
|
||||
}
|
||||
|
||||
if (metadata_ptr2_) {
|
||||
delete metadata_ptr2_;
|
||||
metadata_ptr2_ = nullptr;
|
||||
}
|
||||
EXPECT_OK(DestroyDir(env_, sst_files_dir_));
|
||||
EXPECT_OK(DestroyDir(env_, export_files_dir_));
|
||||
EXPECT_OK(DestroyDir(env_, export_files_dir2_));
|
||||
}
|
||||
|
||||
void DestroyAndRecreateExternalSSTFilesDir() {
|
||||
EXPECT_OK(DestroyDir(env_, sst_files_dir_));
|
||||
EXPECT_OK(env_->CreateDir(sst_files_dir_));
|
||||
EXPECT_OK(DestroyDir(env_, export_files_dir_));
|
||||
EXPECT_OK(DestroyDir(env_, export_files_dir2_));
|
||||
}
|
||||
|
||||
LiveFileMetaData LiveFileMetaDataInit(std::string name, std::string path,
|
||||
@@ -69,9 +79,11 @@ class ImportColumnFamilyTest : public DBTestBase {
|
||||
protected:
|
||||
std::string sst_files_dir_;
|
||||
std::string export_files_dir_;
|
||||
std::string export_files_dir2_;
|
||||
ColumnFamilyHandle* import_cfh_;
|
||||
ColumnFamilyHandle* import_cfh2_;
|
||||
ExportImportFilesMetaData* metadata_ptr_;
|
||||
ExportImportFilesMetaData* metadata_ptr2_;
|
||||
};
|
||||
|
||||
TEST_F(ImportColumnFamilyTest, ImportSSTFileWriterFiles) {
|
||||
@@ -738,6 +750,137 @@ TEST_F(ImportColumnFamilyTest, ImportColumnFamilyNegativeTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ImportColumnFamilyTest, ImportMultiColumnFamilyTest) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"koko"}, options);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
ASSERT_OK(Put(1, Key(i), Key(i) + "_val"));
|
||||
}
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr));
|
||||
|
||||
// Overwrite the value in the same set of keys.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
ASSERT_OK(Put(1, Key(i), Key(i) + "_overwrite"));
|
||||
}
|
||||
|
||||
// Flush again to create another L0 file. It should have higher sequencer.
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
Checkpoint* checkpoint1;
|
||||
Checkpoint* checkpoint2;
|
||||
ASSERT_OK(Checkpoint::Create(db_, &checkpoint1));
|
||||
ASSERT_OK(checkpoint1->ExportColumnFamily(handles_[1], export_files_dir_,
|
||||
&metadata_ptr_));
|
||||
|
||||
// Create a new db and import the files.
|
||||
DB* db_copy;
|
||||
ASSERT_OK(DestroyDir(env_, dbname_ + "/db_copy"));
|
||||
ASSERT_OK(DB::Open(options, dbname_ + "/db_copy", &db_copy));
|
||||
ColumnFamilyHandle* copy_cfh = nullptr;
|
||||
ASSERT_OK(db_copy->CreateColumnFamily(options, "koko", ©_cfh));
|
||||
WriteOptions wo;
|
||||
for (int i = 100; i < 200; ++i) {
|
||||
ASSERT_OK(db_copy->Put(wo, copy_cfh, Key(i), Key(i) + "_val"));
|
||||
}
|
||||
ASSERT_OK(db_copy->Flush(FlushOptions()));
|
||||
for (int i = 100; i < 200; ++i) {
|
||||
ASSERT_OK(db_copy->Put(wo, copy_cfh, Key(i), Key(i) + "_overwrite"));
|
||||
}
|
||||
ASSERT_OK(db_copy->Flush(FlushOptions()));
|
||||
for (int i = 100; i < 200; ++i) {
|
||||
ASSERT_OK(db_copy->Put(wo, copy_cfh, Key(i), Key(i) + "_overwrite2"));
|
||||
}
|
||||
ASSERT_OK(db_copy->Flush(FlushOptions()));
|
||||
|
||||
// Flush again to create another L0 file. It should have higher sequencer.
|
||||
ASSERT_OK(Checkpoint::Create(db_copy, &checkpoint2));
|
||||
ASSERT_OK(checkpoint2->ExportColumnFamily(copy_cfh, export_files_dir2_,
|
||||
&metadata_ptr2_));
|
||||
|
||||
ASSERT_NE(metadata_ptr_, nullptr);
|
||||
ASSERT_NE(metadata_ptr2_, nullptr);
|
||||
delete checkpoint1;
|
||||
delete checkpoint2;
|
||||
ImportColumnFamilyOptions import_options;
|
||||
import_options.move_files = false;
|
||||
|
||||
std::vector<const ExportImportFilesMetaData*> metadatas = {metadata_ptr_,
|
||||
metadata_ptr2_};
|
||||
ASSERT_OK(db_->CreateColumnFamilyWithImport(options, "toto", import_options,
|
||||
metadatas, &import_cfh_));
|
||||
|
||||
std::string value1, value2;
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
ASSERT_OK(db_->Get(ReadOptions(), import_cfh_, Key(i), &value1));
|
||||
ASSERT_EQ(Get(1, Key(i)), value1);
|
||||
}
|
||||
|
||||
for (int i = 100; i < 200; ++i) {
|
||||
ASSERT_OK(db_->Get(ReadOptions(), import_cfh_, Key(i), &value1));
|
||||
ASSERT_OK(db_copy->Get(ReadOptions(), copy_cfh, Key(i), &value2));
|
||||
ASSERT_EQ(value1, value2);
|
||||
}
|
||||
|
||||
ASSERT_OK(db_copy->DropColumnFamily(copy_cfh));
|
||||
ASSERT_OK(db_copy->DestroyColumnFamilyHandle(copy_cfh));
|
||||
delete db_copy;
|
||||
ASSERT_OK(DestroyDir(env_, dbname_ + "/db_copy"));
|
||||
}
|
||||
|
||||
TEST_F(ImportColumnFamilyTest, ImportMultiColumnFamilyWithOverlap) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"koko"}, options);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
ASSERT_OK(Put(1, Key(i), Key(i) + "_val"));
|
||||
}
|
||||
|
||||
Checkpoint* checkpoint1;
|
||||
Checkpoint* checkpoint2;
|
||||
ASSERT_OK(Checkpoint::Create(db_, &checkpoint1));
|
||||
ASSERT_OK(checkpoint1->ExportColumnFamily(handles_[1], export_files_dir_,
|
||||
&metadata_ptr_));
|
||||
|
||||
// Create a new db and import the files.
|
||||
DB* db_copy;
|
||||
ASSERT_OK(DestroyDir(env_, dbname_ + "/db_copy"));
|
||||
ASSERT_OK(DB::Open(options, dbname_ + "/db_copy", &db_copy));
|
||||
ColumnFamilyHandle* copy_cfh = nullptr;
|
||||
ASSERT_OK(db_copy->CreateColumnFamily(options, "koko", ©_cfh));
|
||||
WriteOptions wo;
|
||||
for (int i = 50; i < 150; ++i) {
|
||||
ASSERT_OK(db_copy->Put(wo, copy_cfh, Key(i), Key(i) + "_val"));
|
||||
}
|
||||
ASSERT_OK(db_copy->Flush(FlushOptions()));
|
||||
|
||||
// Flush again to create another L0 file. It should have higher sequencer.
|
||||
ASSERT_OK(Checkpoint::Create(db_copy, &checkpoint2));
|
||||
ASSERT_OK(checkpoint2->ExportColumnFamily(copy_cfh, export_files_dir2_,
|
||||
&metadata_ptr2_));
|
||||
|
||||
ASSERT_NE(metadata_ptr_, nullptr);
|
||||
ASSERT_NE(metadata_ptr2_, nullptr);
|
||||
delete checkpoint1;
|
||||
delete checkpoint2;
|
||||
ImportColumnFamilyOptions import_options;
|
||||
import_options.move_files = false;
|
||||
|
||||
std::vector<const ExportImportFilesMetaData*> metadatas = {metadata_ptr_,
|
||||
metadata_ptr2_};
|
||||
|
||||
ASSERT_EQ(db_->CreateColumnFamilyWithImport(options, "toto", import_options,
|
||||
metadatas, &import_cfh_),
|
||||
Status::InvalidArgument("CFs have overlapping ranges"));
|
||||
|
||||
ASSERT_OK(db_copy->DropColumnFamily(copy_cfh));
|
||||
ASSERT_OK(db_copy->DestroyColumnFamilyHandle(copy_cfh));
|
||||
delete db_copy;
|
||||
ASSERT_OK(DestroyDir(env_, dbname_ + "/db_copy"));
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+13
-1
@@ -291,6 +291,7 @@ static const std::string min_obsolete_sst_number_to_keep_str =
|
||||
static const std::string base_level_str = "base-level";
|
||||
static const std::string total_sst_files_size = "total-sst-files-size";
|
||||
static const std::string live_sst_files_size = "live-sst-files-size";
|
||||
static const std::string obsolete_sst_files_size = "obsolete-sst-files-size";
|
||||
static const std::string live_sst_files_size_at_temperature =
|
||||
"live-sst-files-size-at-temperature";
|
||||
static const std::string estimate_pending_comp_bytes =
|
||||
@@ -394,6 +395,8 @@ const std::string DB::Properties::kTotalSstFilesSize =
|
||||
rocksdb_prefix + total_sst_files_size;
|
||||
const std::string DB::Properties::kLiveSstFilesSize =
|
||||
rocksdb_prefix + live_sst_files_size;
|
||||
const std::string DB::Properties::kObsoleteSstFilesSize =
|
||||
rocksdb_prefix + obsolete_sst_files_size;
|
||||
const std::string DB::Properties::kBaseLevel = rocksdb_prefix + base_level_str;
|
||||
const std::string DB::Properties::kEstimatePendingCompactionBytes =
|
||||
rocksdb_prefix + estimate_pending_comp_bytes;
|
||||
@@ -565,6 +568,9 @@ const UnorderedMap<std::string, DBPropertyInfo>
|
||||
{DB::Properties::kLiveSstFilesSizeAtTemperature,
|
||||
{false, &InternalStats::HandleLiveSstFilesSizeAtTemperature, nullptr,
|
||||
nullptr, nullptr}},
|
||||
{DB::Properties::kObsoleteSstFilesSize,
|
||||
{false, nullptr, &InternalStats::HandleObsoleteSstFilesSize, nullptr,
|
||||
nullptr}},
|
||||
{DB::Properties::kEstimatePendingCompactionBytes,
|
||||
{false, nullptr, &InternalStats::HandleEstimatePendingCompactionBytes,
|
||||
nullptr, nullptr}},
|
||||
@@ -1395,6 +1401,12 @@ bool InternalStats::HandleLiveSstFilesSize(uint64_t* value, DBImpl* /*db*/,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleObsoleteSstFilesSize(uint64_t* value, DBImpl* db,
|
||||
Version* /*version*/) {
|
||||
*value = db->GetObsoleteSstFilesSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleEstimatePendingCompactionBytes(uint64_t* value,
|
||||
DBImpl* /*db*/,
|
||||
Version* /*version*/) {
|
||||
@@ -1738,7 +1750,7 @@ void InternalStats::DumpCFMapStats(
|
||||
assert(vstorage);
|
||||
|
||||
int num_levels_to_check =
|
||||
(cfd_->ioptions()->compaction_style != kCompactionStyleFIFO)
|
||||
(cfd_->ioptions()->compaction_style == kCompactionStyleLevel)
|
||||
? vstorage->num_levels() - 1
|
||||
: 1;
|
||||
|
||||
|
||||
@@ -819,6 +819,8 @@ class InternalStats {
|
||||
bool HandleBaseLevel(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleTotalSstFilesSize(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleLiveSstFilesSize(uint64_t* value, DBImpl* db, Version* version);
|
||||
bool HandleObsoleteSstFilesSize(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
bool HandleEstimatePendingCompactionBytes(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
bool HandleEstimateTableReadersMem(uint64_t* value, DBImpl* db,
|
||||
|
||||
@@ -551,6 +551,7 @@ class TestCompactionReasonListener : public EventListener {
|
||||
|
||||
TEST_F(EventListenerTest, CompactionReasonLevel) {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(test::NewSpecialSkipListFactory(
|
||||
|
||||
+3
-2
@@ -20,6 +20,7 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/hash_containers.h"
|
||||
#include "util/udt_util.h"
|
||||
#include "util/xxhash.h"
|
||||
|
||||
@@ -79,7 +80,7 @@ class Reader {
|
||||
|
||||
// Return the recorded user-defined timestamp size that have been read so
|
||||
// far. This only applies to WAL logs.
|
||||
const std::unordered_map<uint32_t, size_t>& GetRecordedTimestampSize() const {
|
||||
const UnorderedMap<uint32_t, size_t>& GetRecordedTimestampSize() const {
|
||||
return recorded_cf_to_ts_sz_;
|
||||
}
|
||||
|
||||
@@ -165,7 +166,7 @@ class Reader {
|
||||
|
||||
// The recorded user-defined timestamp sizes that have been read so far. This
|
||||
// is only for WAL logs.
|
||||
std::unordered_map<uint32_t, size_t> recorded_cf_to_ts_sz_;
|
||||
UnorderedMap<uint32_t, size_t> recorded_cf_to_ts_sz_;
|
||||
|
||||
// Extend record types with the following special values
|
||||
enum {
|
||||
|
||||
+17
-21
@@ -182,9 +182,8 @@ class LogTest
|
||||
|
||||
Slice* get_reader_contents() { return &reader_contents_; }
|
||||
|
||||
void Write(
|
||||
const std::string& msg,
|
||||
const std::unordered_map<uint32_t, size_t>* cf_to_ts_sz = nullptr) {
|
||||
void Write(const std::string& msg,
|
||||
const UnorderedMap<uint32_t, size_t>* cf_to_ts_sz = nullptr) {
|
||||
if (cf_to_ts_sz != nullptr && !cf_to_ts_sz->empty()) {
|
||||
ASSERT_OK(writer_->MaybeAddUserDefinedTimestampSizeRecord(*cf_to_ts_sz));
|
||||
}
|
||||
@@ -193,10 +192,9 @@ class LogTest
|
||||
|
||||
size_t WrittenBytes() const { return dest_contents().size(); }
|
||||
|
||||
std::string Read(
|
||||
const WALRecoveryMode wal_recovery_mode =
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords,
|
||||
std::unordered_map<uint32_t, size_t>* cf_to_ts_sz = nullptr) {
|
||||
std::string Read(const WALRecoveryMode wal_recovery_mode =
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords,
|
||||
UnorderedMap<uint32_t, size_t>* cf_to_ts_sz = nullptr) {
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
bool ret = false;
|
||||
@@ -270,9 +268,8 @@ class LogTest
|
||||
}
|
||||
|
||||
void CheckRecordAndTimestampSize(
|
||||
std::string record,
|
||||
std::unordered_map<uint32_t, size_t>& expected_ts_sz) {
|
||||
std::unordered_map<uint32_t, size_t> recorded_ts_sz;
|
||||
std::string record, UnorderedMap<uint32_t, size_t>& expected_ts_sz) {
|
||||
UnorderedMap<uint32_t, size_t> recorded_ts_sz;
|
||||
ASSERT_EQ(record,
|
||||
Read(WALRecoveryMode::
|
||||
kTolerateCorruptedTailRecords /* wal_recovery_mode */,
|
||||
@@ -297,18 +294,18 @@ TEST_P(LogTest, ReadWrite) {
|
||||
}
|
||||
|
||||
TEST_P(LogTest, ReadWriteWithTimestampSize) {
|
||||
std::unordered_map<uint32_t, size_t> ts_sz_one = {
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_one = {
|
||||
{1, sizeof(uint64_t)},
|
||||
};
|
||||
Write("foo", &ts_sz_one);
|
||||
Write("bar");
|
||||
std::unordered_map<uint32_t, size_t> ts_sz_two = {{2, sizeof(char)}};
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_two = {{2, sizeof(char)}};
|
||||
Write("", &ts_sz_two);
|
||||
Write("xxxx");
|
||||
|
||||
CheckRecordAndTimestampSize("foo", ts_sz_one);
|
||||
CheckRecordAndTimestampSize("bar", ts_sz_one);
|
||||
std::unordered_map<uint32_t, size_t> expected_ts_sz_two;
|
||||
UnorderedMap<uint32_t, size_t> expected_ts_sz_two;
|
||||
// User-defined timestamp size records are accumulated and applied to
|
||||
// subsequent records.
|
||||
expected_ts_sz_two.insert(ts_sz_one.begin(), ts_sz_one.end());
|
||||
@@ -320,10 +317,9 @@ TEST_P(LogTest, ReadWriteWithTimestampSize) {
|
||||
}
|
||||
|
||||
TEST_P(LogTest, ReadWriteWithTimestampSizeZeroTimestampIgnored) {
|
||||
std::unordered_map<uint32_t, size_t> ts_sz_one = {{1, sizeof(uint64_t)}};
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_one = {{1, sizeof(uint64_t)}};
|
||||
Write("foo", &ts_sz_one);
|
||||
std::unordered_map<uint32_t, size_t> ts_sz_two(ts_sz_one.begin(),
|
||||
ts_sz_one.end());
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_two(ts_sz_one.begin(), ts_sz_one.end());
|
||||
ts_sz_two.insert(std::make_pair(2, 0));
|
||||
Write("bar", &ts_sz_two);
|
||||
|
||||
@@ -749,7 +745,7 @@ TEST_P(LogTest, RecycleWithTimestampSize) {
|
||||
if (!recyclable_log) {
|
||||
return; // test is only valid for recycled logs
|
||||
}
|
||||
std::unordered_map<uint32_t, size_t> ts_sz_one = {
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_one = {
|
||||
{1, sizeof(uint32_t)},
|
||||
};
|
||||
Write("foo", &ts_sz_one);
|
||||
@@ -765,7 +761,7 @@ TEST_P(LogTest, RecycleWithTimestampSize) {
|
||||
std::unique_ptr<WritableFileWriter> dest_holder(new WritableFileWriter(
|
||||
std::move(sink), "" /* don't care */, FileOptions()));
|
||||
Writer recycle_writer(std::move(dest_holder), 123, true);
|
||||
std::unordered_map<uint32_t, size_t> ts_sz_two = {
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_two = {
|
||||
{2, sizeof(uint64_t)},
|
||||
};
|
||||
ASSERT_OK(recycle_writer.MaybeAddUserDefinedTimestampSizeRecord(ts_sz_two));
|
||||
@@ -1039,18 +1035,18 @@ TEST_P(CompressionLogTest, ReadWriteWithTimestampSize) {
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
std::unordered_map<uint32_t, size_t> ts_sz_one = {
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_one = {
|
||||
{1, sizeof(uint64_t)},
|
||||
};
|
||||
Write("foo", &ts_sz_one);
|
||||
Write("bar");
|
||||
std::unordered_map<uint32_t, size_t> ts_sz_two = {{2, sizeof(char)}};
|
||||
UnorderedMap<uint32_t, size_t> ts_sz_two = {{2, sizeof(char)}};
|
||||
Write("", &ts_sz_two);
|
||||
Write("xxxx");
|
||||
|
||||
CheckRecordAndTimestampSize("foo", ts_sz_one);
|
||||
CheckRecordAndTimestampSize("bar", ts_sz_one);
|
||||
std::unordered_map<uint32_t, size_t> expected_ts_sz_two;
|
||||
UnorderedMap<uint32_t, size_t> expected_ts_sz_two;
|
||||
// User-defined timestamp size records are accumulated and applied to
|
||||
// subsequent records.
|
||||
expected_ts_sz_two.insert(ts_sz_one.begin(), ts_sz_one.end());
|
||||
|
||||
+1
-1
@@ -197,7 +197,7 @@ IOStatus Writer::AddCompressionTypeRecord() {
|
||||
}
|
||||
|
||||
IOStatus Writer::MaybeAddUserDefinedTimestampSizeRecord(
|
||||
const std::unordered_map<uint32_t, size_t>& cf_to_ts_sz,
|
||||
const UnorderedMap<uint32_t, size_t>& cf_to_ts_sz,
|
||||
Env::IOPriority rate_limiter_priority) {
|
||||
std::vector<std::pair<uint32_t, size_t>> ts_sz_to_record;
|
||||
for (const auto& [cf_id, ts_sz] : cf_to_ts_sz) {
|
||||
|
||||
+3
-2
@@ -20,6 +20,7 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/hash_containers.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -95,7 +96,7 @@ class Writer {
|
||||
// kRecyclableUserDefinedTimestampSizeType for these column families.
|
||||
// This timestamp size record applies to all subsequent records.
|
||||
IOStatus MaybeAddUserDefinedTimestampSizeRecord(
|
||||
const std::unordered_map<uint32_t, size_t>& cf_to_ts_sz,
|
||||
const UnorderedMap<uint32_t, size_t>& cf_to_ts_sz,
|
||||
Env::IOPriority rate_limiter_priority = Env::IO_TOTAL);
|
||||
|
||||
WritableFileWriter* file() { return dest_.get(); }
|
||||
@@ -137,7 +138,7 @@ class Writer {
|
||||
// The recorded user-defined timestamp size that have been written so far.
|
||||
// Since the user-defined timestamp size cannot be changed while the DB is
|
||||
// running, existing entry in this map cannot be updated.
|
||||
std::unordered_map<uint32_t, size_t> recorded_cf_to_ts_sz_;
|
||||
UnorderedMap<uint32_t, size_t> recorded_cf_to_ts_sz_;
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
|
||||
@@ -190,6 +190,7 @@ TEST_F(ManualCompactionTest, Test) {
|
||||
TEST_F(ManualCompactionTest, SkipLevel) {
|
||||
DB* db;
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
options.num_levels = 3;
|
||||
// Initially, flushed L0 files won't exceed 100.
|
||||
options.level0_file_num_compaction_trigger = 100;
|
||||
@@ -286,9 +287,9 @@ TEST_F(ManualCompactionTest, SkipLevel) {
|
||||
filter->Reset();
|
||||
ASSERT_OK(db->CompactRange(CompactRangeOptions(), &start, nullptr));
|
||||
ASSERT_EQ(4, filter->NumKeys());
|
||||
// 1 is first compacted to L1 and then further compacted into [2, 4, 8],
|
||||
// so finally the logged level for 1 is L1.
|
||||
ASSERT_EQ(1, filter->KeyLevel("1"));
|
||||
// 1 is first compacted from L0 to L1, and then L1 intra level compaction
|
||||
// compacts [2, 4, 8] only.
|
||||
ASSERT_EQ(0, filter->KeyLevel("1"));
|
||||
ASSERT_EQ(1, filter->KeyLevel("2"));
|
||||
ASSERT_EQ(1, filter->KeyLevel("4"));
|
||||
ASSERT_EQ(1, filter->KeyLevel("8"));
|
||||
|
||||
+1
-1
@@ -596,7 +596,7 @@ class MemTable {
|
||||
const SliceTransform* insert_with_hint_prefix_extractor_;
|
||||
|
||||
// Insert hints for each prefix.
|
||||
UnorderedMapH<Slice, void*, SliceHasher> insert_hints_;
|
||||
UnorderedMapH<Slice, void*, SliceHasher32> insert_hints_;
|
||||
|
||||
// Timestamp of oldest key
|
||||
std::atomic<uint64_t> oldest_key_time_;
|
||||
|
||||
@@ -123,6 +123,7 @@ class PlainTableDBTest : public testing::Test,
|
||||
// Return the current option configuration.
|
||||
Options CurrentOptions() {
|
||||
Options options;
|
||||
options.level_compaction_dynamic_level_bytes = false;
|
||||
|
||||
PlainTableOptions plain_table_options;
|
||||
plain_table_options.user_key_len = 0;
|
||||
|
||||
+16
-3
@@ -394,9 +394,12 @@ class Repairer {
|
||||
auto cf_mems = new ColumnFamilyMemTablesImpl(vset_.GetColumnFamilySet());
|
||||
|
||||
// Read all the records and add to a memtable
|
||||
const UnorderedMap<uint32_t, size_t>& running_ts_sz =
|
||||
vset_.GetRunningColumnFamiliesTimestampSize();
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
WriteBatch batch;
|
||||
|
||||
int counter = 0;
|
||||
while (reader.ReadRecord(&record, &scratch)) {
|
||||
if (record.size() < WriteBatchInternal::kHeader) {
|
||||
@@ -406,8 +409,15 @@ class Repairer {
|
||||
}
|
||||
Status record_status = WriteBatchInternal::SetContents(&batch, record);
|
||||
if (record_status.ok()) {
|
||||
record_status =
|
||||
WriteBatchInternal::InsertInto(&batch, cf_mems, nullptr, nullptr);
|
||||
const UnorderedMap<uint32_t, size_t>& record_ts_sz =
|
||||
reader.GetRecordedTimestampSize();
|
||||
record_status = HandleWriteBatchTimestampSizeDifference(
|
||||
&batch, running_ts_sz, record_ts_sz,
|
||||
TimestampSizeConsistencyMode::kVerifyConsistency);
|
||||
if (record_status.ok()) {
|
||||
record_status =
|
||||
WriteBatchInternal::InsertInto(&batch, cf_mems, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
if (record_status.ok()) {
|
||||
counter += WriteBatchInternal::Count(&batch);
|
||||
@@ -550,6 +560,8 @@ class Repairer {
|
||||
AddColumnFamily(props->column_family_name, t->column_family_id);
|
||||
}
|
||||
t->meta.oldest_ancester_time = props->creation_time;
|
||||
t->meta.user_defined_timestamps_persisted =
|
||||
static_cast<bool>(props->user_defined_timestamps_persisted);
|
||||
}
|
||||
if (status.ok()) {
|
||||
uint64_t tail_size = 0;
|
||||
@@ -693,7 +705,8 @@ class Repairer {
|
||||
table->meta.oldest_ancester_time, table->meta.file_creation_time,
|
||||
table->meta.epoch_number, table->meta.file_checksum,
|
||||
table->meta.file_checksum_func_name, table->meta.unique_id,
|
||||
table->meta.compensated_range_deletion_size, table->meta.tail_size);
|
||||
table->meta.compensated_range_deletion_size, table->meta.tail_size,
|
||||
table->meta.user_defined_timestamps_persisted);
|
||||
}
|
||||
s = dummy_version_builder.Apply(&dummy_edit);
|
||||
if (s.ok()) {
|
||||
|
||||
+74
-2
@@ -3,17 +3,17 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/db_with_timestamp_test_util.h"
|
||||
#include "file/file_util.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -315,6 +315,78 @@ TEST_F(RepairTest, UnflushedSst) {
|
||||
ASSERT_EQ(Get("key"), "val");
|
||||
}
|
||||
|
||||
class RepairTestWithTimestamp : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
RepairTestWithTimestamp()
|
||||
: DBBasicTestWithTimestampBase("repair_test_with_timestamp") {}
|
||||
|
||||
Status Put(const Slice& key, const Slice& ts, const Slice& value) {
|
||||
WriteOptions write_opts;
|
||||
return db_->Put(write_opts, handles_[0], key, ts, value);
|
||||
}
|
||||
|
||||
void CheckGet(const ReadOptions& read_opts, const Slice& key,
|
||||
const std::string& expected_value) {
|
||||
std::string actual_value;
|
||||
ASSERT_OK(db_->Get(read_opts, handles_[0], key, &actual_value));
|
||||
ASSERT_EQ(expected_value, actual_value);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(RepairTestWithTimestamp, UnflushedSst) {
|
||||
Destroy(last_options_);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
std::string ts = Timestamp(0, 0);
|
||||
const size_t kTimestampSize = ts.size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
|
||||
ASSERT_OK(DB::Open(options, dbname_, column_families, &handles_, &db_));
|
||||
|
||||
ASSERT_OK(Put("key", ts, "val"));
|
||||
VectorLogPtr wal_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
||||
ASSERT_EQ(wal_files.size(), 1);
|
||||
{
|
||||
uint64_t total_ssts_size;
|
||||
std::unordered_map<std::string, uint64_t> sst_files;
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
||||
ASSERT_EQ(total_ssts_size, 0);
|
||||
}
|
||||
// Need to get path before Close() deletes db_, but delete it after Close() to
|
||||
// ensure Close() didn't change the manifest.
|
||||
std::string manifest_path =
|
||||
DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
|
||||
|
||||
Close();
|
||||
ASSERT_OK(env_->FileExists(manifest_path));
|
||||
ASSERT_OK(env_->DeleteFile(manifest_path));
|
||||
ASSERT_OK(RepairDB(dbname_, options));
|
||||
ASSERT_OK(DB::Open(options, dbname_, column_families, &handles_, &db_));
|
||||
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
||||
ASSERT_EQ(wal_files.size(), 0);
|
||||
{
|
||||
uint64_t total_ssts_size;
|
||||
std::unordered_map<std::string, uint64_t> sst_files;
|
||||
ASSERT_OK(GetAllDataFiles(kTableFile, &sst_files, &total_ssts_size));
|
||||
ASSERT_GT(total_ssts_size, 0);
|
||||
}
|
||||
|
||||
ReadOptions read_opts;
|
||||
Slice read_ts_slice = ts;
|
||||
read_opts.timestamp = &read_ts_slice;
|
||||
CheckGet(read_opts, "key", "val");
|
||||
}
|
||||
|
||||
TEST_F(RepairTest, SeparateWalDir) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -93,7 +93,7 @@ TEST_F(SeqnoTimeTest, TemperatureBasicUniversal) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// All data is hot, only output to penultimate level
|
||||
ASSERT_EQ("0,0,0,0,0,1", FilesPerLevel());
|
||||
@@ -114,7 +114,7 @@ TEST_F(SeqnoTimeTest, TemperatureBasicUniversal) {
|
||||
});
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
}
|
||||
@@ -128,7 +128,7 @@ TEST_F(SeqnoTimeTest, TemperatureBasicUniversal) {
|
||||
});
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->WaitForCompact());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
CompactRangeOptions cro;
|
||||
@@ -226,7 +226,8 @@ TEST_F(SeqnoTimeTest, TemperatureBasicLevel) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
// Second to last level
|
||||
MoveFilesToLevel(5);
|
||||
ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
|
||||
ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
|
||||
|
||||
|
||||
+25
-27
@@ -75,7 +75,7 @@ TableCache::TableCache(const ImmutableOptions& ioptions,
|
||||
cache_(cache),
|
||||
immortal_tables_(false),
|
||||
block_cache_tracer_(block_cache_tracer),
|
||||
loader_mutex_(kLoadConcurency, kGetSliceNPHash64UnseededFnPtr),
|
||||
loader_mutex_(kLoadConcurency),
|
||||
io_tracer_(io_tracer),
|
||||
db_session_id_(db_session_id) {
|
||||
if (ioptions_.row_cache) {
|
||||
@@ -90,7 +90,7 @@ TableCache::~TableCache() {}
|
||||
Status TableCache::GetTableReader(
|
||||
const ReadOptions& ro, const FileOptions& file_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, bool sequential_mode, bool record_read_stats,
|
||||
const FileMetaData& file_meta, bool sequential_mode,
|
||||
uint8_t block_protection_bytes_per_key, HistogramImpl* file_read_hist,
|
||||
std::unique_ptr<TableReader>* table_reader,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
@@ -127,11 +127,11 @@ Status TableCache::GetTableReader(
|
||||
}
|
||||
StopWatch sw(ioptions_.clock, ioptions_.stats, TABLE_OPEN_IO_MICROS);
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(
|
||||
std::move(file), fname, ioptions_.clock, io_tracer_,
|
||||
record_read_stats ? ioptions_.stats : nullptr, SST_READ_MICROS,
|
||||
file_read_hist, ioptions_.rate_limiter.get(), ioptions_.listeners,
|
||||
file_temperature, level == ioptions_.num_levels - 1));
|
||||
new RandomAccessFileReader(std::move(file), fname, ioptions_.clock,
|
||||
io_tracer_, ioptions_.stats, SST_READ_MICROS,
|
||||
file_read_hist, ioptions_.rate_limiter.get(),
|
||||
ioptions_.listeners, file_temperature,
|
||||
level == ioptions_.num_levels - 1));
|
||||
UniqueId64x2 expected_unique_id;
|
||||
if (ioptions_.verify_sst_unique_id_in_manifest) {
|
||||
expected_unique_id = file_meta.unique_id;
|
||||
@@ -146,7 +146,8 @@ Status TableCache::GetTableReader(
|
||||
false /* force_direct_prefetch */, level, block_cache_tracer_,
|
||||
max_file_size_for_l0_meta_pin, db_session_id_,
|
||||
file_meta.fd.GetNumber(), expected_unique_id,
|
||||
file_meta.fd.largest_seqno, file_meta.tail_size),
|
||||
file_meta.fd.largest_seqno, file_meta.tail_size,
|
||||
file_meta.user_defined_timestamps_persisted),
|
||||
std::move(file_reader), file_meta.fd.GetFileSize(), table_reader,
|
||||
prefetch_index_and_filter_in_cache);
|
||||
TEST_SYNC_POINT("TableCache::GetTableReader:0");
|
||||
@@ -160,8 +161,8 @@ Status TableCache::FindTable(
|
||||
const FileMetaData& file_meta, TypedHandle** handle,
|
||||
uint8_t block_protection_bytes_per_key,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor,
|
||||
const bool no_io, bool record_read_stats, HistogramImpl* file_read_hist,
|
||||
bool skip_filters, int level, bool prefetch_index_and_filter_in_cache,
|
||||
const bool no_io, HistogramImpl* file_read_hist, bool skip_filters,
|
||||
int level, bool prefetch_index_and_filter_in_cache,
|
||||
size_t max_file_size_for_l0_meta_pin, Temperature file_temperature) {
|
||||
PERF_TIMER_GUARD_WITH_CLOCK(find_table_nanos, ioptions_.clock);
|
||||
uint64_t number = file_meta.fd.GetNumber();
|
||||
@@ -174,7 +175,7 @@ Status TableCache::FindTable(
|
||||
if (no_io) {
|
||||
return Status::Incomplete("Table not found in table_cache, no_io is set");
|
||||
}
|
||||
MutexLock load_lock(loader_mutex_.get(key));
|
||||
MutexLock load_lock(&loader_mutex_.Get(key));
|
||||
// We check the cache again under loading mutex
|
||||
*handle = cache_.Lookup(key);
|
||||
if (*handle != nullptr) {
|
||||
@@ -183,7 +184,7 @@ Status TableCache::FindTable(
|
||||
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
Status s = GetTableReader(ro, file_options, internal_comparator, file_meta,
|
||||
false /* sequential mode */, record_read_stats,
|
||||
false /* sequential mode */,
|
||||
block_protection_bytes_per_key, file_read_hist,
|
||||
&table_reader, prefix_extractor, skip_filters,
|
||||
level, prefetch_index_and_filter_in_cache,
|
||||
@@ -232,8 +233,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
s = FindTable(options, file_options, icomparator, file_meta, &handle,
|
||||
block_protection_bytes_per_key, prefix_extractor,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
!for_compaction /* record_read_stats */, file_read_hist,
|
||||
skip_filters, level,
|
||||
file_read_hist, skip_filters, level,
|
||||
true /* prefetch_index_and_filter_in_cache */,
|
||||
max_file_size_for_l0_meta_pin, file_meta.temperature);
|
||||
if (s.ok()) {
|
||||
@@ -438,8 +438,8 @@ Status TableCache::Get(
|
||||
s = FindTable(options, file_options_, internal_comparator, file_meta,
|
||||
&handle, block_protection_bytes_per_key, prefix_extractor,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
true /* record_read_stats */, file_read_hist, skip_filters,
|
||||
level, true /* prefetch_index_and_filter_in_cache */,
|
||||
file_read_hist, skip_filters, level,
|
||||
true /* prefetch_index_and_filter_in_cache */,
|
||||
max_file_size_for_l0_meta_pin, file_meta.temperature);
|
||||
if (s.ok()) {
|
||||
t = cache_.Value(handle);
|
||||
@@ -541,7 +541,7 @@ Status TableCache::MultiGetFilter(
|
||||
s = FindTable(options, file_options_, internal_comparator, file_meta,
|
||||
&handle, block_protection_bytes_per_key, prefix_extractor,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
true /* record_read_stats */, file_read_hist,
|
||||
file_read_hist,
|
||||
/*skip_filters=*/false, level,
|
||||
true /* prefetch_index_and_filter_in_cache */,
|
||||
/*max_file_size_for_l0_meta_pin=*/0, file_meta.temperature);
|
||||
@@ -658,11 +658,10 @@ uint64_t TableCache::ApproximateOffsetOf(
|
||||
TableReader* table_reader = file_meta.fd.table_reader;
|
||||
TypedHandle* table_handle = nullptr;
|
||||
if (table_reader == nullptr) {
|
||||
const bool for_compaction = (caller == TableReaderCaller::kCompaction);
|
||||
Status s = FindTable(
|
||||
read_options, file_options_, internal_comparator, file_meta,
|
||||
&table_handle, block_protection_bytes_per_key, prefix_extractor,
|
||||
false /* no_io */, !for_compaction /* record_read_stats */);
|
||||
Status s =
|
||||
FindTable(read_options, file_options_, internal_comparator, file_meta,
|
||||
&table_handle, block_protection_bytes_per_key,
|
||||
prefix_extractor, false /* no_io */);
|
||||
if (s.ok()) {
|
||||
table_reader = cache_.Value(table_handle);
|
||||
}
|
||||
@@ -688,11 +687,10 @@ uint64_t TableCache::ApproximateSize(
|
||||
TableReader* table_reader = file_meta.fd.table_reader;
|
||||
TypedHandle* table_handle = nullptr;
|
||||
if (table_reader == nullptr) {
|
||||
const bool for_compaction = (caller == TableReaderCaller::kCompaction);
|
||||
Status s = FindTable(
|
||||
read_options, file_options_, internal_comparator, file_meta,
|
||||
&table_handle, block_protection_bytes_per_key, prefix_extractor,
|
||||
false /* no_io */, !for_compaction /* record_read_stats */);
|
||||
Status s =
|
||||
FindTable(read_options, file_options_, internal_comparator, file_meta,
|
||||
&table_handle, block_protection_bytes_per_key,
|
||||
prefix_extractor, false /* no_io */);
|
||||
if (s.ok()) {
|
||||
table_reader = cache_.Value(table_handle);
|
||||
}
|
||||
|
||||
+6
-6
@@ -171,9 +171,9 @@ class TableCache {
|
||||
const FileMetaData& file_meta, TypedHandle**,
|
||||
uint8_t block_protection_bytes_per_key,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
|
||||
const bool no_io = false, bool record_read_stats = true,
|
||||
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
|
||||
int level = -1, bool prefetch_index_and_filter_in_cache = true,
|
||||
const bool no_io = false, HistogramImpl* file_read_hist = nullptr,
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
size_t max_file_size_for_l0_meta_pin = 0,
|
||||
Temperature file_temperature = Temperature::kUnknown);
|
||||
|
||||
@@ -243,8 +243,8 @@ class TableCache {
|
||||
const ReadOptions& ro, const FileOptions& file_options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const FileMetaData& file_meta, bool sequential_mode,
|
||||
bool record_read_stats, uint8_t block_protection_bytes_per_key,
|
||||
HistogramImpl* file_read_hist, std::unique_ptr<TableReader>* table_reader,
|
||||
uint8_t block_protection_bytes_per_key, HistogramImpl* file_read_hist,
|
||||
std::unique_ptr<TableReader>* table_reader,
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor = nullptr,
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
@@ -275,7 +275,7 @@ class TableCache {
|
||||
std::string row_cache_id_;
|
||||
bool immortal_tables_;
|
||||
BlockCacheTracer* const block_cache_tracer_;
|
||||
Striped<port::Mutex, Slice> loader_mutex_;
|
||||
Striped<CacheAlignedWrapper<port::Mutex>> loader_mutex_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
std::string db_session_id_;
|
||||
};
|
||||
|
||||
@@ -68,8 +68,8 @@ DEFINE_SYNC_AND_ASYNC(Status, TableCache::MultiGet)
|
||||
s = FindTable(options, file_options_, internal_comparator, file_meta,
|
||||
&handle, block_protection_bytes_per_key, prefix_extractor,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
true /* record_read_stats */, file_read_hist, skip_filters,
|
||||
level, true /* prefetch_index_and_filter_in_cache */,
|
||||
file_read_hist, skip_filters, level,
|
||||
true /* prefetch_index_and_filter_in_cache */,
|
||||
0 /*max_file_size_for_l0_meta_pin*/, file_meta.temperature);
|
||||
TEST_SYNC_POINT_CALLBACK("TableCache::MultiGet:FindTable", &s);
|
||||
if (s.ok()) {
|
||||
|
||||
@@ -1328,7 +1328,6 @@ class VersionBuilder::Rep {
|
||||
read_options, file_options_,
|
||||
*(base_vstorage_->InternalComparator()), *file_meta, &handle,
|
||||
block_protection_bytes_per_key, prefix_extractor, false /*no_io */,
|
||||
true /* record_read_stats */,
|
||||
internal_stats->GetFileReadHist(level), false, level,
|
||||
prefetch_index_and_filter_in_cache, max_file_size_for_l0_meta_pin,
|
||||
file_meta->temperature);
|
||||
|
||||
+155
-146
@@ -73,7 +73,8 @@ class VersionBuilderTest : public testing::Test {
|
||||
/* marked_for_compact */ false, Temperature::kUnknown,
|
||||
oldest_blob_file_number, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, epoch_number, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
f->compensated_file_size = file_size;
|
||||
f->num_entries = num_entries;
|
||||
f->num_deletions = num_deletions;
|
||||
@@ -130,13 +131,13 @@ class VersionBuilderTest : public testing::Test {
|
||||
constexpr SequenceNumber largest_seqno = 300;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
edit->AddFile(level, table_file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
Temperature::kUnknown, blob_file_number,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
epoch_number, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
edit->AddFile(
|
||||
level, table_file_number, path_id, file_size, GetInternalKey(smallest),
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, Temperature::kUnknown, blob_file_number,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, epoch_number,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2,
|
||||
0, 0, /* user_defined_timestamps_persisted */ true);
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo(VersionStorageInfo* vstorage) {
|
||||
@@ -183,12 +184,12 @@ TEST_F(VersionBuilderTest, ApplyAndSaveTo) {
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(2, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(
|
||||
2, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.DeleteFile(3, 27U);
|
||||
|
||||
EnvOptions env_options;
|
||||
@@ -231,12 +232,12 @@ TEST_F(VersionBuilderTest, ApplyAndSaveToDynamic) {
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(3, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(
|
||||
3, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
version_edit.DeleteFile(0, 1U);
|
||||
version_edit.DeleteFile(0, 88U);
|
||||
@@ -283,12 +284,12 @@ TEST_F(VersionBuilderTest, ApplyAndSaveToDynamic2) {
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(4, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(
|
||||
4, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.DeleteFile(0, 1U);
|
||||
version_edit.DeleteFile(0, 88U);
|
||||
version_edit.DeleteFile(4, 6U);
|
||||
@@ -320,36 +321,36 @@ TEST_F(VersionBuilderTest, ApplyMultipleAndSaveTo) {
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(2, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(2, 676, 0, 100U, GetInternalKey("401"),
|
||||
GetInternalKey("450"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(2, 636, 0, 100U, GetInternalKey("601"),
|
||||
GetInternalKey("650"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(2, 616, 0, 100U, GetInternalKey("501"),
|
||||
GetInternalKey("550"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(2, 606, 0, 100U, GetInternalKey("701"),
|
||||
GetInternalKey("750"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(
|
||||
2, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.AddFile(
|
||||
2, 676, 0, 100U, GetInternalKey("401"), GetInternalKey("450"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.AddFile(
|
||||
2, 636, 0, 100U, GetInternalKey("601"), GetInternalKey("650"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.AddFile(
|
||||
2, 616, 0, 100U, GetInternalKey("501"), GetInternalKey("550"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.AddFile(
|
||||
2, 606, 0, 100U, GetInternalKey("701"), GetInternalKey("750"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
@@ -384,53 +385,53 @@ TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
|
||||
kCompactionStyleLevel, nullptr, false);
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(2, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(2, 676, 0, 100U, GetInternalKey("401"),
|
||||
GetInternalKey("450"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(2, 636, 0, 100U, GetInternalKey("601"),
|
||||
GetInternalKey("650"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(2, 616, 0, 100U, GetInternalKey("501"),
|
||||
GetInternalKey("550"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(2, 606, 0, 100U, GetInternalKey("701"),
|
||||
GetInternalKey("750"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(
|
||||
2, 666, 0, 100U, GetInternalKey("301"), GetInternalKey("350"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.AddFile(
|
||||
2, 676, 0, 100U, GetInternalKey("401"), GetInternalKey("450"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.AddFile(
|
||||
2, 636, 0, 100U, GetInternalKey("601"), GetInternalKey("650"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.AddFile(
|
||||
2, 616, 0, 100U, GetInternalKey("501"), GetInternalKey("550"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit.AddFile(
|
||||
2, 606, 0, 100U, GetInternalKey("701"), GetInternalKey("750"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
ASSERT_OK(version_builder.Apply(&version_edit));
|
||||
|
||||
VersionEdit version_edit2;
|
||||
version_edit.AddFile(2, 808, 0, 100U, GetInternalKey("901"),
|
||||
GetInternalKey("950"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(
|
||||
2, 808, 0, 100U, GetInternalKey("901"), GetInternalKey("950"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit2.DeleteFile(2, 616);
|
||||
version_edit2.DeleteFile(2, 636);
|
||||
version_edit.AddFile(2, 806, 0, 100U, GetInternalKey("801"),
|
||||
GetInternalKey("850"), 200, 200, false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
version_edit.AddFile(
|
||||
2, 806, 0, 100U, GetInternalKey("801"), GetInternalKey("850"), 200, 200,
|
||||
false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
ASSERT_OK(version_builder.Apply(&version_edit2));
|
||||
ASSERT_OK(version_builder.SaveTo(&new_vstorage));
|
||||
@@ -535,14 +536,14 @@ TEST_F(VersionBuilderTest, ApplyFileDeletionAndAddition) {
|
||||
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
addition.AddFile(level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest, smallest_seq),
|
||||
GetInternalKey(largest, largest_seq), smallest_seqno,
|
||||
largest_seqno, marked_for_compaction, Temperature::kUnknown,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
addition.AddFile(
|
||||
level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest, smallest_seq),
|
||||
GetInternalKey(largest, largest_seq), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
ASSERT_OK(builder.Apply(&addition));
|
||||
|
||||
@@ -586,13 +587,13 @@ TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyInBase) {
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
edit.AddFile(new_level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
edit.AddFile(
|
||||
new_level, file_number, path_id, file_size, GetInternalKey(smallest),
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
@@ -623,13 +624,13 @@ TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyApplied) {
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
edit.AddFile(level, file_number, path_id, file_size, GetInternalKey(smallest),
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, Temperature::kUnknown,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
edit.AddFile(
|
||||
level, file_number, path_id, file_size, GetInternalKey(smallest),
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
@@ -637,13 +638,13 @@ TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyApplied) {
|
||||
|
||||
constexpr int new_level = 2;
|
||||
|
||||
other_edit.AddFile(new_level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
other_edit.AddFile(
|
||||
new_level, file_number, path_id, file_size, GetInternalKey(smallest),
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
const Status s = builder.Apply(&other_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
@@ -674,13 +675,13 @@ TEST_F(VersionBuilderTest, ApplyFileAdditionAndDeletion) {
|
||||
|
||||
VersionEdit addition;
|
||||
|
||||
addition.AddFile(level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
addition.AddFile(
|
||||
level, file_number, path_id, file_size, GetInternalKey(smallest),
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0,
|
||||
0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
ASSERT_OK(builder.Apply(&addition));
|
||||
|
||||
@@ -1253,7 +1254,8 @@ TEST_F(VersionBuilderTest, SaveBlobFilesToConcurrentJobs) {
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, Temperature::kUnknown, blob_file_number,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime, 2 /*epoch_number*/,
|
||||
checksum_value, checksum_method, kNullUniqueId64x2, 0, 0);
|
||||
checksum_value, checksum_method, kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
@@ -1341,7 +1343,8 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
|
||||
/* oldest_blob_file_number */ 16, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
|
||||
edit.AddFile(/* level */ 1, /* file_number */ 700, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("801"),
|
||||
@@ -1351,7 +1354,8 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
|
||||
/* oldest_blob_file_number */ 1000, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
edit.AddBlobFile(/* blob_file_number */ 1000, /* total_blob_count */ 2000,
|
||||
/* total_blob_bytes */ 200000,
|
||||
/* checksum_method */ std::string(),
|
||||
@@ -1572,7 +1576,8 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
|
||||
Temperature::kUnknown,
|
||||
/* oldest_blob_file_number */ 1, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
|
||||
// Add an SST that does not reference any blob files.
|
||||
edit.AddFile(
|
||||
@@ -1582,7 +1587,8 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
|
||||
/* largest_seqno */ 2200, /* marked_for_compaction */ false,
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
|
||||
// Delete a file that references a blob file.
|
||||
edit.DeleteFile(/* level */ 1, /* file_number */ 6);
|
||||
@@ -1605,7 +1611,8 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
|
||||
/* oldest_blob_file_number */ 3, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
|
||||
// Trivially move a file that does not reference any blob files.
|
||||
edit.DeleteFile(/* level */ 1, /* file_number */ 13);
|
||||
@@ -1617,7 +1624,8 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownEpochNumber, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
|
||||
// Add one more SST file that references a blob file, then promptly
|
||||
// delete it in a second version edit before the new version gets saved.
|
||||
@@ -1631,7 +1639,8 @@ TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
|
||||
/* oldest_blob_file_number */ 5, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownEpochNumber,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0,
|
||||
/* user_defined_timestamps_persisted */ true);
|
||||
|
||||
VersionEdit edit2;
|
||||
|
||||
@@ -1732,7 +1741,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForL0FilesSortedByEpochNumber) {
|
||||
/* oldest_blob_file_number */ kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
1 /* epoch_number */, kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit_1.AddFile(
|
||||
/* level */ 0, /* file_number */ 2U, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("b", 2),
|
||||
@@ -1742,7 +1751,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForL0FilesSortedByEpochNumber) {
|
||||
/* oldest_blob_file_number */ kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
1 /* epoch_number */, kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
VersionBuilder version_builder_1(EnvOptions(), &ioptions_,
|
||||
nullptr /* table_cache */, &vstorage_,
|
||||
@@ -1769,7 +1778,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForL0FilesSortedByEpochNumber) {
|
||||
/* oldest_blob_file_number */ kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
1 /* epoch_number */, kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0, /* user_defined_timestamps_persisted */ true);
|
||||
version_edit_2.AddFile(
|
||||
/* level */ 0, /* file_number */ 2U, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("b", 2),
|
||||
@@ -1779,7 +1788,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForL0FilesSortedByEpochNumber) {
|
||||
/* oldest_blob_file_number */ kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
2 /* epoch_number */, kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0, /* user_defined_timestamps_persisted */ true);
|
||||
|
||||
VersionBuilder version_builder_2(EnvOptions(), &ioptions_,
|
||||
nullptr /* table_cache */, &vstorage_,
|
||||
|
||||
+19
-1
@@ -244,6 +244,14 @@ bool VersionEdit::EncodeTo(std::string* dst) const {
|
||||
PutVarint64(&varint_tail_size, f.tail_size);
|
||||
PutLengthPrefixedSlice(dst, Slice(varint_tail_size));
|
||||
}
|
||||
if (!f.user_defined_timestamps_persisted) {
|
||||
// The default value for the flag is true, it's only explicitly persisted
|
||||
// when it's false. We are putting 0 as the value here to signal false
|
||||
// (i.e. UDTS not persisted).
|
||||
PutVarint32(dst, NewFileCustomTag::kUserDefinedTimestampsPersisted);
|
||||
char p = static_cast<char>(0);
|
||||
PutLengthPrefixedSlice(dst, Slice(&p, 1));
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",
|
||||
dst);
|
||||
@@ -427,6 +435,12 @@ const char* VersionEdit::DecodeNewFile4From(Slice* input) {
|
||||
return "invalid tail start offset";
|
||||
}
|
||||
break;
|
||||
case kUserDefinedTimestampsPersisted:
|
||||
if (field.size() != 1) {
|
||||
return "user-defined timestamps persisted field wrong size";
|
||||
}
|
||||
f.user_defined_timestamps_persisted = (field[0] == 1);
|
||||
break;
|
||||
default:
|
||||
if ((custom_tag & kCustomTagNonSafeIgnoreMask) != 0) {
|
||||
// Should not proceed if cannot understand it
|
||||
@@ -862,8 +876,10 @@ std::string VersionEdit::DebugString(bool hex_key) const {
|
||||
InternalUniqueIdToExternal(&id);
|
||||
r.append(UniqueIdToHumanString(EncodeUniqueIdBytes(&id)));
|
||||
}
|
||||
r.append(" tail size:");
|
||||
r.append(" tail size: ");
|
||||
AppendNumberTo(&r, f.tail_size);
|
||||
r.append(" User-defined timestamps persisted: ");
|
||||
r.append(f.user_defined_timestamps_persisted ? "true" : "false");
|
||||
}
|
||||
|
||||
for (const auto& blob_file_addition : blob_file_additions_) {
|
||||
@@ -980,6 +996,8 @@ std::string VersionEdit::DebugJSON(int edit_num, bool hex_key) const {
|
||||
jw << "Temperature" << static_cast<int>(f.temperature);
|
||||
}
|
||||
jw << "TailSize" << f.tail_size;
|
||||
jw << "UserDefinedTimestampsPersisted"
|
||||
<< f.user_defined_timestamps_persisted;
|
||||
jw.EndArrayedObject();
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -91,6 +91,7 @@ enum NewFileCustomTag : uint32_t {
|
||||
kEpochNumber = 13,
|
||||
kCompensatedRangeDeletionSize = 14,
|
||||
kTailSize = 15,
|
||||
kUserDefinedTimestampsPersisted = 16,
|
||||
|
||||
// If this bit for the custom tag is set, opening DB should fail if
|
||||
// we don't know this field.
|
||||
@@ -243,6 +244,11 @@ struct FileMetaData {
|
||||
// "Tail" refers to all blocks after data blocks till the end of the SST file
|
||||
uint64_t tail_size = 0;
|
||||
|
||||
// Value of the `AdvancedColumnFamilyOptions.persist_user_defined_timestamps`
|
||||
// flag when the file is created. Default to true, only when this flag is
|
||||
// false, it's explicitly written to Manifest.
|
||||
bool user_defined_timestamps_persisted = true;
|
||||
|
||||
FileMetaData() = default;
|
||||
|
||||
FileMetaData(uint64_t file, uint32_t file_path_id, uint64_t file_size,
|
||||
@@ -255,7 +261,7 @@ struct FileMetaData {
|
||||
const std::string& _file_checksum_func_name,
|
||||
UniqueId64x2 _unique_id,
|
||||
const uint64_t _compensated_range_deletion_size,
|
||||
uint64_t _tail_size)
|
||||
uint64_t _tail_size, bool _user_defined_timestamps_persisted)
|
||||
: fd(file, file_path_id, file_size, smallest_seq, largest_seq),
|
||||
smallest(smallest_key),
|
||||
largest(largest_key),
|
||||
@@ -269,7 +275,8 @@ struct FileMetaData {
|
||||
file_checksum(_file_checksum),
|
||||
file_checksum_func_name(_file_checksum_func_name),
|
||||
unique_id(std::move(_unique_id)),
|
||||
tail_size(_tail_size) {
|
||||
tail_size(_tail_size),
|
||||
user_defined_timestamps_persisted(_user_defined_timestamps_persisted) {
|
||||
TEST_SYNC_POINT_CALLBACK("FileMetaData::FileMetaData", this);
|
||||
}
|
||||
|
||||
@@ -454,7 +461,7 @@ class VersionEdit {
|
||||
const std::string& file_checksum_func_name,
|
||||
const UniqueId64x2& unique_id,
|
||||
const uint64_t compensated_range_deletion_size,
|
||||
uint64_t tail_size) {
|
||||
uint64_t tail_size, bool user_defined_timestamps_persisted) {
|
||||
assert(smallest_seqno <= largest_seqno);
|
||||
new_files_.emplace_back(
|
||||
level,
|
||||
@@ -463,7 +470,8 @@ class VersionEdit {
|
||||
temperature, oldest_blob_file_number, oldest_ancester_time,
|
||||
file_creation_time, epoch_number, file_checksum,
|
||||
file_checksum_func_name, unique_id,
|
||||
compensated_range_deletion_size, tail_size));
|
||||
compensated_range_deletion_size, tail_size,
|
||||
user_defined_timestamps_persisted));
|
||||
if (!HasLastSequence() || largest_seqno > GetLastSequence()) {
|
||||
SetLastSequence(largest_seqno);
|
||||
}
|
||||
|
||||
+14
-10
@@ -45,7 +45,7 @@ TEST_F(VersionEditTest, EncodeDecode) {
|
||||
kBig + 500 + i, kBig + 600 + i, false, Temperature::kUnknown,
|
||||
kInvalidBlobFileNumber, 888, 678,
|
||||
kBig + 300 + i /* epoch_number */, "234", "crc32c",
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0, true);
|
||||
edit.DeleteFile(4, kBig + 700 + i);
|
||||
}
|
||||
|
||||
@@ -65,24 +65,24 @@ TEST_F(VersionEditTest, EncodeDecodeNewFile4) {
|
||||
kBig + 600, true, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
300 /* epoch_number */, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
|
||||
edit.AddFile(4, 301, 3, 100, InternalKey("foo", kBig + 501, kTypeValue),
|
||||
InternalKey("zoo", kBig + 601, kTypeDeletion), kBig + 501,
|
||||
kBig + 601, false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
301 /* epoch_number */, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, false);
|
||||
edit.AddFile(5, 302, 0, 100, InternalKey("foo", kBig + 502, kTypeValue),
|
||||
InternalKey("zoo", kBig + 602, kTypeDeletion), kBig + 502,
|
||||
kBig + 602, true, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
666, 888, 302 /* epoch_number */, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
|
||||
edit.AddFile(5, 303, 0, 100, InternalKey("foo", kBig + 503, kTypeBlobIndex),
|
||||
InternalKey("zoo", kBig + 603, kTypeBlobIndex), kBig + 503,
|
||||
kBig + 603, true, Temperature::kUnknown, 1001,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
303 /* epoch_number */, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
|
||||
|
||||
edit.DeleteFile(4, 700);
|
||||
|
||||
@@ -99,7 +99,7 @@ TEST_F(VersionEditTest, EncodeDecodeNewFile4) {
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
auto& new_files = parsed.GetNewFiles();
|
||||
ASSERT_TRUE(new_files[0].second.marked_for_compaction);
|
||||
ASSERT_TRUE(!new_files[1].second.marked_for_compaction);
|
||||
ASSERT_FALSE(new_files[1].second.marked_for_compaction);
|
||||
ASSERT_TRUE(new_files[2].second.marked_for_compaction);
|
||||
ASSERT_TRUE(new_files[3].second.marked_for_compaction);
|
||||
ASSERT_EQ(3u, new_files[0].second.fd.GetPathId());
|
||||
@@ -113,6 +113,10 @@ TEST_F(VersionEditTest, EncodeDecodeNewFile4) {
|
||||
ASSERT_EQ(kInvalidBlobFileNumber,
|
||||
new_files[2].second.oldest_blob_file_number);
|
||||
ASSERT_EQ(1001, new_files[3].second.oldest_blob_file_number);
|
||||
ASSERT_TRUE(new_files[0].second.user_defined_timestamps_persisted);
|
||||
ASSERT_FALSE(new_files[1].second.user_defined_timestamps_persisted);
|
||||
ASSERT_TRUE(new_files[2].second.user_defined_timestamps_persisted);
|
||||
ASSERT_TRUE(new_files[3].second.user_defined_timestamps_persisted);
|
||||
}
|
||||
|
||||
TEST_F(VersionEditTest, ForwardCompatibleNewFile4) {
|
||||
@@ -123,12 +127,12 @@ TEST_F(VersionEditTest, ForwardCompatibleNewFile4) {
|
||||
kBig + 600, true, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
300 /* epoch_number */, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
|
||||
edit.AddFile(4, 301, 3, 100, InternalKey("foo", kBig + 501, kTypeValue),
|
||||
InternalKey("zoo", kBig + 601, kTypeDeletion), kBig + 501,
|
||||
kBig + 601, false, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
686, 868, 301 /* epoch_number */, "234", "crc32c",
|
||||
kNullUniqueId64x2, 0, 0);
|
||||
kNullUniqueId64x2, 0, 0, true);
|
||||
edit.DeleteFile(4, 700);
|
||||
|
||||
edit.SetComparatorName("foo");
|
||||
@@ -177,7 +181,7 @@ TEST_F(VersionEditTest, NewFile4NotSupportedField) {
|
||||
kBig + 600, true, Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
300 /* epoch_number */, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
|
||||
|
||||
edit.SetComparatorName("foo");
|
||||
edit.SetLogNumber(kBig + 100);
|
||||
@@ -208,7 +212,7 @@ TEST_F(VersionEditTest, EncodeEmptyFile) {
|
||||
Temperature::kUnknown, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
1 /*epoch_number*/, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0);
|
||||
kUnknownFileChecksumFuncName, kNullUniqueId64x2, 0, 0, true);
|
||||
std::string buffer;
|
||||
ASSERT_TRUE(!edit.EncodeTo(&buffer));
|
||||
}
|
||||
|
||||
+44
-23
@@ -39,6 +39,7 @@
|
||||
#include "db/version_builder.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/version_edit_handler.h"
|
||||
#include "file/file_util.h"
|
||||
#include "table/compaction_merging_iterator.h"
|
||||
|
||||
#if USE_COROUTINES
|
||||
@@ -2190,7 +2191,12 @@ Version::Version(ColumnFamilyData* column_family_data, VersionSet* vset,
|
||||
MaxFileSizeForL0MetaPin(mutable_cf_options_)),
|
||||
version_number_(version_number),
|
||||
io_tracer_(io_tracer),
|
||||
use_async_io_(env_->GetFileSystem()->use_async_io()) {}
|
||||
use_async_io_(false) {
|
||||
if (CheckFSFeatureSupport(env_->GetFileSystem().get(),
|
||||
FSSupportedOps::kAsyncIO)) {
|
||||
use_async_io_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
Status Version::GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
const Slice& blob_index_slice,
|
||||
@@ -3390,6 +3396,7 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
// maintaining it to be over 1.0, we scale the original score by 10x
|
||||
// if it is larger than 1.0.
|
||||
const double kScoreScale = 10.0;
|
||||
int max_output_level = MaxOutputLevel(immutable_options.allow_ingest_behind);
|
||||
for (int level = 0; level <= MaxInputLevel(); level++) {
|
||||
double score;
|
||||
if (level == 0) {
|
||||
@@ -3417,7 +3424,7 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
// For universal compaction, we use level0 score to indicate
|
||||
// compaction score for the whole DB. Adding other levels as if
|
||||
// they are L0 files.
|
||||
for (int i = 1; i < num_levels(); i++) {
|
||||
for (int i = 1; i <= max_output_level; i++) {
|
||||
// It's possible that a subset of the files in a level may be in a
|
||||
// compaction, due to delete triggered compaction or trivial move.
|
||||
// In that case, the below check may not catch a level being
|
||||
@@ -3459,12 +3466,11 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
// Level-based involves L0->L0 compactions that can lead to oversized
|
||||
// L0 files. Take into account size as well to avoid later giant
|
||||
// compactions to the base level.
|
||||
// If score in L0 is always too high, L0->L1 will always be
|
||||
// prioritized over L1->L2 compaction and L1 will accumulate to
|
||||
// too large. But if L0 score isn't high enough, L0 will accumulate
|
||||
// and data is not moved to L1 fast enough. With potential L0->L0
|
||||
// compaction, number of L0 files aren't always an indication of
|
||||
// L0 oversizing, and we also need to consider total size of L0.
|
||||
// If score in L0 is always too high, L0->LBase will always be
|
||||
// prioritized over LBase->LBase+1 compaction and LBase will
|
||||
// accumulate to too large. But if L0 score isn't high enough, L0 will
|
||||
// accumulate and data is not moved to LBase fast enough. The score
|
||||
// calculation below takes into account L0 size vs LBase size.
|
||||
if (immutable_options.level_compaction_dynamic_level_bytes) {
|
||||
if (total_size >= mutable_cf_options.max_bytes_for_level_base) {
|
||||
// When calculating estimated_compaction_needed_bytes, we assume
|
||||
@@ -3476,10 +3482,13 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
score = std::max(score, 1.01);
|
||||
}
|
||||
if (total_size > level_max_bytes_[base_level_]) {
|
||||
// In this case, we compare L0 size with actual L1 size and make
|
||||
// sure score is more than 1.0 (10.0 after scaled) if L0 is larger
|
||||
// than L1. Since in this case L1 score is lower than 10.0, L0->L1
|
||||
// is prioritized over L1->L2.
|
||||
// In this case, we compare L0 size with actual LBase size and
|
||||
// make sure score is more than 1.0 (10.0 after scaled) if L0 is
|
||||
// larger than LBase. Since LBase score = LBase size /
|
||||
// (target size + total_downcompact_bytes) where
|
||||
// total_downcompact_bytes = total_size > LBase size,
|
||||
// LBase score is lower than 10.0. So L0->LBase is prioritized
|
||||
// over LBase -> LBase+1.
|
||||
uint64_t base_level_size = 0;
|
||||
for (auto f : files_[base_level_]) {
|
||||
base_level_size += f->compensated_file_size;
|
||||
@@ -3561,16 +3570,18 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
}
|
||||
}
|
||||
}
|
||||
ComputeFilesMarkedForCompaction();
|
||||
ComputeFilesMarkedForCompaction(max_output_level);
|
||||
if (!immutable_options.allow_ingest_behind) {
|
||||
ComputeBottommostFilesMarkedForCompaction();
|
||||
}
|
||||
if (mutable_cf_options.ttl > 0) {
|
||||
if (mutable_cf_options.ttl > 0 &&
|
||||
compaction_style_ == kCompactionStyleLevel) {
|
||||
ComputeExpiredTtlFiles(immutable_options, mutable_cf_options.ttl);
|
||||
}
|
||||
if (mutable_cf_options.periodic_compaction_seconds > 0) {
|
||||
ComputeFilesMarkedForPeriodicCompaction(
|
||||
immutable_options, mutable_cf_options.periodic_compaction_seconds);
|
||||
immutable_options, mutable_cf_options.periodic_compaction_seconds,
|
||||
max_output_level);
|
||||
}
|
||||
|
||||
if (mutable_cf_options.enable_blob_garbage_collection &&
|
||||
@@ -3584,14 +3595,14 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
EstimateCompactionBytesNeeded(mutable_cf_options);
|
||||
}
|
||||
|
||||
void VersionStorageInfo::ComputeFilesMarkedForCompaction() {
|
||||
void VersionStorageInfo::ComputeFilesMarkedForCompaction(int last_level) {
|
||||
files_marked_for_compaction_.clear();
|
||||
int last_qualify_level = 0;
|
||||
|
||||
// Do not include files from the last level with data
|
||||
// If table properties collector suggests a file on the last level,
|
||||
// we should not move it to a new level.
|
||||
for (int level = num_levels() - 1; level >= 1; level--) {
|
||||
for (int level = last_level; level >= 1; level--) {
|
||||
if (!files_[level].empty()) {
|
||||
last_qualify_level = level - 1;
|
||||
break;
|
||||
@@ -3635,7 +3646,7 @@ void VersionStorageInfo::ComputeExpiredTtlFiles(
|
||||
|
||||
void VersionStorageInfo::ComputeFilesMarkedForPeriodicCompaction(
|
||||
const ImmutableOptions& ioptions,
|
||||
const uint64_t periodic_compaction_seconds) {
|
||||
const uint64_t periodic_compaction_seconds, int last_level) {
|
||||
assert(periodic_compaction_seconds > 0);
|
||||
|
||||
files_marked_for_periodic_compaction_.clear();
|
||||
@@ -3656,7 +3667,7 @@ void VersionStorageInfo::ComputeFilesMarkedForPeriodicCompaction(
|
||||
const uint64_t allowed_time_limit =
|
||||
current_time - periodic_compaction_seconds;
|
||||
|
||||
for (int level = 0; level < num_levels(); level++) {
|
||||
for (int level = 0; level <= last_level; level++) {
|
||||
for (auto f : files_[level]) {
|
||||
if (!f->being_compacted) {
|
||||
// Compute a file's modification time in the following order:
|
||||
@@ -4700,7 +4711,7 @@ void VersionStorageInfo::CalculateBaseBytes(const ImmutableOptions& ioptions,
|
||||
assert(base_level_ == 1);
|
||||
base_level_size = base_bytes_max;
|
||||
} else {
|
||||
base_level_size = cur_level_size;
|
||||
base_level_size = std::max(static_cast<uint64_t>(1), cur_level_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6449,7 +6460,8 @@ Status VersionSet::WriteCurrentStateToManifest(
|
||||
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);
|
||||
f->compensated_range_deletion_size, f->tail_size,
|
||||
f->user_defined_timestamps_persisted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7003,6 +7015,16 @@ void VersionSet::GetObsoleteFiles(std::vector<ObsoleteFileInfo>* files,
|
||||
obsolete_manifests_.swap(*manifest_filenames);
|
||||
}
|
||||
|
||||
uint64_t VersionSet::GetObsoleteSstFilesSize() const {
|
||||
uint64_t ret = 0;
|
||||
for (auto& f : obsolete_files_) {
|
||||
if (f.metadata != nullptr) {
|
||||
ret += f.metadata->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ColumnFamilyData* VersionSet::CreateColumnFamily(
|
||||
const ColumnFamilyOptions& cf_options, const ReadOptions& read_options,
|
||||
const VersionEdit* edit) {
|
||||
@@ -7126,8 +7148,7 @@ Status VersionSet::VerifyFileMetadata(const ReadOptions& read_options,
|
||||
status = table_cache->FindTable(
|
||||
read_options, file_opts, *icmp, meta_copy, &handle,
|
||||
cf_opts->block_protection_bytes_per_key, pe,
|
||||
/*no_io=*/false, /*record_read_stats=*/true,
|
||||
internal_stats->GetFileReadHist(level), false, level,
|
||||
/*no_io=*/false, internal_stats->GetFileReadHist(level), false, level,
|
||||
/*prefetch_index_and_filter_in_cache*/ false, max_sz_for_l0_meta_pin,
|
||||
meta_copy.temperature);
|
||||
if (handle) {
|
||||
|
||||
+19
-3
@@ -204,7 +204,7 @@ class VersionStorageInfo {
|
||||
|
||||
// This computes files_marked_for_compaction_ and is called by
|
||||
// ComputeCompactionScore()
|
||||
void ComputeFilesMarkedForCompaction();
|
||||
void ComputeFilesMarkedForCompaction(int last_level);
|
||||
|
||||
// This computes ttl_expired_files_ and is called by
|
||||
// ComputeCompactionScore()
|
||||
@@ -215,7 +215,7 @@ class VersionStorageInfo {
|
||||
// ComputeCompactionScore()
|
||||
void ComputeFilesMarkedForPeriodicCompaction(
|
||||
const ImmutableOptions& ioptions,
|
||||
const uint64_t periodic_compaction_seconds);
|
||||
const uint64_t periodic_compaction_seconds, int last_level);
|
||||
|
||||
// This computes bottommost_files_marked_for_compaction_ and is called by
|
||||
// ComputeCompactionScore() or UpdateOldestSnapshot().
|
||||
@@ -465,6 +465,7 @@ class VersionStorageInfo {
|
||||
|
||||
// REQUIRES: ComputeCompactionScore has been called
|
||||
// REQUIRES: DB mutex held during access
|
||||
// Used by Leveled Compaction only.
|
||||
const autovector<std::pair<int, FileMetaData*>>& ExpiredTtlFiles() const {
|
||||
assert(finalized_);
|
||||
return expired_ttl_files_;
|
||||
@@ -472,6 +473,7 @@ class VersionStorageInfo {
|
||||
|
||||
// REQUIRES: ComputeCompactionScore has been called
|
||||
// REQUIRES: DB mutex held during access
|
||||
// Used by Leveled and Universal Compaction.
|
||||
const autovector<std::pair<int, FileMetaData*>>&
|
||||
FilesMarkedForPeriodicCompaction() const {
|
||||
assert(finalized_);
|
||||
@@ -680,7 +682,7 @@ class VersionStorageInfo {
|
||||
|
||||
// This vector contains list of files marked for compaction and also not
|
||||
// currently being compacted. It is protected by DB mutex. It is calculated in
|
||||
// ComputeCompactionScore()
|
||||
// ComputeCompactionScore(). Used by Leveled and Universal Compaction.
|
||||
autovector<std::pair<int, FileMetaData*>> files_marked_for_compaction_;
|
||||
|
||||
autovector<std::pair<int, FileMetaData*>> expired_ttl_files_;
|
||||
@@ -1466,7 +1468,21 @@ class VersionSet {
|
||||
std::vector<std::string>* manifest_filenames,
|
||||
uint64_t min_pending_output);
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
uint64_t GetObsoleteSstFilesSize() const;
|
||||
|
||||
ColumnFamilySet* GetColumnFamilySet() { return column_family_set_.get(); }
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>& GetRunningColumnFamiliesTimestampSize()
|
||||
const {
|
||||
return column_family_set_->GetRunningColumnFamiliesTimestampSize();
|
||||
}
|
||||
|
||||
const UnorderedMap<uint32_t, size_t>&
|
||||
GetColumnFamiliesTimestampSizeForRecord() const {
|
||||
return column_family_set_->GetColumnFamiliesTimestampSizeForRecord();
|
||||
}
|
||||
|
||||
RefedColumnFamilySet GetRefedColumnFamilySet() {
|
||||
return RefedColumnFamilySet(GetColumnFamilySet());
|
||||
}
|
||||
|
||||
Loaded 100 of 213 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user