mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35f2f3c897 | |||
| 2428b727a9 | |||
| 2a383f21f4 | |||
| baf22b4ee6 | |||
| f35042ca40 | |||
| 22f2936b35 | |||
| 5efec84c60 | |||
| ff8953380f | |||
| 9eb002fcf0 | |||
| c625b8d017 | |||
| d10801e983 | |||
| b6269b078a | |||
| 2b367fa8cc | |||
| 6878cedcc3 | |||
| 0729b287e9 | |||
| add68bd28a | |||
| a207c27809 | |||
| 5de333fd99 | |||
| 9b0a32f802 | |||
| 48c468c22e | |||
| c55460c734 | |||
| e51be2c5a1 | |||
| 74a652a45f | |||
| f58d276764 | |||
| a53563d86e | |||
| 6450e9fc38 | |||
| 87e2358736 | |||
| e3a96c4823 | |||
| f63331ebaf | |||
| 82b81dc8b5 | |||
| a756fb9c85 | |||
| 61f83dfeb7 | |||
| e7c24168d8 | |||
| 052c24a668 | |||
| d4b75d295f | |||
| 23ffed9cb7 | |||
| 87882736ef | |||
| e95c570047 | |||
| 543a201b93 | |||
| d56f74a4db | |||
| a7fd1d0881 | |||
| 8ca081780b | |||
| d057e8326d | |||
| fd2079938d | |||
| 9b25d26dc8 | |||
| b01a428d9b | |||
| a685a701ca | |||
| a074d46a5a | |||
| c268859aaa | |||
| dbe3810c74 | |||
| 08af0ae3f0 | |||
| 0879c24040 | |||
| 8b2f60b668 | |||
| b278152261 | |||
| 4811115b3e | |||
| 3f7e929865 | |||
| 8e91bd90d2 | |||
| 8f52972cf9 | |||
| ab7f7c9e49 | |||
| 066b51126d | |||
| 1d34cd797e | |||
| 9501279d5f | |||
| e8f218cb68 | |||
| 0804b44fb6 | |||
| a4b8ac9a73 | |||
| e0ff365a76 | |||
| d6006f9c9b | |||
| 74b7c0d249 | |||
| e352bd5742 | |||
| eec79b39a6 | |||
| ddf439c561 | |||
| 3aee4fbd41 | |||
| 9ddb55a8f6 | |||
| 4361d6d163 | |||
| daf7e77a6b | |||
| 2febf1c45c | |||
| 4171e3db9b | |||
| 55f7ded80d | |||
| 3b27725245 | |||
| c521a9ab2b | |||
| 61c9bd49c1 | |||
| 2e5388178f | |||
| c4a503f3df | |||
| 84eef260de | |||
| 9b41082d4a | |||
| 42eaa45c1b | |||
| 6b4cdacf41 | |||
| 87e82a41a9 | |||
| 66ca5ac427 | |||
| bbc85a5f22 | |||
| f4529a54bb | |||
| 5535d06b9c | |||
| d5f3b77f23 | |||
| 9e885939a3 |
+7
-1
@@ -816,9 +816,12 @@ set(SOURCES
|
||||
tools/ldb_tool.cc
|
||||
tools/sst_dump_tool.cc
|
||||
tools/trace_analyzer_tool.cc
|
||||
trace_replay/trace_replay.cc
|
||||
trace_replay/block_cache_tracer.cc
|
||||
trace_replay/io_tracer.cc
|
||||
trace_replay/trace_record_handler.cc
|
||||
trace_replay/trace_record_result.cc
|
||||
trace_replay/trace_record.cc
|
||||
trace_replay/trace_replay.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
@@ -849,6 +852,7 @@ set(SOURCES
|
||||
utilities/cassandra/format.cc
|
||||
utilities/cassandra/merge_operator.cc
|
||||
utilities/checkpoint/checkpoint_impl.cc
|
||||
utilities/compaction_filters.cc
|
||||
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc
|
||||
utilities/debug.cc
|
||||
utilities/env_mirror.cc
|
||||
@@ -857,6 +861,7 @@ set(SOURCES
|
||||
utilities/fault_injection_fs.cc
|
||||
utilities/leveldb_options/leveldb_options.cc
|
||||
utilities/memory/memory_util.cc
|
||||
utilities/merge_operators.cc
|
||||
utilities/merge_operators/bytesxor.cc
|
||||
utilities/merge_operators/max.cc
|
||||
utilities/merge_operators/put.cc
|
||||
@@ -876,6 +881,7 @@ set(SOURCES
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
utilities/trace/replayer_impl.cc
|
||||
utilities/transactions/lock/lock_manager.cc
|
||||
utilities/transactions/lock/point/point_lock_tracker.cc
|
||||
utilities/transactions/lock/point/point_lock_manager.cc
|
||||
|
||||
+41
-2
@@ -1,15 +1,53 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Bug Fixes
|
||||
* If the primary's CURRENT file is missing or inaccessible, the secondary instance should not hang repeatedly trying to switch to a new MANIFEST. It should instead return the error code encountered while accessing the file.
|
||||
* Restoring backups with BackupEngine is now a logically atomic operation, so that if a restore operation is interrupted, DB::Open on it will fail. Using BackupEngineOptions::sync (default) ensures atomicity even in case of power loss or OS crash.
|
||||
* Fixed a race related to the destruction of `ColumnFamilyData` objects. The earlier logic unlocked the DB mutex before destroying the thread-local `SuperVersion` pointers, which could result in a process crash if another thread managed to get a reference to the `ColumnFamilyData` object.
|
||||
* Removed a call to `RenameFile()` on a non-existent info log file ("LOG") when opening a new DB. Such a call was guaranteed to fail though did not impact applications since we swallowed the error. Now we also stopped swallowing errors in renaming "LOG" file.
|
||||
* Fixed an issue where `OnFlushCompleted` was not called for atomic flush.
|
||||
* Fixed a bug affecting the batched `MultiGet` API when used with keys spanning multiple column families and `sorted_input == false`.
|
||||
* Fixed a potential incorrect result in opt mode and assertion failures caused by releasing snapshot(s) during compaction.
|
||||
* Fixed passing of BlobFileCompletionCallback to Compaction job and Atomic flush job which was default paramter (nullptr). BlobFileCompletitionCallback is internal callback that manages addition of blob files to SSTFileManager.
|
||||
* Fixed MultiGet not updating the block_read_count and block_read_byte PerfContext counters
|
||||
|
||||
### New Features
|
||||
* Made the EventListener extend the Customizable class.
|
||||
* EventListeners that have a non-empty Name() and that are registered with the ObjectRegistry can now be serialized to/from the OPTIONS file.
|
||||
* Insert warm blocks (data blocks, uncompressed dict blocks, index and filter blocks) in Block cache during flush under option BlockBasedTableOptions.prepopulate_block_cache. Previously it was enabled for only data blocks.
|
||||
* BlockBasedTableOptions.prepopulate_block_cache can be dynamically configured using DB::SetOptions.
|
||||
* Add CompactionOptionsFIFO.age_for_warm, which allows RocksDB to move old files to warm tier in FIFO compactions. Note that file temperature is still an experimental feature.
|
||||
* Add a comment to suggest btrfs user to disable file preallocation by setting `options.allow_fallocate=false`.
|
||||
* Fast forward option in Trace replay changed to double type to allow replaying at a lower speed, by settings the value between 0 and 1. This option can be set via `ReplayOptions` in `Replayer::Replay()`, or via `--trace_replay_fast_forward` in db_bench.
|
||||
* Add property `LiveSstFilesSizeAtTemperature` to retrieve sst file size at different temperature.
|
||||
* Added a stat rocksdb.secondary.cache.hits
|
||||
* Added a PerfContext counter secondary_cache_hit_count
|
||||
* The integrated BlobDB implementation now supports the tickers `BLOB_DB_BLOB_FILE_BYTES_READ`, `BLOB_DB_GC_NUM_KEYS_RELOCATED`, and `BLOB_DB_GC_BYTES_RELOCATED`, as well as the histograms `BLOB_DB_COMPRESSION_MICROS` and `BLOB_DB_DECOMPRESSION_MICROS`.
|
||||
* Added hybrid configuration of Ribbon filter and Bloom filter where some LSM levels use Ribbon for memory space efficiency and some use Bloom for speed. See NewRibbonFilterPolicy. This also changes the default behavior of NewRibbonFilterPolicy to use Bloom for flushes under Leveled and Universal compaction and Ribbon otherwise. The C API function `rocksdb_filterpolicy_create_ribbon` is unchanged but adds new `rocksdb_filterpolicy_create_ribbon_hybrid`.
|
||||
|
||||
## Public API change
|
||||
* Added APIs to decode and replay trace file via Replayer class. Added `DB::NewDefaultReplayer()` to create a default Replayer instance. Added `TraceReader::Reset()` to restart reading a trace file. Created trace_record.h, trace_record_result.h and utilities/replayer.h files to access the decoded Trace records, replay them, and query the actual operation results.
|
||||
* Added Configurable::GetOptionsMap to the public API for use in creating new Customizable classes.
|
||||
* Generalized bits_per_key parameters in C API from int to double for greater configurability.
|
||||
|
||||
### Performance Improvements
|
||||
* Try to avoid updating DBOptions if `SetDBOptions()` does not change any option value.
|
||||
|
||||
### Behavior Changes
|
||||
* `StringAppendOperator` additionally accepts a string as the delimiter.
|
||||
* BackupEngineOptions::sync (default true) now applies to restoring backups in addition to creating backups. This could slow down restores, but ensures they are fully persisted before returning OK. (Consider increasing max_background_operations to improve performance.)
|
||||
|
||||
## 6.23.0 (2021-07-16)
|
||||
### Behavior Changes
|
||||
* Obsolete keys in the bottommost level that were preserved for a snapshot will now be cleaned upon snapshot release in all cases. This form of compaction (snapshot release triggered compaction) previously had an artificial limitation that multiple tombstones needed to be present.
|
||||
|
||||
### Bug Fixes
|
||||
* Blob file checksums are now printed in hexadecimal format when using the `manifest_dump` `ldb` command.
|
||||
* `GetLiveFilesMetaData()` now populates the `temperature`, `oldest_ancester_time`, and `file_creation_time` fields of its `LiveFileMetaData` results when the information is available. Previously these fields always contained zero indicating unknown.
|
||||
* Fix mismatches of OnCompaction{Begin,Completed} in case of DisableManualCompaction().
|
||||
* Fix continuous logging of an existing background error on every user write
|
||||
* Fix a bug that `Get()` return Status::OK() and an empty value for non-existent key when `read_options.read_tier = kBlockCacheTier`.
|
||||
* Fix a bug that `Get()` return Status::OK() and an empty value for non-existent key when `read_options.read_tier = kBlockCacheTier`.
|
||||
* Fix a bug that stat in `get_context` didn't accumulate to statistics when query is failed.
|
||||
* Fixed handling of DBOptions::wal_dir with LoadLatestOptions() or ldb --try_load_options on a copied or moved DB. Previously, when the WAL directory is same as DB directory (default), a copied or moved DB would reference the old path of the DB as the WAL directory, potentially corrupting both copies. Under this change, the wal_dir from DB::GetOptions() or LoadLatestOptions() may now be empty, indicating that the current DB directory is used for WALs. This is also a subtle API change.
|
||||
|
||||
### New Features
|
||||
* ldb has a new feature, `list_live_files_metadata`, that shows the live SST files, as well as their LSM storage level and the column family they belong to.
|
||||
@@ -21,6 +59,7 @@
|
||||
### Public API change
|
||||
* Added APIs to the Customizable class to allow developers to create their own Customizable classes. Created the utilities/customizable_util.h file to contain helper methods for developing new Customizable classes.
|
||||
* Change signature of SecondaryCache::Name(). Make SecondaryCache customizable and add SecondaryCache::CreateFromString method.
|
||||
|
||||
## 6.22.0 (2021-06-18)
|
||||
### Behavior Changes
|
||||
* Added two additional tickers, MEMTABLE_PAYLOAD_BYTES_AT_FLUSH and MEMTABLE_GARBAGE_BYTES_AT_FLUSH. These stats can be used to estimate the ratio of "garbage" (outdated) bytes in the memtable that are discarded at flush time.
|
||||
|
||||
@@ -525,7 +525,6 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
TESTS_FAILING_ASC = \
|
||||
c_test \
|
||||
db_test \
|
||||
db_test2 \
|
||||
env_test \
|
||||
range_locking_test \
|
||||
testutil_test \
|
||||
|
||||
@@ -335,6 +335,9 @@ cpp_library(
|
||||
"tools/sst_dump_tool.cc",
|
||||
"trace_replay/block_cache_tracer.cc",
|
||||
"trace_replay/io_tracer.cc",
|
||||
"trace_replay/trace_record.cc",
|
||||
"trace_replay/trace_record_handler.cc",
|
||||
"trace_replay/trace_record_result.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/build_version.cc",
|
||||
"util/coding.cc",
|
||||
@@ -368,6 +371,7 @@ cpp_library(
|
||||
"utilities/cassandra/format.cc",
|
||||
"utilities/cassandra/merge_operator.cc",
|
||||
"utilities/checkpoint/checkpoint_impl.cc",
|
||||
"utilities/compaction_filters.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
"utilities/debug.cc",
|
||||
@@ -377,6 +381,7 @@ cpp_library(
|
||||
"utilities/fault_injection_fs.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators.cc",
|
||||
"utilities/merge_operators/bytesxor.cc",
|
||||
"utilities/merge_operators/max.cc",
|
||||
"utilities/merge_operators/put.cc",
|
||||
@@ -396,6 +401,7 @@ cpp_library(
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/trace/replayer_impl.cc",
|
||||
"utilities/transactions/lock/lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_tracker.cc",
|
||||
@@ -648,6 +654,9 @@ cpp_library(
|
||||
"tools/sst_dump_tool.cc",
|
||||
"trace_replay/block_cache_tracer.cc",
|
||||
"trace_replay/io_tracer.cc",
|
||||
"trace_replay/trace_record.cc",
|
||||
"trace_replay/trace_record_handler.cc",
|
||||
"trace_replay/trace_record_result.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/build_version.cc",
|
||||
"util/coding.cc",
|
||||
@@ -681,6 +690,7 @@ cpp_library(
|
||||
"utilities/cassandra/format.cc",
|
||||
"utilities/cassandra/merge_operator.cc",
|
||||
"utilities/checkpoint/checkpoint_impl.cc",
|
||||
"utilities/compaction_filters.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
"utilities/debug.cc",
|
||||
@@ -690,6 +700,7 @@ cpp_library(
|
||||
"utilities/fault_injection_fs.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators.cc",
|
||||
"utilities/merge_operators/bytesxor.cc",
|
||||
"utilities/merge_operators/max.cc",
|
||||
"utilities/merge_operators/put.cc",
|
||||
@@ -709,6 +720,7 @@ cpp_library(
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/trace/replayer_impl.cc",
|
||||
"utilities/transactions/lock/lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_tracker.cc",
|
||||
|
||||
@@ -189,9 +189,6 @@ EOF
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
if test -z "$USE_FOLLY_DISTRIBUTED_MUTEX"; then
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
fi
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
|
||||
@@ -167,6 +167,4 @@ VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
|
||||
@@ -168,6 +168,4 @@ VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
USE_FOLLY_DISTRIBUTED_MUTEX=1
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
|
||||
@@ -1220,7 +1220,7 @@ FBCODE_STRESS_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Run RocksDB whitebox crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'cd $WORKING_DIR; mkdir /dev/shm/rocksdb_whitebox_crash_test && TEST_TMPDIR=\$(mktemp -d --tmpdir=/dev/shm/rocksdb_whitebox_crash_test) python3 rocksdb/src/tools/db_crashtest.py --stress_cmd=buck-out/dbg/gen/rocks/tools/rocks_db_stress -secondary_cache_uri=\"$SECONDARY_CACHE_URI\" --env_uri=$ENV_URI -checkpoint_one_in=0 -backup_one_in=0 -cache_size=134217728 -cache_numshardbits=4 -logtostderr=false whitebox || $CONTRUN_NAME=whitebox_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; mkdir /dev/shm/rocksdb_whitebox_crash_test && TEST_TMPDIR=\$(mktemp -d --tmpdir=/dev/shm/rocksdb_whitebox_crash_test) python3 rocksdb/src/tools/db_crashtest.py --stress_cmd=buck-out/dbg/gen/rocks/tools/rocks_db_stress -secondary_cache_uri=\"$SECONDARY_CACHE_URI\" --env_uri=$ENV_URI -checkpoint_one_in=0 -backup_one_in=0 -cache_size=134217728 -cache_numshardbits=4 $EXTRA_DB_STRESS_ARGS -logtostderr=false whitebox || $CONTRUN_NAME=whitebox_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
|
||||
Vendored
+2
-1
@@ -280,7 +280,8 @@ class ClockCacheShard final : public CacheShard {
|
||||
Cache::Handle* Lookup(const Slice& key, uint32_t hash,
|
||||
const Cache::CacheItemHelper* /*helper*/,
|
||||
const Cache::CreateCallback& /*create_cb*/,
|
||||
Cache::Priority /*priority*/, bool /*wait*/) override {
|
||||
Cache::Priority /*priority*/, bool /*wait*/,
|
||||
Statistics* /*stats*/) override {
|
||||
return Lookup(key, hash);
|
||||
}
|
||||
bool Release(Cache::Handle* handle, bool /*useful*/,
|
||||
|
||||
Vendored
+10
-1
@@ -13,6 +13,8 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -418,7 +420,7 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
const Slice& key, uint32_t hash,
|
||||
const ShardedCache::CacheItemHelper* helper,
|
||||
const ShardedCache::CreateCallback& create_cb, Cache::Priority priority,
|
||||
bool wait) {
|
||||
bool wait, Statistics* stats) {
|
||||
LRUHandle* e = nullptr;
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
@@ -471,11 +473,18 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
e->Unref();
|
||||
e->Free();
|
||||
e = nullptr;
|
||||
} else {
|
||||
PERF_COUNTER_ADD(secondary_cache_hit_count, 1);
|
||||
RecordTick(stats, SECONDARY_CACHE_HITS);
|
||||
}
|
||||
} else {
|
||||
// If wait is false, we always return a handle and let the caller
|
||||
// release the handle after checking for success or failure
|
||||
e->SetIncomplete(true);
|
||||
// This may be slightly inaccurate, if the lookup eventually fails.
|
||||
// But the probability is very low.
|
||||
PERF_COUNTER_ADD(secondary_cache_hit_count, 1);
|
||||
RecordTick(stats, SECONDARY_CACHE_HITS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+4
-3
@@ -319,10 +319,11 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash,
|
||||
const ShardedCache::CacheItemHelper* helper,
|
||||
const ShardedCache::CreateCallback& create_cb,
|
||||
ShardedCache::Priority priority,
|
||||
bool wait) override;
|
||||
ShardedCache::Priority priority, bool wait,
|
||||
Statistics* stats) override;
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash) override {
|
||||
return Lookup(key, hash, nullptr, nullptr, Cache::Priority::LOW, true);
|
||||
return Lookup(key, hash, nullptr, nullptr, Cache::Priority::LOW, true,
|
||||
nullptr);
|
||||
}
|
||||
virtual bool Release(Cache::Handle* handle, bool /*useful*/,
|
||||
bool force_erase) override {
|
||||
|
||||
Vendored
+14
-6
@@ -378,7 +378,7 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
class DBSecondaryCacheTest : public DBTestBase {
|
||||
public:
|
||||
DBSecondaryCacheTest()
|
||||
: DBTestBase("/db_secondary_cache_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("db_secondary_cache_test", /*env_do_fsync=*/true) {
|
||||
fault_fs_.reset(new FaultInjectionTestFS(env_->GetFileSystem()));
|
||||
fault_env_.reset(new CompositeEnvWrapper(env_, fault_fs_));
|
||||
}
|
||||
@@ -468,6 +468,7 @@ TEST_F(LRUSecondaryCacheTest, BasicTest) {
|
||||
std::make_shared<TestSecondaryCache>(2048);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
std::shared_ptr<Statistics> stats = CreateDBStatistics();
|
||||
|
||||
Random rnd(301);
|
||||
std::string str1 = rnd.RandomString(1020);
|
||||
@@ -476,22 +477,29 @@ TEST_F(LRUSecondaryCacheTest, BasicTest) {
|
||||
str1.length()));
|
||||
std::string str2 = rnd.RandomString(1020);
|
||||
TestItem* item2 = new TestItem(str2.data(), str2.length());
|
||||
// k2 should be demoted to NVM
|
||||
// k1 should be demoted to NVM
|
||||
ASSERT_OK(cache->Insert("k2", item2, &LRUSecondaryCacheTest::helper_,
|
||||
str2.length()));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
Cache::Handle* handle;
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
handle =
|
||||
cache->Lookup("k2", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
// This lookup should promote k1 and demote k2
|
||||
handle = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
handle =
|
||||
cache->Lookup("k1", &LRUSecondaryCacheTest::helper_, test_item_creator,
|
||||
Cache::Priority::LOW, true, stats.get());
|
||||
ASSERT_NE(handle, nullptr);
|
||||
cache->Release(handle);
|
||||
ASSERT_EQ(secondary_cache->num_inserts(), 2u);
|
||||
ASSERT_EQ(secondary_cache->num_lookups(), 1u);
|
||||
ASSERT_EQ(stats->getTickerCount(SECONDARY_CACHE_HITS),
|
||||
secondary_cache->num_lookups());
|
||||
PerfContext perf_ctx = *get_perf_context();
|
||||
ASSERT_EQ(perf_ctx.secondary_cache_hit_count, secondary_cache->num_lookups());
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
|
||||
Vendored
+2
-2
@@ -83,10 +83,10 @@ Cache::Handle* ShardedCache::Lookup(const Slice& key,
|
||||
const CacheItemHelper* helper,
|
||||
const CreateCallback& create_cb,
|
||||
Priority priority, bool wait,
|
||||
Statistics* /*stats*/) {
|
||||
Statistics* stats) {
|
||||
uint32_t hash = HashSlice(key);
|
||||
return GetShard(Shard(hash))
|
||||
->Lookup(key, hash, helper, create_cb, priority, wait);
|
||||
->Lookup(key, hash, helper, create_cb, priority, wait, stats);
|
||||
}
|
||||
|
||||
bool ShardedCache::IsReady(Handle* handle) {
|
||||
|
||||
Vendored
+2
-1
@@ -34,7 +34,8 @@ class CacheShard {
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash,
|
||||
const Cache::CacheItemHelper* helper,
|
||||
const Cache::CreateCallback& create_cb,
|
||||
Cache::Priority priority, bool wait) = 0;
|
||||
Cache::Priority priority, bool wait,
|
||||
Statistics* stats) = 0;
|
||||
virtual bool Release(Cache::Handle* handle, bool useful,
|
||||
bool force_erase) = 0;
|
||||
virtual bool IsReady(Cache::Handle* handle) = 0;
|
||||
|
||||
@@ -228,6 +228,7 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
assert(blob);
|
||||
assert(compressed_blob);
|
||||
assert(compressed_blob->empty());
|
||||
assert(immutable_options_);
|
||||
|
||||
if (blob_compression_type_ == kNoCompression) {
|
||||
return Status::OK();
|
||||
@@ -242,7 +243,16 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
if (!CompressData(*blob, info, compression_format_version, compressed_blob)) {
|
||||
bool success = false;
|
||||
|
||||
{
|
||||
StopWatch stop_watch(immutable_options_->clock, immutable_options_->stats,
|
||||
BLOB_DB_COMPRESSION_MICROS);
|
||||
success =
|
||||
CompressData(*blob, info, compression_format_version, compressed_blob);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
return Status::Corruption("Error compressing blob");
|
||||
}
|
||||
|
||||
|
||||
+40
-20
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/filename.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/slice.h"
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -42,25 +44,28 @@ Status BlobFileReader::Create(
|
||||
|
||||
assert(file_reader);
|
||||
|
||||
Statistics* const statistics = immutable_options.stats;
|
||||
|
||||
CompressionType compression_type = kNoCompression;
|
||||
|
||||
{
|
||||
const Status s =
|
||||
ReadHeader(file_reader.get(), column_family_id, &compression_type);
|
||||
const Status s = ReadHeader(file_reader.get(), column_family_id, statistics,
|
||||
&compression_type);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Status s = ReadFooter(file_size, file_reader.get());
|
||||
const Status s = ReadFooter(file_reader.get(), file_size, statistics);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
blob_file_reader->reset(
|
||||
new BlobFileReader(std::move(file_reader), file_size, compression_type));
|
||||
new BlobFileReader(std::move(file_reader), file_size, compression_type,
|
||||
immutable_options.clock, statistics));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -127,6 +132,7 @@ Status BlobFileReader::OpenFile(
|
||||
|
||||
Status BlobFileReader::ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
uint32_t column_family_id,
|
||||
Statistics* statistics,
|
||||
CompressionType* compression_type) {
|
||||
assert(file_reader);
|
||||
assert(compression_type);
|
||||
@@ -141,8 +147,9 @@ Status BlobFileReader::ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
constexpr uint64_t read_offset = 0;
|
||||
constexpr size_t read_size = BlobLogHeader::kSize;
|
||||
|
||||
const Status s = ReadFromFile(file_reader, read_offset, read_size,
|
||||
&header_slice, &buf, &aligned_buf);
|
||||
const Status s =
|
||||
ReadFromFile(file_reader, read_offset, read_size, statistics,
|
||||
&header_slice, &buf, &aligned_buf);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -175,8 +182,8 @@ Status BlobFileReader::ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status BlobFileReader::ReadFooter(uint64_t file_size,
|
||||
const RandomAccessFileReader* file_reader) {
|
||||
Status BlobFileReader::ReadFooter(const RandomAccessFileReader* file_reader,
|
||||
uint64_t file_size, Statistics* statistics) {
|
||||
assert(file_size >= BlobLogHeader::kSize + BlobLogFooter::kSize);
|
||||
assert(file_reader);
|
||||
|
||||
@@ -190,8 +197,9 @@ Status BlobFileReader::ReadFooter(uint64_t file_size,
|
||||
const uint64_t read_offset = file_size - BlobLogFooter::kSize;
|
||||
constexpr size_t read_size = BlobLogFooter::kSize;
|
||||
|
||||
const Status s = ReadFromFile(file_reader, read_offset, read_size,
|
||||
&footer_slice, &buf, &aligned_buf);
|
||||
const Status s =
|
||||
ReadFromFile(file_reader, read_offset, read_size, statistics,
|
||||
&footer_slice, &buf, &aligned_buf);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -220,14 +228,16 @@ Status BlobFileReader::ReadFooter(uint64_t file_size,
|
||||
|
||||
Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
uint64_t read_offset, size_t read_size,
|
||||
Slice* slice, Buffer* buf,
|
||||
AlignedBuf* aligned_buf) {
|
||||
Statistics* statistics, Slice* slice,
|
||||
Buffer* buf, AlignedBuf* aligned_buf) {
|
||||
assert(slice);
|
||||
assert(buf);
|
||||
assert(aligned_buf);
|
||||
|
||||
assert(file_reader);
|
||||
|
||||
RecordTick(statistics, BLOB_DB_BLOB_FILE_BYTES_READ, read_size);
|
||||
|
||||
Status s;
|
||||
|
||||
if (file_reader->use_direct_io()) {
|
||||
@@ -256,10 +266,13 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
|
||||
BlobFileReader::BlobFileReader(
|
||||
std::unique_ptr<RandomAccessFileReader>&& file_reader, uint64_t file_size,
|
||||
CompressionType compression_type)
|
||||
CompressionType compression_type, SystemClock* clock,
|
||||
Statistics* statistics)
|
||||
: file_reader_(std::move(file_reader)),
|
||||
file_size_(file_size),
|
||||
compression_type_(compression_type) {
|
||||
compression_type_(compression_type),
|
||||
clock_(clock),
|
||||
statistics_(statistics) {
|
||||
assert(file_reader_);
|
||||
}
|
||||
|
||||
@@ -304,7 +317,7 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
TEST_SYNC_POINT("BlobFileReader::GetBlob:ReadFromFile");
|
||||
|
||||
const Status s = ReadFromFile(file_reader_.get(), record_offset,
|
||||
static_cast<size_t>(record_size),
|
||||
static_cast<size_t>(record_size), statistics_,
|
||||
&record_slice, &buf, &aligned_buf);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -324,8 +337,8 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
const Slice value_slice(record_slice.data() + adjustment, value_size);
|
||||
|
||||
{
|
||||
const Status s =
|
||||
UncompressBlobIfNeeded(value_slice, compression_type, value);
|
||||
const Status s = UncompressBlobIfNeeded(value_slice, compression_type,
|
||||
clock_, statistics_, value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -382,6 +395,8 @@ Status BlobFileReader::VerifyBlob(const Slice& record_slice,
|
||||
|
||||
Status BlobFileReader::UncompressBlobIfNeeded(const Slice& value_slice,
|
||||
CompressionType compression_type,
|
||||
SystemClock* clock,
|
||||
Statistics* statistics,
|
||||
PinnableSlice* value) {
|
||||
assert(value);
|
||||
|
||||
@@ -399,9 +414,14 @@ Status BlobFileReader::UncompressBlobIfNeeded(const Slice& value_slice,
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
constexpr MemoryAllocator* allocator = nullptr;
|
||||
|
||||
CacheAllocationPtr output =
|
||||
UncompressData(info, value_slice.data(), value_slice.size(),
|
||||
&uncompressed_size, compression_format_version, allocator);
|
||||
CacheAllocationPtr output;
|
||||
|
||||
{
|
||||
StopWatch stop_watch(clock, statistics, BLOB_DB_DECOMPRESSION_MICROS);
|
||||
output = UncompressData(info, value_slice.data(), value_slice.size(),
|
||||
&uncompressed_size, compression_format_version,
|
||||
allocator);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"BlobFileReader::UncompressBlobIfNeeded:TamperWithResult", &output);
|
||||
|
||||
@@ -21,6 +21,7 @@ class HistogramImpl;
|
||||
struct ReadOptions;
|
||||
class Slice;
|
||||
class PinnableSlice;
|
||||
class Statistics;
|
||||
|
||||
class BlobFileReader {
|
||||
public:
|
||||
@@ -44,7 +45,8 @@ class BlobFileReader {
|
||||
|
||||
private:
|
||||
BlobFileReader(std::unique_ptr<RandomAccessFileReader>&& file_reader,
|
||||
uint64_t file_size, CompressionType compression_type);
|
||||
uint64_t file_size, CompressionType compression_type,
|
||||
SystemClock* clock, Statistics* statistics);
|
||||
|
||||
static Status OpenFile(const ImmutableOptions& immutable_options,
|
||||
const FileOptions& file_opts,
|
||||
@@ -55,17 +57,17 @@ class BlobFileReader {
|
||||
std::unique_ptr<RandomAccessFileReader>* file_reader);
|
||||
|
||||
static Status ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
uint32_t column_family_id,
|
||||
uint32_t column_family_id, Statistics* statistics,
|
||||
CompressionType* compression_type);
|
||||
|
||||
static Status ReadFooter(uint64_t file_size,
|
||||
const RandomAccessFileReader* file_reader);
|
||||
static Status ReadFooter(const RandomAccessFileReader* file_reader,
|
||||
uint64_t file_size, Statistics* statistics);
|
||||
|
||||
using Buffer = std::unique_ptr<char[]>;
|
||||
|
||||
static Status ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
uint64_t read_offset, size_t read_size,
|
||||
Slice* slice, Buffer* buf,
|
||||
Statistics* statistics, Slice* slice, Buffer* buf,
|
||||
AlignedBuf* aligned_buf);
|
||||
|
||||
static Status VerifyBlob(const Slice& record_slice, const Slice& user_key,
|
||||
@@ -73,6 +75,8 @@ class BlobFileReader {
|
||||
|
||||
static Status UncompressBlobIfNeeded(const Slice& value_slice,
|
||||
CompressionType compression_type,
|
||||
SystemClock* clock,
|
||||
Statistics* statistics,
|
||||
PinnableSlice* value);
|
||||
|
||||
static void SaveValue(const Slice& src, PinnableSlice* dst);
|
||||
@@ -80,6 +84,8 @@ class BlobFileReader {
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader_;
|
||||
uint64_t file_size_;
|
||||
CompressionType compression_type_;
|
||||
SystemClock* clock_;
|
||||
Statistics* statistics_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
#define MAX_HEADER_SIZE(a, b, c) (a > b ? (a > c ? a : c) : (b > c ? b : c))
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class RandomAccessFileReader;
|
||||
@@ -69,10 +71,13 @@ class BlobLogSequentialReader {
|
||||
Statistics* statistics_;
|
||||
|
||||
Slice buffer_;
|
||||
char header_buf_[BlobLogRecord::kHeaderSize];
|
||||
char header_buf_[MAX_HEADER_SIZE(BlobLogHeader::kSize, BlobLogFooter::kSize,
|
||||
BlobLogRecord::kHeaderSize)];
|
||||
|
||||
// which byte to read next
|
||||
uint64_t next_byte_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#undef MAX_HEADER_SIZE
|
||||
@@ -16,7 +16,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBBlobBasicTest : public DBTestBase {
|
||||
protected:
|
||||
DBBlobBasicTest()
|
||||
: DBTestBase("/db_blob_basic_test", /* env_do_fsync */ false) {}
|
||||
: DBTestBase("db_blob_basic_test", /* env_do_fsync */ false) {}
|
||||
};
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetBlob) {
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBBlobCompactionTest : public DBTestBase {
|
||||
public:
|
||||
explicit DBBlobCompactionTest()
|
||||
: DBTestBase("/db_blob_compaction_test", /*env_do_fsync=*/false) {}
|
||||
: DBTestBase("db_blob_compaction_test", /*env_do_fsync=*/false) {}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const std::vector<InternalStats::CompactionStats>& GetCompactionStats() {
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBBlobCorruptionTest : public DBTestBase {
|
||||
protected:
|
||||
DBBlobCorruptionTest()
|
||||
: DBTestBase("/db_blob_corruption_test", /* env_do_fsync */ false) {}
|
||||
: DBTestBase("db_blob_corruption_test", /* env_do_fsync */ false) {}
|
||||
|
||||
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
||||
// Pick file to corrupt
|
||||
|
||||
@@ -41,8 +41,7 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
Tier::kImmutableMemtables,
|
||||
Tier::kL0SstFile, Tier::kLnSstFile};
|
||||
|
||||
DBBlobIndexTest()
|
||||
: DBTestBase("/db_blob_index_test", /*env_do_fsync=*/true) {}
|
||||
DBBlobIndexTest() : DBTestBase("db_blob_index_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
ColumnFamilyHandle* cfh() { return dbfull()->DefaultColumnFamily(); }
|
||||
|
||||
|
||||
@@ -3029,9 +3029,9 @@ unsigned char rocksdb_options_get_advise_random_on_open(
|
||||
return opt->rep.advise_random_on_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_experimental_allow_mempurge(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.experimental_allow_mempurge = v;
|
||||
void rocksdb_options_set_experimental_mempurge_threshold(rocksdb_options_t* opt,
|
||||
double v) {
|
||||
opt->rep.experimental_mempurge_threshold = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_access_hint_on_compaction_start(
|
||||
@@ -3840,7 +3840,8 @@ void rocksdb_filterpolicy_destroy(rocksdb_filterpolicy_t* filter) {
|
||||
delete filter;
|
||||
}
|
||||
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(int bits_per_key, bool original_format) {
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(
|
||||
double bits_per_key, bool original_format) {
|
||||
// Make a rocksdb_filterpolicy_t, but override all of its methods so
|
||||
// they delegate to a NewBloomFilterPolicy() instead of user
|
||||
// supplied C functions.
|
||||
@@ -3875,16 +3876,17 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(int bits_per_ke
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_full(int bits_per_key) {
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_full(
|
||||
double bits_per_key) {
|
||||
return rocksdb_filterpolicy_create_bloom_format(bits_per_key, false);
|
||||
}
|
||||
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom(int bits_per_key) {
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom(double bits_per_key) {
|
||||
return rocksdb_filterpolicy_create_bloom_format(bits_per_key, true);
|
||||
}
|
||||
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_format(
|
||||
int bloom_equivalent_bits_per_key) {
|
||||
double bloom_equivalent_bits_per_key, int bloom_before_level) {
|
||||
// Make a rocksdb_filterpolicy_t, but override all of its methods so
|
||||
// they delegate to a NewRibbonFilterPolicy() instead of user
|
||||
// supplied C functions.
|
||||
@@ -3911,7 +3913,8 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_format(
|
||||
static void DoNothing(void*) {}
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
wrapper->rep_ = NewRibbonFilterPolicy(bloom_equivalent_bits_per_key);
|
||||
wrapper->rep_ =
|
||||
NewRibbonFilterPolicy(bloom_equivalent_bits_per_key, bloom_before_level);
|
||||
wrapper->state_ = nullptr;
|
||||
wrapper->delete_filter_ = nullptr;
|
||||
wrapper->destructor_ = &Wrapper::DoNothing;
|
||||
@@ -3919,9 +3922,15 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_format(
|
||||
}
|
||||
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon(
|
||||
int bloom_equivalent_bits_per_key) {
|
||||
double bloom_equivalent_bits_per_key) {
|
||||
return rocksdb_filterpolicy_create_ribbon_format(
|
||||
bloom_equivalent_bits_per_key);
|
||||
bloom_equivalent_bits_per_key, /*bloom_before_level = disabled*/ -1);
|
||||
}
|
||||
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_hybrid(
|
||||
double bloom_equivalent_bits_per_key, int bloom_before_level) {
|
||||
return rocksdb_filterpolicy_create_ribbon_format(
|
||||
bloom_equivalent_bits_per_key, bloom_before_level);
|
||||
}
|
||||
|
||||
rocksdb_mergeoperator_t* rocksdb_mergeoperator_create(
|
||||
@@ -4875,6 +4884,27 @@ void rocksdb_optimistictransaction_options_set_set_snapshot(
|
||||
opt->rep.set_snapshot = v;
|
||||
}
|
||||
|
||||
char* rocksdb_optimistictransactiondb_property_value(
|
||||
rocksdb_optimistictransactiondb_t* db, const char* propname) {
|
||||
std::string tmp;
|
||||
if (db->rep->GetProperty(Slice(propname), &tmp)) {
|
||||
// We use strdup() since we expect human readable output.
|
||||
return strdup(tmp.c_str());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int rocksdb_optimistictransactiondb_property_int(
|
||||
rocksdb_optimistictransactiondb_t* db, const char* propname,
|
||||
uint64_t* out_val) {
|
||||
if (db->rep->GetIntProperty(Slice(propname), out_val)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_column_family_handle_t* rocksdb_transactiondb_create_column_family(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_options_t* column_family_options,
|
||||
@@ -4945,6 +4975,27 @@ void rocksdb_transactiondb_release_snapshot(
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
char* rocksdb_transactiondb_property_value(rocksdb_transactiondb_t* db,
|
||||
const char* propname) {
|
||||
std::string tmp;
|
||||
if (db->rep->GetProperty(Slice(propname), &tmp)) {
|
||||
// We use strdup() since we expect human readable output.
|
||||
return strdup(tmp.c_str());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int rocksdb_transactiondb_property_int(rocksdb_transactiondb_t* db,
|
||||
const char* propname,
|
||||
uint64_t* out_val) {
|
||||
if (db->rep->GetIntProperty(Slice(propname), out_val)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rocksdb_transaction_t* rocksdb_transaction_begin(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* write_options,
|
||||
@@ -5347,12 +5398,31 @@ rocksdb_transaction_t* rocksdb_optimistictransaction_begin(
|
||||
return old_txn;
|
||||
}
|
||||
|
||||
// Write batch into OptimisticTransactionDB
|
||||
void rocksdb_optimistictransactiondb_write(
|
||||
rocksdb_optimistictransactiondb_t* otxn_db,
|
||||
const rocksdb_writeoptions_t* options, rocksdb_writebatch_t* batch,
|
||||
char** errptr) {
|
||||
SaveError(errptr, otxn_db->rep->Write(options->rep, &batch->rep));
|
||||
}
|
||||
|
||||
void rocksdb_optimistictransactiondb_close(
|
||||
rocksdb_optimistictransactiondb_t* otxn_db) {
|
||||
delete otxn_db->rep;
|
||||
delete otxn_db;
|
||||
}
|
||||
|
||||
rocksdb_checkpoint_t* rocksdb_optimistictransactiondb_checkpoint_object_create(
|
||||
rocksdb_optimistictransactiondb_t* otxn_db, char** errptr) {
|
||||
Checkpoint* checkpoint;
|
||||
if (SaveError(errptr, Checkpoint::Create(otxn_db->rep, &checkpoint))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_checkpoint_t* result = new rocksdb_checkpoint_t;
|
||||
result->rep = checkpoint;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_free(void* ptr) { free(ptr); }
|
||||
|
||||
rocksdb_pinnableslice_t* rocksdb_get_pinned(
|
||||
|
||||
+12
-8
@@ -1043,21 +1043,25 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
StartPhase("filter");
|
||||
for (run = 0; run <= 3; run++) {
|
||||
// First run uses custom filter
|
||||
// Second run uses old block-based bloom filter
|
||||
// Third run uses full bloom filter
|
||||
for (run = 0; run <= 4; run++) {
|
||||
// run=0 uses custom filter
|
||||
// run=1 uses old block-based bloom filter
|
||||
// run=2 run uses full bloom filter
|
||||
// run=3 uses Ribbon
|
||||
// run=4 uses Ribbon-Bloom hybrid configuration
|
||||
CheckNoError(err);
|
||||
rocksdb_filterpolicy_t* policy;
|
||||
if (run == 0) {
|
||||
policy = rocksdb_filterpolicy_create(NULL, FilterDestroy, FilterCreate,
|
||||
FilterKeyMatch, NULL, FilterName);
|
||||
} else if (run == 1) {
|
||||
policy = rocksdb_filterpolicy_create_bloom(8);
|
||||
policy = rocksdb_filterpolicy_create_bloom(8.0);
|
||||
} else if (run == 2) {
|
||||
policy = rocksdb_filterpolicy_create_bloom_full(8);
|
||||
policy = rocksdb_filterpolicy_create_bloom_full(8.0);
|
||||
} else if (run == 3) {
|
||||
policy = rocksdb_filterpolicy_create_ribbon(8.0);
|
||||
} else {
|
||||
policy = rocksdb_filterpolicy_create_ribbon(8);
|
||||
policy = rocksdb_filterpolicy_create_ribbon_hybrid(8.0, 1);
|
||||
}
|
||||
rocksdb_block_based_options_set_filter_policy(table_options, policy);
|
||||
|
||||
@@ -1123,7 +1127,7 @@ int main(int argc, char** argv) {
|
||||
} else if (run == 1) {
|
||||
// Essentially a fingerprint of the block-based Bloom schema
|
||||
CheckCondition(hits == 241);
|
||||
} else if (run == 2) {
|
||||
} else if (run == 2 || run == 4) {
|
||||
// Essentially a fingerprint of full Bloom schema, format_version=5
|
||||
CheckCondition(hits == 188);
|
||||
} else {
|
||||
|
||||
+21
-30
@@ -457,7 +457,7 @@ void SuperVersion::Cleanup() {
|
||||
to_delete.push_back(m);
|
||||
}
|
||||
current->Unref();
|
||||
cfd->UnrefAndTryDelete(this);
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
|
||||
void SuperVersion::Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
@@ -475,10 +475,10 @@ void SuperVersion::Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
|
||||
namespace {
|
||||
void SuperVersionUnrefHandle(void* ptr) {
|
||||
// UnrefHandle is called when a thread exists or a ThreadLocalPtr gets
|
||||
// destroyed. When former happens, the thread shouldn't see kSVInUse.
|
||||
// When latter happens, we are in ~ColumnFamilyData(), no get should happen as
|
||||
// well.
|
||||
// UnrefHandle is called when a thread exits or a ThreadLocalPtr gets
|
||||
// destroyed. When the former happens, the thread shouldn't see kSVInUse.
|
||||
// When the latter happens, only super_version_ holds a reference
|
||||
// to ColumnFamilyData, so no further queries are possible.
|
||||
SuperVersion* sv = static_cast<SuperVersion*>(ptr);
|
||||
bool was_last_ref __attribute__((__unused__));
|
||||
was_last_ref = sv->Unref();
|
||||
@@ -505,7 +505,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* _dummy_versions,
|
||||
Cache* _table_cache, WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options,
|
||||
const FileOptions& file_options, ColumnFamilySet* column_family_set,
|
||||
const FileOptions* file_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_session_id)
|
||||
@@ -668,7 +668,7 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
}
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::UnrefAndTryDelete(SuperVersion* sv_under_cleanup) {
|
||||
bool ColumnFamilyData::UnrefAndTryDelete() {
|
||||
int old_refs = refs_.fetch_sub(1);
|
||||
assert(old_refs > 0);
|
||||
|
||||
@@ -678,22 +678,17 @@ bool ColumnFamilyData::UnrefAndTryDelete(SuperVersion* sv_under_cleanup) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If called under SuperVersion::Cleanup, we should not re-enter Cleanup on
|
||||
// the same SuperVersion. (But while installing a new SuperVersion, this
|
||||
// cfd could be referenced only by two SuperVersions.)
|
||||
if (old_refs == 2 && super_version_ != nullptr &&
|
||||
super_version_ != sv_under_cleanup) {
|
||||
if (old_refs == 2 && super_version_ != nullptr) {
|
||||
// Only the super_version_ holds me
|
||||
SuperVersion* sv = super_version_;
|
||||
super_version_ = nullptr;
|
||||
// Release SuperVersion reference kept in ThreadLocalPtr.
|
||||
// This must be done outside of mutex_ since unref handler can lock mutex.
|
||||
sv->db_mutex->Unlock();
|
||||
|
||||
// Release SuperVersion references kept in ThreadLocalPtr.
|
||||
local_sv_.reset();
|
||||
sv->db_mutex->Lock();
|
||||
|
||||
if (sv->Unref()) {
|
||||
// May delete this ColumnFamilyData after calling Cleanup()
|
||||
// Note: sv will delete this ColumnFamilyData during Cleanup()
|
||||
assert(sv->cfd == this);
|
||||
sv->Cleanup();
|
||||
delete sv;
|
||||
return true;
|
||||
@@ -1059,20 +1054,17 @@ uint64_t ColumnFamilyData::GetLiveSstFilesSize() const {
|
||||
}
|
||||
|
||||
MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq,
|
||||
uint64_t log_number) {
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
return new MemTable(internal_comparator_, ioptions_, mutable_cf_options,
|
||||
write_buffer_manager_, earliest_seq, id_, log_number);
|
||||
write_buffer_manager_, earliest_seq, id_);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::CreateNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq,
|
||||
uint64_t log_number) {
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
if (mem_ != nullptr) {
|
||||
delete mem_->Unref();
|
||||
}
|
||||
SetMemtable(
|
||||
ConstructNewMemtable(mutable_cf_options, earliest_seq, log_number));
|
||||
SetMemtable(ConstructNewMemtable(mutable_cf_options, earliest_seq));
|
||||
mem_->Ref();
|
||||
}
|
||||
|
||||
@@ -1261,14 +1253,13 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex) {
|
||||
db_mutex->AssertHeld();
|
||||
return InstallSuperVersion(sv_context, db_mutex, mutable_cf_options_);
|
||||
return InstallSuperVersion(sv_context, mutable_cf_options_);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex,
|
||||
SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
SuperVersion* new_superversion = sv_context->new_superversion.release();
|
||||
new_superversion->db_mutex = db_mutex;
|
||||
new_superversion->mutable_cf_options = mutable_cf_options;
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_);
|
||||
SuperVersion* old_superversion = super_version_;
|
||||
@@ -1462,14 +1453,14 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_session_id)
|
||||
: max_column_family_(0),
|
||||
file_options_(file_options),
|
||||
dummy_cfd_(new ColumnFamilyData(
|
||||
ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr,
|
||||
nullptr, ColumnFamilyOptions(), *db_options, file_options, nullptr,
|
||||
nullptr, ColumnFamilyOptions(), *db_options, &file_options_, nullptr,
|
||||
block_cache_tracer, io_tracer, db_session_id)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
file_options_(file_options),
|
||||
table_cache_(table_cache),
|
||||
write_buffer_manager_(_write_buffer_manager),
|
||||
write_controller_(_write_controller),
|
||||
@@ -1541,7 +1532,7 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
*db_options_, file_options_, this, block_cache_tracer_, io_tracer_,
|
||||
*db_options_, &file_options_, this, block_cache_tracer_, io_tracer_,
|
||||
db_session_id_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
|
||||
+6
-10
@@ -208,8 +208,6 @@ struct SuperVersion {
|
||||
uint64_t version_number;
|
||||
WriteStallCondition write_stall_condition;
|
||||
|
||||
InstrumentedMutex* db_mutex;
|
||||
|
||||
// should be called outside the mutex
|
||||
SuperVersion() = default;
|
||||
~SuperVersion();
|
||||
@@ -281,8 +279,7 @@ class ColumnFamilyData {
|
||||
// UnrefAndTryDelete() decreases the reference count and do free if needed,
|
||||
// return true if this is freed else false, UnrefAndTryDelete() can only
|
||||
// be called while holding a DB mutex, or during single-threaded recovery.
|
||||
// sv_under_cleanup is only provided when called from SuperVersion::Cleanup.
|
||||
bool UnrefAndTryDelete(SuperVersion* sv_under_cleanup = nullptr);
|
||||
bool UnrefAndTryDelete();
|
||||
|
||||
// SetDropped() can only be called under following conditions:
|
||||
// 1) Holding a DB mutex,
|
||||
@@ -371,10 +368,9 @@ class ColumnFamilyData {
|
||||
|
||||
// See Memtable constructor for explanation of earliest_seq param.
|
||||
MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq,
|
||||
uint64_t log_number = 0);
|
||||
SequenceNumber earliest_seq);
|
||||
void CreateNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq, uint64_t log_number = 0);
|
||||
SequenceNumber earliest_seq);
|
||||
|
||||
TableCache* table_cache() const { return table_cache_.get(); }
|
||||
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
|
||||
@@ -454,7 +450,6 @@ class ColumnFamilyData {
|
||||
// the clients to allocate SuperVersion outside of mutex.
|
||||
// IMPORTANT: Only call this from DBImpl::InstallSuperVersion()
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex);
|
||||
@@ -531,7 +526,7 @@ class ColumnFamilyData {
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const FileOptions& file_options,
|
||||
const FileOptions* file_options,
|
||||
ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
@@ -722,6 +717,8 @@ class ColumnFamilySet {
|
||||
std::unordered_map<uint32_t, ColumnFamilyData*> column_family_data_;
|
||||
|
||||
uint32_t max_column_family_;
|
||||
const FileOptions file_options_;
|
||||
|
||||
ColumnFamilyData* dummy_cfd_;
|
||||
// We don't hold the refcount here, since default column family always exists
|
||||
// We are also not responsible for cleaning up default_cfd_cache_. This is
|
||||
@@ -731,7 +728,6 @@ class ColumnFamilySet {
|
||||
|
||||
const std::string db_name_;
|
||||
const ImmutableDBOptions* const db_options_;
|
||||
const FileOptions file_options_;
|
||||
Cache* table_cache_;
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
WriteController* write_controller_;
|
||||
|
||||
@@ -211,9 +211,9 @@ Compaction::Compaction(
|
||||
std::vector<CompactionInputFiles> _inputs, int _output_level,
|
||||
uint64_t _target_file_size, uint64_t _max_compaction_bytes,
|
||||
uint32_t _output_path_id, CompressionType _compression,
|
||||
CompressionOptions _compression_opts, uint32_t _max_subcompactions,
|
||||
std::vector<FileMetaData*> _grandparents, bool _manual_compaction,
|
||||
double _score, bool _deletion_compaction,
|
||||
CompressionOptions _compression_opts, Temperature _output_temperature,
|
||||
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
|
||||
bool _manual_compaction, double _score, bool _deletion_compaction,
|
||||
CompactionReason _compaction_reason)
|
||||
: input_vstorage_(vstorage),
|
||||
start_level_(_inputs[0].level),
|
||||
@@ -229,6 +229,7 @@ Compaction::Compaction(
|
||||
output_path_id_(_output_path_id),
|
||||
output_compression_(_compression),
|
||||
output_compression_opts_(_compression_opts),
|
||||
output_temperature_(_output_temperature),
|
||||
deletion_compaction_(_deletion_compaction),
|
||||
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
|
||||
grandparents_(std::move(_grandparents)),
|
||||
@@ -308,6 +309,12 @@ bool Compaction::IsTrivialMove() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (start_level_ == output_level_) {
|
||||
// It doesn't make sense if compaction picker picks files just to trivial
|
||||
// move to the same level.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Used in universal compaction, where trivial move can be done if the
|
||||
// input files are non overlapping
|
||||
if ((mutable_cf_options_.compaction_options_universal.allow_trivial_move) &&
|
||||
|
||||
@@ -76,7 +76,8 @@ class Compaction {
|
||||
std::vector<CompactionInputFiles> inputs, int output_level,
|
||||
uint64_t target_file_size, uint64_t max_compaction_bytes,
|
||||
uint32_t output_path_id, CompressionType compression,
|
||||
CompressionOptions compression_opts, uint32_t max_subcompactions,
|
||||
CompressionOptions compression_opts,
|
||||
Temperature output_temperature, uint32_t max_subcompactions,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
bool manual_compaction = false, double score = -1,
|
||||
bool deletion_compaction = false,
|
||||
@@ -299,6 +300,8 @@ class Compaction {
|
||||
|
||||
uint64_t max_compaction_bytes() const { return max_compaction_bytes_; }
|
||||
|
||||
Temperature output_temperature() const { return output_temperature_; }
|
||||
|
||||
uint32_t max_subcompactions() const { return max_subcompactions_; }
|
||||
|
||||
uint64_t MinInputFileOldestAncesterTime() const;
|
||||
@@ -356,6 +359,7 @@ class Compaction {
|
||||
const uint32_t output_path_id_;
|
||||
CompressionType output_compression_;
|
||||
CompressionOptions output_compression_opts_;
|
||||
Temperature output_temperature_;
|
||||
// If true, then the compaction can be done by simply deleting input files.
|
||||
const bool deletion_compaction_;
|
||||
|
||||
|
||||
@@ -38,4 +38,6 @@ struct CompactionIterationStats {
|
||||
// Blob related statistics
|
||||
uint64_t num_blobs_read = 0;
|
||||
uint64_t total_blob_bytes_read = 0;
|
||||
uint64_t num_blobs_relocated = 0;
|
||||
uint64_t total_blob_bytes_relocated = 0;
|
||||
};
|
||||
|
||||
@@ -16,22 +16,6 @@
|
||||
#include "table/internal_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
#define DEFINITELY_IN_SNAPSHOT(seq, snapshot) \
|
||||
((seq) <= (snapshot) && \
|
||||
(snapshot_checker_ == nullptr || \
|
||||
LIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) == \
|
||||
SnapshotCheckerResult::kInSnapshot)))
|
||||
|
||||
#define DEFINITELY_NOT_IN_SNAPSHOT(seq, snapshot) \
|
||||
((seq) > (snapshot) || \
|
||||
(snapshot_checker_ != nullptr && \
|
||||
UNLIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) == \
|
||||
SnapshotCheckerResult::kNotInSnapshot)))
|
||||
|
||||
#define IN_EARLIEST_SNAPSHOT(seq) \
|
||||
((seq) <= earliest_snapshot_ && \
|
||||
(snapshot_checker_ == nullptr || LIKELY(IsInEarliestSnapshot(seq))))
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
@@ -511,11 +495,11 @@ void CompactionIterator::NextFromInput() {
|
||||
"Unexpected key type %d for compaction output",
|
||||
ikey_.type);
|
||||
}
|
||||
assert(current_user_key_snapshot_ == last_snapshot);
|
||||
if (current_user_key_snapshot_ != last_snapshot) {
|
||||
assert(current_user_key_snapshot_ >= last_snapshot);
|
||||
if (current_user_key_snapshot_ < last_snapshot) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"current_user_key_snapshot_ (%" PRIu64
|
||||
") != last_snapshot (%" PRIu64 ")",
|
||||
") < last_snapshot (%" PRIu64 ")",
|
||||
current_user_key_snapshot_, last_snapshot);
|
||||
}
|
||||
|
||||
@@ -571,10 +555,20 @@ void CompactionIterator::NextFromInput() {
|
||||
ParseInternalKey(input_.key(), &next_ikey, allow_data_in_errors_)
|
||||
.ok() &&
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key)) {
|
||||
#ifndef NDEBUG
|
||||
const Compaction* c =
|
||||
compaction_ ? compaction_->real_compaction() : nullptr;
|
||||
#endif
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::NextFromInput:SingleDelete:1",
|
||||
const_cast<Compaction*>(c));
|
||||
|
||||
// Check whether the next key belongs to the same snapshot as the
|
||||
// SingleDelete.
|
||||
if (prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot)) {
|
||||
DefinitelyNotInSnapshot(next_ikey.sequence, prev_snapshot)) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::NextFromInput:SingleDelete:2", nullptr);
|
||||
if (next_ikey.type == kTypeSingleDeletion) {
|
||||
// We encountered two SingleDeletes in a row. This could be due to
|
||||
// unexpected user input.
|
||||
@@ -586,8 +580,8 @@ void CompactionIterator::NextFromInput() {
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
} else if (has_outputted_key_ ||
|
||||
DEFINITELY_IN_SNAPSHOT(
|
||||
ikey_.sequence, earliest_write_conflict_snapshot_)) {
|
||||
DefinitelyInSnapshot(ikey_.sequence,
|
||||
earliest_write_conflict_snapshot_)) {
|
||||
// Found a matching value, we can drop the single delete and the
|
||||
// value. It is safe to drop both records since we've already
|
||||
// outputted a key in this snapshot, or there is no earlier
|
||||
@@ -620,6 +614,9 @@ void CompactionIterator::NextFromInput() {
|
||||
// Set up the Put to be outputted in the next iteration.
|
||||
// (Optimization 3).
|
||||
clear_and_output_next_key_ = true;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::NextFromInput:KeepSDForWW",
|
||||
/*arg=*/nullptr);
|
||||
}
|
||||
} else {
|
||||
// We hit the next snapshot without hitting a put, so the iterator
|
||||
@@ -635,7 +632,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// iteration. If the next key is corrupt, we return before the
|
||||
// comparison, so the value of has_current_user_key does not matter.
|
||||
has_current_user_key_ = false;
|
||||
if (compaction_ != nullptr && IN_EARLIEST_SNAPSHOT(ikey_.sequence) &&
|
||||
if (compaction_ != nullptr &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
// Key doesn't exist outside of this range.
|
||||
@@ -679,7 +677,7 @@ void CompactionIterator::NextFromInput() {
|
||||
(ikey_.type == kTypeDeletion ||
|
||||
(ikey_.type == kTypeDeletionWithTimestamp &&
|
||||
cmp_with_history_ts_low_ < 0)) &&
|
||||
IN_EARLIEST_SNAPSHOT(ikey_.sequence) &&
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
@@ -722,6 +720,13 @@ void CompactionIterator::NextFromInput() {
|
||||
ikey_.user_key, &level_ptrs_));
|
||||
ParsedInternalKey next_ikey;
|
||||
AdvanceInputIter();
|
||||
#ifndef NDEBUG
|
||||
const Compaction* c =
|
||||
compaction_ ? compaction_->real_compaction() : nullptr;
|
||||
#endif
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::NextFromInput:BottommostDelete:1",
|
||||
const_cast<Compaction*>(c));
|
||||
// Skip over all versions of this key that happen to occur in the same
|
||||
// snapshot range as the delete.
|
||||
//
|
||||
@@ -734,7 +739,7 @@ void CompactionIterator::NextFromInput() {
|
||||
.ok()) &&
|
||||
cmp_->EqualWithoutTimestamp(ikey_.user_key, next_ikey.user_key) &&
|
||||
(prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot))) {
|
||||
DefinitelyNotInSnapshot(next_ikey.sequence, prev_snapshot))) {
|
||||
AdvanceInputIter();
|
||||
}
|
||||
// If you find you still need to output a row with this key, we need to output the
|
||||
@@ -914,6 +919,9 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
++iter_stats_.num_blobs_read;
|
||||
iter_stats_.total_blob_bytes_read += bytes_read;
|
||||
|
||||
++iter_stats_.num_blobs_relocated;
|
||||
iter_stats_.total_blob_bytes_relocated += blob_index.size();
|
||||
|
||||
value_ = blob_value_;
|
||||
|
||||
if (ExtractLargeValueIfNeededImpl()) {
|
||||
@@ -977,7 +985,8 @@ void CompactionIterator::PrepareOutput() {
|
||||
if (valid_ && compaction_ != nullptr &&
|
||||
!compaction_->allow_ingest_behind() &&
|
||||
ikeyNotNeededForIncrementalSnapshot() && bottommost_level_ &&
|
||||
IN_EARLIEST_SNAPSHOT(ikey_.sequence) && ikey_.type != kTypeMerge) {
|
||||
DefinitelyInSnapshot(ikey_.sequence, earliest_snapshot_) &&
|
||||
ikey_.type != kTypeMerge) {
|
||||
assert(ikey_.type != kTypeDeletion && ikey_.type != kTypeSingleDeletion);
|
||||
if (ikey_.type == kTypeDeletion || ikey_.type == kTypeSingleDeletion) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
@@ -1053,7 +1062,7 @@ inline bool CompactionIterator::ikeyNotNeededForIncrementalSnapshot() {
|
||||
(ikey_.sequence < preserve_deletes_seqnum_);
|
||||
}
|
||||
|
||||
bool CompactionIterator::IsInEarliestSnapshot(SequenceNumber sequence) {
|
||||
bool CompactionIterator::IsInCurrentEarliestSnapshot(SequenceNumber sequence) {
|
||||
assert(snapshot_checker_ != nullptr);
|
||||
bool pre_condition = (earliest_snapshot_ == kMaxSequenceNumber ||
|
||||
(earliest_snapshot_iter_ != snapshots_->end() &&
|
||||
|
||||
@@ -99,6 +99,8 @@ class CompactionIterator {
|
||||
virtual Version* input_version() const = 0;
|
||||
|
||||
virtual bool DoesInputReferenceBlobFiles() const = 0;
|
||||
|
||||
virtual const Compaction* real_compaction() const = 0;
|
||||
};
|
||||
|
||||
class RealCompaction : public CompactionProxy {
|
||||
@@ -152,6 +154,8 @@ class CompactionIterator {
|
||||
return compaction_->DoesInputReferenceBlobFiles();
|
||||
}
|
||||
|
||||
const Compaction* real_compaction() const override { return compaction_; }
|
||||
|
||||
private:
|
||||
const Compaction* compaction_;
|
||||
};
|
||||
@@ -267,7 +271,13 @@ class CompactionIterator {
|
||||
SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
bool IsInEarliestSnapshot(SequenceNumber sequence);
|
||||
bool IsInCurrentEarliestSnapshot(SequenceNumber sequence);
|
||||
|
||||
bool DefinitelyInSnapshot(SequenceNumber seq, SequenceNumber snapshot);
|
||||
|
||||
bool DefinitelyNotInSnapshot(SequenceNumber seq, SequenceNumber snapshot);
|
||||
|
||||
bool InCurrentEarliestSnapshot(SequenceNumber seq);
|
||||
|
||||
// Extract user-defined timestamp from user key if possible and compare it
|
||||
// with *full_history_ts_low_ if applicable.
|
||||
@@ -412,4 +422,27 @@ class CompactionIterator {
|
||||
manual_compaction_canceled_->load(std::memory_order_relaxed));
|
||||
}
|
||||
};
|
||||
|
||||
inline bool CompactionIterator::DefinitelyInSnapshot(SequenceNumber seq,
|
||||
SequenceNumber snapshot) {
|
||||
return ((seq) <= (snapshot) &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
|
||||
SnapshotCheckerResult::kInSnapshot)));
|
||||
}
|
||||
|
||||
inline bool CompactionIterator::DefinitelyNotInSnapshot(
|
||||
SequenceNumber seq, SequenceNumber snapshot) {
|
||||
return ((seq) > (snapshot) ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) ==
|
||||
SnapshotCheckerResult::kNotInSnapshot)));
|
||||
}
|
||||
|
||||
inline bool CompactionIterator::InCurrentEarliestSnapshot(SequenceNumber seq) {
|
||||
return ((seq) <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(IsInCurrentEarliestSnapshot(seq))));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -184,6 +184,8 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
|
||||
bool DoesInputReferenceBlobFiles() const override { return false; }
|
||||
|
||||
const Compaction* real_compaction() const override { return nullptr; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
bool is_bottommost_level = false;
|
||||
|
||||
@@ -107,6 +107,8 @@ const char* GetCompactionReasonString(CompactionReason compaction_reason) {
|
||||
return "ExternalSstIngestion";
|
||||
case CompactionReason::kPeriodicCompaction:
|
||||
return "PeriodicCompaction";
|
||||
case CompactionReason::kChangeTemperature:
|
||||
return "ChangeTemperature";
|
||||
case CompactionReason::kNumOfReasons:
|
||||
// fall through
|
||||
default:
|
||||
@@ -879,7 +881,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
|
||||
UpdateCompactionJobStats(stats);
|
||||
|
||||
auto stream = event_logger_->LogToBuffer(log_buffer_);
|
||||
auto stream = event_logger_->LogToBuffer(log_buffer_, 8192);
|
||||
stream << "job" << job_id_ << "event"
|
||||
<< "compaction_finished"
|
||||
<< "compaction_time_micros" << stats.micros
|
||||
@@ -1367,6 +1369,16 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
RecordTick(stats_, FILTER_OPERATION_TOTAL_TIME,
|
||||
c_iter_stats.total_filter_time);
|
||||
|
||||
if (c_iter_stats.num_blobs_relocated > 0) {
|
||||
RecordTick(stats_, BLOB_DB_GC_NUM_KEYS_RELOCATED,
|
||||
c_iter_stats.num_blobs_relocated);
|
||||
}
|
||||
if (c_iter_stats.total_blob_bytes_relocated > 0) {
|
||||
RecordTick(stats_, BLOB_DB_GC_BYTES_RELOCATED,
|
||||
c_iter_stats.total_blob_bytes_relocated);
|
||||
}
|
||||
|
||||
RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
|
||||
RecordCompactionIOStats();
|
||||
|
||||
@@ -1927,11 +1939,12 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
|
||||
// Pass temperature of botommost files to FileSystem.
|
||||
FileOptions fo_copy = file_options_;
|
||||
Temperature temperature = Temperature::kUnknown;
|
||||
if (bottommost_level_) {
|
||||
fo_copy.temperature = temperature =
|
||||
Temperature temperature = sub_compact->compaction->output_temperature();
|
||||
if (temperature == Temperature::kUnknown && bottommost_level_) {
|
||||
temperature =
|
||||
sub_compact->compaction->mutable_cf_options()->bottommost_temperature;
|
||||
}
|
||||
fo_copy.temperature = temperature;
|
||||
|
||||
Status s;
|
||||
IOStatus io_s = NewWritableFile(fs_.get(), fname, &writable_file, fo_copy);
|
||||
|
||||
@@ -334,8 +334,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(), mutable_db_options_,
|
||||
compaction_input_files, output_level, 1024 * 1024, 10 * 1024 * 1024, 0,
|
||||
kNoCompression, cfd->GetLatestMutableCFOptions()->compression_opts, 0,
|
||||
{}, true);
|
||||
kNoCompression, cfd->GetLatestMutableCFOptions()->compression_opts,
|
||||
Temperature::kUnknown, 0, {}, true);
|
||||
compaction.SetInputVersion(cfd->current());
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
|
||||
@@ -358,7 +358,7 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
output_level, compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id, compression_type,
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
compact_options.max_subcompactions,
|
||||
Temperature::kUnknown, compact_options.max_subcompactions,
|
||||
/* grandparents */ {}, true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
@@ -634,7 +634,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
compact_range_options.max_subcompactions, /* grandparents */ {},
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
/* grandparents */ {},
|
||||
/* is manual */ true);
|
||||
RegisterCompaction(c);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
@@ -812,7 +813,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options, vstorage, output_level),
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
Temperature::kUnknown, compact_range_options.max_subcompactions,
|
||||
std::move(grandparents),
|
||||
/* is manual compaction */ true);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::CompactRange:Return", compaction);
|
||||
|
||||
@@ -111,7 +111,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOTtl);
|
||||
@@ -154,7 +154,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
{comp_inputs}, 0, 16 * 1024 * 1024 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */,
|
||||
0 /* output path ID */, mutable_cf_options.compression,
|
||||
mutable_cf_options.compression_opts, 0 /* max_subcompactions */, {},
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
0 /* max_subcompactions */, {},
|
||||
/* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false,
|
||||
CompactionReason::kFIFOReduceNumFiles);
|
||||
@@ -203,13 +204,119 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
std::move(inputs), 0, 0, 0, 0, kNoCompression,
|
||||
mutable_cf_options.compression_opts,
|
||||
mutable_cf_options.compression_opts, Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompactionToWarm(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) {
|
||||
if (mutable_cf_options.compaction_options_fifo.age_for_warm == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
|
||||
int64_t _current_time;
|
||||
auto status = ioptions_.clock->GetCurrentTime(&_current_time);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: Couldn't get current time: %s. "
|
||||
"Not doing compactions based on warm threshold. ",
|
||||
cf_name.c_str(), status.ToString().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
if (!level0_compactions_in_progress_.empty()) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: Already executing compaction. Parallel "
|
||||
"compactions are not supported",
|
||||
cf_name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
inputs.emplace_back();
|
||||
inputs[0].level = 0;
|
||||
|
||||
// avoid underflow
|
||||
if (current_time > mutable_cf_options.compaction_options_fifo.age_for_warm) {
|
||||
uint64_t create_time_threshold =
|
||||
current_time - mutable_cf_options.compaction_options_fifo.age_for_warm;
|
||||
uint64_t compaction_size = 0;
|
||||
// We will ideally identify a file qualifying for warm tier by knowing
|
||||
// the timestamp for the youngest entry in the file. However, right now
|
||||
// we don't have the information. We infer it by looking at timestamp
|
||||
// of the next file's (which is just younger) oldest entry's timestamp.
|
||||
FileMetaData* prev_file = nullptr;
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
FileMetaData* f = *ritr;
|
||||
assert(f);
|
||||
if (f->being_compacted) {
|
||||
// Right now this probably won't happen as we never try to schedule
|
||||
// two compactions in parallel, so here we just simply don't schedule
|
||||
// anything.
|
||||
return nullptr;
|
||||
}
|
||||
uint64_t oldest_ancester_time = f->TryGetOldestAncesterTime();
|
||||
if (oldest_ancester_time == kUnknownOldestAncesterTime) {
|
||||
// Older files might not have enough information. It is possible to
|
||||
// handle these files by looking at newer files, but maintaining the
|
||||
// logic isn't worth it.
|
||||
break;
|
||||
}
|
||||
if (oldest_ancester_time > create_time_threshold) {
|
||||
// The previous file (which has slightly older data) doesn't qualify
|
||||
// for warm tier.
|
||||
break;
|
||||
}
|
||||
if (prev_file != nullptr) {
|
||||
compaction_size += prev_file->fd.GetFileSize();
|
||||
if (compaction_size > mutable_cf_options.max_compaction_bytes) {
|
||||
break;
|
||||
}
|
||||
inputs[0].files.push_back(prev_file);
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with next file's oldest time %" PRIu64 " for warm",
|
||||
cf_name.c_str(), prev_file->fd.GetNumber(),
|
||||
oldest_ancester_time);
|
||||
}
|
||||
if (f->temperature == Temperature::kUnknown ||
|
||||
f->temperature == Temperature::kHot) {
|
||||
prev_file = f;
|
||||
} else if (!inputs[0].files.empty()) {
|
||||
// A warm file newer than files picked.
|
||||
break;
|
||||
} else {
|
||||
assert(prev_file == nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs[0].files.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, mutable_db_options,
|
||||
std::move(inputs), 0, 0 /* output file size limit */,
|
||||
0 /* max compaction bytes, not applicable */, 0 /* output path ID */,
|
||||
mutable_cf_options.compression, mutable_cf_options.compression_opts,
|
||||
Temperature::kWarm,
|
||||
/* max_subcompactions */ 0, {}, /* is manual */ false,
|
||||
vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ false, CompactionReason::kChangeTemperature);
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options, VersionStorageInfo* vstorage,
|
||||
@@ -225,6 +332,10 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
c = PickSizeCompaction(cf_name, mutable_cf_options, mutable_db_options,
|
||||
vstorage, log_buffer);
|
||||
}
|
||||
if (c == nullptr) {
|
||||
c = PickCompactionToWarm(cf_name, mutable_cf_options, mutable_db_options,
|
||||
vstorage, log_buffer);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,12 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
Compaction* PickCompactionToWarm(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableDBOptions& mutable_db_options,
|
||||
VersionStorageInfo* version,
|
||||
LogBuffer* log_buffer);
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
@@ -336,6 +336,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level_),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
|
||||
@@ -98,7 +98,9 @@ class CompactionPickerTest : public testing::Test {
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0, bool marked_for_compact = false) {
|
||||
size_t compensated_file_size = 0, bool marked_for_compact = false,
|
||||
Temperature temperature = Temperature::kUnknown,
|
||||
uint64_t oldest_ancestor_time = kUnknownOldestAncesterTime) {
|
||||
VersionStorageInfo* vstorage;
|
||||
if (temp_vstorage_) {
|
||||
vstorage = temp_vstorage_.get();
|
||||
@@ -115,6 +117,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->temperature = temperature;
|
||||
f->oldest_ancester_time = oldest_ancestor_time;
|
||||
vstorage->AddFile(level, f);
|
||||
files_.emplace_back(f);
|
||||
file_map_.insert({file_number, {f, level}});
|
||||
@@ -757,6 +761,245 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
vstorage_->CompactionScore(0) >= 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarm1) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(3U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarm2) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 4000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarmMaxSize) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 9;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 4000);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 5000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarmWithExistingWarm) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 4000);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kWarm, threshold_time - 5000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarmWithOngoing) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 4000);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kWarm, threshold_time - 5000);
|
||||
file_map_[2].first->being_compacted = true;
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
// Stop if a file is being compacted
|
||||
ASSERT_TRUE(compaction.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FIFOToWarmWithHotBetweenWarms) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const uint64_t kFileSize = 100000;
|
||||
const uint64_t kMaxSize = kFileSize * 100000;
|
||||
uint64_t kWarmThreshold = 2000;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.age_for_warm = kWarmThreshold;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
uint64_t threshold_time =
|
||||
static_cast<uint64_t>(current_time) - kWarmThreshold;
|
||||
Add(0, 6U, "240", "290", 2 * kFileSize, 0, 2900, 3000, 0, true,
|
||||
Temperature::kUnknown, static_cast<uint64_t>(current_time) - 100);
|
||||
Add(0, 5U, "240", "290", 2 * kFileSize, 0, 2700, 2800, 0, true,
|
||||
Temperature::kUnknown, threshold_time + 100);
|
||||
Add(0, 4U, "260", "300", 1 * kFileSize, 0, 2500, 2600, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 2000);
|
||||
Add(0, 3U, "200", "300", 4 * kFileSize, 0, 2300, 2400, 0, true,
|
||||
Temperature::kWarm, threshold_time - 3000);
|
||||
Add(0, 2U, "200", "300", 4 * kFileSize, 0, 2100, 2200, 0, true,
|
||||
Temperature::kUnknown, threshold_time - 4000);
|
||||
Add(0, 1U, "200", "300", 4 * kFileSize, 0, 2000, 2100, 0, true,
|
||||
Temperature::kWarm, threshold_time - 5000);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
ASSERT_EQ(fifo_compaction_picker.NeedsCompaction(vstorage_.get()), true);
|
||||
std::unique_ptr<Compaction> compaction(fifo_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
|
||||
&log_buffer_));
|
||||
// Stop if a file is being compacted
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
|
||||
@@ -728,6 +728,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
|
||||
1, enable_compression),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, start_level,
|
||||
enable_compression),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
@@ -955,6 +956,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */,
|
||||
CompactionReason::kFilesMarkedForCompaction);
|
||||
@@ -1029,6 +1031,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
|
||||
output_level, 1, true /* enable_compression */),
|
||||
GetCompressionOptions(mutable_cf_options_, vstorage_, output_level,
|
||||
true /* enable_compression */),
|
||||
Temperature::kUnknown,
|
||||
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
|
||||
score_, false /* deletion_compaction */, compaction_reason);
|
||||
}
|
||||
|
||||
@@ -337,8 +337,9 @@ class PartialDeleteCompactionFilter : public CompactionFilter {
|
||||
TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
auto delete_comp_filter = PartialDeleteCompactionFilter();
|
||||
options.compaction_filter = &delete_comp_filter;
|
||||
std::unique_ptr<CompactionFilter> delete_comp_filter(
|
||||
new PartialDeleteCompactionFilter());
|
||||
options.compaction_filter = delete_comp_filter.get();
|
||||
options.compaction_service =
|
||||
std::make_shared<MyTestCompactionService>(dbname_, options);
|
||||
|
||||
|
||||
+29
-2
@@ -31,7 +31,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBBasicTest : public DBTestBase {
|
||||
public:
|
||||
DBBasicTest() : DBTestBase("/db_basic_test", /*env_do_fsync=*/false) {}
|
||||
DBBasicTest() : DBTestBase("db_basic_test", /*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTest, OpenWhenOpen) {
|
||||
@@ -1362,6 +1362,28 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFSnapshot) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFUnsorted) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"one", "two"}, options);
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(2, "baz", "xyz"));
|
||||
ASSERT_OK(Put(1, "abc", "def"));
|
||||
|
||||
// Note: keys for the same CF do not form a consecutive range
|
||||
std::vector<int> cfs{1, 2, 1};
|
||||
std::vector<std::string> keys{"foo", "baz", "abc"};
|
||||
std::vector<std::string> values;
|
||||
|
||||
values =
|
||||
MultiGet(cfs, keys, /* snapshot */ nullptr, /* batched */ GetParam());
|
||||
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(values[0], "bar");
|
||||
ASSERT_EQ(values[1], "xyz");
|
||||
ASSERT_EQ(values[2], "def");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBMultiGetTestWithParam, DBMultiGetTestWithParam,
|
||||
testing::Bool());
|
||||
|
||||
@@ -2576,7 +2598,7 @@ class DBBasicTestTrackWal : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBBasicTestTrackWal()
|
||||
: DBTestBase("/db_basic_test_track_wal", /*env_do_fsync=*/false) {}
|
||||
: DBTestBase("db_basic_test_track_wal", /*env_do_fsync=*/false) {}
|
||||
|
||||
int CountWalFiles() {
|
||||
VectorLogPtr log_files;
|
||||
@@ -2734,6 +2756,11 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions(), handles_[cf]));
|
||||
}
|
||||
}
|
||||
// Clear compressed cache, which is always pre-populated
|
||||
if (compressed_cache_) {
|
||||
compressed_cache_->SetCapacity(0);
|
||||
compressed_cache_->SetCapacity(1048576);
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckValue(int i, const std::string& value) {
|
||||
|
||||
+225
-3
@@ -7,6 +7,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
@@ -14,9 +15,12 @@
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -39,7 +43,7 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
const size_t kValueSize = 100;
|
||||
|
||||
DBBlockCacheTest()
|
||||
: DBTestBase("/db_block_cache_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_block_cache_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
BlockBasedTableOptions GetTableOptions() {
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -497,12 +501,98 @@ TEST_F(DBBlockCacheTest, WarmCacheWithDataBlocksDuringFlush) {
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
}
|
||||
|
||||
// This test cache data, index and filter blocks during flush.
|
||||
TEST_F(DBBlockCacheTest, WarmCacheWithBlocksDuringFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewLRUCache(1 << 25, 0, false);
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.prepopulate_block_cache =
|
||||
BlockBasedTableOptions::PrepopulateBlockCache::kFlushOnly;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
for (size_t i = 1; i <= kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(i * 3, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_HIT));
|
||||
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(i * 2,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, DynamicallyWarmCacheDuringFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewLRUCache(1 << 25, 0, false);
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
table_options.prepopulate_block_cache =
|
||||
BlockBasedTableOptions::PrepopulateBlockCache::kFlushOnly;
|
||||
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
|
||||
for (size_t i = 1; i <= 5; i++) {
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(
|
||||
0, options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"block_based_table_factory", "{prepopulate_block_cache=kDisable;}"}}));
|
||||
|
||||
for (size_t i = 6; i <= kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(
|
||||
1, options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getAndResetTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@@ -1212,13 +1302,145 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Disable LinkFile so that we can physically copy a DB using Checkpoint.
|
||||
// Disable file GetUniqueId to enable stable cache keys.
|
||||
class StableCacheKeyTestFS : public FaultInjectionTestFS {
|
||||
public:
|
||||
explicit StableCacheKeyTestFS(const std::shared_ptr<FileSystem>& base)
|
||||
: FaultInjectionTestFS(base) {
|
||||
SetFailGetUniqueId(true);
|
||||
}
|
||||
|
||||
virtual ~StableCacheKeyTestFS() override {}
|
||||
|
||||
IOStatus LinkFile(const std::string&, const std::string&, const IOOptions&,
|
||||
IODebugContext*) override {
|
||||
return IOStatus::NotSupported("Disabled");
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBBlockCacheTest, StableCacheKeys) {
|
||||
std::shared_ptr<StableCacheKeyTestFS> test_fs{
|
||||
new StableCacheKeyTestFS(env_->GetFileSystem())};
|
||||
std::unique_ptr<CompositeEnvWrapper> test_env{
|
||||
new CompositeEnvWrapper(env_, test_fs)};
|
||||
|
||||
for (bool compressed : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.env = test_env.get();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
std::function<void()> verify_stats;
|
||||
if (compressed) {
|
||||
if (!Snappy_Supported()) {
|
||||
fprintf(stderr, "skipping compressed test, snappy unavailable\n");
|
||||
continue;
|
||||
}
|
||||
options.compression = CompressionType::kSnappyCompression;
|
||||
table_options.no_block_cache = true;
|
||||
table_options.block_cache_compressed = NewLRUCache(1 << 25, 0, false);
|
||||
verify_stats = [&options] {
|
||||
// One for ordinary SST file and one for external SST file
|
||||
ASSERT_EQ(
|
||||
2, options.statistics->getTickerCount(BLOCK_CACHE_COMPRESSED_ADD));
|
||||
};
|
||||
} else {
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache = NewLRUCache(1 << 25, 0, false);
|
||||
verify_stats = [&options] {
|
||||
ASSERT_EQ(2, options.statistics->getTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(2, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(2,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
};
|
||||
}
|
||||
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Ordinary SST file
|
||||
ASSERT_OK(Put("key1", "abc"));
|
||||
std::string something_compressible(500U, 'x');
|
||||
ASSERT_OK(Put("key1a", something_compressible));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// External SST file
|
||||
std::string external = dbname_ + "/external.sst";
|
||||
{
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
ASSERT_OK(sst_file_writer.Open(external));
|
||||
ASSERT_OK(sst_file_writer.Put("key2", "abc"));
|
||||
ASSERT_OK(sst_file_writer.Put("key2a", something_compressible));
|
||||
ExternalSstFileInfo external_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&external_info));
|
||||
IngestExternalFileOptions ingest_opts;
|
||||
ASSERT_OK(db_->IngestExternalFile({external}, ingest_opts));
|
||||
}
|
||||
#else
|
||||
// Another ordinary SST file
|
||||
ASSERT_OK(Put("key2", "abc"));
|
||||
ASSERT_OK(Put("key2a", something_compressible));
|
||||
ASSERT_OK(Flush());
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(Get("key1"), std::string("abc"));
|
||||
ASSERT_EQ(Get("key2"), std::string("abc"));
|
||||
verify_stats();
|
||||
|
||||
// Make sure we can cache hit after re-open
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(Get("key1"), std::string("abc"));
|
||||
ASSERT_EQ(Get("key2"), std::string("abc"));
|
||||
verify_stats();
|
||||
|
||||
// Make sure we can cache hit even on a full copy of the DB. Using
|
||||
// StableCacheKeyTestFS, Checkpoint will resort to full copy not hard link.
|
||||
// (Checkpoint not available in LITE mode to test this.)
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto db_copy_name = dbname_ + "-copy";
|
||||
Checkpoint* checkpoint;
|
||||
ASSERT_OK(Checkpoint::Create(db_, &checkpoint));
|
||||
ASSERT_OK(checkpoint->CreateCheckpoint(db_copy_name));
|
||||
delete checkpoint;
|
||||
|
||||
Close();
|
||||
Destroy(options);
|
||||
SaveAndRestore<std::string> save_dbname(&dbname_, db_copy_name);
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(Get("key1"), std::string("abc"));
|
||||
ASSERT_EQ(Get("key2"), std::string("abc"));
|
||||
verify_stats();
|
||||
|
||||
// And ensure that re-ingesting the same external file into a different DB
|
||||
// uses same cache keys
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IngestExternalFileOptions ingest_opts;
|
||||
ASSERT_OK(db_->IngestExternalFile({external}, ingest_opts));
|
||||
|
||||
ASSERT_EQ(Get("key2"), std::string("abc"));
|
||||
verify_stats();
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
Close();
|
||||
Destroy(options);
|
||||
}
|
||||
}
|
||||
|
||||
class DBBlockCachePinningTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<bool, PinningTier, PinningTier, PinningTier>> {
|
||||
public:
|
||||
DBBlockCachePinningTest()
|
||||
: DBTestBase("/db_block_cache_test", /*env_do_fsync=*/false) {}
|
||||
: DBTestBase("db_block_cache_test", /*env_do_fsync=*/false) {}
|
||||
|
||||
void SetUp() override {
|
||||
partition_index_and_filters_ = std::get<0>(GetParam());
|
||||
|
||||
@@ -28,7 +28,7 @@ using BFP = BloomFilterPolicy;
|
||||
class DBBloomFilterTest : public DBTestBase {
|
||||
public:
|
||||
DBBloomFilterTest()
|
||||
: DBTestBase("/db_bloom_filter_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_bloom_filter_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
class DBBloomFilterTestWithParam : public DBTestBase,
|
||||
@@ -42,7 +42,7 @@ class DBBloomFilterTestWithParam : public DBTestBase,
|
||||
|
||||
public:
|
||||
DBBloomFilterTestWithParam()
|
||||
: DBTestBase("/db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
|
||||
~DBBloomFilterTestWithParam() override {}
|
||||
|
||||
@@ -1094,7 +1094,7 @@ class DBBloomFilterTestVaryPrefixAndFormatVer
|
||||
|
||||
public:
|
||||
DBBloomFilterTestVaryPrefixAndFormatVer()
|
||||
: DBTestBase("/db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_bloom_filter_tests", /*env_do_fsync=*/true) {}
|
||||
|
||||
~DBBloomFilterTestVaryPrefixAndFormatVer() override {}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ static std::string NEW_VALUE = "NewValue";
|
||||
class DBTestCompactionFilter : public DBTestBase {
|
||||
public:
|
||||
DBTestCompactionFilter()
|
||||
: DBTestBase("/db_compaction_filter_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_compaction_filter_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
// Param variant of DBTestBase::ChangeCompactOptions
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBCompactionTest : public DBTestBase {
|
||||
public:
|
||||
DBCompactionTest()
|
||||
: DBTestBase("/db_compaction_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_compaction_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
class DBCompactionTestWithParam
|
||||
@@ -40,7 +40,7 @@ class DBCompactionTestWithParam
|
||||
public testing::WithParamInterface<std::tuple<uint32_t, bool>> {
|
||||
public:
|
||||
DBCompactionTestWithParam()
|
||||
: DBTestBase("/db_compaction_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("db_compaction_test", /*env_do_fsync=*/true) {
|
||||
max_subcompactions_ = std::get<0>(GetParam());
|
||||
exclusive_manual_compaction_ = std::get<1>(GetParam());
|
||||
}
|
||||
@@ -58,7 +58,7 @@ class DBCompactionTestWithBottommostParam
|
||||
public testing::WithParamInterface<BottommostLevelCompaction> {
|
||||
public:
|
||||
DBCompactionTestWithBottommostParam()
|
||||
: DBTestBase("/db_compaction_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("db_compaction_test", /*env_do_fsync=*/true) {
|
||||
bottommost_level_compaction_ = GetParam();
|
||||
}
|
||||
|
||||
@@ -2332,7 +2332,7 @@ TEST_P(DBCompactionTestWithParam, LevelCompactionCFPathUse) {
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt1);
|
||||
CreateColumnFamilies({"one"},option_vector[1]);
|
||||
|
||||
// Configura CF2 specific paths.
|
||||
// Configure CF2 specific paths.
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2", 500 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_2", 4 * 1024 * 1024);
|
||||
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_3", 1024 * 1024 * 1024);
|
||||
@@ -2387,13 +2387,16 @@ TEST_P(DBCompactionTestWithParam, LevelCompactionCFPathUse) {
|
||||
// Check that default column family uses db_paths.
|
||||
// And Column family "one" uses cf_paths.
|
||||
|
||||
// First three 110KB files are not going to second path.
|
||||
// After that, (100K, 200K)
|
||||
// The compaction in level0 outputs the sst files in level1.
|
||||
// The first path cannot hold level1's data(400KB+400KB > 500KB),
|
||||
// so every compaction move a sst file to second path. Please
|
||||
// refer to LevelCompactionBuilder::GetPathId.
|
||||
for (int num = 0; num < 3; num++) {
|
||||
generate_file();
|
||||
}
|
||||
check_sstfilecount(0, 1);
|
||||
check_sstfilecount(1, 2);
|
||||
|
||||
// Another 110KB triggers a compaction to 400K file to fill up first path
|
||||
generate_file();
|
||||
check_sstfilecount(1, 3);
|
||||
|
||||
@@ -5005,7 +5008,7 @@ class CompactionPriTest : public DBTestBase,
|
||||
public testing::WithParamInterface<uint32_t> {
|
||||
public:
|
||||
CompactionPriTest()
|
||||
: DBTestBase("/compaction_pri_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("compaction_pri_test", /*env_do_fsync=*/true) {
|
||||
compaction_pri_ = GetParam();
|
||||
}
|
||||
|
||||
@@ -6746,6 +6749,69 @@ TEST_F(DBCompactionTest, CompactionWithChecksumHandoffManifest2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, FIFOWarm) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.age_for_warm = 1000;
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
Reopen(options);
|
||||
|
||||
int total_warm = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kWarm) {
|
||||
total_warm++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_warm);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBTestDynamicLevel : public DBTestBase {
|
||||
public:
|
||||
DBTestDynamicLevel()
|
||||
: DBTestBase("/db_dynamic_level_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_dynamic_level_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBEncryptionTest : public DBTestBase {
|
||||
public:
|
||||
DBEncryptionTest()
|
||||
: DBTestBase("/db_encryption_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_encryption_test", /*env_do_fsync=*/true) {}
|
||||
Env* GetTargetEnv() {
|
||||
if (encrypted_env_ != nullptr) {
|
||||
return (static_cast<EnvWrapper*>(encrypted_env_))->target();
|
||||
|
||||
+16
-1
@@ -127,7 +127,22 @@ Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
return wal_manager_.GetSortedWalFiles(files);
|
||||
|
||||
// Disable deletion in order to avoid the case where a file is deleted in
|
||||
// the middle of the process so IO error is returned.
|
||||
Status s = DisableFileDeletions();
|
||||
bool file_deletion_supported = !s.IsNotSupported();
|
||||
if (s.ok() || !file_deletion_supported) {
|
||||
s = wal_manager_.GetSortedWalFiles(files);
|
||||
if (file_deletion_supported) {
|
||||
Status s2 = EnableFileDeletions(false);
|
||||
if (!s2.ok() && s.ok()) {
|
||||
s = s2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::GetCurrentWalFile(std::unique_ptr<LogFile>* current_log_file) {
|
||||
|
||||
+109
-11
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
@@ -30,7 +31,7 @@ static std::string NEW_VALUE = "NewValue";
|
||||
|
||||
class DBFlushTest : public DBTestBase {
|
||||
public:
|
||||
DBFlushTest() : DBTestBase("/db_flush_test", /*env_do_fsync=*/true) {}
|
||||
DBFlushTest() : DBTestBase("db_flush_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
class DBFlushDirectIOTest : public DBFlushTest,
|
||||
@@ -662,6 +663,87 @@ TEST_F(DBFlushTest, StatisticsGarbageRangeDeletes) {
|
||||
Close();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// This simple Listener can only handle one flush at a time.
|
||||
class TestFlushListener : public EventListener {
|
||||
public:
|
||||
TestFlushListener(Env* env, DBFlushTest* test)
|
||||
: slowdown_count(0), stop_count(0), db_closed(), env_(env), test_(test) {
|
||||
db_closed = false;
|
||||
}
|
||||
|
||||
~TestFlushListener() override {
|
||||
prev_fc_info_.status.PermitUncheckedError(); // Ignore the status
|
||||
}
|
||||
void OnTableFileCreated(const TableFileCreationInfo& info) override {
|
||||
// remember the info for later checking the FlushJobInfo.
|
||||
prev_fc_info_ = info;
|
||||
ASSERT_GT(info.db_name.size(), 0U);
|
||||
ASSERT_GT(info.cf_name.size(), 0U);
|
||||
ASSERT_GT(info.file_path.size(), 0U);
|
||||
ASSERT_GT(info.job_id, 0);
|
||||
ASSERT_GT(info.table_properties.data_size, 0U);
|
||||
ASSERT_GT(info.table_properties.raw_key_size, 0U);
|
||||
ASSERT_GT(info.table_properties.raw_value_size, 0U);
|
||||
ASSERT_GT(info.table_properties.num_data_blocks, 0U);
|
||||
ASSERT_GT(info.table_properties.num_entries, 0U);
|
||||
ASSERT_EQ(info.file_checksum, kUnknownFileChecksum);
|
||||
ASSERT_EQ(info.file_checksum_func_name, kUnknownFileChecksumFuncName);
|
||||
}
|
||||
|
||||
void OnFlushCompleted(DB* db, const FlushJobInfo& info) override {
|
||||
flushed_dbs_.push_back(db);
|
||||
flushed_column_family_names_.push_back(info.cf_name);
|
||||
if (info.triggered_writes_slowdown) {
|
||||
slowdown_count++;
|
||||
}
|
||||
if (info.triggered_writes_stop) {
|
||||
stop_count++;
|
||||
}
|
||||
// verify whether the previously created file matches the flushed file.
|
||||
ASSERT_EQ(prev_fc_info_.db_name, db->GetName());
|
||||
ASSERT_EQ(prev_fc_info_.cf_name, info.cf_name);
|
||||
ASSERT_EQ(prev_fc_info_.job_id, info.job_id);
|
||||
ASSERT_EQ(prev_fc_info_.file_path, info.file_path);
|
||||
ASSERT_EQ(TableFileNameToNumber(info.file_path), info.file_number);
|
||||
|
||||
// Note: the following chunk relies on the notification pertaining to the
|
||||
// database pointed to by DBTestBase::db_, and is thus bypassed when
|
||||
// that assumption does not hold (see the test case MultiDBMultiListeners
|
||||
// below).
|
||||
ASSERT_TRUE(test_);
|
||||
if (db == test_->db_) {
|
||||
std::vector<std::vector<FileMetaData>> files_by_level;
|
||||
test_->dbfull()->TEST_GetFilesMetaData(db->DefaultColumnFamily(),
|
||||
&files_by_level);
|
||||
|
||||
ASSERT_FALSE(files_by_level.empty());
|
||||
auto it = std::find_if(files_by_level[0].begin(), files_by_level[0].end(),
|
||||
[&](const FileMetaData& meta) {
|
||||
return meta.fd.GetNumber() == info.file_number;
|
||||
});
|
||||
ASSERT_NE(it, files_by_level[0].end());
|
||||
ASSERT_EQ(info.oldest_blob_file_number, it->oldest_blob_file_number);
|
||||
}
|
||||
|
||||
ASSERT_EQ(db->GetEnv()->GetThreadID(), info.thread_id);
|
||||
ASSERT_GT(info.thread_id, 0U);
|
||||
}
|
||||
|
||||
std::vector<std::string> flushed_column_family_names_;
|
||||
std::vector<DB*> flushed_dbs_;
|
||||
int slowdown_count;
|
||||
int stop_count;
|
||||
bool db_closing;
|
||||
std::atomic_bool db_closed;
|
||||
TableFileCreationInfo prev_fc_info_;
|
||||
|
||||
protected:
|
||||
Env* env_;
|
||||
DBFlushTest* test_;
|
||||
};
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
@@ -694,7 +776,11 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
// Enforce size of a single MemTable to 64MB (64MB = 67108864 bytes).
|
||||
options.write_buffer_size = 1 << 20;
|
||||
// Activate the MemPurge prototype.
|
||||
options.experimental_allow_mempurge = true;
|
||||
options.experimental_mempurge_threshold = 1.0;
|
||||
#ifndef ROCKSDB_LITE
|
||||
TestFlushListener* listener = new TestFlushListener(options.env, this);
|
||||
options.listeners.emplace_back(listener);
|
||||
#endif // !ROCKSDB_LITE
|
||||
ASSERT_OK(TryReopen(options));
|
||||
uint32_t mempurge_count = 0;
|
||||
uint32_t sst_count = 0;
|
||||
@@ -808,7 +894,7 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
// Assert that at least one flush to storage has been performed
|
||||
ASSERT_GT(sst_count, EXPECTED_SST_COUNT);
|
||||
// (which will consequently increase the number of mempurges recorded too).
|
||||
ASSERT_EQ(mempurge_count, mempurge_count_record);
|
||||
ASSERT_GE(mempurge_count, mempurge_count_record);
|
||||
|
||||
// Assert that there is no data corruption, even with
|
||||
// a flush to storage.
|
||||
@@ -837,11 +923,15 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
|
||||
options.compression = kNoCompression;
|
||||
options.inplace_update_support = false;
|
||||
options.allow_concurrent_memtable_write = true;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TestFlushListener* listener = new TestFlushListener(options.env, this);
|
||||
options.listeners.emplace_back(listener);
|
||||
#endif // !ROCKSDB_LITE
|
||||
// Enforce size of a single MemTable to 64MB (64MB = 67108864 bytes).
|
||||
options.write_buffer_size = 1 << 20;
|
||||
// Activate the MemPurge prototype.
|
||||
options.experimental_allow_mempurge = true;
|
||||
options.experimental_mempurge_threshold = 1.0;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
uint32_t mempurge_count = 0;
|
||||
@@ -1034,7 +1124,10 @@ TEST_F(DBFlushTest, MemPurgeAndCompactionFilter) {
|
||||
options.compression = kNoCompression;
|
||||
options.inplace_update_support = false;
|
||||
options.allow_concurrent_memtable_write = true;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TestFlushListener* listener = new TestFlushListener(options.env, this);
|
||||
options.listeners.emplace_back(listener);
|
||||
#endif // !ROCKSDB_LITE
|
||||
// Create a ConditionalUpdate compaction filter
|
||||
// that will update all the values of the KV pairs
|
||||
// where the keys are "lower" than KEY4.
|
||||
@@ -1044,7 +1137,8 @@ TEST_F(DBFlushTest, MemPurgeAndCompactionFilter) {
|
||||
// Enforce size of a single MemTable to 64MB (64MB = 67108864 bytes).
|
||||
options.write_buffer_size = 1 << 20;
|
||||
// Activate the MemPurge prototype.
|
||||
options.experimental_allow_mempurge = true;
|
||||
options.experimental_mempurge_threshold = 1.0;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
uint32_t mempurge_count = 0;
|
||||
@@ -1116,10 +1210,11 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
|
||||
options.inplace_update_support = false;
|
||||
options.allow_concurrent_memtable_write = true;
|
||||
|
||||
// Enforce size of a single MemTable to 1MB.
|
||||
// Enforce size of a single MemTable to 128KB.
|
||||
options.write_buffer_size = 128 << 10;
|
||||
// Activate the MemPurge prototype.
|
||||
options.experimental_allow_mempurge = true;
|
||||
options.experimental_mempurge_threshold = 1.0;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
const size_t KVSIZE = 10;
|
||||
@@ -1158,7 +1253,7 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
|
||||
// more than would fit in maximum allowed memtables.
|
||||
Random rnd(719);
|
||||
const size_t NUM_REPEAT = 100;
|
||||
const size_t RAND_KEY_LENGTH = 8192;
|
||||
const size_t RAND_KEY_LENGTH = 4096;
|
||||
const size_t RAND_VALUES_LENGTH = 1024;
|
||||
std::vector<std::string> values_default(KVSIZE), values_pikachu(KVSIZE);
|
||||
|
||||
@@ -1235,7 +1330,10 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
|
||||
const uint32_t EXPECTED_SST_COUNT = 0;
|
||||
|
||||
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
|
||||
if (options.experimental_mempurge_threshold ==
|
||||
std::numeric_limits<double>::max()) {
|
||||
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
|
||||
}
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
// Check that there was no data corruption anywhere,
|
||||
|
||||
+61
-48
@@ -94,14 +94,17 @@
|
||||
#include "table/table_builder.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "trace_replay/trace_replay.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/defer.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/trace/replayer_impl.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -557,7 +560,7 @@ Status DBImpl::CloseHelper() {
|
||||
// flushing (but need to implement something
|
||||
// else than imm()->IsFlushPending() because the output
|
||||
// memtables added to imm() dont trigger flushes).
|
||||
if (immutable_db_options_.experimental_allow_mempurge) {
|
||||
if (immutable_db_options_.experimental_mempurge_threshold > 0.0) {
|
||||
Status flush_ret;
|
||||
mutex_.Unlock();
|
||||
for (ColumnFamilyData* cf : *versions_->GetColumnFamilySet()) {
|
||||
@@ -640,7 +643,7 @@ Status DBImpl::CloseHelper() {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"Unable to Sync WAL file %s with error -- %s",
|
||||
LogFileName(immutable_db_options_.wal_dir, log_number).c_str(),
|
||||
LogFileName(immutable_db_options_.GetWalDir(), log_number).c_str(),
|
||||
s.ToString().c_str());
|
||||
// Retain the first error
|
||||
if (ret.ok()) {
|
||||
@@ -734,7 +737,8 @@ void DBImpl::MaybeIgnoreError(Status* s) const {
|
||||
const Status DBImpl::CreateArchivalDirectory() {
|
||||
if (immutable_db_options_.WAL_ttl_seconds > 0 ||
|
||||
immutable_db_options_.WAL_size_limit_MB > 0) {
|
||||
std::string archivalPath = ArchivalDirectory(immutable_db_options_.wal_dir);
|
||||
std::string archivalPath =
|
||||
ArchivalDirectory(immutable_db_options_.GetWalDir());
|
||||
return env_->CreateDirIfMissing(archivalPath);
|
||||
}
|
||||
return Status::OK();
|
||||
@@ -1136,9 +1140,19 @@ Status DBImpl::SetDBOptions(
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
s = GetMutableDBOptionsFromStrings(mutable_db_options_, options_map,
|
||||
&new_options);
|
||||
|
||||
if (new_options.bytes_per_sync == 0) {
|
||||
new_options.bytes_per_sync = 1024 * 1024;
|
||||
}
|
||||
|
||||
if (MutableDBOptionsAreEqual(mutable_db_options_, new_options)) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"SetDBOptions(), input option value is not changed, "
|
||||
"skipping updating.");
|
||||
persist_options_status.PermitUncheckedError();
|
||||
return s;
|
||||
}
|
||||
|
||||
DBOptions new_db_options =
|
||||
BuildDBOptions(immutable_db_options_, new_options);
|
||||
if (s.ok()) {
|
||||
@@ -1784,6 +1798,8 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
}
|
||||
// If timestamp is used, we use read callback to ensure <key,t,s> is returned
|
||||
// only if t <= read_opts.timestamp and s <= snapshot.
|
||||
// HACK: temporarily overwrite input struct field but restore
|
||||
SaveAndRestore<ReadCallback*> restore_callback(&get_impl_options.callback);
|
||||
if (ts_sz > 0) {
|
||||
assert(!get_impl_options
|
||||
.callback); // timestamp with callback is not supported
|
||||
@@ -2276,20 +2292,18 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
multiget_cf_data;
|
||||
size_t cf_start = 0;
|
||||
ColumnFamilyHandle* cf = sorted_keys[0]->column_family;
|
||||
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
KeyContext* key_ctx = sorted_keys[i];
|
||||
if (key_ctx->column_family != cf) {
|
||||
multiget_cf_data.emplace_back(
|
||||
MultiGetColumnFamilyData(cf, cf_start, i - cf_start, nullptr));
|
||||
multiget_cf_data.emplace_back(cf, cf_start, i - cf_start, nullptr);
|
||||
cf_start = i;
|
||||
cf = key_ctx->column_family;
|
||||
}
|
||||
}
|
||||
{
|
||||
// multiget_cf_data.emplace_back(
|
||||
// MultiGetColumnFamilyData(cf, cf_start, num_keys - cf_start, nullptr));
|
||||
multiget_cf_data.emplace_back(cf, cf_start, num_keys - cf_start, nullptr);
|
||||
}
|
||||
|
||||
multiget_cf_data.emplace_back(cf, cf_start, num_keys - cf_start, nullptr);
|
||||
|
||||
std::function<MultiGetColumnFamilyData*(
|
||||
autovector<MultiGetColumnFamilyData,
|
||||
MultiGetContext::MAX_BATCH_SIZE>::iterator&)>
|
||||
@@ -2349,7 +2363,7 @@ struct CompareKeyContext {
|
||||
static_cast<ColumnFamilyHandleImpl*>(lhs->column_family);
|
||||
uint32_t cfd_id1 = cfh->cfd()->GetID();
|
||||
const Comparator* comparator = cfh->cfd()->user_comparator();
|
||||
cfh = static_cast<ColumnFamilyHandleImpl*>(lhs->column_family);
|
||||
cfh = static_cast<ColumnFamilyHandleImpl*>(rhs->column_family);
|
||||
uint32_t cfd_id2 = cfh->cfd()->GetID();
|
||||
|
||||
if (cfd_id1 < cfd_id2) {
|
||||
@@ -2373,39 +2387,24 @@ struct CompareKeyContext {
|
||||
void DBImpl::PrepareMultiGetKeys(
|
||||
size_t num_keys, bool sorted_input,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys) {
|
||||
#ifndef NDEBUG
|
||||
if (sorted_input) {
|
||||
for (size_t index = 0; index < sorted_keys->size(); ++index) {
|
||||
if (index > 0) {
|
||||
KeyContext* lhs = (*sorted_keys)[index - 1];
|
||||
KeyContext* rhs = (*sorted_keys)[index];
|
||||
ColumnFamilyHandleImpl* cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(lhs->column_family);
|
||||
uint32_t cfd_id1 = cfh->cfd()->GetID();
|
||||
const Comparator* comparator = cfh->cfd()->user_comparator();
|
||||
cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(lhs->column_family);
|
||||
uint32_t cfd_id2 = cfh->cfd()->GetID();
|
||||
#ifndef NDEBUG
|
||||
CompareKeyContext key_context_less;
|
||||
|
||||
assert(cfd_id1 <= cfd_id2);
|
||||
if (cfd_id1 < cfd_id2) {
|
||||
continue;
|
||||
}
|
||||
for (size_t index = 1; index < sorted_keys->size(); ++index) {
|
||||
const KeyContext* const lhs = (*sorted_keys)[index - 1];
|
||||
const KeyContext* const rhs = (*sorted_keys)[index];
|
||||
|
||||
// Both keys are from the same column family
|
||||
int cmp = comparator->CompareWithoutTimestamp(
|
||||
*(lhs->key), /*a_has_ts=*/false, *(rhs->key), /*b_has_ts=*/false);
|
||||
assert(cmp <= 0);
|
||||
}
|
||||
index++;
|
||||
// lhs should be <= rhs, or in other words, rhs should NOT be < lhs
|
||||
assert(!key_context_less(rhs, lhs));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!sorted_input) {
|
||||
CompareKeyContext sort_comparator;
|
||||
std::sort(sorted_keys->begin(), sorted_keys->begin() + num_keys,
|
||||
sort_comparator);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::sort(sorted_keys->begin(), sorted_keys->begin() + num_keys,
|
||||
CompareKeyContext());
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
@@ -3946,10 +3945,10 @@ Status DBImpl::GetDbSessionId(std::string& session_id) const {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::SetDbSessionId() {
|
||||
std::string DBImpl::GenerateDbSessionId(Env* env) {
|
||||
// GenerateUniqueId() generates an identifier that has a negligible
|
||||
// probability of being duplicated, ~128 bits of entropy
|
||||
std::string uuid = env_->GenerateUniqueId();
|
||||
std::string uuid = env->GenerateUniqueId();
|
||||
|
||||
// Hash and reformat that down to a more compact format, 20 characters
|
||||
// in base-36 ([0-9A-Z]), which is ~103 bits of entropy, which is enough
|
||||
@@ -3960,15 +3959,21 @@ void DBImpl::SetDbSessionId() {
|
||||
// * Visually distinct from DB id format
|
||||
uint64_t a = NPHash64(uuid.data(), uuid.size(), 1234U);
|
||||
uint64_t b = NPHash64(uuid.data(), uuid.size(), 5678U);
|
||||
db_session_id_.resize(20);
|
||||
std::string db_session_id;
|
||||
db_session_id.resize(20);
|
||||
static const char* const base36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
size_t i = 0;
|
||||
for (; i < 10U; ++i, a /= 36U) {
|
||||
db_session_id_[i] = base36[a % 36];
|
||||
db_session_id[i] = base36[a % 36];
|
||||
}
|
||||
for (; i < 20U; ++i, b /= 36U) {
|
||||
db_session_id_[i] = base36[b % 36];
|
||||
db_session_id[i] = base36[b % 36];
|
||||
}
|
||||
return db_session_id;
|
||||
}
|
||||
|
||||
void DBImpl::SetDbSessionId() {
|
||||
db_session_id_ = GenerateDbSessionId(env_);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::SetDbSessionId", &db_session_id_);
|
||||
}
|
||||
|
||||
@@ -4038,7 +4043,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
ImmutableDBOptions soptions(SanitizeOptions(dbname, options));
|
||||
Env* env = soptions.env;
|
||||
std::vector<std::string> filenames;
|
||||
bool wal_in_db_path = IsWalDirSameAsDBPath(&soptions);
|
||||
bool wal_in_db_path = soptions.IsWalDirSameAsDBPath();
|
||||
|
||||
// Reset the logger because it holds a handle to the
|
||||
// log file and prevents cleanup and directory removal
|
||||
@@ -4104,7 +4109,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
std::vector<std::string> walDirFiles;
|
||||
std::string archivedir = ArchivalDirectory(dbname);
|
||||
bool wal_dir_exists = false;
|
||||
if (dbname != soptions.wal_dir) {
|
||||
if (!soptions.IsWalDirSameAsDBPath(dbname)) {
|
||||
wal_dir_exists = env->GetChildren(soptions.wal_dir, &walDirFiles).ok();
|
||||
archivedir = ArchivalDirectory(soptions.wal_dir);
|
||||
}
|
||||
@@ -4194,6 +4199,8 @@ Status DBImpl::WriteOptionsFile(bool need_mutex_lock,
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::WriteOptionsFile:1");
|
||||
TEST_SYNC_POINT("DBImpl::WriteOptionsFile:2");
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::WriteOptionsFile:PersistOptions",
|
||||
&db_options);
|
||||
|
||||
std::string file_name =
|
||||
TempOptionsFileName(GetName(), versions_->NewFileNumber());
|
||||
@@ -4360,9 +4367,7 @@ SequenceNumber DBImpl::GetEarliestMemTableSequenceNumber(SuperVersion* sv,
|
||||
|
||||
return earliest_seq;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
bool cache_only,
|
||||
SequenceNumber lower_bound_seq,
|
||||
@@ -5104,11 +5109,19 @@ Status DBImpl::EndTrace() {
|
||||
s = tracer_->Close();
|
||||
tracer_.reset();
|
||||
} else {
|
||||
return Status::IOError("No trace file to close");
|
||||
s = Status::IOError("No trace file to close");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::NewDefaultReplayer(
|
||||
const std::vector<ColumnFamilyHandle*>& handles,
|
||||
std::unique_ptr<TraceReader>&& reader,
|
||||
std::unique_ptr<Replayer>* replayer) {
|
||||
replayer->reset(new ReplayerImpl(this, handles, std::move(reader)));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::StartBlockCacheTrace(
|
||||
const TraceOptions& trace_options,
|
||||
std::unique_ptr<TraceWriter>&& trace_writer) {
|
||||
|
||||
@@ -51,8 +51,13 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/status.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "rocksdb/trace_reader_writer.h"
|
||||
#endif // ROCKSDB_LITE
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "rocksdb/utilities/replayer.h"
|
||||
#endif // ROCKSDB_LITE
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
@@ -464,6 +469,12 @@ class DBImpl : public DB {
|
||||
using DB::EndTrace;
|
||||
virtual Status EndTrace() override;
|
||||
|
||||
using DB::NewDefaultReplayer;
|
||||
virtual Status NewDefaultReplayer(
|
||||
const std::vector<ColumnFamilyHandle*>& handles,
|
||||
std::unique_ptr<TraceReader>&& reader,
|
||||
std::unique_ptr<Replayer>* replayer) override;
|
||||
|
||||
using DB::StartBlockCacheTrace;
|
||||
Status StartBlockCacheTrace(
|
||||
const TraceOptions& options,
|
||||
@@ -1103,6 +1114,8 @@ class DBImpl : public DB {
|
||||
State state_;
|
||||
};
|
||||
|
||||
static std::string GenerateDbSessionId(Env* env);
|
||||
|
||||
protected:
|
||||
const std::string dbname_;
|
||||
std::string db_id_;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#include <cinttypes>
|
||||
#include <deque>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
@@ -198,7 +199,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
need_cancel = true;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:AfterPickMemtables");
|
||||
|
||||
bool switched_to_mempurge = false;
|
||||
// Within flush_job.Run, rocksdb may call event listener to notify
|
||||
// file creation and deletion.
|
||||
//
|
||||
@@ -206,7 +207,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// and EventListener callback will be called when the db_mutex
|
||||
// is unlocked by the current thread.
|
||||
if (s.ok()) {
|
||||
s = flush_job.Run(&logs_with_prep_tracker_, &file_meta);
|
||||
s = flush_job.Run(&logs_with_prep_tracker_, &file_meta,
|
||||
&switched_to_mempurge);
|
||||
need_cancel = false;
|
||||
}
|
||||
|
||||
@@ -282,7 +284,9 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// from never needing it or ignoring the flush job status
|
||||
io_s.PermitUncheckedError();
|
||||
}
|
||||
if (s.ok()) {
|
||||
// If flush ran smoothly and no mempurge happened
|
||||
// install new SST file path.
|
||||
if (s.ok() && (!switched_to_mempurge)) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// may temporarily unlock and lock the mutex.
|
||||
NotifyOnFlushCompleted(cfd, mutable_cf_options,
|
||||
@@ -407,10 +411,13 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
stats_, &event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri, io_tracer_, db_id_, db_session_id_,
|
||||
cfd->GetFullHistoryTsLow()));
|
||||
cfd->GetFullHistoryTsLow(), &blob_callback_));
|
||||
}
|
||||
|
||||
std::vector<FileMetaData> file_meta(num_cfs);
|
||||
// Use of deque<bool> because vector<bool>
|
||||
// is specific and doesn't allow &v[i].
|
||||
std::deque<bool> switched_to_mempurge(num_cfs, false);
|
||||
Status s;
|
||||
IOStatus log_io_s = IOStatus::OK();
|
||||
assert(num_cfs == static_cast<int>(jobs.size()));
|
||||
@@ -460,10 +467,13 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
assert(switched_to_mempurge.size() ==
|
||||
static_cast<long unsigned int>(num_cfs));
|
||||
// TODO (yanqin): parallelize jobs with threads.
|
||||
for (int i = 1; i != num_cfs; ++i) {
|
||||
exec_status[i].second =
|
||||
jobs[i]->Run(&logs_with_prep_tracker_, &file_meta[i]);
|
||||
jobs[i]->Run(&logs_with_prep_tracker_, &file_meta[i],
|
||||
&(switched_to_mempurge.at(i)));
|
||||
exec_status[i].first = true;
|
||||
io_status[i] = jobs[i]->io_status();
|
||||
}
|
||||
@@ -475,8 +485,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
}
|
||||
assert(exec_status.size() > 0);
|
||||
assert(!file_meta.empty());
|
||||
exec_status[0].second =
|
||||
jobs[0]->Run(&logs_with_prep_tracker_, &file_meta[0]);
|
||||
exec_status[0].second = jobs[0]->Run(
|
||||
&logs_with_prep_tracker_, file_meta.data() /* &file_meta[0] */,
|
||||
switched_to_mempurge.empty() ? nullptr : &(switched_to_mempurge.at(0)));
|
||||
exec_status[0].first = true;
|
||||
io_status[0] = jobs[0]->io_status();
|
||||
|
||||
@@ -590,6 +601,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<FileMetaData*> tmp_file_meta;
|
||||
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
|
||||
committed_flush_jobs_info;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const auto& mems = jobs[i]->GetMemTables();
|
||||
if (!cfds[i]->IsDropped() && !mems.empty()) {
|
||||
@@ -597,13 +610,18 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
mems_list.emplace_back(&mems);
|
||||
mutable_cf_options_list.emplace_back(&all_mutable_cf_options[i]);
|
||||
tmp_file_meta.emplace_back(&file_meta[i]);
|
||||
#ifndef ROCKSDB_LITE
|
||||
committed_flush_jobs_info.emplace_back(
|
||||
jobs[i]->GetCommittedFlushJobsInfo());
|
||||
#endif //! ROCKSDB_LITE
|
||||
}
|
||||
}
|
||||
|
||||
s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, tmp_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), &logs_with_prep_tracker_, &mutex_, tmp_file_meta,
|
||||
&job_context->memtables_to_free, directories_.GetDbDir(), log_buffer);
|
||||
committed_flush_jobs_info, &job_context->memtables_to_free,
|
||||
directories_.GetDbDir(), log_buffer);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -649,6 +667,11 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
immutable_db_options_.sst_file_manager.get());
|
||||
assert(all_mutable_cf_options.size() == static_cast<size_t>(num_cfs));
|
||||
for (int i = 0; s.ok() && i != num_cfs; ++i) {
|
||||
// If mempurge happened instead of Flush,
|
||||
// no NotifyOnFlushCompleted call (no SST file created).
|
||||
if (switched_to_mempurge[i]) {
|
||||
continue;
|
||||
}
|
||||
if (cfds[i]->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
@@ -1257,7 +1280,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
&manual_compaction_paused_, nullptr, db_id_, db_session_id_,
|
||||
c->column_family_data()->GetFullHistoryTsLow());
|
||||
c->column_family_data()->GetFullHistoryTsLow(), &blob_callback_);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
@@ -2403,7 +2426,7 @@ void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req,
|
||||
// future changes. Therefore, we add the following if
|
||||
// statement - note that calling it twice (or more)
|
||||
// doesn't break anything.
|
||||
if (immutable_db_options_.experimental_allow_mempurge) {
|
||||
if (immutable_db_options_.experimental_mempurge_threshold > 0.0) {
|
||||
// If imm() contains silent memtables,
|
||||
// requesting a flush will mark the imm_needed as true.
|
||||
cfd->imm()->FlushRequested();
|
||||
@@ -2549,7 +2572,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
|
||||
for (const auto& iter : flush_req) {
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
if (immutable_db_options_.experimental_allow_mempurge) {
|
||||
if (immutable_db_options_.experimental_mempurge_threshold > 0.0) {
|
||||
// If imm() contains silent memtables,
|
||||
// requesting a flush will mark the imm_needed as true.
|
||||
cfd->imm()->FlushRequested();
|
||||
@@ -2785,9 +2808,11 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
|
||||
if (prepicked_compaction != nullptr &&
|
||||
prepicked_compaction->task_token != nullptr) {
|
||||
// Releasing task tokens affects the DB state, so must be done before we
|
||||
// potentially signal the DB close process to proceed below.
|
||||
prepicked_compaction->task_token->ReleaseOnce();
|
||||
// Releasing task tokens affects (and asserts on) the DB state, so
|
||||
// must be done before we potentially signal the DB close process to
|
||||
// proceed below.
|
||||
prepicked_compaction->task_token.reset();
|
||||
;
|
||||
}
|
||||
|
||||
if (made_progress ||
|
||||
@@ -3168,7 +3193,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&compaction_job_stats, thread_pri, io_tracer_,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr,
|
||||
is_manual ? manual_compaction->canceled : nullptr, db_id_,
|
||||
db_session_id_, c->column_family_data()->GetFullHistoryTsLow());
|
||||
db_session_id_, c->column_family_data()->GetFullHistoryTsLow(),
|
||||
&blob_callback_);
|
||||
compaction_job.Prepare();
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
@@ -3467,7 +3493,7 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
if (UNLIKELY(sv_context->new_superversion == nullptr)) {
|
||||
sv_context->NewSuperVersion();
|
||||
}
|
||||
cfd->InstallSuperVersion(sv_context, &mutex_, mutable_cf_options);
|
||||
cfd->InstallSuperVersion(sv_context, mutable_cf_options);
|
||||
|
||||
// There may be a small data race here. The snapshot tricking bottommost
|
||||
// compaction may already be released here. But assuming there will always be
|
||||
|
||||
+20
-20
@@ -38,20 +38,26 @@ uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
}
|
||||
|
||||
Status DBImpl::DisableFileDeletions() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return DisableFileDeletionsWithLock();
|
||||
Status s;
|
||||
int my_disable_delete_obsolete_files;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
s = DisableFileDeletionsWithLock();
|
||||
my_disable_delete_obsolete_files = disable_delete_obsolete_files_;
|
||||
}
|
||||
if (my_disable_delete_obsolete_files == 1) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Disabled");
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
my_disable_delete_obsolete_files);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::DisableFileDeletionsWithLock() {
|
||||
mutex_.AssertHeld();
|
||||
++disable_delete_obsolete_files_;
|
||||
if (disable_delete_obsolete_files_ == 1) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Disabled");
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -215,7 +221,8 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
|
||||
// Add log files in wal_dir
|
||||
if (immutable_db_options_.wal_dir != dbname_) {
|
||||
|
||||
if (!immutable_db_options_.IsWalDirSameAsDBPath(dbname_)) {
|
||||
std::vector<std::string> log_files;
|
||||
Status s = env_->GetChildren(immutable_db_options_.wal_dir, &log_files);
|
||||
s.PermitUncheckedError(); // TODO: What should we do on error?
|
||||
@@ -395,10 +402,10 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
blob_file.GetPath());
|
||||
}
|
||||
|
||||
auto wal_dir = immutable_db_options_.GetWalDir();
|
||||
for (auto file_num : state.log_delete_files) {
|
||||
if (file_num > 0) {
|
||||
candidate_files.emplace_back(LogFileName(file_num),
|
||||
immutable_db_options_.wal_dir);
|
||||
candidate_files.emplace_back(LogFileName(file_num), wal_dir);
|
||||
}
|
||||
}
|
||||
for (const auto& filename : state.manifest_delete_files) {
|
||||
@@ -517,12 +524,6 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
break;
|
||||
case kOptionsFile:
|
||||
keep = (number >= optsfile_num2);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::PurgeObsoleteFiles:CheckOptionsFiles:1",
|
||||
reinterpret_cast<void*>(&number));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::PurgeObsoleteFiles:CheckOptionsFiles:2",
|
||||
reinterpret_cast<void*>(&keep));
|
||||
break;
|
||||
case kCurrentFile:
|
||||
case kDBLockFile:
|
||||
@@ -547,8 +548,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
fname = BlobFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else {
|
||||
dir_to_sync =
|
||||
(type == kWalFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
dir_to_sync = (type == kWalFile) ? wal_dir : dbname_;
|
||||
fname = dir_to_sync +
|
||||
((!dir_to_sync.empty() && dir_to_sync.back() == '/') ||
|
||||
(!to_delete.empty() && to_delete.front() == '/')
|
||||
|
||||
+43
-28
@@ -111,16 +111,28 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
result.recycle_log_file_num = 0;
|
||||
}
|
||||
|
||||
if (result.wal_dir.empty()) {
|
||||
if (result.db_paths.size() == 0) {
|
||||
result.db_paths.emplace_back(dbname, std::numeric_limits<uint64_t>::max());
|
||||
} else if (result.wal_dir.empty()) {
|
||||
// Use dbname as default
|
||||
result.wal_dir = dbname;
|
||||
}
|
||||
if (result.wal_dir.back() == '/') {
|
||||
result.wal_dir = result.wal_dir.substr(0, result.wal_dir.size() - 1);
|
||||
if (!result.wal_dir.empty()) {
|
||||
// If there is a wal_dir already set, check to see if the wal_dir is the
|
||||
// same as the dbname AND the same as the db_path[0] (which must exist from
|
||||
// a few lines ago). If the wal_dir matches both of these values, then clear
|
||||
// the wal_dir value, which will make wal_dir == dbname. Most likely this
|
||||
// condition was the result of reading an old options file where we forced
|
||||
// wal_dir to be set (to dbname).
|
||||
auto npath = NormalizePath(dbname + "/");
|
||||
if (npath == NormalizePath(result.wal_dir + "/") &&
|
||||
npath == NormalizePath(result.db_paths[0].path + "/")) {
|
||||
result.wal_dir.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (result.db_paths.size() == 0) {
|
||||
result.db_paths.emplace_back(dbname, std::numeric_limits<uint64_t>::max());
|
||||
if (!result.wal_dir.empty() && result.wal_dir.back() == '/') {
|
||||
result.wal_dir = result.wal_dir.substr(0, result.wal_dir.size() - 1);
|
||||
}
|
||||
|
||||
if (result.use_direct_reads && result.compaction_readahead_size == 0) {
|
||||
@@ -141,7 +153,7 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ImmutableDBOptions immutable_db_options(result);
|
||||
if (!IsWalDirSameAsDBPath(&immutable_db_options)) {
|
||||
if (!immutable_db_options.IsWalDirSameAsDBPath()) {
|
||||
// Either the WAL dir and db_paths[0]/db_name are not the same, or we
|
||||
// cannot tell for sure. In either case, assume they're different and
|
||||
// explicitly cleanup the trash log files (bypass DeleteScheduler)
|
||||
@@ -149,13 +161,14 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
// DeleteScheduler::CleanupDirectory on the same dir later, it will be
|
||||
// safe
|
||||
std::vector<std::string> filenames;
|
||||
Status s = result.env->GetChildren(result.wal_dir, &filenames);
|
||||
auto wal_dir = immutable_db_options.GetWalDir();
|
||||
Status s = result.env->GetChildren(wal_dir, &filenames);
|
||||
s.PermitUncheckedError(); //**TODO: What to do on error?
|
||||
for (std::string& filename : filenames) {
|
||||
if (filename.find(".log.trash", filename.length() -
|
||||
std::string(".log.trash").length()) !=
|
||||
std::string::npos) {
|
||||
std::string trash_file = result.wal_dir + "/" + filename;
|
||||
std::string trash_file = wal_dir + "/" + filename;
|
||||
result.env->DeleteFile(trash_file).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
@@ -543,12 +556,12 @@ Status DBImpl::Recover(
|
||||
// Note that prev_log_number() is no longer used, but we pay
|
||||
// attention to it in case we are recovering a database
|
||||
// produced by an older version of rocksdb.
|
||||
auto wal_dir = immutable_db_options_.GetWalDir();
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &files_in_wal_dir);
|
||||
s = env_->GetChildren(wal_dir, &files_in_wal_dir);
|
||||
}
|
||||
if (s.IsNotFound()) {
|
||||
return Status::InvalidArgument("wal_dir not found",
|
||||
immutable_db_options_.wal_dir);
|
||||
return Status::InvalidArgument("wal_dir not found", wal_dir);
|
||||
} else if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -564,8 +577,7 @@ Status DBImpl::Recover(
|
||||
"existing log file: ",
|
||||
file);
|
||||
} else {
|
||||
wal_files[number] =
|
||||
LogFileName(immutable_db_options_.wal_dir, number);
|
||||
wal_files[number] = LogFileName(wal_dir, number);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,7 +642,7 @@ Status DBImpl::Recover(
|
||||
// Clear memtables if recovery failed
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
cfd->CreateNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
kMaxSequenceNumber, cfd->GetLogNumber());
|
||||
kMaxSequenceNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -645,7 +657,7 @@ Status DBImpl::Recover(
|
||||
if (s.ok()) {
|
||||
const std::string normalized_dbname = NormalizePath(dbname_);
|
||||
const std::string normalized_wal_dir =
|
||||
NormalizePath(immutable_db_options_.wal_dir);
|
||||
NormalizePath(immutable_db_options_.GetWalDir());
|
||||
if (immutable_db_options_.best_efforts_recovery) {
|
||||
filenames = std::move(files_in_dbname);
|
||||
} else if (normalized_dbname == normalized_wal_dir) {
|
||||
@@ -850,7 +862,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
// update the file number allocation counter in VersionSet.
|
||||
versions_->MarkFileNumberUsed(wal_number);
|
||||
// Open the log file
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, wal_number);
|
||||
std::string fname =
|
||||
LogFileName(immutable_db_options_.GetWalDir(), wal_number);
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Recovering log #%" PRIu64 " mode %d", wal_number,
|
||||
@@ -1066,7 +1079,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
flushed = true;
|
||||
|
||||
cfd->CreateNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
*next_sequence, cfd->GetLogNumber());
|
||||
*next_sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1204,8 +1217,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
flushed = true;
|
||||
|
||||
cfd->CreateNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
versions_->LastSequence(),
|
||||
cfd->GetLogNumber());
|
||||
versions_->LastSequence());
|
||||
}
|
||||
data_seen = true;
|
||||
}
|
||||
@@ -1276,7 +1288,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
Status DBImpl::GetLogSizeAndMaybeTruncate(uint64_t wal_number, bool truncate,
|
||||
LogFileNumberSize* log_ptr) {
|
||||
LogFileNumberSize log(wal_number);
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, wal_number);
|
||||
std::string fname =
|
||||
LogFileName(immutable_db_options_.GetWalDir(), wal_number);
|
||||
Status s;
|
||||
// This gets the appear size of the wals, not including preallocated space.
|
||||
s = env_->GetFileSize(fname, &log.size);
|
||||
@@ -1421,8 +1434,10 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
meta.fd.GetFileSize(), s.ToString().c_str());
|
||||
mutex_.Lock();
|
||||
|
||||
io_s.PermitUncheckedError(); // TODO(AR) is this correct, or should we
|
||||
// return io_s if not ok()?
|
||||
// TODO(AR) is this ok?
|
||||
if (!io_s.ok() && s.ok()) {
|
||||
s = io_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
@@ -1516,15 +1531,14 @@ IOStatus DBImpl::CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_);
|
||||
FileOptions opt_file_options =
|
||||
fs_->OptimizeForLogWrite(file_options_, db_options);
|
||||
std::string log_fname =
|
||||
LogFileName(immutable_db_options_.wal_dir, log_file_num);
|
||||
std::string wal_dir = immutable_db_options_.GetWalDir();
|
||||
std::string log_fname = LogFileName(wal_dir, log_file_num);
|
||||
|
||||
if (recycle_log_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"reusing log %" PRIu64 " from recycle list\n",
|
||||
recycle_log_number);
|
||||
std::string old_log_fname =
|
||||
LogFileName(immutable_db_options_.wal_dir, recycle_log_number);
|
||||
std::string old_log_fname = LogFileName(wal_dir, recycle_log_number);
|
||||
TEST_SYNC_POINT("DBImpl::CreateWAL:BeforeReuseWritableFile1");
|
||||
TEST_SYNC_POINT("DBImpl::CreateWAL:BeforeReuseWritableFile2");
|
||||
io_s = fs_->ReuseWritableFile(log_fname, old_log_fname, opt_file_options,
|
||||
@@ -1575,7 +1589,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
|
||||
DBImpl* impl = new DBImpl(db_options, dbname, seq_per_batch, batch_per_txn);
|
||||
s = impl->env_->CreateDirIfMissing(impl->immutable_db_options_.wal_dir);
|
||||
s = impl->env_->CreateDirIfMissing(impl->immutable_db_options_.GetWalDir());
|
||||
if (s.ok()) {
|
||||
std::vector<std::string> paths;
|
||||
for (auto& db_path : impl->immutable_db_options_.db_paths) {
|
||||
@@ -1607,7 +1621,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
return s;
|
||||
}
|
||||
|
||||
impl->wal_in_db_path_ = IsWalDirSameAsDBPath(&impl->immutable_db_options_);
|
||||
impl->wal_in_db_path_ = impl->immutable_db_options_.IsWalDirSameAsDBPath();
|
||||
|
||||
impl->mutex_.Lock();
|
||||
// Handles create_if_missing, error_if_exists
|
||||
@@ -1674,6 +1688,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
|
||||
impl->DeleteObsoleteFiles();
|
||||
s = impl->directories_.GetDbDir()->Fsync(IOOptions(), nullptr);
|
||||
TEST_SYNC_POINT("DBImpl::Open:AfterDeleteFilesAndSyncDir");
|
||||
}
|
||||
if (s.ok()) {
|
||||
// In WritePrepared there could be gap in sequence numbers. This breaks
|
||||
|
||||
@@ -41,6 +41,9 @@ Status DBImplSecondary::Recover(
|
||||
->Recover(column_families, &manifest_reader_, &manifest_reporter_,
|
||||
&manifest_reader_status_);
|
||||
if (!s.ok()) {
|
||||
if (manifest_reader_status_) {
|
||||
manifest_reader_status_->PermitUncheckedError();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
@@ -97,10 +100,10 @@ Status DBImplSecondary::FindNewLogNumbers(std::vector<uint64_t>* logs) {
|
||||
assert(logs != nullptr);
|
||||
std::vector<std::string> filenames;
|
||||
Status s;
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &filenames);
|
||||
s = env_->GetChildren(immutable_db_options_.GetWalDir(), &filenames);
|
||||
if (s.IsNotFound()) {
|
||||
return Status::InvalidArgument("Failed to open wal_dir",
|
||||
immutable_db_options_.wal_dir);
|
||||
immutable_db_options_.GetWalDir());
|
||||
} else if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -140,7 +143,8 @@ Status DBImplSecondary::MaybeInitLogReader(
|
||||
// initialize log reader from log_number
|
||||
// TODO: min_log_number_to_keep_2pc check needed?
|
||||
// Open the log file
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, log_number);
|
||||
std::string fname =
|
||||
LogFileName(immutable_db_options_.GetWalDir(), log_number);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Recovering log #%" PRIu64 " mode %d", log_number,
|
||||
static_cast<int>(immutable_db_options_.wal_recovery_mode));
|
||||
@@ -253,8 +257,8 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
curr_log_num != log_number)) {
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MemTable* new_mem = cfd->ConstructNewMemtable(
|
||||
mutable_cf_options, seq_of_batch, log_number);
|
||||
MemTable* new_mem =
|
||||
cfd->ConstructNewMemtable(mutable_cf_options, seq_of_batch);
|
||||
cfd->mem()->SetNextLogNumber(log_number);
|
||||
cfd->imm()->Add(cfd->mem(), &job_context->memtables_to_free);
|
||||
new_mem->Ref();
|
||||
@@ -627,7 +631,7 @@ Status DB::OpenAsSecondary(
|
||||
&impl->write_controller_, impl->io_tracer_));
|
||||
impl->column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
|
||||
impl->wal_in_db_path_ = IsWalDirSameAsDBPath(&impl->immutable_db_options_);
|
||||
impl->wal_in_db_path_ = impl->immutable_db_options_.IsWalDirSameAsDBPath();
|
||||
|
||||
impl->mutex_.Lock();
|
||||
s = impl->Recover(column_families, true, false, false);
|
||||
|
||||
@@ -220,7 +220,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
bool need_log_sync = write_options.sync;
|
||||
bool need_log_dir_sync = need_log_sync && !log_dir_synced_;
|
||||
if (!two_write_queues_ || !disable_memtable) {
|
||||
assert(!two_write_queues_ || !disable_memtable);
|
||||
{
|
||||
// With concurrent writes we do preprocess only in the write thread that
|
||||
// also does write to memtable to avoid sync issue on shared data structure
|
||||
// with the other thread
|
||||
@@ -1651,12 +1652,7 @@ Status DBImpl::TrimMemtableHistory(WriteContext* context) {
|
||||
for (auto& cfd : cfds) {
|
||||
autovector<MemTable*> to_delete;
|
||||
bool trimmed = cfd->imm()->TrimHistory(
|
||||
&to_delete, cfd->mem()->ApproximateMemoryUsage());
|
||||
if (!to_delete.empty()) {
|
||||
for (auto m : to_delete) {
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
&context->memtables_to_free_, cfd->mem()->ApproximateMemoryUsage());
|
||||
if (trimmed) {
|
||||
context->superversion_context.NewSuperVersion();
|
||||
assert(context->superversion_context.new_superversion.get() != nullptr);
|
||||
@@ -1805,8 +1801,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
}
|
||||
if (s.ok()) {
|
||||
SequenceNumber seq = versions_->LastSequence();
|
||||
new_mem =
|
||||
cfd->ConstructNewMemtable(mutable_cf_options, seq, new_log_number);
|
||||
new_mem = cfd->ConstructNewMemtable(mutable_cf_options, seq);
|
||||
context->superversion_context.NewSuperVersion();
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
|
||||
@@ -109,31 +109,30 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
}
|
||||
|
||||
// Get wal file in wal_dir
|
||||
if (dbname.compare(options.wal_dir) != 0) {
|
||||
if (!env->GetChildren(options.wal_dir, &files).ok()) {
|
||||
Error(options.info_log,
|
||||
"Error when reading %s dir\n",
|
||||
options.wal_dir.c_str());
|
||||
const auto& wal_dir = options.GetWalDir(dbname);
|
||||
if (!options.IsWalDirSameAsDBPath(dbname)) {
|
||||
if (!env->GetChildren(wal_dir, &files).ok()) {
|
||||
Error(options.info_log, "Error when reading %s dir\n", wal_dir.c_str());
|
||||
return;
|
||||
}
|
||||
wal_info.clear();
|
||||
for (const std::string& file : files) {
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
if (type == kWalFile) {
|
||||
if (env->GetFileSize(options.wal_dir + "/" + file, &file_size).ok()) {
|
||||
if (env->GetFileSize(wal_dir + "/" + file, &file_size).ok()) {
|
||||
wal_info.append(file)
|
||||
.append(" size: ")
|
||||
.append(std::to_string(file_size))
|
||||
.append(" ; ");
|
||||
} else {
|
||||
Error(options.info_log, "Error when reading LOG file %s/%s\n",
|
||||
options.wal_dir.c_str(), file.c_str());
|
||||
wal_dir.c_str(), file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Header(options.info_log, "Write Ahead Log file in %s: %s\n",
|
||||
options.wal_dir.c_str(), wal_info.c_str());
|
||||
Header(options.info_log, "Write Ahead Log file in %s: %s\n", wal_dir.c_str(),
|
||||
wal_info.c_str());
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBTestInPlaceUpdate : public DBTestBase {
|
||||
public:
|
||||
DBTestInPlaceUpdate()
|
||||
: DBTestBase("/db_inplace_update_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_inplace_update_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBTestInPlaceUpdate, InPlaceUpdate) {
|
||||
|
||||
@@ -15,8 +15,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBIOFailureTest : public DBTestBase {
|
||||
public:
|
||||
DBIOFailureTest()
|
||||
: DBTestBase("/db_io_failure_test", /*env_do_fsync=*/true) {}
|
||||
DBIOFailureTest() : DBTestBase("db_io_failure_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
@@ -35,7 +35,7 @@ class DummyReadCallback : public ReadCallback {
|
||||
class DBIteratorTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBIteratorTest() : DBTestBase("/db_iterator_test", /*env_do_fsync=*/true) {}
|
||||
DBIteratorTest() : DBTestBase("db_iterator_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
Iterator* NewIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family = nullptr) {
|
||||
|
||||
@@ -29,7 +29,7 @@ class DbKvChecksumTest
|
||||
public ::testing::WithParamInterface<std::tuple<WriteBatchOpType, char>> {
|
||||
public:
|
||||
DbKvChecksumTest()
|
||||
: DBTestBase("/db_kv_checksum_test", /*env_do_fsync=*/false) {
|
||||
: DBTestBase("db_kv_checksum_test", /*env_do_fsync=*/false) {
|
||||
op_type_ = std::get<0>(GetParam());
|
||||
corrupt_byte_addend_ = std::get<1>(GetParam());
|
||||
}
|
||||
|
||||
+20
-13
@@ -21,7 +21,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBTestXactLogIterator : public DBTestBase {
|
||||
public:
|
||||
DBTestXactLogIterator()
|
||||
: DBTestBase("/db_log_iter_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_log_iter_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::unique_ptr<TransactionLogIterator> OpenTransactionLogIter(
|
||||
const SequenceNumber seq) {
|
||||
@@ -185,31 +185,38 @@ TEST_F(DBTestXactLogIterator, TransactionLogIteratorCorruptedLog) {
|
||||
do {
|
||||
Options options = OptionsForLogIterTest();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
ASSERT_OK(Put("key" + ToString(i), DummyString(10)));
|
||||
}
|
||||
ASSERT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(dbfull()->FlushWAL(false));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->FlushWAL(false));
|
||||
|
||||
// Corrupt this log to create a gap
|
||||
ROCKSDB_NAMESPACE::VectorLogPtr wal_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
|
||||
VectorLogPtr wal_files;
|
||||
ASSERT_OK(db_->GetSortedWalFiles(wal_files));
|
||||
ASSERT_FALSE(wal_files.empty());
|
||||
|
||||
const auto logfile_path = dbname_ + "/" + wal_files.front()->PathName();
|
||||
if (mem_env_) {
|
||||
mem_env_->Truncate(logfile_path, wal_files.front()->SizeFileBytes() / 2);
|
||||
} else {
|
||||
ASSERT_EQ(0, truncate(logfile_path.c_str(),
|
||||
wal_files.front()->SizeFileBytes() / 2));
|
||||
}
|
||||
ASSERT_OK(test::TruncateFile(env_, logfile_path,
|
||||
wal_files.front()->SizeFileBytes() / 2));
|
||||
|
||||
ASSERT_OK(db_->EnableFileDeletions());
|
||||
|
||||
// Insert a new entry to a new log file
|
||||
ASSERT_OK(Put("key1025", DummyString(10)));
|
||||
ASSERT_OK(dbfull()->FlushWAL(false));
|
||||
ASSERT_OK(db_->FlushWAL(false));
|
||||
|
||||
// Try to read from the beginning. Should stop before the gap and read less
|
||||
// than 1025 entries
|
||||
auto iter = OpenTransactionLogIter(0);
|
||||
int count;
|
||||
int count = 0;
|
||||
SequenceNumber last_sequence_read = ReadRecords(iter, count, false);
|
||||
ASSERT_LT(last_sequence_read, 1025U);
|
||||
|
||||
// Try to read past the gap, should be able to seek to key1025
|
||||
auto iter2 = OpenTransactionLogIter(last_sequence_read + 1);
|
||||
ExpectRecords(1, iter2);
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBMemTableTest : public DBTestBase {
|
||||
public:
|
||||
DBMemTableTest() : DBTestBase("/db_memtable_test", /*env_do_fsync=*/true) {}
|
||||
DBMemTableTest() : DBTestBase("db_memtable_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
class MockMemTableRep : public MemTableRep {
|
||||
|
||||
@@ -44,7 +44,7 @@ class LimitedStringAppendMergeOp : public StringAppendTESTOperator {
|
||||
class DBMergeOperandTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperandTest()
|
||||
: DBTestBase("/db_merge_operand_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_merge_operand_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
|
||||
@@ -37,7 +37,7 @@ class TestReadCallback : public ReadCallback {
|
||||
class DBMergeOperatorTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperatorTest()
|
||||
: DBTestBase("/db_merge_operator_test", /*env_do_fsync=*/false) {}
|
||||
: DBTestBase("db_merge_operator_test", /*env_do_fsync=*/false) {}
|
||||
|
||||
std::string GetWithReadCallback(SnapshotChecker* snapshot_checker,
|
||||
const Slice& key,
|
||||
|
||||
+61
-1
@@ -27,7 +27,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBOptionsTest : public DBTestBase {
|
||||
public:
|
||||
DBOptionsTest() : DBTestBase("/db_options_test", /*env_do_fsync=*/true) {}
|
||||
DBOptionsTest() : DBTestBase("db_options_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::unordered_map<std::string, std::string> GetMutableDBOptionsMap(
|
||||
@@ -98,6 +98,66 @@ TEST_F(DBOptionsTest, ImmutableTrackAndVerifyWalsInManifest) {
|
||||
// RocksDB lite don't support dynamic options.
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBOptionsTest, AvoidUpdatingOptions) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.max_background_jobs = 4;
|
||||
options.delayed_write_rate = 1024;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
bool is_changed_stats = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::WriteOptionsFile:PersistOptions", [&](void* /*arg*/) {
|
||||
ASSERT_FALSE(is_changed_stats); // should only save options file once
|
||||
is_changed_stats = true;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// helper function to check the status and reset after each check
|
||||
auto is_changed = [&] {
|
||||
bool ret = is_changed_stats;
|
||||
is_changed_stats = false;
|
||||
return ret;
|
||||
};
|
||||
|
||||
// without changing the value, but it's sanitized to a different value
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"bytes_per_sync", "0"}}));
|
||||
ASSERT_TRUE(is_changed());
|
||||
|
||||
// without changing the value
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"max_background_jobs", "4"}}));
|
||||
ASSERT_FALSE(is_changed());
|
||||
|
||||
// changing the value
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"bytes_per_sync", "123"}}));
|
||||
ASSERT_TRUE(is_changed());
|
||||
|
||||
// update again
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"bytes_per_sync", "123"}}));
|
||||
ASSERT_FALSE(is_changed());
|
||||
|
||||
// without changing a default value
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"strict_bytes_per_sync", "false"}}));
|
||||
ASSERT_FALSE(is_changed());
|
||||
|
||||
// now change
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"strict_bytes_per_sync", "true"}}));
|
||||
ASSERT_TRUE(is_changed());
|
||||
|
||||
// multiple values without change
|
||||
ASSERT_OK(dbfull()->SetDBOptions(
|
||||
{{"max_total_wal_size", "0"}, {"stats_dump_period_sec", "600"}}));
|
||||
ASSERT_FALSE(is_changed());
|
||||
|
||||
// multiple values with change
|
||||
ASSERT_OK(dbfull()->SetDBOptions(
|
||||
{{"max_open_files", "100"}, {"stats_dump_period_sec", "600"}}));
|
||||
ASSERT_TRUE(is_changed());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, GetLatestDBOptions) {
|
||||
// GetOptions should be able to get latest option changed by SetOptions.
|
||||
Options options;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBPropertiesTest : public DBTestBase {
|
||||
public:
|
||||
DBPropertiesTest()
|
||||
: DBTestBase("/db_properties_test", /*env_do_fsync=*/false) {}
|
||||
: DBTestBase("db_properties_test", /*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
+11
-1
@@ -14,7 +14,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBRangeDelTest : public DBTestBase {
|
||||
public:
|
||||
DBRangeDelTest() : DBTestBase("/db_range_del_test", /*env_do_fsync=*/false) {}
|
||||
DBRangeDelTest() : DBTestBase("db_range_del_test", /*env_do_fsync=*/false) {}
|
||||
|
||||
std::string GetNumericStr(int key) {
|
||||
uint64_t uint64_key = static_cast<uint64_t>(key);
|
||||
@@ -660,6 +660,16 @@ TEST_F(DBRangeDelTest, TableEvictedDuringScan) {
|
||||
ASSERT_EQ(kNum, expected);
|
||||
delete iter;
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
// Also test proper cache handling in GetRangeTombstoneIterator,
|
||||
// via TablesRangeTombstoneSummary. (This once triggered memory leak
|
||||
// report with ASAN.)
|
||||
opts.max_open_files = 1;
|
||||
Reopen(opts);
|
||||
|
||||
std::string str;
|
||||
ASSERT_OK(dbfull()->TablesRangeTombstoneSummary(db_->DefaultColumnFamily(),
|
||||
100, &str));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetCoveredKeyFromMutableMemtable) {
|
||||
|
||||
+18
-4
@@ -20,7 +20,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBSecondaryTest : public DBTestBase {
|
||||
public:
|
||||
DBSecondaryTest()
|
||||
: DBTestBase("/db_secondary_test", /*env_do_fsync=*/true),
|
||||
: DBTestBase("db_secondary_test", /*env_do_fsync=*/true),
|
||||
secondary_path_(),
|
||||
handles_secondary_(),
|
||||
db_secondary_(nullptr) {
|
||||
@@ -115,6 +115,18 @@ void DBSecondaryTest::CheckFileTypeCounts(const std::string& dir,
|
||||
ASSERT_EQ(expected_manifest, manifest_cnt);
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, NonExistingDb) {
|
||||
Destroy(last_options_);
|
||||
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = env_;
|
||||
options.max_open_files = -1;
|
||||
const std::string dbname = "/doesnt/exist";
|
||||
Status s =
|
||||
DB::OpenAsSecondary(options, dbname, secondary_path_, &db_secondary_);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, ReopenAsSecondary) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
@@ -599,17 +611,19 @@ TEST_F(DBSecondaryTest, SwitchToNewManifestDuringOpen) {
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ReactiveVersionSet::MaybeSwitchManifest:AfterGetCurrentManifestPath:0",
|
||||
"VersionSet::ProcessManifestWrites:BeforeNewManifest"},
|
||||
{"VersionSet::ProcessManifestWrites:AfterNewManifest",
|
||||
{"DBImpl::Open:AfterDeleteFilesAndSyncDir",
|
||||
"ReactiveVersionSet::MaybeSwitchManifest:AfterGetCurrentManifestPath:"
|
||||
"1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Make sure db calls RecoverLogFiles so as to trigger a manifest write,
|
||||
// which causes the db to switch to a new MANIFEST upon start.
|
||||
port::Thread ro_db_thread([&]() {
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
options1.max_open_files = -1;
|
||||
Status s = TryOpenSecondary(options1);
|
||||
ASSERT_TRUE(s.IsTryAgain());
|
||||
|
||||
// Try again
|
||||
OpenSecondary(options1);
|
||||
CloseSecondary();
|
||||
});
|
||||
|
||||
+92
-2
@@ -18,7 +18,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBSSTTest : public DBTestBase {
|
||||
public:
|
||||
DBSSTTest() : DBTestBase("/db_sst_test", /*env_do_fsync=*/true) {}
|
||||
DBSSTTest() : DBTestBase("db_sst_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -757,7 +757,7 @@ class DBWALTestWithParam
|
||||
public testing::WithParamInterface<std::tuple<std::string, bool>> {
|
||||
public:
|
||||
explicit DBWALTestWithParam()
|
||||
: DBTestBase("/db_wal_test_with_params", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("db_wal_test_with_params", /*env_do_fsync=*/true) {
|
||||
wal_dir_ = std::get<0>(GetParam());
|
||||
wal_dir_same_as_dbname_ = std::get<1>(GetParam());
|
||||
}
|
||||
@@ -1569,6 +1569,96 @@ TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
ASSERT_EQ(total_sst_files_size, 0);
|
||||
}
|
||||
|
||||
// This test if blob files are recorded by SST File Manager when Compaction job
|
||||
// creates/delete them and in case of AtomicFlush.
|
||||
TEST_F(DBSSTTest, DBWithSFMForBlobFilesAtomicFlush) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
Options options = CurrentOptions();
|
||||
options.sst_file_manager = sst_file_manager;
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_garbage_collection = true;
|
||||
options.blob_garbage_collection_age_cutoff = 0.5;
|
||||
options.atomic_flush = true;
|
||||
|
||||
int files_added = 0;
|
||||
int files_deleted = 0;
|
||||
int files_scheduled_to_delete = 0;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnAddFile", [&](void* arg) {
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (EndsWith(*file_path, ".blob")) {
|
||||
files_added++;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::OnDeleteFile", [&](void* arg) {
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (EndsWith(*file_path, ".blob")) {
|
||||
files_deleted++;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SstFileManagerImpl::ScheduleFileDeletion", [&](void* arg) {
|
||||
assert(arg);
|
||||
const std::string* const file_path =
|
||||
static_cast<const std::string*>(arg);
|
||||
if (EndsWith(*file_path, ".blob")) {
|
||||
++files_scheduled_to_delete;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
|
||||
ASSERT_OK(Put("key_1", "value_1"));
|
||||
ASSERT_OK(Put("key_2", "value_2"));
|
||||
ASSERT_OK(Put("key_3", "value_3"));
|
||||
ASSERT_OK(Put("key_4", "value_4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Overwrite will create the garbage data.
|
||||
ASSERT_OK(Put("key_3", "new_value_3"));
|
||||
ASSERT_OK(Put("key_4", "new_value_4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("Key5", "blob_value5"));
|
||||
ASSERT_OK(Put("Key6", "blob_value6"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(files_added, 3);
|
||||
ASSERT_EQ(files_deleted, 0);
|
||||
ASSERT_EQ(files_scheduled_to_delete, 0);
|
||||
files_added = 0;
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
// Compaction job will create a new file and delete the older files.
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
|
||||
ASSERT_EQ(files_added, 1);
|
||||
ASSERT_EQ(files_deleted, 1);
|
||||
ASSERT_EQ(files_scheduled_to_delete, 1);
|
||||
|
||||
Close();
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
sfm->WaitForEmptyTrash();
|
||||
ASSERT_EQ(files_deleted, 4);
|
||||
ASSERT_EQ(files_scheduled_to_delete, 4);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBStatisticsTest : public DBTestBase {
|
||||
public:
|
||||
DBStatisticsTest()
|
||||
: DBTestBase("/db_statistics_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_statistics_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBStatisticsTest, CompressionStatsTest) {
|
||||
|
||||
@@ -52,7 +52,7 @@ class DBTablePropertiesTest : public DBTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBTablePropertiesTest()
|
||||
: DBTestBase("/db_table_properties_test", /*env_do_fsync=*/false) {}
|
||||
: DBTestBase("db_table_properties_test", /*env_do_fsync=*/false) {}
|
||||
TablePropertiesCollection TestGetPropertiesOfTablesInRange(
|
||||
std::vector<Range> ranges, std::size_t* num_properties = nullptr,
|
||||
std::size_t* num_files = nullptr);
|
||||
@@ -281,7 +281,7 @@ class DBTableHostnamePropertyTest
|
||||
public ::testing::WithParamInterface<std::tuple<int, std::string>> {
|
||||
public:
|
||||
DBTableHostnamePropertyTest()
|
||||
: DBTestBase("/db_table_hostname_property_test",
|
||||
: DBTestBase("db_table_hostname_property_test",
|
||||
/*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBTestTailingIterator : public DBTestBase {
|
||||
public:
|
||||
DBTestTailingIterator()
|
||||
: DBTestBase("/db_tailing_iterator_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("db_tailing_iterator_test", /*env_do_fsync=*/true) {}
|
||||
};
|
||||
|
||||
TEST_F(DBTestTailingIterator, TailingIteratorSingle) {
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// If fsync needs to be covered in a test, put it in other places.
|
||||
class DBTest : public DBTestBase {
|
||||
public:
|
||||
DBTest() : DBTestBase("/db_test", /*env_do_fsync=*/false) {}
|
||||
DBTest() : DBTestBase("db_test", /*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
class DBTestWithParam
|
||||
|
||||
+974
-202
File diff suppressed because it is too large
Load Diff
+18
-1
@@ -403,7 +403,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
Status Sync() override {
|
||||
++env_->sync_counter_;
|
||||
if (env_->corrupt_in_sync_) {
|
||||
Append(std::string(33000, ' '));
|
||||
EXPECT_OK(Append(std::string(33000, ' ')));
|
||||
return Status::IOError("Ingested Sync Failure");
|
||||
}
|
||||
if (env_->skip_fsync_) {
|
||||
@@ -675,6 +675,14 @@ class SpecialEnv : public EnvWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
Status RenameFile(const std::string& src, const std::string& dest) override {
|
||||
rename_count_.fetch_add(1);
|
||||
if (rename_error_.load(std::memory_order_acquire)) {
|
||||
return Status::NotSupported("Simulated `RenameFile()` error.");
|
||||
}
|
||||
return target()->RenameFile(src, dest);
|
||||
}
|
||||
|
||||
// Something to return when mocking current time
|
||||
const int64_t maybe_starting_time_;
|
||||
|
||||
@@ -702,6 +710,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Force write to log files to fail while this pointer is non-nullptr
|
||||
std::atomic<bool> log_write_error_;
|
||||
|
||||
// Force `RenameFile()` to fail while this pointer is non-nullptr
|
||||
std::atomic<bool> rename_error_{false};
|
||||
|
||||
// Slow down every log write, in micro-seconds.
|
||||
std::atomic<int> log_write_slowdown_;
|
||||
|
||||
@@ -745,6 +756,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
std::atomic<int> delete_count_;
|
||||
|
||||
std::atomic<int> rename_count_{0};
|
||||
|
||||
std::atomic<bool> is_wal_sync_thread_safe_{true};
|
||||
|
||||
std::atomic<size_t> compaction_readahead_size_{};
|
||||
@@ -764,6 +777,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
class OnFileDeletionListener : public EventListener {
|
||||
public:
|
||||
OnFileDeletionListener() : matched_count_(0), expected_file_name_("") {}
|
||||
const char* Name() const override { return kClassName(); }
|
||||
static const char* kClassName() { return "OnFileDeletionListener"; }
|
||||
|
||||
void SetExpectedFileName(const std::string file_name) {
|
||||
expected_file_name_ = file_name;
|
||||
@@ -788,6 +803,8 @@ class OnFileDeletionListener : public EventListener {
|
||||
|
||||
class FlushCounterListener : public EventListener {
|
||||
public:
|
||||
const char* Name() const override { return kClassName(); }
|
||||
static const char* kClassName() { return "FlushCounterListener"; }
|
||||
std::atomic<int> count{0};
|
||||
std::atomic<FlushReason> expected_flush_reason{FlushReason::kOthers};
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class DBTestUniversalCompaction : public DBTestUniversalCompactionBase {
|
||||
class DBTestUniversalCompaction2 : public DBTestBase {
|
||||
public:
|
||||
DBTestUniversalCompaction2()
|
||||
: DBTestBase("/db_universal_compaction_test2", /*env_do_fsync=*/false) {}
|
||||
: DBTestBase("db_universal_compaction_test2", /*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ class EnrichedSpecialEnv : public SpecialEnv {
|
||||
class DBWALTestWithEnrichedEnv : public DBTestBase {
|
||||
public:
|
||||
DBWALTestWithEnrichedEnv()
|
||||
: DBTestBase("/db_wal_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("db_wal_test", /*env_do_fsync=*/true) {
|
||||
enriched_env_ = new EnrichedSpecialEnv(env_->target());
|
||||
auto options = CurrentOptions();
|
||||
options.env = enriched_env_;
|
||||
|
||||
@@ -31,7 +31,7 @@ std::string Timestamp(uint64_t ts) {
|
||||
class TimestampCompatibleCompactionTest : public DBTestBase {
|
||||
public:
|
||||
TimestampCompatibleCompactionTest()
|
||||
: DBTestBase("/ts_compatible_compaction_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("ts_compatible_compaction_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::string Get(const std::string& key, uint64_t ts) {
|
||||
ReadOptions read_opts;
|
||||
|
||||
@@ -17,7 +17,7 @@ class DBWriteBufferManagerTest : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBWriteBufferManagerTest()
|
||||
: DBTestBase("/db_write_buffer_manager_test", /*env_do_fsync=*/false) {}
|
||||
: DBTestBase("db_write_buffer_manager_test", /*env_do_fsync=*/false) {}
|
||||
bool cost_cache_;
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// Test variations of WriteImpl.
|
||||
class DBWriteTest : public DBTestBase, public testing::WithParamInterface<int> {
|
||||
public:
|
||||
DBWriteTest() : DBTestBase("/db_write_test", /*env_do_fsync=*/true) {}
|
||||
DBWriteTest() : DBTestBase("db_write_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
Options GetOptions() { return DBTestBase::GetOptions(GetParam()); }
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class DeleteFileTest : public DBTestBase {
|
||||
const std::string wal_dir_;
|
||||
|
||||
DeleteFileTest()
|
||||
: DBTestBase("/deletefile_test", /*env_do_fsync=*/true),
|
||||
: DBTestBase("deletefile_test", /*env_do_fsync=*/true),
|
||||
numlevels_(7),
|
||||
wal_dir_(dbname_ + "/wal_files") {}
|
||||
|
||||
|
||||
+5
-2
@@ -327,7 +327,8 @@ const Status& ErrorHandler::SetBGError(const Status& bg_err,
|
||||
}
|
||||
|
||||
// Allow some error specific overrides
|
||||
if (new_bg_err == Status::NoSpace()) {
|
||||
if (new_bg_err.subcode() == IOStatus::SubCode::kNoSpace ||
|
||||
new_bg_err.subcode() == IOStatus::SubCode::kSpaceLimit) {
|
||||
new_bg_err = OverrideNoSpaceError(new_bg_err, &auto_recovery);
|
||||
}
|
||||
|
||||
@@ -349,7 +350,8 @@ const Status& ErrorHandler::SetBGError(const Status& bg_err,
|
||||
recovery_in_prog_ = true;
|
||||
|
||||
// Kick-off error specific recovery
|
||||
if (bg_error_ == Status::NoSpace()) {
|
||||
if (new_bg_err.subcode() == IOStatus::SubCode::kNoSpace ||
|
||||
new_bg_err.subcode() == IOStatus::SubCode::kSpaceLimit) {
|
||||
RecoverFromNoSpace();
|
||||
}
|
||||
}
|
||||
@@ -392,6 +394,7 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
if (BackgroundErrorReason::kManifestWrite == reason ||
|
||||
BackgroundErrorReason::kManifestWriteNoWAL == reason) {
|
||||
// Always returns ok
|
||||
ROCKS_LOG_INFO(db_options_.info_log, "Disabling File Deletions");
|
||||
db_->DisableFileDeletionsWithLock().PermitUncheckedError();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class DBErrorHandlingFSTest : public DBTestBase {
|
||||
public:
|
||||
DBErrorHandlingFSTest()
|
||||
: DBTestBase("/db_error_handling_fs_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("db_error_handling_fs_test", /*env_do_fsync=*/true) {
|
||||
fault_fs_.reset(new FaultInjectionTestFS(env_->GetFileSystem()));
|
||||
fault_env_.reset(new CompositeEnvWrapper(env_, fault_fs_));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,18 @@
|
||||
|
||||
#include "db/event_helpers.h"
|
||||
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/utilities/customizable_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status EventListener::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& id,
|
||||
std::shared_ptr<EventListener>* result) {
|
||||
return LoadSharedObject<EventListener>(config_options, id, nullptr, result);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
namespace {
|
||||
template <class T>
|
||||
|
||||
@@ -22,7 +22,7 @@ class ExternalSSTFileBasicTest
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
ExternalSSTFileBasicTest()
|
||||
: DBTestBase("/external_sst_file_basic_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("external_sst_file_basic_test", /*env_do_fsync=*/true) {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
fault_injection_test_env_.reset(new FaultInjectionTestEnv(env_));
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
|
||||
@@ -45,7 +45,7 @@ class ExternSSTFileLinkFailFallbackTest
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
ExternSSTFileLinkFailFallbackTest()
|
||||
: DBTestBase("/external_sst_file_test", /*env_do_fsync=*/true),
|
||||
: DBTestBase("external_sst_file_test", /*env_do_fsync=*/true),
|
||||
test_env_(new ExternalSSTTestEnv(env_, true)) {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
EXPECT_EQ(DestroyDir(env_, sst_files_dir_), Status::OK());
|
||||
@@ -74,7 +74,7 @@ class ExternalSSTFileTest
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
public:
|
||||
ExternalSSTFileTest()
|
||||
: DBTestBase("/external_sst_file_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("external_sst_file_test", /*env_do_fsync=*/true) {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
+236
-32
@@ -196,8 +196,8 @@ void FlushJob::PickMemTable() {
|
||||
base_->Ref(); // it is likely that we do not need this reference
|
||||
}
|
||||
|
||||
Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
FileMetaData* file_meta) {
|
||||
Status FlushJob::Run(LogsWithPrepTracker* prep_tracker, FileMetaData* file_meta,
|
||||
bool* switched_to_mempurge) {
|
||||
TEST_SYNC_POINT("FlushJob::Start");
|
||||
db_mutex_->AssertHeld();
|
||||
assert(pick_memtable_called);
|
||||
@@ -228,13 +228,13 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
prev_cpu_read_nanos = IOSTATS(cpu_read_nanos);
|
||||
}
|
||||
Status mempurge_s = Status::NotFound("No MemPurge.");
|
||||
if (db_options_.experimental_allow_mempurge &&
|
||||
if ((db_options_.experimental_mempurge_threshold > 0.0) &&
|
||||
(cfd_->GetFlushReason() == FlushReason::kWriteBufferFull) &&
|
||||
(!mems_.empty())) {
|
||||
(!mems_.empty()) && MemPurgeDecider()) {
|
||||
mempurge_s = MemPurge();
|
||||
if (!mempurge_s.ok()) {
|
||||
// Mempurge is typically aborted when the new_mem output memtable
|
||||
// is filled at more than XX % capacity (currently: 60%).
|
||||
// Mempurge is typically aborted when the output
|
||||
// bytes cannot be contained onto a single output memtable.
|
||||
if (mempurge_s.IsAborted()) {
|
||||
ROCKS_LOG_INFO(db_options_.info_log, "Mempurge process aborted: %s\n",
|
||||
mempurge_s.ToString().c_str());
|
||||
@@ -244,6 +244,16 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "Mempurge process failed: %s\n",
|
||||
mempurge_s.ToString().c_str());
|
||||
}
|
||||
} else {
|
||||
if (switched_to_mempurge) {
|
||||
*switched_to_mempurge = true;
|
||||
} else {
|
||||
// The mempurge process was successful, but no switch_to_mempurge
|
||||
// pointer provided so no way to propagate the state of flush job.
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Mempurge process succeeded"
|
||||
"but no 'switched_to_mempurge' ptr provided.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
Status s;
|
||||
@@ -333,24 +343,21 @@ void FlushJob::Cancel() {
|
||||
base_->Unref();
|
||||
}
|
||||
|
||||
uint64_t FlushJob::ExtractEarliestLogFileNumber() {
|
||||
uint64_t earliest_logno = 0;
|
||||
for (MemTable* m : mems_) {
|
||||
uint64_t logno = m->GetEarliestLogFileNumber();
|
||||
if (logno > 0 && (earliest_logno == 0 || logno < earliest_logno)) {
|
||||
earliest_logno = logno;
|
||||
}
|
||||
}
|
||||
return earliest_logno;
|
||||
}
|
||||
|
||||
Status FlushJob::MemPurge() {
|
||||
Status s;
|
||||
db_mutex_->AssertHeld();
|
||||
db_mutex_->Unlock();
|
||||
assert(!mems_.empty());
|
||||
|
||||
// Measure purging time.
|
||||
const uint64_t start_micros = clock_->NowMicros();
|
||||
const uint64_t start_cpu_micros = clock_->CPUNanos() / 1000;
|
||||
|
||||
MemTable* new_mem = nullptr;
|
||||
// For performance/log investigation purposes:
|
||||
// look at how much useful payload we harvest in the new_mem.
|
||||
// This value is then printed to the DB log.
|
||||
double new_mem_capacity = 0.0;
|
||||
|
||||
// Create two iterators, one for the memtable data (contains
|
||||
// info from puts + deletes), and one for the memtable
|
||||
@@ -387,8 +394,6 @@ Status FlushJob::MemPurge() {
|
||||
NewMergingIterator(&(cfd_->internal_comparator()), memtables.data(),
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
|
||||
uint64_t earliest_logno = ExtractEarliestLogFileNumber();
|
||||
|
||||
auto* ioptions = cfd_->ioptions();
|
||||
|
||||
// Place iterator at the First (meaning most recent) key node.
|
||||
@@ -405,8 +410,8 @@ Status FlushJob::MemPurge() {
|
||||
// or at least range tombstones, copy over the info
|
||||
// to the new memtable.
|
||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||
// Arbitrary heuristic: maxSize is 60% cpacity.
|
||||
size_t maxSize = ((mutable_cf_options_.write_buffer_size + 6U) / 10U);
|
||||
// MaxSize is the size of a memtable.
|
||||
size_t maxSize = mutable_cf_options_.write_buffer_size;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter;
|
||||
if (ioptions->compaction_filter_factory != nullptr &&
|
||||
ioptions->compaction_filter_factory->ShouldFilterTableFileCreation(
|
||||
@@ -429,7 +434,7 @@ Status FlushJob::MemPurge() {
|
||||
|
||||
new_mem = new MemTable((cfd_->internal_comparator()), *(cfd_->ioptions()),
|
||||
mutable_cf_options_, cfd_->write_buffer_mgr(),
|
||||
earliest_seqno, cfd_->GetID(), earliest_logno);
|
||||
earliest_seqno, cfd_->GetID());
|
||||
assert(new_mem != nullptr);
|
||||
|
||||
Env* env = db_options_.env;
|
||||
@@ -493,6 +498,7 @@ Status FlushJob::MemPurge() {
|
||||
// and destroy new_mem.
|
||||
if (new_mem->ApproximateMemoryUsage() > maxSize) {
|
||||
s = Status::Aborted("Mempurge filled more than one memtable.");
|
||||
new_mem_capacity = 1.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -537,6 +543,7 @@ Status FlushJob::MemPurge() {
|
||||
// and destroy new_mem.
|
||||
if (new_mem->ApproximateMemoryUsage() > maxSize) {
|
||||
s = Status::Aborted(Slice("Mempurge filled more than one memtable."));
|
||||
new_mem_capacity = 1.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -551,19 +558,29 @@ Status FlushJob::MemPurge() {
|
||||
new_mem->SetFirstSequenceNumber(new_first_seqno);
|
||||
|
||||
// The new_mem is added to the list of immutable memtables
|
||||
// only if it filled at less than 60% capacity (arbitrary heuristic).
|
||||
if (new_mem->ApproximateMemoryUsage() < maxSize) {
|
||||
// only if it filled at less than 100% capacity and isn't flagged
|
||||
// as in need of being flushed.
|
||||
if (new_mem->ApproximateMemoryUsage() < maxSize &&
|
||||
!(new_mem->ShouldFlushNow())) {
|
||||
db_mutex_->Lock();
|
||||
cfd_->imm()->Add(new_mem,
|
||||
&job_context_->memtables_to_free,
|
||||
false /* -> trigger_flush=false:
|
||||
* adding this memtable
|
||||
* will not trigger a flush.
|
||||
*/);
|
||||
uint64_t new_mem_id = mems_[0]->GetID();
|
||||
|
||||
new_mem->SetID(new_mem_id);
|
||||
|
||||
// This addition will not trigger another flush, because
|
||||
// we do not call SchedulePendingFlush().
|
||||
cfd_->imm()->Add(new_mem, &job_context_->memtables_to_free);
|
||||
new_mem->Ref();
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Piggyback FlushJobInfo on the first flushed memtable.
|
||||
db_mutex_->AssertHeld();
|
||||
meta_.fd.file_size = 0;
|
||||
mems_[0]->SetFlushJobInfo(GetFlushJobInfo());
|
||||
#endif // !ROCKSDB_LITE
|
||||
db_mutex_->Unlock();
|
||||
} else {
|
||||
s = Status::Aborted(Slice("Mempurge filled more than one memtable."));
|
||||
new_mem_capacity = 1.0;
|
||||
if (new_mem) {
|
||||
job_context_->memtables_to_free.push_back(new_mem);
|
||||
}
|
||||
@@ -585,10 +602,188 @@ Status FlushJob::MemPurge() {
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::FlushJob:MemPurgeUnsuccessful");
|
||||
}
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Mempurge lasted %" PRIu64
|
||||
" microseconds, and %" PRIu64
|
||||
" cpu "
|
||||
"microseconds. Status is %s ok. Perc capacity: %f\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, micros,
|
||||
cpu_micros, s.ok() ? "" : "not", new_mem_capacity);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool FlushJob::MemPurgeDecider() {
|
||||
double threshold = db_options_.experimental_mempurge_threshold;
|
||||
// Never trigger mempurge if threshold is not a strictly positive value.
|
||||
if (!(threshold > 0.0)) {
|
||||
return false;
|
||||
}
|
||||
if (threshold > (1.0 * mems_.size())) {
|
||||
return true;
|
||||
}
|
||||
// Payload and useful_payload (in bytes).
|
||||
// The useful payload ratio of a given MemTable
|
||||
// is estimated to be useful_payload/payload.
|
||||
uint64_t payload = 0, useful_payload = 0, entry_size = 0;
|
||||
|
||||
// Local variables used repetitively inside the for-loop
|
||||
// when iterating over the sampled entries.
|
||||
Slice key_slice, value_slice;
|
||||
ParsedInternalKey res;
|
||||
SnapshotImpl min_snapshot;
|
||||
std::string vget;
|
||||
Status mget_s, parse_s;
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0, sqno = 0,
|
||||
min_seqno_snapshot = 0;
|
||||
bool get_res, can_be_useful_payload, not_in_next_mems;
|
||||
|
||||
// If estimated_useful_payload is > threshold,
|
||||
// then flush to storage, else MemPurge.
|
||||
double estimated_useful_payload = 0.0;
|
||||
// Cochran formula for determining sample size.
|
||||
// 95% confidence interval, 7% precision.
|
||||
// n0 = (1.96*1.96)*0.25/(0.07*0.07) = 196.0
|
||||
double n0 = 196.0;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
|
||||
// Iterate over each memtable of the set.
|
||||
for (auto mem_iter = std::begin(mems_); mem_iter != std::end(mems_);
|
||||
mem_iter++) {
|
||||
MemTable* mt = *mem_iter;
|
||||
|
||||
// Else sample from the table.
|
||||
uint64_t nentries = mt->num_entries();
|
||||
// Corrected Cochran formula for small populations
|
||||
// (converges to n0 for large populations).
|
||||
uint64_t target_sample_size =
|
||||
static_cast<uint64_t>(ceil(n0 / (1.0 + (n0 / nentries))));
|
||||
std::unordered_set<const char*> sentries = {};
|
||||
// Populate sample entries set.
|
||||
mt->UniqueRandomSample(target_sample_size, &sentries);
|
||||
|
||||
// Estimate the garbage ratio by comparing if
|
||||
// each sample corresponds to a valid entry.
|
||||
for (const char* ss : sentries) {
|
||||
key_slice = GetLengthPrefixedSlice(ss);
|
||||
parse_s = ParseInternalKey(key_slice, &res, true /*log_err_key*/);
|
||||
if (!parse_s.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Memtable Decider: ParseInternalKey did not parse "
|
||||
"key_slice %s successfully.",
|
||||
key_slice.data());
|
||||
}
|
||||
|
||||
// Size of the entry is "key size (+ value size if KV entry)"
|
||||
entry_size = key_slice.size();
|
||||
if (res.type == kTypeValue) {
|
||||
value_slice =
|
||||
GetLengthPrefixedSlice(key_slice.data() + key_slice.size());
|
||||
entry_size += value_slice.size();
|
||||
}
|
||||
|
||||
// Count entry bytes as payload.
|
||||
payload += entry_size;
|
||||
|
||||
LookupKey lkey(res.user_key, kMaxSequenceNumber);
|
||||
|
||||
// Paranoia: zero out these values just in case.
|
||||
max_covering_tombstone_seq = 0;
|
||||
sqno = 0;
|
||||
|
||||
// Pick the oldest existing snapshot that is more recent
|
||||
// than the sequence number of the sampled entry.
|
||||
min_seqno_snapshot = kMaxSequenceNumber;
|
||||
for (SequenceNumber seq_num : existing_snapshots_) {
|
||||
if (seq_num > res.sequence && seq_num < min_seqno_snapshot) {
|
||||
min_seqno_snapshot = seq_num;
|
||||
}
|
||||
}
|
||||
min_snapshot.number_ = min_seqno_snapshot;
|
||||
ro.snapshot =
|
||||
min_seqno_snapshot < kMaxSequenceNumber ? &min_snapshot : nullptr;
|
||||
|
||||
// Estimate if the sample entry is valid or not.
|
||||
get_res = mt->Get(lkey, &vget, nullptr, &mget_s, &merge_context,
|
||||
&max_covering_tombstone_seq, &sqno, ro);
|
||||
if (!get_res) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Memtable Get returned false when Get(sampled entry). "
|
||||
"Yet each sample entry should exist somewhere in the memtable, "
|
||||
"unrelated to whether it has been deleted or not.");
|
||||
}
|
||||
|
||||
// TODO(bjlemaire): evaluate typeMerge.
|
||||
// This is where the sampled entry is estimated to be
|
||||
// garbage or not. Note that this is a garbage *estimation*
|
||||
// because we do not include certain items such as
|
||||
// CompactionFitlers triggered at flush, or if the same delete
|
||||
// has been inserted twice or more in the memtable.
|
||||
|
||||
// Evaluate if the entry can be useful payload
|
||||
// Situation #1: entry is a KV entry, was found in the memtable mt
|
||||
// and the sequence numbers match.
|
||||
can_be_useful_payload = (res.type == kTypeValue) && get_res &&
|
||||
mget_s.ok() && (sqno == res.sequence);
|
||||
|
||||
// Situation #2: entry is a delete entry, was found in the memtable mt
|
||||
// (because gres==true) and no valid KV entry is found.
|
||||
// (note: duplicate delete entries are also taken into
|
||||
// account here, because the sequence number 'sqno'
|
||||
// in memtable->Get(&sqno) operation is set to be equal
|
||||
// to the most recent delete entry as well).
|
||||
can_be_useful_payload |=
|
||||
((res.type == kTypeDeletion) || (res.type == kTypeSingleDeletion)) &&
|
||||
mget_s.IsNotFound() && get_res && (sqno == res.sequence);
|
||||
|
||||
// If there is a chance that the entry is useful payload
|
||||
// Verify that the entry does not appear in the following memtables
|
||||
// (memtables with greater memtable ID/larger sequence numbers).
|
||||
if (can_be_useful_payload) {
|
||||
not_in_next_mems = true;
|
||||
for (auto next_mem_iter = mem_iter + 1;
|
||||
next_mem_iter != std::end(mems_); next_mem_iter++) {
|
||||
if ((*next_mem_iter)
|
||||
->Get(lkey, &vget, nullptr, &mget_s, &merge_context,
|
||||
&max_covering_tombstone_seq, &sqno, ro)) {
|
||||
not_in_next_mems = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (not_in_next_mems) {
|
||||
useful_payload += entry_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (payload > 0) {
|
||||
// We use the estimated useful payload ratio to
|
||||
// evaluate how many of the memtable bytes are useful bytes.
|
||||
estimated_useful_payload +=
|
||||
(mt->ApproximateMemoryUsage()) * (useful_payload * 1.0 / payload);
|
||||
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"Mempurge sampling - found garbage ratio from sampling: %f.\n",
|
||||
(payload - useful_payload) * 1.0 / payload);
|
||||
} else {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Mempurge sampling: null payload measured, and collected "
|
||||
"sample size is %zu\n.",
|
||||
sentries.size());
|
||||
}
|
||||
}
|
||||
// We convert the total number of useful payload bytes
|
||||
// into the proportion of memtable necessary to store all these bytes.
|
||||
// We compare this proportion with the threshold value.
|
||||
return ((estimated_useful_payload / mutable_cf_options_.write_buffer_size) <
|
||||
threshold);
|
||||
}
|
||||
|
||||
Status FlushJob::WriteLevel0Table() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
@@ -775,8 +970,16 @@ Status FlushJob::WriteLevel0Table() {
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
stats.micros = clock_->NowMicros() - start_micros;
|
||||
stats.cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUNanos() / 1000 - start_cpu_micros;
|
||||
stats.micros = micros;
|
||||
stats.cpu_micros = cpu_micros;
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Flush lasted %" PRIu64
|
||||
" microseconds, and %" PRIu64 " cpu microseconds.\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, micros,
|
||||
cpu_micros);
|
||||
|
||||
if (has_output) {
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
@@ -796,6 +999,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written + stats.bytes_written_blob);
|
||||
RecordFlushIOStats();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -83,7 +83,8 @@ class FlushJob {
|
||||
// Once PickMemTable() is called, either Run() or Cancel() has to be called.
|
||||
void PickMemTable();
|
||||
Status Run(LogsWithPrepTracker* prep_tracker = nullptr,
|
||||
FileMetaData* file_meta = nullptr);
|
||||
FileMetaData* file_meta = nullptr,
|
||||
bool* switched_to_mempurge = nullptr);
|
||||
void Cancel();
|
||||
const autovector<MemTable*>& GetMemTables() const { return mems_; }
|
||||
|
||||
@@ -117,13 +118,13 @@ class FlushJob {
|
||||
// of development. At the moment it is only compatible with the Get, Put,
|
||||
// Delete operations as well as Iterators and CompactionFilters.
|
||||
// For this early version, "MemPurge" is called by setting the
|
||||
// options.experimental_allow_mempurge flag as "true". When this is
|
||||
// options.experimental_mempurge_threshold value as >0.0. When this is
|
||||
// the case, ALL automatic flush operations (kWRiteBufferManagerFull) will
|
||||
// first go through the MemPurge process. herefore, we strongly
|
||||
// first go through the MemPurge process. Therefore, we strongly
|
||||
// recommend all users not to set this flag as true given that the MemPurge
|
||||
// process has not matured yet.
|
||||
Status MemPurge();
|
||||
uint64_t ExtractEarliestLogFileNumber();
|
||||
bool MemPurgeDecider();
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::unique_ptr<FlushJobInfo> GetFlushJobInfo() const;
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
@@ -418,12 +418,19 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
for (auto cfd : all_cfds) {
|
||||
mutable_cf_options_list.push_back(cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
|
||||
committed_flush_jobs_info;
|
||||
#ifndef ROCKSDB_LITE
|
||||
for (auto& job : flush_jobs) {
|
||||
committed_flush_jobs_info.push_back(job->GetCommittedFlushJobsInfo());
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
Status s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, all_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), nullptr /* prep_tracker */, &mutex_, file_meta_ptrs,
|
||||
&job_context.memtables_to_free, nullptr /* db_directory */,
|
||||
nullptr /* log_buffer */);
|
||||
committed_flush_jobs_info, &job_context.memtables_to_free,
|
||||
nullptr /* db_directory */, nullptr /* log_buffer */);
|
||||
ASSERT_OK(s);
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class ImportColumnFamilyTest : public DBTestBase {
|
||||
public:
|
||||
ImportColumnFamilyTest()
|
||||
: DBTestBase("/import_column_family_test", /*env_do_fsync=*/true) {
|
||||
: DBTestBase("import_column_family_test", /*env_do_fsync=*/true) {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
export_files_dir_ = test::PerThreadDBPath(env_, "export");
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
|
||||
@@ -262,6 +262,8 @@ 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 live_sst_files_size_at_temperature =
|
||||
"live-sst-files-size-at-temperature";
|
||||
static const std::string estimate_pending_comp_bytes =
|
||||
"estimate-pending-compaction-bytes";
|
||||
static const std::string aggregated_table_properties =
|
||||
@@ -369,6 +371,8 @@ const std::string DB::Properties::kBlockCachePinnedUsage =
|
||||
rocksdb_prefix + block_cache_pinned_usage;
|
||||
const std::string DB::Properties::kOptionsStatistics =
|
||||
rocksdb_prefix + options_statistics;
|
||||
const std::string DB::Properties::kLiveSstFilesSizeAtTemperature =
|
||||
rocksdb_prefix + live_sst_files_size_at_temperature;
|
||||
|
||||
const std::unordered_map<std::string, DBPropertyInfo>
|
||||
InternalStats::ppt_name_to_info = {
|
||||
@@ -482,6 +486,9 @@ const std::unordered_map<std::string, DBPropertyInfo>
|
||||
{DB::Properties::kLiveSstFilesSize,
|
||||
{false, nullptr, &InternalStats::HandleLiveSstFilesSize, nullptr,
|
||||
nullptr}},
|
||||
{DB::Properties::kLiveSstFilesSizeAtTemperature,
|
||||
{true, &InternalStats::HandleLiveSstFilesSizeAtTemperature, nullptr,
|
||||
nullptr, nullptr}},
|
||||
{DB::Properties::kEstimatePendingCompactionBytes,
|
||||
{false, nullptr, &InternalStats::HandleEstimatePendingCompactionBytes,
|
||||
nullptr, nullptr}},
|
||||
@@ -682,6 +689,28 @@ bool InternalStats::HandleBlockCacheEntryStatsMap(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleLiveSstFilesSizeAtTemperature(std::string* value,
|
||||
Slice suffix) {
|
||||
uint64_t temperature;
|
||||
bool ok = ConsumeDecimalNumber(&suffix, &temperature) && suffix.empty();
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t size = 0;
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
for (int level = 0; level < vstorage->num_levels(); level++) {
|
||||
for (const auto& file_meta : vstorage->LevelFiles(level)) {
|
||||
if (static_cast<uint8_t>(file_meta->temperature) == temperature) {
|
||||
size += file_meta->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*value = ToString(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
const DBPropertyInfo* GetPropertyInfo(const Slice& property) {
|
||||
std::string ppt_name = GetPropertyNameAndArg(property).first.ToString();
|
||||
auto ppt_info_iter = InternalStats::ppt_name_to_info.find(ppt_name);
|
||||
|
||||
@@ -675,6 +675,7 @@ class InternalStats {
|
||||
bool HandleBlockCacheEntryStats(std::string* value, Slice suffix);
|
||||
bool HandleBlockCacheEntryStatsMap(std::map<std::string, std::string>* values,
|
||||
Slice suffix);
|
||||
bool HandleLiveSstFilesSizeAtTemperature(std::string* value, Slice suffix);
|
||||
// Total number of background errors encountered. Every time a flush task
|
||||
// or compaction task fails, this counter is incremented. The failure can
|
||||
// be caused by any possible reason, including file system errors, out of
|
||||
|
||||
+31
-25
@@ -38,7 +38,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class EventListenerTest : public DBTestBase {
|
||||
public:
|
||||
EventListenerTest() : DBTestBase("/listener_test", /*env_do_fsync=*/true) {}
|
||||
EventListenerTest() : DBTestBase("listener_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
static std::string BlobStr(uint64_t blob_file_number, uint64_t offset,
|
||||
uint64_t size) {
|
||||
@@ -356,32 +356,38 @@ TEST_F(EventListenerTest, MultiCF) {
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
options.enable_thread_tracking = true;
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
TestFlushListener* listener = new TestFlushListener(options.env, this);
|
||||
options.listeners.emplace_back(listener);
|
||||
options.table_properties_collector_factories.push_back(
|
||||
std::make_shared<TestPropertiesCollectorFactory>());
|
||||
std::vector<std::string> cf_names = {
|
||||
"pikachu", "ilya", "muromec", "dobrynia",
|
||||
"nikitich", "alyosha", "popovich"};
|
||||
CreateAndReopenWithCF(cf_names, options);
|
||||
for (auto atomic_flush : {false, true}) {
|
||||
options.atomic_flush = atomic_flush;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
TestFlushListener* listener = new TestFlushListener(options.env, this);
|
||||
options.listeners.emplace_back(listener);
|
||||
options.table_properties_collector_factories.push_back(
|
||||
std::make_shared<TestPropertiesCollectorFactory>());
|
||||
std::vector<std::string> cf_names = {"pikachu", "ilya", "muromec",
|
||||
"dobrynia", "nikitich", "alyosha",
|
||||
"popovich"};
|
||||
CreateAndReopenWithCF(cf_names, options);
|
||||
|
||||
ASSERT_OK(Put(1, "pikachu", std::string(90000, 'p')));
|
||||
ASSERT_OK(Put(2, "ilya", std::string(90000, 'i')));
|
||||
ASSERT_OK(Put(3, "muromec", std::string(90000, 'm')));
|
||||
ASSERT_OK(Put(4, "dobrynia", std::string(90000, 'd')));
|
||||
ASSERT_OK(Put(5, "nikitich", std::string(90000, 'n')));
|
||||
ASSERT_OK(Put(6, "alyosha", std::string(90000, 'a')));
|
||||
ASSERT_OK(Put(7, "popovich", std::string(90000, 'p')));
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_EQ(listener->flushed_dbs_.size(), i);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
|
||||
}
|
||||
ASSERT_OK(Put(1, "pikachu", std::string(90000, 'p')));
|
||||
ASSERT_OK(Put(2, "ilya", std::string(90000, 'i')));
|
||||
ASSERT_OK(Put(3, "muromec", std::string(90000, 'm')));
|
||||
ASSERT_OK(Put(4, "dobrynia", std::string(90000, 'd')));
|
||||
ASSERT_OK(Put(5, "nikitich", std::string(90000, 'n')));
|
||||
ASSERT_OK(Put(6, "alyosha", std::string(90000, 'a')));
|
||||
ASSERT_OK(Put(7, "popovich", std::string(90000, 'p')));
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_EQ(listener->flushed_dbs_.size(), i);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
|
||||
}
|
||||
|
||||
// make sure callback functions are called in the right order
|
||||
for (size_t i = 0; i < cf_names.size(); i++) {
|
||||
ASSERT_EQ(listener->flushed_dbs_[i], db_);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_[i], cf_names[i]);
|
||||
// make sure callback functions are called in the right order
|
||||
for (size_t i = 0; i < cf_names.size(); i++) {
|
||||
ASSERT_EQ(listener->flushed_dbs_[i], db_);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_[i], cf_names[i]);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -67,8 +67,7 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
SequenceNumber latest_seq, uint32_t column_family_id,
|
||||
uint64_t current_logfile_number)
|
||||
SequenceNumber latest_seq, uint32_t column_family_id)
|
||||
: comparator_(cmp),
|
||||
moptions_(ioptions, mutable_cf_options),
|
||||
refs_(0),
|
||||
@@ -99,7 +98,6 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
earliest_seqno_(latest_seq),
|
||||
creation_seq_(latest_seq),
|
||||
mem_next_logfile_number_(0),
|
||||
mem_min_logfile_number_(current_logfile_number),
|
||||
min_prep_log_referenced_(0),
|
||||
locks_(moptions_.inplace_update_support
|
||||
? moptions_.inplace_update_num_locks
|
||||
|
||||
+25
-19
@@ -14,6 +14,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
@@ -106,8 +107,7 @@ class MemTable {
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
SequenceNumber earliest_seq, uint32_t column_family_id,
|
||||
uint64_t current_logfile_number = 0);
|
||||
SequenceNumber earliest_seq, uint32_t column_family_id);
|
||||
// No copying allowed
|
||||
MemTable(const MemTable&) = delete;
|
||||
MemTable& operator=(const MemTable&) = delete;
|
||||
@@ -146,6 +146,26 @@ class MemTable {
|
||||
return approximate_memory_usage_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Returns a vector of unique random memtable entries of size 'sample_size'.
|
||||
//
|
||||
// Note: the entries are stored in the unordered_set as length-prefixed keys,
|
||||
// hence their representation in the set as "const char*".
|
||||
// Note2: the size of the output set 'entries' is not enforced to be strictly
|
||||
// equal to 'target_sample_size'. Its final size might be slightly
|
||||
// greater or slightly less than 'target_sample_size'
|
||||
//
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable (unless this Memtable is immutable).
|
||||
// REQUIRES: SkipList memtable representation. This function is not
|
||||
// implemented for any other type of memtable representation (vectorrep,
|
||||
// hashskiplist,...).
|
||||
void UniqueRandomSample(const uint64_t& target_sample_size,
|
||||
std::unordered_set<const char*>* entries) {
|
||||
// TODO(bjlemaire): at the moment, only supported by skiplistrep.
|
||||
// Extend it to all other memtable representations.
|
||||
table_->UniqueRandomSample(num_entries(), target_sample_size, entries);
|
||||
}
|
||||
|
||||
// This method heuristically determines if the memtable should continue to
|
||||
// host more data.
|
||||
bool ShouldScheduleFlush() const {
|
||||
@@ -388,16 +408,6 @@ class MemTable {
|
||||
// operations on the same MemTable.
|
||||
void SetNextLogNumber(uint64_t num) { mem_next_logfile_number_ = num; }
|
||||
|
||||
// Set the earliest log file number that (possibly)
|
||||
// contains entries from this memtable.
|
||||
void SetEarliestLogFileNumber(uint64_t logno) {
|
||||
mem_min_logfile_number_ = logno;
|
||||
}
|
||||
|
||||
// Return the earliest log file number that (possibly)
|
||||
// contains entries from this memtable.
|
||||
uint64_t GetEarliestLogFileNumber() { return mem_min_logfile_number_; }
|
||||
|
||||
// if this memtable contains data from a committed
|
||||
// two phase transaction we must take note of the
|
||||
// log which contains that data so we can know
|
||||
@@ -482,6 +492,9 @@ class MemTable {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Returns a heuristic flush decision
|
||||
bool ShouldFlushNow();
|
||||
|
||||
private:
|
||||
enum FlushStateEnum { FLUSH_NOT_REQUESTED, FLUSH_REQUESTED, FLUSH_SCHEDULED };
|
||||
|
||||
@@ -528,10 +541,6 @@ class MemTable {
|
||||
// The log files earlier than this number can be deleted.
|
||||
uint64_t mem_next_logfile_number_;
|
||||
|
||||
// The earliest log containing entries inserted into
|
||||
// this memtable.
|
||||
uint64_t mem_min_logfile_number_;
|
||||
|
||||
// the earliest log containing a prepared section
|
||||
// which has been inserted into this memtable.
|
||||
std::atomic<uint64_t> min_prep_log_referenced_;
|
||||
@@ -573,9 +582,6 @@ class MemTable {
|
||||
std::unique_ptr<FlushJobInfo> flush_job_info_;
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Returns a heuristic flush decision
|
||||
bool ShouldFlushNow();
|
||||
|
||||
// Updates flush_state_ using ShouldFlushNow()
|
||||
void UpdateFlushState();
|
||||
|
||||
|
||||
+40
-20
@@ -5,10 +5,12 @@
|
||||
//
|
||||
#include "db/memtable_list.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
@@ -340,6 +342,14 @@ void MemTableList::PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
ThreadStatus::STAGE_PICK_MEMTABLES_TO_FLUSH);
|
||||
const auto& memlist = current_->memlist_;
|
||||
bool atomic_flush = false;
|
||||
|
||||
// Note: every time MemTableList::Add(mem) is called, it adds the new mem
|
||||
// at the FRONT of the memlist (memlist.push_front(mem)). Therefore, by
|
||||
// iterating through the memlist starting at the end, the vector<MemTable*>
|
||||
// ret is filled with memtables already sorted in increasing MemTable ID.
|
||||
// However, when the mempurge feature is activated, new memtables with older
|
||||
// IDs will be added to the memlist. Therefore we std::sort(ret) at the end to
|
||||
// return a vector of memtables sorted by increasing memtable ID.
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
if (!atomic_flush && m->atomic_flush_seqno_ != kMaxSequenceNumber) {
|
||||
@@ -361,6 +371,15 @@ void MemTableList::PickMemtablesToFlush(uint64_t max_memtable_id,
|
||||
if (!atomic_flush || num_flush_not_started_ == 0) {
|
||||
flush_requested_ = false; // start-flush request is complete
|
||||
}
|
||||
|
||||
// Sort the list of memtables by increasing memtable ID.
|
||||
// This is useful when the mempurge feature is activated
|
||||
// and the memtables are not guaranteed to be sorted in
|
||||
// the memlist vector.
|
||||
std::sort(ret->begin(), ret->end(),
|
||||
[](const MemTable* m1, const MemTable* m2) -> bool {
|
||||
return m1->GetID() < m2->GetID();
|
||||
});
|
||||
}
|
||||
|
||||
void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
@@ -521,9 +540,14 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
// and don't commit anything to the manifest file.
|
||||
RemoveMemTablesOrRestoreFlags(s, cfd, batch_count, log_buffer,
|
||||
to_delete, mu);
|
||||
// Note: cfd->SetLogNumber is only called when a VersionEdit
|
||||
// is written to MANIFEST. When mempurge is succesful, we skip
|
||||
// this step, therefore cfd->GetLogNumber is always is
|
||||
// earliest log with data unflushed.
|
||||
// Notify new head of manifest write queue.
|
||||
// wake up all the waiting writers
|
||||
// TODO(bjlemaire): explain full reason needed or investigate more.
|
||||
// TODO(bjlemaire): explain full reason WakeUpWaitingManifestWriters
|
||||
// needed or investigate more.
|
||||
vset->WakeUpWaitingManifestWriters();
|
||||
*io_s = IOStatus::OK();
|
||||
}
|
||||
@@ -534,8 +558,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
}
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
void MemTableList::Add(MemTable* m, autovector<MemTable*>* to_delete,
|
||||
bool trigger_flush) {
|
||||
void MemTableList::Add(MemTable* m, autovector<MemTable*>* to_delete) {
|
||||
assert(static_cast<int>(current_->memlist_.size()) >= num_flush_not_started_);
|
||||
InstallNewVersion();
|
||||
// this method is used to move mutable memtable into an immutable list.
|
||||
@@ -546,8 +569,7 @@ void MemTableList::Add(MemTable* m, autovector<MemTable*>* to_delete,
|
||||
current_->Add(m, to_delete);
|
||||
m->MarkImmutable();
|
||||
num_flush_not_started_++;
|
||||
|
||||
if (num_flush_not_started_ > 0 && trigger_flush) {
|
||||
if (num_flush_not_started_ == 1) {
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
UpdateCachedValuesFromMemTableListVersion();
|
||||
@@ -694,21 +716,6 @@ void MemTableList::RemoveMemTablesOrRestoreFlags(
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the earliest log that possibly contain entries
|
||||
// from one of the memtables of this memtable_list.
|
||||
uint64_t MemTableList::EarliestLogContainingData() {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
for (auto& m : current_->memlist_) {
|
||||
uint64_t log = m->GetEarliestLogFileNumber();
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
}
|
||||
|
||||
return min_log;
|
||||
}
|
||||
|
||||
uint64_t MemTableList::PrecomputeMinLogContainingPrepSection(
|
||||
const std::unordered_set<MemTable*>* memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
@@ -736,6 +743,8 @@ Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<const autovector<MemTable*>*>& mems_list, VersionSet* vset,
|
||||
LogsWithPrepTracker* prep_tracker, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_metas,
|
||||
const autovector<std::list<std::unique_ptr<FlushJobInfo>>*>&
|
||||
committed_flush_jobs_info,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
@@ -765,6 +774,17 @@ Status InstallMemtableAtomicFlushResults(
|
||||
(*mems_list[k])[i]->SetFlushCompleted(true);
|
||||
(*mems_list[k])[i]->SetFileNumber(file_metas[k]->fd.GetNumber());
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (committed_flush_jobs_info[k]) {
|
||||
assert(!mems_list[k]->empty());
|
||||
assert((*mems_list[k])[0]);
|
||||
std::unique_ptr<FlushJobInfo> flush_job_info =
|
||||
(*mems_list[k])[0]->ReleaseFlushJobInfo();
|
||||
committed_flush_jobs_info[k]->push_back(std::move(flush_job_info));
|
||||
}
|
||||
#else //! ROCKSDB_LITE
|
||||
(void)committed_flush_jobs_info;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
Status s;
|
||||
|
||||
+7
-6
@@ -140,6 +140,8 @@ class MemTableListVersion {
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, LogsWithPrepTracker* prep_tracker,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData*>& file_meta,
|
||||
const autovector<std::list<std::unique_ptr<FlushJobInfo>>*>&
|
||||
committed_flush_jobs_info,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
@@ -275,8 +277,7 @@ class MemTableList {
|
||||
// By default, adding memtables will flag that the memtable list needs to be
|
||||
// flushed, but in certain situations, like after a mempurge, we may want to
|
||||
// avoid flushing the memtable list upon addition of a memtable.
|
||||
void Add(MemTable* m, autovector<MemTable*>* to_delete,
|
||||
bool trigger_flush = true);
|
||||
void Add(MemTable* m, autovector<MemTable*>* to_delete);
|
||||
|
||||
// Returns an estimate of the number of bytes of data in use.
|
||||
size_t ApproximateMemoryUsage();
|
||||
@@ -347,10 +348,6 @@ class MemTableList {
|
||||
|
||||
size_t* current_memory_usage() { return ¤t_memory_usage_; }
|
||||
|
||||
// Returns the earliest log that possibly contain entries
|
||||
// from one of the memtables of this memtable_list.
|
||||
uint64_t EarliestLogContainingData();
|
||||
|
||||
// Returns the min log containing the prep section after memtables listsed in
|
||||
// `memtables_to_flush` are flushed and their status is persisted in manifest.
|
||||
uint64_t PrecomputeMinLogContainingPrepSection(
|
||||
@@ -402,6 +399,8 @@ class MemTableList {
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, LogsWithPrepTracker* prep_tracker,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData*>& file_meta,
|
||||
const autovector<std::list<std::unique_ptr<FlushJobInfo>>*>&
|
||||
committed_flush_jobs_info,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
@@ -452,6 +451,8 @@ extern Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<const autovector<MemTable*>*>& mems_list, VersionSet* vset,
|
||||
LogsWithPrepTracker* prep_tracker, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_meta,
|
||||
const autovector<std::list<std::unique_ptr<FlushJobInfo>>*>&
|
||||
committed_flush_jobs_info,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -182,12 +182,21 @@ class MemTableListTest : public testing::Test {
|
||||
for (auto& meta : file_metas) {
|
||||
file_meta_ptrs.push_back(&meta);
|
||||
}
|
||||
std::vector<std::list<std::unique_ptr<FlushJobInfo>>>
|
||||
committed_flush_jobs_info_storage(cf_ids.size());
|
||||
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
|
||||
committed_flush_jobs_info;
|
||||
for (int i = 0; i < static_cast<int>(cf_ids.size()); ++i) {
|
||||
committed_flush_jobs_info.push_back(
|
||||
&committed_flush_jobs_info_storage[i]);
|
||||
}
|
||||
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
return InstallMemtableAtomicFlushResults(
|
||||
&lists, cfds, mutable_cf_options_list, mems_list, &versions,
|
||||
nullptr /* prep_tracker */, &mutex, file_meta_ptrs, to_delete, nullptr,
|
||||
&log_buffer);
|
||||
nullptr /* prep_tracker */, &mutex, file_meta_ptrs,
|
||||
committed_flush_jobs_info, to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user