mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a0a0f5ff4 | ||
|
|
0def41ab2c | ||
|
|
0f1b41ece2 | ||
|
|
1a9bfff3c2 | ||
|
|
4b2122578e | ||
|
|
b8afcd1a48 | ||
|
|
8adcb6f153 | ||
|
|
7eb1adb532 | ||
|
|
0be3abf7b6 | ||
|
|
05fa171beb | ||
|
|
9b186c8d11 | ||
|
|
476a98ca30 | ||
|
|
925c63a96b | ||
|
|
6d83a75595 | ||
|
|
31b2397470 | ||
|
|
695c653e11 | ||
|
|
0e736666a0 | ||
|
|
1ec5a07d8e | ||
|
|
29c6610617 | ||
|
|
84a8dd994c | ||
|
|
2a0ee4ddd8 | ||
|
|
56359da691 | ||
|
|
46c37a6327 | ||
|
|
f7764cb6b2 | ||
|
|
6d802639f7 | ||
|
|
5e10baa412 | ||
|
|
72571d09ad | ||
|
|
07b09c7548 | ||
|
|
4069afeede | ||
|
|
24e2b05e61 | ||
|
|
5735ff4e03 | ||
|
|
30e097e365 | ||
|
|
b7a9d414c8 | ||
|
|
be99011f08 | ||
|
|
48eb646787 | ||
|
|
325dcdf2e5 | ||
|
|
743a02d6f6 |
No files matched your search
@@ -11,9 +11,9 @@ runs:
|
||||
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
|
||||
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
|
||||
JAVA_HOME: C:/Program Files/BellSoft/LibericaJDK-8
|
||||
SNAPPY_HOME: ${{ github.workspace }}/thirdparty/snappy-1.1.8
|
||||
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.1.8;${{ github.workspace }}/thirdparty/snappy-1.1.8/build
|
||||
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.1.8/build/Debug/snappy.lib
|
||||
SNAPPY_HOME: ${{ github.workspace }}/thirdparty/snappy-1.2.2
|
||||
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.2.2;${{ github.workspace }}/thirdparty/snappy-1.2.2/build
|
||||
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.2.2/build/Debug/snappy.lib
|
||||
run: |-
|
||||
# NOTE: if ... Exit $LASTEXITCODE lines needed to exit and report failure
|
||||
echo ===================== Install Dependencies =====================
|
||||
@@ -22,14 +22,14 @@ runs:
|
||||
mkdir $Env:THIRDPARTY_HOME
|
||||
cd $Env:THIRDPARTY_HOME
|
||||
echo "Building Snappy dependency..."
|
||||
curl -Lo snappy-1.1.8.zip https://github.com/google/snappy/archive/refs/tags/1.1.8.zip
|
||||
curl -Lo snappy-1.2.2.zip https://github.com/google/snappy/archive/refs/tags/1.2.2.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
unzip -q snappy-1.1.8.zip
|
||||
unzip -q snappy-1.2.2.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd snappy-1.1.8
|
||||
cd snappy-1.2.2
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" ..
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" .. -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
msbuild Snappy.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
|
||||
+34
@@ -1,6 +1,40 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 10.2.3 (05/19/2025)
|
||||
* Fix a failure in IngestExternalFiles() when ingesting an external table with the allow_db_generated_files option set to false.
|
||||
|
||||
## 10.2.2 (05/08/2025)
|
||||
### Bug Fixes
|
||||
* Pass wrapped WritableFileWriter pointer to ExternalTableBuilder so that the file checksum can be correctly calculated and returned by SstFileWriter for external table files.
|
||||
|
||||
## 10.2.1 (04/24/2025)
|
||||
### Bug Fixes
|
||||
* Fix improper initialization of ExternalTableOptions
|
||||
|
||||
## 10.2.0 (04/21/2025)
|
||||
### New Features
|
||||
* Provide histogram stats `COMPACTION_PREFETCH_BYTES` to measure number of bytes for RocksDB's prefetching (as opposed to file
|
||||
system's prefetch) on SST file during compaction read
|
||||
* A new API DB::GetNewestUserDefinedTimestamp is added to return the newest user defined timestamp seen in a column family
|
||||
* Introduce API `IngestWriteBatchWithIndex()` for ingesting updates into DB while bypassing memtable writes. This improves performance when writing a large write batch to the DB.
|
||||
* Add a new CF option `memtable_op_scan_flush_trigger` that triggers a flush of the memtable if an iterator's Seek()/Next() scans over a certain number of invisible entries from the memtable.
|
||||
|
||||
### Public API Changes
|
||||
* AdvancedColumnFamilyOptions.max_write_buffer_number_to_maintain is deleted. It's deprecated since introduction of a better option max_write_buffer_size_to_maintain since RocksDB 6.5.0.
|
||||
* Deprecated API `DB::MaxMemCompactionLevel()`.
|
||||
* Deprecated `ReadOptions::ignore_range_deletions`.
|
||||
* Deprecated API `experimental::PromoteL0()`.
|
||||
* Added arbitrary string map for additional options to be overriden for remote compactions
|
||||
* The fail_if_options_file_error option in DBOptions has been removed. The behavior now is to always return failure in any API that fails to persist the OPTIONS file.
|
||||
|
||||
### Behavior Changes
|
||||
* Make stats `PREFETCH_BYTES_USEFUL`, `PREFETCH_HITS`, `PREFETCH_BYTES` only account for prefetching during user initiated scan
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in Posix file system that the FSWritableFile created via `FileSystem::ReopenWritableFile` internally does not track the correct file size.
|
||||
* Fix a bug where tail size of remote compaction output is not persisted in primary db's manifest
|
||||
|
||||
## 10.1.0 (03/24/2025)
|
||||
### New Features
|
||||
* Added a new `DBOptions.calculate_sst_write_lifetime_hint_set` setting that allows to customize which compaction styles SST write lifetime hint calculation is allowed on. Today RocksDB supports only two modes `kCompactionStyleLevel` and `kCompactionStyleUniversal`.
|
||||
|
||||
+61
-15
@@ -11,18 +11,29 @@ CRASHTEST_MAKE=$(MAKE) -f crash_test.mk
|
||||
CRASHTEST_PY=$(PYTHON) -u tools/db_crashtest.py --stress_cmd=$(DB_STRESS_CMD) --cleanup_cmd='$(DB_CLEANUP_CMD)'
|
||||
|
||||
.PHONY: crash_test crash_test_with_atomic_flush crash_test_with_txn \
|
||||
crash_test_with_wc_txn crash_test_with_wp_txn crash_test_with_wup_txn \
|
||||
crash_test_with_best_efforts_recovery crash_test_with_ts \
|
||||
crash_test_with_multiops_wc_txn \
|
||||
crash_test_with_multiops_wp_txn \
|
||||
crash_test_with_multiops_wup_txn \
|
||||
crash_test_with_optimistic_txn \
|
||||
crash_test_with_tiered_storage \
|
||||
blackbox_crash_test blackbox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_wc_txn blackbox_crash_test_with_wp_txn \
|
||||
blackbox_crash_test_with_wup_txn \
|
||||
blackbox_crash_test_with_txn blackbox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_best_efforts_recovery \
|
||||
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
|
||||
whitebox_crash_test_with_txn whitebox_crash_test_with_ts \
|
||||
blackbox_crash_test_with_multiops_wc_txn \
|
||||
blackbox_crash_test_with_multiops_wp_txn \
|
||||
crash_test_with_tiered_storage blackbox_crash_test_with_tiered_storage \
|
||||
whitebox_crash_test_with_tiered_storage \
|
||||
whitebox_crash_test_with_optimistic_txn \
|
||||
blackbox_crash_test_with_multiops_wup_txn \
|
||||
blackbox_crash_test_with_optimistic_txn \
|
||||
blackbox_crash_test_with_tiered_storage \
|
||||
whitebox_crash_test whitebox_crash_test_with_atomic_flush \
|
||||
whitebox_crash_test_with_wc_txn whitebox_crash_test_with_wp_txn \
|
||||
whitebox_crash_test_with_wup_txn \
|
||||
whitebox_crash_test_with_txn whitebox_crash_test_with_ts \
|
||||
whitebox_crash_test_with_optimistic_txn \
|
||||
whitebox_crash_test_with_tiered_storage \
|
||||
|
||||
crash_test: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
@@ -34,10 +45,20 @@ crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_atomic_flush
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_atomic_flush
|
||||
|
||||
crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
crash_test_with_wc_txn: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_txn
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_wc_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_wc_txn
|
||||
|
||||
crash_test_with_wp_txn: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_wp_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_wp_txn
|
||||
|
||||
crash_test_with_wup_txn: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
$(CRASHTEST_MAKE) whitebox_crash_test_with_wup_txn
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_wup_txn
|
||||
|
||||
crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
# Do not parallelize
|
||||
@@ -62,6 +83,9 @@ crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
|
||||
crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wp_txn
|
||||
|
||||
crash_test_with_multiops_wup_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_MAKE) blackbox_crash_test_with_multiops_wup_txn
|
||||
|
||||
blackbox_crash_test: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(CRASHTEST_PY) blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -69,8 +93,14 @@ blackbox_crash_test: $(DB_STRESS_CMD)
|
||||
blackbox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --cf_consistency blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
blackbox_crash_test_with_wc_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 0 $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_wp_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 1 $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_wup_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn blackbox --txn_write_policy 2 $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_best_efforts_recovery: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -79,10 +109,13 @@ blackbox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --enable_ts blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wc_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_multiops_txn --write_policy write_committed blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 0 blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wp_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_multiops_txn --write_policy write_prepared blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 1 blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_multiops_wup_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_multiops_txn --txn_write_policy 2 blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --test_tiered_storage blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -104,9 +137,17 @@ whitebox_crash_test_with_atomic_flush: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --cf_consistency whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
whitebox_crash_test_with_wc_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 0 \
|
||||
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_wp_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 1 \
|
||||
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_wup_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --txn whitebox --txn_write_policy 2 \
|
||||
--random_kill_odd $(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_ts: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --enable_ts whitebox --random_kill_odd \
|
||||
@@ -119,3 +160,8 @@ whitebox_crash_test_with_tiered_storage: $(DB_STRESS_CMD)
|
||||
whitebox_crash_test_with_optimistic_txn: $(DB_STRESS_CMD)
|
||||
$(CRASHTEST_PY) --optimistic_txn whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
# Old names DEPRECATED
|
||||
crash_test_with_txn: crash_test_with_wc_txn
|
||||
whitebox_crash_test_with_txn: whitebox_crash_test_with_wc_txn
|
||||
blackbox_crash_test_with_txn: blackbox_crash_test_with_wc_txn
|
||||
+27
-23
@@ -42,9 +42,9 @@ Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
|
||||
void ArenaWrappedDBIter::Init(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
|
||||
const SequenceNumber& sequence, uint64_t version_number,
|
||||
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
|
||||
bool expose_blob_index, bool allow_refresh, ReadOnlyMemTable* active_mem) {
|
||||
read_options_ = read_options;
|
||||
if (!CheckFSFeatureSupport(env->GetFileSystem().get(),
|
||||
FSSupportedOps::kAsyncIO)) {
|
||||
@@ -52,15 +52,14 @@ void ArenaWrappedDBIter::Init(
|
||||
}
|
||||
read_options_.total_order_seek |= ioptions.prefix_seek_opt_in_only;
|
||||
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem) DBIter(env, read_options_, ioptions, mutable_cf_options,
|
||||
ioptions.user_comparator,
|
||||
/* iter */ nullptr, version, sequence, true,
|
||||
max_sequential_skip_in_iteration, read_callback,
|
||||
cfh, expose_blob_index);
|
||||
db_iter_ = DBIter::NewIter(
|
||||
env, read_options_, ioptions, mutable_cf_options,
|
||||
ioptions.user_comparator, /*internal_iter=*/nullptr, version, sequence,
|
||||
read_callback, cfh, expose_blob_index, active_mem, &arena_);
|
||||
|
||||
sv_number_ = version_number;
|
||||
allow_refresh_ = allow_refresh;
|
||||
allow_mark_memtable_for_flush_ = active_mem;
|
||||
memtable_range_tombstone_iter_ = nullptr;
|
||||
}
|
||||
|
||||
@@ -166,9 +165,8 @@ void ArenaWrappedDBIter::DoRefresh(const Snapshot* snapshot,
|
||||
read_callback_->Refresh(read_seq);
|
||||
}
|
||||
Init(env, read_options_, cfd->ioptions(), sv->mutable_cf_options, sv->current,
|
||||
read_seq, sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback_, cfh_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
read_seq, sv->version_number, read_callback_, cfh_, expose_blob_index_,
|
||||
allow_refresh_, allow_mark_memtable_for_flush_ ? sv->mem : nullptr);
|
||||
|
||||
InternalIterator* internal_iter = db_impl->NewInternalIterator(
|
||||
read_options_, cfd, sv, &arena_, read_seq,
|
||||
@@ -253,20 +251,26 @@ Status ArenaWrappedDBIter::Refresh(const Snapshot* snapshot) {
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index, bool allow_refresh) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, ioptions, mutable_cf_options, version, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
cfh, expose_blob_index, allow_refresh);
|
||||
Env* env, const ReadOptions& read_options, ColumnFamilyHandleImpl* cfh,
|
||||
SuperVersion* sv, const SequenceNumber& sequence,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, bool expose_blob_index,
|
||||
bool allow_refresh, bool allow_mark_memtable_for_flush) {
|
||||
ArenaWrappedDBIter* db_iter = new ArenaWrappedDBIter();
|
||||
db_iter->Init(env, read_options, cfh->cfd()->ioptions(),
|
||||
sv->mutable_cf_options, sv->current, sequence,
|
||||
sv->version_number, read_callback, cfh, expose_blob_index,
|
||||
allow_refresh,
|
||||
allow_mark_memtable_for_flush ? sv->mem : nullptr);
|
||||
if (cfh != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(cfh, read_callback, expose_blob_index);
|
||||
db_iter->StoreRefreshInfo(cfh, read_callback, expose_blob_index);
|
||||
}
|
||||
|
||||
return iter;
|
||||
InternalIterator* internal_iter = db_impl->NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfh->cfd(), sv, db_iter->GetArena(), sequence,
|
||||
/*allow_unprepared_value=*/true, db_iter);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+14
-13
@@ -19,7 +19,6 @@
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -99,13 +98,19 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
|
||||
bool PrepareValue() override { return db_iter_->PrepareValue(); }
|
||||
|
||||
void Prepare(const std::vector<ScanOptions>& scan_opts) override {
|
||||
db_iter_->Prepare(scan_opts);
|
||||
}
|
||||
|
||||
// FIXME: we could just pass SV in for mutable cf option, version and version
|
||||
// number, but this is used by SstFileReader which does not have a SV.
|
||||
void Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const SequenceNumber& sequence, uint64_t version_number,
|
||||
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
|
||||
bool expose_blob_index, bool allow_refresh);
|
||||
bool expose_blob_index, bool allow_refresh,
|
||||
ReadOnlyMemTable* active_mem);
|
||||
|
||||
// Store some parameters so we can refresh the iterator at a later point
|
||||
// with these same params
|
||||
@@ -128,20 +133,16 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
ReadCallback* read_callback_;
|
||||
bool expose_blob_index_ = false;
|
||||
bool allow_refresh_ = true;
|
||||
bool allow_mark_memtable_for_flush_ = true;
|
||||
// If this is nullptr, it means the mutable memtable does not contain range
|
||||
// tombstone when added under this DBIter.
|
||||
std::unique_ptr<TruncatedRangeDelIterator>* memtable_range_tombstone_iter_ =
|
||||
nullptr;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
// `cfh` is used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh = nullptr, bool expose_blob_index = false,
|
||||
bool allow_refresh = true);
|
||||
Env* env, const ReadOptions& read_options, ColumnFamilyHandleImpl* cfh,
|
||||
SuperVersion* sv, const SequenceNumber& sequence,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, bool expose_blob_index,
|
||||
bool allow_refresh, bool allow_mark_memtable_for_flush);
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -3295,6 +3295,16 @@ uint64_t rocksdb_options_get_periodic_compaction_seconds(
|
||||
return opt->rep.periodic_compaction_seconds;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_op_scan_flush_trigger(rocksdb_options_t* opt,
|
||||
uint32_t n) {
|
||||
opt->rep.memtable_op_scan_flush_trigger = n;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_options_get_memtable_op_scan_flush_trigger(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.memtable_op_scan_flush_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_enable_statistics(rocksdb_options_t* opt) {
|
||||
opt->rep.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
}
|
||||
@@ -3804,16 +3814,6 @@ int rocksdb_options_get_min_write_buffer_number_to_merge(
|
||||
return opt->rep.min_write_buffer_number_to_merge;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_number_to_maintain(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_write_buffer_number_to_maintain = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_write_buffer_number_to_maintain(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_number_to_maintain;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_size_to_maintain(
|
||||
rocksdb_options_t* opt, int64_t n) {
|
||||
opt->rep.max_write_buffer_size_to_maintain = n;
|
||||
|
||||
+10
-12
@@ -2129,6 +2129,10 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(100000 ==
|
||||
rocksdb_options_get_periodic_compaction_seconds(o));
|
||||
|
||||
rocksdb_options_set_memtable_op_scan_flush_trigger(o, 100);
|
||||
CheckCondition(100 ==
|
||||
rocksdb_options_get_memtable_op_scan_flush_trigger(o));
|
||||
|
||||
rocksdb_options_set_ttl(o, 5000);
|
||||
CheckCondition(5000 == rocksdb_options_get_ttl(o));
|
||||
|
||||
@@ -2146,10 +2150,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(o, 64);
|
||||
CheckCondition(64 ==
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(o, 50000);
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(o));
|
||||
@@ -2407,8 +2407,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
|
||||
CheckCondition(
|
||||
64 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(copy));
|
||||
@@ -2572,6 +2570,12 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(100000 ==
|
||||
rocksdb_options_get_periodic_compaction_seconds(o));
|
||||
|
||||
rocksdb_options_set_memtable_op_scan_flush_trigger(copy, 800);
|
||||
CheckCondition(800 ==
|
||||
rocksdb_options_get_memtable_op_scan_flush_trigger(copy));
|
||||
CheckCondition(100 ==
|
||||
rocksdb_options_get_memtable_op_scan_flush_trigger(o));
|
||||
|
||||
rocksdb_options_set_ttl(copy, 8000);
|
||||
CheckCondition(8000 == rocksdb_options_get_ttl(copy));
|
||||
CheckCondition(5000 == rocksdb_options_get_ttl(o));
|
||||
@@ -2596,12 +2600,6 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(copy, 128);
|
||||
CheckCondition(
|
||||
128 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
|
||||
CheckCondition(64 ==
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(copy, 9000);
|
||||
CheckCondition(9000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
|
||||
|
||||
+7
-6
@@ -262,15 +262,10 @@ ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
|
||||
if (result.max_write_buffer_number < 2) {
|
||||
result.max_write_buffer_number = 2;
|
||||
}
|
||||
// fall back max_write_buffer_number_to_maintain if
|
||||
// max_write_buffer_size_to_maintain is not set
|
||||
if (result.max_write_buffer_size_to_maintain < 0) {
|
||||
result.max_write_buffer_size_to_maintain =
|
||||
result.max_write_buffer_number *
|
||||
static_cast<int64_t>(result.write_buffer_size);
|
||||
} else if (result.max_write_buffer_size_to_maintain == 0 &&
|
||||
result.max_write_buffer_number_to_maintain < 0) {
|
||||
result.max_write_buffer_number_to_maintain = result.max_write_buffer_number;
|
||||
}
|
||||
// bloom filter size shouldn't exceed 1/4 of memtable size.
|
||||
if (result.memtable_prefix_bloom_size_ratio > 0.25) {
|
||||
@@ -453,6 +448,13 @@ ColumnFamilyOptions SanitizeCfOptions(const ImmutableDBOptions& db_options,
|
||||
result.preclude_last_level_data_seconds = 0;
|
||||
}
|
||||
|
||||
if (read_only && result.memtable_op_scan_flush_trigger != 0) {
|
||||
ROCKS_LOG_WARN(db_options.info_log.get(),
|
||||
"option memtable_op_scan_flush_trigger is sanitized to "
|
||||
"0(disabled) for read only DB.");
|
||||
result.memtable_op_scan_flush_trigger = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -577,7 +579,6 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
mem_(nullptr),
|
||||
imm_(ioptions_.min_write_buffer_number_to_merge,
|
||||
ioptions_.max_write_buffer_number_to_maintain,
|
||||
ioptions_.max_write_buffer_size_to_maintain),
|
||||
super_version_(nullptr),
|
||||
super_version_number_(0),
|
||||
|
||||
@@ -537,6 +537,12 @@ class ColumnFamilyData {
|
||||
assert(!ts_low.empty());
|
||||
const Comparator* ucmp = user_comparator();
|
||||
assert(ucmp);
|
||||
// Guard against resurrected full_history_ts_low persisted in MANIFEST
|
||||
// from previous DB sessions. This could happen if UDT was enabled and then
|
||||
// disabled.
|
||||
if (ucmp->timestamp_size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (full_history_ts_low_.empty() ||
|
||||
ucmp->CompareTimestamp(ts_low, full_history_ts_low_) > 0) {
|
||||
full_history_ts_low_ = std::move(ts_low);
|
||||
@@ -544,6 +550,11 @@ class ColumnFamilyData {
|
||||
}
|
||||
|
||||
const std::string& GetFullHistoryTsLow() const {
|
||||
const Comparator* ucmp = user_comparator();
|
||||
assert(ucmp);
|
||||
if (ucmp->timestamp_size() == 0) {
|
||||
assert(full_history_ts_low_.empty());
|
||||
}
|
||||
return full_history_ts_low_;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
env_->skip_fsync_ = true;
|
||||
dbname_ = test::PerThreadDBPath("column_family_test");
|
||||
db_options_.create_if_missing = true;
|
||||
db_options_.fail_if_options_file_error = true;
|
||||
db_options_.env = env_;
|
||||
}
|
||||
|
||||
@@ -2176,7 +2175,7 @@ TEST_P(ColumnFamilyTest, FlushStaleColumnFamilies) {
|
||||
ASSERT_TRUE(has_cf2_sst);
|
||||
|
||||
ASSERT_OK(Flush(0));
|
||||
ASSERT_EQ(0, dbfull()->TEST_total_log_size());
|
||||
ASSERT_EQ(0, dbfull()->TEST_wals_total_size());
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,9 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
dbname_, db_id_, db_session_id_, GetCompactionId(sub_compact),
|
||||
thread_pri_, compaction->compaction_reason(),
|
||||
compaction->is_full_compaction(), compaction->is_manual_compaction(),
|
||||
compaction->bottommost_level());
|
||||
compaction->bottommost_level(), compaction->start_level(),
|
||||
compaction->output_level());
|
||||
|
||||
CompactionServiceScheduleResponse response =
|
||||
db_options_.compaction_service->Schedule(info, compaction_input_binary);
|
||||
switch (response.status) {
|
||||
@@ -240,7 +242,8 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
meta.marked_for_compaction = file.marked_for_compaction;
|
||||
meta.unique_id = file.unique_id;
|
||||
meta.temperature = file.file_temperature;
|
||||
|
||||
meta.tail_size =
|
||||
FileMetaData::CalculateTailSize(file_size, file.table_properties);
|
||||
auto cfd = compaction->column_family_data();
|
||||
CompactionOutputs* compaction_outputs =
|
||||
sub_compact->Outputs(file.is_proximal_level_output);
|
||||
|
||||
@@ -22,9 +22,9 @@ class MyTestCompactionService : public CompactionService {
|
||||
options_(options),
|
||||
statistics_(statistics),
|
||||
start_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false),
|
||||
false, false, false, -1, -1),
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false),
|
||||
false, false, false, -1, -1),
|
||||
listeners_(listeners),
|
||||
table_properties_collector_factories_(
|
||||
std::move(table_properties_collector_factories)) {}
|
||||
@@ -84,6 +84,7 @@ class MyTestCompactionService : public CompactionService {
|
||||
options_override.table_factory = options_.table_factory;
|
||||
options_override.sst_partitioner_factory = options_.sst_partitioner_factory;
|
||||
options_override.statistics = statistics_;
|
||||
options_override.info_log = options_.info_log;
|
||||
if (!listeners_.empty()) {
|
||||
options_override.listeners = listeners_;
|
||||
}
|
||||
@@ -277,8 +278,17 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
Statistics* primary_statistics = GetPrimaryStatistics();
|
||||
Statistics* compactor_statistics = GetCompactorStatistics();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTable::PrefetchTail::TaiSizeNotRecorded",
|
||||
[&](void* /* arg */) {
|
||||
// Trigger assertion to verify precise tail prefetch size calculation
|
||||
assert(false);
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
GenerateTestData();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
VerifyTestData();
|
||||
|
||||
auto my_cs = GetCompactionService();
|
||||
@@ -380,6 +390,7 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
ASSERT_FALSE(result.stats.is_full_compaction);
|
||||
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
@@ -890,6 +901,12 @@ TEST_F(CompactionServiceTest, TruncatedOutput) {
|
||||
Slice end(end_str);
|
||||
uint64_t comp_num = my_cs->GetCompactionNum();
|
||||
|
||||
// Skip calculating tail size to avoid crashing due to truncated file size
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"FileMetaData::CalculateTailSize", [&](void* arg) {
|
||||
bool* skip = static_cast<bool*>(arg);
|
||||
*skip = true;
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionServiceCompactionJob::Run:0", [&](void* arg) {
|
||||
CompactionServiceResult* compaction_result =
|
||||
@@ -906,7 +923,7 @@ TEST_F(CompactionServiceTest, TruncatedOutput) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_GT(file_size, 0);
|
||||
|
||||
ASSERT_OK(test::TruncateFile(env_, file_name, file_size / 2));
|
||||
ASSERT_OK(test::TruncateFile(env_, file_name, file_size / 4));
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -1250,17 +1267,31 @@ TEST_F(CompactionServiceTest, PrecludeLastLevel) {
|
||||
|
||||
// Verify Output Stats
|
||||
auto my_cs = GetCompactionService();
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_GT(result.internal_stats.output_level_stats.cpu_micros, 0);
|
||||
ASSERT_GT(result.internal_stats.output_level_stats.micros, 0);
|
||||
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_records +
|
||||
result.internal_stats.proximal_level_stats.num_output_records,
|
||||
kNumTrigger * kNumKeys);
|
||||
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_files +
|
||||
result.internal_stats.proximal_level_stats.num_output_files,
|
||||
2);
|
||||
{
|
||||
CompactionServiceResult result;
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_GT(result.internal_stats.output_level_stats.cpu_micros, 0);
|
||||
ASSERT_GT(result.internal_stats.output_level_stats.micros, 0);
|
||||
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_records +
|
||||
result.internal_stats.proximal_level_stats.num_output_records,
|
||||
kNumTrigger * kNumKeys);
|
||||
ASSERT_EQ(result.internal_stats.output_level_stats.num_output_files +
|
||||
result.internal_stats.proximal_level_stats.num_output_files,
|
||||
2);
|
||||
|
||||
CompactionServiceJobInfo info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(0, info.base_input_level);
|
||||
ASSERT_EQ(kNumLevels - 1, info.output_level);
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
// Disable Preclude feature and run full compaction to the bottommost level
|
||||
{
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
CompactionServiceJobInfo info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(kNumLevels - 2, info.base_input_level);
|
||||
ASSERT_EQ(kNumLevels - 1, info.output_level);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, ConcurrentCompaction) {
|
||||
@@ -1330,12 +1361,16 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
ASSERT_EQ(true, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(1, info.base_input_level);
|
||||
ASSERT_EQ(2, info.output_level);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(Env::USER, info.priority);
|
||||
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
|
||||
ASSERT_EQ(true, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(1, info.base_input_level);
|
||||
ASSERT_EQ(2, info.output_level);
|
||||
|
||||
// Test priority BOTTOM
|
||||
env_->SetBackgroundThreads(1, Env::BOTTOM);
|
||||
@@ -1367,18 +1402,24 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(Env::BOTTOM, info.priority);
|
||||
ASSERT_EQ(0, info.base_input_level);
|
||||
ASSERT_EQ(db_->NumberLevels() - 1, info.output_level);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(Env::BOTTOM, info.priority);
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum, info.compaction_reason);
|
||||
ASSERT_EQ(false, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(0, info.base_input_level);
|
||||
ASSERT_EQ(db_->NumberLevels() - 1, info.output_level);
|
||||
|
||||
// Test Non-Bottommost Level
|
||||
options.num_levels = 4;
|
||||
ReopenWithCompactionService(&options);
|
||||
my_cs =
|
||||
static_cast_with_check<MyTestCompactionService>(GetCompactionService());
|
||||
int compaction_num = my_cs->GetCompactionNum();
|
||||
ASSERT_EQ(0, compaction_num);
|
||||
|
||||
for (int i = 0; i < options.level0_file_num_compaction_trigger; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
@@ -1387,16 +1428,22 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// This is trivial move. Done locally.
|
||||
ASSERT_EQ(0, my_cs->GetCompactionNum());
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(false, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(false, info.bottommost_level);
|
||||
ASSERT_EQ(-1, info.base_input_level);
|
||||
ASSERT_EQ(-1, info.output_level);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(false, info.is_manual_compaction);
|
||||
ASSERT_EQ(false, info.is_full_compaction);
|
||||
ASSERT_EQ(false, info.bottommost_level);
|
||||
ASSERT_EQ(-1, info.base_input_level);
|
||||
ASSERT_EQ(-1, info.output_level);
|
||||
|
||||
// Test Full Compaction + Bottommost Level
|
||||
options.num_levels = 6;
|
||||
@@ -1411,7 +1458,10 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
MoveFilesToLevel(options.num_levels - 1);
|
||||
|
||||
// Force final level compaction
|
||||
// base_input_level == output_level == last_level
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
@@ -1423,10 +1473,15 @@ TEST_F(CompactionServiceTest, CompactionInfo) {
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(options.num_levels - 1, info.base_input_level);
|
||||
ASSERT_EQ(options.num_levels - 1, info.output_level);
|
||||
ASSERT_EQ(true, info.is_manual_compaction);
|
||||
ASSERT_EQ(true, info.is_full_compaction);
|
||||
ASSERT_EQ(true, info.bottommost_level);
|
||||
ASSERT_EQ(CompactionReason::kManualCompaction, info.compaction_reason);
|
||||
ASSERT_EQ(options.num_levels - 1, info.base_input_level);
|
||||
ASSERT_EQ(options.num_levels - 1, info.output_level);
|
||||
ASSERT_EQ("0,0,0,0,0,1", FilesPerLevel());
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, FallbackLocalAuto) {
|
||||
|
||||
+10
-14
@@ -75,11 +75,9 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
ret.emplace_back(CurrentFileName(""));
|
||||
ret.emplace_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
// The OPTIONS file number is zero in read-write mode when OPTIONS file
|
||||
// writing failed and the DB was configured with
|
||||
// `fail_if_options_file_error == false`. In read-only mode the OPTIONS file
|
||||
// number is zero when no OPTIONS file exist at all. In those cases we do not
|
||||
// record any OPTIONS file in the live file list.
|
||||
// In read-only mode the OPTIONS file number is zero when no OPTIONS file
|
||||
// exist at all. In this cases we do not record any OPTIONS file in the live
|
||||
// file list.
|
||||
if (versions_->options_file_number() != 0) {
|
||||
ret.emplace_back(OptionsFileName("", versions_->options_file_number()));
|
||||
}
|
||||
@@ -185,14 +183,14 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) {
|
||||
Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_wal_file) {
|
||||
uint64_t current_logfile_number;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
current_logfile_number = logfile_number_;
|
||||
current_logfile_number = cur_wal_number_;
|
||||
}
|
||||
|
||||
return wal_manager_.GetLiveWalFile(current_logfile_number, current_log_file);
|
||||
return wal_manager_.GetLiveWalFile(current_logfile_number, current_wal_file);
|
||||
}
|
||||
|
||||
Status DBImpl::GetLiveFilesStorageInfo(
|
||||
@@ -332,7 +330,7 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
const uint64_t options_size = versions_->options_file_size_;
|
||||
const uint64_t min_log_num = MinLogNumberToKeep();
|
||||
// Ensure consistency with manifest for track_and_verify_wals_in_manifest
|
||||
const uint64_t max_log_num = logfile_number_;
|
||||
const uint64_t max_log_num = cur_wal_number_;
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -369,11 +367,9 @@ Status DBImpl::GetLiveFilesStorageInfo(
|
||||
}
|
||||
}
|
||||
|
||||
// The OPTIONS file number is zero in read-write mode when OPTIONS file
|
||||
// writing failed and the DB was configured with
|
||||
// `fail_if_options_file_error == false`. In read-only mode the OPTIONS file
|
||||
// number is zero when no OPTIONS file exist at all. In those cases we do not
|
||||
// record any OPTIONS file in the live file list.
|
||||
// In read-only mode the OPTIONS file number is zero when no OPTIONS file
|
||||
// exist at all. In this cases we do not record any OPTIONS file in the live
|
||||
// file list.
|
||||
if (options_number != 0) {
|
||||
results.emplace_back();
|
||||
LiveFileStorageInfo& info = results.back();
|
||||
|
||||
+121
-102
@@ -74,6 +74,7 @@
|
||||
#include "options/cf_options.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "util/udt_util.h"
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
#include "port/jemalloc_helper.h"
|
||||
#endif
|
||||
@@ -169,7 +170,6 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
bool read_only)
|
||||
: dbname_(dbname),
|
||||
own_info_log_(options.info_log == nullptr),
|
||||
init_logger_creation_s_(),
|
||||
initial_db_options_(SanitizeOptions(dbname, options, read_only,
|
||||
&init_logger_creation_s_)),
|
||||
env_(initial_db_options_.env),
|
||||
@@ -185,7 +185,6 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
mutex_(stats_, immutable_db_options_.clock, DB_MUTEX_WAIT_MICROS,
|
||||
immutable_db_options_.use_adaptive_mutex),
|
||||
#endif // COERCE_CONTEXT_SWITCH
|
||||
default_cf_handle_(nullptr),
|
||||
error_handler_(this, immutable_db_options_, &mutex_),
|
||||
event_logger_(immutable_db_options_.info_log.get()),
|
||||
max_total_in_memory_state_(0),
|
||||
@@ -194,45 +193,15 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
file_options_, immutable_db_options_)),
|
||||
seq_per_batch_(seq_per_batch),
|
||||
batch_per_txn_(batch_per_txn),
|
||||
next_job_id_(1),
|
||||
shutting_down_(false),
|
||||
reject_new_background_jobs_(false),
|
||||
db_lock_(nullptr),
|
||||
manual_compaction_paused_(false),
|
||||
bg_cv_(&mutex_),
|
||||
logfile_number_(0),
|
||||
log_dir_synced_(false),
|
||||
log_empty_(true),
|
||||
persist_stats_cf_handle_(nullptr),
|
||||
log_sync_cv_(&log_write_mutex_),
|
||||
total_log_size_(0),
|
||||
is_snapshot_supported_(true),
|
||||
wal_sync_cv_(&wal_write_mutex_),
|
||||
write_buffer_manager_(immutable_db_options_.write_buffer_manager.get()),
|
||||
write_thread_(immutable_db_options_),
|
||||
nonmem_write_thread_(immutable_db_options_),
|
||||
write_controller_(mutable_db_options_.delayed_write_rate),
|
||||
last_batch_group_size_(0),
|
||||
unscheduled_flushes_(0),
|
||||
unscheduled_compactions_(0),
|
||||
bg_bottom_compaction_scheduled_(0),
|
||||
bg_compaction_scheduled_(0),
|
||||
num_running_compactions_(0),
|
||||
bg_flush_scheduled_(0),
|
||||
num_running_flushes_(0),
|
||||
bg_purge_scheduled_(0),
|
||||
disable_delete_obsolete_files_(0),
|
||||
pending_purge_obsolete_files_(0),
|
||||
delete_obsolete_files_last_run_(immutable_db_options_.clock->NowMicros()),
|
||||
has_unpersisted_data_(false),
|
||||
unable_to_release_oldest_log_(false),
|
||||
num_running_ingest_file_(0),
|
||||
wal_manager_(immutable_db_options_, file_options_, io_tracer_,
|
||||
seq_per_batch),
|
||||
bg_work_paused_(0),
|
||||
bg_compaction_paused_(0),
|
||||
refitting_level_(false),
|
||||
opened_successfully_(false),
|
||||
periodic_task_scheduler_(),
|
||||
two_write_queues_(options.two_write_queues),
|
||||
manual_wal_flush_(options.manual_wal_flush),
|
||||
// last_sequencee_ is always maintained by the main queue that also writes
|
||||
@@ -250,14 +219,11 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
// requires a custom gc for compaction, we use that to set use_custom_gc_
|
||||
// as well.
|
||||
use_custom_gc_(seq_per_batch),
|
||||
shutdown_initiated_(false),
|
||||
own_sfm_(options.sst_file_manager == nullptr),
|
||||
closed_(false),
|
||||
atomic_flush_install_cv_(&mutex_),
|
||||
blob_callback_(immutable_db_options_.sst_file_manager.get(), &mutex_,
|
||||
&error_handler_, &event_logger_,
|
||||
immutable_db_options_.listeners, dbname_),
|
||||
lock_wal_count_(0) {
|
||||
immutable_db_options_.listeners, dbname_) {
|
||||
// !batch_per_trx_ implies seq_per_batch_ because it is only unset for
|
||||
// WriteUnprepared, which should use seq_per_batch_.
|
||||
assert(batch_per_txn_ || seq_per_batch_);
|
||||
@@ -636,8 +602,8 @@ Status DBImpl::CloseHelper() {
|
||||
mutex_.Lock();
|
||||
}
|
||||
{
|
||||
InstrumentedMutexLock lock(&log_write_mutex_);
|
||||
for (auto l : logs_to_free_) {
|
||||
InstrumentedMutexLock lock(&wal_write_mutex_);
|
||||
for (auto l : wals_to_free_) {
|
||||
delete l;
|
||||
}
|
||||
for (auto& log : logs_) {
|
||||
@@ -1180,11 +1146,11 @@ Status DBImpl::TablesRangeTombstoneSummary(ColumnFamilyHandle* column_family,
|
||||
|
||||
void DBImpl::ScheduleBgLogWriterClose(JobContext* job_context) {
|
||||
mutex_.AssertHeld();
|
||||
if (!job_context->logs_to_free.empty()) {
|
||||
for (auto l : job_context->logs_to_free) {
|
||||
if (!job_context->wals_to_free.empty()) {
|
||||
for (auto l : job_context->wals_to_free) {
|
||||
AddToLogsToFreeQueue(l);
|
||||
}
|
||||
job_context->logs_to_free.clear();
|
||||
job_context->wals_to_free.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1443,7 +1409,7 @@ Status DBImpl::SetDBOptions(
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
if (wal_other_option_changed ||
|
||||
total_log_size_ > GetMaxTotalWalSize()) {
|
||||
wals_total_size_.LoadRelaxed() > GetMaxTotalWalSize()) {
|
||||
Status purge_wal_status = SwitchWAL(&write_context);
|
||||
if (!purge_wal_status.ok()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
@@ -1470,14 +1436,9 @@ Status DBImpl::SetDBOptions(
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "SetDBOptions() succeeded");
|
||||
new_options.Dump(immutable_db_options_.info_log.get());
|
||||
if (!persist_options_status.ok()) {
|
||||
if (immutable_db_options_.fail_if_options_file_error) {
|
||||
s = Status::IOError(
|
||||
"SetDBOptions() succeeded, but unable to persist options",
|
||||
persist_options_status.ToString());
|
||||
}
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"Unable to persist options in SetDBOptions() -- %s",
|
||||
persist_options_status.ToString().c_str());
|
||||
s = Status::IOError(
|
||||
"SetDBOptions() succeeded, but unable to persist options",
|
||||
persist_options_status.ToString());
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log, "SetDBOptions failed");
|
||||
@@ -1512,8 +1473,8 @@ Status DBImpl::FlushWAL(const WriteOptions& write_options, bool sync) {
|
||||
if (manual_wal_flush_) {
|
||||
IOStatus io_s;
|
||||
{
|
||||
// We need to lock log_write_mutex_ since logs_ might change concurrently
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
// We need to lock wal_write_mutex_ since logs_ might change concurrently
|
||||
InstrumentedMutexLock wl(&wal_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
io_s = cur_log_writer->WriteBuffer(write_options);
|
||||
}
|
||||
@@ -1540,7 +1501,7 @@ Status DBImpl::FlushWAL(const WriteOptions& write_options, bool sync) {
|
||||
}
|
||||
|
||||
bool DBImpl::WALBufferIsEmpty() {
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
auto res = cur_log_writer->BufferIsEmpty();
|
||||
return res;
|
||||
@@ -1548,7 +1509,7 @@ bool DBImpl::WALBufferIsEmpty() {
|
||||
|
||||
Status DBImpl::GetOpenWalSizes(std::map<uint64_t, uint64_t>& number_to_size) {
|
||||
assert(number_to_size.empty());
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
for (auto& log : logs_) {
|
||||
auto* open_file = log.writer->file();
|
||||
if (open_file) {
|
||||
@@ -1590,15 +1551,15 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
|
||||
uint64_t up_to_number;
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
assert(!logs_.empty());
|
||||
|
||||
maybe_active_number = logfile_number_;
|
||||
maybe_active_number = cur_wal_number_;
|
||||
up_to_number =
|
||||
include_current_wal ? maybe_active_number : maybe_active_number - 1;
|
||||
|
||||
while (logs_.front().number <= up_to_number && logs_.front().IsSyncing()) {
|
||||
log_sync_cv_.Wait();
|
||||
wal_sync_cv_.Wait();
|
||||
}
|
||||
// First check that logs are safe to sync in background.
|
||||
if (include_current_wal &&
|
||||
@@ -1622,7 +1583,7 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
|
||||
}
|
||||
}
|
||||
|
||||
need_wal_dir_sync = !log_dir_synced_;
|
||||
need_wal_dir_sync = !wal_dir_synced_;
|
||||
}
|
||||
|
||||
if (include_current_wal) {
|
||||
@@ -1695,7 +1656,7 @@ IOStatus DBImpl::SyncWalImpl(bool include_current_wal,
|
||||
/*arg=*/nullptr);
|
||||
}
|
||||
{
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
for (auto* wal : wals_internally_closed) {
|
||||
// We can only modify the state of log::Writer under the mutex
|
||||
bool was_closed = wal->PublishIfClosed();
|
||||
@@ -1812,9 +1773,9 @@ Status DBImpl::UnlockWAL() {
|
||||
|
||||
void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
|
||||
VersionEdit* synced_wals) {
|
||||
log_write_mutex_.AssertHeld();
|
||||
if (synced_dir && logfile_number_ == up_to) {
|
||||
log_dir_synced_ = true;
|
||||
wal_write_mutex_.AssertHeld();
|
||||
if (synced_dir && cur_wal_number_ == up_to) {
|
||||
wal_dir_synced_ = true;
|
||||
}
|
||||
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;) {
|
||||
auto& wal = *it;
|
||||
@@ -1836,7 +1797,7 @@ void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
|
||||
(immutable_db_options_.background_close_inactive_wals &&
|
||||
wal.GetPreSyncSize() == wal.writer->file()->GetFlushedSize())) {
|
||||
// Fully synced
|
||||
logs_to_free_.push_back(wal.ReleaseWriter());
|
||||
wals_to_free_.push_back(wal.ReleaseWriter());
|
||||
it = logs_.erase(it);
|
||||
} else {
|
||||
wal.FinishSync();
|
||||
@@ -1849,17 +1810,17 @@ void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
|
||||
++it;
|
||||
}
|
||||
}
|
||||
log_sync_cv_.SignalAll();
|
||||
wal_sync_cv_.SignalAll();
|
||||
}
|
||||
|
||||
void DBImpl::MarkLogsNotSynced(uint64_t up_to) {
|
||||
log_write_mutex_.AssertHeld();
|
||||
wal_write_mutex_.AssertHeld();
|
||||
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;
|
||||
++it) {
|
||||
auto& wal = *it;
|
||||
wal.FinishSync();
|
||||
}
|
||||
log_sync_cv_.SignalAll();
|
||||
wal_sync_cv_.SignalAll();
|
||||
}
|
||||
|
||||
SequenceNumber DBImpl::GetLatestSequenceNumber() const {
|
||||
@@ -1895,6 +1856,69 @@ Status DBImpl::GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::GetNewestUserDefinedTimestamp(ColumnFamilyHandle* column_family,
|
||||
std::string* newest_timestamp) {
|
||||
if (newest_timestamp == nullptr) {
|
||||
return Status::InvalidArgument("newest_timestamp is nullptr");
|
||||
}
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
if (column_family == nullptr) {
|
||||
cfd = default_cf_handle_->cfd();
|
||||
} else {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
assert(cfh != nullptr);
|
||||
cfd = cfh->cfd();
|
||||
}
|
||||
assert(cfd != nullptr && cfd->user_comparator() != nullptr);
|
||||
if (cfd->user_comparator()->timestamp_size() == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Timestamp is not enabled in this column family");
|
||||
}
|
||||
if (cfd->ioptions().persist_user_defined_timestamps) {
|
||||
return Status::NotSupported(
|
||||
"GetNewestUserDefinedTimestamp doesn't support the case when user"
|
||||
"defined timestamps are persisted.");
|
||||
}
|
||||
|
||||
Status status;
|
||||
// Acquire SuperVersion
|
||||
SuperVersion* sv = GetAndRefSuperVersion(cfd);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
bool enter_write_thread = sv->mem == cfd->mem();
|
||||
WriteThread::Writer w;
|
||||
// Enter write thread to read the mutable memtable to avoid racing access
|
||||
// with concurrent writes. No need to enter nonmem_write_thread_ since this
|
||||
// call only care about memtable writes, not WAL writes.
|
||||
if (enter_write_thread) {
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
WaitForPendingWrites();
|
||||
}
|
||||
*newest_timestamp = sv->mem->GetNewestUDT().ToString();
|
||||
assert(!newest_timestamp->empty() || sv->mem->IsEmpty());
|
||||
if (enter_write_thread) {
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
}
|
||||
}
|
||||
// Read from immutable memtables if nothing found in mutable memtable.
|
||||
if (newest_timestamp->empty()) {
|
||||
*newest_timestamp = sv->imm->GetNewestUDT().ToString();
|
||||
}
|
||||
// Read from SST files if no result can be found in memtables.
|
||||
if (newest_timestamp->empty() && sv->current->GetSstFilesSize() != 0) {
|
||||
// full_history_ts_low is used to track the exclusive upperbound of
|
||||
// flushed user defined timestamp. So we can use it to deduce the newest
|
||||
// timestamp in the SST files that the column family has seen.
|
||||
Slice full_history_ts_low = sv->full_history_ts_low;
|
||||
if (!full_history_ts_low.empty()) {
|
||||
GetU64CutoffTsFromFullHistoryTsLow(&full_history_ts_low,
|
||||
newest_timestamp);
|
||||
}
|
||||
}
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
return status;
|
||||
}
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(const ReadOptions& read_options,
|
||||
Arena* arena,
|
||||
SequenceNumber sequence,
|
||||
@@ -1928,10 +1952,10 @@ void DBImpl::BackgroundCallPurge() {
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallPurge:beforeMutexLock");
|
||||
mutex_.Lock();
|
||||
|
||||
while (!logs_to_free_queue_.empty()) {
|
||||
assert(!logs_to_free_queue_.empty());
|
||||
log::Writer* log_writer = *(logs_to_free_queue_.begin());
|
||||
logs_to_free_queue_.pop_front();
|
||||
while (!wals_to_free_queue_.empty()) {
|
||||
assert(!wals_to_free_queue_.empty());
|
||||
log::Writer* log_writer = *(wals_to_free_queue_.begin());
|
||||
wals_to_free_queue_.pop_front();
|
||||
mutex_.Unlock();
|
||||
delete log_writer;
|
||||
mutex_.Lock();
|
||||
@@ -3597,7 +3621,7 @@ Status DBImpl::CreateColumnFamilyImpl(const ReadOptions& read_options,
|
||||
edit.AddColumnFamily(column_family_name);
|
||||
uint32_t new_id = versions_->GetColumnFamilySet()->GetNextColumnFamilyID();
|
||||
edit.SetColumnFamily(new_id);
|
||||
edit.SetLogNumber(logfile_number_);
|
||||
edit.SetLogNumber(cur_wal_number_);
|
||||
edit.SetComparatorName(cf_options.comparator->Name());
|
||||
edit.SetPersistUserDefinedTimestamps(
|
||||
cf_options.persist_user_defined_timestamps);
|
||||
@@ -3796,6 +3820,16 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
|
||||
return s.ok() || s.IsIncomplete();
|
||||
}
|
||||
|
||||
std::unique_ptr<MultiScan> DBImpl::NewMultiScan(
|
||||
const ReadOptions& _read_options, ColumnFamilyHandle* column_family,
|
||||
const std::vector<ScanOptions>& scan_opts) {
|
||||
std::unique_ptr<Iterator> iter(NewIterator(_read_options, column_family));
|
||||
iter->Prepare(scan_opts);
|
||||
std::unique_ptr<MultiScan> ms_iter =
|
||||
std::make_unique<MultiScan>(scan_opts, std::move(iter));
|
||||
return ms_iter;
|
||||
}
|
||||
|
||||
Iterator* DBImpl::NewIterator(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
if (_read_options.io_activity != Env::IOActivity::kUnknown &&
|
||||
@@ -3852,11 +3886,12 @@ Iterator* DBImpl::NewIterator(const ReadOptions& _read_options,
|
||||
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv,
|
||||
/* allow_unprepared_value */ true);
|
||||
result = NewDBIterator(
|
||||
env_, read_options, cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, sv->current, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */, cfh);
|
||||
result = DBIter::NewIter(env_, read_options, cfd->ioptions(),
|
||||
sv->mutable_cf_options, cfd->user_comparator(),
|
||||
iter, sv->current, kMaxSequenceNumber,
|
||||
/*read_callback=*/nullptr, cfh,
|
||||
/*expose_blob_index=*/false,
|
||||
/*active_mem=*/sv->mem);
|
||||
} else {
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
|
||||
@@ -3934,18 +3969,9 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(
|
||||
// Laying out the iterators in the order of being accessed makes it more
|
||||
// likely that any iterator pointer is close to the iterator it points to so
|
||||
// that they are likely to be in the same cache line and/or page.
|
||||
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, cfh->cfd()->ioptions(), sv->mutable_cf_options,
|
||||
sv->current, snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback, cfh, expose_blob_index, allow_refresh);
|
||||
|
||||
InternalIterator* internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfh->cfd(), sv, db_iter->GetArena(), snapshot,
|
||||
/* allow_unprepared_value */ true, db_iter);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
|
||||
return db_iter;
|
||||
return NewArenaWrappedDbIterator(
|
||||
env_, read_options, cfh, sv, snapshot, read_callback, this,
|
||||
expose_blob_index, allow_refresh, /*allow_mark_memtable_for_flush=*/true);
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> DBImpl::NewCoalescingIterator(
|
||||
@@ -4070,13 +4096,11 @@ Status DBImpl::NewIterators(
|
||||
cf_sv_pair.super_version,
|
||||
/* allow_unprepared_value */ true);
|
||||
iterators->push_back(
|
||||
NewDBIterator(env_, read_options, cf_sv_pair.cfd->ioptions(),
|
||||
cf_sv_pair.super_version->mutable_cf_options,
|
||||
cf_sv_pair.cfd->user_comparator(), iter,
|
||||
cf_sv_pair.super_version->current, kMaxSequenceNumber,
|
||||
cf_sv_pair.super_version->mutable_cf_options
|
||||
.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/, cf_sv_pair.cfh));
|
||||
DBIter::NewIter(env_, read_options, cf_sv_pair.cfd->ioptions(),
|
||||
cf_sv_pair.super_version->mutable_cf_options,
|
||||
cf_sv_pair.cfd->user_comparator(), iter,
|
||||
cf_sv_pair.super_version->current, kMaxSequenceNumber,
|
||||
nullptr /*read_callback*/, cf_sv_pair.cfh));
|
||||
}
|
||||
} else {
|
||||
for (const auto& cf_sv_pair : cf_sv_pairs) {
|
||||
@@ -5435,12 +5459,7 @@ Status DBImpl::WriteOptionsFile(const WriteOptions& write_options,
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"Unnable to persist options -- %s", s.ToString().c_str());
|
||||
if (immutable_db_options_.fail_if_options_file_error) {
|
||||
s = Status::IOError("Unable to persist options.", s.ToString().c_str());
|
||||
} else {
|
||||
// Ignore error
|
||||
s = Status::OK();
|
||||
}
|
||||
s = Status::IOError("Unable to persist options.", s.ToString().c_str());
|
||||
}
|
||||
|
||||
// Restore lock if appropriate
|
||||
|
||||
+171
-133
@@ -173,10 +173,10 @@ struct DBOpenLogRecordReadReporter : public log::Reader::Reporter {
|
||||
|
||||
void OldLogRecord(size_t bytes) override;
|
||||
|
||||
uint64_t GetCorruptedLogNumber() const { return corrupted_log_number_; }
|
||||
uint64_t GetCorruptedLogNumber() const { return corrupted_wal_number_; }
|
||||
|
||||
private:
|
||||
uint64_t corrupted_log_number_ = kMaxSequenceNumber;
|
||||
uint64_t corrupted_wal_number_ = kMaxSequenceNumber;
|
||||
};
|
||||
|
||||
// While DB is the public interface of RocksDB, and DBImpl is the actual
|
||||
@@ -256,6 +256,10 @@ class DBImpl : public DB {
|
||||
Status WriteWithCallback(const WriteOptions& options, WriteBatch* updates,
|
||||
UserWriteCallback* user_write_cb) override;
|
||||
|
||||
Status IngestWriteBatchWithIndex(
|
||||
const WriteOptions& options,
|
||||
std::shared_ptr<WriteBatchWithIndex> wbwi) override;
|
||||
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& _read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
@@ -379,6 +383,11 @@ class DBImpl : public DB {
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
|
||||
using DB::NewMultiScan;
|
||||
std::unique_ptr<MultiScan> NewMultiScan(
|
||||
const ReadOptions& _read_options, ColumnFamilyHandle* column_family,
|
||||
const std::vector<ScanOptions>& scan_opts) override;
|
||||
|
||||
const Snapshot* GetSnapshot() override;
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
|
||||
@@ -497,6 +506,9 @@ class DBImpl : public DB {
|
||||
Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string* ts_low) override;
|
||||
|
||||
Status GetNewestUserDefinedTimestamp(ColumnFamilyHandle* column_family,
|
||||
std::string* newest_timestamp) override;
|
||||
|
||||
Status GetDbIdentity(std::string& identity) const override;
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(const IOOptions& opts,
|
||||
@@ -530,11 +542,11 @@ class DBImpl : public DB {
|
||||
|
||||
// Get the known flushed sizes of WALs that might still be written to
|
||||
// or have pending sync.
|
||||
// NOTE: unlike alive_log_files_, this function includes WALs that might
|
||||
// NOTE: unlike alive_wal_files_, this function includes WALs that might
|
||||
// be obsolete (but not obsolete to a pending Checkpoint) and not yet fully
|
||||
// synced.
|
||||
Status GetOpenWalSizes(std::map<uint64_t, uint64_t>& number_to_size);
|
||||
Status GetCurrentWalFile(std::unique_ptr<WalFile>* current_log_file) override;
|
||||
Status GetCurrentWalFile(std::unique_ptr<WalFile>* current_wal_file) override;
|
||||
Status GetCreationTimeOfOldestFile(uint64_t* creation_time) override;
|
||||
|
||||
Status GetUpdatesSince(
|
||||
@@ -1068,7 +1080,7 @@ class DBImpl : public DB {
|
||||
|
||||
void AddToLogsToFreeQueue(log::Writer* log_writer) {
|
||||
mutex_.AssertHeld();
|
||||
logs_to_free_queue_.push_back(log_writer);
|
||||
wals_to_free_queue_.push_back(log_writer);
|
||||
}
|
||||
|
||||
void AddSuperVersionsToFreeQueue(SuperVersion* sv) {
|
||||
@@ -1133,7 +1145,7 @@ class DBImpl : public DB {
|
||||
bool TEST_UnableToReleaseOldestLog() { return unable_to_release_oldest_log_; }
|
||||
|
||||
bool TEST_IsLogGettingFlushed() {
|
||||
return alive_log_files_.begin()->getting_flushed;
|
||||
return alive_wal_files_.begin()->getting_flushed;
|
||||
}
|
||||
|
||||
Status TEST_SwitchMemtable(ColumnFamilyData* cfd = nullptr);
|
||||
@@ -1213,7 +1225,9 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t TEST_LogfileNumber();
|
||||
|
||||
uint64_t TEST_total_log_size() const { return total_log_size_; }
|
||||
uint64_t TEST_wals_total_size() const {
|
||||
return wals_total_size_.LoadRelaxed();
|
||||
}
|
||||
|
||||
void TEST_GetAllBlockCaches(std::unordered_set<const Cache*>* cache_set);
|
||||
|
||||
@@ -1371,16 +1385,16 @@ class DBImpl : public DB {
|
||||
|
||||
// State below is protected by mutex_
|
||||
// With two_write_queues enabled, some of the variables that accessed during
|
||||
// WriteToWAL need different synchronization: log_empty_, alive_log_files_,
|
||||
// logs_, logfile_number_. Refer to the definition of each variable below for
|
||||
// WriteToWAL need different synchronization: wal_empty_, alive_wal_files_,
|
||||
// logs_, cur_wal_number_. Refer to the definition of each variable below for
|
||||
// more description.
|
||||
//
|
||||
// `mutex_` can be a hot lock in some workloads, so it deserves dedicated
|
||||
// cachelines.
|
||||
mutable CacheAlignedInstrumentedMutex mutex_;
|
||||
|
||||
ColumnFamilyHandleImpl* default_cf_handle_;
|
||||
InternalStats* default_cf_internal_stats_;
|
||||
ColumnFamilyHandleImpl* default_cf_handle_ = nullptr;
|
||||
InternalStats* default_cf_internal_stats_ = nullptr;
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
@@ -1392,7 +1406,7 @@ class DBImpl : public DB {
|
||||
|
||||
// only used for dynamically adjusting max_total_wal_size. it is a sum of
|
||||
// [write_buffer_size * max_write_buffer_number] over all column families
|
||||
std::atomic<uint64_t> max_total_in_memory_state_;
|
||||
std::atomic<uint64_t> max_total_in_memory_state_ = 0;
|
||||
|
||||
// The options to access storage files
|
||||
const FileOptions file_options_;
|
||||
@@ -1419,14 +1433,14 @@ class DBImpl : public DB {
|
||||
|
||||
// Each flush or compaction gets its own job id. this counter makes sure
|
||||
// they're unique
|
||||
std::atomic<int> next_job_id_;
|
||||
std::atomic<int> next_job_id_ = 1;
|
||||
|
||||
std::atomic<bool> shutting_down_;
|
||||
std::atomic<bool> shutting_down_ = false;
|
||||
|
||||
// No new background jobs can be queued if true. This is used to prevent new
|
||||
// background jobs from being queued after WaitForCompact() completes waiting
|
||||
// all background jobs then attempts to close when close_db_ option is true.
|
||||
bool reject_new_background_jobs_;
|
||||
bool reject_new_background_jobs_ = false;
|
||||
|
||||
// RecoveryContext struct stores the context about version edits along
|
||||
// with corresponding column_family_data and column_family_options.
|
||||
@@ -1524,11 +1538,11 @@ class DBImpl : public DB {
|
||||
// ingests `wbwi` is done.
|
||||
// @param memtable_updated Whether the same write that ingests wbwi has
|
||||
// updated memtable. This is useful for determining whether to set bg
|
||||
// error when IngestWBWI fails.
|
||||
Status IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
const WBWIMemTable::SeqnoRange& assigned_seqno,
|
||||
uint64_t min_prep_log, SequenceNumber last_seqno,
|
||||
bool memtable_updated, bool ignore_missing_cf);
|
||||
// error when IngestWBWIAsMemtable fails.
|
||||
Status IngestWBWIAsMemtable(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
const WBWIMemTable::SeqnoRange& assigned_seqno,
|
||||
uint64_t min_prep_log, SequenceNumber last_seqno,
|
||||
bool memtable_updated, bool ignore_missing_cf);
|
||||
|
||||
// If disable_memtable is set the application logic must guarantee that the
|
||||
// batch will still be skipped from memtable during the recovery. An excption
|
||||
@@ -1558,18 +1572,17 @@ class DBImpl : public DB {
|
||||
Status WriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
UserWriteCallback* user_write_cb = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
uint64_t* wal_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false, uint64_t* seq_used = nullptr,
|
||||
size_t batch_cnt = 0,
|
||||
PreReleaseCallback* pre_release_callback = nullptr,
|
||||
PostMemTableCallback* post_memtable_callback = nullptr,
|
||||
std::shared_ptr<WriteBatchWithIndex> wbwi = nullptr,
|
||||
uint64_t min_prep_log = 0);
|
||||
std::shared_ptr<WriteBatchWithIndex> wbwi = nullptr);
|
||||
|
||||
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
UserWriteCallback* user_write_cb = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
uint64_t* wal_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false,
|
||||
uint64_t* seq_used = nullptr);
|
||||
|
||||
@@ -1596,7 +1609,7 @@ class DBImpl : public DB {
|
||||
Status WriteImplWALOnly(
|
||||
WriteThread* write_thread, const WriteOptions& options,
|
||||
WriteBatch* updates, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
UserWriteCallback* user_write_cb, uint64_t* wal_used,
|
||||
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
|
||||
const PublishLastSeq publish_last_seq, const bool disable_memtable);
|
||||
@@ -1757,9 +1770,9 @@ class DBImpl : public DB {
|
||||
}
|
||||
};
|
||||
|
||||
struct LogFileNumberSize {
|
||||
explicit LogFileNumberSize(uint64_t _number) : number(_number) {}
|
||||
LogFileNumberSize() {}
|
||||
struct WalFileNumberSize {
|
||||
explicit WalFileNumberSize(uint64_t _number) : number(_number) {}
|
||||
WalFileNumberSize() {}
|
||||
void AddSize(uint64_t new_size) { size += new_size; }
|
||||
uint64_t number;
|
||||
uint64_t size = 0;
|
||||
@@ -1781,6 +1794,13 @@ class DBImpl : public DB {
|
||||
if (writer->file()) {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
s = writer->WriteBuffer(WriteOptions());
|
||||
if (attempt_truncate_size < SIZE_MAX &&
|
||||
attempt_truncate_size < writer->file()->GetFileSize()) {
|
||||
Status s2 = writer->file()->writable_file()->Truncate(
|
||||
attempt_truncate_size, IOOptions{}, nullptr);
|
||||
// This is just a best effort attempt
|
||||
s2.PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
delete writer;
|
||||
writer = nullptr;
|
||||
@@ -1813,6 +1833,11 @@ class DBImpl : public DB {
|
||||
getting_synced = false;
|
||||
}
|
||||
|
||||
void SetAttemptTruncateSize(uint64_t size) {
|
||||
assert(attempt_truncate_size == SIZE_MAX);
|
||||
attempt_truncate_size = size;
|
||||
}
|
||||
|
||||
uint64_t number;
|
||||
// Visual Studio doesn't support deque's member to be noncopyable because
|
||||
// of a std::unique_ptr as a member.
|
||||
@@ -1825,15 +1850,20 @@ class DBImpl : public DB {
|
||||
// to be persisted even if appends happen during sync so it can be used for
|
||||
// tracking the synced size in MANIFEST.
|
||||
uint64_t pre_sync_size = 0;
|
||||
// When < SIZE_MAX, attempt to truncate the WAL to this size on close,
|
||||
// because a bad entry was written to it beyond that point and it likely
|
||||
// won't be recoverable with the bad entry.
|
||||
uint64_t attempt_truncate_size = SIZE_MAX;
|
||||
};
|
||||
|
||||
struct LogContext {
|
||||
explicit LogContext(bool need_sync = false)
|
||||
: need_log_sync(need_sync), need_log_dir_sync(need_sync) {}
|
||||
bool need_log_sync = false;
|
||||
bool need_log_dir_sync = false;
|
||||
struct WalContext {
|
||||
explicit WalContext(bool need_sync = false)
|
||||
: need_wal_sync(need_sync), need_wal_dir_sync(need_sync) {}
|
||||
bool need_wal_sync = false;
|
||||
bool need_wal_dir_sync = false;
|
||||
log::Writer* writer = nullptr;
|
||||
LogFileNumberSize* log_file_number_size = nullptr;
|
||||
WalFileNumberSize* wal_file_number_size = nullptr;
|
||||
uint64_t prev_size = SIZE_MAX;
|
||||
};
|
||||
|
||||
// PurgeFileInfo is a structure to hold information of files to be deleted in
|
||||
@@ -2041,10 +2071,10 @@ class DBImpl : public DB {
|
||||
JobContext* job_context, LogBuffer* log_buffer, Env::Priority thread_pri);
|
||||
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
// corrupted_log_found is set to true if we recover from a corrupted log file.
|
||||
// corrupted_wal_found is set to true if we recover from a corrupted log file.
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only,
|
||||
bool is_retry, bool* corrupted_log_found,
|
||||
bool is_retry, bool* corrupted_wal_found,
|
||||
RecoveryContext* recovery_ctx);
|
||||
|
||||
void SetupLogFilesRecovery(
|
||||
@@ -2152,12 +2182,12 @@ class DBImpl : public DB {
|
||||
// log file to its actual size, thereby freeing preallocated space.
|
||||
// Return success even if truncate fails
|
||||
Status GetLogSizeAndMaybeTruncate(uint64_t wal_number, bool truncate,
|
||||
LogFileNumberSize* log);
|
||||
WalFileNumberSize* log);
|
||||
|
||||
// Restore alive_log_files_ and total_log_size_ after recovery.
|
||||
// Restore alive_wal_files_ and wals_total_size_ after recovery.
|
||||
// It needs to run only when there's no flush during recovery
|
||||
// (e.g. avoid_flush_during_recovery=true). May also trigger flush
|
||||
// in case total_log_size > max_total_wal_size.
|
||||
// in case wals_total_size > max_total_wal_size.
|
||||
Status RestoreAliveLogFiles(const std::vector<uint64_t>& log_numbers);
|
||||
|
||||
// num_bytes: for slowdown case, delay time is calculated based on
|
||||
@@ -2306,7 +2336,7 @@ class DBImpl : public DB {
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status PreprocessWrite(const WriteOptions& write_options,
|
||||
LogContext* log_context, WriteContext* write_context);
|
||||
WalContext* log_context, WriteContext* write_context);
|
||||
|
||||
// Merge write batches in the write group into merged_batch.
|
||||
// Returns OK if merge is successful.
|
||||
@@ -2317,20 +2347,21 @@ class DBImpl : public DB {
|
||||
|
||||
IOStatus WriteToWAL(const WriteBatch& merged_batch,
|
||||
const WriteOptions& write_options,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
log::Writer* log_writer, uint64_t* wal_used,
|
||||
uint64_t* log_size,
|
||||
LogFileNumberSize& log_file_number_size,
|
||||
WalFileNumberSize& wal_file_number_size,
|
||||
SequenceNumber sequence);
|
||||
|
||||
IOStatus WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence,
|
||||
LogFileNumberSize& log_file_number_size);
|
||||
IOStatus WriteGroupToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* wal_used,
|
||||
bool need_wal_sync, bool need_wal_dir_sync,
|
||||
SequenceNumber sequence,
|
||||
WalFileNumberSize& wal_file_number_size);
|
||||
|
||||
IOStatus ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used,
|
||||
SequenceNumber* last_sequence, size_t seq_inc);
|
||||
IOStatus ConcurrentWriteGroupToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* wal_used,
|
||||
SequenceNumber* last_sequence,
|
||||
size_t seq_inc);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
// Caller must hold mutex_.
|
||||
@@ -2344,7 +2375,7 @@ class DBImpl : public DB {
|
||||
void WALIOStatusCheck(const IOStatus& status);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ in case of memtable insert error.
|
||||
void MemTableInsertStatusCheck(const Status& memtable_insert_status);
|
||||
void HandleMemTableInsertFailure(const Status& nonok_memtable_insert_status);
|
||||
|
||||
Status CompactFilesImpl(const CompactionOptions& compact_options,
|
||||
ColumnFamilyData* cfd, Version* version,
|
||||
@@ -2697,7 +2728,7 @@ class DBImpl : public DB {
|
||||
ErrorIteratorFuncType error_iterator_func);
|
||||
|
||||
// Lock over the persistent DB state. Non-nullptr iff successfully acquired.
|
||||
FileLock* db_lock_;
|
||||
FileLock* db_lock_ = nullptr;
|
||||
|
||||
// Guards changes to DB and CF options to ensure consistency between
|
||||
// * In-memory options objects
|
||||
@@ -2711,20 +2742,20 @@ class DBImpl : public DB {
|
||||
// Guards reads and writes to in-memory stats_history_.
|
||||
InstrumentedMutex stats_history_mutex_;
|
||||
|
||||
// In addition to mutex_, log_write_mutex_ protects writes to logs_ and
|
||||
// logfile_number_. With two_write_queues it also protects alive_log_files_,
|
||||
// and log_empty_. Refer to the definition of each variable below for more
|
||||
// In addition to mutex_, wal_write_mutex_ protects writes to logs_ and
|
||||
// cur_wal_number_. With two_write_queues it also protects alive_wal_files_,
|
||||
// and wal_empty_. Refer to the definition of each variable below for more
|
||||
// details.
|
||||
// Note: to avoid deadlock, if needed to acquire both log_write_mutex_ and
|
||||
// mutex_, the order should be first mutex_ and then log_write_mutex_.
|
||||
InstrumentedMutex log_write_mutex_;
|
||||
// Note: to avoid deadlock, if needed to acquire both wal_write_mutex_ and
|
||||
// mutex_, the order should be first mutex_ and then wal_write_mutex_.
|
||||
InstrumentedMutex wal_write_mutex_;
|
||||
|
||||
// If zero, manual compactions are allowed to proceed. If non-zero, manual
|
||||
// compactions may still be running, but will quickly fail with
|
||||
// `Status::Incomplete`. The value indicates how many threads have paused
|
||||
// manual compactions. It is accessed in read mode outside the DB mutex in
|
||||
// compaction code paths.
|
||||
std::atomic<int> manual_compaction_paused_;
|
||||
std::atomic<int> manual_compaction_paused_ = false;
|
||||
|
||||
// This condition variable is signaled on these conditions:
|
||||
// * whenever bg_compaction_scheduled_ goes down to 0
|
||||
@@ -2740,106 +2771,114 @@ class DBImpl : public DB {
|
||||
// * whenever SetOptions successfully updates options.
|
||||
// * whenever a column family is dropped.
|
||||
InstrumentedCondVar bg_cv_;
|
||||
// Writes are protected by locking both mutex_ and log_write_mutex_, and reads
|
||||
// must be under either mutex_ or log_write_mutex_. Since after ::Open,
|
||||
// logfile_number_ is currently updated only in write_thread_, it can be read
|
||||
|
||||
ColumnFamilyHandleImpl* persist_stats_cf_handle_ = nullptr;
|
||||
|
||||
bool persistent_stats_cfd_exists_ = true;
|
||||
|
||||
// Writes are protected by locking both mutex_ and wal_write_mutex_, and reads
|
||||
// must be under either mutex_ or wal_write_mutex_. Since after ::Open,
|
||||
// cur_wal_number_ is currently updated only in write_thread_, it can be read
|
||||
// from the same write_thread_ without any locks.
|
||||
uint64_t logfile_number_;
|
||||
uint64_t cur_wal_number_ = 0;
|
||||
|
||||
// Log files that we can recycle. Must be protected by db mutex_.
|
||||
std::deque<uint64_t> log_recycle_files_;
|
||||
std::deque<uint64_t> wal_recycle_files_;
|
||||
|
||||
// The minimum log file number taht can be recycled, if log recycling is
|
||||
// enabled. This is used to ensure that log files created by previous
|
||||
// instances of the database are not recycled, as we cannot be sure they
|
||||
// were created in the recyclable format.
|
||||
uint64_t min_log_number_to_recycle_;
|
||||
// Protected by log_write_mutex_.
|
||||
bool log_dir_synced_;
|
||||
// Without two_write_queues, read and writes to log_empty_ are protected by
|
||||
uint64_t min_wal_number_to_recycle_ = 0;
|
||||
|
||||
// Protected by wal_write_mutex_.
|
||||
bool wal_dir_synced_ = false;
|
||||
|
||||
// Without two_write_queues, read and writes to wal_empty_ are protected by
|
||||
// mutex_. Since it is currently updated/read only in write_thread_, it can be
|
||||
// accessed from the same write_thread_ without any locks. With
|
||||
// two_write_queues writes, where it can be updated in different threads,
|
||||
// read and writes are protected by log_write_mutex_ instead. This is to avoid
|
||||
// expensive mutex_ lock during WAL write, which update log_empty_.
|
||||
bool log_empty_;
|
||||
|
||||
ColumnFamilyHandleImpl* persist_stats_cf_handle_;
|
||||
|
||||
bool persistent_stats_cfd_exists_ = true;
|
||||
// read and writes are protected by wal_write_mutex_ instead. This is to avoid
|
||||
// expensive mutex_ lock during WAL write, which update wal_empty_.
|
||||
bool wal_empty_ = true;
|
||||
|
||||
// The current WAL file and those that have not been found obsolete from
|
||||
// memtable flushes. A WAL not on this list might still be pending writer
|
||||
// flush and/or sync and close and might still be in logs_. alive_log_files_
|
||||
// is protected by mutex_ and log_write_mutex_ with details as follows:
|
||||
// flush and/or sync and close and might still be in logs_. alive_wal_files_
|
||||
// is protected by mutex_ and wal_write_mutex_ with details as follows:
|
||||
// 1. read by FindObsoleteFiles() which can be called in either application
|
||||
// thread or RocksDB bg threads, both mutex_ and log_write_mutex_ are
|
||||
// thread or RocksDB bg threads, both mutex_ and wal_write_mutex_ are
|
||||
// held.
|
||||
// 2. pop_front() by FindObsoleteFiles(), both mutex_ and log_write_mutex_
|
||||
// 2. pop_front() by FindObsoleteFiles(), both mutex_ and wal_write_mutex_
|
||||
// are held.
|
||||
// 3. push_back() by DBImpl::Open() and DBImpl::RestoreAliveLogFiles()
|
||||
// (actually called by Open()), only mutex_ is held because at this point,
|
||||
// the DB::Open() call has not returned success to application, and the
|
||||
// only other thread(s) that can conflict are bg threads calling
|
||||
// FindObsoleteFiles() which ensure that both mutex_ and log_write_mutex_
|
||||
// are held when accessing alive_log_files_.
|
||||
// FindObsoleteFiles() which ensure that both mutex_ and wal_write_mutex_
|
||||
// are held when accessing alive_wal_files_.
|
||||
// 4. read by DBImpl::Open() is protected by mutex_.
|
||||
// 5. push_back() by SwitchMemtable(). Both mutex_ and log_write_mutex_ are
|
||||
// 5. push_back() by SwitchMemtable(). Both mutex_ and wal_write_mutex_ are
|
||||
// held. This is done by the write group leader. Note that in the case of
|
||||
// two-write-queues, another WAL-only write thread can be writing to the
|
||||
// WAL concurrently. See 9.
|
||||
// 6. read by SwitchWAL() with both mutex_ and log_write_mutex_ held. This is
|
||||
// 6. read by SwitchWAL() with both mutex_ and wal_write_mutex_ held. This is
|
||||
// done by write group leader.
|
||||
// 7. read by ConcurrentWriteToWAL() by the write group leader in the case of
|
||||
// two-write-queues. Only log_write_mutex_ is held to protect concurrent
|
||||
// two-write-queues. Only wal_write_mutex_ is held to protect concurrent
|
||||
// pop_front() by FindObsoleteFiles().
|
||||
// 8. read by PreprocessWrite() by the write group leader. log_write_mutex_
|
||||
// 8. read by PreprocessWrite() by the write group leader. wal_write_mutex_
|
||||
// is held to protect the data structure from concurrent pop_front() by
|
||||
// FindObsoleteFiles().
|
||||
// 9. read by ConcurrentWriteToWAL() by a WAL-only write thread in the case
|
||||
// of two-write-queues. Only log_write_mutex_ is held. This suffices to
|
||||
// of two-write-queues. Only wal_write_mutex_ is held. This suffices to
|
||||
// protect the data structure from concurrent push_back() by current
|
||||
// write group leader as well as pop_front() by FindObsoleteFiles().
|
||||
std::deque<LogFileNumberSize> alive_log_files_;
|
||||
std::deque<WalFileNumberSize> alive_wal_files_;
|
||||
|
||||
// Total size of all "alive" WALs (for easy access without synchronization)
|
||||
RelaxedAtomic<uint64_t> wals_total_size_{0};
|
||||
|
||||
// Log files that aren't fully synced, and the current log file.
|
||||
// Synchronization:
|
||||
// 1. read by FindObsoleteFiles() which can be called either in application
|
||||
// thread or RocksDB bg threads. log_write_mutex_ is always held, while
|
||||
// thread or RocksDB bg threads. wal_write_mutex_ is always held, while
|
||||
// some reads are performed without mutex_.
|
||||
// 2. pop_front() by FindObsoleteFiles() with only log_write_mutex_ held.
|
||||
// 3. read by DBImpl::Open() with both mutex_ and log_write_mutex_.
|
||||
// 4. emplace_back() by DBImpl::Open() with both mutex_ and log_write_mutex.
|
||||
// 2. pop_front() by FindObsoleteFiles() with only wal_write_mutex_ held.
|
||||
// 3. read by DBImpl::Open() with both mutex_ and wal_write_mutex_.
|
||||
// 4. emplace_back() by DBImpl::Open() with both mutex_ and wal_write_mutex.
|
||||
// Note that at this point, DB::Open() has not returned success to
|
||||
// application, thus the only other thread(s) that can conflict are bg
|
||||
// threads calling FindObsoleteFiles(). See 1.
|
||||
// 5. iteration and clear() from CloseHelper() always hold log_write_mutex
|
||||
// 5. iteration and clear() from CloseHelper() always hold wal_write_mutex
|
||||
// and mutex_.
|
||||
// 6. back() called by APIs FlushWAL() and LockWAL() are protected by only
|
||||
// log_write_mutex_. These two can be called by application threads after
|
||||
// wal_write_mutex_. These two can be called by application threads after
|
||||
// DB::Open() returns success to applications.
|
||||
// 7. read by SyncWAL(), another API, protected by only log_write_mutex_.
|
||||
// 7. read by SyncWAL(), another API, protected by only wal_write_mutex_.
|
||||
// 8. read by MarkLogsNotSynced() and MarkLogsSynced() are protected by
|
||||
// log_write_mutex_.
|
||||
// 9. erase() by MarkLogsSynced() protected by log_write_mutex_.
|
||||
// 10. read by SyncClosedWals() protected by only log_write_mutex_. This can
|
||||
// wal_write_mutex_.
|
||||
// 9. erase() by MarkLogsSynced() protected by wal_write_mutex_.
|
||||
// 10. read by SyncClosedWals() protected by only wal_write_mutex_. This can
|
||||
// happen in bg flush threads after DB::Open() returns success to
|
||||
// applications.
|
||||
// 11. reads, e.g. front(), iteration, and back() called by PreprocessWrite()
|
||||
// holds only the log_write_mutex_. This is done by the write group
|
||||
// holds only the wal_write_mutex_. This is done by the write group
|
||||
// leader. A bg thread calling FindObsoleteFiles() or MarkLogsSynced()
|
||||
// can happen concurrently. This is fine because log_write_mutex_ is used
|
||||
// can happen concurrently. This is fine because wal_write_mutex_ is used
|
||||
// by all parties. See 2, 5, 9.
|
||||
// 12. reads, empty(), back() called by SwitchMemtable() hold both mutex_ and
|
||||
// log_write_mutex_. This happens in the write group leader.
|
||||
// wal_write_mutex_. This happens in the write group leader.
|
||||
// 13. emplace_back() by SwitchMemtable() hold both mutex_ and
|
||||
// log_write_mutex_. This happens in the write group leader. Can conflict
|
||||
// wal_write_mutex_. This happens in the write group leader. Can conflict
|
||||
// with bg threads calling FindObsoleteFiles(), MarkLogsSynced(),
|
||||
// SyncClosedWals(), etc. as well as application threads calling
|
||||
// FlushWAL(), SyncWAL(), LockWAL(). This is fine because all parties
|
||||
// require at least log_write_mutex_.
|
||||
// require at least wal_write_mutex_.
|
||||
// 14. iteration called in WriteToWAL(write_group) protected by
|
||||
// log_write_mutex_. This is done by write group leader when
|
||||
// wal_write_mutex_. This is done by write group leader when
|
||||
// two-write-queues is disabled and write needs to sync logs.
|
||||
// 15. back() called in ConcurrentWriteToWAL() protected by log_write_mutex_.
|
||||
// 15. back() called in ConcurrentWriteToWAL() protected by wal_write_mutex_.
|
||||
// This can be done by the write group leader if two-write-queues is
|
||||
// enabled. It can also be done by another WAL-only write thread.
|
||||
//
|
||||
@@ -2856,23 +2895,22 @@ class DBImpl : public DB {
|
||||
std::deque<LogWriterNumber> logs_;
|
||||
|
||||
// Signaled when getting_synced becomes false for some of the logs_.
|
||||
InstrumentedCondVar log_sync_cv_;
|
||||
InstrumentedCondVar wal_sync_cv_;
|
||||
// This is the app-level state that is written to the WAL but will be used
|
||||
// only during recovery. Using this feature enables not writing the state to
|
||||
// memtable on normal writes and hence improving the throughput. Each new
|
||||
// write of the state will replace the previous state entirely even if the
|
||||
// keys in the two consecutive states do not overlap.
|
||||
// It is protected by log_write_mutex_ when two_write_queues_ is enabled.
|
||||
// It is protected by wal_write_mutex_ when two_write_queues_ is enabled.
|
||||
// Otherwise only the heaad of write_thread_ can access it.
|
||||
WriteBatch cached_recoverable_state_;
|
||||
std::atomic<bool> cached_recoverable_state_empty_ = {true};
|
||||
std::atomic<uint64_t> total_log_size_;
|
||||
|
||||
// If this is non-empty, we need to delete these log files in background
|
||||
// threads. Protected by log_write_mutex_.
|
||||
autovector<log::Writer*> logs_to_free_;
|
||||
// threads. Protected by wal_write_mutex_.
|
||||
autovector<log::Writer*> wals_to_free_;
|
||||
|
||||
bool is_snapshot_supported_;
|
||||
bool is_snapshot_supported_ = true;
|
||||
|
||||
std::map<uint64_t, std::map<std::string, uint64_t>> stats_history_;
|
||||
|
||||
@@ -2896,7 +2934,7 @@ class DBImpl : public DB {
|
||||
// sleep if it uses up the quota.
|
||||
// Note: This is to protect memtable and compaction. If the batch only writes
|
||||
// to the WAL its size need not to be included in this.
|
||||
uint64_t last_batch_group_size_;
|
||||
uint64_t last_batch_group_size_ = 0;
|
||||
|
||||
FlushScheduler flush_scheduler_;
|
||||
|
||||
@@ -2955,32 +2993,32 @@ class DBImpl : public DB {
|
||||
std::unordered_set<uint64_t> files_grabbed_for_purge_;
|
||||
|
||||
// A queue to store log writers to close. Protected by db mutex_.
|
||||
std::deque<log::Writer*> logs_to_free_queue_;
|
||||
std::deque<log::Writer*> wals_to_free_queue_;
|
||||
|
||||
std::deque<SuperVersion*> superversions_to_free_queue_;
|
||||
|
||||
int unscheduled_flushes_;
|
||||
int unscheduled_flushes_ = 0;
|
||||
|
||||
int unscheduled_compactions_;
|
||||
int unscheduled_compactions_ = 0;
|
||||
|
||||
// count how many background compactions are running or have been scheduled in
|
||||
// the BOTTOM pool
|
||||
int bg_bottom_compaction_scheduled_;
|
||||
int bg_bottom_compaction_scheduled_ = 0;
|
||||
|
||||
// count how many background compactions are running or have been scheduled
|
||||
int bg_compaction_scheduled_;
|
||||
int bg_compaction_scheduled_ = 0;
|
||||
|
||||
// stores the number of compactions are currently running
|
||||
int num_running_compactions_;
|
||||
int num_running_compactions_ = 0;
|
||||
|
||||
// number of background memtable flush jobs, submitted to the HIGH pool
|
||||
int bg_flush_scheduled_;
|
||||
int bg_flush_scheduled_ = 0;
|
||||
|
||||
// stores the number of flushes are currently running
|
||||
int num_running_flushes_;
|
||||
int num_running_flushes_ = 0;
|
||||
|
||||
// number of background obsolete file purge jobs, submitted to the HIGH pool
|
||||
int bg_purge_scheduled_;
|
||||
int bg_purge_scheduled_ = 0;
|
||||
|
||||
std::deque<ManualCompactionState*> manual_compaction_dequeue_;
|
||||
|
||||
@@ -2990,11 +3028,11 @@ class DBImpl : public DB {
|
||||
// This enables two different threads to call
|
||||
// EnableFileDeletions() and DisableFileDeletions()
|
||||
// without any synchronization
|
||||
int disable_delete_obsolete_files_;
|
||||
int disable_delete_obsolete_files_ = 0;
|
||||
|
||||
// Number of times FindObsoleteFiles has found deletable files and the
|
||||
// corresponding call to PurgeObsoleteFiles has not yet finished.
|
||||
int pending_purge_obsolete_files_;
|
||||
int pending_purge_obsolete_files_ = 0;
|
||||
|
||||
// last time when DeleteObsoleteFiles with full scan was executed. Originally
|
||||
// initialized with startup time.
|
||||
@@ -3006,12 +3044,12 @@ class DBImpl : public DB {
|
||||
// The mutex used by switch_cv_. mutex_ should be acquired beforehand.
|
||||
std::mutex switch_mutex_;
|
||||
// Number of threads intending to write to memtable
|
||||
std::atomic<size_t> pending_memtable_writes_ = {};
|
||||
std::atomic<size_t> pending_memtable_writes_{0};
|
||||
|
||||
// A flag indicating whether the current rocksdb database has any
|
||||
// data that is not yet persisted into either WAL or SST file.
|
||||
// Used when disableWAL is true.
|
||||
std::atomic<bool> has_unpersisted_data_;
|
||||
std::atomic<bool> has_unpersisted_data_{false};
|
||||
|
||||
// if an attempt was made to flush all column families that
|
||||
// the oldest log depends on but uncommitted data in the oldest
|
||||
@@ -3019,26 +3057,26 @@ class DBImpl : public DB {
|
||||
// We must attempt to free the dependent memtables again
|
||||
// at a later time after the transaction in the oldest
|
||||
// log is fully commited.
|
||||
bool unable_to_release_oldest_log_;
|
||||
bool unable_to_release_oldest_log_{false};
|
||||
|
||||
// Number of running IngestExternalFile() or CreateColumnFamilyWithImport()
|
||||
// calls.
|
||||
// REQUIRES: mutex held
|
||||
int num_running_ingest_file_;
|
||||
int num_running_ingest_file_ = 0;
|
||||
|
||||
WalManager wal_manager_;
|
||||
|
||||
// A value of > 0 temporarily disables scheduling of background work
|
||||
int bg_work_paused_;
|
||||
int bg_work_paused_ = 0;
|
||||
|
||||
// A value of > 0 temporarily disables scheduling of background compaction
|
||||
int bg_compaction_paused_;
|
||||
int bg_compaction_paused_ = 0;
|
||||
|
||||
// Guard against multiple concurrent refitting
|
||||
bool refitting_level_;
|
||||
bool refitting_level_ = false;
|
||||
|
||||
// Indicate DB was opened successfully
|
||||
bool opened_successfully_;
|
||||
bool opened_successfully_ = false;
|
||||
|
||||
// The min threshold to triggere bottommost compaction for removing
|
||||
// garbages, among all column families.
|
||||
@@ -3084,13 +3122,13 @@ class DBImpl : public DB {
|
||||
// error recovery from going on in parallel. The latter, shutting_down_,
|
||||
// is set a little later during the shutdown after scheduling memtable
|
||||
// flushes
|
||||
std::atomic<bool> shutdown_initiated_;
|
||||
std::atomic<bool> shutdown_initiated_{false};
|
||||
// Flag to indicate whether sst_file_manager object was allocated in
|
||||
// DB::Open() or passed to us
|
||||
bool own_sfm_;
|
||||
|
||||
// Flag to check whether Close() has been called on this DB
|
||||
bool closed_;
|
||||
bool closed_ = false;
|
||||
// save the closing status, for re-calling the close()
|
||||
Status closing_status_;
|
||||
// mutex for DB::Close()
|
||||
@@ -3126,7 +3164,7 @@ class DBImpl : public DB {
|
||||
|
||||
// The number of LockWAL called without matching UnlockWAL call.
|
||||
// See also lock_wal_write_token_
|
||||
uint32_t lock_wal_count_;
|
||||
uint32_t lock_wal_count_ = 0;
|
||||
};
|
||||
|
||||
class GetWithTimestampReadCallback : public ReadCallback {
|
||||
|
||||
@@ -168,7 +168,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// had not been committed yet. Make sure we sync them to keep the persisted
|
||||
// WAL state at least as new as the persisted SST state.
|
||||
const bool needs_to_sync_closed_wals =
|
||||
logfile_number_ > 0 &&
|
||||
cur_wal_number_ > 0 &&
|
||||
(versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1 ||
|
||||
allow_2pc());
|
||||
|
||||
@@ -224,7 +224,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
bool need_cancel = false;
|
||||
IOStatus log_io_s = IOStatus::OK();
|
||||
if (needs_to_sync_closed_wals) {
|
||||
// SyncClosedWals() may unlock and re-lock the log_write_mutex multiple
|
||||
// SyncClosedWals() may unlock and re-lock the wal_write_mutex multiple
|
||||
// times.
|
||||
VersionEdit synced_wals;
|
||||
bool error_recovery_in_prog = error_handler_.IsRecoveryInProgress();
|
||||
@@ -512,7 +512,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
job_context->job_id, flush_reason);
|
||||
}
|
||||
|
||||
if (logfile_number_ > 0) {
|
||||
if (cur_wal_number_ > 0) {
|
||||
// TODO (yanqin) investigate whether we should sync the closed logs for
|
||||
// single column family case.
|
||||
VersionEdit synced_wals;
|
||||
@@ -528,7 +528,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
if (!log_io_s.ok() && !log_io_s.IsShutdownInProgress() &&
|
||||
!log_io_s.IsColumnFamilyDropped()) {
|
||||
if (total_log_size_ > 0) {
|
||||
if (wals_total_size_.LoadRelaxed() > 0) {
|
||||
error_handler_.SetBGError(log_io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
|
||||
@@ -224,13 +224,13 @@ void DBImpl::TEST_EndWrite(void* w) {
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_LogsToFreeSize() {
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
return logs_to_free_.size();
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
return wals_to_free_.size();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_LogfileNumber() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return logfile_number_;
|
||||
return cur_wal_number_;
|
||||
}
|
||||
|
||||
void DBImpl::TEST_GetAllBlockCaches(
|
||||
|
||||
+35
-35
@@ -28,7 +28,7 @@ uint64_t DBImpl::MinLogNumberToKeep() {
|
||||
return versions_->min_log_number_to_keep();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::MinLogNumberToRecycle() { return min_log_number_to_recycle_; }
|
||||
uint64_t DBImpl::MinLogNumberToRecycle() { return min_wal_number_to_recycle_; }
|
||||
|
||||
uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
mutex_.AssertHeld();
|
||||
@@ -272,77 +272,77 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
|
||||
// logs_ is empty when called during recovery, in which case there can't yet
|
||||
// be any tracked obsolete logs
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
|
||||
if (alive_log_files_.empty() || logs_.empty()) {
|
||||
if (alive_wal_files_.empty() || logs_.empty()) {
|
||||
mutex_.AssertHeld();
|
||||
// We may reach here if the db is DBImplSecondary
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
bool mutex_unlocked = false;
|
||||
if (!alive_log_files_.empty() && !logs_.empty()) {
|
||||
if (!alive_wal_files_.empty() && !logs_.empty()) {
|
||||
uint64_t min_log_number = job_context->log_number;
|
||||
size_t num_alive_log_files = alive_log_files_.size();
|
||||
size_t num_alive_wal_files = alive_wal_files_.size();
|
||||
// find newly obsoleted log files
|
||||
while (alive_log_files_.begin()->number < min_log_number) {
|
||||
auto& earliest = *alive_log_files_.begin();
|
||||
while (alive_wal_files_.begin()->number < min_log_number) {
|
||||
auto& earliest = *alive_wal_files_.begin();
|
||||
if (immutable_db_options_.recycle_log_file_num >
|
||||
log_recycle_files_.size() &&
|
||||
wal_recycle_files_.size() &&
|
||||
earliest.number >= MinLogNumberToRecycle()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"adding log %" PRIu64 " to recycle list\n",
|
||||
earliest.number);
|
||||
log_recycle_files_.push_back(earliest.number);
|
||||
wal_recycle_files_.push_back(earliest.number);
|
||||
} else {
|
||||
job_context->log_delete_files.push_back(earliest.number);
|
||||
}
|
||||
if (job_context->size_log_to_delete == 0) {
|
||||
job_context->prev_total_log_size = total_log_size_;
|
||||
job_context->num_alive_log_files = num_alive_log_files;
|
||||
job_context->prev_wals_total_size = wals_total_size_.LoadRelaxed();
|
||||
job_context->num_alive_wal_files = num_alive_wal_files;
|
||||
}
|
||||
job_context->size_log_to_delete += earliest.size;
|
||||
total_log_size_ -= earliest.size;
|
||||
alive_log_files_.pop_front();
|
||||
wals_total_size_.FetchSubRelaxed(earliest.size);
|
||||
alive_wal_files_.pop_front();
|
||||
|
||||
// Current log should always stay alive since it can't have
|
||||
// number < MinLogNumber().
|
||||
assert(alive_log_files_.size());
|
||||
assert(alive_wal_files_.size());
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
mutex_.Unlock();
|
||||
mutex_unlocked = true;
|
||||
TEST_SYNC_POINT_CALLBACK("FindObsoleteFiles::PostMutexUnlock", nullptr);
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
while (!logs_.empty() && logs_.front().number < min_log_number) {
|
||||
auto& log = logs_.front();
|
||||
if (log.IsSyncing()) {
|
||||
log_sync_cv_.Wait();
|
||||
wal_sync_cv_.Wait();
|
||||
// logs_ could have changed while we were waiting.
|
||||
continue;
|
||||
}
|
||||
// This WAL file is not live, so it's OK if we never sync the rest of it.
|
||||
// If it's already closed, then it's been fully synced. If
|
||||
// !background_close_inactive_wals then we need to Close it before
|
||||
// removing from logs_ but not blocking while holding log_write_mutex_.
|
||||
// removing from logs_ but not blocking while holding wal_write_mutex_.
|
||||
if (!immutable_db_options_.background_close_inactive_wals &&
|
||||
log.writer->file()) {
|
||||
// We are taking ownership of and pinning the front entry, so we can
|
||||
// expect it to be the same after releasing and re-acquiring the lock
|
||||
log.PrepareForSync();
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
// TODO: maybe check the return value of Close.
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
auto s = log.writer->file()->Close({});
|
||||
s.PermitUncheckedError();
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
log.writer->PublishIfClosed();
|
||||
assert(&log == &logs_.front());
|
||||
log.FinishSync();
|
||||
log_sync_cv_.SignalAll();
|
||||
wal_sync_cv_.SignalAll();
|
||||
}
|
||||
logs_to_free_.push_back(log.ReleaseWriter());
|
||||
wals_to_free_.push_back(log.ReleaseWriter());
|
||||
logs_.pop_front();
|
||||
}
|
||||
// Current log cannot be obsolete.
|
||||
@@ -350,16 +350,16 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
|
||||
// We're just cleaning up for DB::Write().
|
||||
assert(job_context->logs_to_free.empty());
|
||||
job_context->logs_to_free = logs_to_free_;
|
||||
assert(job_context->wals_to_free.empty());
|
||||
job_context->wals_to_free = wals_to_free_;
|
||||
|
||||
logs_to_free_.clear();
|
||||
log_write_mutex_.Unlock();
|
||||
wals_to_free_.clear();
|
||||
wal_write_mutex_.Unlock();
|
||||
if (mutex_unlocked) {
|
||||
mutex_.Lock();
|
||||
}
|
||||
job_context->log_recycle_files.assign(log_recycle_files_.begin(),
|
||||
log_recycle_files_.end());
|
||||
job_context->log_recycle_files.assign(wal_recycle_files_.begin(),
|
||||
wal_recycle_files_.end());
|
||||
}
|
||||
|
||||
// Delete obsolete files and log status and information of file deletion
|
||||
@@ -431,7 +431,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
state.sst_live.end());
|
||||
std::unordered_set<uint64_t> blob_live_set(state.blob_live.begin(),
|
||||
state.blob_live.end());
|
||||
std::unordered_set<uint64_t> log_recycle_files_set(
|
||||
std::unordered_set<uint64_t> wal_recycle_files_set(
|
||||
state.log_recycle_files.begin(), state.log_recycle_files.end());
|
||||
std::unordered_set<uint64_t> quarantine_files_set(
|
||||
state.files_to_quarantine.begin(), state.files_to_quarantine.end());
|
||||
@@ -491,13 +491,13 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
std::unique(candidate_files.begin(), candidate_files.end()),
|
||||
candidate_files.end());
|
||||
|
||||
if (state.prev_total_log_size > 0) {
|
||||
if (state.prev_wals_total_size > 0) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Try to delete WAL files size %" PRIu64
|
||||
", prev total WAL file size %" PRIu64
|
||||
", number of live WAL files %" ROCKSDB_PRIszt ".\n",
|
||||
state.job_id, state.size_log_to_delete,
|
||||
state.prev_total_log_size, state.num_alive_log_files);
|
||||
state.prev_wals_total_size, state.num_alive_wal_files);
|
||||
}
|
||||
|
||||
std::vector<std::string> old_info_log_files;
|
||||
@@ -532,7 +532,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
optsfile_num2 = std::min(optsfile_num2, state.min_options_file_number);
|
||||
|
||||
// Close WALs before trying to delete them.
|
||||
for (const auto w : state.logs_to_free) {
|
||||
for (const auto w : state.wals_to_free) {
|
||||
// TODO: maybe check the return value of Close.
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
auto s = w->Close({});
|
||||
@@ -559,8 +559,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
case kWalFile:
|
||||
keep = ((number >= state.log_number) ||
|
||||
(number == state.prev_log_number) ||
|
||||
(log_recycle_files_set.find(number) !=
|
||||
log_recycle_files_set.end()));
|
||||
(wal_recycle_files_set.find(number) !=
|
||||
wal_recycle_files_set.end()));
|
||||
break;
|
||||
case kDescriptorFile:
|
||||
// Keep my manifest file, and any newer incarnations'
|
||||
|
||||
+18
-18
@@ -1119,7 +1119,7 @@ void DBOpenLogRecordReadReporter::Corruption(size_t bytes, const Status& s,
|
||||
static_cast<int>(bytes), s.ToString().c_str());
|
||||
if (status != nullptr && status->ok()) {
|
||||
*status = s;
|
||||
corrupted_log_number_ = log_number;
|
||||
corrupted_wal_number_ = log_number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1902,8 +1902,8 @@ void DBImpl::FinishLogFilesRecovery(int job_id, const Status& status) {
|
||||
}
|
||||
|
||||
Status DBImpl::GetLogSizeAndMaybeTruncate(uint64_t wal_number, bool truncate,
|
||||
LogFileNumberSize* log_ptr) {
|
||||
LogFileNumberSize log(wal_number);
|
||||
WalFileNumberSize* log_ptr) {
|
||||
WalFileNumberSize log(wal_number);
|
||||
std::string fname =
|
||||
LogFileName(immutable_db_options_.GetWalDir(), wal_number);
|
||||
Status s;
|
||||
@@ -1946,27 +1946,27 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
assert(immutable_db_options_.avoid_flush_during_recovery);
|
||||
// Mark these as alive so they'll be considered for deletion later by
|
||||
// FindObsoleteFiles()
|
||||
total_log_size_ = 0;
|
||||
log_empty_ = false;
|
||||
wals_total_size_.StoreRelaxed(0);
|
||||
wal_empty_ = false;
|
||||
uint64_t min_wal_with_unflushed_data =
|
||||
versions_->MinLogNumberWithUnflushedData();
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (!allow_2pc() && wal_number < min_wal_with_unflushed_data) {
|
||||
// In non-2pc mode, the WAL files not backing unflushed data are not
|
||||
// alive, thus should not be added to the alive_log_files_.
|
||||
// alive, thus should not be added to the alive_wal_files_.
|
||||
continue;
|
||||
}
|
||||
// We preallocate space for wals, but then after a crash and restart, those
|
||||
// preallocated space are not needed anymore. It is likely only the last
|
||||
// log has such preallocated space, so we only truncate for the last log.
|
||||
LogFileNumberSize log;
|
||||
WalFileNumberSize log;
|
||||
s = GetLogSizeAndMaybeTruncate(
|
||||
wal_number, /*truncate=*/(wal_number == wal_numbers.back()), &log);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
total_log_size_ += log.size;
|
||||
alive_log_files_.push_back(log);
|
||||
wals_total_size_.FetchAddRelaxed(log.size);
|
||||
alive_wal_files_.push_back(log);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -2449,18 +2449,18 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
if (s.ok()) {
|
||||
// Prevent log files created by previous instance from being recycled.
|
||||
// They might be in alive_log_file_, and might get recycled otherwise.
|
||||
impl->min_log_number_to_recycle_ = new_log_number;
|
||||
impl->min_wal_number_to_recycle_ = new_log_number;
|
||||
}
|
||||
if (s.ok()) {
|
||||
InstrumentedMutexLock wl(&impl->log_write_mutex_);
|
||||
impl->logfile_number_ = new_log_number;
|
||||
InstrumentedMutexLock wl(&impl->wal_write_mutex_);
|
||||
impl->cur_wal_number_ = new_log_number;
|
||||
assert(new_log != nullptr);
|
||||
assert(impl->logs_.empty());
|
||||
impl->logs_.emplace_back(new_log_number, new_log);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
impl->alive_log_files_.emplace_back(impl->logfile_number_);
|
||||
impl->alive_wal_files_.emplace_back(impl->cur_wal_number_);
|
||||
// In WritePrepared there could be gap in sequence numbers. This breaks
|
||||
// the trick we use in kPointInTimeRecovery which assumes the first seq in
|
||||
// the log right after the corrupted log is one larger than the last seq
|
||||
@@ -2473,14 +2473,14 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
if (recovered_seq != kMaxSequenceNumber) {
|
||||
WriteBatch empty_batch;
|
||||
WriteBatchInternal::SetSequence(&empty_batch, recovered_seq);
|
||||
uint64_t log_used, log_size;
|
||||
uint64_t wal_used, log_size;
|
||||
log::Writer* log_writer = impl->logs_.back().writer;
|
||||
LogFileNumberSize& log_file_number_size = impl->alive_log_files_.back();
|
||||
WalFileNumberSize& wal_file_number_size = impl->alive_wal_files_.back();
|
||||
|
||||
assert(log_writer->get_log_number() == log_file_number_size.number);
|
||||
assert(log_writer->get_log_number() == wal_file_number_size.number);
|
||||
impl->mutex_.AssertHeld();
|
||||
s = impl->WriteToWAL(empty_batch, write_options, log_writer, &log_used,
|
||||
&log_size, log_file_number_size, recovered_seq);
|
||||
s = impl->WriteToWAL(empty_batch, write_options, log_writer, &wal_used,
|
||||
&log_size, wal_file_number_size, recovered_seq);
|
||||
if (s.ok()) {
|
||||
// Need to fsync, otherwise it might get lost after a power reset.
|
||||
s = impl->FlushWAL(write_options, false);
|
||||
|
||||
@@ -185,16 +185,10 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& _read_options,
|
||||
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
|
||||
: latest_snapshot;
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, cfd->ioptions(), super_version->mutable_cf_options,
|
||||
super_version->current, read_seq,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfd, super_version, db_iter->GetArena(),
|
||||
read_seq, /* allow_unprepared_value */ true, db_iter);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
return NewArenaWrappedDbIterator(
|
||||
env_, read_options, cfh, super_version, read_seq, read_callback, this,
|
||||
/*expose_blob_index=*/false, /*allow_refresh=*/false,
|
||||
/*allow_mark_memtable_for_flush=*/false);
|
||||
}
|
||||
|
||||
Status DBImplReadOnly::NewIterators(
|
||||
@@ -231,36 +225,32 @@ Status DBImplReadOnly::NewIterators(
|
||||
? static_cast<const SnapshotImpl*>(read_options.snapshot)->number_
|
||||
: latest_snapshot;
|
||||
|
||||
autovector<std::tuple<ColumnFamilyData*, SuperVersion*>> cfd_to_sv;
|
||||
autovector<std::tuple<ColumnFamilyHandleImpl*, SuperVersion*>> cfh_to_sv;
|
||||
|
||||
const bool check_read_ts =
|
||||
read_options.timestamp && read_options.timestamp->size() > 0;
|
||||
for (auto cfh : column_families) {
|
||||
auto* cfd = static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
|
||||
auto* sv = cfd->GetSuperVersion()->Ref();
|
||||
cfd_to_sv.emplace_back(cfd, sv);
|
||||
cfh_to_sv.emplace_back(static_cast_with_check<ColumnFamilyHandleImpl>(cfh),
|
||||
sv);
|
||||
if (check_read_ts) {
|
||||
const Status s =
|
||||
FailIfReadCollapsedHistory(cfd, sv, *(read_options.timestamp));
|
||||
if (!s.ok()) {
|
||||
for (auto prev_entry : cfd_to_sv) {
|
||||
for (auto prev_entry : cfh_to_sv) {
|
||||
std::get<1>(prev_entry)->Unref();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(cfd_to_sv.size() == column_families.size());
|
||||
for (auto [cfd, sv] : cfd_to_sv) {
|
||||
assert(cfh_to_sv.size() == column_families.size());
|
||||
for (auto [cfh, sv] : cfh_to_sv) {
|
||||
auto* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, cfd->ioptions(), sv->mutable_cf_options,
|
||||
sv->current, read_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback);
|
||||
auto* internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfd, sv, db_iter->GetArena(), read_seq,
|
||||
/* allow_unprepared_value */ true, db_iter);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
env_, read_options, cfh, sv, read_seq, read_callback, this,
|
||||
/*expose_blob_index=*/false, /*allow_refresh=*/false,
|
||||
/*allow_mark_memtable_for_flush=*/false);
|
||||
iterators->push_back(db_iter);
|
||||
}
|
||||
|
||||
|
||||
@@ -566,17 +566,10 @@ ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
|
||||
assert(snapshot == kMaxSequenceNumber);
|
||||
snapshot = versions_->LastSequence();
|
||||
assert(snapshot != kMaxSequenceNumber);
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, cfh->cfd()->ioptions(),
|
||||
super_version->mutable_cf_options, super_version->current, snapshot,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback, cfh, expose_blob_index,
|
||||
allow_refresh);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
db_iter->GetReadOptions(), cfh->cfd(), super_version, db_iter->GetArena(),
|
||||
snapshot, /* allow_unprepared_value */ true, db_iter);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
return NewArenaWrappedDbIterator(env_, read_options, cfh, super_version,
|
||||
snapshot, read_callback, this,
|
||||
expose_blob_index, allow_refresh,
|
||||
/*allow_mark_memtable_for_flush=*/false);
|
||||
}
|
||||
|
||||
Status DBImplSecondary::NewIterators(
|
||||
@@ -705,13 +698,13 @@ Status DBImplSecondary::CheckConsistency() {
|
||||
|
||||
Status DBImplSecondary::TryCatchUpWithPrimary() {
|
||||
assert(versions_.get() != nullptr);
|
||||
assert(manifest_reader_.get() != nullptr);
|
||||
Status s;
|
||||
// read the manifest and apply new changes to the secondary instance
|
||||
std::unordered_set<ColumnFamilyData*> cfds_changed;
|
||||
JobContext job_context(0, true /*create_superversion*/);
|
||||
{
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
assert(manifest_reader_.get() != nullptr);
|
||||
s = static_cast_with_check<ReactiveVersionSet>(versions_.get())
|
||||
->ReadAndApply(&mutex_, &manifest_reader_,
|
||||
manifest_reader_status_.get(), &cfds_changed,
|
||||
@@ -991,7 +984,7 @@ Status DB::OpenAndCompact(
|
||||
}
|
||||
|
||||
// 2. Load the options
|
||||
DBOptions db_options;
|
||||
DBOptions base_db_options;
|
||||
ConfigOptions config_options;
|
||||
config_options.env = override_options.env;
|
||||
config_options.ignore_unknown_options = true;
|
||||
@@ -1004,13 +997,22 @@ Status DB::OpenAndCompact(
|
||||
std::string options_file_name =
|
||||
OptionsFileName(name, compaction_input.options_file_number);
|
||||
|
||||
s = LoadOptionsFromFile(config_options, options_file_name, &db_options,
|
||||
s = LoadOptionsFromFile(config_options, options_file_name, &base_db_options,
|
||||
&all_column_families);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// 3. Override pointer configurations in DBOptions with
|
||||
// 3. Options to Override
|
||||
// Override serializable configurations from override_options.options_map
|
||||
DBOptions db_options;
|
||||
s = GetDBOptionsFromMap(config_options, base_db_options,
|
||||
override_options.options_map, &db_options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Override options that are directly set as shared ptrs in
|
||||
// CompactionServiceOptionsOverride
|
||||
db_options.env = override_options.env;
|
||||
db_options.file_checksum_gen_factory =
|
||||
@@ -1021,6 +1023,7 @@ Status DB::OpenAndCompact(
|
||||
// We will close the DB after the compaction anyway.
|
||||
// Open as many files as needed for the compaction.
|
||||
db_options.max_open_files = -1;
|
||||
db_options.info_log = override_options.info_log;
|
||||
|
||||
// 4. Filter CFs that are needed for OpenAndCompact()
|
||||
// We do not need to open all column families for the remote compaction.
|
||||
@@ -1030,6 +1033,18 @@ Status DB::OpenAndCompact(
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (auto& cf : all_column_families) {
|
||||
if (cf.name == compaction_input.cf_name) {
|
||||
ColumnFamilyOptions cf_options;
|
||||
// Override serializable configurations from override_options.options_map
|
||||
s = GetColumnFamilyOptionsFromMap(config_options, cf.options,
|
||||
override_options.options_map,
|
||||
&cf_options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
cf.options = std::move(cf_options);
|
||||
|
||||
// Override options that are directly set as shared ptrs in
|
||||
// CompactionServiceOptionsOverride
|
||||
cf.options.comparator = override_options.comparator;
|
||||
cf.options.merge_operator = override_options.merge_operator;
|
||||
cf.options.compaction_filter = override_options.compaction_filter;
|
||||
@@ -1041,6 +1056,7 @@ Status DB::OpenAndCompact(
|
||||
override_options.sst_partitioner_factory;
|
||||
cf.options.table_properties_collector_factories =
|
||||
override_options.table_properties_collector_factories;
|
||||
|
||||
column_families.emplace_back(cf);
|
||||
} else if (cf.name == kDefaultColumnFamilyName) {
|
||||
column_families.emplace_back(cf);
|
||||
@@ -1057,6 +1073,9 @@ Status DB::OpenAndCompact(
|
||||
}
|
||||
assert(db);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImplSecondary::OpenAndCompact::AfterOpenAsSecondary:0", db);
|
||||
|
||||
// 6. Find the handle of the Column Family that this will compact
|
||||
ColumnFamilyHandle* cfh = nullptr;
|
||||
for (auto* handle : handles) {
|
||||
|
||||
+274
-176
@@ -157,7 +157,7 @@ Status DBImpl::Write(const WriteOptions& write_options, WriteBatch* my_batch) {
|
||||
if (s.ok()) {
|
||||
s = WriteImpl(write_options, my_batch, /*callback=*/nullptr,
|
||||
/*user_write_cb=*/nullptr,
|
||||
/*log_used=*/nullptr);
|
||||
/*wal_used=*/nullptr);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -190,11 +190,38 @@ Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
const WBWIMemTable::SeqnoRange& assigned_seqno,
|
||||
uint64_t prep_log,
|
||||
SequenceNumber last_seqno_after_ingest,
|
||||
bool memtable_updated, bool ignore_missing_cf) {
|
||||
Status DBImpl::IngestWriteBatchWithIndex(
|
||||
const WriteOptions& write_options,
|
||||
std::shared_ptr<WriteBatchWithIndex> wbwi) {
|
||||
if (!wbwi) {
|
||||
return Status::InvalidArgument("Batch is nullptr!");
|
||||
}
|
||||
if (!write_options.disableWAL) {
|
||||
return Status::NotSupported(
|
||||
"IngestWriteBatchWithIndex does not support disableWAL=true");
|
||||
}
|
||||
Status s;
|
||||
if (write_options.protection_bytes_per_key > 0) {
|
||||
s = WriteBatchInternal::UpdateProtectionInfo(
|
||||
wbwi->GetWriteBatch(), write_options.protection_bytes_per_key);
|
||||
}
|
||||
if (s.ok()) {
|
||||
WriteBatch dummy_empty_batch;
|
||||
s = WriteImpl(
|
||||
write_options, /*updates=*/&dummy_empty_batch, /*callback=*/nullptr,
|
||||
/*user_write_cb=*/nullptr, /*log_used=*/nullptr, /*log_ref=*/0,
|
||||
/*disable_memtable=*/false, /*seq_used=*/nullptr,
|
||||
/*batch_cnt=*/0, /*pre_release_callback=*/nullptr,
|
||||
/*post_memtable_callback=*/nullptr, /*wbwi=*/wbwi);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::IngestWBWIAsMemtable(
|
||||
std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
const WBWIMemTable::SeqnoRange& assigned_seqno, uint64_t min_prep_log,
|
||||
SequenceNumber last_seqno_after_ingest, bool memtable_updated,
|
||||
bool ignore_missing_cf) {
|
||||
// Keys in new memtable have seqno > last_seqno_after_ingest >= keys in wbwi.
|
||||
assert(assigned_seqno.upper_bound <= last_seqno_after_ingest);
|
||||
// Keys in the current memtable have seqno <= LastSequence() < keys in wbwi.
|
||||
@@ -238,12 +265,30 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
wbwi_memtable->AssignSequenceNumbers(assigned_seqno);
|
||||
// This is needed to keep the WAL that contains Prepare alive until
|
||||
// committed data in this memtable is persisted.
|
||||
wbwi_memtable->SetMinPrepLog(prep_log);
|
||||
wbwi_memtable->SetMinPrepLog(min_prep_log);
|
||||
memtables.push_back(wbwi_memtable);
|
||||
cfd->Ref();
|
||||
cfds.push_back(cfd);
|
||||
}
|
||||
|
||||
autovector<ColumnFamilyData*> cfds_for_atomic_flush;
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
SelectColumnFamiliesForAtomicFlush(&cfds_for_atomic_flush);
|
||||
for (auto cfd : cfds_for_atomic_flush) {
|
||||
bool found = false;
|
||||
for (auto existing_cfd : cfds) {
|
||||
if (existing_cfd == cfd) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
cfd->Ref();
|
||||
cfds.push_back(cfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop writes to the DB by entering both write threads
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (two_write_queues_) {
|
||||
@@ -253,15 +298,16 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
|
||||
// Switch memtable and add WBWIMemTables
|
||||
Status s;
|
||||
for (size_t i = 0; i < memtables.size(); ++i) {
|
||||
assert(!immutable_db_options_.atomic_flush);
|
||||
// NOTE: to support atomic flush, need to call
|
||||
// SelectColumnFamiliesForAtomicFlush()
|
||||
for (size_t i = 0; i < cfds.size(); ++i) {
|
||||
WriteContext write_context;
|
||||
// TODO: not switch on empty memtable, may need to update metadata
|
||||
// like NextLogNumber(), earliest_seqno and memtable id.
|
||||
s = SwitchMemtable(cfds[i], &write_context, memtables[i],
|
||||
last_seqno_after_ingest);
|
||||
if (i < memtables.size()) {
|
||||
s = SwitchMemtable(cfds[i], &write_context, memtables[i],
|
||||
last_seqno_after_ingest);
|
||||
} else {
|
||||
s = SwitchMemtable(cfds[i], &write_context);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
// SwitchMemtable() can only fail if a new WAL is to be created, this
|
||||
// should only happen for the first call to SwitchMemtable(). log will
|
||||
@@ -301,9 +347,18 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
continue;
|
||||
}
|
||||
cfd->imm()->FlushRequested();
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
// TODO: a new flush reason for ingesting memtable
|
||||
GenerateFlushRequest({cfd}, FlushReason::kExternalFileIngestion,
|
||||
&flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
FlushRequest flush_req;
|
||||
// TODO: a new flush reason for ingesting memtable
|
||||
GenerateFlushRequest({cfd}, FlushReason::kExternalFileIngestion,
|
||||
GenerateFlushRequest(cfds, FlushReason::kExternalFileIngestion,
|
||||
&flush_req);
|
||||
EnqueuePendingFlush(flush_req);
|
||||
}
|
||||
@@ -314,13 +369,12 @@ Status DBImpl::IngestWBWI(std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
|
||||
Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
UserWriteCallback* user_write_cb, uint64_t* wal_used,
|
||||
uint64_t log_ref, bool disable_memtable,
|
||||
uint64_t* seq_used, size_t batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback,
|
||||
PostMemTableCallback* post_memtable_callback,
|
||||
std::shared_ptr<WriteBatchWithIndex> wbwi,
|
||||
uint64_t prep_log) {
|
||||
std::shared_ptr<WriteBatchWithIndex> wbwi) {
|
||||
assert(!seq_per_batch_ || batch_cnt != 0);
|
||||
assert(my_batch == nullptr || my_batch->Count() == 0 ||
|
||||
write_options.protection_bytes_per_key == 0 ||
|
||||
@@ -409,9 +463,17 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
return Status::NotSupported(
|
||||
"DeleteRange is not compatible with row cache.");
|
||||
}
|
||||
// Whether the WBWI is from transaction commit or a direct write
|
||||
// (IngestWriteBatchWithIndex())
|
||||
bool ingest_wbwi_for_commit = false;
|
||||
if (wbwi) {
|
||||
assert(prep_log > 0);
|
||||
// Used only in WriteCommittedTxn::CommitInternal() with no `callback`.
|
||||
if (my_batch->HasCommit()) {
|
||||
ingest_wbwi_for_commit = true;
|
||||
assert(log_ref);
|
||||
} else {
|
||||
// Only supports disableWAL for directly ingesting WBWI for now.
|
||||
assert(write_options.disableWAL);
|
||||
}
|
||||
assert(!callback);
|
||||
if (immutable_db_options_.unordered_write) {
|
||||
return Status::NotSupported(
|
||||
@@ -421,9 +483,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
return Status::NotSupported(
|
||||
"Ingesting WriteBatch does not support pipelined_write");
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
if (!wbwi->GetOverwriteKey()) {
|
||||
return Status::NotSupported(
|
||||
"Ingesting WriteBatch does not support atomic_flush");
|
||||
"WriteBatchWithIndex ingestion requires overwrite_key=true");
|
||||
}
|
||||
}
|
||||
// Otherwise IsLatestPersistentState optimization does not make sense
|
||||
@@ -444,7 +506,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// they don't consume sequence.
|
||||
return WriteImplWALOnly(
|
||||
&nonmem_write_thread_, write_options, my_batch, callback, user_write_cb,
|
||||
log_used, log_ref, seq_used, batch_cnt, pre_release_callback,
|
||||
wal_used, log_ref, seq_used, batch_cnt, pre_release_callback,
|
||||
assign_order, kDontPublishLastSeq, disable_memtable);
|
||||
}
|
||||
|
||||
@@ -458,7 +520,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// sequence in in increasing order, iii) call pre_release_callback serially
|
||||
Status status = WriteImplWALOnly(
|
||||
&write_thread_, write_options, my_batch, callback, user_write_cb,
|
||||
log_used, log_ref, &seq, sub_batch_cnt, pre_release_callback,
|
||||
wal_used, log_ref, &seq, sub_batch_cnt, pre_release_callback,
|
||||
kDoAssignOrder, kDoPublishLastSeq, disable_memtable);
|
||||
TEST_SYNC_POINT("DBImpl::WriteImpl:UnorderedWriteAfterWriteWAL");
|
||||
if (!status.ok()) {
|
||||
@@ -477,7 +539,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
return PipelinedWriteImpl(write_options, my_batch, callback, user_write_cb,
|
||||
log_used, log_ref, disable_memtable, seq_used);
|
||||
wal_used, log_ref, disable_memtable, seq_used);
|
||||
}
|
||||
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
@@ -524,16 +586,19 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
assert(tmp_s.ok());
|
||||
}
|
||||
}
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
if (w.status.ok()) { // Don't publish a partial batch write
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
} else {
|
||||
HandleMemTableInsertFailure(w.status);
|
||||
}
|
||||
write_thread_.ExitAsBatchGroupFollower(&w);
|
||||
}
|
||||
assert(w.state == WriteThread::STATE_COMPLETED);
|
||||
// STATE_COMPLETED conditional below handles exit
|
||||
}
|
||||
if (w.state == WriteThread::STATE_COMPLETED) {
|
||||
if (log_used != nullptr) {
|
||||
*log_used = w.log_used;
|
||||
if (wal_used != nullptr) {
|
||||
*wal_used = w.wal_used;
|
||||
}
|
||||
if (seq_used != nullptr) {
|
||||
*seq_used = w.sequence;
|
||||
@@ -549,7 +614,8 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// when it finds suitable, and finish them in the same write batch.
|
||||
// This is how a write job could be done by the other writer.
|
||||
WriteContext write_context;
|
||||
LogContext log_context(write_options.sync);
|
||||
// FIXME: also check disableWAL like others?
|
||||
WalContext wal_context(write_options.sync);
|
||||
WriteThread::WriteGroup write_group;
|
||||
bool in_parallel_group = false;
|
||||
uint64_t last_sequence = kMaxSequenceNumber;
|
||||
@@ -563,7 +629,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// PreprocessWrite does its own perf timing.
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
status = PreprocessWrite(write_options, &log_context, &write_context);
|
||||
status = PreprocessWrite(write_options, &wal_context, &write_context);
|
||||
if (!two_write_queues_) {
|
||||
// Assign it after ::PreprocessWrite since the sequence might advance
|
||||
// inside it by WriteRecoverableState
|
||||
@@ -631,7 +697,13 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
continue;
|
||||
}
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
if (wbwi && !ingest_wbwi_for_commit) {
|
||||
// for transaction write, tracer only needs the commit marker which
|
||||
// is in writer->batch
|
||||
tracer_->Write(wbwi->GetWriteBatch()).PermitUncheckedError();
|
||||
} else {
|
||||
tracer_->Write(writer->batch).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -689,22 +761,21 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (!two_write_queues_) {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
assert(log_context.log_file_number_size);
|
||||
LogFileNumberSize& log_file_number_size =
|
||||
*(log_context.log_file_number_size);
|
||||
assert(wal_context.wal_file_number_size);
|
||||
wal_context.prev_size = wal_context.writer->file()->GetFileSize();
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
io_s =
|
||||
WriteToWAL(write_group, log_context.writer, log_used,
|
||||
log_context.need_log_sync, log_context.need_log_dir_sync,
|
||||
last_sequence + 1, log_file_number_size);
|
||||
io_s = WriteGroupToWAL(write_group, wal_context.writer, wal_used,
|
||||
wal_context.need_wal_sync,
|
||||
wal_context.need_wal_dir_sync, last_sequence + 1,
|
||||
*wal_context.wal_file_number_size);
|
||||
}
|
||||
} else {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
// LastAllocatedSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
io_s = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
seq_inc);
|
||||
io_s = ConcurrentWriteGroupToWAL(write_group, wal_used, &last_sequence,
|
||||
seq_inc);
|
||||
} else {
|
||||
// Otherwise we inc seq number for memtable writes
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
@@ -716,16 +787,16 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
last_sequence += seq_inc;
|
||||
// Seqno assigned to this write are [current_sequence, last_sequence]
|
||||
|
||||
if (log_context.need_log_sync) {
|
||||
if (wal_context.need_wal_sync) {
|
||||
VersionEdit synced_wals;
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
if (status.ok()) {
|
||||
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
|
||||
MarkLogsSynced(cur_wal_number_, wal_context.need_wal_dir_sync,
|
||||
&synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(logfile_number_);
|
||||
MarkLogsNotSynced(cur_wal_number_);
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
if (status.ok() && synced_wals.IsWalAddition()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
@@ -760,7 +831,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
writer->sequence = next_sequence;
|
||||
if (writer->pre_release_callback) {
|
||||
Status ws = writer->pre_release_callback->Callback(
|
||||
writer->sequence, disable_memtable, writer->log_used, index++,
|
||||
writer->sequence, disable_memtable, writer->wal_used, index++,
|
||||
pre_release_callback_cnt);
|
||||
if (!ws.ok()) {
|
||||
status = pre_release_cb_status = ws;
|
||||
@@ -834,12 +905,13 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// handle exit, false means somebody else did
|
||||
should_exit_batch_group = write_thread_.CompleteParallelMemTableWriter(&w);
|
||||
}
|
||||
if (wbwi) {
|
||||
if (status.ok() && w.status.ok()) {
|
||||
if (wbwi && status.ok() && w.status.ok()) {
|
||||
uint32_t wbwi_count = wbwi->GetWriteBatch()->Count();
|
||||
// skip empty batch case
|
||||
if (wbwi_count) {
|
||||
// w.batch contains (potentially empty) commit time batch updates,
|
||||
// only ingest wbwi if w.batch is applied to memtable successfully
|
||||
uint32_t memtable_update_count = w.batch->Count();
|
||||
uint32_t wbwi_count = wbwi->GetWriteBatch()->Count();
|
||||
// Seqno assigned to this write are [last_seq + 1 - seq_inc, last_seq].
|
||||
// seq_inc includes w.batch (memtable updates) and wbwi
|
||||
// w.batch gets first `memtable_update_count` sequence numbers.
|
||||
@@ -852,10 +924,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
if (two_write_queues_) {
|
||||
assert(ub <= versions_->LastAllocatedSequence());
|
||||
}
|
||||
status = IngestWBWI(wbwi, {/*lower_bound=*/lb, /*upper_bound=*/ub},
|
||||
prep_log, last_sequence,
|
||||
/*memtable_updated=*/memtable_update_count > 0,
|
||||
write_options.ignore_missing_column_families);
|
||||
status =
|
||||
IngestWBWIAsMemtable(wbwi, {/*lower_bound=*/lb, /*upper_bound=*/ub},
|
||||
/*min_prep_log=*/log_ref, last_sequence,
|
||||
/*memtable_updated=*/memtable_update_count > 0,
|
||||
write_options.ignore_missing_column_families);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,9 +946,19 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
// Note: if we are to resume after non-OK statuses we need to revisit how
|
||||
// we react to non-OK statuses here.
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
if (w.status.ok()) { // Don't publish a partial batch write
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
}
|
||||
if (!w.status.ok()) {
|
||||
if (wal_context.prev_size < SIZE_MAX) {
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
if (logs_.back().number == wal_context.wal_file_number_size->number) {
|
||||
logs_.back().SetAttemptTruncateSize(wal_context.prev_size);
|
||||
}
|
||||
}
|
||||
HandleMemTableInsertFailure(w.status);
|
||||
}
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
write_thread_.ExitAsBatchGroupLeader(write_group, status);
|
||||
}
|
||||
|
||||
@@ -888,7 +971,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
uint64_t* wal_used, uint64_t log_ref,
|
||||
bool disable_memtable, uint64_t* seq_used) {
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
@@ -905,10 +988,10 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
if (w.callback && !w.callback->AllowWriteBatching()) {
|
||||
write_thread_.WaitForMemTableWriters();
|
||||
}
|
||||
LogContext log_context(!write_options.disableWAL && write_options.sync);
|
||||
WalContext wal_context(!write_options.disableWAL && write_options.sync);
|
||||
// PreprocessWrite does its own perf timing.
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
w.status = PreprocessWrite(write_options, &log_context, &write_context);
|
||||
w.status = PreprocessWrite(write_options, &wal_context, &write_context);
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
// This can set non-OK status if callback fail.
|
||||
@@ -977,13 +1060,13 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
wal_write_group.size - 1);
|
||||
RecordTick(stats_, WRITE_DONE_BY_OTHER, wal_write_group.size - 1);
|
||||
}
|
||||
assert(log_context.log_file_number_size);
|
||||
LogFileNumberSize& log_file_number_size =
|
||||
*(log_context.log_file_number_size);
|
||||
io_s =
|
||||
WriteToWAL(wal_write_group, log_context.writer, log_used,
|
||||
log_context.need_log_sync, log_context.need_log_dir_sync,
|
||||
current_sequence, log_file_number_size);
|
||||
assert(wal_context.wal_file_number_size);
|
||||
WalFileNumberSize& wal_file_number_size =
|
||||
*(wal_context.wal_file_number_size);
|
||||
io_s = WriteGroupToWAL(wal_write_group, wal_context.writer, wal_used,
|
||||
wal_context.need_wal_sync,
|
||||
wal_context.need_wal_dir_sync, current_sequence,
|
||||
wal_file_number_size);
|
||||
w.status = io_s;
|
||||
}
|
||||
|
||||
@@ -995,13 +1078,13 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
VersionEdit synced_wals;
|
||||
if (log_context.need_log_sync) {
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
if (wal_context.need_wal_sync) {
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
if (w.status.ok()) {
|
||||
MarkLogsSynced(logfile_number_, log_context.need_log_dir_sync,
|
||||
MarkLogsSynced(cur_wal_number_, wal_context.need_wal_dir_sync,
|
||||
&synced_wals);
|
||||
} else {
|
||||
MarkLogsNotSynced(logfile_number_);
|
||||
MarkLogsNotSynced(cur_wal_number_);
|
||||
}
|
||||
}
|
||||
if (w.status.ok() && synced_wals.IsWalAddition()) {
|
||||
@@ -1032,7 +1115,12 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
&flush_scheduler_, &trim_history_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
false /*concurrent_memtable_writes*/, seq_per_batch_, batch_per_txn_);
|
||||
versions_->SetLastSequence(memtable_write_group.last_sequence);
|
||||
if (memtable_write_group.status
|
||||
.ok()) { // Don't publish a partial batch write
|
||||
versions_->SetLastSequence(memtable_write_group.last_sequence);
|
||||
} else {
|
||||
HandleMemTableInsertFailure(memtable_write_group.status);
|
||||
}
|
||||
write_thread_.ExitAsMemTableWriter(&w, memtable_write_group);
|
||||
}
|
||||
} else {
|
||||
@@ -1061,8 +1149,11 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
versions_->SetLastSequence(w.write_group->last_sequence);
|
||||
if (w.status.ok()) { // Don't publish a partial batch write
|
||||
versions_->SetLastSequence(w.write_group->last_sequence);
|
||||
} else {
|
||||
HandleMemTableInsertFailure(w.status);
|
||||
}
|
||||
write_thread_.ExitAsMemTableWriter(&w, *w.write_group);
|
||||
}
|
||||
}
|
||||
@@ -1134,7 +1225,7 @@ Status DBImpl::UnorderedWriteMemtable(const WriteOptions& write_options,
|
||||
Status DBImpl::WriteImplWALOnly(
|
||||
WriteThread* write_thread, const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
UserWriteCallback* user_write_cb, uint64_t* log_used,
|
||||
UserWriteCallback* user_write_cb, uint64_t* wal_used,
|
||||
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
|
||||
const PublishLastSeq publish_last_seq, const bool disable_memtable) {
|
||||
@@ -1147,8 +1238,8 @@ Status DBImpl::WriteImplWALOnly(
|
||||
write_thread->JoinBatchGroup(&w);
|
||||
assert(w.state != WriteThread::STATE_PARALLEL_MEMTABLE_WRITER);
|
||||
if (w.state == WriteThread::STATE_COMPLETED) {
|
||||
if (log_used != nullptr) {
|
||||
*log_used = w.log_used;
|
||||
if (wal_used != nullptr) {
|
||||
*wal_used = w.wal_used;
|
||||
}
|
||||
if (seq_used != nullptr) {
|
||||
*seq_used = w.sequence;
|
||||
@@ -1164,10 +1255,10 @@ Status DBImpl::WriteImplWALOnly(
|
||||
|
||||
// TODO(myabandeh): Make preliminary checks thread-safe so we could do them
|
||||
// without paying the cost of obtaining the mutex.
|
||||
LogContext log_context;
|
||||
WalContext wal_context;
|
||||
WriteContext write_context;
|
||||
Status status =
|
||||
PreprocessWrite(write_options, &log_context, &write_context);
|
||||
PreprocessWrite(write_options, &wal_context, &write_context);
|
||||
WriteStatusCheckOnLocked(status);
|
||||
|
||||
if (!status.ok()) {
|
||||
@@ -1264,8 +1355,8 @@ Status DBImpl::WriteImplWALOnly(
|
||||
}
|
||||
Status status;
|
||||
if (!write_options.disableWAL) {
|
||||
IOStatus io_s =
|
||||
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
IOStatus io_s = ConcurrentWriteGroupToWAL(write_group, wal_used,
|
||||
&last_sequence, seq_inc);
|
||||
status = io_s;
|
||||
// last_sequence may not be set if there is an error
|
||||
// This error checking and return is moved up to avoid using uninitialized
|
||||
@@ -1317,7 +1408,7 @@ Status DBImpl::WriteImplWALOnly(
|
||||
if (!writer->CallbackFailed() && writer->pre_release_callback) {
|
||||
assert(writer->sequence != kMaxSequenceNumber);
|
||||
Status ws = writer->pre_release_callback->Callback(
|
||||
writer->sequence, disable_memtable, writer->log_used, index++,
|
||||
writer->sequence, disable_memtable, writer->wal_used, index++,
|
||||
pre_release_callback_cnt);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
@@ -1386,24 +1477,22 @@ void DBImpl::WALIOStatusCheck(const IOStatus& io_status) {
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::MemTableInsertStatusCheck(const Status& status) {
|
||||
// A non-OK status here indicates that the state implied by the
|
||||
// WAL has diverged from the in-memory state. This could be
|
||||
// because of a corrupt write_batch (very bad), or because the
|
||||
// client specified an invalid column family and didn't specify
|
||||
// ignore_missing_column_families.
|
||||
if (!status.ok()) {
|
||||
mutex_.Lock();
|
||||
assert(!error_handler_.IsBGWorkStopped());
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
void DBImpl::HandleMemTableInsertFailure(const Status& status) {
|
||||
assert(!status.ok());
|
||||
// A non-OK status on memtable insert indicates that the state implied by the
|
||||
// WAL has diverged from the in-memory state. This could be because of a
|
||||
// corrupt write_batch (very bad), or because the client specified an invalid
|
||||
// column family and didn't specify ignore_missing_column_families.
|
||||
mutex_.Lock();
|
||||
assert(!error_handler_.IsBGWorkStopped());
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kMemTable);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
LogContext* log_context,
|
||||
WalContext* wal_context,
|
||||
WriteContext* write_context) {
|
||||
assert(write_context != nullptr && log_context != nullptr);
|
||||
assert(write_context != nullptr && wal_context != nullptr);
|
||||
Status status;
|
||||
|
||||
if (error_handler_.IsDBStopped()) {
|
||||
@@ -1413,7 +1502,8 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_GUARD(write_scheduling_flushes_compactions_time);
|
||||
|
||||
if (UNLIKELY(status.ok() && total_log_size_ > GetMaxTotalWalSize())) {
|
||||
if (UNLIKELY(status.ok() &&
|
||||
wals_total_size_.LoadRelaxed() > GetMaxTotalWalSize())) {
|
||||
assert(versions_);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
const ColumnFamilySet* const column_families =
|
||||
@@ -1482,17 +1572,17 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
WriteBufferManagerStallWrites();
|
||||
}
|
||||
}
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
if (status.ok() && log_context->need_log_sync) {
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
if (status.ok() && wal_context->need_wal_sync) {
|
||||
// Wait until the parallel syncs are finished. Any sync process has to sync
|
||||
// the front log too so it is enough to check the status of front()
|
||||
// We do a while loop since log_sync_cv_ is signalled when any sync is
|
||||
// We do a while loop since wal_sync_cv_ is signalled when any sync is
|
||||
// finished
|
||||
// Note: there does not seem to be a reason to wait for parallel sync at
|
||||
// this early step but it is not important since parallel sync (SyncWAL) and
|
||||
// need_log_sync are usually not used together.
|
||||
// need_wal_sync are usually not used together.
|
||||
while (logs_.front().IsSyncing()) {
|
||||
log_sync_cv_.Wait();
|
||||
wal_sync_cv_.Wait();
|
||||
}
|
||||
for (auto& log : logs_) {
|
||||
// This is just to prevent the logs to be synced by a parallel SyncWAL
|
||||
@@ -1503,12 +1593,12 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
log.PrepareForSync();
|
||||
}
|
||||
} else {
|
||||
log_context->need_log_sync = false;
|
||||
wal_context->need_wal_sync = false;
|
||||
}
|
||||
log_context->writer = logs_.back().writer;
|
||||
log_context->need_log_dir_sync =
|
||||
log_context->need_log_dir_sync && !log_dir_synced_;
|
||||
log_context->log_file_number_size = std::addressof(alive_log_files_.back());
|
||||
wal_context->writer = logs_.back().writer;
|
||||
wal_context->need_wal_dir_sync =
|
||||
wal_context->need_wal_dir_sync && !wal_dir_synced_;
|
||||
wal_context->wal_file_number_size = std::addressof(alive_wal_files_.back());
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -1559,12 +1649,12 @@ Status DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
}
|
||||
|
||||
// When two_write_queues_ is disabled, this function is called from the only
|
||||
// write thread. Otherwise this must be called holding log_write_mutex_.
|
||||
// write thread. Otherwise this must be called holding wal_write_mutex_.
|
||||
IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
const WriteOptions& write_options,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
log::Writer* log_writer, uint64_t* wal_used,
|
||||
uint64_t* log_size,
|
||||
LogFileNumberSize& log_file_number_size,
|
||||
WalFileNumberSize& wal_file_number_size,
|
||||
SequenceNumber sequence) {
|
||||
assert(log_size != nullptr);
|
||||
|
||||
@@ -1576,7 +1666,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
}
|
||||
*log_size = log_entry.size();
|
||||
// When two_write_queues_ WriteToWAL has to be protected from concurretn calls
|
||||
// from the two queues anyway and log_write_mutex_ is already held. Otherwise
|
||||
// from the two queues anyway and wal_write_mutex_ is already held. Otherwise
|
||||
// if manual_wal_flush_ is enabled we need to protect log_writer->AddRecord
|
||||
// from possible concurrent calls via the FlushWAL by the application.
|
||||
const bool needs_locking = manual_wal_flush_ && !two_write_queues_;
|
||||
@@ -1584,7 +1674,7 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
// manual_wal_flush_ feature (by UNLIKELY) instead of the more common case
|
||||
// when we do not need any locking.
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
}
|
||||
IOStatus io_s = log_writer->MaybeAddUserDefinedTimestampSizeRecord(
|
||||
write_options, versions_->GetColumnFamiliesTimestampSizeForRecord());
|
||||
@@ -1594,23 +1684,24 @@ IOStatus DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
io_s = log_writer->AddRecord(write_options, log_entry, sequence);
|
||||
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
}
|
||||
if (log_used != nullptr) {
|
||||
*log_used = logfile_number_;
|
||||
if (wal_used != nullptr) {
|
||||
*wal_used = cur_wal_number_;
|
||||
assert(*wal_used == wal_file_number_size.number);
|
||||
}
|
||||
total_log_size_ += log_entry.size();
|
||||
log_file_number_size.AddSize(*log_size);
|
||||
log_empty_ = false;
|
||||
wals_total_size_.FetchAddRelaxed(log_entry.size());
|
||||
wal_file_number_size.AddSize(*log_size);
|
||||
wal_empty_ = false;
|
||||
|
||||
return io_s;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
bool need_log_sync, bool need_log_dir_sync,
|
||||
SequenceNumber sequence,
|
||||
LogFileNumberSize& log_file_number_size) {
|
||||
IOStatus DBImpl::WriteGroupToWAL(const WriteThread::WriteGroup& write_group,
|
||||
log::Writer* log_writer, uint64_t* wal_used,
|
||||
bool need_wal_sync, bool need_wal_dir_sync,
|
||||
SequenceNumber sequence,
|
||||
WalFileNumberSize& wal_file_number_size) {
|
||||
IOStatus io_s;
|
||||
assert(!two_write_queues_);
|
||||
assert(!write_group.leader->disable_wal);
|
||||
@@ -1625,10 +1716,10 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
}
|
||||
|
||||
if (merged_batch == write_group.leader->batch) {
|
||||
write_group.leader->log_used = logfile_number_;
|
||||
write_group.leader->wal_used = cur_wal_number_;
|
||||
} else if (write_with_wal > 1) {
|
||||
for (auto writer : write_group) {
|
||||
writer->log_used = logfile_number_;
|
||||
writer->wal_used = cur_wal_number_;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1640,14 +1731,14 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
WriteOptions write_options;
|
||||
write_options.rate_limiter_priority =
|
||||
write_group.leader->rate_limiter_priority;
|
||||
io_s = WriteToWAL(*merged_batch, write_options, log_writer, log_used,
|
||||
&log_size, log_file_number_size, sequence);
|
||||
io_s = WriteToWAL(*merged_batch, write_options, log_writer, wal_used,
|
||||
&log_size, wal_file_number_size, sequence);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
}
|
||||
|
||||
if (io_s.ok() && need_log_sync) {
|
||||
if (io_s.ok() && need_wal_sync) {
|
||||
StopWatch sw(immutable_db_options_.clock, stats_, WAL_FILE_SYNC_MICROS);
|
||||
// It's safe to access logs_ with unlocked mutex_ here because:
|
||||
// - we've set getting_synced=true for all logs,
|
||||
@@ -1657,15 +1748,15 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
// - as long as other threads don't modify it, it's safe to read
|
||||
// from std::deque from multiple threads concurrently.
|
||||
//
|
||||
// Sync operation should work with locked log_write_mutex_, because:
|
||||
// Sync operation should work with locked wal_write_mutex_, because:
|
||||
// when DBOptions.manual_wal_flush_ is set,
|
||||
// FlushWAL function will be invoked by another thread.
|
||||
// if without locked log_write_mutex_, the log file may get data
|
||||
// if without locked wal_write_mutex_, the log file may get data
|
||||
// corruption
|
||||
|
||||
const bool needs_locking = manual_wal_flush_ && !two_write_queues_;
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
}
|
||||
|
||||
if (io_s.ok()) {
|
||||
@@ -1688,10 +1779,10 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
}
|
||||
|
||||
if (UNLIKELY(needs_locking)) {
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
}
|
||||
|
||||
if (io_s.ok() && need_log_dir_sync) {
|
||||
if (io_s.ok() && need_wal_dir_sync) {
|
||||
// We only sync WAL directory the first time WAL syncing is
|
||||
// requested, so that in case users never turn on WAL sync,
|
||||
// we can avoid the disk I/O in the write code path.
|
||||
@@ -1706,7 +1797,7 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
auto stats = default_cf_internal_stats_;
|
||||
if (need_log_sync) {
|
||||
if (need_wal_sync) {
|
||||
stats->AddDBStats(InternalStats::kIntStatsWalFileSynced, 1);
|
||||
RecordTick(stats_, WAL_FILE_SYNCED);
|
||||
}
|
||||
@@ -1723,8 +1814,8 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
return io_s;
|
||||
}
|
||||
|
||||
IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
const WriteThread::WriteGroup& write_group, uint64_t* log_used,
|
||||
IOStatus DBImpl::ConcurrentWriteGroupToWAL(
|
||||
const WriteThread::WriteGroup& write_group, uint64_t* wal_used,
|
||||
SequenceNumber* last_sequence, size_t seq_inc) {
|
||||
IOStatus io_s;
|
||||
|
||||
@@ -1741,14 +1832,14 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
return io_s;
|
||||
}
|
||||
|
||||
// We need to lock log_write_mutex_ since logs_ and alive_log_files might be
|
||||
// We need to lock wal_write_mutex_ since logs_ and alive_wal_files might be
|
||||
// pushed back concurrently
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
if (merged_batch == write_group.leader->batch) {
|
||||
write_group.leader->log_used = logfile_number_;
|
||||
write_group.leader->wal_used = cur_wal_number_;
|
||||
} else if (write_with_wal > 1) {
|
||||
for (auto writer : write_group) {
|
||||
writer->log_used = logfile_number_;
|
||||
writer->wal_used = cur_wal_number_;
|
||||
}
|
||||
}
|
||||
*last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
@@ -1756,9 +1847,9 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
LogFileNumberSize& log_file_number_size = alive_log_files_.back();
|
||||
WalFileNumberSize& wal_file_number_size = alive_wal_files_.back();
|
||||
|
||||
assert(log_writer->get_log_number() == log_file_number_size.number);
|
||||
assert(log_writer->get_log_number() == wal_file_number_size.number);
|
||||
|
||||
uint64_t log_size;
|
||||
|
||||
@@ -1766,13 +1857,13 @@ IOStatus DBImpl::ConcurrentWriteToWAL(
|
||||
WriteOptions write_options;
|
||||
write_options.rate_limiter_priority =
|
||||
write_group.leader->rate_limiter_priority;
|
||||
io_s = WriteToWAL(*merged_batch, write_options, log_writer, log_used,
|
||||
&log_size, log_file_number_size, sequence);
|
||||
io_s = WriteToWAL(*merged_batch, write_options, log_writer, wal_used,
|
||||
&log_size, wal_file_number_size, sequence);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
|
||||
if (io_s.ok()) {
|
||||
const bool concurrent = true;
|
||||
@@ -1800,7 +1891,7 @@ Status DBImpl::WriteRecoverableState() {
|
||||
bool dont_care_bool;
|
||||
SequenceNumber next_seq;
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
}
|
||||
SequenceNumber seq;
|
||||
if (two_write_queues_) {
|
||||
@@ -1815,13 +1906,17 @@ Status DBImpl::WriteRecoverableState() {
|
||||
0 /*recovery_log_number*/, this, false /* concurrent_memtable_writes */,
|
||||
&next_seq, &dont_care_bool, seq_per_batch_);
|
||||
auto last_seq = next_seq - 1;
|
||||
if (two_write_queues_) {
|
||||
versions_->FetchAddLastAllocatedSequence(last_seq - seq);
|
||||
versions_->SetLastPublishedSequence(last_seq);
|
||||
if (status.ok()) { // Don't publish a partial batch write
|
||||
if (two_write_queues_) {
|
||||
versions_->FetchAddLastAllocatedSequence(last_seq - seq);
|
||||
versions_->SetLastPublishedSequence(last_seq);
|
||||
}
|
||||
versions_->SetLastSequence(last_seq);
|
||||
} else {
|
||||
HandleMemTableInsertFailure(status);
|
||||
}
|
||||
versions_->SetLastSequence(last_seq);
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
}
|
||||
if (status.ok() && recoverable_state_pre_release_callback_) {
|
||||
const bool DISABLE_MEMTABLE = true;
|
||||
@@ -1893,7 +1988,10 @@ void DBImpl::AssignAtomicFlushSeq(const autovector<ColumnFamilyData*>& cfds) {
|
||||
assert(immutable_db_options_.atomic_flush);
|
||||
auto seq = versions_->LastSequence();
|
||||
for (auto cfd : cfds) {
|
||||
cfd->imm()->AssignAtomicFlushSeq(seq);
|
||||
// cfd can be nullptr, see ScheduleFlushes()
|
||||
if (cfd) {
|
||||
cfd->imm()->AssignAtomicFlushSeq(seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1902,11 +2000,11 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
assert(write_context != nullptr);
|
||||
Status status;
|
||||
|
||||
if (alive_log_files_.begin()->getting_flushed) {
|
||||
if (alive_wal_files_.begin()->getting_flushed) {
|
||||
return status;
|
||||
}
|
||||
|
||||
auto oldest_alive_log = alive_log_files_.begin()->number;
|
||||
auto oldest_alive_log = alive_wal_files_.begin()->number;
|
||||
bool flush_wont_release_oldest_log = false;
|
||||
if (allow_2pc()) {
|
||||
auto oldest_log_with_uncommitted_prep =
|
||||
@@ -1936,14 +2034,14 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
// transactions then we cannot flush this log until those transactions are
|
||||
// commited.
|
||||
unable_to_release_oldest_log_ = false;
|
||||
alive_log_files_.begin()->getting_flushed = true;
|
||||
alive_wal_files_.begin()->getting_flushed = true;
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Flushing all column families with data in WAL number %" PRIu64
|
||||
". Total log size is %" PRIu64 " while max_total_wal_size is %" PRIu64,
|
||||
oldest_alive_log, total_log_size_.load(), GetMaxTotalWalSize());
|
||||
oldest_alive_log, wals_total_size_.LoadRelaxed(), GetMaxTotalWalSize());
|
||||
// no need to refcount because drop is happening in write thread, so can't
|
||||
// happen while we're in the write thread
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
@@ -2413,21 +2511,21 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
// Do this without holding the dbmutex lock.
|
||||
assert(versions_->prev_log_number() == 0);
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
}
|
||||
bool creating_new_log = !log_empty_;
|
||||
bool creating_new_log = !wal_empty_;
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
}
|
||||
uint64_t recycle_log_number = 0;
|
||||
// If file deletion is disabled, don't recycle logs since it'll result in
|
||||
// the file getting renamed
|
||||
if (creating_new_log && immutable_db_options_.recycle_log_file_num &&
|
||||
!log_recycle_files_.empty() && IsFileDeletionsEnabled()) {
|
||||
recycle_log_number = log_recycle_files_.front();
|
||||
!wal_recycle_files_.empty() && IsFileDeletionsEnabled()) {
|
||||
recycle_log_number = wal_recycle_files_.front();
|
||||
}
|
||||
uint64_t new_log_number =
|
||||
creating_new_log ? versions_->NewFileNumber() : logfile_number_;
|
||||
creating_new_log ? versions_->NewFileNumber() : cur_wal_number_;
|
||||
// For use outside of holding DB mutex
|
||||
const MutableCFOptions mutable_cf_options_copy =
|
||||
cfd->GetLatestMutableCFOptions();
|
||||
@@ -2453,14 +2551,14 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
mutex_.Unlock();
|
||||
if (creating_new_log) {
|
||||
PredecessorWALInfo info;
|
||||
log_write_mutex_.Lock();
|
||||
wal_write_mutex_.Lock();
|
||||
if (!logs_.empty()) {
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
info = PredecessorWALInfo(cur_log_writer->get_log_number(),
|
||||
cur_log_writer->file()->GetFileSize(),
|
||||
cur_log_writer->GetLastSeqnoRecorded());
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
wal_write_mutex_.Unlock();
|
||||
// TODO: Write buffer size passed in should be max of all CF's instead
|
||||
// of mutable_cf_options.write_buffer_size.
|
||||
io_s = CreateWAL(write_options, new_log_number, recycle_log_number,
|
||||
@@ -2501,11 +2599,11 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
// concurrent full purges don't delete the file while we're recycling it.
|
||||
// To achieve that we hold the old log number in the recyclable list until
|
||||
// after it has been renamed.
|
||||
assert(log_recycle_files_.front() == recycle_log_number);
|
||||
log_recycle_files_.pop_front();
|
||||
assert(wal_recycle_files_.front() == recycle_log_number);
|
||||
wal_recycle_files_.pop_front();
|
||||
}
|
||||
if (s.ok() && creating_new_log) {
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
InstrumentedMutexLock l(&wal_write_mutex_);
|
||||
assert(new_log != nullptr);
|
||||
if (!logs_.empty()) {
|
||||
// Alway flush the buffer of the last log before switching to a new one
|
||||
@@ -2527,11 +2625,11 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
logfile_number_ = new_log_number;
|
||||
log_empty_ = true;
|
||||
log_dir_synced_ = false;
|
||||
logs_.emplace_back(logfile_number_, new_log);
|
||||
alive_log_files_.emplace_back(logfile_number_);
|
||||
cur_wal_number_ = new_log_number;
|
||||
wal_empty_ = true;
|
||||
wal_dir_synced_ = false;
|
||||
logs_.emplace_back(cur_wal_number_, new_log);
|
||||
alive_wal_files_.emplace_back(cur_wal_number_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2562,7 +2660,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
// obsolete. So we should track the WAL obsoletion event before actually
|
||||
// updating the empty CF's log number.
|
||||
uint64_t min_wal_number_to_keep =
|
||||
versions_->PreComputeMinLogNumberWithUnflushedData(logfile_number_);
|
||||
versions_->PreComputeMinLogNumberWithUnflushedData(cur_wal_number_);
|
||||
if (min_wal_number_to_keep >
|
||||
versions_->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
// TODO: plumb Env::IOActivity, Env::IOPriority
|
||||
@@ -2597,7 +2695,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
|
||||
for (auto cf : empty_cfs) {
|
||||
if (cf->IsEmpty()) {
|
||||
cf->SetLogNumber(logfile_number_);
|
||||
cf->SetLogNumber(cur_wal_number_);
|
||||
// MEMPURGE: No need to change this, because new adds
|
||||
// should still receive new sequence numbers.
|
||||
cf->mem()->SetCreationSeq(versions_->LastSequence());
|
||||
@@ -2614,14 +2712,14 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
// advance the log number. no need to persist this in the manifest
|
||||
if (cf->IsEmpty()) {
|
||||
if (creating_new_log) {
|
||||
cf->SetLogNumber(logfile_number_);
|
||||
cf->SetLogNumber(cur_wal_number_);
|
||||
}
|
||||
cf->mem()->SetCreationSeq(versions_->LastSequence());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfd->mem()->SetNextLogNumber(logfile_number_);
|
||||
cfd->mem()->SetNextLogNumber(cur_wal_number_);
|
||||
assert(new_mem != nullptr);
|
||||
cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_);
|
||||
if (new_imm) {
|
||||
@@ -2633,7 +2731,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context,
|
||||
// we always try to flush all immutable memtable. For atomic flush, these
|
||||
// two memtables will be marked eligible for flush in the same call to
|
||||
// AssignAtomicFlushSeq().
|
||||
new_imm->SetNextLogNumber(logfile_number_);
|
||||
new_imm->SetNextLogNumber(cur_wal_number_);
|
||||
cfd->imm()->Add(new_imm, &context->memtables_to_free_);
|
||||
}
|
||||
new_mem->Ref();
|
||||
|
||||
+29
-26
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "db/db_iter.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
@@ -42,9 +41,8 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* cmp, InternalIterator* iter,
|
||||
const Version* version, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
|
||||
bool expose_blob_index)
|
||||
bool expose_blob_index, ReadOnlyMemTable* active_mem)
|
||||
: prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
|
||||
env_(_env),
|
||||
clock_(ioptions.clock),
|
||||
@@ -58,11 +56,21 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
read_callback_(read_callback),
|
||||
sequence_(s),
|
||||
statistics_(ioptions.stats),
|
||||
max_skip_(max_sequential_skip_in_iterations),
|
||||
max_skip_(mutable_cf_options.max_sequential_skip_in_iterations),
|
||||
max_skippable_internal_keys_(read_options.max_skippable_internal_keys),
|
||||
num_internal_keys_skipped_(0),
|
||||
iterate_lower_bound_(read_options.iterate_lower_bound),
|
||||
iterate_upper_bound_(read_options.iterate_upper_bound),
|
||||
cfh_(cfh),
|
||||
timestamp_ub_(read_options.timestamp),
|
||||
timestamp_lb_(read_options.iter_start_ts),
|
||||
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0),
|
||||
active_mem_(active_mem),
|
||||
memtable_seqno_lb_((active_mem_ && !active_mem_->IsEmpty())
|
||||
? active_mem_->GetFirstSequenceNumber()
|
||||
: kMaxSequenceNumber),
|
||||
memtable_op_scan_flush_trigger_(
|
||||
mutable_cf_options.memtable_op_scan_flush_trigger),
|
||||
direction_(kForward),
|
||||
valid_(false),
|
||||
current_entry_is_merged_(false),
|
||||
@@ -76,11 +84,7 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
expose_blob_index_(expose_blob_index),
|
||||
allow_unprepared_value_(read_options.allow_unprepared_value),
|
||||
is_blob_(false),
|
||||
arena_mode_(arena_mode),
|
||||
cfh_(cfh),
|
||||
timestamp_ub_(read_options.timestamp),
|
||||
timestamp_lb_(read_options.iter_start_ts),
|
||||
timestamp_size_(timestamp_ub_ ? timestamp_ub_->size() : 0) {
|
||||
arena_mode_(arena_mode) {
|
||||
RecordTick(statistics_, NO_ITERATOR_CREATED);
|
||||
if (pin_thru_lifetime_) {
|
||||
pinned_iters_mgr_.StartPinning();
|
||||
@@ -369,6 +373,8 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// to one.
|
||||
bool reseek_done = false;
|
||||
|
||||
uint64_t mem_ops_scanned = 0;
|
||||
bool marked_for_flush = false;
|
||||
do {
|
||||
// Will update is_key_seqnum_zero_ as soon as we parsed the current key
|
||||
// but we need to save the previous value to be used in the loop.
|
||||
@@ -425,6 +431,12 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) <= 0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
if (memtable_op_scan_flush_trigger_ && active_mem_ &&
|
||||
ikey_.sequence >= memtable_seqno_lb_ && !marked_for_flush &&
|
||||
++mem_ops_scanned >= memtable_op_scan_flush_trigger_) {
|
||||
active_mem_->MarkForFlush();
|
||||
marked_for_flush = true;
|
||||
}
|
||||
} else {
|
||||
assert(!skipping_saved_key ||
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) > 0);
|
||||
@@ -446,6 +458,12 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
!iter_.iter()->IsKeyPinned() /* copy */);
|
||||
skipping_saved_key = true;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
if (memtable_op_scan_flush_trigger_ && active_mem_ &&
|
||||
ikey_.sequence >= memtable_seqno_lb_ && !marked_for_flush &&
|
||||
++mem_ops_scanned >= memtable_op_scan_flush_trigger_) {
|
||||
active_mem_->MarkForFlush();
|
||||
marked_for_flush = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
@@ -1224,6 +1242,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
|
||||
if (timestamp_lb_ != nullptr) {
|
||||
saved_key_.SetInternalKey(ikey);
|
||||
} else {
|
||||
saved_key_.SetUserKey(ikey.user_key);
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
@@ -1790,21 +1810,4 @@ void DBIter::SeekToLast() {
|
||||
StripTimestampFromUserKey(saved_key_.GetUserKey(), timestamp_size_)));
|
||||
}
|
||||
}
|
||||
|
||||
Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, read_options, ioptions, mutable_cf_options, user_key_comparator,
|
||||
internal_iter, version, sequence, false,
|
||||
max_sequential_skip_in_iterations, read_callback, cfh, expose_blob_index);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+61
-30
@@ -57,6 +57,33 @@ class Version;
|
||||
// numbers, deletion markers, overwrites, etc.
|
||||
class DBIter final : public Iterator {
|
||||
public:
|
||||
// Return a new DBIter that reads from `internal_iter` at the specified
|
||||
// `sequence` number.
|
||||
//
|
||||
// @param active_mem Pointer to the active memtable that `internal_iter`
|
||||
// is reading from. If not null, the memtable can be marked for flush
|
||||
// according to option mutable_cf_options.memtable_op_scan_flush_trigger.
|
||||
// @param arena_mode If true, the DBIter will be allocated from the arena.
|
||||
static DBIter* NewIter(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const Version* version, const SequenceNumber& sequence,
|
||||
ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh = nullptr,
|
||||
bool expose_blob_index = false,
|
||||
ReadOnlyMemTable* active_mem = nullptr,
|
||||
Arena* arena = nullptr) {
|
||||
void* mem = arena ? arena->AllocateAligned(sizeof(DBIter))
|
||||
: operator new(sizeof(DBIter));
|
||||
DBIter* db_iter = new (mem)
|
||||
DBIter(env, read_options, ioptions, mutable_cf_options,
|
||||
user_key_comparator, internal_iter, version, sequence, arena,
|
||||
read_callback, cfh, expose_blob_index, active_mem);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
// The following is grossly complicated. TODO: clean it up
|
||||
// Which direction is the iterator currently moving?
|
||||
// (1) When moving forward:
|
||||
@@ -113,14 +140,6 @@ class DBIter final : public Iterator {
|
||||
uint64_t skip_count_;
|
||||
};
|
||||
|
||||
DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, const Version* version, SequenceNumber s,
|
||||
bool arena_mode, uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, ColumnFamilyHandleImpl* cfh,
|
||||
bool expose_blob_index);
|
||||
|
||||
// No copying allowed
|
||||
DBIter(const DBIter&) = delete;
|
||||
void operator=(const DBIter&) = delete;
|
||||
@@ -220,7 +239,26 @@ class DBIter final : public Iterator {
|
||||
|
||||
bool PrepareValue() override;
|
||||
|
||||
void Prepare(const std::vector<ScanOptions>& scan_opts) override {
|
||||
std::optional<std::vector<ScanOptions>> new_scan_opts;
|
||||
new_scan_opts.emplace(scan_opts);
|
||||
scan_opts_.swap(new_scan_opts);
|
||||
if (!scan_opts.empty()) {
|
||||
iter_.Prepare(&scan_opts_.value());
|
||||
} else {
|
||||
iter_.Prepare(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, const Version* version, SequenceNumber s,
|
||||
bool arena_mode, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh, bool expose_blob_index,
|
||||
ReadOnlyMemTable* active_mem);
|
||||
|
||||
class BlobReader {
|
||||
public:
|
||||
BlobReader(const Version* version, ReadTier read_tier,
|
||||
@@ -425,6 +463,21 @@ class DBIter final : public Iterator {
|
||||
IterKey prefix_;
|
||||
|
||||
Status status_;
|
||||
Slice lazy_blob_index_;
|
||||
|
||||
// List of operands for merge operator.
|
||||
MergeContext merge_context_;
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
ColumnFamilyHandleImpl* cfh_;
|
||||
const Slice* const timestamp_ub_;
|
||||
const Slice* const timestamp_lb_;
|
||||
const size_t timestamp_size_;
|
||||
std::string saved_timestamp_;
|
||||
std::optional<std::vector<ScanOptions>> scan_opts_;
|
||||
ReadOnlyMemTable* active_mem_;
|
||||
SequenceNumber memtable_seqno_lb_;
|
||||
uint32_t memtable_op_scan_flush_trigger_;
|
||||
Direction direction_;
|
||||
bool valid_;
|
||||
bool current_entry_is_merged_;
|
||||
@@ -443,29 +496,7 @@ class DBIter final : public Iterator {
|
||||
// the stacked BlobDB implementation is used, false otherwise.
|
||||
bool expose_blob_index_;
|
||||
bool allow_unprepared_value_;
|
||||
Slice lazy_blob_index_;
|
||||
bool is_blob_;
|
||||
bool arena_mode_;
|
||||
// List of operands for merge operator.
|
||||
MergeContext merge_context_;
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
ColumnFamilyHandleImpl* cfh_;
|
||||
const Slice* const timestamp_ub_;
|
||||
const Slice* const timestamp_lb_;
|
||||
const size_t timestamp_size_;
|
||||
std::string saved_timestamp_;
|
||||
};
|
||||
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified `sequence` number
|
||||
// into appropriate user keys.
|
||||
Iterator* NewDBIterator(
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const Version* version, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, ReadCallback* read_callback,
|
||||
ColumnFamilyHandleImpl* cfh = nullptr, bool expose_blob_index = false);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -528,11 +528,10 @@ TEST_F(DBIteratorStressTest, StressTest) {
|
||||
internal_iter->target_hidden_fraction =
|
||||
target_hidden_fraction;
|
||||
internal_iter->trace = trace;
|
||||
db_iter.reset(NewDBIterator(
|
||||
db_iter.reset(DBIter::NewIter(
|
||||
env_, ropt, ImmutableOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, sequence,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
internal_iter, /*version=*/nullptr, sequence,
|
||||
nullptr /*read_callback*/));
|
||||
}
|
||||
|
||||
|
||||
+105
-167
@@ -259,10 +259,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -294,10 +293,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -322,10 +320,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -356,10 +353,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -393,10 +389,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -425,10 +420,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
@@ -465,10 +459,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -492,10 +485,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -517,10 +509,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -554,10 +545,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
@@ -586,10 +576,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -631,10 +620,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -664,10 +652,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -696,10 +683,9 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -710,10 +696,9 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -735,11 +720,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkipCountSkips) {
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
2 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -782,10 +766,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -820,10 +804,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->AddPut("c", "200");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -851,10 +835,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->AddPut("c", "200");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 202 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -886,10 +870,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
}
|
||||
internal_iter->AddPut("c", "200");
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -906,10 +890,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
}
|
||||
internal_iter->AddPut("c", "200");
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 200 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -944,10 +927,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -981,10 +964,10 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1033,10 +1016,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.max_skippable_internal_keys = 0;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -1081,10 +1063,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -1127,10 +1108,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -1167,10 +1147,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -1204,10 +1183,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
@@ -1236,10 +1214,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -1275,10 +1252,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -1314,10 +1290,10 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.max_skippable_internal_keys = i;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -1369,10 +1345,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
options.max_sequential_skip_in_iterations = 1000;
|
||||
ro.max_skippable_internal_keys = i;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -1412,11 +1387,11 @@ TEST_F(DBIteratorTest, DBIteratorTimedPutBasic) {
|
||||
internal_iter->AddTimedPut("d", "3", /*write_unix_time=*/0);
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
options.max_sequential_skip_in_iterations = 1;
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
7 /* sequence */, /*max_sequential_skip_in_iterations*/ 1,
|
||||
nullptr /* read_callback */));
|
||||
7 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1463,11 +1438,10 @@ TEST_F(DBIteratorTest, DBIterator1) {
|
||||
internal_iter->AddMerge("b", "2");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
1 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
1 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1493,11 +1467,10 @@ TEST_F(DBIteratorTest, DBIterator2) {
|
||||
internal_iter->AddMerge("b", "2");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
0 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
0 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1519,11 +1492,10 @@ TEST_F(DBIteratorTest, DBIterator3) {
|
||||
internal_iter->AddMerge("b", "2");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
2 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1545,11 +1517,10 @@ TEST_F(DBIteratorTest, DBIterator4) {
|
||||
internal_iter->AddMerge("b", "2");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
4 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
4 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1580,10 +1551,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1605,10 +1575,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1630,10 +1599,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1655,10 +1623,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 3 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1680,10 +1647,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1705,10 +1671,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1730,10 +1695,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1753,10 +1717,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddMerge("a", "merge_2");
|
||||
internal_iter->AddPut("b", "val_b");
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1785,10 +1748,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1810,10 +1772,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1835,10 +1796,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1860,10 +1820,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 3 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -1881,10 +1840,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1906,10 +1864,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1931,10 +1888,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->AddMerge("a", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1976,10 +1932,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2013,10 +1968,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2056,10 +2010,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2099,10 +2052,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2147,10 +2099,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2196,10 +2147,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2239,10 +2189,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 9 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2288,10 +2237,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 13 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2338,10 +2286,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->AddDeletion("c");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 14 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2371,11 +2318,10 @@ TEST_F(DBIteratorTest, DBIterator8) {
|
||||
internal_iter->AddPut("b", "0");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2403,11 +2349,10 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
internal_iter->AddMerge("d", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2471,11 +2416,10 @@ TEST_F(DBIteratorTest, DBIterator10) {
|
||||
internal_iter->AddPut("d", "4");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
|
||||
db_iter->Seek("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2512,7 +2456,7 @@ TEST_F(DBIteratorTest, SeekToLastOccurrenceSeq0) {
|
||||
internal_iter->AddPut("b", "2");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, 0 /* force seek */, nullptr /* read_callback */));
|
||||
@@ -2542,11 +2486,10 @@ TEST_F(DBIteratorTest, DBIterator11) {
|
||||
internal_iter->AddMerge("b", "2");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
1 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
1 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2571,7 +2514,7 @@ TEST_F(DBIteratorTest, DBIterator12) {
|
||||
internal_iter->AddSingleDeletion("b");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, 0 /* force seek */, nullptr /* read_callback */));
|
||||
@@ -2610,11 +2553,11 @@ TEST_F(DBIteratorTest, DBIterator13) {
|
||||
internal_iter->AddPut(key, "8");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
options.max_sequential_skip_in_iterations = 3;
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, 3 /* max_sequential_skip_in_iterations */,
|
||||
nullptr /* read_callback */));
|
||||
2 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), key);
|
||||
@@ -2640,11 +2583,11 @@ TEST_F(DBIteratorTest, DBIterator14) {
|
||||
internal_iter->AddPut("c", "9");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
options.max_sequential_skip_in_iterations = 1;
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
4 /* sequence */, 1 /* max_sequential_skip_in_iterations */,
|
||||
nullptr /* read_callback */));
|
||||
4 /* sequence */, nullptr /* read_callback */));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2680,11 +2623,11 @@ class DBIterWithMergeIterTest : public testing::Test {
|
||||
InternalIterator* merge_iter =
|
||||
NewMergingIterator(&icomp_, child_iters.data(), 2u);
|
||||
|
||||
db_iter_.reset(NewDBIterator(
|
||||
options_.max_sequential_skip_in_iterations = 3;
|
||||
db_iter_.reset(DBIter::NewIter(
|
||||
env_, ro_, ImmutableOptions(options_), MutableCFOptions(options_),
|
||||
BytewiseComparator(), merge_iter, nullptr /* version */,
|
||||
8 /* read data earlier than seqId 8 */,
|
||||
3 /* max iterators before reseek */, nullptr /* read_callback */));
|
||||
8 /* read data earlier than seqId 8 */, nullptr /* read_callback */));
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
@@ -3120,11 +3063,10 @@ TEST_F(DBIteratorTest, SeekPrefixTombstones) {
|
||||
internal_iter->Finish();
|
||||
|
||||
ro.prefix_same_as_start = true;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
|
||||
int skipped_keys = 0;
|
||||
|
||||
@@ -3157,11 +3099,10 @@ TEST_F(DBIteratorTest, SeekToFirstLowerBound) {
|
||||
Slice lower_bound(lower_bound_str);
|
||||
ro.iterate_lower_bound = &lower_bound;
|
||||
Options options;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
if (i == kNumKeys + 1) {
|
||||
@@ -3197,11 +3138,10 @@ TEST_F(DBIteratorTest, PrevLowerBound) {
|
||||
Slice lower_bound(lower_bound_str);
|
||||
ro.iterate_lower_bound = &lower_bound;
|
||||
Options options;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
for (int i = kNumKeys; i >= kLowerBound; --i) {
|
||||
@@ -3226,11 +3166,10 @@ TEST_F(DBIteratorTest, SeekLessLowerBound) {
|
||||
Slice lower_bound(lower_bound_str);
|
||||
ro.iterate_lower_bound = &lower_bound;
|
||||
Options options;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
|
||||
auto before_lower_bound_str = std::to_string(kLowerBound - 1);
|
||||
Slice before_lower_bound(lower_bound_str);
|
||||
@@ -3252,11 +3191,10 @@ TEST_F(DBIteratorTest, ReverseToForwardWithDisappearingKeys) {
|
||||
}
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
std::unique_ptr<Iterator> db_iter(DBIter::NewIter(
|
||||
env_, ReadOptions(), ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
10 /* sequence */, nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekForPrev("a");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -3824,6 +3824,166 @@ TEST_F(DBIteratorTest, IteratorsConsistentViewExplicitSnapshot) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, MemtableOpsScanFlushTriggerWithSeek) {
|
||||
// Tests that option memtable_op_scan_flush_trigger works when the limit
|
||||
// is reached during a Seek() operation.
|
||||
const int kTrigger = 10;
|
||||
Random* r = Random::GetTLSInstance();
|
||||
|
||||
for (int trigger : {kTrigger, kTrigger + 1}) {
|
||||
for (bool delete_only : {false, true}) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_op_scan_flush_trigger = trigger;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Base data that will be covered by a consecutive sequence of tombstones.
|
||||
int kNumKeys = delete_only ? kTrigger : kTrigger / 2;
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), r->RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(6));
|
||||
|
||||
if (delete_only) {
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(SingleDelete(Key(i)));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), r->RandomString(100)));
|
||||
}
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(Delete(Key(i)));
|
||||
}
|
||||
}
|
||||
|
||||
SetPerfLevel(PerfLevel::kEnableCount);
|
||||
get_perf_context()->Reset();
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
|
||||
// Seek to the first key, this will scan through all the tombstones and
|
||||
// hidden puts
|
||||
iter->Seek(Key(0));
|
||||
ASSERT_FALSE(
|
||||
iter->Valid()); // All keys are deleted, so iterator is not valid
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(get_perf_context()->next_on_memtable_count, kTrigger);
|
||||
|
||||
// Skipping kNumTrigger memtable entries in a single iterator operation
|
||||
// should mark the memtable for flush.
|
||||
//
|
||||
// At the end of a write, we check and update memtable to request a flush
|
||||
ASSERT_OK(Put(Key(11), "val"));
|
||||
// Before a write, we schedule memtables for flush if requested.
|
||||
ASSERT_OK(Put(Key(12), "val"));
|
||||
ASSERT_OK(db_->WaitForCompact({}));
|
||||
|
||||
if (trigger <= kTrigger) {
|
||||
// Check if memtable was flushed due to scan trigger
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
uint64_t val = 0;
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
|
||||
ASSERT_EQ(0, val);
|
||||
} else {
|
||||
uint64_t val = 0;
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
|
||||
ASSERT_EQ(kNumKeys, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, MemtableOpsScanFlushTriggerWithNext) {
|
||||
// Tests that option memtable_op_scan_flush_trigger works when the limit
|
||||
// is reached during a Next() operation, and not trigger a flush when
|
||||
// the limit is reached across multiple Next() operations.
|
||||
const int kTrigger = 10;
|
||||
Random* r = Random::GetTLSInstance();
|
||||
|
||||
for (int trigger : {kTrigger, kTrigger + 1}) {
|
||||
for (bool delete_only : {false, true}) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_op_scan_flush_trigger = trigger;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Base data that will be covered by a consecutive sequence of tombstones.
|
||||
int kNumKeys = delete_only ? kTrigger : kTrigger / 2;
|
||||
for (int i = 0; i <= kNumKeys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), r->RandomString(100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(6));
|
||||
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
if (delete_only) {
|
||||
for (int i = 1; i <= kNumKeys; ++i) {
|
||||
ASSERT_OK(SingleDelete(Key(i)));
|
||||
}
|
||||
} else {
|
||||
for (int i = 1; i <= kNumKeys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), r->RandomString(100)));
|
||||
}
|
||||
for (int i = 1; i <= kNumKeys; ++i) {
|
||||
ASSERT_OK(Delete(Key(i)));
|
||||
}
|
||||
}
|
||||
|
||||
// Total number of tombstones and hidden puts scanned across multiple
|
||||
// Next() operations below will be kTrigger, and it should not trigger a
|
||||
// flush when the limit is kTrigger + 1.
|
||||
ASSERT_OK(Put(Key(kNumKeys + 1), "v1"));
|
||||
ASSERT_OK(Delete(Key(kNumKeys + 2)));
|
||||
ASSERT_OK(Put(Key(kNumKeys + 3), "v3"));
|
||||
|
||||
SetPerfLevel(PerfLevel::kEnableCount);
|
||||
get_perf_context()->Reset();
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
iter->Seek(Key(0));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->value(), "val");
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(get_perf_context()->next_on_memtable_count, 0);
|
||||
iter->Next();
|
||||
// kTrigger tombstones and invisible puts and 1 for the visible put
|
||||
ASSERT_EQ(get_perf_context()->next_on_memtable_count, kTrigger + 1);
|
||||
iter->Next();
|
||||
ASSERT_EQ(get_perf_context()->next_on_memtable_count, kTrigger + 3);
|
||||
|
||||
// Skipping kNumTrigger memtable entries in a single iterator operation
|
||||
// should mark the memtable for flush.
|
||||
//
|
||||
// At the end of a write, we check and update memtable to request a flush
|
||||
ASSERT_OK(Put(Key(11), "val"));
|
||||
// Before a write, we schedule memtables for flush if requested.
|
||||
ASSERT_OK(Put(Key(12), "val"));
|
||||
ASSERT_OK(db_->WaitForCompact({}));
|
||||
|
||||
if (trigger <= kTrigger) {
|
||||
// Check if memtable was flushed due to scan trigger
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
uint64_t val = 0;
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
|
||||
ASSERT_EQ(0, val);
|
||||
} else {
|
||||
uint64_t val = 0;
|
||||
ASSERT_TRUE(
|
||||
db_->GetIntProperty("rocksdb.num-deletes-active-mem-table", &val));
|
||||
ASSERT_EQ(kNumKeys + 1, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -312,12 +312,12 @@ TEST_P(DbKvChecksumTest, WriteToWALCorrupted) {
|
||||
// Corrupted write batch leads to read-only mode, so we have to
|
||||
// reopen for every attempt.
|
||||
Reopen(options);
|
||||
auto log_size_pre_write = dbfull()->TEST_total_log_size();
|
||||
auto log_size_pre_write = dbfull()->TEST_wals_total_size();
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_TRUE(ExecuteWrite(nullptr /* cf_handle */).IsCorruption());
|
||||
// Confirm that nothing was written to WAL
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_wals_total_size());
|
||||
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
@@ -350,12 +350,12 @@ TEST_P(DbKvChecksumTest, WriteToWALWithColumnFamilyCorrupted) {
|
||||
// Corrupted write batch leads to read-only mode, so we have to
|
||||
// reopen for every attempt.
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
auto log_size_pre_write = dbfull()->TEST_total_log_size();
|
||||
auto log_size_pre_write = dbfull()->TEST_wals_total_size();
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_TRUE(ExecuteWrite(nullptr /* cf_handle */).IsCorruption());
|
||||
// Confirm that nothing was written to WAL
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_wals_total_size());
|
||||
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
@@ -487,7 +487,7 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALCorrupted) {
|
||||
// Reopen DB since it failed WAL write which lead to read-only mode
|
||||
Reopen(options);
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
auto log_size_pre_write = dbfull()->TEST_total_log_size();
|
||||
auto log_size_pre_write = dbfull()->TEST_wals_total_size();
|
||||
leader_batch_and_status =
|
||||
GetWriteBatch(GetCFHandleToUse(nullptr, op_type1_),
|
||||
8 /* protection_bytes_per_key */, op_type1_);
|
||||
@@ -499,7 +499,7 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALCorrupted) {
|
||||
SyncPoint::GetInstance()->ClearCallBack("WriteThread::JoinBatchGroup:Wait");
|
||||
ASSERT_EQ(1, leader_count);
|
||||
// Nothing should have been written to WAL
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_wals_total_size());
|
||||
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
|
||||
|
||||
corrupt_byte_offset++;
|
||||
@@ -599,7 +599,7 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALWithColumnFamilyCorrupted) {
|
||||
// Reopen DB since it failed WAL write which lead to read-only mode
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "ramen"}, options);
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
auto log_size_pre_write = dbfull()->TEST_total_log_size();
|
||||
auto log_size_pre_write = dbfull()->TEST_wals_total_size();
|
||||
leader_batch_and_status =
|
||||
GetWriteBatch(GetCFHandleToUse(handles_[1], op_type1_),
|
||||
8 /* protection_bytes_per_key */, op_type1_);
|
||||
@@ -612,7 +612,7 @@ TEST_P(DbKvChecksumTestMergedBatch, WriteToWALWithColumnFamilyCorrupted) {
|
||||
|
||||
ASSERT_EQ(1, leader_count);
|
||||
// Nothing should have been written to WAL
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_total_log_size());
|
||||
ASSERT_EQ(log_size_pre_write, dbfull()->TEST_wals_total_size());
|
||||
ASSERT_TRUE(dbfull()->TEST_GetBGError().IsCorruption());
|
||||
|
||||
corrupt_byte_offset++;
|
||||
|
||||
+17
-22
@@ -322,31 +322,26 @@ TEST_F(DBOptionsTest, SetWithCustomMemTableFactory) {
|
||||
}
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Try with fail_if_options_file_error=false/true to update the options
|
||||
for (bool on_error : {false, true}) {
|
||||
options.fail_if_options_file_error = on_error;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = false;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = false;
|
||||
|
||||
options.memtable_factory.reset(new DummySkipListFactory());
|
||||
Reopen(options);
|
||||
options.memtable_factory.reset(new DummySkipListFactory());
|
||||
Reopen(options);
|
||||
|
||||
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions(cfh, {{"disable_auto_compactions", "true"}}));
|
||||
ColumnFamilyDescriptor cfd;
|
||||
ASSERT_OK(cfh->GetDescriptor(&cfd));
|
||||
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
|
||||
DummySkipListFactory::kClassName());
|
||||
ColumnFamilyHandle* test = nullptr;
|
||||
ASSERT_OK(dbfull()->CreateColumnFamily(options, "test", &test));
|
||||
ASSERT_OK(test->GetDescriptor(&cfd));
|
||||
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
|
||||
DummySkipListFactory::kClassName());
|
||||
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
|
||||
ASSERT_OK(dbfull()->SetOptions(cfh, {{"disable_auto_compactions", "true"}}));
|
||||
ColumnFamilyDescriptor cfd;
|
||||
ASSERT_OK(cfh->GetDescriptor(&cfd));
|
||||
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
|
||||
DummySkipListFactory::kClassName());
|
||||
ColumnFamilyHandle* test = nullptr;
|
||||
ASSERT_OK(dbfull()->CreateColumnFamily(options, "test", &test));
|
||||
ASSERT_OK(test->GetDescriptor(&cfd));
|
||||
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
|
||||
DummySkipListFactory::kClassName());
|
||||
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(test));
|
||||
delete test;
|
||||
}
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(test));
|
||||
delete test;
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetBytesPerSync) {
|
||||
|
||||
@@ -508,6 +508,81 @@ TEST_F(DBSecondaryTest, OpenAsSecondary) {
|
||||
verify_db_func("new_foo_value", "new_bar_value");
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, OptionsOverrideTest) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.preserve_internal_time_seconds = 300;
|
||||
options.compaction_readahead_size = 200;
|
||||
options.blob_compaction_readahead_size = 100;
|
||||
Reopen(options);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
ASSERT_OK(Put("foo", "foo_value" + std::to_string(i)));
|
||||
ASSERT_OK(Put("bar", "bar_value" + std::to_string(i)));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
CompactionServiceInput input;
|
||||
|
||||
ColumnFamilyMetaData meta;
|
||||
db_->GetColumnFamilyMetaData(&meta);
|
||||
for (auto& file : meta.levels[0].files) {
|
||||
ASSERT_EQ(0, meta.levels[0].level);
|
||||
input.input_files.push_back(file.name);
|
||||
}
|
||||
ASSERT_EQ(input.input_files.size(), 3);
|
||||
|
||||
input.output_level = 1;
|
||||
input.options_file_number = dbfull()->GetVersionSet()->options_file_number();
|
||||
input.cf_name = kDefaultColumnFamilyName;
|
||||
ASSERT_OK(db_->GetDbIdentity(input.db_id));
|
||||
|
||||
ASSERT_EQ(db_->GetOptions().compaction_readahead_size, 200);
|
||||
ASSERT_EQ(db_->GetOptions().blob_compaction_readahead_size, 100);
|
||||
|
||||
Close();
|
||||
|
||||
std::string compaction_input_binary;
|
||||
ASSERT_OK(input.Write(&compaction_input_binary));
|
||||
std::string compaction_result_binary;
|
||||
|
||||
CompactionServiceOptionsOverride override_options;
|
||||
override_options.env = env_;
|
||||
override_options.table_factory.reset(
|
||||
NewBlockBasedTableFactory(BlockBasedTableOptions()));
|
||||
|
||||
ASSERT_OK(
|
||||
StringToMap("compaction_readahead_size=8388608;"
|
||||
"blob_compaction_readahead_size=4194304;"
|
||||
"some_invalid_option=ignore_me;"
|
||||
"env=this_should_not_fail;"
|
||||
"max_open_files=100;", // this should be always overriden as
|
||||
// -1 in remote compaction
|
||||
&override_options.options_map));
|
||||
|
||||
bool verified = false;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImplSecondary::OpenAndCompact::AfterOpenAsSecondary:0",
|
||||
[&](void* arg) {
|
||||
auto secondary_db = static_cast<DB*>(arg);
|
||||
auto secondary_db_options = secondary_db->GetOptions();
|
||||
// DBOption
|
||||
ASSERT_EQ(secondary_db_options.compaction_readahead_size, 8388608);
|
||||
ASSERT_EQ(secondary_db_options.max_open_files, -1);
|
||||
// CFOption
|
||||
ASSERT_EQ(secondary_db_options.blob_compaction_readahead_size, 4194304);
|
||||
verified = true;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(DB::OpenAndCompact(OpenAndCompactOptions(), dbname_,
|
||||
secondary_path_, compaction_input_binary,
|
||||
&compaction_result_binary, override_options));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ASSERT_TRUE(verified);
|
||||
}
|
||||
|
||||
namespace {
|
||||
class TraceFileEnv : public EnvWrapper {
|
||||
public:
|
||||
|
||||
+7
-7
@@ -3351,11 +3351,6 @@ class ModelDB : public DB {
|
||||
using DB::NumberLevels;
|
||||
int NumberLevels(ColumnFamilyHandle* /*column_family*/) override { return 1; }
|
||||
|
||||
using DB::MaxMemCompactionLevel;
|
||||
int MaxMemCompactionLevel(ColumnFamilyHandle* /*column_family*/) override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
using DB::Level0StopWriteTrigger;
|
||||
int Level0StopWriteTrigger(ColumnFamilyHandle* /*column_family*/) override {
|
||||
return -1;
|
||||
@@ -3412,7 +3407,7 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
Status GetCurrentWalFile(
|
||||
std::unique_ptr<LogFile>* /*current_log_file*/) override {
|
||||
std::unique_ptr<LogFile>* /*current_wal_file*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -3451,6 +3446,11 @@ class ModelDB : public DB {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetNewestUserDefinedTimestamp(
|
||||
ColumnFamilyHandle* /*cf*/, std::string* /*newest_timestamp*/) override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override { return nullptr; }
|
||||
|
||||
private:
|
||||
@@ -6419,7 +6419,7 @@ TEST_F(DBTest, TestLogCleanup) {
|
||||
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
ASSERT_OK(Put(Key(i), "val"));
|
||||
// only 2 memtables will be alive, so logs_to_free needs to always be below
|
||||
// only 2 memtables will be alive, so wals_to_free needs to always be below
|
||||
// 2
|
||||
ASSERT_LT(dbfull()->TEST_LogsToFreeSize(), static_cast<size_t>(3));
|
||||
}
|
||||
|
||||
@@ -592,7 +592,6 @@ Options DBTestBase::GetOptions(
|
||||
options_override.level_compaction_dynamic_level_bytes;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.fail_if_options_file_error = true;
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -3024,13 +3024,13 @@ TEST_F(DBWALTest, GetCompressedWalsAfterSync) {
|
||||
options.wal_compression = kZSTD;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Write something to memtable and WAL so that log_empty_ will be false after
|
||||
// Write something to memtable and WAL so that wal_empty_ will be false after
|
||||
// next DB::Open().
|
||||
ASSERT_OK(Put("a", "v"));
|
||||
|
||||
Reopen(options);
|
||||
|
||||
// New WAL is created, thanks to !log_empty_.
|
||||
// New WAL is created, thanks to !wal_empty_.
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
|
||||
ASSERT_OK(Put("b", "v"));
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
#include "utilities/merge_operators/string_append/stringappend2.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
std::string EncodeAsUint64(uint64_t v) {
|
||||
std::string dst;
|
||||
PutFixed64(&dst, v);
|
||||
return dst;
|
||||
}
|
||||
} // namespace
|
||||
class DBBasicTestWithTimestamp : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
DBBasicTestWithTimestamp()
|
||||
@@ -1480,13 +1487,24 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToUserKeyBeforeSavedKey) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp,
|
||||
FIXME_ReverseIterationWithBlobAndUnpreparedValue) {
|
||||
class ReverseIterationWithUnpreparedBlobTest
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<std::tuple<bool, uint64_t>> {
|
||||
public:
|
||||
ReverseIterationWithUnpreparedBlobTest()
|
||||
: DBBasicTestWithTimestampBase(
|
||||
"db_basic_test_with_timestamp_reverse_with_unprepare") {}
|
||||
};
|
||||
INSTANTIATE_TEST_CASE_P(ReverseIterationWithUnpreparedBlobTest,
|
||||
ReverseIterationWithUnpreparedBlobTest,
|
||||
::testing::Combine(::testing::Values(true, false),
|
||||
::testing::Values(0, 2)));
|
||||
TEST_P(ReverseIterationWithUnpreparedBlobTest, Basic) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
options.enable_blob_files = true;
|
||||
options.max_sequential_skip_in_iterations = 0;
|
||||
options.max_sequential_skip_in_iterations = std::get<1>(GetParam());
|
||||
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
@@ -1501,7 +1519,7 @@ TEST_F(DBBasicTestWithTimestamp,
|
||||
for (uint64_t key = 0; key <= kMaxKey; ++key) {
|
||||
for (size_t i = 0; i < write_timestamps.size(); ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key1(key), write_timestamps[i],
|
||||
"value" + std::to_string(i)));
|
||||
Key1(key) + "value" + std::to_string(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1513,17 +1531,28 @@ TEST_F(DBBasicTestWithTimestamp,
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &read_timestamp;
|
||||
read_opts.allow_unprepared_value = true;
|
||||
read_opts.allow_unprepared_value = std::get<0>(GetParam());
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_opts));
|
||||
|
||||
it->SeekForPrev(Key1(kMaxKey));
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
uint64_t key = kMaxKey;
|
||||
int count = 0;
|
||||
while (it->Valid()) {
|
||||
ASSERT_OK(it->status());
|
||||
|
||||
// FIXME: PrepareValue() should succeed and status() should remain OK
|
||||
ASSERT_FALSE(it->PrepareValue());
|
||||
ASSERT_TRUE(it->status().IsCorruption());
|
||||
ASSERT_TRUE(it->PrepareValue());
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_EQ(it->key(), Key1(key));
|
||||
ASSERT_EQ(it->timestamp(), Timestamp(3, 0));
|
||||
ASSERT_EQ(it->value(), Key1(key) + "value" + std::to_string(1));
|
||||
key--;
|
||||
count++;
|
||||
it->Prev();
|
||||
}
|
||||
ASSERT_OK(it->status());
|
||||
ASSERT_EQ(kMaxKey + 1, count);
|
||||
}
|
||||
|
||||
Close();
|
||||
@@ -3746,17 +3775,42 @@ INSTANTIATE_TEST_CASE_P(
|
||||
test::UserDefinedTimestampTestMode::kStripUserDefinedTimestamp,
|
||||
test::UserDefinedTimestampTestMode::kNormal));
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, EnableDisableUDT) {
|
||||
// Test params:
|
||||
// 1) whether to flush before close
|
||||
class EnableDisableUDTTest : public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
EnableDisableUDTTest()
|
||||
: DBBasicTestWithTimestampBase("/enable_disable_udt") {}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(EnableDisableUDTTest, EnableDisableUDTTest,
|
||||
::testing::Values(true, false));
|
||||
|
||||
TEST_P(EnableDisableUDTTest, Basic) {
|
||||
Options options = CurrentOptions();
|
||||
// Un-flushed data before close will involve a WAL replay on DB reopen.
|
||||
bool flush_before_close = GetParam();
|
||||
options.env = env_;
|
||||
// Create a column family without user-defined timestamps.
|
||||
options.comparator = BytewiseComparator();
|
||||
options.persist_user_defined_timestamps = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ReadOptions ropts;
|
||||
std::string read_ts;
|
||||
std::string value;
|
||||
std::string key_ts;
|
||||
|
||||
// Create one SST file, its user keys have no user-defined timestamps.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "val1"));
|
||||
ASSERT_OK(Flush(0));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "val0"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "bar", "val0"));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), "bar", "baz"));
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "foo", &value));
|
||||
ASSERT_EQ("val0", value);
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "bar", &value).IsNotFound());
|
||||
if (flush_before_close) {
|
||||
ASSERT_OK(Flush(0));
|
||||
}
|
||||
Close();
|
||||
|
||||
// Reopen the existing column family and enable user-defined timestamps
|
||||
@@ -3765,47 +3819,63 @@ TEST_F(DBBasicTestWithTimestamp, EnableDisableUDT) {
|
||||
options.persist_user_defined_timestamps = false;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "foo", &value).IsInvalidArgument());
|
||||
std::string read_ts;
|
||||
PutFixed64(&read_ts, 0);
|
||||
ReadOptions ropts;
|
||||
// Read data from previous session before and after compaction.
|
||||
read_ts = EncodeAsUint64(1);
|
||||
Slice read_ts_slice = read_ts;
|
||||
ropts.timestamp = &read_ts_slice;
|
||||
std::string key_ts;
|
||||
// Entries in pre-existing SST files are treated as if they have minimum
|
||||
// user-defined timestamps.
|
||||
ASSERT_OK(db_->Get(ropts, "foo", &value, &key_ts));
|
||||
ASSERT_EQ("val1", value);
|
||||
ASSERT_EQ(read_ts, key_ts);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "foo", &value).IsInvalidArgument());
|
||||
// Entries in pre-existing SST files are treated as if they have minimum
|
||||
// user-defined timestamps.
|
||||
ASSERT_OK(db_->Get(ropts, "foo", &value, &key_ts));
|
||||
ASSERT_EQ("val0", value);
|
||||
ASSERT_EQ(EncodeAsUint64(0), key_ts);
|
||||
ASSERT_TRUE(db_->Get(ropts, "bar", &value, &key_ts).IsNotFound());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
// Do timestamped read / write.
|
||||
std::string write_ts;
|
||||
PutFixed64(&write_ts, 1);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", write_ts, "val2"));
|
||||
read_ts.clear();
|
||||
PutFixed64(&read_ts, 1);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", EncodeAsUint64(1), "val1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "bar", EncodeAsUint64(1), "val1"));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), "bar", "baz", EncodeAsUint64(2)));
|
||||
ASSERT_OK(db_->Get(ropts, "foo", &value, &key_ts));
|
||||
ASSERT_EQ("val2", value);
|
||||
ASSERT_EQ(write_ts, key_ts);
|
||||
ASSERT_EQ("val1", value);
|
||||
ASSERT_EQ(EncodeAsUint64(1), key_ts);
|
||||
ASSERT_OK(db_->Get(ropts, "bar", &value, &key_ts));
|
||||
ASSERT_EQ("val1", value);
|
||||
ASSERT_EQ(EncodeAsUint64(1), key_ts);
|
||||
read_ts = EncodeAsUint64(2);
|
||||
ASSERT_TRUE(db_->Get(ropts, "bar", &value, &key_ts).IsNotFound());
|
||||
// The user keys in this SST file don't have user-defined timestamps either,
|
||||
// because `persist_user_defined_timestamps` flag is set to false.
|
||||
ASSERT_OK(Flush(0));
|
||||
if (flush_before_close) {
|
||||
ASSERT_OK(Flush(0));
|
||||
}
|
||||
Close();
|
||||
|
||||
// Reopen the existing column family while disabling user-defined timestamps.
|
||||
options.comparator = BytewiseComparator();
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_TRUE(db_->Get(ropts, "foo", &value).IsInvalidArgument());
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "foo", &value));
|
||||
ASSERT_EQ("val2", value);
|
||||
// Read data from previous session before and after compaction.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ASSERT_TRUE(db_->Get(ropts, "foo", &value).IsInvalidArgument());
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "foo", &value));
|
||||
ASSERT_EQ("val1", value);
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "bar", &value).IsNotFound());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
// Continue to write / read the column family without user-defined timestamps.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "val3"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "val2"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "bar", "val2"));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), "bar", "baz"));
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "foo", &value));
|
||||
ASSERT_EQ("val3", value);
|
||||
ASSERT_EQ("val2", value);
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "bar", &value).IsNotFound());
|
||||
if (flush_before_close) {
|
||||
ASSERT_OK(Flush(0));
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -4844,6 +4914,117 @@ TEST_F(DBBasicTestWithTimestamp, TimestampFilterTableReadOnGet) {
|
||||
Close();
|
||||
}
|
||||
|
||||
class GetNewestUserDefinedTimestampTest : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
explicit GetNewestUserDefinedTimestampTest()
|
||||
: DBBasicTestWithTimestampBase("get_newest_udt_test") {}
|
||||
};
|
||||
|
||||
TEST_F(GetNewestUserDefinedTimestampTest, Basic) {
|
||||
std::string newest_timestamp;
|
||||
// UDT disabled, get InvalidArgument.
|
||||
ASSERT_TRUE(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp)
|
||||
.IsInvalidArgument());
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.max_write_buffer_number = 5;
|
||||
options.min_write_buffer_number_to_merge = 4;
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// UDT persisted, get NotSupported.
|
||||
ASSERT_TRUE(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp)
|
||||
.IsNotSupported());
|
||||
|
||||
options.persist_user_defined_timestamps = false;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_TRUE(
|
||||
db_->GetNewestUserDefinedTimestamp(nullptr, nullptr).IsInvalidArgument());
|
||||
|
||||
ColumnFamilyHandleImpl* cfh = static_cast_with_check<ColumnFamilyHandleImpl>(
|
||||
db_->DefaultColumnFamily());
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
// The column family hasn't seen any user defined timestamp
|
||||
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
|
||||
ASSERT_TRUE(newest_timestamp.empty());
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(1), "val1"));
|
||||
// Testing get newest timestamp from mutable memtable.
|
||||
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
|
||||
ASSERT_EQ(EncodeAsUint64(1), newest_timestamp);
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(2), "val2"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable(cfd));
|
||||
// Testing get the newest timestamp from immutable memtable because the
|
||||
// mutable one is empty.
|
||||
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
|
||||
ASSERT_EQ(EncodeAsUint64(2), newest_timestamp);
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(3), "val3"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(4), "val4"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable(cfd));
|
||||
// Testing get the newest timestamp from the more recent immutable memtable
|
||||
// when there are multiple immutable memtables.
|
||||
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
|
||||
ASSERT_EQ(EncodeAsUint64(4), newest_timestamp);
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(5), "val5"));
|
||||
// Testing get newest timestamp from mutable memtable when it has data, in the
|
||||
// presence of immutable memtables.
|
||||
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
|
||||
ASSERT_EQ(EncodeAsUint64(5), newest_timestamp);
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
// After flushing and all the user defined timestamp are flushed. User defined
|
||||
// timestamp info for SST files is available from MANIFEST.
|
||||
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
|
||||
ASSERT_EQ(EncodeAsUint64(5), newest_timestamp);
|
||||
|
||||
Reopen(options);
|
||||
// Similar after flush, when there is no memtables, but some SST files,
|
||||
// if MANIFEST records the upperbound of flushed timestamps because timestamps
|
||||
// are not persisted in SST files, this info can be found.
|
||||
ASSERT_OK(db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
|
||||
ASSERT_EQ(EncodeAsUint64(5), newest_timestamp);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(GetNewestUserDefinedTimestampTest, ConcurrentWrites) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
|
||||
options.persist_user_defined_timestamps = false;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(10);
|
||||
std::atomic<uint64_t> current_ts{0};
|
||||
for (int i = 0; i < 10; i++) {
|
||||
threads.emplace_back([this, i, ¤t_ts]() {
|
||||
if (i % 2 == 0) {
|
||||
std::string newest_timestamp;
|
||||
ASSERT_OK(
|
||||
db_->GetNewestUserDefinedTimestamp(nullptr, &newest_timestamp));
|
||||
} else {
|
||||
uint64_t write_ts = current_ts.fetch_add(1);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), Key(1), EncodeAsUint64(write_ts),
|
||||
"val" + std::to_string(i)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+75
-1
@@ -987,7 +987,7 @@ TEST_P(DBWriteTest, RecycleLogToggleTest) {
|
||||
|
||||
options.recycle_log_file_num = 1;
|
||||
Reopen(options);
|
||||
// 1.log is added to alive_log_files_
|
||||
// 1.log is added to alive_wal_files_
|
||||
ASSERT_OK(Put(Key(2), "val1"));
|
||||
ASSERT_OK(Flush());
|
||||
// 1.log should be deleted and not recycled, since it
|
||||
@@ -1000,6 +1000,80 @@ TEST_P(DBWriteTest, RecycleLogToggleTest) {
|
||||
ASSERT_EQ(Get(Key(1)), "val2");
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, IngestWriteBatchWithIndex) {
|
||||
if (GetParam() == kPipelinedWrite) {
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = GetOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
Options cf_options = GetOptions();
|
||||
cf_options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
CreateColumnFamilies({"cf1", "cf2"}, cf_options);
|
||||
ReopenWithColumnFamilies({"default", "cf1", "cf2"},
|
||||
{options, cf_options, cf_options});
|
||||
|
||||
// default cf
|
||||
auto wbwi1 = std::make_shared<WriteBatchWithIndex>(options.comparator, 0,
|
||||
/*overwrite_key=*/true);
|
||||
ASSERT_OK(wbwi1->Put("key1", "value1"));
|
||||
ASSERT_OK(wbwi1->Put("key2", "value2"));
|
||||
if (GetParam() == kPipelinedWrite) {
|
||||
ASSERT_TRUE(db_->IngestWriteBatchWithIndex({}, wbwi1).IsNotSupported());
|
||||
return;
|
||||
}
|
||||
// Test disableWAL=false
|
||||
ASSERT_TRUE(db_->IngestWriteBatchWithIndex({}, wbwi1).IsNotSupported());
|
||||
|
||||
WriteOptions wo;
|
||||
wo.disableWAL = true;
|
||||
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, wbwi1));
|
||||
ASSERT_EQ("value1", Get("key1"));
|
||||
ASSERT_EQ("value2", Get("key2"));
|
||||
|
||||
// Test with overwrites
|
||||
auto wbwi = std::make_shared<WriteBatchWithIndex>(options.comparator, 0,
|
||||
/*overwrite_key=*/true);
|
||||
ASSERT_OK(wbwi->Put("key2", "value3"));
|
||||
ASSERT_OK(wbwi->Delete("key1")); // Delete an existing key
|
||||
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, wbwi));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key1"));
|
||||
ASSERT_EQ("value3", Get("key2"));
|
||||
|
||||
auto wbwi2 = std::make_shared<WriteBatchWithIndex>(options.comparator, 0,
|
||||
/*overwrite_key=*/true);
|
||||
ASSERT_OK(wbwi2->Put(handles_[1], "cf1_key1", "cf1_value1"));
|
||||
ASSERT_OK(wbwi2->Delete(handles_[1], "cf1_key2"));
|
||||
// Test ingestion with column family
|
||||
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, wbwi2));
|
||||
ASSERT_EQ("cf1_value1", Get(1, "cf1_key1"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "cf1_key2"));
|
||||
|
||||
auto wbwi3 = std::make_shared<WriteBatchWithIndex>(options.comparator, 0,
|
||||
/*overwrite_key=*/true);
|
||||
ASSERT_OK(wbwi3->Merge(handles_[2], "cf2_key1", "cf2_value1"));
|
||||
ASSERT_OK(wbwi3->Merge(handles_[2], "cf2_key1", "cf2_value2"));
|
||||
// Test ingestion with merge operations
|
||||
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, wbwi3));
|
||||
ASSERT_EQ("cf2_value1,cf2_value2", Get(2, "cf2_key1"));
|
||||
|
||||
// Test with overwrite_key = false
|
||||
auto wbwi_no_overwrite = std::make_shared<WriteBatchWithIndex>(
|
||||
options.comparator, 0, /*overwrite_key=*/false);
|
||||
ASSERT_OK(wbwi_no_overwrite->Put("key1", "value1"));
|
||||
Status s = db_->IngestWriteBatchWithIndex(wo, wbwi_no_overwrite);
|
||||
ASSERT_TRUE(s.IsNotSupported());
|
||||
|
||||
auto empty_wbwi = std::make_shared<WriteBatchWithIndex>(
|
||||
options.comparator, 0, /*overwrite_key=*/true);
|
||||
ASSERT_OK(db_->IngestWriteBatchWithIndex(wo, empty_wbwi));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Should fail when trying to ingest to non-existent column family
|
||||
ASSERT_NOK(db_->IngestWriteBatchWithIndex(wo, wbwi2));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBWriteTestInstance, DBWriteTest,
|
||||
testing::Values(DBTestBase::kDefault,
|
||||
DBTestBase::kConcurrentWALWrites,
|
||||
|
||||
+10
-4
@@ -275,9 +275,6 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
return;
|
||||
}
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Set regular background error\n");
|
||||
|
||||
bool paranoid = db_options_.paranoid_checks;
|
||||
Status::Severity sev = Status::Severity::kFatalError;
|
||||
Status new_bg_err;
|
||||
@@ -335,12 +332,21 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
if (!s.ok() && (s.severity() > bg_error_.severity())) {
|
||||
bg_error_ = s;
|
||||
} else {
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"ErrorHandler: Hit less severe background error\n");
|
||||
|
||||
// This error is less severe than previously encountered error. Don't
|
||||
// take any further action
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool stop = bg_error_.severity() >= Status::Severity::kHardError;
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log,
|
||||
"ErrorHandler: Set regular background error, auto_recovery=%d, stop=%d\n",
|
||||
int{auto_recovery}, int{stop});
|
||||
|
||||
recover_context_ = context;
|
||||
if (auto_recovery) {
|
||||
recovery_in_prog_ = true;
|
||||
@@ -351,7 +357,7 @@ void ErrorHandler::HandleKnownErrors(const Status& bg_err,
|
||||
RecoverFromNoSpace();
|
||||
}
|
||||
}
|
||||
if (bg_error_.severity() >= Status::Severity::kHardError) {
|
||||
if (stop) {
|
||||
is_db_stopped_.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2693,6 +2693,81 @@ TEST_F(ExternalSSTFileBasicTest, IngestWithTemperature) {
|
||||
}
|
||||
}
|
||||
|
||||
// This tests an internal user's exact usage and expectation of the
|
||||
// IngestExternalFiles APIs to bulk load and replace files.
|
||||
TEST_F(ExternalSSTFileBasicTest,
|
||||
AtomicReplaceColumnFamilyWithIngestedVersionKey) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
|
||||
options.num_levels = 7;
|
||||
options.disallow_memtable_writes = false;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
std::string data_file_original = sst_files_dir_ + "data_original";
|
||||
ASSERT_OK(sst_file_writer.Open(data_file_original));
|
||||
ASSERT_OK(sst_file_writer.Put("ukey1", "uval1_orig"));
|
||||
ASSERT_OK(sst_file_writer.Put("ukey2", "uval2_orig"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
ASSERT_OK(db_->IngestExternalFile(db_->DefaultColumnFamily(),
|
||||
{data_file_original},
|
||||
IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_OK(Put("data_version", "v_original"));
|
||||
ASSERT_OK(Flush());
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "data_version", &value));
|
||||
ASSERT_EQ(value, "v_original");
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "ukey1", &value));
|
||||
ASSERT_EQ(value, "uval1_orig");
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "ukey2", &value));
|
||||
ASSERT_EQ(value, "uval2_orig");
|
||||
// Set up a 1) data version key file on L0, and 2) a user data file on L6
|
||||
// to test the initial transitioning to use `atomic_replace_range`.
|
||||
ASSERT_EQ("1,0,0,0,0,0,1", FilesPerLevel());
|
||||
|
||||
// Test multiple cycles of replacing by atomically ingest a data file and a
|
||||
// version key file while replace the whole range in the column family.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
std::string version_file_path =
|
||||
sst_files_dir_ + "version" + std::to_string(i);
|
||||
ASSERT_OK(sst_file_writer.Open(version_file_path));
|
||||
ASSERT_OK(sst_file_writer.Put("data_version", "v" + std::to_string(i)));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
std::string file_path = sst_files_dir_ + std::to_string(i);
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
ASSERT_OK(sst_file_writer.Put("ukey1", "uval1" + std::to_string(i)));
|
||||
ASSERT_OK(sst_file_writer.Put("ukey2", "uval2" + std::to_string(i)));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
IngestExternalFileArg arg;
|
||||
arg.column_family = db_->DefaultColumnFamily();
|
||||
arg.external_files = {version_file_path, file_path};
|
||||
arg.atomic_replace_range = {{nullptr, nullptr}};
|
||||
// Test both fail_if_not_bottomost_level: true and false
|
||||
arg.options.fail_if_not_bottommost_level = i % 2 == 0;
|
||||
arg.options.snapshot_consistency = false;
|
||||
// Ingest 1) a new data version file and 2) a new user data file while erase
|
||||
// the whole column family
|
||||
Status s = db_->IngestExternalFiles({arg});
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Check ingestion result and the expected LSM shape:
|
||||
// Two files on L6, 1) a data version file 2) a user data file.
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "ukey1", &value));
|
||||
ASSERT_EQ(value, "uval1" + std::to_string(i));
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "ukey2", &value));
|
||||
ASSERT_EQ(value, "uval2" + std::to_string(i));
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "data_version", &value));
|
||||
ASSERT_EQ(value, "v" + std::to_string(i));
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, FailIfNotBottommostLevelAndDisallowMemtable) {
|
||||
for (bool disallow_memtable : {false, true}) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
@@ -610,16 +610,8 @@ Status ExternalSstFileIngestionJob::AssignLevelsForOneBatch(
|
||||
current_time = oldest_ancester_time =
|
||||
static_cast<uint64_t>(temp_current_time);
|
||||
}
|
||||
uint64_t tail_size = 0;
|
||||
bool contain_no_data_blocks = file->table_properties.num_entries > 0 &&
|
||||
(file->table_properties.num_entries ==
|
||||
file->table_properties.num_range_deletions);
|
||||
if (file->table_properties.tail_start_offset > 0 ||
|
||||
contain_no_data_blocks) {
|
||||
uint64_t file_size = file->fd.GetFileSize();
|
||||
assert(file->table_properties.tail_start_offset <= file_size);
|
||||
tail_size = file_size - file->table_properties.tail_start_offset;
|
||||
}
|
||||
uint64_t tail_size = FileMetaData::CalculateTailSize(
|
||||
file->fd.GetFileSize(), file->table_properties);
|
||||
|
||||
bool marked_for_compaction =
|
||||
file->table_properties.num_range_deletions == 1 &&
|
||||
|
||||
+2
-2
@@ -901,9 +901,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
for (ReadOnlyMemTable* m : mems_) {
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Flushing memtable id %" PRIu64
|
||||
" with next log file: %" PRIu64 "\n",
|
||||
" with next log file: %" PRIu64 ", marked_for_flush: %d\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, m->GetID(),
|
||||
m->GetNextLogNumber());
|
||||
m->GetNextLogNumber(), m->IsMarkedForFlush());
|
||||
if (logical_strip_timestamp) {
|
||||
memtables.push_back(m->NewTimestampStrippingIterator(
|
||||
ro, /*seqno_to_time_mapping=*/nullptr, &arena,
|
||||
|
||||
@@ -201,15 +201,8 @@ Status ImportColumnFamilyJob::Run() {
|
||||
const auto& f = files_to_import_[i][j];
|
||||
const auto& file_metadata = *metadatas_[i][j];
|
||||
|
||||
uint64_t tail_size = 0;
|
||||
bool contain_no_data_blocks = f.table_properties.num_entries > 0 &&
|
||||
(f.table_properties.num_entries ==
|
||||
f.table_properties.num_range_deletions);
|
||||
if (f.table_properties.tail_start_offset > 0 || contain_no_data_blocks) {
|
||||
uint64_t file_size = f.fd.GetFileSize();
|
||||
assert(f.table_properties.tail_start_offset <= file_size);
|
||||
tail_size = file_size - f.table_properties.tail_start_offset;
|
||||
}
|
||||
uint64_t tail_size = FileMetaData::CalculateTailSize(f.fd.GetFileSize(),
|
||||
f.table_properties);
|
||||
|
||||
VersionEdit dummy_version_edit;
|
||||
dummy_version_edit.AddFile(
|
||||
|
||||
+7
-7
@@ -123,7 +123,7 @@ struct JobContext {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
|
||||
return memtables_to_free.size() > 0 || wals_to_free.size() > 0 ||
|
||||
job_snapshot != nullptr || sv_have_sth;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ struct JobContext {
|
||||
// contexts for installing superversions for multiple column families
|
||||
std::vector<SuperVersionContext> superversion_contexts;
|
||||
|
||||
autovector<log::Writer*> logs_to_free;
|
||||
autovector<log::Writer*> wals_to_free;
|
||||
|
||||
// the current manifest_file_number, log_number and prev_log_number
|
||||
// that corresponds to the set of files in 'live'.
|
||||
@@ -207,8 +207,8 @@ struct JobContext {
|
||||
uint64_t prev_log_number;
|
||||
|
||||
uint64_t min_pending_output = 0;
|
||||
uint64_t prev_total_log_size = 0;
|
||||
size_t num_alive_log_files = 0;
|
||||
uint64_t prev_wals_total_size = 0;
|
||||
size_t num_alive_wal_files = 0;
|
||||
uint64_t size_log_to_delete = 0;
|
||||
|
||||
// Snapshot taken before flush/compaction job.
|
||||
@@ -237,18 +237,18 @@ struct JobContext {
|
||||
for (auto m : memtables_to_free) {
|
||||
delete m;
|
||||
}
|
||||
for (auto l : logs_to_free) {
|
||||
for (auto l : wals_to_free) {
|
||||
delete l;
|
||||
}
|
||||
|
||||
memtables_to_free.clear();
|
||||
logs_to_free.clear();
|
||||
wals_to_free.clear();
|
||||
job_snapshot.reset();
|
||||
}
|
||||
|
||||
~JobContext() {
|
||||
assert(memtables_to_free.size() == 0);
|
||||
assert(logs_to_free.size() == 0);
|
||||
assert(wals_to_free.size() == 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+7
-5
@@ -147,7 +147,6 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
const Comparator* ucmp = cmp.user_comparator();
|
||||
assert(ucmp);
|
||||
ts_sz_ = ucmp->timestamp_size();
|
||||
persist_user_defined_timestamps_ = ioptions.persist_user_defined_timestamps;
|
||||
}
|
||||
|
||||
MemTable::~MemTable() {
|
||||
@@ -175,6 +174,11 @@ size_t MemTable::ApproximateMemoryUsage() {
|
||||
}
|
||||
|
||||
bool MemTable::ShouldFlushNow() {
|
||||
if (IsMarkedForFlush()) {
|
||||
// TODO: dedicated flush reason when marked for flush
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is set if memtable_max_range_deletions is > 0,
|
||||
// and that many range deletions are done
|
||||
if (memtable_max_range_deletions_ > 0 &&
|
||||
@@ -1801,7 +1805,7 @@ uint64_t MemTable::GetMinLogContainingPrepSection() {
|
||||
}
|
||||
|
||||
void MemTable::MaybeUpdateNewestUDT(const Slice& user_key) {
|
||||
if (ts_sz_ == 0 || persist_user_defined_timestamps_) {
|
||||
if (ts_sz_ == 0) {
|
||||
return;
|
||||
}
|
||||
const Comparator* ucmp = GetInternalKeyComparator().user_comparator();
|
||||
@@ -1812,9 +1816,7 @@ void MemTable::MaybeUpdateNewestUDT(const Slice& user_key) {
|
||||
}
|
||||
|
||||
const Slice& MemTable::GetNewestUDT() const {
|
||||
// This path should not be invoked for MemTables that does not enable the UDT
|
||||
// in Memtable only feature.
|
||||
assert(ts_sz_ > 0 && !persist_user_defined_timestamps_);
|
||||
assert(ts_sz_ > 0);
|
||||
return newest_udt_;
|
||||
}
|
||||
|
||||
|
||||
+13
-8
@@ -30,6 +30,7 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "table/multiget_context.h"
|
||||
#include "util/atomic.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/dynamic_bloom.h"
|
||||
#include "util/hash.h"
|
||||
@@ -354,13 +355,13 @@ class ReadOnlyMemTable {
|
||||
// be flushed to storage
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
uint64_t GetNextLogNumber() const { return mem_next_logfile_number_; }
|
||||
uint64_t GetNextLogNumber() const { return mem_next_walfile_number_; }
|
||||
|
||||
// Sets the next active logfile number when this memtable is about to
|
||||
// be flushed to storage
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
void SetNextLogNumber(uint64_t num) { mem_next_logfile_number_ = num; }
|
||||
void SetNextLogNumber(uint64_t num) { mem_next_walfile_number_ = num; }
|
||||
|
||||
// REQUIRES: db_mutex held.
|
||||
void SetID(uint64_t id) { id_ = id; }
|
||||
@@ -496,6 +497,10 @@ class ReadOnlyMemTable {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MarkForFlush() { marked_for_flush_.StoreRelaxed(true); }
|
||||
|
||||
bool IsMarkedForFlush() const { return marked_for_flush_.LoadRelaxed(); }
|
||||
|
||||
protected:
|
||||
friend class MemTableList;
|
||||
|
||||
@@ -511,7 +516,7 @@ class ReadOnlyMemTable {
|
||||
VersionEdit edit_;
|
||||
|
||||
// The log files earlier than this number can be deleted.
|
||||
uint64_t mem_next_logfile_number_{0};
|
||||
uint64_t mem_next_walfile_number_{0};
|
||||
|
||||
// Memtable id to track flush.
|
||||
uint64_t id_ = 0;
|
||||
@@ -524,6 +529,8 @@ class ReadOnlyMemTable {
|
||||
|
||||
// Flush job info of the current memtable.
|
||||
std::unique_ptr<FlushJobInfo> flush_job_info_;
|
||||
|
||||
RelaxedAtomic<bool> marked_for_flush_{false};
|
||||
};
|
||||
|
||||
class MemTable final : public ReadOnlyMemTable {
|
||||
@@ -818,6 +825,8 @@ class MemTable final : public ReadOnlyMemTable {
|
||||
is_range_del_table_empty_;
|
||||
}
|
||||
|
||||
// Gets the newest user defined timestamps in the memtable. This should only
|
||||
// be called when user defined timestamp is enabled.
|
||||
const Slice& GetNewestUDT() const override;
|
||||
|
||||
// Returns Corruption status if verification fails.
|
||||
@@ -893,14 +902,10 @@ class MemTable final : public ReadOnlyMemTable {
|
||||
// Size in bytes for the user-defined timestamps.
|
||||
size_t ts_sz_;
|
||||
|
||||
// Whether to persist user-defined timestamps
|
||||
bool persist_user_defined_timestamps_;
|
||||
|
||||
// Newest user-defined timestamp contained in this MemTable. For ts1, and ts2
|
||||
// if Comparator::CompareTimestamp(ts1, ts2) > 0, ts1 is considered newer than
|
||||
// ts2. We track this field for a MemTable if its column family has UDT
|
||||
// feature enabled and the `persist_user_defined_timestamp` flag is false.
|
||||
// Otherwise, this field just contains an empty Slice.
|
||||
// feature enabled.
|
||||
Slice newest_udt_;
|
||||
|
||||
// Updates flush_state_ using ShouldFlushNow()
|
||||
|
||||
+16
-11
@@ -51,9 +51,7 @@ void MemTableListVersion::UnrefMemTable(
|
||||
|
||||
MemTableListVersion::MemTableListVersion(
|
||||
size_t* parent_memtable_list_memory_usage, const MemTableListVersion& old)
|
||||
: max_write_buffer_number_to_maintain_(
|
||||
old.max_write_buffer_number_to_maintain_),
|
||||
max_write_buffer_size_to_maintain_(
|
||||
: max_write_buffer_size_to_maintain_(
|
||||
old.max_write_buffer_size_to_maintain_),
|
||||
parent_memtable_list_memory_usage_(parent_memtable_list_memory_usage) {
|
||||
memlist_ = old.memlist_;
|
||||
@@ -69,10 +67,8 @@ MemTableListVersion::MemTableListVersion(
|
||||
|
||||
MemTableListVersion::MemTableListVersion(
|
||||
size_t* parent_memtable_list_memory_usage,
|
||||
int max_write_buffer_number_to_maintain,
|
||||
int64_t max_write_buffer_size_to_maintain)
|
||||
: max_write_buffer_number_to_maintain_(max_write_buffer_number_to_maintain),
|
||||
max_write_buffer_size_to_maintain_(max_write_buffer_size_to_maintain),
|
||||
: max_write_buffer_size_to_maintain_(max_write_buffer_size_to_maintain),
|
||||
parent_memtable_list_memory_usage_(parent_memtable_list_memory_usage) {}
|
||||
|
||||
void MemTableListVersion::Ref() { ++refs_; }
|
||||
@@ -323,8 +319,7 @@ void MemTableListVersion::Remove(ReadOnlyMemTable* m,
|
||||
memlist_.remove(m);
|
||||
|
||||
m->MarkFlushed();
|
||||
if (max_write_buffer_size_to_maintain_ > 0 ||
|
||||
max_write_buffer_number_to_maintain_ > 0) {
|
||||
if (max_write_buffer_size_to_maintain_ > 0) {
|
||||
memlist_history_.push_front(m);
|
||||
// Unable to get size of mutable memtable at this point, pass 0 to
|
||||
// TrimHistory as a best effort.
|
||||
@@ -356,9 +351,6 @@ bool MemTableListVersion::MemtableLimitExceeded(size_t usage) {
|
||||
// whether to trim history
|
||||
return MemoryAllocatedBytesExcludingLast() + usage >=
|
||||
static_cast<size_t>(max_write_buffer_size_to_maintain_);
|
||||
} else if (max_write_buffer_number_to_maintain_ > 0) {
|
||||
return memlist_.size() + memlist_history_.size() >
|
||||
static_cast<size_t>(max_write_buffer_number_to_maintain_);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -382,6 +374,19 @@ bool MemTableListVersion::TrimHistory(autovector<ReadOnlyMemTable*>* to_delete,
|
||||
return ret;
|
||||
}
|
||||
|
||||
const Slice& MemTableListVersion::GetNewestUDT() const {
|
||||
static Slice kEmptySlice;
|
||||
for (auto it = memlist_.begin(); it != memlist_.end(); ++it) {
|
||||
ReadOnlyMemTable* m = *it;
|
||||
Slice timestamp = m->GetNewestUDT();
|
||||
assert(!timestamp.empty() || m->IsEmpty());
|
||||
if (!timestamp.empty()) {
|
||||
return m->GetNewestUDT();
|
||||
}
|
||||
}
|
||||
return kEmptySlice;
|
||||
}
|
||||
|
||||
// Returns true if there is at least one memtable on which flush has
|
||||
// not yet started.
|
||||
bool MemTableList::IsFlushPending() const {
|
||||
|
||||
+6
-5
@@ -45,7 +45,6 @@ class MemTableListVersion {
|
||||
explicit MemTableListVersion(size_t* parent_memtable_list_memory_usage,
|
||||
const MemTableListVersion& old);
|
||||
explicit MemTableListVersion(size_t* parent_memtable_list_memory_usage,
|
||||
int max_write_buffer_number_to_maintain,
|
||||
int64_t max_write_buffer_size_to_maintain);
|
||||
|
||||
void Ref();
|
||||
@@ -150,6 +149,12 @@ class MemTableListVersion {
|
||||
|
||||
int NumFlushed() const { return static_cast<int>(memlist_history_.size()); }
|
||||
|
||||
// Gets the newest user defined timestamps from the immutable memtables.
|
||||
// This returns the newest user defined timestamp found in the most recent
|
||||
// immutable memtable. This should only be called when user defined timestamp
|
||||
// is enabled.
|
||||
const Slice& GetNewestUDT() const;
|
||||
|
||||
private:
|
||||
friend class MemTableList;
|
||||
|
||||
@@ -209,8 +214,6 @@ class MemTableListVersion {
|
||||
// (used during Transaction validation)
|
||||
std::list<ReadOnlyMemTable*> memlist_history_;
|
||||
|
||||
// Maximum number of MemTables to keep in memory (including both flushed
|
||||
const int max_write_buffer_number_to_maintain_;
|
||||
// Maximum size of MemTables to keep in memory (including both flushed
|
||||
// and not-yet-flushed tables).
|
||||
const int64_t max_write_buffer_size_to_maintain_;
|
||||
@@ -238,13 +241,11 @@ class MemTableList {
|
||||
public:
|
||||
// A list of memtables.
|
||||
explicit MemTableList(int min_write_buffer_number_to_merge,
|
||||
int max_write_buffer_number_to_maintain,
|
||||
int64_t max_write_buffer_size_to_maintain)
|
||||
: imm_flush_needed(false),
|
||||
imm_trim_needed(false),
|
||||
min_write_buffer_number_to_merge_(min_write_buffer_number_to_merge),
|
||||
current_(new MemTableListVersion(¤t_memory_usage_,
|
||||
max_write_buffer_number_to_maintain,
|
||||
max_write_buffer_size_to_maintain)),
|
||||
num_flush_not_started_(0),
|
||||
commit_in_progress_(false),
|
||||
|
||||
@@ -220,7 +220,7 @@ class MemTableListTest : public testing::Test {
|
||||
|
||||
TEST_F(MemTableListTest, Empty) {
|
||||
// Create an empty MemTableList and validate basic functions.
|
||||
MemTableList list(1, 0, 0);
|
||||
MemTableList list(1, 0);
|
||||
|
||||
ASSERT_EQ(0, list.NumNotFlushed());
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
@@ -239,10 +239,8 @@ TEST_F(MemTableListTest, Empty) {
|
||||
TEST_F(MemTableListTest, GetTest) {
|
||||
// Create MemTableList
|
||||
int min_write_buffer_number_to_merge = 2;
|
||||
int max_write_buffer_number_to_maintain = 0;
|
||||
int64_t max_write_buffer_size_to_maintain = 0;
|
||||
MemTableList list(min_write_buffer_number_to_merge,
|
||||
max_write_buffer_number_to_maintain,
|
||||
max_write_buffer_size_to_maintain);
|
||||
|
||||
SequenceNumber seq = 1;
|
||||
@@ -407,10 +405,8 @@ TEST_F(MemTableListTest, GetTest) {
|
||||
TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
// Create MemTableList
|
||||
int min_write_buffer_number_to_merge = 2;
|
||||
int max_write_buffer_number_to_maintain = 2;
|
||||
int64_t max_write_buffer_size_to_maintain = 2 * Arena::kInlineSize;
|
||||
MemTableList list(min_write_buffer_number_to_merge,
|
||||
max_write_buffer_number_to_maintain,
|
||||
max_write_buffer_size_to_maintain);
|
||||
|
||||
SequenceNumber seq = 1;
|
||||
@@ -653,11 +649,9 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
|
||||
// Create MemTableList
|
||||
int min_write_buffer_number_to_merge = 3;
|
||||
int max_write_buffer_number_to_maintain = 7;
|
||||
int64_t max_write_buffer_size_to_maintain =
|
||||
7 * static_cast<int>(options.write_buffer_size);
|
||||
MemTableList list(min_write_buffer_number_to_merge,
|
||||
max_write_buffer_number_to_maintain,
|
||||
max_write_buffer_size_to_maintain);
|
||||
|
||||
// Create some MemTables
|
||||
@@ -949,13 +943,11 @@ TEST_F(MemTableListTest, AtomicFlushTest) {
|
||||
|
||||
// Create MemTableLists
|
||||
int min_write_buffer_number_to_merge = 3;
|
||||
int max_write_buffer_number_to_maintain = 7;
|
||||
int64_t max_write_buffer_size_to_maintain =
|
||||
7 * static_cast<int64_t>(options.write_buffer_size);
|
||||
autovector<MemTableList*> lists;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
lists.emplace_back(new MemTableList(min_write_buffer_number_to_merge,
|
||||
max_write_buffer_number_to_maintain,
|
||||
max_write_buffer_size_to_maintain));
|
||||
}
|
||||
|
||||
@@ -1104,11 +1096,9 @@ TEST_F(MemTableListWithTimestampTest, GetTableNewestUDT) {
|
||||
|
||||
// Create MemTableList
|
||||
int min_write_buffer_number_to_merge = 1;
|
||||
int max_write_buffer_number_to_maintain = 4;
|
||||
int64_t max_write_buffer_size_to_maintain =
|
||||
4 * static_cast<int>(options.write_buffer_size);
|
||||
MemTableList list(min_write_buffer_number_to_merge,
|
||||
max_write_buffer_number_to_maintain,
|
||||
max_write_buffer_size_to_maintain);
|
||||
|
||||
// Create some MemTables
|
||||
|
||||
+10
-13
@@ -1339,11 +1339,7 @@ TEST_P(PlainTableDBTest, AdaptiveTable) {
|
||||
INSTANTIATE_TEST_CASE_P(PlainTableDBTest, PlainTableDBTest, ::testing::Bool());
|
||||
|
||||
TEST_P(PlainTableDBTest, DeleteRangeNotSupported) {
|
||||
// XXX: After attempting DeleteRange with PlainTable, Writes will permanently
|
||||
// fail. Even if re-opening the DB, if WAL is used, the WAL is not recoverable
|
||||
// (without manual intervention). Furthermore, a partial write batch can
|
||||
// be exposed to readers, breaking WriteBatch atomicity.
|
||||
for (bool use_write_batch : {/*false, */ true}) {
|
||||
for (bool use_write_batch : {false, true}) {
|
||||
DestroyAndReopen();
|
||||
|
||||
ASSERT_OK(Put("a0001111", "1"));
|
||||
@@ -1362,12 +1358,7 @@ TEST_P(PlainTableDBTest, DeleteRangeNotSupported) {
|
||||
ASSERT_EQ(Get("a0001111"), "1");
|
||||
ASSERT_EQ(Get("b0001111"), "2");
|
||||
ASSERT_EQ(Get("c0001111"), "3");
|
||||
if (use_write_batch) {
|
||||
// XXX: broken WriteBatch atomicity
|
||||
ASSERT_EQ(Get("d0001111"), "4");
|
||||
} else {
|
||||
ASSERT_EQ(Get("d0001111"), "NOT_FOUND");
|
||||
}
|
||||
ASSERT_EQ(Get("d0001111"), "NOT_FOUND"); // expect WriteBatch atomicity
|
||||
ASSERT_EQ(Get("e0001111"), "NOT_FOUND");
|
||||
|
||||
ASSERT_EQ(Put("e0001111", "5").code(), Status::Code::kNotSupported);
|
||||
@@ -1377,8 +1368,14 @@ TEST_P(PlainTableDBTest, DeleteRangeNotSupported) {
|
||||
ASSERT_EQ(dbfull()->TEST_FlushMemTable().code(),
|
||||
Status::Code::kNotSupported);
|
||||
|
||||
// XXX: WAL is not recoverable
|
||||
ASSERT_EQ(TryReopen().code(), Status::Code::kNotSupported);
|
||||
// WAL is recoverable (at least in standard configurations)
|
||||
ASSERT_OK(TryReopen());
|
||||
|
||||
ASSERT_EQ(Get("a0001111"), "1");
|
||||
ASSERT_EQ(Get("b0001111"), "2");
|
||||
ASSERT_EQ(Get("c0001111"), "3");
|
||||
ASSERT_EQ(Get("d0001111"), "NOT_FOUND");
|
||||
ASSERT_EQ(Get("e0001111"), "NOT_FOUND");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-8
@@ -578,14 +578,7 @@ class Repairer {
|
||||
static_cast<bool>(props->user_defined_timestamps_persisted);
|
||||
}
|
||||
if (status.ok()) {
|
||||
uint64_t tail_size = 0;
|
||||
bool contain_no_data_blocks =
|
||||
props->num_entries > 0 &&
|
||||
(props->num_entries == props->num_range_deletions);
|
||||
if (props->tail_start_offset > 0 || contain_no_data_blocks) {
|
||||
assert(props->tail_start_offset <= file_size);
|
||||
tail_size = file_size - props->tail_start_offset;
|
||||
}
|
||||
uint64_t tail_size = FileMetaData::CalculateTailSize(file_size, *props);
|
||||
t->meta.tail_size = tail_size;
|
||||
}
|
||||
ColumnFamilyData* cfd = nullptr;
|
||||
|
||||
@@ -380,6 +380,33 @@ struct FileMetaData {
|
||||
assert(!res || fd.smallest_seqno == fd.largest_seqno);
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint64_t CalculateTailSize(uint64_t file_size,
|
||||
const TableProperties& props) {
|
||||
#ifndef NDEBUG
|
||||
bool skip = false;
|
||||
TEST_SYNC_POINT_CALLBACK("FileMetaData::CalculateTailSize", &skip);
|
||||
if (skip) {
|
||||
return 0;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
uint64_t tail_size = 0;
|
||||
|
||||
// Differentiate between a file with no data blocks (tail_start_offset = 0)
|
||||
// and a file with unknown tail_start_offset (also set to 0 due to
|
||||
// non-negative integer storage limitation)
|
||||
bool contain_no_data_blocks =
|
||||
props.num_entries == 0 ||
|
||||
(props.num_entries > 0 &&
|
||||
(props.num_entries == props.num_range_deletions));
|
||||
|
||||
if (props.tail_start_offset > 0 || contain_no_data_blocks) {
|
||||
assert(props.tail_start_offset <= file_size);
|
||||
tail_size = file_size - props.tail_start_offset;
|
||||
}
|
||||
|
||||
return tail_size;
|
||||
}
|
||||
};
|
||||
|
||||
// A compressed copy of file meta data that just contain minimum data needed
|
||||
|
||||
+15
-1
@@ -1002,7 +1002,8 @@ class LevelIterator final : public InternalIterator {
|
||||
skip_filters_(skip_filters),
|
||||
allow_unprepared_value_(allow_unprepared_value),
|
||||
is_next_read_sequential_(false),
|
||||
to_return_sentinel_(false) {
|
||||
to_return_sentinel_(false),
|
||||
scan_opts_(nullptr) {
|
||||
// Empty level is not supported.
|
||||
assert(flevel_ != nullptr && flevel_->num_files > 0);
|
||||
if (range_tombstone_iter_ptr_) {
|
||||
@@ -1098,6 +1099,13 @@ class LevelIterator final : public InternalIterator {
|
||||
read_seq_ = read_seq;
|
||||
}
|
||||
|
||||
void Prepare(const std::vector<ScanOptions>* scan_opts) override {
|
||||
scan_opts_ = scan_opts;
|
||||
if (file_iter_.iter()) {
|
||||
file_iter_.Prepare(scan_opts_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Return true if at least one invalid file is seen and skipped.
|
||||
bool SkipEmptyFileForward();
|
||||
@@ -1223,6 +1231,7 @@ class LevelIterator final : public InternalIterator {
|
||||
bool prefix_exhausted_ = false;
|
||||
// Whether next/prev key is a sentinel key.
|
||||
bool to_return_sentinel_ = false;
|
||||
const std::vector<ScanOptions>* scan_opts_;
|
||||
|
||||
// Sets flags for if we should return the sentinel key next.
|
||||
// The condition for returning sentinel is reaching the end of current
|
||||
@@ -1533,6 +1542,11 @@ void LevelIterator::SetFileIterator(InternalIterator* iter) {
|
||||
}
|
||||
|
||||
InternalIterator* old_iter = file_iter_.Set(iter);
|
||||
// Since this is a new table iterator, no need to call Prepare() if
|
||||
// scan_opts_ is null
|
||||
if (iter && scan_opts_) {
|
||||
file_iter_.Prepare(scan_opts_);
|
||||
}
|
||||
|
||||
// Update the read pattern for PrefetchBuffer.
|
||||
if (is_next_read_sequential_) {
|
||||
|
||||
+3
-3
@@ -132,7 +132,7 @@ class WriteThread {
|
||||
size_t protection_bytes_per_key;
|
||||
PreReleaseCallback* pre_release_callback;
|
||||
PostMemTableCallback* post_memtable_callback;
|
||||
uint64_t log_used; // log number that this batch was inserted into
|
||||
uint64_t wal_used; // log number that this batch was inserted into
|
||||
uint64_t log_ref; // log number that memtable insert should reference
|
||||
WriteCallback* callback;
|
||||
UserWriteCallback* user_write_cb;
|
||||
@@ -161,7 +161,7 @@ class WriteThread {
|
||||
protection_bytes_per_key(0),
|
||||
pre_release_callback(nullptr),
|
||||
post_memtable_callback(nullptr),
|
||||
log_used(0),
|
||||
wal_used(0),
|
||||
log_ref(0),
|
||||
callback(nullptr),
|
||||
user_write_cb(nullptr),
|
||||
@@ -190,7 +190,7 @@ class WriteThread {
|
||||
protection_bytes_per_key(_batch->GetProtectionBytesPerKey()),
|
||||
pre_release_callback(_pre_release_callback),
|
||||
post_memtable_callback(_post_memtable_callback),
|
||||
log_used(0),
|
||||
wal_used(0),
|
||||
log_ref(_log_ref),
|
||||
callback(_callback),
|
||||
user_write_cb(_user_write_cb),
|
||||
|
||||
@@ -106,7 +106,6 @@ DECLARE_uint64(db_write_buffer_size);
|
||||
DECLARE_int32(write_buffer_size);
|
||||
DECLARE_int32(max_write_buffer_number);
|
||||
DECLARE_int32(min_write_buffer_number_to_merge);
|
||||
DECLARE_int32(max_write_buffer_number_to_maintain);
|
||||
DECLARE_int64(max_write_buffer_size_to_maintain);
|
||||
DECLARE_bool(use_write_buffer_manager);
|
||||
DECLARE_double(memtable_prefix_bloom_size_ratio);
|
||||
@@ -321,7 +320,6 @@ DECLARE_int32(approximate_size_one_in);
|
||||
DECLARE_bool(best_efforts_recovery);
|
||||
DECLARE_bool(skip_verifydb);
|
||||
DECLARE_bool(paranoid_file_checks);
|
||||
DECLARE_bool(fail_if_options_file_error);
|
||||
DECLARE_uint64(batch_protection_bytes_per_key);
|
||||
DECLARE_uint32(memtable_protection_bytes_per_key);
|
||||
DECLARE_uint32(block_protection_bytes_per_key);
|
||||
@@ -424,6 +422,8 @@ DECLARE_uint32(commit_bypass_memtable_one_in);
|
||||
DECLARE_bool(track_and_verify_wals);
|
||||
DECLARE_bool(enable_remote_compaction);
|
||||
DECLARE_bool(auto_refresh_iterator_with_snapshot);
|
||||
DECLARE_uint32(memtable_op_scan_flush_trigger);
|
||||
DECLARE_uint32(ingest_wbwi_one_in);
|
||||
|
||||
constexpr long KB = 1024;
|
||||
constexpr int kRandomValueMaxFactor = 3;
|
||||
|
||||
@@ -168,20 +168,6 @@ DEFINE_int32(min_write_buffer_number_to_merge,
|
||||
"writing less data to storage if there are duplicate records in"
|
||||
" each of these individual write buffers.");
|
||||
|
||||
DEFINE_int32(max_write_buffer_number_to_maintain,
|
||||
ROCKSDB_NAMESPACE::Options().max_write_buffer_number_to_maintain,
|
||||
"The total maximum number of write buffers to maintain in memory "
|
||||
"including copies of buffers that have already been flushed. "
|
||||
"Unlike max_write_buffer_number, this parameter does not affect "
|
||||
"flushing. This controls the minimum amount of write history "
|
||||
"that will be available in memory for conflict checking when "
|
||||
"Transactions are used. If this value is too low, some "
|
||||
"transactions may fail at commit time due to not being able to "
|
||||
"determine whether there were any write conflicts. Setting this "
|
||||
"value to 0 will cause write buffers to be freed immediately "
|
||||
"after they are flushed. If this value is set to -1, "
|
||||
"'max_write_buffer_number' will be used.");
|
||||
|
||||
DEFINE_int64(max_write_buffer_size_to_maintain,
|
||||
ROCKSDB_NAMESPACE::Options().max_write_buffer_size_to_maintain,
|
||||
"The total maximum size of write buffers to maintain in memory "
|
||||
@@ -856,6 +842,11 @@ DEFINE_bool(track_and_verify_wals,
|
||||
DEFINE_bool(enable_remote_compaction, false,
|
||||
"Enable (simulated) Remote Compaction");
|
||||
|
||||
DEFINE_uint32(ingest_wbwi_one_in, 0,
|
||||
"If set, will call"
|
||||
"IngestWriteBatchWithIndex() instead of regular write operations "
|
||||
"once every N writes.");
|
||||
|
||||
static bool ValidateInt32Percent(const char* flagname, int32_t value) {
|
||||
if (value < 0 || value > 100) {
|
||||
fprintf(stderr, "Invalid value for --%s: %d, 0<= pct <=100 \n", flagname,
|
||||
@@ -1098,10 +1089,6 @@ DEFINE_bool(paranoid_file_checks, true,
|
||||
"After writing every SST file, reopen it and read all the keys "
|
||||
"and validate checksums");
|
||||
|
||||
DEFINE_bool(fail_if_options_file_error, false,
|
||||
"Fail operations that fail to detect or properly persist options "
|
||||
"file.");
|
||||
|
||||
DEFINE_uint64(batch_protection_bytes_per_key, 0,
|
||||
"If nonzero, enables integrity protection in `WriteBatch` at the "
|
||||
"specified number of bytes per key. Currently the only supported "
|
||||
@@ -1487,4 +1474,9 @@ DEFINE_bool(
|
||||
ROCKSDB_NAMESPACE::ReadOptions().auto_refresh_iterator_with_snapshot,
|
||||
"ReadOptions.auto_refresh_iterator_with_snapshot");
|
||||
|
||||
DEFINE_uint32(
|
||||
memtable_op_scan_flush_trigger,
|
||||
ROCKSDB_NAMESPACE::ColumnFamilyOptions().memtable_op_scan_flush_trigger,
|
||||
"Sets CF option memtable_op_scan_flush_trigger.");
|
||||
|
||||
#endif // GFLAGS
|
||||
@@ -265,7 +265,7 @@ class DbStressListener : public EventListener {
|
||||
fault_fs_guard->DisableAllThreadLocalErrorInjection();
|
||||
// TODO(hx235): only exempt the flush thread during error recovery instead
|
||||
// of all the flush threads from error injection
|
||||
fault_fs_guard->SetIOActivtiesExcludedFromFaultInjection(
|
||||
fault_fs_guard->SetIOActivitiesExcludedFromFaultInjection(
|
||||
{Env::IOActivity::kFlush});
|
||||
}
|
||||
}
|
||||
@@ -275,7 +275,7 @@ class DbStressListener : public EventListener {
|
||||
RandomSleep();
|
||||
if (FLAGS_error_recovery_with_no_fault_injection && fault_fs_guard) {
|
||||
fault_fs_guard->EnableAllThreadLocalErrorInjection();
|
||||
fault_fs_guard->SetIOActivtiesExcludedFromFaultInjection({});
|
||||
fault_fs_guard->SetIOActivitiesExcludedFromFaultInjection({});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3376,8 +3376,6 @@ void StressTest::PrintEnv() const {
|
||||
FLAGS_sync_fault_injection);
|
||||
fprintf(stdout, "Best efforts recovery : %d\n",
|
||||
static_cast<int>(FLAGS_best_efforts_recovery));
|
||||
fprintf(stdout, "Fail if OPTIONS file error: %d\n",
|
||||
static_cast<int>(FLAGS_fail_if_options_file_error));
|
||||
fprintf(stdout, "User timestamp size bytes : %d\n",
|
||||
static_cast<int>(FLAGS_user_timestamp_size));
|
||||
fprintf(stdout, "Persist user defined timestamps : %d\n",
|
||||
@@ -4047,8 +4045,6 @@ void InitializeOptionsFromFlags(
|
||||
options.max_write_buffer_number = FLAGS_max_write_buffer_number;
|
||||
options.min_write_buffer_number_to_merge =
|
||||
FLAGS_min_write_buffer_number_to_merge;
|
||||
options.max_write_buffer_number_to_maintain =
|
||||
FLAGS_max_write_buffer_number_to_maintain;
|
||||
options.max_write_buffer_size_to_maintain =
|
||||
FLAGS_max_write_buffer_size_to_maintain;
|
||||
options.memtable_prefix_bloom_size_ratio =
|
||||
@@ -4257,7 +4253,6 @@ void InitializeOptionsFromFlags(
|
||||
|
||||
options.best_efforts_recovery = FLAGS_best_efforts_recovery;
|
||||
options.paranoid_file_checks = FLAGS_paranoid_file_checks;
|
||||
options.fail_if_options_file_error = FLAGS_fail_if_options_file_error;
|
||||
|
||||
if (FLAGS_user_timestamp_size > 0) {
|
||||
CheckAndSetOptionsForUserTimestamp(options);
|
||||
@@ -4321,6 +4316,8 @@ void InitializeOptionsFromFlags(
|
||||
if (FLAGS_enable_remote_compaction) {
|
||||
options.compaction_service = std::make_shared<DbStressCompactionService>();
|
||||
}
|
||||
|
||||
options.memtable_op_scan_flush_trigger = FLAGS_memtable_op_scan_flush_trigger;
|
||||
}
|
||||
|
||||
void InitializeOptionsGeneral(
|
||||
|
||||
@@ -1845,7 +1845,17 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
} else if (FLAGS_use_merge) {
|
||||
if (!FLAGS_use_txn) {
|
||||
if (FLAGS_user_timestamp_size == 0) {
|
||||
s = db_->Merge(write_opts, cfh, k, v);
|
||||
if (FLAGS_ingest_wbwi_one_in &&
|
||||
thread->rand.OneIn(FLAGS_ingest_wbwi_one_in)) {
|
||||
auto wbwi = std::make_shared<WriteBatchWithIndex>(
|
||||
options_.comparator, 0, /*overwrite_key=*/true);
|
||||
s = wbwi->Merge(cfh, k, v);
|
||||
if (s.ok()) {
|
||||
s = db_->IngestWriteBatchWithIndex(write_opts, wbwi);
|
||||
}
|
||||
} else {
|
||||
s = db_->Merge(write_opts, cfh, k, v);
|
||||
}
|
||||
} else {
|
||||
s = db_->Merge(write_opts, cfh, k, write_ts, v);
|
||||
}
|
||||
@@ -1857,7 +1867,17 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
} else {
|
||||
if (!FLAGS_use_txn) {
|
||||
if (FLAGS_user_timestamp_size == 0) {
|
||||
s = db_->Put(write_opts, cfh, k, v);
|
||||
if (FLAGS_ingest_wbwi_one_in &&
|
||||
thread->rand.OneIn(FLAGS_ingest_wbwi_one_in)) {
|
||||
auto wbwi = std::make_shared<WriteBatchWithIndex>(
|
||||
options_.comparator, 0, /*overwrite_key=*/true);
|
||||
s = wbwi->Put(cfh, k, v);
|
||||
if (s.ok()) {
|
||||
s = db_->IngestWriteBatchWithIndex(write_opts, wbwi);
|
||||
}
|
||||
} else {
|
||||
s = db_->Put(write_opts, cfh, k, v);
|
||||
}
|
||||
} else {
|
||||
s = db_->Put(write_opts, cfh, k, write_ts, v);
|
||||
}
|
||||
@@ -1949,7 +1969,17 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
}
|
||||
if (!FLAGS_use_txn) {
|
||||
if (FLAGS_user_timestamp_size == 0) {
|
||||
s = db_->Delete(write_opts, cfh, key);
|
||||
if (FLAGS_ingest_wbwi_one_in &&
|
||||
thread->rand.OneIn(FLAGS_ingest_wbwi_one_in)) {
|
||||
auto wbwi = std::make_shared<WriteBatchWithIndex>(
|
||||
options_.comparator, 0, /*overwrite_key=*/true);
|
||||
s = wbwi->Delete(cfh, key);
|
||||
if (s.ok()) {
|
||||
s = db_->IngestWriteBatchWithIndex(write_opts, wbwi);
|
||||
}
|
||||
} else {
|
||||
s = db_->Delete(write_opts, cfh, key);
|
||||
}
|
||||
} else {
|
||||
s = db_->Delete(write_opts, cfh, key, write_ts);
|
||||
}
|
||||
@@ -2006,7 +2036,17 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
}
|
||||
if (!FLAGS_use_txn) {
|
||||
if (FLAGS_user_timestamp_size == 0) {
|
||||
s = db_->SingleDelete(write_opts, cfh, key);
|
||||
if (FLAGS_ingest_wbwi_one_in &&
|
||||
thread->rand.OneIn(FLAGS_ingest_wbwi_one_in)) {
|
||||
auto wbwi = std::make_shared<WriteBatchWithIndex>(
|
||||
options_.comparator, 0, /*overwrite_key=*/true);
|
||||
s = wbwi->SingleDelete(cfh, key);
|
||||
if (s.ok()) {
|
||||
s = db_->IngestWriteBatchWithIndex(write_opts, wbwi);
|
||||
}
|
||||
} else {
|
||||
s = db_->SingleDelete(write_opts, cfh, key);
|
||||
}
|
||||
} else {
|
||||
s = db_->SingleDelete(write_opts, cfh, key, write_ts);
|
||||
}
|
||||
|
||||
Vendored
+20
-10
@@ -322,8 +322,17 @@ class PosixFileSystem : public FileSystem {
|
||||
if (options.use_mmap_writes) {
|
||||
MaybeForceDisableMmap(fd);
|
||||
}
|
||||
uint64_t initial_file_size = 0;
|
||||
if (reopen) {
|
||||
s = GetFileSize(fname, IOOptions(), &initial_file_size, nullptr);
|
||||
if (!s.ok()) {
|
||||
close(fd);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (options.use_mmap_writes && !forceMmapOff_) {
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
|
||||
result->reset(
|
||||
new PosixMmapFile(fname, fd, page_size_, options, initial_file_size));
|
||||
} else if (options.use_direct_writes && !options.use_mmap_writes) {
|
||||
#ifdef OS_MACOSX
|
||||
if (fcntl(fd, F_NOCACHE, 1) == -1) {
|
||||
@@ -343,7 +352,7 @@ class PosixFileSystem : public FileSystem {
|
||||
#endif
|
||||
result->reset(new PosixWritableFile(
|
||||
fname, fd, GetLogicalBlockSizeForWriteIfNeeded(options, fname, fd),
|
||||
options));
|
||||
options, initial_file_size));
|
||||
} else {
|
||||
// disable mmap writes
|
||||
EnvOptions no_mmap_writes_options = options;
|
||||
@@ -352,7 +361,7 @@ class PosixFileSystem : public FileSystem {
|
||||
new PosixWritableFile(fname, fd,
|
||||
GetLogicalBlockSizeForWriteIfNeeded(
|
||||
no_mmap_writes_options, fname, fd),
|
||||
no_mmap_writes_options));
|
||||
no_mmap_writes_options, initial_file_size));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -418,7 +427,8 @@ class PosixFileSystem : public FileSystem {
|
||||
MaybeForceDisableMmap(fd);
|
||||
}
|
||||
if (options.use_mmap_writes && !forceMmapOff_) {
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options));
|
||||
result->reset(new PosixMmapFile(fname, fd, page_size_, options,
|
||||
/*initial_file_size=*/0));
|
||||
} else if (options.use_direct_writes && !options.use_mmap_writes) {
|
||||
#ifdef OS_MACOSX
|
||||
if (fcntl(fd, F_NOCACHE, 1) == -1) {
|
||||
@@ -438,16 +448,16 @@ class PosixFileSystem : public FileSystem {
|
||||
#endif
|
||||
result->reset(new PosixWritableFile(
|
||||
fname, fd, GetLogicalBlockSizeForWriteIfNeeded(options, fname, fd),
|
||||
options));
|
||||
options, /*initial_file_size=*/0));
|
||||
} else {
|
||||
// disable mmap writes
|
||||
FileOptions no_mmap_writes_options = options;
|
||||
no_mmap_writes_options.use_mmap_writes = false;
|
||||
result->reset(
|
||||
new PosixWritableFile(fname, fd,
|
||||
GetLogicalBlockSizeForWriteIfNeeded(
|
||||
no_mmap_writes_options, fname, fd),
|
||||
no_mmap_writes_options));
|
||||
result->reset(new PosixWritableFile(
|
||||
fname, fd,
|
||||
GetLogicalBlockSizeForWriteIfNeeded(no_mmap_writes_options, fname,
|
||||
fd),
|
||||
no_mmap_writes_options, /*initial_file_size=*/0));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Vendored
+6
-4
@@ -1138,7 +1138,8 @@ IOStatus PosixMmapFile::Msync() {
|
||||
}
|
||||
|
||||
PosixMmapFile::PosixMmapFile(const std::string& fname, int fd, size_t page_size,
|
||||
const EnvOptions& options)
|
||||
const EnvOptions& options,
|
||||
uint64_t initial_file_size)
|
||||
: filename_(fname),
|
||||
fd_(fd),
|
||||
page_size_(page_size),
|
||||
@@ -1147,7 +1148,7 @@ PosixMmapFile::PosixMmapFile(const std::string& fname, int fd, size_t page_size,
|
||||
limit_(nullptr),
|
||||
dst_(nullptr),
|
||||
last_sync_(nullptr),
|
||||
file_offset_(0) {
|
||||
file_offset_(initial_file_size) {
|
||||
#ifdef ROCKSDB_FALLOCATE_PRESENT
|
||||
allow_fallocate_ = options.allow_fallocate;
|
||||
fallocate_with_keep_size_ = options.fallocate_with_keep_size;
|
||||
@@ -1317,12 +1318,13 @@ IOStatus PosixMmapFile::Allocate(uint64_t offset, uint64_t len,
|
||||
*/
|
||||
PosixWritableFile::PosixWritableFile(const std::string& fname, int fd,
|
||||
size_t logical_block_size,
|
||||
const EnvOptions& options)
|
||||
const EnvOptions& options,
|
||||
uint64_t initial_file_size)
|
||||
: FSWritableFile(options),
|
||||
filename_(fname),
|
||||
use_direct_io_(options.use_direct_writes),
|
||||
fd_(fd),
|
||||
filesize_(0),
|
||||
filesize_(initial_file_size),
|
||||
logical_sector_size_(logical_block_size) {
|
||||
#ifdef ROCKSDB_FALLOCATE_PRESENT
|
||||
allow_fallocate_ = options.allow_fallocate;
|
||||
|
||||
Vendored
+3
-2
@@ -374,7 +374,8 @@ class PosixWritableFile : public FSWritableFile {
|
||||
public:
|
||||
explicit PosixWritableFile(const std::string& fname, int fd,
|
||||
size_t logical_block_size,
|
||||
const EnvOptions& options);
|
||||
const EnvOptions& options,
|
||||
uint64_t initial_file_size);
|
||||
virtual ~PosixWritableFile();
|
||||
|
||||
// Need to implement this so the file is truncated correctly
|
||||
@@ -469,7 +470,7 @@ class PosixMmapFile : public FSWritableFile {
|
||||
|
||||
public:
|
||||
PosixMmapFile(const std::string& fname, int fd, size_t page_size,
|
||||
const EnvOptions& options);
|
||||
const EnvOptions& options, uint64_t initial_file_size);
|
||||
~PosixMmapFile();
|
||||
|
||||
// Means Close() will properly take care of truncate
|
||||
|
||||
@@ -126,6 +126,8 @@ Status FilePrefetchBuffer::Read(BufferInfo* buf, const IOOptions& opts,
|
||||
|
||||
if (usage_ == FilePrefetchBufferUsage::kUserScanPrefetch) {
|
||||
RecordTick(stats_, PREFETCH_BYTES, read_len);
|
||||
} else if (usage_ == FilePrefetchBufferUsage::kCompactionPrefetch) {
|
||||
RecordInHistogram(stats_, COMPACTION_PREFETCH_BYTES, read_len);
|
||||
}
|
||||
if (!use_fs_buffer) {
|
||||
// Update the buffer size.
|
||||
@@ -154,7 +156,9 @@ Status FilePrefetchBuffer::ReadAsync(BufferInfo* buf, const IOOptions& opts,
|
||||
&(buf->del_fn_), /*aligned_buf =*/nullptr);
|
||||
req.status.PermitUncheckedError();
|
||||
if (s.ok()) {
|
||||
RecordTick(stats_, PREFETCH_BYTES, read_len);
|
||||
if (usage_ == FilePrefetchBufferUsage::kUserScanPrefetch) {
|
||||
RecordTick(stats_, PREFETCH_BYTES, read_len);
|
||||
}
|
||||
buf->async_read_in_progress_ = true;
|
||||
}
|
||||
return s;
|
||||
|
||||
@@ -134,6 +134,7 @@ struct BufferInfo {
|
||||
enum class FilePrefetchBufferUsage {
|
||||
kTableOpenPrefetchTail,
|
||||
kUserScanPrefetch,
|
||||
kCompactionPrefetch,
|
||||
kUnknown,
|
||||
};
|
||||
|
||||
@@ -574,6 +575,9 @@ class FilePrefetchBuffer {
|
||||
size_t& read_len, uint64_t& aligned_useful_len);
|
||||
|
||||
void UpdateStats(bool found_in_buffer, size_t length_found) {
|
||||
if (usage_ != FilePrefetchBufferUsage::kUserScanPrefetch) {
|
||||
return;
|
||||
}
|
||||
if (found_in_buffer) {
|
||||
RecordTick(stats_, PREFETCH_HITS);
|
||||
}
|
||||
|
||||
+31
-5
@@ -299,9 +299,18 @@ TEST_P(PrefetchTest, Basic) {
|
||||
const uint64_t prev_table_open_prefetch_tail_hit =
|
||||
options.statistics->getTickerCount(TABLE_OPEN_PREFETCH_TAIL_HIT);
|
||||
|
||||
HistogramData pre_compaction_prefetch_bytes;
|
||||
options.statistics->histogramData(COMPACTION_PREFETCH_BYTES,
|
||||
&pre_compaction_prefetch_bytes);
|
||||
ASSERT_EQ(pre_compaction_prefetch_bytes.count, 0);
|
||||
|
||||
// commenting out the line below causes the example to work correctly
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &least, &greatest));
|
||||
|
||||
HistogramData post_compaction_prefetch_bytes;
|
||||
options.statistics->histogramData(COMPACTION_PREFETCH_BYTES,
|
||||
&post_compaction_prefetch_bytes);
|
||||
|
||||
HistogramData cur_table_open_prefetch_tail_read;
|
||||
options.statistics->histogramData(TABLE_OPEN_PREFETCH_TAIL_READ_BYTES,
|
||||
&cur_table_open_prefetch_tail_read);
|
||||
@@ -318,6 +327,7 @@ TEST_P(PrefetchTest, Basic) {
|
||||
ASSERT_GT(fs->GetPrefetchCount(), 1);
|
||||
ASSERT_EQ(0, buff_prefetch_count);
|
||||
fs->ClearPrefetchCount();
|
||||
ASSERT_EQ(post_compaction_prefetch_bytes.count, 0);
|
||||
} else {
|
||||
ASSERT_FALSE(fs->IsPrefetchCalled());
|
||||
// To rule out false positive by the SST file tail prefetch during
|
||||
@@ -331,6 +341,20 @@ TEST_P(PrefetchTest, Basic) {
|
||||
prev_table_open_prefetch_tail_hit);
|
||||
ASSERT_GE(cur_table_open_prefetch_tail_miss,
|
||||
prev_table_open_prefetch_tail_miss);
|
||||
|
||||
ASSERT_GT(post_compaction_prefetch_bytes.count, 0);
|
||||
|
||||
// Not an exact match due to potential roundup/down for alignment
|
||||
auto expected_compaction_readahead_size =
|
||||
Options().compaction_readahead_size;
|
||||
ASSERT_LE(post_compaction_prefetch_bytes.max,
|
||||
expected_compaction_readahead_size * 1.1);
|
||||
ASSERT_GE(post_compaction_prefetch_bytes.max,
|
||||
expected_compaction_readahead_size * 0.9);
|
||||
ASSERT_LE(post_compaction_prefetch_bytes.average,
|
||||
expected_compaction_readahead_size * 1.1);
|
||||
ASSERT_GE(post_compaction_prefetch_bytes.average,
|
||||
expected_compaction_readahead_size * 0.9);
|
||||
}
|
||||
|
||||
for (bool disable_io : {false, true}) {
|
||||
@@ -3251,8 +3275,9 @@ TEST_F(FilePrefetchBufferTest, SyncReadaheadStats) {
|
||||
ReadaheadParams readahead_params;
|
||||
readahead_params.initial_readahead_size = 8192;
|
||||
readahead_params.max_readahead_size = 8192;
|
||||
FilePrefetchBuffer fpb(readahead_params, true, false, fs(), nullptr,
|
||||
stats.get());
|
||||
FilePrefetchBuffer fpb(
|
||||
readahead_params, true, false, fs(), nullptr, stats.get(),
|
||||
nullptr /* cb */, FilePrefetchBufferUsage::kUserScanPrefetch /* usage */);
|
||||
Slice result;
|
||||
// Simulate a seek of 4096 bytes at offset 0. Due to the readahead settings,
|
||||
// it will do a read of offset 0 and length - (4096 + 8192) 12288.
|
||||
@@ -3497,9 +3522,10 @@ TEST_P(FSBufferPrefetchTest, FSBufferPrefetchStatsInternals) {
|
||||
size_t num_buffers = use_async_prefetch ? 2 : 1;
|
||||
readahead_params.num_buffers = num_buffers;
|
||||
|
||||
FilePrefetchBuffer fpb(readahead_params, true /* enable */,
|
||||
false /* track_min_offset */, fs(), clock(),
|
||||
stats.get());
|
||||
FilePrefetchBuffer fpb(
|
||||
readahead_params, true /* enable */, false /* track_min_offset */, fs(),
|
||||
clock(), stats.get(), nullptr /* cb */,
|
||||
FilePrefetchBufferUsage::kUserScanPrefetch /* usage */);
|
||||
|
||||
int overlap_buffer_write_ct = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
enum class IterBoundCheck : char {
|
||||
kUnknown = 0,
|
||||
kOutOfBound,
|
||||
kInbound,
|
||||
};
|
||||
|
||||
// This structure encapsulates the result of NextAndGetResult()
|
||||
struct IterateResult {
|
||||
// The lifetime of key is guaranteed until Next()/NextAndGetResult() is
|
||||
// called.
|
||||
Slice key;
|
||||
// If the iterator becomes invalid after a NextAndGetResult(), the table
|
||||
// iterator should set this to indicate whether it became invalid due
|
||||
// to the next key being out of bound (kOutOfBound) or it reached end
|
||||
// of file (kUnknown). If the iiterator is still valid, this should
|
||||
// be set to kInbound.
|
||||
IterBoundCheck bound_check_result = IterBoundCheck::kUnknown;
|
||||
// If false, PrepareValue() needs to be called before value()
|
||||
// This is useful if the table reader wants to materialize the value in a
|
||||
// lazy manner. In that case, it can set this to false and RocksDB
|
||||
// guarantees that it'll call PrepareValue() before calling value().
|
||||
bool value_prepared = true;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -171,15 +171,6 @@ struct AdvancedColumnFamilyOptions {
|
||||
// Default: 1
|
||||
int min_write_buffer_number_to_merge = 1;
|
||||
|
||||
// DEPRECATED
|
||||
// The total maximum number of write buffers to maintain in memory including
|
||||
// copies of buffers that have already been flushed. Unlike
|
||||
// max_write_buffer_number, this parameter does not affect flushing.
|
||||
// This parameter is being replaced by max_write_buffer_size_to_maintain.
|
||||
// If both parameters are set to non-zero values, this parameter will be
|
||||
// ignored.
|
||||
int max_write_buffer_number_to_maintain = 0;
|
||||
|
||||
// The target number of write history bytes to hold in memory. Write history
|
||||
// comprises the latest write buffers (memtables). To reach the target, write
|
||||
// buffers that were most recently flushed to SST files may be retained in
|
||||
@@ -1105,6 +1096,20 @@ struct AdvancedColumnFamilyOptions {
|
||||
// additional key comparison during memtable lookup.
|
||||
bool paranoid_memory_checks = false;
|
||||
|
||||
// When an iterator scans this number of invisible entries (tombstones or
|
||||
// hidden puts) from the active memtable during a single iterator operation,
|
||||
// we will attempt to flush the memtable. Currently only forward scans are
|
||||
// supported (SeekToFirst(), Seek() and Next()).
|
||||
// This option helps to reduce the overhead of scanning through a
|
||||
// large number of entries in memtable.
|
||||
// Users should consider enable deletion-triggered-compaction (see
|
||||
// CompactOnDeletionCollectorFactory) together with this option to compact
|
||||
// away tombstones after the memtable is flushed.
|
||||
//
|
||||
// Default: 0 (disabled)
|
||||
// Dynamically changeable through the SetOptions() API.
|
||||
uint32_t memtable_op_scan_flush_trigger = 0;
|
||||
|
||||
// Create ColumnFamilyOptions with default values for all fields
|
||||
AdvancedColumnFamilyOptions();
|
||||
// Create ColumnFamilyOptions from Options
|
||||
|
||||
+5
-5
@@ -1341,6 +1341,11 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_periodic_compaction_seconds(
|
||||
rocksdb_options_t*, uint64_t);
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_options_get_periodic_compaction_seconds(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_memtable_op_scan_flush_trigger(rocksdb_options_t*,
|
||||
uint32_t);
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_options_get_memtable_op_scan_flush_trigger(rocksdb_options_t*);
|
||||
|
||||
enum {
|
||||
rocksdb_statistics_level_disable_all = 0,
|
||||
@@ -1448,11 +1453,6 @@ rocksdb_options_set_min_write_buffer_number_to_merge(rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(rocksdb_options_t*,
|
||||
int);
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(rocksdb_options_t*,
|
||||
int64_t);
|
||||
extern ROCKSDB_LIBRARY_API int64_t
|
||||
|
||||
+56
-5
@@ -22,6 +22,7 @@
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/metadata.h"
|
||||
#include "rocksdb/multi_scan.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/snapshot.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
@@ -30,6 +31,7 @@
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/user_write_callback.h"
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "rocksdb/version.h"
|
||||
#include "rocksdb/wide_columns.h"
|
||||
|
||||
@@ -616,7 +618,7 @@ class DB {
|
||||
const Slice& /*key*/, const Slice& /*ts*/,
|
||||
const Slice& /*value*/);
|
||||
|
||||
// Apply the specified updates to the database.
|
||||
// Apply the specified updates atomically to the database.
|
||||
// If `updates` contains no update, WAL will still be synced if
|
||||
// options.sync=true.
|
||||
// Returns OK on success, non-OK on failure.
|
||||
@@ -632,6 +634,21 @@ class DB {
|
||||
"WriteWithCallback not implemented for this interface.");
|
||||
}
|
||||
|
||||
// EXPERIMENTAL, subject to change
|
||||
// Ingest a WriteBatchWithIndex into DB, bypassing memtable writes for better
|
||||
// write performance. Useful when there is a large number of updates
|
||||
// in the write batch.
|
||||
// The WriteBatchWithIndex must be created with overwrite_key=true.
|
||||
// Currently this requires WriteOptions::disableWAL=true.
|
||||
// The following options are currently not supported:
|
||||
// - unordered_write
|
||||
// - enable_pipelined_write
|
||||
virtual Status IngestWriteBatchWithIndex(
|
||||
const WriteOptions& /*options*/,
|
||||
std::shared_ptr<WriteBatchWithIndex> /*wbwi*/) {
|
||||
return Status::NotSupported("IngestWriteBatchWithIndex not implemented.");
|
||||
}
|
||||
|
||||
// If the column family specified by "column_family" contains an entry for
|
||||
// "key", return the corresponding value in "*value". If the entry is a plain
|
||||
// key-value, return the value as-is; if it is a wide-column entity, return
|
||||
@@ -1073,6 +1090,18 @@ class DB {
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families) = 0;
|
||||
|
||||
// Get an iterator that scans multiple key ranges. The scan ranges should
|
||||
// be in increasing order of start key. See multi_scan_iterator.h for more
|
||||
// details.
|
||||
virtual std::unique_ptr<MultiScan> NewMultiScan(
|
||||
const ReadOptions& /*options*/, ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<ScanOptions>& /*scan_opts*/) {
|
||||
std::unique_ptr<Iterator> iter(NewErrorIterator(Status::NotSupported()));
|
||||
std::unique_ptr<MultiScan> ms_iter =
|
||||
std::make_unique<MultiScan>(std::move(iter));
|
||||
return ms_iter;
|
||||
}
|
||||
|
||||
// Return a handle to the current DB state. Iterators created with
|
||||
// this handle will all observe a stable snapshot of the current DB
|
||||
// state. The caller must call ReleaseSnapshot(result) when the
|
||||
@@ -1658,9 +1687,12 @@ class DB {
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family) = 0;
|
||||
virtual int NumberLevels() { return NumberLevels(DefaultColumnFamily()); }
|
||||
|
||||
// DEPRECATED:
|
||||
// Maximum level to which a new compacted memtable is pushed if it
|
||||
// does not create overlap.
|
||||
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) = 0;
|
||||
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* /*column_family*/) {
|
||||
return 0;
|
||||
}
|
||||
virtual int MaxMemCompactionLevel() {
|
||||
return MaxMemCompactionLevel(DefaultColumnFamily());
|
||||
}
|
||||
@@ -1766,6 +1798,25 @@ class DB {
|
||||
virtual Status GetFullHistoryTsLow(ColumnFamilyHandle* column_family,
|
||||
std::string* ts_low) = 0;
|
||||
|
||||
// EXPERIMENTAL
|
||||
// Get the newest timestamp of the column family. This is only for when the
|
||||
// column family enables user defined timestamp and when timestamps are not
|
||||
// persisted in SST files, a.k.a `persist_user_defined_timestamps=false`.
|
||||
// This checks the mutable memtable, the immutable memtable and the SST files,
|
||||
// and returns the first newest user defined timestamp found.
|
||||
// When user defined timestamp is not persisted in SST files, metadata in
|
||||
// MANIFEST tracks the most recently seen timestamp for SST files, so the
|
||||
// newest timestamp in SST files can be found.
|
||||
// OK status is returned if finding the newest timestamp succeeds, if
|
||||
// `newest_timestamp` is empty, it means the column family hasn't seen any
|
||||
// timestamp. The returned timestamp is encoded, util method `DecodeU64Ts` can
|
||||
// be used to decode it into uint64_t.
|
||||
// User-defined timestamp is required to be increasing per key, the return
|
||||
// value of this API would be most useful if the user-defined timestamp is
|
||||
// monotonically increasing across keys.
|
||||
virtual Status GetNewestUserDefinedTimestamp(
|
||||
ColumnFamilyHandle* column_family, std::string* newest_timestamp) = 0;
|
||||
|
||||
// Suspend deleting obsolete files. Compactions will continue to occur,
|
||||
// but no obsolete files will be deleted. To resume file deletions, each
|
||||
// call to DisableFileDeletions() must be matched by a subsequent call to
|
||||
@@ -1877,12 +1928,12 @@ class DB {
|
||||
// Retrieve information about the current wal file
|
||||
//
|
||||
// Note that the log might have rolled after this call in which case
|
||||
// the current_log_file would not point to the current log file.
|
||||
// the current_wal_file would not point to the current log file.
|
||||
//
|
||||
// Additionally, for the sake of optimization current_log_file->StartSequence
|
||||
// Additionally, for the sake of optimization current_wal_file->StartSequence
|
||||
// would always be set to 0
|
||||
virtual Status GetCurrentWalFile(
|
||||
std::unique_ptr<WalFile>* current_log_file) = 0;
|
||||
std::unique_ptr<WalFile>* current_wal_file) = 0;
|
||||
|
||||
// IngestExternalFile() will load a list of external SST files (1) into the DB
|
||||
// Two primary modes are supported:
|
||||
|
||||
@@ -21,6 +21,11 @@ Status SuggestCompactRange(DB* db, ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end);
|
||||
Status SuggestCompactRange(DB* db, const Slice* begin, const Slice* end);
|
||||
|
||||
// DEPRECATED: this API may be removed in a future release.
|
||||
// This operation can be done through CompactRange() by setting
|
||||
// CompactRangeOptions::bottommost_level_compaction set to
|
||||
// BottommostLevelCompaction::kSkip and setting target level.
|
||||
//
|
||||
// Move all L0 files to target_level skipping compaction.
|
||||
// This operation succeeds only if the files in L0 have disjoint ranges; this
|
||||
// is guaranteed to happen, for instance, if keys are inserted in sorted
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/advanced_iterator.h"
|
||||
#include "rocksdb/customizable.h"
|
||||
#include "rocksdb/file_checksum.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/iterator_base.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
@@ -58,6 +60,47 @@ class ExternalTableFactory;
|
||||
// the external table implementation.
|
||||
// TODO: Specify which options are relevant
|
||||
|
||||
class ExternalTableIterator : public IteratorBase {
|
||||
public:
|
||||
virtual ~ExternalTableIterator() {}
|
||||
|
||||
// This can optionally be called to prepare the iterator for a series
|
||||
// of scans. The scan_opts parameter specifies the order of scans to
|
||||
// follow, as well as the limits for those scans. After calling this,
|
||||
// the caller will Seek() the iterator to successive start keys in scan_opts.
|
||||
//
|
||||
// If Prepare() is called again with a different scan_opts pointer, it
|
||||
// means the iterator will be reused for a new multi scan. If scan_opts
|
||||
// is null, then the previous Prepare() can be discarded.
|
||||
//
|
||||
// The caller guarantees the lifetime of scan_opts until its either cleared
|
||||
// or replaced by another Prepare().
|
||||
// TODO: Update the contract to trim the scan_opts range to only include
|
||||
// scans that potentially intersect the file key range.
|
||||
//
|
||||
// If the sequence of Seeks is interrupted by seeking to some other target
|
||||
// key, then the iterator is free to discard anything done during Prepare.
|
||||
virtual void Prepare(const ScanOptions scan_opts[], size_t num_opts) = 0;
|
||||
|
||||
// Similar to Next(), except it also fills the result and returns whether
|
||||
// the iterator is on a valid key or not
|
||||
virtual bool NextAndGetResult(IterateResult* result) = 0;
|
||||
|
||||
// Prepares the value if its lazily materialized. The implementation can
|
||||
// request that this be called by setting value_prepared to false in
|
||||
// IterateResult. Next() should always implicitly materialize the
|
||||
// value.
|
||||
bool PrepareValue() override = 0;
|
||||
|
||||
// Return the current key's value
|
||||
virtual Slice value() const = 0;
|
||||
|
||||
// Return the current position bounds check result - kInbound if the
|
||||
// position is a valid key, kOutOfBound if the key is out of bound (i.e
|
||||
// scan has terminated), or kUnknown if end of file.
|
||||
virtual IterBoundCheck UpperBoundCheckResult() = 0;
|
||||
};
|
||||
|
||||
class ExternalTableReader {
|
||||
public:
|
||||
virtual ~ExternalTableReader() {}
|
||||
@@ -65,8 +108,9 @@ class ExternalTableReader {
|
||||
// Return an Iterator that can be used to scan the table file.
|
||||
// The read_options can optionally contain the upper bound
|
||||
// key (exclusive) of the scan in iterate_upper_bound.
|
||||
virtual Iterator* NewIterator(const ReadOptions& read_options,
|
||||
const SliceTransform* prefix_extractor) = 0;
|
||||
virtual ExternalTableIterator* NewIterator(
|
||||
const ReadOptions& read_options,
|
||||
const SliceTransform* prefix_extractor) = 0;
|
||||
|
||||
// Point lookup the given key and return its value
|
||||
virtual Status Get(const ReadOptions& read_options, const Slice& key,
|
||||
@@ -81,6 +125,17 @@ class ExternalTableReader {
|
||||
std::vector<std::string>* values,
|
||||
std::vector<Status>* statuses) = 0;
|
||||
|
||||
// Allocate and return the contents of the properties block. If the builder
|
||||
// supports PutPropertiesBlock(), then this must be supported. The
|
||||
// properties block should be written to the table file as is (no
|
||||
// compression or mutation of any kind), and its offset in the file
|
||||
// should be returned in file_offset.
|
||||
virtual Status GetPropertiesBlock(std::unique_ptr<char[]>* /*property_block*/,
|
||||
uint64_t* /*size*/,
|
||||
uint64_t* /*file_offset*/) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
// Return TableProperties for the file. At a minimum, the following
|
||||
// properties need to be returned -
|
||||
// comparator_name
|
||||
@@ -135,6 +190,11 @@ class ExternalTableBuilder {
|
||||
// Finish().
|
||||
virtual uint64_t FileSize() const = 0;
|
||||
|
||||
// Write the raw properties block as is in the table file
|
||||
virtual Status PutPropertiesBlock(const Slice& /*property_block*/) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
// As mentioned in earlier comments, the following table properties must be
|
||||
// returned at a minimum -
|
||||
// comparator_name
|
||||
@@ -153,11 +213,17 @@ class ExternalTableBuilder {
|
||||
struct ExternalTableOptions {
|
||||
const std::shared_ptr<const SliceTransform>& prefix_extractor;
|
||||
const Comparator* comparator;
|
||||
const std::shared_ptr<FileSystem>& fs;
|
||||
const FileOptions& file_options;
|
||||
|
||||
ExternalTableOptions(
|
||||
const std::shared_ptr<const SliceTransform>& _prefix_extractor,
|
||||
const Comparator* _comparator)
|
||||
: prefix_extractor(_prefix_extractor), comparator(_comparator) {}
|
||||
const Comparator* _comparator, const std::shared_ptr<FileSystem>& _fs,
|
||||
const FileOptions& _file_options)
|
||||
: prefix_extractor(_prefix_extractor),
|
||||
comparator(_comparator),
|
||||
fs(_fs),
|
||||
file_options(_file_options) {}
|
||||
};
|
||||
|
||||
struct ExternalTableBuilderOptions {
|
||||
@@ -194,9 +260,11 @@ class ExternalTableFactory : public Customizable {
|
||||
const ExternalTableOptions& table_options,
|
||||
std::unique_ptr<ExternalTableReader>* table_reader) const = 0;
|
||||
|
||||
// The table builder should use the file pointer to append to the file.
|
||||
// Do not sync or close the file after finishing. RocksDB will do that.
|
||||
virtual ExternalTableBuilder* NewTableBuilder(
|
||||
const ExternalTableBuilderOptions& builder_options,
|
||||
const std::string& file_path) const = 0;
|
||||
const std::string& file_path, FSWritableFile* file) const = 0;
|
||||
};
|
||||
|
||||
// Allocate a TableFactory that wraps around an ExternalTableFactory. Use this
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/iterator_base.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/wide_columns.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -93,6 +94,18 @@ class Iterator : public IteratorBase {
|
||||
assert(false);
|
||||
return Slice();
|
||||
}
|
||||
|
||||
// RocksDB Internal - DO NOT USE
|
||||
// Prepare the iterator to scan the ranges specified in scan_opts. The
|
||||
// upper bound and other table specific limits may be specified. This will
|
||||
// typically be followed by Seeks to the start keys in the order they're
|
||||
// specified in scan_opts. If the user does a Seek to some other target key,
|
||||
// the iterator should disregard the scan_opts from that point onwards and
|
||||
// behave like a normal iterator. Its the user's responsibility to again
|
||||
// call Prepare().
|
||||
// If Prepare() is called, it overrides the iterate_upper_bound in
|
||||
// ReadOptions
|
||||
virtual void Prepare(const std::vector<ScanOptions>& /*scan_opts*/) {}
|
||||
};
|
||||
|
||||
// Return an empty iterator (yields nothing).
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// EXPERIMENTAL
|
||||
//
|
||||
// An iterator that returns results from multiple scan ranges. The ranges are
|
||||
// expected to be in increasing sorted order.
|
||||
// The results are returned in nested container objects that can be iterated
|
||||
// using an std::input_iterator.
|
||||
//
|
||||
// MultiScan
|
||||
// |
|
||||
// ---
|
||||
// |
|
||||
// MultiScanIterator <-- std::input_iterator (returns a Scan object for each
|
||||
// | scan range)
|
||||
// ---
|
||||
// |
|
||||
// Scan
|
||||
// |
|
||||
// ---
|
||||
// |
|
||||
// ScanIterator <-- std::input_iterator (returns the KVs of a single
|
||||
// scan range)
|
||||
//
|
||||
// The application on top of RocksDB
|
||||
// would use this as follows -
|
||||
//
|
||||
// std::vector<ScanOptions> scans{{.start = Slice("bar")},
|
||||
// {.start = Slice("foo")}};
|
||||
// std::unique_ptr<MultiScan> iter.reset(
|
||||
// db->NewMultiScan());
|
||||
// try {
|
||||
// for (auto scan : *iter) {
|
||||
// for (auto it : scan) {
|
||||
// // Do something with key - it.first
|
||||
// // Do something with value - it.second
|
||||
// }
|
||||
// }
|
||||
// } catch (MultiScanException& ex) {
|
||||
// // Check ex.status()
|
||||
// } catch (std::logic_error& ex) {
|
||||
// // Check ex.what()
|
||||
// }
|
||||
|
||||
class MultiScanException : public std::runtime_error {
|
||||
public:
|
||||
explicit MultiScanException(Status& s)
|
||||
: std::runtime_error(s.ToString()), s_(s) {}
|
||||
|
||||
Status& status() { return s_; }
|
||||
|
||||
private:
|
||||
Status s_;
|
||||
};
|
||||
|
||||
// A container object encapsulating a single scan range. It supports an
|
||||
// std::input_iterator for a single pass iteration of the KVs in the range.
|
||||
// A Status exception is thrown if there is an error in scanning the range.
|
||||
class Scan {
|
||||
public:
|
||||
class ScanIterator;
|
||||
|
||||
explicit Scan(Iterator* db_iter) : db_iter_(db_iter) {}
|
||||
|
||||
ScanIterator begin() { return ScanIterator(db_iter_); }
|
||||
|
||||
std::nullptr_t end() { return nullptr; }
|
||||
|
||||
class ScanIterator {
|
||||
public:
|
||||
using self_type = ScanIterator;
|
||||
using value_type = std::pair<Slice, Slice>;
|
||||
using reference = std::pair<Slice, Slice>&;
|
||||
using pointer = std::pair<Slice, Slice>*;
|
||||
using difference_type = int;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
explicit ScanIterator(Iterator* db_iter) : db_iter_(db_iter) {
|
||||
valid_ = db_iter_->Valid();
|
||||
if (valid_) {
|
||||
result_ = value_type(db_iter_->key(), db_iter_->value());
|
||||
}
|
||||
}
|
||||
|
||||
ScanIterator() : db_iter_(nullptr), valid_(false) {}
|
||||
|
||||
~ScanIterator() { assert(status_.ok()); }
|
||||
|
||||
ScanIterator& operator++() {
|
||||
if (!valid_) {
|
||||
throw std::logic_error("Trying to advance invalid iterator");
|
||||
} else {
|
||||
db_iter_->Next();
|
||||
status_ = db_iter_->status();
|
||||
if (!status_.ok()) {
|
||||
throw MultiScanException(status_);
|
||||
} else {
|
||||
valid_ = db_iter_->Valid();
|
||||
if (valid_) {
|
||||
result_ = value_type(db_iter_->key(), db_iter_->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(std::nullptr_t /*other*/) const { return !valid_; }
|
||||
|
||||
bool operator!=(std::nullptr_t /*other*/) const { return valid_; }
|
||||
|
||||
reference operator*() {
|
||||
if (!valid_) {
|
||||
throw std::logic_error("Trying to deref invalid iterator");
|
||||
}
|
||||
return result_;
|
||||
}
|
||||
reference operator->() {
|
||||
if (!valid_) {
|
||||
throw std::logic_error("Trying to deref invalid iterator");
|
||||
}
|
||||
return result_;
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator* db_iter_;
|
||||
bool valid_;
|
||||
Status status_;
|
||||
value_type result_;
|
||||
};
|
||||
|
||||
private:
|
||||
Iterator* db_iter_;
|
||||
};
|
||||
|
||||
// A container object encapsulating the scan ranges for a multi scan.
|
||||
// It supports an std::input_iterator for a single pass iteration of the
|
||||
// ScanOptions in scan_opts, which can be dereferenced to get the container
|
||||
// (Scan) for a single range.
|
||||
// A Status exception is thrown if there is an error.
|
||||
class MultiScan {
|
||||
public:
|
||||
MultiScan(const std::vector<ScanOptions>& scan_opts,
|
||||
std::unique_ptr<Iterator>&& db_iter)
|
||||
: scan_opts_(scan_opts), db_iter_(std::move(db_iter)) {}
|
||||
|
||||
explicit MultiScan(std::unique_ptr<Iterator>&& db_iter)
|
||||
: db_iter_(std::move(db_iter)) {}
|
||||
|
||||
class MultiScanIterator {
|
||||
public:
|
||||
MultiScanIterator(const MultiScanIterator&) = delete;
|
||||
MultiScanIterator operator=(MultiScanIterator&) = delete;
|
||||
|
||||
using self_type = MultiScanIterator;
|
||||
using value_type = Scan;
|
||||
using reference = Scan&;
|
||||
using pointer = Scan*;
|
||||
using difference_type = int;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
MultiScanIterator(const std::vector<ScanOptions>& scan_opts,
|
||||
Iterator* db_iter)
|
||||
: scan_opts_(scan_opts), idx_(0), db_iter_(db_iter), scan_(db_iter_) {
|
||||
if (scan_opts_.empty()) {
|
||||
throw std::logic_error("Zero scans in multi-scan");
|
||||
}
|
||||
db_iter_->Seek(*scan_opts_[idx_].range.start);
|
||||
status_ = db_iter_->status();
|
||||
if (!status_.ok()) {
|
||||
throw MultiScanException(status_);
|
||||
}
|
||||
}
|
||||
|
||||
explicit MultiScanIterator(const std::vector<ScanOptions>& scan_opts)
|
||||
: scan_opts_(scan_opts),
|
||||
idx_(scan_opts_.size()),
|
||||
db_iter_(nullptr),
|
||||
scan_(nullptr) {}
|
||||
|
||||
~MultiScanIterator() { assert(status_.ok()); }
|
||||
|
||||
MultiScanIterator& operator++() {
|
||||
if (idx_ >= scan_opts_.size()) {
|
||||
throw std::logic_error("Index out of range");
|
||||
}
|
||||
idx_++;
|
||||
if (idx_ < scan_opts_.size()) {
|
||||
db_iter_->Seek(*scan_opts_[idx_].range.start);
|
||||
status_ = db_iter_->status();
|
||||
if (!status_.ok()) {
|
||||
throw MultiScanException(status_);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(std::nullptr_t /*other*/) const {
|
||||
return idx_ >= scan_opts_.size();
|
||||
}
|
||||
|
||||
bool operator!=(std::nullptr_t /*other*/) const {
|
||||
return idx_ < scan_opts_.size();
|
||||
}
|
||||
|
||||
reference operator*() { return scan_; }
|
||||
reference operator->() { return scan_; }
|
||||
|
||||
private:
|
||||
const std::vector<ScanOptions>& scan_opts_;
|
||||
size_t idx_;
|
||||
Iterator* db_iter_;
|
||||
Status status_;
|
||||
Scan scan_;
|
||||
};
|
||||
|
||||
MultiScanIterator begin() {
|
||||
return MultiScanIterator(scan_opts_, db_iter_.get());
|
||||
}
|
||||
|
||||
std::nullptr_t end() { return nullptr; }
|
||||
|
||||
private:
|
||||
const std::vector<ScanOptions> scan_opts_;
|
||||
std::unique_ptr<Iterator> db_iter_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+76
-55
@@ -477,12 +477,19 @@ struct CompactionServiceJobInfo {
|
||||
bool is_manual_compaction;
|
||||
bool bottommost_level;
|
||||
|
||||
// the smallest input level of the compaction.
|
||||
// (same as Compaction::start_level and CompactionJobInfo::base_input_level)
|
||||
int base_input_level;
|
||||
// the output level of the compaction.
|
||||
int output_level;
|
||||
|
||||
CompactionServiceJobInfo(std::string db_name_, std::string db_id_,
|
||||
std::string db_session_id_, uint64_t job_id_,
|
||||
Env::Priority priority_,
|
||||
CompactionReason compaction_reason_,
|
||||
bool is_full_compaction_, bool is_manual_compaction_,
|
||||
bool bottommost_level_)
|
||||
bool bottommost_level_, int base_input_level_,
|
||||
int output_level_)
|
||||
: db_name(std::move(db_name_)),
|
||||
db_id(std::move(db_id_)),
|
||||
db_session_id(std::move(db_session_id_)),
|
||||
@@ -491,7 +498,9 @@ struct CompactionServiceJobInfo {
|
||||
compaction_reason(compaction_reason_),
|
||||
is_full_compaction(is_full_compaction_),
|
||||
is_manual_compaction(is_manual_compaction_),
|
||||
bottommost_level(bottommost_level_) {}
|
||||
bottommost_level(bottommost_level_),
|
||||
base_input_level(base_input_level_),
|
||||
output_level(output_level_) {}
|
||||
};
|
||||
|
||||
struct CompactionServiceScheduleResponse {
|
||||
@@ -815,6 +824,7 @@ struct DBOptions {
|
||||
// If it is non empty, the log files will be in the specified dir,
|
||||
// and the db data dir's absolute path will be used as the log file
|
||||
// name's prefix.
|
||||
// NOTE: not for WALs
|
||||
std::string db_log_dir = "";
|
||||
|
||||
// This specifies the absolute dir path for write-ahead logs (WAL).
|
||||
@@ -895,21 +905,24 @@ struct DBOptions {
|
||||
// be created.
|
||||
// If max_log_file_size == 0, all logs will be written to one
|
||||
// log file.
|
||||
// NOTE: not for WALs
|
||||
size_t max_log_file_size = 0;
|
||||
|
||||
// Time for the info log file to roll (in seconds).
|
||||
// If specified with non-zero value, log file will be rolled
|
||||
// if it has been active longer than `log_file_time_to_roll`.
|
||||
// Default: 0 (disabled)
|
||||
// NOTE: not for WALs
|
||||
size_t log_file_time_to_roll = 0;
|
||||
|
||||
// Maximal info log files to be kept.
|
||||
// Default: 1000
|
||||
// NOTE: not for WALs
|
||||
size_t keep_log_file_num = 1000;
|
||||
|
||||
// Recycle log files.
|
||||
// If non-zero, we will reuse previously written log files for new
|
||||
// logs, overwriting the old data. The value indicates how many
|
||||
// Recycle WAL files.
|
||||
// If non-zero, we will reuse previously written WAL files for new
|
||||
// WALs, overwriting the old data. The value indicates how many
|
||||
// such files we will keep around at any point in time for later
|
||||
// use. This is more efficient because the blocks are already
|
||||
// allocated and fdatasync does not need to update the inode after
|
||||
@@ -1297,14 +1310,6 @@ struct DBOptions {
|
||||
// currently.
|
||||
WalFilter* wal_filter = nullptr;
|
||||
|
||||
// DEPRECATED: This option might be removed in a future release.
|
||||
//
|
||||
// If true, then DB::Open, CreateColumnFamily, DropColumnFamily, and
|
||||
// SetOptions will fail if options file is not properly persisted.
|
||||
//
|
||||
// DEFAULT: true
|
||||
bool fail_if_options_file_error = true;
|
||||
|
||||
// If true, then print malloc stats together with rocksdb.stats
|
||||
// when printing to LOG.
|
||||
// DEFAULT: false
|
||||
@@ -1423,9 +1428,10 @@ struct DBOptions {
|
||||
// prefix_same_as_start=true can take advantage of prefix seek optimizations.
|
||||
bool prefix_seek_opt_in_only = false;
|
||||
|
||||
// The number of bytes to prefetch when reading the log. This is mostly useful
|
||||
// for reading a remotely located log, as it can save the number of
|
||||
// round-trips. If 0, then the prefetching is disabled.
|
||||
// The number of bytes to prefetch when reading the DB manifest and WAL files
|
||||
// during DB::Open (and variants). This is mostly useful for reading a
|
||||
// remotely located log, as it can save the number of round-trips. If 0, then
|
||||
// the prefetching is disabled.
|
||||
//
|
||||
// Default: 0
|
||||
size_t log_readahead_size = 0;
|
||||
@@ -1703,6 +1709,50 @@ enum ReadTier {
|
||||
kMemtableTier = 0x3 // data in memtable. used for memtable-only iterators.
|
||||
};
|
||||
|
||||
// A range of keys. In case of user_defined timestamp, if enabled, `start` and
|
||||
// `limit` should point to key without timestamp part.
|
||||
struct Range {
|
||||
Slice start;
|
||||
Slice limit;
|
||||
|
||||
Range() {}
|
||||
Range(const Slice& s, const Slice& l) : start(s), limit(l) {}
|
||||
};
|
||||
|
||||
// A key range with optional endpoints. In case of user_defined timestamp, if
|
||||
// enabled, `start` and `limit` should point to key without timestamp part.
|
||||
struct RangeOpt {
|
||||
// When start.has_value() == false, refers to starting before every key
|
||||
OptSlice start;
|
||||
// When limit.has_value() == false, refers to ending after every key
|
||||
OptSlice limit;
|
||||
|
||||
RangeOpt() {}
|
||||
RangeOpt(const OptSlice& s, const OptSlice& l) : start(s), limit(l) {}
|
||||
};
|
||||
|
||||
// EXPERIMENTAL
|
||||
//
|
||||
// Options for a RocksDB scan request. Only forward scans for now.
|
||||
// We may add other options such as prefix scan in the future.
|
||||
struct ScanOptions {
|
||||
// The scan range. Mandatory for start to be set, limit is optional
|
||||
RangeOpt range;
|
||||
|
||||
// A map of name,value pairs that can be passed by the user to an
|
||||
// external table reader. This is completely opaque to RocksDB and is
|
||||
// ignored by the natively supported table readers like block based and plain
|
||||
// table. This is only useful for Iterator.
|
||||
std::optional<std::unordered_map<std::string, std::string>> property_bag;
|
||||
|
||||
// An unbounded scan with a start key
|
||||
explicit ScanOptions(const Slice& _start) : range(_start, OptSlice()) {}
|
||||
|
||||
// A bounded scan with a start key and upper bound
|
||||
ScanOptions(const Slice& _start, const Slice& _upper_bound)
|
||||
: range(_start, _upper_bound) {}
|
||||
};
|
||||
|
||||
// Options that control read operations
|
||||
struct ReadOptions {
|
||||
// *** BEGIN options relevant to point lookups as well as scans ***
|
||||
@@ -1784,6 +1834,10 @@ struct ReadOptions {
|
||||
// block cache.
|
||||
bool fill_cache = true;
|
||||
|
||||
// DEPRECATED: This option might be removed in a future release.
|
||||
// There should be no noticeable performance difference whether this option
|
||||
// is turned on or off when a DB does not use DeleteRange().
|
||||
//
|
||||
// If true, range tombstones handling will be skipped in key lookup paths.
|
||||
// For DB instances that don't use DeleteRange() calls, this setting can
|
||||
// be used to optimize the read performance.
|
||||
@@ -1996,22 +2050,6 @@ struct ReadOptions {
|
||||
// EXPERIMENTAL
|
||||
Env::IOActivity io_activity = Env::IOActivity::kUnknown;
|
||||
|
||||
// EXPERIMENTAL
|
||||
// An optional weight of values to be returned by a scan. Once the
|
||||
// weight is reached or exceeded the scan is terminated (i.e Next()
|
||||
// invalidates the iterator). In the case of a DB with one of the built-in
|
||||
// table formats, such as BlockBasedTable, the weight is simply the number
|
||||
// of key-value pairs. In the case of an ExternalTableReader, the weight is
|
||||
// passed through to the table reader and the interpretation is upto the
|
||||
// reader implementation.
|
||||
uint64_t weight = 0;
|
||||
|
||||
// A map of name,value pairs that can be passed by the user to an
|
||||
// external table reader. This is completely opaque to RocksDB and is
|
||||
// ignored by the natively supported table readers like block based and plain
|
||||
// table. This is only useful for Iterator.
|
||||
std::optional<std::unordered_map<std::string, std::string>> property_bag;
|
||||
|
||||
// *** END options for RocksDB internal use only ***
|
||||
|
||||
ReadOptions() {}
|
||||
@@ -2245,29 +2283,6 @@ struct CompactRangeOptions {
|
||||
double blob_garbage_collection_age_cutoff = -1;
|
||||
};
|
||||
|
||||
// A range of keys. In case of user_defined timestamp, if enabled, `start` and
|
||||
// `limit` should point to key without timestamp part.
|
||||
struct Range {
|
||||
Slice start;
|
||||
Slice limit;
|
||||
|
||||
Range() {}
|
||||
Range(const Slice& s, const Slice& l) : start(s), limit(l) {}
|
||||
};
|
||||
|
||||
// A key range with optional endpoints. In case of user_defined timestamp, if
|
||||
// enabled, `start` and `limit` should point to key without timestamp part.
|
||||
struct RangeOpt {
|
||||
// When start.has_value() == false, refers to starting before every key
|
||||
OptSlice start;
|
||||
// When limit.has_value() == false, refers to ending after every key
|
||||
OptSlice limit;
|
||||
|
||||
RangeOpt() {}
|
||||
RangeOpt(const OptSlice& s, const OptSlice& l) : start(s), limit(l) {}
|
||||
// RangeOpt(const Slice& s, const Slice& l) : start(s), limit(l) {}
|
||||
};
|
||||
|
||||
// IngestExternalFileOptions is used by IngestExternalFile()
|
||||
struct IngestExternalFileOptions {
|
||||
// Can be set to true to move the files instead of copying them.
|
||||
@@ -2500,10 +2515,16 @@ struct CompactionServiceOptionsOverride {
|
||||
// to set it here.
|
||||
std::shared_ptr<Statistics> statistics = nullptr;
|
||||
|
||||
// Info Log. If not overriden, default one will be used.
|
||||
std::shared_ptr<Logger> info_log = nullptr;
|
||||
|
||||
// Only compaction generated SST files use this user defined table properties
|
||||
// collector.
|
||||
std::vector<std::shared_ptr<TablePropertiesCollectorFactory>>
|
||||
table_properties_collector_factories;
|
||||
|
||||
// All other options to override. Unknown options will be ignored.
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
};
|
||||
|
||||
struct OpenAndCompactOptions {
|
||||
|
||||
@@ -520,10 +520,11 @@ enum Tickers : uint32_t {
|
||||
// Number of bytes prefetched during user initiated scan
|
||||
PREFETCH_BYTES,
|
||||
|
||||
// Number of prefetched bytes that were actually useful
|
||||
// Number of prefetched bytes that were actually useful during user initiated
|
||||
// scan
|
||||
PREFETCH_BYTES_USEFUL,
|
||||
|
||||
// Number of FS reads avoided due to scan prefetching
|
||||
// Number of FS reads avoided due to prefetching during user initiated scan
|
||||
PREFETCH_HITS,
|
||||
|
||||
// Footer corruption detected when opening an SST file for reading
|
||||
@@ -657,13 +658,17 @@ enum Histograms : uint32_t {
|
||||
ASYNC_READ_BYTES,
|
||||
POLL_WAIT_MICROS,
|
||||
|
||||
// Number of bytes for RocksDB's prefetching (as opposed to file
|
||||
// system's prefetch) on SST file during compaction read
|
||||
COMPACTION_PREFETCH_BYTES,
|
||||
|
||||
// Number of prefetched bytes discarded by RocksDB.
|
||||
PREFETCHED_BYTES_DISCARDED,
|
||||
|
||||
// Wait time for aborting async read in FilePrefetchBuffer destructor
|
||||
ASYNC_PREFETCH_ABORT_MICROS,
|
||||
|
||||
// Number of bytes read for RocksDB's prefetching contents (as opposed to file
|
||||
// Number of bytes for RocksDB's prefetching contents (as opposed to file
|
||||
// system's prefetch) from the end of SST table during block based table open
|
||||
TABLE_OPEN_PREFETCH_TAIL_READ_BYTES,
|
||||
|
||||
|
||||
@@ -289,6 +289,13 @@ class StackableDB : public DB {
|
||||
return db_->NewAttributeGroupIterator(options, column_families);
|
||||
}
|
||||
|
||||
using DB::NewMultiScan;
|
||||
std::unique_ptr<MultiScan> NewMultiScan(
|
||||
const ReadOptions& opts, ColumnFamilyHandle* column_family,
|
||||
const std::vector<ScanOptions>& scan_opts) override {
|
||||
return db_->NewMultiScan(opts, column_family, scan_opts);
|
||||
}
|
||||
|
||||
const Snapshot* GetSnapshot() override { return db_->GetSnapshot(); }
|
||||
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override {
|
||||
@@ -505,13 +512,18 @@ class StackableDB : public DB {
|
||||
return db_->GetFullHistoryTsLow(column_family, ts_low);
|
||||
}
|
||||
|
||||
Status GetNewestUserDefinedTimestamp(ColumnFamilyHandle* column_family,
|
||||
std::string* newest_timestamp) override {
|
||||
return db_->GetNewestUserDefinedTimestamp(column_family, newest_timestamp);
|
||||
}
|
||||
|
||||
Status GetSortedWalFiles(VectorWalPtr& files) override {
|
||||
return db_->GetSortedWalFiles(files);
|
||||
}
|
||||
|
||||
Status GetCurrentWalFile(
|
||||
std::unique_ptr<WalFile>* current_log_file) override {
|
||||
return db_->GetCurrentWalFile(current_log_file);
|
||||
std::unique_ptr<WalFile>* current_wal_file) override {
|
||||
return db_->GetCurrentWalFile(current_wal_file);
|
||||
}
|
||||
|
||||
Status GetCreationTimeOfOldestFile(uint64_t* creation_time) override {
|
||||
|
||||
@@ -24,9 +24,16 @@ class SecondaryIndex;
|
||||
class TransactionDBMutexFactory;
|
||||
|
||||
enum TxnDBWritePolicy {
|
||||
WRITE_COMMITTED = 0, // write only the committed data
|
||||
WRITE_PREPARED, // write data after the prepare phase of 2pc
|
||||
WRITE_UNPREPARED // write data before the prepare phase of 2pc
|
||||
// Write data at transaction commit time
|
||||
WRITE_COMMITTED = 0,
|
||||
|
||||
// EXPERIMENTAL: The remaining write policies are not as mature, well
|
||||
// validated, nor as compatible with other features as WRITE_COMMITTED.
|
||||
|
||||
// Write data after the prepare phase of 2pc
|
||||
WRITE_PREPARED,
|
||||
// Write data before the prepare phase of 2pc
|
||||
WRITE_UNPREPARED
|
||||
};
|
||||
|
||||
constexpr uint32_t kInitialMaxDeadlocks = 5;
|
||||
@@ -361,6 +368,8 @@ struct TransactionOptions {
|
||||
// Only supports write-committed policy. If set to true, the transaction will
|
||||
// skip memtable write and ingest into the DB directly during Commit(). This
|
||||
// makes Commit() much faster for transactions with many operations.
|
||||
// Transaction neeeds to call Prepare() before Commit() for this option to
|
||||
// take effect.
|
||||
// Transactions with Merge() or PutEntity() is not supported yet.
|
||||
//
|
||||
// Note that the transaction will be ingested as an immutable memtable for
|
||||
|
||||
@@ -90,6 +90,8 @@ class WBWIIterator {
|
||||
// Returns n where the current entry is the n-th update to the current key.
|
||||
// The update count starts from 1.
|
||||
// Only valid if WBWI is created with overwrite_key = true.
|
||||
// With overwrite_key=false, update count for each entry is not maintained,
|
||||
// see UpdateExistingEntryWithCfId().
|
||||
virtual uint32_t GetUpdateCount() const { return 0; }
|
||||
};
|
||||
|
||||
@@ -374,9 +376,6 @@ class WriteBatchWithIndex : public WriteBatchBase {
|
||||
uint32_t entry_count = 0;
|
||||
uint32_t overwritten_sd_count = 0;
|
||||
};
|
||||
// Will track CF ID, per CF entry count and overwritten sd count.
|
||||
// Should be enabled when WBWI is empty for correct tracking.
|
||||
void SetTrackPerCFStat(bool track);
|
||||
const std::unordered_map<uint32_t, CFStat>& GetCFStats() const;
|
||||
|
||||
bool GetOverwriteKey() const;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// minor or major version number planned for release.
|
||||
#define ROCKSDB_MAJOR 10
|
||||
#define ROCKSDB_MINOR 2
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_PATCH 3
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
set(JAVA_JUNIT_VERSION "4.13.1")
|
||||
set(JAVA_HAMCR_VERSION "2.2")
|
||||
|
||||
@@ -2055,29 +2055,6 @@ void Java_org_rocksdb_Options_setWalFilter(JNIEnv*, jclass, jlong jhandle,
|
||||
opt->wal_filter = wal_filter;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setFailIfOptionsFileError
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setFailIfOptionsFileError(
|
||||
JNIEnv*, jclass, jlong jhandle, jboolean jfail_if_options_file_error) {
|
||||
auto* opt = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle);
|
||||
opt->fail_if_options_file_error =
|
||||
static_cast<bool>(jfail_if_options_file_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: failIfOptionsFileError
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_Options_failIfOptionsFileError(JNIEnv*, jclass,
|
||||
jlong jhandle) {
|
||||
auto* opt = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle);
|
||||
return static_cast<jboolean>(opt->fail_if_options_file_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setDumpMallocStats
|
||||
@@ -2456,28 +2433,6 @@ void Java_org_rocksdb_Options_setMinWriteBufferNumberToMerge(
|
||||
->min_write_buffer_number_to_merge =
|
||||
static_cast<int>(jmin_write_buffer_number_to_merge);
|
||||
}
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: maxWriteBufferNumberToMaintain
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_Options_maxWriteBufferNumberToMaintain(JNIEnv*, jclass,
|
||||
jlong jhandle) {
|
||||
return reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle)
|
||||
->max_write_buffer_number_to_maintain;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setMaxWriteBufferNumberToMaintain
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setMaxWriteBufferNumberToMaintain(
|
||||
JNIEnv*, jclass, jlong jhandle, jint jmax_write_buffer_number_to_maintain) {
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle)
|
||||
->max_write_buffer_number_to_maintain =
|
||||
static_cast<int>(jmax_write_buffer_number_to_maintain);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
@@ -4496,29 +4451,6 @@ void Java_org_rocksdb_ColumnFamilyOptions_setMinWriteBufferNumberToMerge(
|
||||
static_cast<int>(jmin_write_buffer_number_to_merge);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ColumnFamilyOptions
|
||||
* Method: maxWriteBufferNumberToMaintain
|
||||
* Signature: (J)I
|
||||
*/
|
||||
jint Java_org_rocksdb_ColumnFamilyOptions_maxWriteBufferNumberToMaintain(
|
||||
JNIEnv*, jclass, jlong jhandle) {
|
||||
return reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyOptions*>(jhandle)
|
||||
->max_write_buffer_number_to_maintain;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ColumnFamilyOptions
|
||||
* Method: setMaxWriteBufferNumberToMaintain
|
||||
* Signature: (JI)V
|
||||
*/
|
||||
void Java_org_rocksdb_ColumnFamilyOptions_setMaxWriteBufferNumberToMaintain(
|
||||
JNIEnv*, jclass, jlong jhandle, jint jmax_write_buffer_number_to_maintain) {
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyOptions*>(jhandle)
|
||||
->max_write_buffer_number_to_maintain =
|
||||
static_cast<int>(jmax_write_buffer_number_to_maintain);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_ColumnFamilyOptions
|
||||
* Method: setCompressionType
|
||||
@@ -7524,29 +7456,6 @@ void Java_org_rocksdb_DBOptions_setWalFilter(JNIEnv*, jclass, jlong jhandle,
|
||||
opt->wal_filter = wal_filter;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: setFailIfOptionsFileError
|
||||
* Signature: (JZ)V
|
||||
*/
|
||||
void Java_org_rocksdb_DBOptions_setFailIfOptionsFileError(
|
||||
JNIEnv*, jclass, jlong jhandle, jboolean jfail_if_options_file_error) {
|
||||
auto* opt = reinterpret_cast<ROCKSDB_NAMESPACE::DBOptions*>(jhandle);
|
||||
opt->fail_if_options_file_error =
|
||||
static_cast<bool>(jfail_if_options_file_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: failIfOptionsFileError
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
jboolean Java_org_rocksdb_DBOptions_failIfOptionsFileError(JNIEnv*, jclass,
|
||||
jlong jhandle) {
|
||||
auto* opt = reinterpret_cast<ROCKSDB_NAMESPACE::DBOptions*>(jhandle);
|
||||
return static_cast<jboolean>(opt->fail_if_options_file_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: setDumpMallocStats
|
||||
|
||||
@@ -5889,8 +5889,11 @@ class HistogramTypeJni {
|
||||
return 0x3C;
|
||||
case ROCKSDB_NAMESPACE::Histograms::TABLE_OPEN_PREFETCH_TAIL_READ_BYTES:
|
||||
return 0x3D;
|
||||
case ROCKSDB_NAMESPACE::Histograms::COMPACTION_PREFETCH_BYTES:
|
||||
return 0x3F;
|
||||
case ROCKSDB_NAMESPACE::Histograms::HISTOGRAM_ENUM_MAX:
|
||||
// 0x3D for backwards compatibility on current minor version.
|
||||
// 0x3E is reserved for backwards compatibility on current minor
|
||||
// version.
|
||||
return 0x3E;
|
||||
default:
|
||||
// undefined/default
|
||||
@@ -6033,8 +6036,11 @@ class HistogramTypeJni {
|
||||
case 0x3D:
|
||||
return ROCKSDB_NAMESPACE::Histograms::
|
||||
TABLE_OPEN_PREFETCH_TAIL_READ_BYTES;
|
||||
case 0x3F:
|
||||
return ROCKSDB_NAMESPACE::Histograms::COMPACTION_PREFETCH_BYTES;
|
||||
case 0x3E:
|
||||
// 0x1F for backwards compatibility on current minor version.
|
||||
// 0x3E is reserved for backwards compatibility on current minor
|
||||
// version.
|
||||
return ROCKSDB_NAMESPACE::Histograms::HISTOGRAM_ENUM_MAX;
|
||||
|
||||
default:
|
||||
|
||||
@@ -44,53 +44,6 @@ public interface AdvancedColumnFamilyOptionsInterface<
|
||||
*/
|
||||
int minWriteBufferNumberToMerge();
|
||||
|
||||
/**
|
||||
* The total maximum number of write buffers to maintain in memory including
|
||||
* copies of buffers that have already been flushed. Unlike
|
||||
* {@link AdvancedMutableColumnFamilyOptionsInterface#maxWriteBufferNumber()},
|
||||
* this parameter does not affect flushing.
|
||||
* This controls the minimum amount of write history that will be available
|
||||
* in memory for conflict checking when Transactions are used.
|
||||
* <p>
|
||||
* When using an OptimisticTransactionDB:
|
||||
* If this value is too low, some transactions may fail at commit time due
|
||||
* to not being able to determine whether there were any write conflicts.
|
||||
* <p>
|
||||
* When using a TransactionDB:
|
||||
* If Transaction::SetSnapshot is used, TransactionDB will read either
|
||||
* in-memory write buffers or SST files to do write-conflict checking.
|
||||
* Increasing this value can reduce the number of reads to SST files
|
||||
* done for conflict detection.
|
||||
* <p>
|
||||
* Setting this value to 0 will cause write buffers to be freed immediately
|
||||
* after they are flushed.
|
||||
* If this value is set to -1,
|
||||
* {@link AdvancedMutableColumnFamilyOptionsInterface#maxWriteBufferNumber()}
|
||||
* will be used.
|
||||
* <p>
|
||||
* Default:
|
||||
* If using a TransactionDB/OptimisticTransactionDB, the default value will
|
||||
* be set to the value of
|
||||
* {@link AdvancedMutableColumnFamilyOptionsInterface#maxWriteBufferNumber()}
|
||||
* if it is not explicitly set by the user. Otherwise, the default is 0.
|
||||
*
|
||||
* @param maxWriteBufferNumberToMaintain The maximum number of write
|
||||
* buffers to maintain
|
||||
*
|
||||
* @return the reference to the current options.
|
||||
*/
|
||||
T setMaxWriteBufferNumberToMaintain(
|
||||
int maxWriteBufferNumberToMaintain);
|
||||
|
||||
/**
|
||||
* The total maximum number of write buffers to maintain in memory including
|
||||
* copies of buffers that have already been flushed.
|
||||
*
|
||||
* @return maxWriteBufferNumberToMaintain The maximum number of write buffers
|
||||
* to maintain
|
||||
*/
|
||||
int maxWriteBufferNumberToMaintain();
|
||||
|
||||
/**
|
||||
* Allows thread-safe inplace updates.
|
||||
* If inplace_callback function is not set,
|
||||
|
||||
@@ -835,19 +835,6 @@ public class ColumnFamilyOptions
|
||||
return paranoidFileChecks(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setMaxWriteBufferNumberToMaintain(
|
||||
final int maxWriteBufferNumberToMaintain) {
|
||||
setMaxWriteBufferNumberToMaintain(
|
||||
nativeHandle_, maxWriteBufferNumberToMaintain);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maxWriteBufferNumberToMaintain() {
|
||||
return maxWriteBufferNumberToMaintain(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setCompactionPriority(
|
||||
final CompactionPriority compactionPriority) {
|
||||
@@ -1467,9 +1454,6 @@ public class ColumnFamilyOptions
|
||||
private static native int[] maxBytesForLevelMultiplierAdditional(long handle);
|
||||
private static native void setParanoidFileChecks(long handle, boolean paranoidFileChecks);
|
||||
private static native boolean paranoidFileChecks(long handle);
|
||||
private static native void setMaxWriteBufferNumberToMaintain(
|
||||
final long handle, final int maxWriteBufferNumberToMaintain);
|
||||
private static native int maxWriteBufferNumberToMaintain(final long handle);
|
||||
private static native void setCompactionPriority(
|
||||
final long handle, final byte compactionPriority);
|
||||
private static native byte compactionPriority(final long handle);
|
||||
|
||||
@@ -210,7 +210,9 @@ public enum HistogramType {
|
||||
*/
|
||||
TABLE_OPEN_PREFETCH_TAIL_READ_BYTES((byte) 0x3D),
|
||||
|
||||
// 0x3E for backwards compatibility on current minor version.
|
||||
COMPACTION_PREFETCH_BYTES((byte) 0x3F),
|
||||
|
||||
// 0x3E is reserved for backwards compatibility on current minor version.
|
||||
HISTOGRAM_ENUM_MAX((byte) 0x3E);
|
||||
|
||||
private final byte value;
|
||||
|
||||
@@ -1762,19 +1762,6 @@ public class Options extends RocksObject
|
||||
return paranoidFileChecks(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Options setMaxWriteBufferNumberToMaintain(
|
||||
final int maxWriteBufferNumberToMaintain) {
|
||||
setMaxWriteBufferNumberToMaintain(
|
||||
nativeHandle_, maxWriteBufferNumberToMaintain);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maxWriteBufferNumberToMaintain() {
|
||||
return maxWriteBufferNumberToMaintain(nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Options setCompactionPriority(
|
||||
final CompactionPriority compactionPriority) {
|
||||
@@ -2443,9 +2430,6 @@ public class Options extends RocksObject
|
||||
private static native int[] maxBytesForLevelMultiplierAdditional(long handle);
|
||||
private static native void setParanoidFileChecks(long handle, boolean paranoidFileChecks);
|
||||
private static native boolean paranoidFileChecks(long handle);
|
||||
private static native void setMaxWriteBufferNumberToMaintain(
|
||||
final long handle, final int maxWriteBufferNumberToMaintain);
|
||||
private static native int maxWriteBufferNumberToMaintain(final long handle);
|
||||
private static native void setCompactionPriority(
|
||||
final long handle, final byte compactionPriority);
|
||||
private static native byte compactionPriority(final long handle);
|
||||
|
||||
@@ -398,7 +398,10 @@ public class ReadOptions extends RocksObject {
|
||||
* Default: false
|
||||
*
|
||||
* @return true if keys deleted using the DeleteRange() API will be visible
|
||||
*
|
||||
* @deprecated This option may be remove in a future release.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean ignoreRangeDeletions() {
|
||||
assert(isOwningHandle());
|
||||
return ignoreRangeDeletions(nativeHandle_);
|
||||
@@ -414,7 +417,10 @@ public class ReadOptions extends RocksObject {
|
||||
* @param ignoreRangeDeletions true if keys deleted using the DeleteRange()
|
||||
* API should be visible
|
||||
* @return the reference to the current ReadOptions.
|
||||
*
|
||||
* @deprecated This option may be remove in a future release.
|
||||
*/
|
||||
@Deprecated
|
||||
public ReadOptions setIgnoreRangeDeletions(final boolean ignoreRangeDeletions) {
|
||||
assert(isOwningHandle());
|
||||
setIgnoreRangeDeletions(nativeHandle_, ignoreRangeDeletions);
|
||||
|
||||
@@ -4126,6 +4126,7 @@ public class RocksDB extends RocksObject {
|
||||
*
|
||||
* @return the maximum level
|
||||
*/
|
||||
@Deprecated
|
||||
public int maxMemCompactionLevel() {
|
||||
return maxMemCompactionLevel(null);
|
||||
}
|
||||
@@ -4633,10 +4634,13 @@ public class RocksDB extends RocksObject {
|
||||
* @param targetLevel the target level for L0
|
||||
*
|
||||
* @throws RocksDBException if an error occurs whilst promoting L0
|
||||
*
|
||||
* @deprecated this API may be removed in a future release.
|
||||
*/
|
||||
@Deprecated
|
||||
public void promoteL0(
|
||||
/* @Nullable */final ColumnFamilyHandle columnFamilyHandle,
|
||||
final int targetLevel) throws RocksDBException {
|
||||
/* @Nullable */ final ColumnFamilyHandle columnFamilyHandle, final int targetLevel)
|
||||
throws RocksDBException {
|
||||
promoteL0(nativeHandle_,
|
||||
columnFamilyHandle == null ? 0 : columnFamilyHandle.nativeHandle_,
|
||||
targetLevel);
|
||||
@@ -4648,9 +4652,11 @@ public class RocksDB extends RocksObject {
|
||||
* @param targetLevel the target level for L0
|
||||
*
|
||||
* @throws RocksDBException if an error occurs whilst promoting L0
|
||||
*
|
||||
* @deprecated this API may be removed in a future release.
|
||||
*/
|
||||
public void promoteL0(final int targetLevel)
|
||||
throws RocksDBException {
|
||||
@Deprecated
|
||||
public void promoteL0(final int targetLevel) throws RocksDBException {
|
||||
promoteL0(null, targetLevel);
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ public class Transaction extends RocksObject {
|
||||
* Status::Busy() may be returned if the transaction could not guarantee
|
||||
* that there are no write conflicts. Status::TryAgain() may be returned
|
||||
* if the memtable history size is not large enough
|
||||
* (See max_write_buffer_number_to_maintain).
|
||||
* (See max_write_buffer_size_to_maintain).
|
||||
* <p>
|
||||
* If this transaction was created by a {@link TransactionDB},
|
||||
* Status::Expired() may be returned if this transaction has lived for
|
||||
@@ -689,8 +689,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
* {@link Status.Code#MergeInProgress} if merge operations cannot be
|
||||
* resolved.
|
||||
*
|
||||
@@ -770,8 +769,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
* {@link Status.Code#MergeInProgress} if merge operations cannot be
|
||||
* resolved.
|
||||
*
|
||||
@@ -821,8 +819,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
* {@link Status.Code#MergeInProgress} if merge operations cannot be
|
||||
* resolved.
|
||||
*
|
||||
@@ -880,8 +877,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
* {@link Status.Code#MergeInProgress} if merge operations cannot be
|
||||
* resolved.
|
||||
*
|
||||
@@ -933,8 +929,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
* {@link Status.Code#MergeInProgress} if merge operations cannot be
|
||||
* resolved.
|
||||
*
|
||||
@@ -988,8 +983,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
* {@link Status.Code#MergeInProgress} if merge operations cannot be
|
||||
* resolved.
|
||||
*
|
||||
@@ -1050,8 +1044,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
* {@link Status.Code#MergeInProgress} if merge operations cannot be
|
||||
* resolved.
|
||||
*
|
||||
@@ -1106,8 +1099,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
* {@link Status.Code#MergeInProgress} if merge operations cannot be
|
||||
* resolved.
|
||||
*
|
||||
@@ -1393,8 +1385,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to put the key/value into
|
||||
* @param key the specified key to be inserted.
|
||||
@@ -1430,8 +1421,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to put the key/value into
|
||||
* @param key the specified key to be inserted.
|
||||
@@ -1460,8 +1450,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param key the specified key to be inserted.
|
||||
* @param value the value associated with the specified key.
|
||||
@@ -1536,8 +1525,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param key the specified key to be inserted.
|
||||
* @param value the value associated with the specified key.
|
||||
@@ -1575,8 +1563,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to put the key/value into
|
||||
* @param key the specified key to be inserted.
|
||||
@@ -1645,8 +1632,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to merge the key/value into
|
||||
* @param key the specified key to be merged.
|
||||
@@ -1683,8 +1669,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to merge the key/value into
|
||||
* @param key the specified key to be merged.
|
||||
@@ -1713,8 +1698,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param key the specified key to be merged.
|
||||
* @param value the value associated with the specified key.
|
||||
@@ -1741,8 +1725,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param key the specified key to be merged.
|
||||
* @param value the value associated with the specified key.
|
||||
@@ -1778,8 +1761,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle in which to apply the merge
|
||||
* @param key the specified key to be merged.
|
||||
@@ -1821,8 +1803,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle in which to apply the merge
|
||||
* @param key the specified key to be merged.
|
||||
@@ -1849,8 +1830,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to delete the key/value from
|
||||
* @param key the specified key to be deleted.
|
||||
@@ -1885,8 +1865,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to delete the key/value from
|
||||
* @param key the specified key to be deleted.
|
||||
@@ -1914,8 +1893,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param key the specified key to be deleted.
|
||||
*
|
||||
@@ -2001,8 +1979,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to delete the key/value from
|
||||
* @param key the specified key to be deleted.
|
||||
@@ -2038,8 +2015,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to delete the key/value from
|
||||
* @param key the specified key to be deleted.
|
||||
@@ -2068,8 +2044,7 @@ public class Transaction extends RocksObject {
|
||||
* {@link Status.Code#Busy} if there is a write conflict,
|
||||
* {@link Status.Code#TimedOut} if a lock could not be acquired,
|
||||
* {@link Status.Code#TryAgain} if the memtable history size is not large
|
||||
* enough. See
|
||||
* {@link ColumnFamilyOptions#maxWriteBufferNumberToMaintain()}
|
||||
* enough.
|
||||
*
|
||||
* @param key the specified key to be deleted.
|
||||
*
|
||||
|
||||
@@ -561,19 +561,6 @@ public class ColumnFamilyOptionsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maxWriteBufferNumberToMaintain() {
|
||||
try (final ColumnFamilyOptions opt = new ColumnFamilyOptions()) {
|
||||
int intValue = rand.nextInt();
|
||||
// Size has to be positive
|
||||
intValue = (intValue < 0) ? -intValue : intValue;
|
||||
intValue = (intValue == 0) ? intValue + 1 : intValue;
|
||||
opt.setMaxWriteBufferNumberToMaintain(intValue);
|
||||
assertThat(opt.maxWriteBufferNumberToMaintain()).
|
||||
isEqualTo(intValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compactionPriorities() {
|
||||
try (final ColumnFamilyOptions opt = new ColumnFamilyOptions()) {
|
||||
|
||||
@@ -656,15 +656,6 @@ public class DBOptionsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failIfOptionsFileError() {
|
||||
try (final DBOptions opt = new DBOptions()) {
|
||||
final boolean boolValue = rand.nextBoolean();
|
||||
opt.setFailIfOptionsFileError(boolValue);
|
||||
assertThat(opt.failIfOptionsFileError()).isEqualTo(boolValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dumpMallocStats() {
|
||||
try (final DBOptions opt = new DBOptions()) {
|
||||
|
||||
@@ -96,30 +96,54 @@ public class MutableColumnFamilyOptionsTest {
|
||||
public void mutableColumnFamilyOptions_parse_getOptions_output() {
|
||||
final String optionsString =
|
||||
"bottommost_compression=kDisableCompressionOption; sample_for_compression=0; "
|
||||
+ "blob_garbage_collection_age_cutoff=0.250000; blob_garbage_collection_force_threshold=0.800000;"
|
||||
+ "arena_block_size=1048576; enable_blob_garbage_collection=false; level0_stop_writes_trigger=36; min_blob_size=65536;"
|
||||
+ "blob_compaction_readahead_size=262144; blob_file_starting_level=5; prepopulate_blob_cache=kDisable;"
|
||||
+ "compaction_options_universal={allow_trivial_move=false;stop_style=kCompactionStopStyleTotalSize;min_merge_width=2;"
|
||||
+ "compression_size_percent=-1;max_size_amplification_percent=200;max_merge_width=4294967295;size_ratio=1;}; "
|
||||
+ "target_file_size_base=67108864; max_bytes_for_level_base=268435456; memtable_whole_key_filtering=false; "
|
||||
+ "soft_pending_compaction_bytes_limit=68719476736; blob_compression_type=kNoCompression; max_write_buffer_number=2; "
|
||||
+ "ttl=2592000; compaction_options_fifo={allow_compaction=false;age_for_warm=0;max_table_files_size=1073741824;}; "
|
||||
+ "check_flush_compaction_key_order=true; max_successive_merges=0; inplace_update_num_locks=10000; "
|
||||
+ "bottommost_compression_opts={enabled=false;parallel_threads=1;zstd_max_train_bytes=0;max_dict_bytes=0;"
|
||||
+ "blob_garbage_collection_age_cutoff=0.250000; "
|
||||
+ "blob_garbage_collection_force_threshold=0.800000;"
|
||||
+ "arena_block_size=1048576; enable_blob_garbage_collection=false; "
|
||||
+ "level0_stop_writes_trigger=36; min_blob_size=65536;"
|
||||
+ "blob_compaction_readahead_size=262144; blob_file_starting_level=5; "
|
||||
+ "prepopulate_blob_cache=kDisable;"
|
||||
+ "compaction_options_universal={allow_trivial_move=false;stop_style="
|
||||
+ "kCompactionStopStyleTotalSize;min_merge_width=2;"
|
||||
+ "compression_size_percent=-1;max_size_amplification_percent=200;max_merge_width="
|
||||
+ "4294967295;size_ratio=1;}; "
|
||||
+ "target_file_size_base=67108864; max_bytes_for_level_base=268435456; "
|
||||
+ "memtable_whole_key_filtering=false; "
|
||||
+ "soft_pending_compaction_bytes_limit=68719476736; blob_compression_type=kNoCompression; "
|
||||
+ " max_write_buffer_number=2; "
|
||||
+ "ttl=2592000; "
|
||||
+ "compaction_options_fifo={allow_compaction=false;age_for_warm=0;max_table_files_size="
|
||||
+ "1073741824;}; "
|
||||
+ "check_flush_compaction_key_order=true; max_successive_merges=0; "
|
||||
+ "inplace_update_num_locks=10000; "
|
||||
+ "bottommost_compression_opts={enabled=false;parallel_threads=1;zstd_max_train_bytes=0;"
|
||||
+ "max_dict_bytes=0;"
|
||||
+ "strategy=0;max_dict_buffer_bytes=0;level=32767;window_bits=-14;}; "
|
||||
+ "target_file_size_multiplier=1; max_bytes_for_level_multiplier_additional=5:{7}:{9}:{11}:{13}:{15}:{17}; "
|
||||
+ "enable_blob_files=true; level0_slowdown_writes_trigger=20; compression=kLZ4HCCompression; level0_file_num_compaction_trigger=4; "
|
||||
+ "blob_file_size=268435456; prefix_extractor=nullptr; max_bytes_for_level_multiplier=10.000000; write_buffer_size=67108864; "
|
||||
+ "disable_auto_compactions=false; max_compaction_bytes=1677721600; memtable_huge_page_size=0; "
|
||||
+ "compression_opts={enabled=false;parallel_threads=1;zstd_max_train_bytes=0;max_dict_bytes=0;strategy=0;max_dict_buffer_bytes=0;"
|
||||
+ "target_file_size_multiplier=1; "
|
||||
+ "max_bytes_for_level_multiplier_additional=5:{7}:{9}:{11}:{13}:{15}:{17}; "
|
||||
+ "enable_blob_files=true; level0_slowdown_writes_trigger=20; "
|
||||
+ "compression=kLZ4HCCompression; level0_file_num_compaction_trigger=4; "
|
||||
+ "blob_file_size=268435456; prefix_extractor=nullptr; "
|
||||
+ "max_bytes_for_level_multiplier=10.000000; write_buffer_size=67108864; "
|
||||
+ "disable_auto_compactions=false; max_compaction_bytes=1677721600; "
|
||||
+ "memtable_huge_page_size=0; "
|
||||
+ "compression_opts={enabled=false;parallel_threads=1;zstd_max_train_bytes=0;max_dict_"
|
||||
+ "bytes=0;strategy=0;max_dict_buffer_bytes=0;"
|
||||
+ "level=32767;window_bits=-14;}; "
|
||||
+ "hard_pending_compaction_bytes_limit=274877906944; periodic_compaction_seconds=0; paranoid_file_checks=true; "
|
||||
+ "memtable_prefix_bloom_size_ratio=7.500000; max_sequential_skip_in_iterations=8; report_bg_io_stats=true; "
|
||||
+ "compaction_pri=kMinOverlappingRatio; compaction_style=kCompactionStyleLevel; memtable_factory=SkipListFactory; "
|
||||
+ "comparator=leveldb.BytewiseComparator; bloom_locality=0; compaction_filter_factory=nullptr; "
|
||||
+ "min_write_buffer_number_to_merge=1; max_write_buffer_number_to_maintain=0; compaction_filter=nullptr; merge_operator=nullptr; "
|
||||
+ "num_levels=7; optimize_filters_for_hits=false; force_consistency_checks=true; table_factory=BlockBasedTable; "
|
||||
+ "max_write_buffer_size_to_maintain=0; memtable_insert_with_hint_prefix_extractor=nullptr; level_compaction_dynamic_level_bytes=false; "
|
||||
+ "hard_pending_compaction_bytes_limit=274877906944; periodic_compaction_seconds=0; "
|
||||
+ "paranoid_file_checks=true; "
|
||||
+ "memtable_prefix_bloom_size_ratio=7.500000; max_sequential_skip_in_iterations=8; "
|
||||
+ "report_bg_io_stats=true; "
|
||||
+ "compaction_pri=kMinOverlappingRatio; compaction_style=kCompactionStyleLevel; "
|
||||
+ "memtable_factory=SkipListFactory; "
|
||||
+ "comparator=leveldb.BytewiseComparator; bloom_locality=0; "
|
||||
+ "compaction_filter_factory=nullptr; "
|
||||
+ "min_write_buffer_number_to_merge=1; compaction_filter=nullptr; "
|
||||
+ "merge_operator=nullptr; "
|
||||
+ "num_levels=7; optimize_filters_for_hits=false; force_consistency_checks=true; "
|
||||
+ "table_factory=BlockBasedTable; "
|
||||
+ "max_write_buffer_size_to_maintain=0; "
|
||||
+ "memtable_insert_with_hint_prefix_extractor=nullptr; "
|
||||
+ "level_compaction_dynamic_level_bytes=false; "
|
||||
+ "inplace_update_support=false; experimental_mempurge_threshold=0.003";
|
||||
|
||||
final MutableColumnFamilyOptions.MutableColumnFamilyOptionsBuilder cf =
|
||||
|
||||
@@ -902,15 +902,6 @@ public class OptionsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failIfOptionsFileError() {
|
||||
try (final Options opt = new Options()) {
|
||||
final boolean boolValue = rand.nextBoolean();
|
||||
opt.setFailIfOptionsFileError(boolValue);
|
||||
assertThat(opt.failIfOptionsFileError()).isEqualTo(boolValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dumpMallocStats() {
|
||||
try (final Options opt = new Options()) {
|
||||
@@ -1201,19 +1192,6 @@ public class OptionsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maxWriteBufferNumberToMaintain() {
|
||||
try (final Options options = new Options()) {
|
||||
int intValue = rand.nextInt();
|
||||
// Size has to be positive
|
||||
intValue = (intValue < 0) ? -intValue : intValue;
|
||||
intValue = (intValue == 0) ? intValue + 1 : intValue;
|
||||
options.setMaxWriteBufferNumberToMaintain(intValue);
|
||||
assertThat(options.maxWriteBufferNumberToMaintain()).
|
||||
isEqualTo(intValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compactionPriorities() {
|
||||
try (final Options options = new Options()) {
|
||||
|
||||
@@ -1563,16 +1563,6 @@ public class RocksDBTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maxMemCompactionLevel() throws RocksDBException {
|
||||
try (final Options options = new Options().setCreateIfMissing(true)) {
|
||||
final String dbPath = dbFolder.getRoot().getAbsolutePath();
|
||||
try (final RocksDB db = RocksDB.open(options, dbPath)) {
|
||||
assertThat(db.maxMemCompactionLevel()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void level0StopWriteTrigger() throws RocksDBException {
|
||||
try (final Options options = new Options().setCreateIfMissing(true)) {
|
||||
|
||||
@@ -235,7 +235,7 @@ class WBWIMemTable final : public ReadOnlyMemTable {
|
||||
uint64_t num_entries_;
|
||||
// WBWI can contains updates to multiple CFs. `cf_id_` determines which CF
|
||||
// this memtable is for.
|
||||
uint32_t cf_id_;
|
||||
const uint32_t cf_id_;
|
||||
};
|
||||
|
||||
class WBWIMemTableIterator final : public InternalIterator {
|
||||
|
||||
@@ -336,6 +336,7 @@ const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
|
||||
"rocksdb.error.handler.autoresume.retry.count"},
|
||||
{ASYNC_READ_BYTES, "rocksdb.async.read.bytes"},
|
||||
{POLL_WAIT_MICROS, "rocksdb.poll.wait.micros"},
|
||||
{COMPACTION_PREFETCH_BYTES, "rocksdb.compaction.prefetch.bytes"},
|
||||
{PREFETCHED_BYTES_DISCARDED, "rocksdb.prefetched.bytes.discarded"},
|
||||
{ASYNC_PREFETCH_ABORT_MICROS, "rocksdb.async.prefetch.abort.micros"},
|
||||
{TABLE_OPEN_PREFETCH_TAIL_READ_BYTES,
|
||||
|
||||
@@ -616,7 +616,7 @@ TEST_F(StatsHistoryTest, ForceManualFlushStatsCF) {
|
||||
// LogNumbers: default: 16, stats: 10, pikachu: 5
|
||||
// Since in recovery process, cfd_stats column is created after WAL is
|
||||
// created, synced and MANIFEST is persisted, its log number which depends on
|
||||
// logfile_number_ will be different. Since "pikachu" is never flushed, thus
|
||||
// cur_wal_number_ will be different. Since "pikachu" is never flushed, thus
|
||||
// its log_number should be the smallest of the three.
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_LT(cfd_test->GetLogNumber(), cfd_stats->GetLogNumber());
|
||||
|
||||
@@ -694,7 +694,10 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct MutableCFOptions, memtable_max_range_deletions),
|
||||
OptionType::kUInt32T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
|
||||
{"memtable_op_scan_flush_trigger",
|
||||
{offsetof(struct MutableCFOptions, memtable_op_scan_flush_trigger),
|
||||
OptionType::kUInt32T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable}},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
@@ -749,9 +752,7 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{0, OptionType::kInt, OptionVerificationType::kDeprecated,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"max_write_buffer_number_to_maintain",
|
||||
{offsetof(struct ImmutableCFOptions,
|
||||
max_write_buffer_number_to_maintain),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
{0, OptionType::kInt, OptionVerificationType::kDeprecated,
|
||||
OptionTypeFlags::kNone, nullptr}},
|
||||
{"max_write_buffer_size_to_maintain",
|
||||
{offsetof(struct ImmutableCFOptions,
|
||||
@@ -987,8 +988,6 @@ ImmutableCFOptions::ImmutableCFOptions(const ColumnFamilyOptions& cf_options)
|
||||
compaction_filter_factory(cf_options.compaction_filter_factory),
|
||||
min_write_buffer_number_to_merge(
|
||||
cf_options.min_write_buffer_number_to_merge),
|
||||
max_write_buffer_number_to_maintain(
|
||||
cf_options.max_write_buffer_number_to_maintain),
|
||||
max_write_buffer_size_to_maintain(
|
||||
cf_options.max_write_buffer_size_to_maintain),
|
||||
inplace_update_support(cf_options.inplace_update_support),
|
||||
@@ -1180,6 +1179,8 @@ void MutableCFOptions::Dump(Logger* log) const {
|
||||
bottommost_file_compaction_delay);
|
||||
ROCKS_LOG_INFO(log, " uncache_aggressiveness: %" PRIu32,
|
||||
uncache_aggressiveness);
|
||||
ROCKS_LOG_INFO(log, " memtable_op_scan_flush_trigger: %" PRIu32,
|
||||
memtable_op_scan_flush_trigger);
|
||||
|
||||
// Universal Compaction Options
|
||||
ROCKS_LOG_INFO(log, "compaction_options_universal.size_ratio : %d",
|
||||
|
||||
@@ -40,8 +40,6 @@ struct ImmutableCFOptions {
|
||||
|
||||
int min_write_buffer_number_to_merge;
|
||||
|
||||
int max_write_buffer_number_to_maintain;
|
||||
|
||||
int64_t max_write_buffer_size_to_maintain;
|
||||
|
||||
bool inplace_update_support;
|
||||
@@ -175,7 +173,8 @@ struct MutableCFOptions {
|
||||
memtable_max_range_deletions(options.memtable_max_range_deletions),
|
||||
bottommost_file_compaction_delay(
|
||||
options.bottommost_file_compaction_delay),
|
||||
uncache_aggressiveness(options.uncache_aggressiveness) {
|
||||
uncache_aggressiveness(options.uncache_aggressiveness),
|
||||
memtable_op_scan_flush_trigger(options.memtable_op_scan_flush_trigger) {
|
||||
RefreshDerivedOptions(options.num_levels, options.compaction_style);
|
||||
}
|
||||
|
||||
@@ -230,7 +229,8 @@ struct MutableCFOptions {
|
||||
sample_for_compression(0),
|
||||
memtable_max_range_deletions(0),
|
||||
bottommost_file_compaction_delay(0),
|
||||
uncache_aggressiveness(0) {}
|
||||
uncache_aggressiveness(0),
|
||||
memtable_op_scan_flush_trigger(0) {}
|
||||
|
||||
explicit MutableCFOptions(const Options& options);
|
||||
|
||||
@@ -338,6 +338,7 @@ struct MutableCFOptions {
|
||||
uint32_t memtable_max_range_deletions;
|
||||
uint32_t bottommost_file_compaction_delay;
|
||||
uint32_t uncache_aggressiveness;
|
||||
uint32_t memtable_op_scan_flush_trigger;
|
||||
|
||||
// Derived options
|
||||
// Per-level target file size.
|
||||
|
||||
@@ -319,8 +319,7 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"fail_if_options_file_error",
|
||||
{offsetof(struct ImmutableDBOptions, fail_if_options_file_error),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
{0, OptionType::kBoolean, OptionVerificationType::kDeprecated,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"enable_pipelined_write",
|
||||
{offsetof(struct ImmutableDBOptions, enable_pipelined_write),
|
||||
@@ -772,7 +771,6 @@ ImmutableDBOptions::ImmutableDBOptions(const DBOptions& options)
|
||||
allow_2pc(options.allow_2pc),
|
||||
row_cache(options.row_cache),
|
||||
wal_filter(options.wal_filter),
|
||||
fail_if_options_file_error(options.fail_if_options_file_error),
|
||||
dump_malloc_stats(options.dump_malloc_stats),
|
||||
avoid_flush_during_recovery(options.avoid_flush_during_recovery),
|
||||
allow_ingest_behind(options.allow_ingest_behind),
|
||||
|
||||
@@ -77,7 +77,6 @@ struct ImmutableDBOptions {
|
||||
bool allow_2pc;
|
||||
std::shared_ptr<Cache> row_cache;
|
||||
WalFilter* wal_filter;
|
||||
bool fail_if_options_file_error;
|
||||
bool dump_malloc_stats;
|
||||
bool avoid_flush_during_recovery;
|
||||
bool allow_ingest_behind;
|
||||
|
||||
+5
-5
@@ -43,8 +43,6 @@ AdvancedColumnFamilyOptions::AdvancedColumnFamilyOptions(const Options& options)
|
||||
: max_write_buffer_number(options.max_write_buffer_number),
|
||||
min_write_buffer_number_to_merge(
|
||||
options.min_write_buffer_number_to_merge),
|
||||
max_write_buffer_number_to_maintain(
|
||||
options.max_write_buffer_number_to_maintain),
|
||||
max_write_buffer_size_to_maintain(
|
||||
options.max_write_buffer_size_to_maintain),
|
||||
inplace_update_support(options.inplace_update_support),
|
||||
@@ -113,7 +111,8 @@ AdvancedColumnFamilyOptions::AdvancedColumnFamilyOptions(const Options& options)
|
||||
blob_file_starting_level(options.blob_file_starting_level),
|
||||
blob_cache(options.blob_cache),
|
||||
prepopulate_blob_cache(options.prepopulate_blob_cache),
|
||||
persist_user_defined_timestamps(options.persist_user_defined_timestamps) {
|
||||
persist_user_defined_timestamps(options.persist_user_defined_timestamps),
|
||||
memtable_op_scan_flush_trigger(options.memtable_op_scan_flush_trigger) {
|
||||
assert(memtable_factory.get() != nullptr);
|
||||
if (max_bytes_for_level_multiplier_additional.size() <
|
||||
static_cast<unsigned int>(num_levels)) {
|
||||
@@ -192,8 +191,6 @@ void ColumnFamilyOptions::Dump(Logger* log) const {
|
||||
ROCKS_LOG_HEADER(log, " Options.num_levels: %d", num_levels);
|
||||
ROCKS_LOG_HEADER(log, " Options.min_write_buffer_number_to_merge: %d",
|
||||
min_write_buffer_number_to_merge);
|
||||
ROCKS_LOG_HEADER(log, " Options.max_write_buffer_number_to_maintain: %d",
|
||||
max_write_buffer_number_to_maintain);
|
||||
ROCKS_LOG_HEADER(log,
|
||||
" Options.max_write_buffer_size_to_maintain: %" PRIu64,
|
||||
max_write_buffer_size_to_maintain);
|
||||
@@ -287,6 +284,9 @@ void ColumnFamilyOptions::Dump(Logger* log) const {
|
||||
ROCKS_LOG_HEADER(log,
|
||||
" Options.max_sequential_skip_in_iterations: %" PRIu64,
|
||||
max_sequential_skip_in_iterations);
|
||||
ROCKS_LOG_HEADER(
|
||||
log, " Options.memtable_op_scan_flush_trigger: %" PRIu32,
|
||||
memtable_op_scan_flush_trigger);
|
||||
ROCKS_LOG_HEADER(log,
|
||||
" Options.max_compaction_bytes: %" PRIu64,
|
||||
max_compaction_bytes);
|
||||
|
||||
@@ -153,8 +153,6 @@ void BuildDBOptions(const ImmutableDBOptions& immutable_db_options,
|
||||
options.allow_2pc = immutable_db_options.allow_2pc;
|
||||
options.row_cache = immutable_db_options.row_cache;
|
||||
options.wal_filter = immutable_db_options.wal_filter;
|
||||
options.fail_if_options_file_error =
|
||||
immutable_db_options.fail_if_options_file_error;
|
||||
options.dump_malloc_stats = immutable_db_options.dump_malloc_stats;
|
||||
options.avoid_flush_during_recovery =
|
||||
immutable_db_options.avoid_flush_during_recovery;
|
||||
@@ -301,6 +299,8 @@ void UpdateColumnFamilyOptions(const MutableCFOptions& moptions,
|
||||
cf_opts->default_write_temperature = moptions.default_write_temperature;
|
||||
cf_opts->memtable_max_range_deletions = moptions.memtable_max_range_deletions;
|
||||
cf_opts->uncache_aggressiveness = moptions.uncache_aggressiveness;
|
||||
cf_opts->memtable_op_scan_flush_trigger =
|
||||
moptions.memtable_op_scan_flush_trigger;
|
||||
}
|
||||
|
||||
void UpdateColumnFamilyOptions(const ImmutableCFOptions& ioptions,
|
||||
@@ -313,8 +313,6 @@ void UpdateColumnFamilyOptions(const ImmutableCFOptions& ioptions,
|
||||
cf_opts->compaction_filter_factory = ioptions.compaction_filter_factory;
|
||||
cf_opts->min_write_buffer_number_to_merge =
|
||||
ioptions.min_write_buffer_number_to_merge;
|
||||
cf_opts->max_write_buffer_number_to_maintain =
|
||||
ioptions.max_write_buffer_number_to_maintain;
|
||||
cf_opts->max_write_buffer_size_to_maintain =
|
||||
ioptions.max_write_buffer_size_to_maintain;
|
||||
cf_opts->inplace_update_support = ioptions.inplace_update_support;
|
||||
|
||||
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user