mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
78
Commits
rocksdb_fork
...
10.3.fb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8b5e4a6b7 | ||
|
|
a59ca8d5da | ||
|
|
740240e3b1 | ||
|
|
633631ac79 | ||
|
|
e7d28965ce | ||
|
|
c1b29dfe70 | ||
|
|
a116092ee6 | ||
|
|
f90d89ea52 | ||
|
|
cf149842a2 | ||
|
|
f2d64341f5 | ||
|
|
9c7f4946bb | ||
|
|
9885ced363 | ||
|
|
cda1e40498 | ||
|
|
0c3f08008a | ||
|
|
77af042413 | ||
|
|
acab405fc1 | ||
|
|
9a9a403a89 | ||
|
|
83026c7db2 | ||
|
|
06d4f569a8 | ||
|
|
b42bf48310 | ||
|
|
024194420c | ||
|
|
7c9b580681 | ||
|
|
fc2cf7ead2 | ||
|
|
df7a3a7168 | ||
|
|
2a0886b9a7 | ||
|
|
9c4b94b9e7 | ||
|
|
35e1c6c402 | ||
|
|
8cb2bfa233 | ||
|
|
0e3e349369 | ||
|
|
0102b1769b | ||
|
|
ef67339175 | ||
|
|
36600d8fa0 | ||
|
|
13d865f6f1 | ||
|
|
947a63400f | ||
|
|
f49d76b7ad | ||
|
|
1428e950bd | ||
|
|
e3b7dd7b56 | ||
|
|
9d1a071194 | ||
|
|
72c3887167 | ||
|
|
b2815b6b46 | ||
|
|
6c0e55a2a9 | ||
|
|
0560544e86 | ||
|
|
613e1a9a38 | ||
|
|
bcda3bda04 | ||
|
|
9998478c64 | ||
|
|
c368c6afe8 | ||
|
|
1614345a52 | ||
|
|
c237022831 | ||
|
|
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 }
|
||||
@@ -38,7 +38,7 @@ runs:
|
||||
$env:Path = $env:JAVA_HOME + ";" + $env:Path
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DJNI=1 ..
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DXPRESS=1 -DJNI=1 ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
|
||||
+57
@@ -1,6 +1,63 @@
|
||||
# Rocksdb Change Log
|
||||
> NOTE: Entries for next release do not go here. Follow instructions in `unreleased_history/README.txt`
|
||||
|
||||
## 10.3.4 (06/17/2025)
|
||||
### Public API Changes
|
||||
* Change NewExternalTableFactory to return a unique_ptr instead of shared_ptr.
|
||||
|
||||
## 10.3.3 (06/04/2025)
|
||||
### Bug Fixes
|
||||
* Fix iterator operations returning NotImplemented status if disallow_memtable_writes and paranoid_memory_checks CF options are both set.
|
||||
* Fix Windows-only XPRESS compression
|
||||
* Fix newer gtest compatibility
|
||||
|
||||
## 10.3.2 (05/30/2025)
|
||||
### Behavior Changes
|
||||
* In Remote Compactions, instead of using FileSystem::GetFileSize() for each CompactionOutputFile, use the file size that is being tracked internally as part of the output file's metadata. FileSize is now part of CompactionServiceOutputFile and serialized in the CompactionServiceResult
|
||||
|
||||
## 10.3.1 (05/22/2025)
|
||||
### New Features
|
||||
* Add new experimental `TransactionOptions::large_txn_commit_optimize_byte_threshold` to enable optimizations for large transaction commit by transaction batch data size.
|
||||
|
||||
### Behavior Changes
|
||||
* `TransactionOptions::large_txn_commit_optimize_threshold` now has default value 0 for disabled. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` now has no effect on transactions.
|
||||
|
||||
## 10.3.0 (05/17/2025)
|
||||
### New Features
|
||||
* Add new experimental `CompactionOptionsFIFO::allow_trivial_copy_when_change_temperature` along with `CompactionOptionsFIFO::trivial_copy_buffer_size` to allow optimizing FIFO compactions with tiering when kChangeTemperature to move files from source tier FileSystem to another tier FileSystem via trivial and direct copying raw sst file instead of reading thru the content of the SST file then rebuilding the table files.
|
||||
* Add a new field to Compaction Stats in LOG files for the pre-compression size written to each level.
|
||||
* Add new experimental `TransactionOptions::large_txn_commit_optimize_threshold` to enable optimizations for large transaction commit with per transaction threshold. `TransactionDBOptions::txn_commit_bypass_memtable_threshold` is deprecated in favor of this transaction option.
|
||||
* [internal team use only] Allow an application-defined `request_id` to be passed to RocksDB and propagated to the filesystem via IODebugContext
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug where transaction lock upgrade can incorrectly fail with a Deadlock status. This happens when a transaction has a non-zero timeout and tries to upgrade a shared lock that is also held by another transaction.
|
||||
* Pass wrapped WritableFileWriter pointer to ExternalTableBuilder so that the file checksum can be correctly calculated and returned by SstFileWriter for external table files.
|
||||
* Fix an infinite-loop bug in transaction locking. This can happen if a transaction reaches lock limit and its time out expires before it attempts to wait for it.
|
||||
* Fixed a potential data race with `CompressionOptions::parallel_threads > 1` and a `TablePropertiesCollector` overriding `BlockAdd()`.
|
||||
|
||||
## 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`.
|
||||
|
||||
@@ -2489,7 +2489,7 @@ checkout_folly:
|
||||
fi
|
||||
@# Pin to a particular version for public CI, so that PR authors don't
|
||||
@# need to worry about folly breaking our integration. Update periodically
|
||||
cd third-party/folly && git reset --hard 78286282478e1ae05b2e8cbcf0e2139eab283bea
|
||||
cd third-party/folly && git reset --hard 8e8186f67de7a23d3a07366946b1617343927d84
|
||||
@# NOTE: this hack is required for clang in some cases
|
||||
perl -pi -e 's/int rv = syscall/int rv = (int)syscall/' third-party/folly/folly/detail/Futex.cpp
|
||||
@# NOTE: this hack is required for gcc in some cases
|
||||
|
||||
@@ -118,6 +118,9 @@ fi
|
||||
# fi
|
||||
set -e
|
||||
|
||||
# Exclude third-party from formatting
|
||||
EXCLUDE=':!third-party/'
|
||||
|
||||
uncommitted_code=`git diff HEAD`
|
||||
|
||||
# If there's no uncommitted changes, we assume user are doing post-commit
|
||||
@@ -137,11 +140,11 @@ then
|
||||
# should be relevant for formatting fixes.
|
||||
FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
|
||||
# Get the differences
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" -- $EXCLUDE | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
diffs=$(git diff -U0 HEAD -- $EXCLUDE | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
echo "Checking format of uncommitted changes..."
|
||||
fi
|
||||
|
||||
@@ -187,9 +190,9 @@ fi
|
||||
# Do in-place format adjustment.
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" -- $EXCLUDE | $CLANG_FORMAT_DIFF -i -p 1
|
||||
else
|
||||
git diff -U0 HEAD | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 HEAD -- $EXCLUDE | $CLANG_FORMAT_DIFF -i -p 1
|
||||
fi
|
||||
echo "Files reformatted!"
|
||||
|
||||
|
||||
Vendored
+35
-32
@@ -24,7 +24,14 @@ CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
cache_res_mgr_(std::make_shared<ConcurrentCacheReservationManager>(
|
||||
std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
|
||||
cache_))),
|
||||
disable_cache_(opts.capacity == 0) {}
|
||||
disable_cache_(opts.capacity == 0) {
|
||||
auto mgr =
|
||||
GetBuiltinCompressionManager(cache_options_.compress_format_version);
|
||||
compressor_ = mgr->GetCompressor(cache_options_.compression_opts,
|
||||
cache_options_.compression_type);
|
||||
decompressor_ =
|
||||
mgr->GetDecompressorOptimizeFor(cache_options_.compression_type);
|
||||
}
|
||||
|
||||
CompressedSecondaryCache::~CompressedSecondaryCache() = default;
|
||||
|
||||
@@ -97,25 +104,24 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
kNoCompression, CacheTier::kVolatileTier,
|
||||
create_context, allocator, &value, &charge);
|
||||
} else {
|
||||
UncompressionContext uncompression_context(
|
||||
cache_options_.compression_type);
|
||||
UncompressionInfo uncompression_info(uncompression_context,
|
||||
UncompressionDict::GetEmptyDict(),
|
||||
cache_options_.compression_type);
|
||||
|
||||
size_t uncompressed_size{0};
|
||||
CacheAllocationPtr uncompressed =
|
||||
UncompressData(uncompression_info, (char*)data_ptr,
|
||||
handle_value_charge, &uncompressed_size,
|
||||
cache_options_.compress_format_version, allocator);
|
||||
|
||||
if (!uncompressed) {
|
||||
cache_->Release(lru_handle, /*erase_if_last_ref=*/true);
|
||||
return nullptr;
|
||||
// TODO: can we work some magic with create_cb, which might be based on
|
||||
// custom compression, to decompress without an extra copy in create_cb?
|
||||
Decompressor::Args args;
|
||||
args.compressed_data = Slice(data_ptr, handle_value_charge);
|
||||
args.compression_type = cache_options_.compression_type;
|
||||
s = decompressor_->ExtractUncompressedSize(args);
|
||||
assert(s.ok());
|
||||
if (s.ok()) {
|
||||
auto uncompressed = std::make_unique<char[]>(args.uncompressed_size);
|
||||
s = decompressor_->DecompressBlock(args, uncompressed.get());
|
||||
assert(s.ok());
|
||||
if (s.ok()) {
|
||||
s = helper->create_cb(
|
||||
Slice(uncompressed.get(), args.uncompressed_size), kNoCompression,
|
||||
CacheTier::kVolatileTier, create_context, allocator, &value,
|
||||
&charge);
|
||||
}
|
||||
}
|
||||
s = helper->create_cb(Slice(uncompressed.get(), uncompressed_size),
|
||||
kNoCompression, CacheTier::kVolatileTier,
|
||||
create_context, allocator, &value, &charge);
|
||||
}
|
||||
} else {
|
||||
// The item was not compressed by us. Let the helper create_cb
|
||||
@@ -198,20 +204,17 @@ Status CompressedSecondaryCache::InsertInternal(
|
||||
type == kNoCompression &&
|
||||
!cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_uncompressed_bytes, data_size);
|
||||
CompressionContext compression_context(cache_options_.compression_type,
|
||||
cache_options_.compression_opts);
|
||||
uint64_t sample_for_compression{0};
|
||||
CompressionInfo compression_info(
|
||||
cache_options_.compression_opts, compression_context,
|
||||
CompressionDict::GetEmptyDict(), cache_options_.compression_type,
|
||||
sample_for_compression);
|
||||
|
||||
bool success =
|
||||
CompressData(val, compression_info,
|
||||
cache_options_.compress_format_version, &compressed_val);
|
||||
|
||||
if (!success) {
|
||||
return Status::Corruption("Error compressing value.");
|
||||
CompressionType to_type = kNoCompression;
|
||||
s = compressor_->CompressBlock(val, &compressed_val, &to_type,
|
||||
nullptr /*working_area*/);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// TODO: allow values not compressed when there's no size savings?
|
||||
assert(to_type == cache_options_.compression_type);
|
||||
if (to_type != cache_options_.compression_type) {
|
||||
return Status::Corruption("Failed to compress value.");
|
||||
}
|
||||
|
||||
val = Slice(compressed_val);
|
||||
|
||||
Vendored
+2
@@ -145,6 +145,8 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
const Cache::CacheItemHelper* GetHelper(bool enable_custom_split_merge) const;
|
||||
std::shared_ptr<Cache> cache_;
|
||||
CompressedSecondaryCacheOptions cache_options_;
|
||||
std::unique_ptr<Compressor> compressor_;
|
||||
std::shared_ptr<Decompressor> decompressor_;
|
||||
mutable port::Mutex capacity_mutex_;
|
||||
std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_;
|
||||
bool disable_cache_;
|
||||
|
||||
Vendored
+11
-1
@@ -121,7 +121,17 @@ CacheWithSecondaryAdapter::~CacheWithSecondaryAdapter() {
|
||||
assert(s.ok());
|
||||
assert(placeholder_usage_ == 0);
|
||||
assert(reserved_usage_ == 0);
|
||||
assert(pri_cache_res_->GetTotalMemoryUsed() == sec_capacity);
|
||||
bool pri_cache_res_mismatch =
|
||||
pri_cache_res_->GetTotalMemoryUsed() != sec_capacity;
|
||||
if (pri_cache_res_mismatch) {
|
||||
fprintf(stderr,
|
||||
"~CacheWithSecondaryAdapter: Primary cache reservation: "
|
||||
"%zu, Secondary cache capacity: %zu, "
|
||||
"Secondary cache reserved: %zu\n",
|
||||
pri_cache_res_->GetTotalMemoryUsed(), sec_capacity,
|
||||
sec_reserved_);
|
||||
assert(pri_cache_res_mismatch);
|
||||
}
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
+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, active_mem, cfh, expose_blob_index, &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
|
||||
@@ -267,10 +267,9 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
// TODO: allow user CompressionOptions, including max_compressed_bytes_per_kb
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(blob_compression_type_, opts);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
blob_compression_type_, sample_for_compression);
|
||||
blob_compression_type_);
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
@@ -279,8 +278,8 @@ Status BlobFileBuilder::CompressBlobIfNeeded(
|
||||
{
|
||||
StopWatch stop_watch(immutable_options_->clock, immutable_options_->stats,
|
||||
BLOB_DB_COMPRESSION_MICROS);
|
||||
success =
|
||||
CompressData(*blob, info, compression_format_version, compressed_blob);
|
||||
success = OLD_CompressData(*blob, info, compression_format_version,
|
||||
compressed_blob);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
|
||||
@@ -405,10 +405,9 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(kSnappyCompression, opts);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
kSnappyCompression, sample_for_compression);
|
||||
kSnappyCompression);
|
||||
|
||||
std::string compressed_value;
|
||||
ASSERT_TRUE(Snappy_Compress(info, uncompressed_value.data(),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -250,7 +250,8 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
Status s;
|
||||
|
||||
IOOptions io_options;
|
||||
s = file_reader->PrepareIOOptions(read_options, io_options);
|
||||
IODebugContext dbg;
|
||||
s = file_reader->PrepareIOOptions(read_options, io_options, &dbg);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -259,13 +260,13 @@ Status BlobFileReader::ReadFromFile(const RandomAccessFileReader* file_reader,
|
||||
constexpr char* scratch = nullptr;
|
||||
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, scratch,
|
||||
aligned_buf);
|
||||
aligned_buf, &dbg);
|
||||
} else {
|
||||
buf->reset(new char[read_size]);
|
||||
constexpr AlignedBuf* aligned_scratch = nullptr;
|
||||
|
||||
s = file_reader->Read(io_options, read_offset, read_size, slice, buf->get(),
|
||||
aligned_scratch);
|
||||
aligned_scratch, &dbg);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
@@ -334,7 +335,8 @@ Status BlobFileReader::GetBlob(
|
||||
constexpr bool for_compaction = true;
|
||||
|
||||
IOOptions io_options;
|
||||
s = file_reader_->PrepareIOOptions(read_options, io_options);
|
||||
IODebugContext dbg;
|
||||
s = file_reader_->PrepareIOOptions(read_options, io_options, &dbg);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -463,10 +465,11 @@ void BlobFileReader::MultiGetBlob(
|
||||
PERF_COUNTER_ADD(blob_read_count, num_blobs);
|
||||
PERF_COUNTER_ADD(blob_read_byte, total_len);
|
||||
IOOptions opts;
|
||||
s = file_reader_->PrepareIOOptions(read_options, opts);
|
||||
IODebugContext dbg;
|
||||
s = file_reader_->PrepareIOOptions(read_options, opts, &dbg);
|
||||
if (s.ok()) {
|
||||
s = file_reader_->MultiRead(opts, read_reqs.data(), read_reqs.size(),
|
||||
direct_io ? &aligned_buf : nullptr);
|
||||
direct_io ? &aligned_buf : nullptr, &dbg);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
for (auto& req : read_reqs) {
|
||||
@@ -602,9 +605,9 @@ Status BlobFileReader::UncompressBlobIfNeeded(
|
||||
{
|
||||
PERF_TIMER_GUARD(blob_decompress_time);
|
||||
StopWatch stop_watch(clock, statistics, BLOB_DB_DECOMPRESSION_MICROS);
|
||||
output = UncompressData(info, value_slice.data(), value_slice.size(),
|
||||
&uncompressed_size, compression_format_version,
|
||||
allocator);
|
||||
output = OLD_UncompressData(info, value_slice.data(), value_slice.size(),
|
||||
&uncompressed_size, compression_format_version,
|
||||
allocator);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
|
||||
@@ -75,15 +75,14 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
} else {
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(compression, opts);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
compression, sample_for_compression);
|
||||
compression);
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
ASSERT_TRUE(CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
ASSERT_TRUE(OLD_CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
blobs_to_write[i] = compressed_blobs[i];
|
||||
blob_sizes[i] = compressed_blobs[i].size();
|
||||
}
|
||||
|
||||
@@ -77,15 +77,14 @@ void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
} else {
|
||||
CompressionOptions opts;
|
||||
CompressionContext context(compression, opts);
|
||||
constexpr uint64_t sample_for_compression = 0;
|
||||
CompressionInfo info(opts, context, CompressionDict::GetEmptyDict(),
|
||||
compression, sample_for_compression);
|
||||
compression);
|
||||
|
||||
constexpr uint32_t compression_format_version = 2;
|
||||
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
ASSERT_TRUE(CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
ASSERT_TRUE(OLD_CompressData(blobs[i], info, compression_format_version,
|
||||
&compressed_blobs[i]));
|
||||
blobs_to_write[i] = compressed_blobs[i];
|
||||
blob_sizes[i] = compressed_blobs[i].size();
|
||||
}
|
||||
|
||||
+14
-4
@@ -74,8 +74,8 @@ Status BuildTable(
|
||||
EventLogger* event_logger, int job_id, TableProperties* table_properties,
|
||||
Env::WriteLifeTimeHint write_hint, const std::string* full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback, Version* version,
|
||||
uint64_t* num_input_entries, uint64_t* memtable_payload_bytes,
|
||||
uint64_t* memtable_garbage_bytes) {
|
||||
uint64_t* memtable_payload_bytes, uint64_t* memtable_garbage_bytes,
|
||||
InternalStats::CompactionStats* flush_stats) {
|
||||
assert((tboptions.column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
tboptions.column_family_name.empty());
|
||||
@@ -253,6 +253,10 @@ Status BuildTable(
|
||||
}
|
||||
builder->Add(key_after_flush, value_after_flush);
|
||||
|
||||
if (flush_stats) {
|
||||
flush_stats->num_output_records++;
|
||||
}
|
||||
|
||||
s = meta->UpdateBoundaries(key_after_flush, value_after_flush,
|
||||
ikey.sequence, ikey.type);
|
||||
if (!s.ok()) {
|
||||
@@ -284,6 +288,9 @@ Status BuildTable(
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
std::pair<InternalKey, Slice> kv = tombstone.Serialize();
|
||||
builder->Add(kv.first.Encode(), kv.second);
|
||||
if (flush_stats) {
|
||||
flush_stats->num_output_records++;
|
||||
}
|
||||
InternalKey tombstone_end = tombstone.SerializeEndKey();
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone_end, tombstone.seq_,
|
||||
tboptions.internal_comparator);
|
||||
@@ -305,9 +312,9 @@ Status BuildTable(
|
||||
|
||||
TEST_SYNC_POINT("BuildTable:BeforeFinishBuildTable");
|
||||
const bool empty = builder->IsEmpty();
|
||||
if (num_input_entries != nullptr) {
|
||||
if (flush_stats) {
|
||||
assert(c_iter.HasNumInputEntryScanned());
|
||||
*num_input_entries =
|
||||
flush_stats->num_input_records =
|
||||
c_iter.NumInputEntryScanned() + num_unfragmented_tombstones;
|
||||
}
|
||||
if (!s.ok() || empty) {
|
||||
@@ -334,6 +341,9 @@ Status BuildTable(
|
||||
}
|
||||
|
||||
if (s.ok() && !empty) {
|
||||
if (flush_stats) {
|
||||
flush_stats->bytes_written_pre_comp = builder->PreCompressionSize();
|
||||
}
|
||||
uint64_t file_size = builder->FileSize();
|
||||
meta->fd.file_size = file_size;
|
||||
meta->tail_size = builder->GetTailSize();
|
||||
|
||||
+5
-4
@@ -10,6 +10,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/seqno_to_time_mapping.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
@@ -34,7 +35,6 @@ class SnapshotChecker;
|
||||
class TableCache;
|
||||
class TableBuilder;
|
||||
class WritableFileWriter;
|
||||
class InternalStats;
|
||||
class BlobFileCompletionCallback;
|
||||
|
||||
// Convenience function for NewTableBuilder on the embedded table_factory.
|
||||
@@ -49,6 +49,7 @@ TableBuilder* NewTableBuilder(const TableBuilderOptions& tboptions,
|
||||
//
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown.
|
||||
// @param flush_stats treat flush as level 0 compaction in internal stats
|
||||
Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const TableBuilderOptions& tboptions,
|
||||
@@ -69,8 +70,8 @@ Status BuildTable(
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const std::string* full_history_ts_low = nullptr,
|
||||
BlobFileCompletionCallback* blob_callback = nullptr,
|
||||
Version* version = nullptr, uint64_t* num_input_entries = nullptr,
|
||||
uint64_t* memtable_payload_bytes = nullptr,
|
||||
uint64_t* memtable_garbage_bytes = nullptr);
|
||||
Version* version = nullptr, uint64_t* memtable_payload_bytes = nullptr,
|
||||
uint64_t* memtable_garbage_bytes = nullptr,
|
||||
InternalStats::CompactionStats* flush_stats = nullptr);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -24,12 +24,14 @@
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/sst_file_manager.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
@@ -49,6 +51,7 @@
|
||||
#include "util/stderr_logger.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using ROCKSDB_NAMESPACE::BackgroundErrorReason;
|
||||
using ROCKSDB_NAMESPACE::BackupEngine;
|
||||
using ROCKSDB_NAMESPACE::BackupEngineOptions;
|
||||
using ROCKSDB_NAMESPACE::BackupID;
|
||||
@@ -65,7 +68,9 @@ using ROCKSDB_NAMESPACE::ColumnFamilyMetaData;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyOptions;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilter;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilterFactory;
|
||||
using ROCKSDB_NAMESPACE::CompactionJobInfo;
|
||||
using ROCKSDB_NAMESPACE::CompactionOptionsFIFO;
|
||||
using ROCKSDB_NAMESPACE::CompactionReason;
|
||||
using ROCKSDB_NAMESPACE::CompactRangeOptions;
|
||||
using ROCKSDB_NAMESPACE::Comparator;
|
||||
using ROCKSDB_NAMESPACE::CompressionType;
|
||||
@@ -76,8 +81,11 @@ using ROCKSDB_NAMESPACE::DBOptions;
|
||||
using ROCKSDB_NAMESPACE::DbPath;
|
||||
using ROCKSDB_NAMESPACE::Env;
|
||||
using ROCKSDB_NAMESPACE::EnvOptions;
|
||||
using ROCKSDB_NAMESPACE::EventListener;
|
||||
using ROCKSDB_NAMESPACE::ExternalFileIngestionInfo;
|
||||
using ROCKSDB_NAMESPACE::FileLock;
|
||||
using ROCKSDB_NAMESPACE::FilterPolicy;
|
||||
using ROCKSDB_NAMESPACE::FlushJobInfo;
|
||||
using ROCKSDB_NAMESPACE::FlushOptions;
|
||||
using ROCKSDB_NAMESPACE::HistogramData;
|
||||
using ROCKSDB_NAMESPACE::HyperClockCacheOptions;
|
||||
@@ -90,6 +98,7 @@ using ROCKSDB_NAMESPACE::Logger;
|
||||
using ROCKSDB_NAMESPACE::LRUCacheOptions;
|
||||
using ROCKSDB_NAMESPACE::MemoryAllocator;
|
||||
using ROCKSDB_NAMESPACE::MemoryUtil;
|
||||
using ROCKSDB_NAMESPACE::MemTableInfo;
|
||||
using ROCKSDB_NAMESPACE::MergeOperator;
|
||||
using ROCKSDB_NAMESPACE::NewBloomFilterPolicy;
|
||||
using ROCKSDB_NAMESPACE::NewCompactOnDeletionCollectorFactory;
|
||||
@@ -113,10 +122,12 @@ using ROCKSDB_NAMESPACE::Slice;
|
||||
using ROCKSDB_NAMESPACE::SliceParts;
|
||||
using ROCKSDB_NAMESPACE::SliceTransform;
|
||||
using ROCKSDB_NAMESPACE::Snapshot;
|
||||
using ROCKSDB_NAMESPACE::SstFileManager;
|
||||
using ROCKSDB_NAMESPACE::SstFileMetaData;
|
||||
using ROCKSDB_NAMESPACE::SstFileWriter;
|
||||
using ROCKSDB_NAMESPACE::Status;
|
||||
using ROCKSDB_NAMESPACE::StderrLogger;
|
||||
using ROCKSDB_NAMESPACE::SubcompactionJobInfo;
|
||||
using ROCKSDB_NAMESPACE::TablePropertiesCollectorFactory;
|
||||
using ROCKSDB_NAMESPACE::Transaction;
|
||||
using ROCKSDB_NAMESPACE::TransactionDB;
|
||||
@@ -130,6 +141,8 @@ using ROCKSDB_NAMESPACE::WriteBatch;
|
||||
using ROCKSDB_NAMESPACE::WriteBatchWithIndex;
|
||||
using ROCKSDB_NAMESPACE::WriteBufferManager;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
using ROCKSDB_NAMESPACE::WriteStallCondition;
|
||||
using ROCKSDB_NAMESPACE::WriteStallInfo;
|
||||
|
||||
using std::unordered_set;
|
||||
using std::vector;
|
||||
@@ -139,6 +152,9 @@ extern "C" {
|
||||
struct rocksdb_t {
|
||||
DB* rep;
|
||||
};
|
||||
struct rocksdb_status_ptr_t {
|
||||
Status* rep;
|
||||
};
|
||||
struct rocksdb_backup_engine_t {
|
||||
BackupEngine* rep;
|
||||
};
|
||||
@@ -226,6 +242,9 @@ struct rocksdb_cache_t {
|
||||
struct rocksdb_write_buffer_manager_t {
|
||||
std::shared_ptr<WriteBufferManager> rep;
|
||||
};
|
||||
struct rocksdb_sst_file_manager_t {
|
||||
std::shared_ptr<SstFileManager> rep;
|
||||
};
|
||||
struct rocksdb_livefiles_t {
|
||||
std::vector<LiveFileMetaData> rep;
|
||||
};
|
||||
@@ -292,6 +311,28 @@ struct rocksdb_compactionfiltercontext_t {
|
||||
CompactionFilter::Context rep;
|
||||
};
|
||||
|
||||
struct rocksdb_flushjobinfo_t {
|
||||
FlushJobInfo rep;
|
||||
};
|
||||
struct rocksdb_writestallcondition_t {
|
||||
WriteStallCondition rep;
|
||||
};
|
||||
struct rocksdb_writestallinfo_t {
|
||||
WriteStallInfo rep;
|
||||
};
|
||||
struct rocksdb_memtableinfo_t {
|
||||
MemTableInfo rep;
|
||||
};
|
||||
struct rocksdb_compactionjobinfo_t {
|
||||
CompactionJobInfo rep;
|
||||
};
|
||||
struct rocksdb_subcompactionjobinfo_t {
|
||||
SubcompactionJobInfo rep;
|
||||
};
|
||||
struct rocksdb_externalfileingestioninfo_t {
|
||||
ExternalFileIngestionInfo rep;
|
||||
};
|
||||
|
||||
struct rocksdb_statistics_histogram_data_t {
|
||||
rocksdb_statistics_histogram_data_t() : rep() {}
|
||||
HistogramData rep;
|
||||
@@ -2627,6 +2668,16 @@ rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_readopts(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_iterator_t* base_iterator,
|
||||
const rocksdb_readoptions_t* options) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep =
|
||||
wbwi->rep->NewIteratorWithBase(base_iterator->rep, &options->rep);
|
||||
delete base_iterator;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_column_family_handle_t* column_family) {
|
||||
@@ -2637,6 +2688,17 @@ rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf_readopts(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const rocksdb_readoptions_t* options) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = wbwi->rep->NewIteratorWithBase(
|
||||
column_family->rep, base_iterator->rep, &options->rep);
|
||||
delete base_iterator;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_writebatch_wi_get_from_batch(rocksdb_writebatch_wi_t* wbwi,
|
||||
const rocksdb_options_t* options,
|
||||
const char* key, size_t keylen,
|
||||
@@ -2696,6 +2758,23 @@ char* rocksdb_writebatch_wi_get_from_batch_and_db(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_pinnableslice_t* rocksdb_writebatch_wi_get_pinned_from_batch_and_db(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options, const char* key, size_t keylen,
|
||||
char** errptr) {
|
||||
rocksdb_pinnableslice_t* v = new (rocksdb_pinnableslice_t);
|
||||
Status s = wbwi->rep->GetFromBatchAndDB(db->rep, options->rep,
|
||||
Slice(key, keylen), &v->rep);
|
||||
if (!s.ok()) {
|
||||
delete (v);
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
char* rocksdb_writebatch_wi_get_from_batch_and_db_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
@@ -2717,6 +2796,24 @@ char* rocksdb_writebatch_wi_get_from_batch_and_db_cf(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_pinnableslice_t* rocksdb_writebatch_wi_get_pinned_from_batch_and_db_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_t* db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key,
|
||||
size_t keylen, char** errptr) {
|
||||
rocksdb_pinnableslice_t* v = new (rocksdb_pinnableslice_t);
|
||||
Status s = wbwi->rep->GetFromBatchAndDB(
|
||||
db->rep, options->rep, column_family->rep, Slice(key, keylen), &v->rep);
|
||||
if (!s.ok()) {
|
||||
delete (v);
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void rocksdb_write_writebatch_wi(rocksdb_t* db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
rocksdb_writebatch_wi_t* wbwi, char** errptr) {
|
||||
@@ -2930,6 +3027,352 @@ void rocksdb_block_based_options_set_unpartitioned_pinning_tier(
|
||||
static_cast<ROCKSDB_NAMESPACE::PinningTier>(v);
|
||||
}
|
||||
|
||||
/* FlushJobInfo */
|
||||
|
||||
const char* rocksdb_flushjobinfo_cf_name(const rocksdb_flushjobinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_flushjobinfo_file_path(const rocksdb_flushjobinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.file_path.size();
|
||||
return info->rep.file_path.data();
|
||||
}
|
||||
|
||||
unsigned char rocksdb_flushjobinfo_triggered_writes_slowdown(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.triggered_writes_slowdown;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_flushjobinfo_triggered_writes_stop(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.triggered_writes_stop;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_largest_seqno(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.largest_seqno;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_flushjobinfo_smallest_seqno(
|
||||
const rocksdb_flushjobinfo_t* info) {
|
||||
return info->rep.smallest_seqno;
|
||||
}
|
||||
|
||||
void rocksdb_reset_status(rocksdb_status_ptr_t* status_ptr) {
|
||||
auto ptr = status_ptr->rep;
|
||||
*ptr = Status::OK();
|
||||
}
|
||||
|
||||
/* CompactionJobInfo */
|
||||
|
||||
void rocksdb_compactionjobinfo_status(const rocksdb_compactionjobinfo_t* info,
|
||||
char** errptr) {
|
||||
SaveError(errptr, info->rep.status);
|
||||
}
|
||||
|
||||
const char* rocksdb_compactionjobinfo_cf_name(
|
||||
const rocksdb_compactionjobinfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_input_files_count(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.input_files.size();
|
||||
}
|
||||
|
||||
const char* rocksdb_compactionjobinfo_input_file_at(
|
||||
const rocksdb_compactionjobinfo_t* info, size_t pos, size_t* size) {
|
||||
assert(info != nullptr);
|
||||
assert(pos < info->rep.input_files.size());
|
||||
|
||||
const std::string& path = info->rep.input_files[pos];
|
||||
*size = path.size();
|
||||
return path.data();
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_output_files_count(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.output_files.size();
|
||||
}
|
||||
|
||||
const char* rocksdb_compactionjobinfo_output_file_at(
|
||||
const rocksdb_compactionjobinfo_t* info, size_t pos, size_t* size) {
|
||||
assert(info != nullptr);
|
||||
assert(pos < info->rep.output_files.size());
|
||||
|
||||
const std::string& path = info->rep.output_files[pos];
|
||||
*size = path.size();
|
||||
return path.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_elapsed_micros(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.elapsed_micros;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_num_corrupt_keys(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_corrupt_keys;
|
||||
}
|
||||
|
||||
int rocksdb_compactionjobinfo_base_input_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.base_input_level;
|
||||
}
|
||||
|
||||
int rocksdb_compactionjobinfo_output_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.output_level;
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_num_input_files(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_files;
|
||||
}
|
||||
|
||||
size_t rocksdb_compactionjobinfo_num_input_files_at_output_level(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_files_at_output_level;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_input_records(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_input_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_output_records(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.num_output_records;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_total_input_bytes(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.total_input_bytes;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_compactionjobinfo_total_output_bytes(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return info->rep.stats.total_output_bytes;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_compactionjobinfo_compaction_reason(
|
||||
const rocksdb_compactionjobinfo_t* info) {
|
||||
return static_cast<uint32_t>(info->rep.compaction_reason);
|
||||
}
|
||||
|
||||
/* SubcompactionJobInfo */
|
||||
|
||||
void rocksdb_subcompactionjobinfo_status(
|
||||
const rocksdb_subcompactionjobinfo_t* info, char** errptr) {
|
||||
SaveError(errptr, info->rep.status);
|
||||
}
|
||||
|
||||
const char* rocksdb_subcompactionjobinfo_cf_name(
|
||||
const rocksdb_subcompactionjobinfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_subcompactionjobinfo_thread_id(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.thread_id;
|
||||
}
|
||||
|
||||
int rocksdb_subcompactionjobinfo_base_input_level(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.base_input_level;
|
||||
}
|
||||
|
||||
int rocksdb_subcompactionjobinfo_output_level(
|
||||
const rocksdb_subcompactionjobinfo_t* info) {
|
||||
return info->rep.output_level;
|
||||
}
|
||||
|
||||
/* ExternalFileIngestionInfo */
|
||||
|
||||
const char* rocksdb_externalfileingestioninfo_cf_name(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const char* rocksdb_externalfileingestioninfo_internal_file_path(
|
||||
const rocksdb_externalfileingestioninfo_t* info, size_t* size) {
|
||||
*size = info->rep.internal_file_path.size();
|
||||
return info->rep.internal_file_path.data();
|
||||
}
|
||||
|
||||
/* External write stall info */
|
||||
extern ROCKSDB_LIBRARY_API const char* rocksdb_writestallinfo_cf_name(
|
||||
const rocksdb_writestallinfo_t* info, size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
const rocksdb_writestallcondition_t* rocksdb_writestallinfo_cur(
|
||||
const rocksdb_writestallinfo_t* info) {
|
||||
return reinterpret_cast<const rocksdb_writestallcondition_t*>(
|
||||
&info->rep.condition.cur);
|
||||
}
|
||||
|
||||
const rocksdb_writestallcondition_t* rocksdb_writestallinfo_prev(
|
||||
const rocksdb_writestallinfo_t* info) {
|
||||
return reinterpret_cast<const rocksdb_writestallcondition_t*>(
|
||||
&info->rep.condition.prev);
|
||||
}
|
||||
|
||||
const char* rocksdb_memtableinfo_cf_name(const rocksdb_memtableinfo_t* info,
|
||||
size_t* size) {
|
||||
*size = info->rep.cf_name.size();
|
||||
return info->rep.cf_name.data();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_memtableinfo_first_seqno(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.first_seqno;
|
||||
}
|
||||
uint64_t rocksdb_memtableinfo_earliest_seqno(
|
||||
const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.earliest_seqno;
|
||||
}
|
||||
uint64_t rocksdb_memtableinfo_num_entries(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.num_entries;
|
||||
}
|
||||
uint64_t rocksdb_memtableinfo_num_deletes(const rocksdb_memtableinfo_t* info) {
|
||||
return info->rep.num_deletes;
|
||||
}
|
||||
|
||||
/* event listener */
|
||||
|
||||
struct rocksdb_eventlistener_t : public EventListener {
|
||||
void* state_{};
|
||||
void (*destructor_)(void*){};
|
||||
void (*on_flush_begin)(void*, rocksdb_t*, const rocksdb_flushjobinfo_t*){};
|
||||
void (*on_flush_completed)(void*, rocksdb_t*,
|
||||
const rocksdb_flushjobinfo_t*){};
|
||||
void (*on_compaction_begin)(void*, rocksdb_t*,
|
||||
const rocksdb_compactionjobinfo_t*){};
|
||||
void (*on_compaction_completed)(void*, rocksdb_t*,
|
||||
const rocksdb_compactionjobinfo_t*){};
|
||||
void (*on_subcompaction_begin)(void*,
|
||||
const rocksdb_subcompactionjobinfo_t*){};
|
||||
void (*on_subcompaction_completed)(void*,
|
||||
const rocksdb_subcompactionjobinfo_t*){};
|
||||
void (*on_external_file_ingested)(
|
||||
void*, rocksdb_t*, const rocksdb_externalfileingestioninfo_t*){};
|
||||
void (*on_background_error)(void*, uint32_t, rocksdb_status_ptr_t*){};
|
||||
void (*on_stall_conditions_changed)(void*, const rocksdb_writestallinfo_t*){};
|
||||
void (*on_memtable_sealed)(void*, const rocksdb_memtableinfo_t*){};
|
||||
|
||||
rocksdb_eventlistener_t() = default;
|
||||
|
||||
rocksdb_eventlistener_t(const rocksdb_eventlistener_t&) = delete;
|
||||
rocksdb_eventlistener_t& operator=(const rocksdb_eventlistener_t&) = delete;
|
||||
rocksdb_eventlistener_t(rocksdb_eventlistener_t&&) = delete;
|
||||
rocksdb_eventlistener_t& operator=(rocksdb_eventlistener_t&&) = delete;
|
||||
|
||||
void OnFlushBegin(DB* db, const FlushJobInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_flush_begin(state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_flushjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnFlushCompleted(DB* db, const FlushJobInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_flush_completed(state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_flushjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnCompactionBegin(DB* db, const CompactionJobInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_compaction_begin(
|
||||
state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_compactionjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnCompactionCompleted(DB* db, const CompactionJobInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_compaction_completed(
|
||||
state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_compactionjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnSubcompactionBegin(const SubcompactionJobInfo& info) override {
|
||||
on_subcompaction_begin(
|
||||
state_, reinterpret_cast<const rocksdb_subcompactionjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnSubcompactionCompleted(const SubcompactionJobInfo& info) override {
|
||||
on_subcompaction_completed(
|
||||
state_, reinterpret_cast<const rocksdb_subcompactionjobinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnExternalFileIngested(DB* db,
|
||||
const ExternalFileIngestionInfo& info) override {
|
||||
rocksdb_t c_db = {db};
|
||||
on_external_file_ingested(
|
||||
state_, &c_db,
|
||||
reinterpret_cast<const rocksdb_externalfileingestioninfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnBackgroundError(BackgroundErrorReason reason,
|
||||
Status* status) override {
|
||||
rocksdb_status_ptr_t* s = new rocksdb_status_ptr_t;
|
||||
s->rep = status;
|
||||
on_background_error(state_, static_cast<uint32_t>(reason), s);
|
||||
delete s;
|
||||
}
|
||||
|
||||
void OnStallConditionsChanged(const WriteStallInfo& info) override {
|
||||
on_stall_conditions_changed(
|
||||
state_, reinterpret_cast<const rocksdb_writestallinfo_t*>(&info));
|
||||
}
|
||||
|
||||
void OnMemTableSealed(const MemTableInfo& info) override {
|
||||
on_memtable_sealed(state_,
|
||||
reinterpret_cast<const rocksdb_memtableinfo_t*>(&info));
|
||||
}
|
||||
|
||||
~rocksdb_eventlistener_t() override { destructor_(state_); }
|
||||
};
|
||||
|
||||
rocksdb_eventlistener_t* rocksdb_eventlistener_create(
|
||||
void* state_, void (*destructor_)(void*), on_flush_begin_cb on_flush_begin,
|
||||
on_flush_completed_cb on_flush_completed,
|
||||
on_compaction_begin_cb on_compaction_begin,
|
||||
on_compaction_completed_cb on_compaction_completed,
|
||||
on_subcompaction_begin_cb on_subcompaction_begin,
|
||||
on_subcompaction_completed_cb on_subcompaction_completed,
|
||||
on_external_file_ingested_cb on_external_file_ingested,
|
||||
on_background_error_cb on_background_error,
|
||||
on_stall_conditions_changed_cb on_stall_conditions_changed,
|
||||
on_memtable_sealed_cb on_memtable_sealed) {
|
||||
rocksdb_eventlistener_t* et = new rocksdb_eventlistener_t;
|
||||
et->state_ = state_;
|
||||
et->destructor_ = destructor_;
|
||||
et->on_flush_begin = on_flush_begin;
|
||||
et->on_flush_completed = on_flush_completed;
|
||||
et->on_compaction_begin = on_compaction_begin;
|
||||
et->on_compaction_completed = on_compaction_completed;
|
||||
et->on_subcompaction_begin = on_subcompaction_begin;
|
||||
et->on_subcompaction_completed = on_subcompaction_completed;
|
||||
et->on_external_file_ingested = on_external_file_ingested;
|
||||
et->on_background_error = on_background_error;
|
||||
et->on_stall_conditions_changed = on_stall_conditions_changed;
|
||||
et->on_memtable_sealed = on_memtable_sealed;
|
||||
return et;
|
||||
}
|
||||
|
||||
void rocksdb_eventlistener_destroy(rocksdb_eventlistener_t* t) { delete t; }
|
||||
|
||||
void rocksdb_options_add_eventlistener(rocksdb_options_t* opt,
|
||||
rocksdb_eventlistener_t* t) {
|
||||
opt->rep.listeners.emplace_back(std::shared_ptr<EventListener>(t));
|
||||
}
|
||||
|
||||
rocksdb_cuckoo_table_options_t* rocksdb_cuckoo_options_create() {
|
||||
return new rocksdb_cuckoo_table_options_t;
|
||||
}
|
||||
@@ -3295,6 +3738,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 +4257,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;
|
||||
@@ -4332,6 +4775,8 @@ uint64_t rocksdb_perfcontext_metric(rocksdb_perfcontext_t* context,
|
||||
return rep->internal_recent_skipped_count;
|
||||
case rocksdb_internal_merge_count:
|
||||
return rep->internal_merge_count;
|
||||
case rocksdb_internal_merge_point_lookup_count:
|
||||
return rep->internal_merge_point_lookup_count;
|
||||
case rocksdb_get_snapshot_time:
|
||||
return rep->get_snapshot_time;
|
||||
case rocksdb_get_from_memtable_time:
|
||||
@@ -5239,6 +5684,67 @@ ROCKSDB_LIBRARY_API void rocksdb_write_buffer_manager_set_allow_stall(
|
||||
wbm->rep->SetAllowStall(new_allow_stall);
|
||||
}
|
||||
|
||||
rocksdb_sst_file_manager_t* rocksdb_sst_file_manager_create(
|
||||
rocksdb_env_t* env) {
|
||||
rocksdb_sst_file_manager_t* sfm = new rocksdb_sst_file_manager_t;
|
||||
sfm->rep.reset(ROCKSDB_NAMESPACE::NewSstFileManager(env->rep));
|
||||
return sfm;
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_destroy(rocksdb_sst_file_manager_t* sfm) {
|
||||
delete sfm;
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_set_max_allowed_space_usage(
|
||||
rocksdb_sst_file_manager_t* sfm, uint64_t max_allowed_space) {
|
||||
sfm->rep->SetMaxAllowedSpaceUsage(max_allowed_space);
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_set_compaction_buffer_size(
|
||||
rocksdb_sst_file_manager_t* sfm, uint64_t compaction_buffer_size) {
|
||||
sfm->rep->SetCompactionBufferSize(compaction_buffer_size);
|
||||
}
|
||||
|
||||
bool rocksdb_sst_file_manager_is_max_allowed_space_reached(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->IsMaxAllowedSpaceReached();
|
||||
}
|
||||
|
||||
bool rocksdb_sst_file_manager_is_max_allowed_space_reached_including_compactions(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->IsMaxAllowedSpaceReachedIncludingCompactions();
|
||||
}
|
||||
|
||||
uint64_t rocksdb_sst_file_manager_get_total_size(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->GetTotalSize();
|
||||
}
|
||||
|
||||
int64_t rocksdb_sst_file_manager_get_delete_rate_bytes_per_second(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->GetDeleteRateBytesPerSecond();
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_set_delete_rate_bytes_per_second(
|
||||
rocksdb_sst_file_manager_t* sfm, int64_t delete_rate) {
|
||||
return sfm->rep->SetDeleteRateBytesPerSecond(delete_rate);
|
||||
}
|
||||
|
||||
double rocksdb_sst_file_manager_get_max_trash_db_ratio(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->GetMaxTrashDBRatio();
|
||||
}
|
||||
|
||||
void rocksdb_sst_file_manager_set_max_trash_db_ratio(
|
||||
rocksdb_sst_file_manager_t* sfm, double ratio) {
|
||||
return sfm->rep->SetMaxTrashDBRatio(ratio);
|
||||
}
|
||||
|
||||
uint64_t rocksdb_sst_file_manager_get_total_trash_size(
|
||||
rocksdb_sst_file_manager_t* sfm) {
|
||||
return sfm->rep->GetTotalTrashSize();
|
||||
}
|
||||
|
||||
rocksdb_dbpath_t* rocksdb_dbpath_create(const char* path,
|
||||
uint64_t target_size) {
|
||||
rocksdb_dbpath_t* result = new rocksdb_dbpath_t;
|
||||
|
||||
+114
-12
@@ -103,6 +103,12 @@ static void CheckValue(char* err, const char* expected, char** actual,
|
||||
Free(actual);
|
||||
}
|
||||
|
||||
static void CheckPinnedValue(char* err, const char* expected,
|
||||
const char** actual, size_t actual_length) {
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, *actual, actual_length);
|
||||
}
|
||||
|
||||
static void CheckGet(rocksdb_t* db, const rocksdb_readoptions_t* options,
|
||||
const char* key, const char* expected) {
|
||||
char* err = NULL;
|
||||
@@ -1245,6 +1251,8 @@ int main(int argc, char** argv) {
|
||||
CheckCondition(count == 3);
|
||||
size_t size;
|
||||
char* value;
|
||||
const char* pinned_value;
|
||||
rocksdb_pinnableslice_t* p;
|
||||
value = rocksdb_writebatch_wi_get_from_batch(wbi, options, "box", 3, &size,
|
||||
&err);
|
||||
CheckValue(err, "c", &value, size);
|
||||
@@ -1254,9 +1262,19 @@ int main(int argc, char** argv) {
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions,
|
||||
"foo", 3, &size, &err);
|
||||
CheckValue(err, "hello", &value, size);
|
||||
p = rocksdb_writebatch_wi_get_pinned_from_batch_and_db(wbi, db, roptions,
|
||||
"foo", 3, &err);
|
||||
pinned_value = rocksdb_pinnableslice_value(p, &size);
|
||||
CheckPinnedValue(err, "hello", &pinned_value, size);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
value = rocksdb_writebatch_wi_get_from_batch_and_db(wbi, db, roptions,
|
||||
"box", 3, &size, &err);
|
||||
CheckValue(err, "c", &value, size);
|
||||
p = rocksdb_writebatch_wi_get_pinned_from_batch_and_db(wbi, db, roptions,
|
||||
"box", 3, &err);
|
||||
pinned_value = rocksdb_pinnableslice_value(p, &size);
|
||||
CheckPinnedValue(err, "c", &pinned_value, size);
|
||||
rocksdb_pinnableslice_destroy(p);
|
||||
rocksdb_write_writebatch_wi(db, woptions, wbi, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
@@ -1362,6 +1380,46 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_wi_destroy(wbi);
|
||||
}
|
||||
|
||||
StartPhase("wbwi_iter_readoptions");
|
||||
{
|
||||
rocksdb_readoptions_t* iter_roptions = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_iterate_lower_bound(iter_roptions, "boy", 3);
|
||||
rocksdb_readoptions_set_iterate_upper_bound(iter_roptions, "fool", 4);
|
||||
rocksdb_iterator_t* base_iter = rocksdb_create_iterator(db, iter_roptions);
|
||||
rocksdb_writebatch_wi_t* wbi = rocksdb_writebatch_wi_create(0, 1);
|
||||
rocksdb_writebatch_wi_put(wbi, "bar", 3, "b",
|
||||
1); // should get filtered out
|
||||
rocksdb_writebatch_wi_put(wbi, "cat", 3, "miau", 4);
|
||||
rocksdb_writebatch_wi_put(wbi, "gnu", 3, "muh",
|
||||
3); // should get filtered out
|
||||
rocksdb_iterator_t* iter =
|
||||
rocksdb_writebatch_wi_create_iterator_with_base_readopts(wbi, base_iter,
|
||||
iter_roptions);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "cat", "miau");
|
||||
rocksdb_iter_next(iter);
|
||||
CheckIter(iter, "foo", "hello");
|
||||
rocksdb_iter_prev(iter);
|
||||
CheckIter(iter, "cat", "miau");
|
||||
rocksdb_iter_prev(iter);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_last(iter);
|
||||
CheckIter(iter, "foo", "hello");
|
||||
rocksdb_iter_seek(iter, "b", 1);
|
||||
CheckIter(iter, "cat", "miau");
|
||||
rocksdb_iter_seek_for_prev(iter, "d", 1);
|
||||
CheckIter(iter, "cat", "miau");
|
||||
rocksdb_iter_seek_for_prev(iter, "fool", 3);
|
||||
CheckIter(iter, "foo", "hello");
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_writebatch_wi_destroy(wbi);
|
||||
rocksdb_readoptions_destroy(iter_roptions);
|
||||
}
|
||||
|
||||
StartPhase("multiget");
|
||||
{
|
||||
const char* keys[3] = {"box", "foo", "notfound"};
|
||||
@@ -1792,6 +1850,35 @@ int main(int argc, char** argv) {
|
||||
rocksdb_flush_wal(db, 1, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// Test WriteBatchWithIndex iteration with Column Family
|
||||
rocksdb_writebatch_wi_t* wbwi = rocksdb_writebatch_wi_create(0, true);
|
||||
rocksdb_writebatch_wi_put_cf(wbwi, handles[1], "boat", 4, "row",
|
||||
3); // should be filtered out
|
||||
rocksdb_writebatch_wi_put_cf(wbwi, handles[1], "buffy", 5, "charmed", 7);
|
||||
rocksdb_writebatch_wi_put_cf(wbwi, handles[1], "bus", 3, "yellow",
|
||||
6); // should be filtered out
|
||||
rocksdb_readoptions_t* iter_roptions = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_iterate_lower_bound(iter_roptions, "bu", 2);
|
||||
rocksdb_readoptions_set_iterate_upper_bound(iter_roptions, "buffz", 5);
|
||||
rocksdb_iterator_t* base_iter =
|
||||
rocksdb_create_iterator_cf(db, iter_roptions, handles[1]);
|
||||
rocksdb_iterator_t* wbwi_iter =
|
||||
rocksdb_writebatch_wi_create_iterator_with_base_cf_readopts(
|
||||
wbwi, base_iter, handles[1], iter_roptions);
|
||||
|
||||
CheckCondition(!rocksdb_iter_valid(wbwi_iter));
|
||||
rocksdb_iter_seek_to_first(wbwi_iter);
|
||||
CheckCondition(rocksdb_iter_valid(wbwi_iter));
|
||||
CheckIter(wbwi_iter, "buff", "rocksdb");
|
||||
rocksdb_iter_next(wbwi_iter);
|
||||
CheckIter(wbwi_iter, "buffy", "charmed");
|
||||
rocksdb_iter_next(wbwi_iter);
|
||||
CheckCondition(!rocksdb_iter_valid(wbwi_iter));
|
||||
|
||||
rocksdb_iter_destroy(wbwi_iter);
|
||||
rocksdb_writebatch_wi_destroy(wbwi);
|
||||
rocksdb_readoptions_destroy(iter_roptions);
|
||||
|
||||
const char* keys[3] = {"box", "box", "barfooxx"};
|
||||
const rocksdb_column_family_handle_t* get_handles[3] = {
|
||||
handles[0], handles[1], handles[1]};
|
||||
@@ -2129,6 +2216,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 +2237,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 +2494,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 +2657,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 +2687,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));
|
||||
@@ -4052,6 +4137,23 @@ int main(int argc, char** argv) {
|
||||
rocksdb_cache_destroy(lru);
|
||||
}
|
||||
|
||||
StartPhase("sst_file_manager");
|
||||
{
|
||||
rocksdb_sst_file_manager_t* sst_file_manager;
|
||||
sst_file_manager = rocksdb_sst_file_manager_create(env);
|
||||
rocksdb_sst_file_manager_set_delete_rate_bytes_per_second(sst_file_manager,
|
||||
1);
|
||||
rocksdb_sst_file_manager_set_max_trash_db_ratio(sst_file_manager, 0.75);
|
||||
|
||||
CheckCondition(1 ==
|
||||
rocksdb_sst_file_manager_get_delete_rate_bytes_per_second(
|
||||
sst_file_manager));
|
||||
CheckCondition(0.75 == rocksdb_sst_file_manager_get_max_trash_db_ratio(
|
||||
sst_file_manager));
|
||||
|
||||
rocksdb_sst_file_manager_destroy(sst_file_manager);
|
||||
}
|
||||
|
||||
StartPhase("cancel_all_background_work");
|
||||
rocksdb_cancel_all_background_work(db, 1);
|
||||
|
||||
|
||||
+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();
|
||||
}
|
||||
|
||||
|
||||
@@ -283,6 +283,13 @@ class Compaction {
|
||||
// are non-overlapping and can be trivially moved.
|
||||
bool is_trivial_move() const { return is_trivial_move_; }
|
||||
|
||||
bool is_trivial_copy_compaction() const {
|
||||
return immutable_options_.compaction_style == kCompactionStyleFIFO &&
|
||||
compaction_reason_ == CompactionReason::kChangeTemperature &&
|
||||
mutable_cf_options_.compaction_options_fifo
|
||||
.allow_trivial_copy_when_change_temperature;
|
||||
}
|
||||
|
||||
// How many total levels are there?
|
||||
int number_levels() const { return number_levels_; }
|
||||
|
||||
|
||||
@@ -878,7 +878,14 @@ Status CompactionJob::Run() {
|
||||
UpdateCompactionJobOutputStats(internal_stats_);
|
||||
|
||||
// Verify number of output records
|
||||
if (status.ok() && db_options_.compaction_verify_record_count) {
|
||||
// Only verify on table with format collects table properties
|
||||
const auto& mutable_cf_options = compact_->compaction->mutable_cf_options();
|
||||
if (status.ok() &&
|
||||
(mutable_cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kBlockBasedTableName()) ||
|
||||
mutable_cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kPlainTableName())) &&
|
||||
db_options_.compaction_verify_record_count) {
|
||||
uint64_t total_output_num = 0;
|
||||
for (const auto& state : compact_->sub_compact_states) {
|
||||
for (const auto& output : state.GetOutputs()) {
|
||||
@@ -2293,8 +2300,8 @@ void CompactionJob::LogCompaction() {
|
||||
cfd->GetName().c_str(), scratch);
|
||||
// build event logger report
|
||||
auto stream = event_logger_->Log();
|
||||
stream << "job" << job_id_ << "event" << "compaction_started"
|
||||
<< "compaction_reason"
|
||||
stream << "job" << job_id_ << "event" << "compaction_started" << "cf_name"
|
||||
<< cfd->GetName() << "compaction_reason"
|
||||
<< GetCompactionReasonString(compaction->compaction_reason());
|
||||
for (size_t i = 0; i < compaction->num_input_levels(); ++i) {
|
||||
stream << ("files_L" + std::to_string(compaction->level(i)));
|
||||
|
||||
@@ -427,8 +427,9 @@ struct CompactionServiceInput {
|
||||
// CompactionServiceOutputFile is the metadata for the output SST file
|
||||
struct CompactionServiceOutputFile {
|
||||
std::string file_name;
|
||||
SequenceNumber smallest_seqno;
|
||||
SequenceNumber largest_seqno;
|
||||
uint64_t file_size{};
|
||||
SequenceNumber smallest_seqno{};
|
||||
SequenceNumber largest_seqno{};
|
||||
std::string smallest_internal_key;
|
||||
std::string largest_internal_key;
|
||||
uint64_t oldest_ancester_time = kUnknownOldestAncesterTime;
|
||||
@@ -436,24 +437,26 @@ struct CompactionServiceOutputFile {
|
||||
uint64_t epoch_number = kUnknownEpochNumber;
|
||||
std::string file_checksum = kUnknownFileChecksum;
|
||||
std::string file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
uint64_t paranoid_hash;
|
||||
uint64_t paranoid_hash{};
|
||||
bool marked_for_compaction;
|
||||
UniqueId64x2 unique_id{};
|
||||
TableProperties table_properties;
|
||||
bool is_proximal_level_output;
|
||||
Temperature file_temperature;
|
||||
Temperature file_temperature = Temperature::kUnknown;
|
||||
|
||||
CompactionServiceOutputFile() = default;
|
||||
CompactionServiceOutputFile(
|
||||
const std::string& name, SequenceNumber smallest, SequenceNumber largest,
|
||||
std::string _smallest_internal_key, std::string _largest_internal_key,
|
||||
uint64_t _oldest_ancester_time, uint64_t _file_creation_time,
|
||||
uint64_t _epoch_number, const std::string& _file_checksum,
|
||||
const std::string& name, uint64_t size, SequenceNumber smallest,
|
||||
SequenceNumber largest, std::string _smallest_internal_key,
|
||||
std::string _largest_internal_key, uint64_t _oldest_ancester_time,
|
||||
uint64_t _file_creation_time, uint64_t _epoch_number,
|
||||
const std::string& _file_checksum,
|
||||
const std::string& _file_checksum_func_name, uint64_t _paranoid_hash,
|
||||
bool _marked_for_compaction, UniqueId64x2 _unique_id,
|
||||
const TableProperties& _table_properties, bool _is_proximal_level_output,
|
||||
Temperature _file_temperature)
|
||||
: file_name(name),
|
||||
file_size(size),
|
||||
smallest_seqno(smallest),
|
||||
largest_seqno(largest),
|
||||
smallest_internal_key(std::move(_smallest_internal_key)),
|
||||
|
||||
@@ -232,11 +232,6 @@ class CompactionJobTestBase : public testing::Test {
|
||||
// set default for the tests
|
||||
mutable_cf_options_.target_file_size_base = 1024 * 1024;
|
||||
mutable_cf_options_.max_compaction_bytes = 10 * 1024 * 1024;
|
||||
|
||||
// Turn off compaction_verify_record_count MockTables
|
||||
if (table_type == TableTypeForTest::kMockTable) {
|
||||
db_options_.compaction_verify_record_count = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
@@ -1674,6 +1669,7 @@ TEST_F(CompactionJobTest, ResultSerialization) {
|
||||
UniqueId64x2 id{rnd64.Uniform(UINT64_MAX), rnd64.Uniform(UINT64_MAX)};
|
||||
result.output_files.emplace_back(
|
||||
rnd.RandomString(rnd.Uniform(kStrMaxLen)) /* file_name */,
|
||||
rnd64.Uniform(UINT64_MAX) /* file_size */,
|
||||
rnd64.Uniform(UINT64_MAX) /* smallest_seqno */,
|
||||
rnd64.Uniform(UINT64_MAX) /* largest_seqno */,
|
||||
rnd.RandomBinaryString(
|
||||
|
||||
@@ -54,6 +54,7 @@ Status CompactionOutputs::Finish(
|
||||
}
|
||||
current_output().finished = true;
|
||||
stats_.bytes_written += current_bytes;
|
||||
stats_.bytes_written_pre_comp += builder_->PreCompressionSize();
|
||||
stats_.num_output_files = static_cast<int>(outputs_.size());
|
||||
|
||||
return s;
|
||||
|
||||
@@ -1134,10 +1134,15 @@ TEST_F(CompactionPickerTest, FIFOToCold1) {
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
fifo_options_.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, kColdThreshold}};
|
||||
fifo_options_.allow_trivial_copy_when_change_temperature = true;
|
||||
fifo_options_.trivial_copy_buffer_size = 16 * 1024 * 1024;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
@@ -1186,7 +1191,10 @@ TEST_F(CompactionPickerTest, FIFOToColdMaxCompactionSize) {
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 9;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
@@ -1253,7 +1261,10 @@ TEST_F(CompactionPickerTest, FIFOToColdWithExistingCold) {
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
@@ -1318,7 +1329,10 @@ TEST_F(CompactionPickerTest, FIFOToColdWithHotBetweenCold) {
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
@@ -1385,7 +1399,10 @@ TEST_F(CompactionPickerTest, FIFOToHotAndWarm) {
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 100;
|
||||
mutable_cf_options_.max_compaction_bytes = kFileSize * 100;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
auto copiedIOptions = ioptions_;
|
||||
copiedIOptions.compaction_style = kCompactionStyleFIFO;
|
||||
FIFOCompactionPicker fifo_compaction_picker(copiedIOptions, &icmp_);
|
||||
|
||||
int64_t current_time = 0;
|
||||
ASSERT_OK(Env::Default()->GetCurrentTime(¤t_time));
|
||||
|
||||
@@ -74,10 +74,14 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction_input.output_level, input_files_oss.str().c_str());
|
||||
CompactionServiceJobInfo info(
|
||||
dbname_, db_id_, db_session_id_, GetCompactionId(sub_compact),
|
||||
dbname_, db_id_, db_session_id_,
|
||||
compaction->column_family_data()->GetID(),
|
||||
compaction->column_family_data()->GetName(), 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) {
|
||||
@@ -111,7 +115,7 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
}
|
||||
|
||||
std::string debug_str_before_wait =
|
||||
compaction->input_version()->DebugString();
|
||||
compaction->input_version()->DebugString(/*hex=*/true);
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Waiting for remote compaction...",
|
||||
@@ -122,13 +126,14 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
&compaction_result_binary);
|
||||
|
||||
if (compaction_status != CompactionServiceJobStatus::kSuccess) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"[%s] [JOB %d] Wait() status is not kSuccess. "
|
||||
"\nDebugString Before Wait():\n%s"
|
||||
"\nDebugString After Wait():\n%s",
|
||||
compaction->column_family_data()->GetName().c_str(),
|
||||
job_id_, debug_str_before_wait.c_str(),
|
||||
compaction->input_version()->DebugString().c_str());
|
||||
ROCKS_LOG_ERROR(
|
||||
db_options_.info_log,
|
||||
"[%s] [JOB %d] Wait() status is not kSuccess. "
|
||||
"\nDebugString Before Wait():\n%s"
|
||||
"\nDebugString After Wait():\n%s",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
debug_str_before_wait.c_str(),
|
||||
compaction->input_version()->DebugString(/*hex=*/true).c_str());
|
||||
}
|
||||
|
||||
if (compaction_status == CompactionServiceJobStatus::kUseLocal) {
|
||||
@@ -216,18 +221,24 @@ CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
}
|
||||
|
||||
FileMetaData meta;
|
||||
uint64_t file_size;
|
||||
// FIXME: file_size should be part of CompactionServiceOutputFile so that
|
||||
// we don't get DB corruption if the full file size has not been propagated
|
||||
// back to the caller through the file system (which could have metadata
|
||||
// lag or caching bugs).
|
||||
s = fs_->GetFileSize(tgt_file, IOOptions(), &file_size, nullptr);
|
||||
uint64_t file_size = file.file_size;
|
||||
|
||||
// TODO - Clean this up in the next release.
|
||||
// For backward compatibility - in case the remote worker does not populate
|
||||
// the file_size yet. If missing, continue to populate this from the file
|
||||
// system.
|
||||
if (file_size == 0) {
|
||||
s = fs_->GetFileSize(tgt_file, IOOptions(), &file_size, nullptr);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
sub_compact->status = s;
|
||||
db_options_.compaction_service->OnInstallation(
|
||||
response.scheduled_job_id, CompactionServiceJobStatus::kFailure);
|
||||
return CompactionServiceJobStatus::kFailure;
|
||||
}
|
||||
assert(file_size > 0);
|
||||
|
||||
meta.fd = FileDescriptor(file_num, compaction->output_path_id(), file_size,
|
||||
file.smallest_seqno, file.largest_seqno);
|
||||
meta.smallest.DecodeFrom(file.smallest_internal_key);
|
||||
@@ -240,7 +251,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);
|
||||
@@ -415,14 +427,14 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
for (const auto& output_file : sub_compact->GetOutputs()) {
|
||||
auto& meta = output_file.meta;
|
||||
compaction_result_->output_files.emplace_back(
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.smallest_seqno,
|
||||
meta.fd.largest_seqno, meta.smallest.Encode().ToString(),
|
||||
meta.largest.Encode().ToString(), meta.oldest_ancester_time,
|
||||
meta.file_creation_time, meta.epoch_number, meta.file_checksum,
|
||||
meta.file_checksum_func_name, output_file.validator.GetHash(),
|
||||
meta.marked_for_compaction, meta.unique_id,
|
||||
*output_file.table_properties, output_file.is_proximal_level,
|
||||
meta.temperature);
|
||||
MakeTableFileName(meta.fd.GetNumber()), meta.fd.GetFileSize(),
|
||||
meta.fd.smallest_seqno, meta.fd.largest_seqno,
|
||||
meta.smallest.Encode().ToString(), meta.largest.Encode().ToString(),
|
||||
meta.oldest_ancester_time, meta.file_creation_time, meta.epoch_number,
|
||||
meta.file_checksum, meta.file_checksum_func_name,
|
||||
output_file.validator.GetHash(), meta.marked_for_compaction,
|
||||
meta.unique_id, *output_file.table_properties,
|
||||
output_file.is_proximal_level, meta.temperature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,6 +534,10 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct CompactionServiceOutputFile, file_name),
|
||||
OptionType::kEncodedString, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"file_size",
|
||||
{offsetof(struct CompactionServiceOutputFile, file_size),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone}},
|
||||
{"smallest_seqno",
|
||||
{offsetof(struct CompactionServiceOutputFile, smallest_seqno),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
|
||||
@@ -21,10 +21,10 @@ class MyTestCompactionService : public CompactionService {
|
||||
: db_path_(std::move(db_path)),
|
||||
options_(options),
|
||||
statistics_(statistics),
|
||||
start_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false),
|
||||
wait_info_("na", "na", "na", 0, Env::TOTAL, CompactionReason::kUnknown,
|
||||
false, false, false),
|
||||
start_info_("na", "na", "na", 0, "na", 0, Env::TOTAL,
|
||||
CompactionReason::kUnknown, false, false, false, -1, -1),
|
||||
wait_info_("na", "na", "na", 0, "na", 0, Env::TOTAL,
|
||||
CompactionReason::kUnknown, 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) {
|
||||
@@ -423,6 +434,30 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
|
||||
auto info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(0, info.cf_id);
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, info.cf_name);
|
||||
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(0, info.cf_id);
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, info.cf_name);
|
||||
|
||||
// Test non-default CF
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(CompactRangeOptions(), handles_[1], nullptr, nullptr));
|
||||
my_cs->GetResult(&result);
|
||||
ASSERT_OK(result.status);
|
||||
ASSERT_TRUE(result.stats.is_manual_compaction);
|
||||
ASSERT_TRUE(result.stats.is_remote_compaction);
|
||||
|
||||
info = my_cs->GetCompactionInfoForStart();
|
||||
ASSERT_EQ(handles_[1]->GetID(), info.cf_id);
|
||||
ASSERT_EQ(handles_[1]->GetName(), info.cf_name);
|
||||
|
||||
info = my_cs->GetCompactionInfoForWait();
|
||||
ASSERT_EQ(handles_[1]->GetID(), info.cf_id);
|
||||
ASSERT_EQ(handles_[1]->GetName(), info.cf_name);
|
||||
}
|
||||
|
||||
TEST_F(CompactionServiceTest, CompactionOutputFileIOError) {
|
||||
@@ -890,6 +925,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 +947,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 +1291,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 +1385,17 @@ 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);
|
||||
ASSERT_EQ(kDefaultColumnFamilyName, info.cf_name);
|
||||
|
||||
// Test priority BOTTOM
|
||||
env_->SetBackgroundThreads(1, Env::BOTTOM);
|
||||
@@ -1367,18 +1427,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 +1453,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 +1483,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 +1498,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) {
|
||||
|
||||
@@ -225,6 +225,8 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageUniversal) {
|
||||
flush_stats.micros = 1;
|
||||
flush_stats.bytes_written = bytes_per_file;
|
||||
flush_stats.num_output_files = 1;
|
||||
flush_stats.num_input_records = kNumKeys;
|
||||
flush_stats.num_output_records = kNumKeys;
|
||||
expect_stats[0].Add(flush_stats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
@@ -1080,6 +1082,8 @@ TEST_F(TieredCompactionTest, SequenceBasedTieredStorageLevel) {
|
||||
flush_stats.micros = 1;
|
||||
flush_stats.bytes_written = bytes_per_file;
|
||||
flush_stats.num_output_files = 1;
|
||||
flush_stats.num_input_records = kNumKeys;
|
||||
flush_stats.num_output_records = kNumKeys;
|
||||
expect_stats[0].Add(flush_stats);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
@@ -851,9 +851,6 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
options.env = env_.get();
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
// Skip verifying record count against TableProperties for
|
||||
// MockTables
|
||||
options.compaction_verify_record_count = false;
|
||||
Status s;
|
||||
for (const auto& mode : corruption_modes) {
|
||||
delete db_;
|
||||
|
||||
@@ -5086,6 +5086,7 @@ TEST_F(DBBasicTest, DisallowMemtableWrite) {
|
||||
options_allow.create_if_missing = true;
|
||||
Options options_disallow = options_allow;
|
||||
options_disallow.disallow_memtable_writes = true;
|
||||
options_disallow.paranoid_memory_checks = true;
|
||||
|
||||
DestroyAndReopen(options_allow);
|
||||
// CFs allowing and disallowing memtable write
|
||||
@@ -5125,6 +5126,11 @@ TEST_F(DBBasicTest, DisallowMemtableWrite) {
|
||||
EXPECT_EQ(Get(2, "b2"), "2");
|
||||
EXPECT_EQ(Get(3, "b3"), "NOT_FOUND");
|
||||
|
||||
std::unique_ptr<Iterator> iter(
|
||||
dbfull()->NewIterator(ReadOptions(), handles_[3]));
|
||||
iter->Seek("a3");
|
||||
ASSERT_OK(iter->status());
|
||||
iter.reset();
|
||||
// When the DB is re-opened with WAL entries for a CF that is newly setting
|
||||
// disallow_memtable_writes, we detect that and fail the open gracefully.
|
||||
ASSERT_EQ(TryReopenWithColumnFamilies(
|
||||
|
||||
@@ -506,6 +506,8 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
table_options.prepopulate_block_cache =
|
||||
BlockBasedTableOptions::PrepopulateBlockCache::kFlushOnly;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
// Include a compression dictionary block
|
||||
options.compression_opts.max_dict_bytes = 123;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
@@ -537,6 +539,9 @@ TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT));
|
||||
}
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_FILTER_MISS));
|
||||
|
||||
// Including compression dict
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_MISS));
|
||||
}
|
||||
|
||||
// Verify compaction not counted
|
||||
|
||||
+126
-96
@@ -9434,104 +9434,134 @@ TEST_F(DBCompactionTest, CompactionWithChecksumHandoffManifest2) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, FIFOChangeTemperature) {
|
||||
for (bool write_time_default : {false, true}) {
|
||||
SCOPED_TRACE("write time default? " + std::to_string(write_time_default));
|
||||
for (bool should_allow_trivial_copy : {false, true}) {
|
||||
for (bool write_time_default : {false, true}) {
|
||||
int32_t before_compaction_calls = 0;
|
||||
int32_t after_compaction_calls = 0;
|
||||
if (should_allow_trivial_copy) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TriviaCopyBeforeCompaction",
|
||||
[&](void*) { ++before_compaction_calls; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:TriviaCopyAfterCompaction",
|
||||
[&](void*) { ++after_compaction_calls; });
|
||||
} else {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:BeforeCompaction",
|
||||
[&](void*) { ++before_compaction_calls; });
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.file_temperature_age_thresholds = {{Temperature::kCold, 1000}};
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
if (write_time_default) {
|
||||
options.default_write_temperature = Temperature::kWarm;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
[&](void*) { ++after_compaction_calls; });
|
||||
}
|
||||
|
||||
SCOPED_TRACE("write time default? " + std::to_string(write_time_default));
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.num_levels = 1;
|
||||
options.max_open_files = -1;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.create_if_missing = true;
|
||||
CompactionOptionsFIFO fifo_options;
|
||||
fifo_options.file_temperature_age_thresholds = {
|
||||
{Temperature::kCold, 1000}};
|
||||
fifo_options.max_table_files_size = 100000000;
|
||||
fifo_options.allow_trivial_copy_when_change_temperature =
|
||||
should_allow_trivial_copy;
|
||||
fifo_options.trivial_copy_buffer_size = 4096;
|
||||
options.compaction_options_fifo = fifo_options;
|
||||
env_->SetMockSleep();
|
||||
if (write_time_default) {
|
||||
options.default_write_temperature = Temperature::kWarm;
|
||||
}
|
||||
// Should be ignored (TODO: fail?)
|
||||
options.last_level_temperature = Temperature::kHot;
|
||||
Reopen(options);
|
||||
|
||||
int total_cold = 0;
|
||||
int total_warm = 0;
|
||||
int total_hot = 0;
|
||||
int total_unknown = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
} else if (temperature == Temperature::kWarm) {
|
||||
total_warm++;
|
||||
} else if (temperature == Temperature::kHot) {
|
||||
total_hot++;
|
||||
} else {
|
||||
assert(temperature == Temperature::kUnknown);
|
||||
total_unknown++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// First two L0 files both become eligible for temperature change
|
||||
// compaction They should be compacted one-by-one.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(1200);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
if (write_time_default) {
|
||||
// Also test dynamic option change
|
||||
ASSERT_OK(db_->SetOptions({{"default_write_temperature", "kHot"}}));
|
||||
}
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
if (write_time_default) {
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[1].temperature);
|
||||
// Includes obsolete/deleted files moved to cold
|
||||
ASSERT_EQ(total_warm, 3);
|
||||
ASSERT_EQ(total_hot, 1);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 0);
|
||||
} else {
|
||||
ASSERT_EQ(Temperature::kUnknown,
|
||||
metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown,
|
||||
metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(total_warm, 0);
|
||||
ASSERT_EQ(total_hot, 0);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 4);
|
||||
}
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_cold);
|
||||
|
||||
ASSERT_EQ(2, before_compaction_calls);
|
||||
ASSERT_EQ(2, after_compaction_calls);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
// Should be ignored (TODO: fail?)
|
||||
options.last_level_temperature = Temperature::kHot;
|
||||
Reopen(options);
|
||||
|
||||
int total_cold = 0;
|
||||
int total_warm = 0;
|
||||
int total_hot = 0;
|
||||
int total_unknown = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
|
||||
Temperature temperature = *(static_cast<Temperature*>(arg));
|
||||
if (temperature == Temperature::kCold) {
|
||||
total_cold++;
|
||||
} else if (temperature == Temperature::kWarm) {
|
||||
total_warm++;
|
||||
} else if (temperature == Temperature::kHot) {
|
||||
total_hot++;
|
||||
} else {
|
||||
assert(temperature == Temperature::kUnknown);
|
||||
total_unknown++;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// First two L0 files both become eligible for temperature change compaction
|
||||
// They should be compacted one-by-one.
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(1200);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
if (write_time_default) {
|
||||
// Also test dynamic option change
|
||||
ASSERT_OK(db_->SetOptions({{"default_write_temperature", "kHot"}}));
|
||||
}
|
||||
|
||||
ASSERT_OK(Put(Key(0), "value1"));
|
||||
env_->MockSleepForSeconds(800);
|
||||
ASSERT_OK(Put(Key(2), "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ColumnFamilyMetaData metadata;
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(4, metadata.file_count);
|
||||
if (write_time_default) {
|
||||
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[1].temperature);
|
||||
// Includes obsolete/deleted files moved to cold
|
||||
ASSERT_EQ(total_warm, 3);
|
||||
ASSERT_EQ(total_hot, 1);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 0);
|
||||
} else {
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[0].temperature);
|
||||
ASSERT_EQ(Temperature::kUnknown, metadata.levels[0].files[1].temperature);
|
||||
ASSERT_EQ(total_warm, 0);
|
||||
ASSERT_EQ(total_hot, 0);
|
||||
// Includes non-SST DB files
|
||||
ASSERT_GT(total_unknown, 4);
|
||||
}
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
|
||||
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
|
||||
ASSERT_EQ(2, total_cold);
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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();
|
||||
|
||||
@@ -3504,6 +3504,63 @@ TEST_F(DBFlushTest, DBStuckAfterAtomicFlushError) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, VerifyOutputRecordCount) {
|
||||
for (bool use_plain_table : {false, true}) {
|
||||
Options options = CurrentOptions();
|
||||
options.flush_verify_memtable_count = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
DestroyAndReopen(options);
|
||||
// Verify flush output record count verification in different table
|
||||
// formats
|
||||
if (use_plain_table) {
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
}
|
||||
|
||||
// Verify that flush output record count verification does not produce false
|
||||
// positives.
|
||||
ASSERT_OK(Merge("k0", "v1"));
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
ASSERT_OK(SingleDelete("k2"));
|
||||
ASSERT_OK(Delete("k2"));
|
||||
ASSERT_OK(Delete("k3"));
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), "k1", "k3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Verify that flush output record count verification catch corruption
|
||||
DestroyAndReopen(options);
|
||||
if (use_plain_table) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PlainTableBuilder::Add::skip",
|
||||
[&](void* skip) { *(bool*)skip = true; });
|
||||
|
||||
} else {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::Add::skip",
|
||||
[&](void* skip) { *(bool*)skip = true; });
|
||||
}
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
const char* expect =
|
||||
"Number of keys in flush output SST files does not match";
|
||||
|
||||
// 1. During DB open flush
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), expect));
|
||||
|
||||
// 2. During regular flush
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v1"));
|
||||
s = Flush();
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(), expect));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+124
-103
@@ -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,14 @@ 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);
|
||||
// TODO(cbi): Add support for `memtable_op_scan_flush_trigger` for tailing
|
||||
// iterator. This requires refreshing DBIter's pointer to active_mem when
|
||||
// tailing iterator refreshes to new memtable internally.
|
||||
result = DBIter::NewIter(env_, read_options, cfd->ioptions(),
|
||||
sv->mutable_cf_options, cfd->user_comparator(),
|
||||
iter, sv->current, kMaxSequenceNumber,
|
||||
/*read_callback=*/nullptr, /*active_mem=*/nullptr,
|
||||
cfh, /*expose_blob_index=*/false);
|
||||
} else {
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
|
||||
@@ -3934,18 +3971,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(
|
||||
@@ -4069,14 +4097,12 @@ Status DBImpl::NewIterators(
|
||||
auto iter = new ForwardIterator(this, read_options, cf_sv_pair.cfd,
|
||||
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));
|
||||
iterators->push_back(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*/, /*active_mem=*/nullptr, cf_sv_pair.cfh));
|
||||
}
|
||||
} else {
|
||||
for (const auto& cf_sv_pair : cf_sv_pairs) {
|
||||
@@ -5435,12 +5461,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
|
||||
|
||||
+172
-134
@@ -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,
|
||||
@@ -2546,7 +2577,7 @@ class DBImpl : public DB {
|
||||
bool ShouldntRunManualCompaction(ManualCompactionState* m);
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
|
||||
void UpdateDeletionCompactionStats(const std::unique_ptr<Compaction>& c);
|
||||
void UpdateFIFOCompactionStatus(const std::unique_ptr<Compaction>& c);
|
||||
|
||||
// May open and read table files for table property.
|
||||
// Should not be called while holding mutex_.
|
||||
@@ -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 {
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/options.h"
|
||||
@@ -168,7 +170,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 +226,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 +514,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 +530,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
|
||||
@@ -3786,11 +3788,251 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
c->num_input_files(0));
|
||||
if (status.ok() && io_s.ok()) {
|
||||
UpdateDeletionCompactionStats(c);
|
||||
UpdateFIFOCompactionStatus(c);
|
||||
}
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:AfterCompaction",
|
||||
c->column_family_data());
|
||||
} else if (c->is_trivial_copy_compaction()) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction:TriviaCopyBeforeCompaction",
|
||||
c->column_family_data());
|
||||
assert(c->num_input_files(1) == 0);
|
||||
assert(c->column_family_data()->ioptions().compaction_style ==
|
||||
kCompactionStyleFIFO);
|
||||
assert(c->compaction_reason() == CompactionReason::kChangeTemperature);
|
||||
|
||||
compaction_job_stats.num_input_files = c->num_input_files(0);
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
compaction_job_stats, job_context->job_id);
|
||||
|
||||
std::vector<FileMetaData> out_files;
|
||||
for (const auto& in_file : *c->inputs(0)) {
|
||||
const uint64_t out_file_number = versions_->NewFileNumber();
|
||||
const std::string in_fname =
|
||||
TableFileName(c->immutable_options().cf_paths,
|
||||
in_file->fd.GetNumber(), in_file->fd.GetPathId());
|
||||
const std::string out_fname =
|
||||
TableFileName(c->immutable_options().cf_paths, out_file_number,
|
||||
c->output_path_id());
|
||||
|
||||
// TODO (mikechuang): Currently skip calling
|
||||
// EventHelpers::NotifyTableFileCreationStarted for the trivial copy.
|
||||
// Since it's a trivial copy we should ideally use the exact
|
||||
// TableProperties from the input file but that will break some existing
|
||||
// stress tests. For now skip the listener call for the FIFO
|
||||
// kChangeTemperature trivial copy move.
|
||||
|
||||
int64_t tmp_current_time = 0;
|
||||
auto get_time_status =
|
||||
immutable_db_options_.clock->GetCurrentTime(&tmp_current_time);
|
||||
if (!get_time_status.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] WARNING: Failed to get current time %s "
|
||||
"status=%s",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
get_time_status.ToString().c_str());
|
||||
}
|
||||
uint64_t out_file_creation_time = static_cast<uint64_t>(tmp_current_time);
|
||||
|
||||
FileOptions copied_file_options = file_options_;
|
||||
copied_file_options.temperature = c->output_temperature();
|
||||
std::unique_ptr<WritableFileWriter> dest_writer;
|
||||
{
|
||||
std::unique_ptr<FSWritableFile> dest_file;
|
||||
IOStatus writable_file_io_status =
|
||||
immutable_db_options_.fs.get()->NewWritableFile(
|
||||
out_fname, copied_file_options, &dest_file, nullptr /* dbg */);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"NewWritableFile::FileOptions.temperature",
|
||||
const_cast<Temperature*>(&copied_file_options.temperature));
|
||||
if (!writable_file_io_status.ok()) {
|
||||
io_s = writable_file_io_status;
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Error: Abort trivial copy compaction, failed to open "
|
||||
"NewWritableFile %s\n"
|
||||
" out_fname=%s, temperature=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(), out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
io_s.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
FileTypeSet tmp_set = immutable_db_options_.checksum_handoff_file_types;
|
||||
dest_writer.reset(new WritableFileWriter(
|
||||
std::move(dest_file), out_fname, copied_file_options,
|
||||
immutable_db_options_.clock, io_tracer_,
|
||||
immutable_db_options_.stats, Histograms::SST_WRITE_MICROS,
|
||||
c->immutable_options().listeners,
|
||||
immutable_db_options_.file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kTableFile), false));
|
||||
}
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Started copying from: %s\n"
|
||||
" temperature=%s, to: %s, temperature=%s, buffer_size=%" PRIu64,
|
||||
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
c->mutable_cf_options()
|
||||
.compaction_options_fifo.trivial_copy_buffer_size);
|
||||
// Add IO_LOW HINT for compaction
|
||||
IOOptions copy_files_compaction_io_options;
|
||||
copy_files_compaction_io_options.rate_limiter_priority =
|
||||
Env::IOPriority::IO_LOW;
|
||||
copy_files_compaction_io_options.type = IOType::kData;
|
||||
copy_files_compaction_io_options.io_activity =
|
||||
Env::IOActivity::kCompaction;
|
||||
|
||||
IOStatus copy_file_io_status = CopyFile(
|
||||
immutable_db_options_.fs.get() /* fileSystem */,
|
||||
in_fname /* source */, in_file->temperature /* src_temp_hint */,
|
||||
dest_writer /* dest_writer */, 0 /* size */, true /* use_fsync */,
|
||||
io_tracer_ /* io_tracer*/,
|
||||
c->mutable_cf_options()
|
||||
.compaction_options_fifo
|
||||
.trivial_copy_buffer_size /* max_read_buffer_size
|
||||
*/
|
||||
,
|
||||
copy_files_compaction_io_options /* readIOOptions */,
|
||||
copy_files_compaction_io_options /* writeIOOptions */);
|
||||
if (dest_writer) {
|
||||
IOOptions close_files_compaction_io_options;
|
||||
close_files_compaction_io_options.rate_limiter_priority =
|
||||
Env::IOPriority::IO_LOW;
|
||||
close_files_compaction_io_options.type = IOType::kData;
|
||||
close_files_compaction_io_options.io_activity =
|
||||
Env::IOActivity::kCompaction;
|
||||
// Close the dest_write
|
||||
io_s = dest_writer->Close(close_files_compaction_io_options);
|
||||
if (!io_s.ok()) {
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Failed to close the writer. Failed to copy from: %s\n"
|
||||
" temperature=%s, to=%s, temperature=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
io_s.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
io_s = copy_file_io_status;
|
||||
|
||||
if (!io_s.ok()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Failed to copy from: %s\n"
|
||||
" temperature=%s, to=%s, temperature=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
io_s.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] Successfully copying from: %s\n"
|
||||
" temperature=%s, to=%s, temperature=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
out_fname.c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
io_s.ToString().c_str());
|
||||
|
||||
FileMetaData out_file_metadata{
|
||||
out_file_number,
|
||||
c->output_path_id(),
|
||||
in_file->fd.GetFileSize(),
|
||||
in_file->smallest,
|
||||
in_file->largest,
|
||||
in_file->fd.smallest_seqno,
|
||||
in_file->fd.largest_seqno,
|
||||
false /* marked_for_compact */,
|
||||
c->output_temperature() /* temperature */,
|
||||
in_file->oldest_blob_file_number,
|
||||
in_file->oldest_ancester_time,
|
||||
out_file_creation_time,
|
||||
c->MinInputFileEpochNumber(),
|
||||
dest_writer->GetFileChecksum(),
|
||||
dest_writer->GetFileChecksumFuncName(),
|
||||
in_file->unique_id,
|
||||
in_file->compensated_range_deletion_size,
|
||||
in_file->tail_size,
|
||||
in_file->user_defined_timestamps_persisted};
|
||||
|
||||
out_files.push_back(std::move(out_file_metadata));
|
||||
}
|
||||
|
||||
// Update version set
|
||||
if (status.ok() && io_s.ok()) {
|
||||
// NOTE: ChangeTemperature should only copy one file at one file
|
||||
// hence *c->inputs(0) == out_files.size() == 1 if copy succeeded
|
||||
assert(c->inputs(0)->size() == 1);
|
||||
assert(out_files.size() == 1);
|
||||
|
||||
auto out_file_metadata_it = out_files.begin();
|
||||
for (const auto& in_file : *c->inputs(0)) {
|
||||
if (out_file_metadata_it == out_files.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
c->edit()->DeleteFile(c->level(), in_file->fd.GetNumber());
|
||||
c->edit()->AddFile(c->level(), *out_file_metadata_it);
|
||||
++out_file_metadata_it;
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(
|
||||
c->column_family_data(), read_options, write_options, c->edit(),
|
||||
&mutex_, directories_.GetDbDir(),
|
||||
/*new_descriptor_log=*/false, /*column_family_options=*/nullptr,
|
||||
[&c, &compaction_released](const Status& s) {
|
||||
c->ReleaseCompactionFiles(s);
|
||||
compaction_released = true;
|
||||
});
|
||||
}
|
||||
|
||||
// TODO (mikechuang): Currently skip calling
|
||||
// EventHelper::LogAndNotifyTableFileCreationFinished for the trivial copy.
|
||||
// Since it's a trivial copy we should ideally use the exact TableProperties
|
||||
// from the input file but that will break some existing stress tests. For
|
||||
// now skip the listener call for the FIFO kChangeTemperature trivial copy
|
||||
// move.
|
||||
|
||||
if (io_s.ok()) {
|
||||
io_s = versions_->io_status();
|
||||
}
|
||||
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), job_context->superversion_contexts.data());
|
||||
if (status.ok() && io_s.ok()) {
|
||||
UpdateFIFOCompactionStatus(c);
|
||||
} else {
|
||||
for (const auto& in_file : *c->inputs(0)) {
|
||||
const std::string in_fname =
|
||||
TableFileName(c->immutable_options().cf_paths,
|
||||
in_file->fd.GetNumber(), in_file->fd.GetPathId());
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] Failed to do trvial copy compaction: %s"
|
||||
" temperature=%s, to temperature=%s, status=%s, io_status=%s",
|
||||
c->column_family_data()->GetName().c_str(), in_fname.c_str(),
|
||||
temperature_to_string[in_file->temperature].c_str(),
|
||||
temperature_to_string[c->output_temperature()].c_str(),
|
||||
status.ToString().c_str(), io_s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
*made_progress = true;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::BackgroundCompaction:TriviaCopyAfterCompaction",
|
||||
c->column_family_data());
|
||||
} else if (!trivial_move_disallowed && c->IsTrivialMove()) {
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction:TrivialMove");
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:BeforeCompaction",
|
||||
@@ -4176,8 +4418,7 @@ bool DBImpl::MCOverlap(ManualCompactionState* m, ManualCompactionState* m1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DBImpl::UpdateDeletionCompactionStats(
|
||||
const std::unique_ptr<Compaction>& c) {
|
||||
void DBImpl::UpdateFIFOCompactionStatus(const std::unique_ptr<Compaction>& c) {
|
||||
if (c == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -4191,6 +4432,9 @@ void DBImpl::UpdateDeletionCompactionStats(
|
||||
case CompactionReason::kFIFOTtl:
|
||||
RecordTick(stats_, FIFO_TTL_COMPACTIONS);
|
||||
break;
|
||||
case CompactionReason::kChangeTemperature:
|
||||
RecordTick(stats_, FIFO_CHANGE_TEMPERATURE_COMPACTIONS);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
@@ -84,6 +84,7 @@ void DBImpl::TEST_GetFilesMetaData(
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_Current_Manifest_FileNo() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return versions_->manifest_file_number();
|
||||
}
|
||||
|
||||
@@ -224,13 +225,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'
|
||||
|
||||
+66
-41
@@ -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;
|
||||
}
|
||||
@@ -2000,6 +2000,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
const size_t ts_sz = ucmp->timestamp_size();
|
||||
const bool logical_strip_timestamp =
|
||||
ts_sz > 0 && !cfd->ioptions().persist_user_defined_timestamps;
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats flush_stats(CompactionReason::kFlush,
|
||||
1 /* count */);
|
||||
{
|
||||
ScopedArenaPtr<InternalIterator> iter(
|
||||
logical_strip_timestamp
|
||||
@@ -2072,19 +2075,20 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
kMaxSequenceNumber);
|
||||
Version* version = cfd->current();
|
||||
version->Ref();
|
||||
uint64_t num_input_entries = 0;
|
||||
s = BuildTable(dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(),
|
||||
iter.get(), std::move(range_del_iters), &meta,
|
||||
&blob_file_additions, snapshot_seqs, earliest_snapshot,
|
||||
earliest_write_conflict_snapshot, kMaxSequenceNumber,
|
||||
snapshot_checker, paranoid_file_checks,
|
||||
cfd->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_,
|
||||
job_id, nullptr /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
&num_input_entries);
|
||||
TableProperties temp_table_proerties;
|
||||
s = BuildTable(
|
||||
dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::move(range_del_iters), &meta, &blob_file_additions,
|
||||
snapshot_seqs, earliest_snapshot, earliest_write_conflict_snapshot,
|
||||
kMaxSequenceNumber, snapshot_checker, paranoid_file_checks,
|
||||
cfd->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kRecovery,
|
||||
nullptr /* seqno_to_time_mapping */, &event_logger_, job_id,
|
||||
&temp_table_proerties /* table_properties */, write_hint,
|
||||
nullptr /*full_history_ts_low*/, &blob_callback_, version,
|
||||
nullptr /* memtable_payload_bytes */,
|
||||
nullptr /* memtable_garbage_bytes */, &flush_stats);
|
||||
version->Unref();
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
@@ -2100,10 +2104,31 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
}
|
||||
|
||||
uint64_t total_num_entries = mem->NumEntries();
|
||||
if (s.ok() && total_num_entries != num_input_entries) {
|
||||
if (s.ok() && total_num_entries != flush_stats.num_input_records) {
|
||||
std::string msg = "Expected " + std::to_string(total_num_entries) +
|
||||
" entries in memtable, but read " +
|
||||
std::to_string(num_input_entries);
|
||||
std::to_string(flush_stats.num_input_records);
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush during recover: %s",
|
||||
cfd->GetName().c_str(), job_id, msg.c_str());
|
||||
if (immutable_db_options_.flush_verify_memtable_count) {
|
||||
s = Status::Corruption(msg);
|
||||
}
|
||||
}
|
||||
// Only verify on table with format collects table properties
|
||||
const auto& mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
if (s.ok() &&
|
||||
(mutable_cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kBlockBasedTableName()) ||
|
||||
mutable_cf_options.table_factory->IsInstanceOf(
|
||||
TableFactory::kPlainTableName())) &&
|
||||
flush_stats.num_output_records != temp_table_proerties.num_entries) {
|
||||
std::string msg =
|
||||
"Number of keys in flush output SST files does not match "
|
||||
"number of keys added to the table. Expected " +
|
||||
std::to_string(flush_stats.num_output_records) + " but there are " +
|
||||
std::to_string(temp_table_proerties.num_entries) +
|
||||
" in output SST files";
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush during recover: %s",
|
||||
cfd->GetName().c_str(), job_id, msg.c_str());
|
||||
@@ -2151,25 +2176,25 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
stats.micros = immutable_db_options_.clock->NowMicros() - start_micros;
|
||||
flush_stats.micros = immutable_db_options_.clock->NowMicros() - start_micros;
|
||||
|
||||
if (has_output) {
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
flush_stats.bytes_written = meta.fd.GetFileSize();
|
||||
flush_stats.num_output_files = 1;
|
||||
}
|
||||
|
||||
const auto& blobs = edit->GetBlobFileAdditions();
|
||||
for (const auto& blob : blobs) {
|
||||
stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
flush_stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
flush_stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
|
||||
cfd->internal_stats()->AddCompactionStats(level, Env::Priority::USER, stats);
|
||||
cfd->internal_stats()->AddCompactionStats(level, Env::Priority::USER,
|
||||
flush_stats);
|
||||
cfd->internal_stats()->AddCFStats(
|
||||
InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written + stats.bytes_written_blob);
|
||||
flush_stats.bytes_written + flush_stats.bytes_written_blob);
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, meta.fd.GetFileSize());
|
||||
return s;
|
||||
}
|
||||
@@ -2449,18 +2474,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 +2498,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,
|
||||
@@ -735,13 +728,13 @@ Status DBImplSecondary::TryCatchUpWithPrimary() {
|
||||
// instance
|
||||
if (s.ok()) {
|
||||
s = FindAndRecoverLogFiles(&cfds_changed, &job_context);
|
||||
}
|
||||
if (s.IsPathNotFound()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Secondary tries to read WAL, but WAL file(s) have already "
|
||||
"been purged by primary.");
|
||||
s = Status::OK();
|
||||
if (s.IsPathNotFound()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Secondary tries to read WAL, but WAL file(s) have already "
|
||||
"been purged by primary.");
|
||||
s = Status::OK();
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
for (auto cfd : cfds_changed) {
|
||||
@@ -916,7 +909,7 @@ Status DBImplSecondary::CompactWithoutInstallation(
|
||||
ROCKS_LOG_ERROR(
|
||||
immutable_db_options_.info_log,
|
||||
"GetCompactionInputsFromFileNumbers() failed - %s.\n DebugString: %s",
|
||||
s.ToString().c_str(), version->DebugString().c_str());
|
||||
s.ToString().c_str(), version->DebugString(/*hex=*/true).c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+275
-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,12 @@ 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);
|
||||
RecordTick(stats_, NUMBER_WBWI_INGEST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,9 +947,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 +972,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 +989,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 +1061,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 +1079,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 +1116,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 +1150,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 +1226,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 +1239,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 +1256,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 +1356,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 +1409,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 +1478,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 +1503,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 +1573,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 +1594,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 +1650,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 +1667,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 +1675,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 +1685,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 +1717,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 +1732,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 +1749,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 +1780,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 +1798,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 +1815,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 +1833,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 +1848,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 +1858,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 +1892,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 +1907,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 +1989,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 +2001,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 +2035,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 +2512,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 +2552,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 +2600,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 +2626,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 +2661,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 +2696,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 +2713,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 +2732,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();
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/testutil.h"
|
||||
|
||||
+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,
|
||||
ReadOnlyMemTable* active_mem,
|
||||
ColumnFamilyHandleImpl* cfh = nullptr,
|
||||
bool expose_blob_index = false,
|
||||
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* const active_mem_;
|
||||
const SequenceNumber memtable_seqno_lb_;
|
||||
const 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,12 +528,11 @@ 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,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, /*version=*/nullptr, sequence,
|
||||
nullptr /*read_callback*/, /*active_mem=*/nullptr));
|
||||
}
|
||||
|
||||
// Do a random operation. It's important to do it on ref_it
|
||||
|
||||
+168
-228
@@ -259,11 +259,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -294,11 +293,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -322,11 +320,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -356,11 +353,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -393,11 +389,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -425,11 +420,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableCount);
|
||||
@@ -465,11 +459,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -492,11 +485,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -517,11 +509,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -554,11 +545,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableCount);
|
||||
@@ -586,11 +576,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -631,11 +620,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -664,11 +652,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -696,11 +683,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
@@ -710,11 +696,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
@@ -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 */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -782,11 +766,11 @@ 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 */));
|
||||
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -820,11 +804,11 @@ 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 */));
|
||||
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -851,11 +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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -886,11 +869,11 @@ 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 */));
|
||||
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
@@ -906,11 +889,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 */, 200 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -944,11 +926,11 @@ 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 */));
|
||||
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -981,11 +963,11 @@ 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 */));
|
||||
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1033,11 +1015,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1081,11 +1062,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1127,11 +1107,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1167,11 +1146,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1204,11 +1182,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1236,11 +1213,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1275,11 +1251,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1314,11 +1289,11 @@ 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 */));
|
||||
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1369,11 +1344,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1412,11 +1386,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 */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1463,11 +1437,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 */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1493,11 +1466,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 */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1519,11 +1491,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 */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1545,11 +1516,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 */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1580,11 +1550,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1605,11 +1574,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1630,11 +1598,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1655,11 +1622,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1680,11 +1646,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1705,11 +1670,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1730,11 +1694,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1753,11 +1716,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -1785,11 +1747,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1810,11 +1771,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1835,11 +1795,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1860,11 +1819,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
ASSERT_OK(db_iter->status());
|
||||
@@ -1881,11 +1839,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1906,11 +1863,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1931,11 +1887,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1976,11 +1931,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2013,11 +1967,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2056,11 +2009,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2099,11 +2051,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2147,11 +2098,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2196,11 +2146,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2239,11 +2188,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2288,11 +2236,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2338,11 +2285,10 @@ 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 */));
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2371,11 +2317,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 */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2403,11 +2348,11 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
internal_iter->AddMerge("d", "merge_6");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
DBIter::NewIter(env_, ro, ImmutableOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
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 */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->Seek("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2512,10 +2456,10 @@ 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 */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -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 */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2571,10 +2514,10 @@ 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 */));
|
||||
10 /* sequence */, nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -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 */, /*active_mem=*/nullptr));
|
||||
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 */, /*active_mem=*/nullptr));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2680,11 +2623,12 @@ 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 */,
|
||||
/*active_mem=*/nullptr));
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
@@ -3120,11 +3064,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 */, /*active_mem=*/nullptr));
|
||||
|
||||
int skipped_keys = 0;
|
||||
|
||||
@@ -3157,11 +3100,11 @@ 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(
|
||||
env_, ro, ImmutableOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
DBIter::NewIter(env_, ro, ImmutableOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
nullptr /* read_callback */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
if (i == kNumKeys + 1) {
|
||||
@@ -3197,11 +3140,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 */, /*active_mem=*/nullptr));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
for (int i = kNumKeys; i >= kLowerBound; --i) {
|
||||
@@ -3226,11 +3168,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 */, /*active_mem=*/nullptr));
|
||||
|
||||
auto before_lower_bound_str = std::to_string(kLowerBound - 1);
|
||||
Slice before_lower_bound(lower_bound_str);
|
||||
@@ -3252,11 +3193,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 */, /*active_mem=*/nullptr));
|
||||
|
||||
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) {
|
||||
|
||||
@@ -377,6 +377,8 @@ TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
NewBloomFilterPolicy(kBloomBitsPerKey, false));
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
// The checks assume kTableCount number of files
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -567,7 +569,7 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
options.target_file_size_base = 8192;
|
||||
options.max_bytes_for_level_base = 10000;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
// This ensures there no compaction happening when we call GetProperty().
|
||||
// The checks assume kTableCount number of files
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
+104
-8
@@ -144,6 +144,103 @@ TEST_F(DBTest, MockEnvTest) {
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(DBTest, RequestIdPlumbingTest) {
|
||||
// test that request_id is passed to the filesystem, from
|
||||
// ReadOptions to IODebugContext
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
|
||||
// Create a mock environment to capture IODebugContext during reads
|
||||
const std::string* captured_request_id_dbg;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read:IODebugContext", [&](void* arg) {
|
||||
IODebugContext* dbg = static_cast<IODebugContext*>(arg);
|
||||
if (dbg == nullptr) {
|
||||
captured_request_id_dbg = nullptr;
|
||||
} else {
|
||||
captured_request_id_dbg = dbg->request_id;
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("k1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// test request_id plumbing during a get
|
||||
{
|
||||
const std::string test_request_id = "test_request_id_123";
|
||||
ReadOptions read_opts;
|
||||
read_opts.request_id = &test_request_id;
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(read_opts, "k1", &value));
|
||||
|
||||
// Verify the request_id was propagated to the file system
|
||||
ASSERT_NE(captured_request_id_dbg, nullptr);
|
||||
ASSERT_EQ(*captured_request_id_dbg, test_request_id);
|
||||
}
|
||||
|
||||
captured_request_id_dbg = nullptr;
|
||||
|
||||
// test request_id plumbing during iterator seek
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
{
|
||||
ReadOptions read_opts;
|
||||
const std::string request_id = "test_request_id_456";
|
||||
read_opts.request_id = &request_id;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
iter->Seek("k2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
// Verify the request_id was propagated to the file system
|
||||
ASSERT_NE(captured_request_id_dbg, nullptr);
|
||||
ASSERT_EQ(*captured_request_id_dbg, request_id);
|
||||
}
|
||||
|
||||
// test request_id plumbing during multiget
|
||||
captured_request_id_dbg = nullptr;
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::MultiRead:IODebugContext", [&](void* arg) {
|
||||
IODebugContext* dbg = static_cast<IODebugContext*>(arg);
|
||||
if (dbg == nullptr) {
|
||||
captured_request_id_dbg = nullptr;
|
||||
} else {
|
||||
captured_request_id_dbg = dbg->request_id;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("k3", "v3"));
|
||||
ASSERT_OK(Put("k4", "v4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
{
|
||||
ReadOptions read_opts;
|
||||
const std::string multiget_request_id = "test_request_id_789";
|
||||
read_opts.request_id = &multiget_request_id;
|
||||
|
||||
std::vector<std::string> values;
|
||||
std::vector<Slice> keys = {Slice("k3"), Slice("k4")};
|
||||
|
||||
values.resize(keys.size());
|
||||
|
||||
std::vector<ColumnFamilyHandle*> cfhs(keys.size(),
|
||||
db_->DefaultColumnFamily());
|
||||
db_->MultiGet(read_opts, cfhs, keys, &values);
|
||||
|
||||
ASSERT_NE(captured_request_id_dbg, nullptr);
|
||||
ASSERT_EQ(*captured_request_id_dbg, multiget_request_id);
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MemEnvTest) {
|
||||
std::unique_ptr<Env> env{NewMemEnv(Env::Default())};
|
||||
Options options;
|
||||
@@ -3351,11 +3448,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 +3504,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 +3543,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:
|
||||
@@ -5463,7 +5560,6 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
options.max_bytes_for_level_multiplier = 8;
|
||||
options.max_background_compactions = 1;
|
||||
options.num_levels = 5;
|
||||
options.compaction_verify_record_count = false;
|
||||
std::shared_ptr<mock::MockTableFactory> mtf(new mock::MockTableFactory);
|
||||
options.table_factory = mtf;
|
||||
|
||||
@@ -6419,7 +6515,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));
|
||||
}
|
||||
|
||||
+2
-4
@@ -1722,16 +1722,14 @@ TEST_P(CompressionFailuresTest, CompressionFailures) {
|
||||
});
|
||||
} else if (compression_failure_type_ == kTestDecompressionFail) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UncompressBlockData:TamperWithReturnValue", [](void* arg) {
|
||||
"DecompressBlockData:TamperWithReturnValue", [](void* arg) {
|
||||
Status* ret = static_cast<Status*>(arg);
|
||||
ASSERT_OK(*ret);
|
||||
*ret = Status::Corruption("kTestDecompressionFail");
|
||||
});
|
||||
} else if (compression_failure_type_ == kTestDecompressionCorruption) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UncompressBlockData:"
|
||||
"TamperWithDecompressionOutput",
|
||||
[](void* arg) {
|
||||
"DecompressBlockData:TamperWithDecompressionOutput", [](void* arg) {
|
||||
BlockContents* contents = static_cast<BlockContents*>(arg);
|
||||
// Ensure uncompressed data != original data
|
||||
const size_t len = contents->data.size() + 1;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,55 +260,6 @@ TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
s = sst_file_writer.DeleteRange(Key(100), Key(200));
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::IngestExternalFile:AfterIncIngestFileCounter",
|
||||
"ExternalSSTFileBasicTest.LiveWriteStart"},
|
||||
{"WriteThread::JoinBatchGroup:Wait",
|
||||
"DBImpl::IngestExternalFile:AfterIncIngestFileCounter:2"},
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
PerfContext* write_thread_perf_context;
|
||||
std::thread write_thread([&] {
|
||||
TEST_SYNC_POINT("ExternalSSTFileBasicTest.LiveWriteStart");
|
||||
SetPerfLevel(kEnableWait);
|
||||
write_thread_perf_context = get_perf_context();
|
||||
write_thread_perf_context->Reset();
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "bar", "v2"));
|
||||
ASSERT_GT(write_thread_perf_context->write_thread_wait_nanos, 0);
|
||||
// Test sync points were used to make sure this live write enter write
|
||||
// thread after the file ingestion entered write thread. So by the time this
|
||||
// live write finishes, the latest seqno is 1 means file ingestion used
|
||||
// seqno 0.
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 1U);
|
||||
});
|
||||
|
||||
// Add file using file path
|
||||
SetPerfLevel(kEnableTimeExceptForMutex);
|
||||
PerfContext* perf_ctx = get_perf_context();
|
||||
perf_ctx->Reset();
|
||||
s = DeprecatedAddFile({file1});
|
||||
ASSERT_GT(perf_context.file_ingestion_nanos, 0);
|
||||
ASSERT_GT(perf_context.file_ingestion_blocking_live_writes_nanos, 0);
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
|
||||
write_thread.join();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Re-ingest the file just to check the perf context not enabled at and below
|
||||
// kEnableWait.
|
||||
SetPerfLevel(kEnableWait);
|
||||
perf_ctx->Reset();
|
||||
IngestExternalFileOptions opts;
|
||||
opts.allow_global_seqno = true;
|
||||
opts.allow_blocking_flush = true;
|
||||
ASSERT_OK(db_->IngestExternalFile({file1}, opts));
|
||||
ASSERT_EQ(perf_context.file_ingestion_nanos, 0);
|
||||
ASSERT_EQ(perf_context.file_ingestion_blocking_live_writes_nanos, 0);
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
@@ -2693,6 +2644,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 &&
|
||||
|
||||
+50
-27
@@ -868,7 +868,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ts_sz > 0 && !cfd_->ioptions().persist_user_defined_timestamps;
|
||||
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats flush_stats(CompactionReason::kFlush,
|
||||
1 /* count**/);
|
||||
{
|
||||
auto write_hint = base_->storage_info()->CalculateSSTWriteHint(
|
||||
/*level=*/0, db_options_.calculate_sst_write_lifetime_hint_set);
|
||||
@@ -887,7 +889,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
ro.total_order_seek = true;
|
||||
ro.io_activity = Env::IOActivity::kFlush;
|
||||
Arena arena;
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
uint64_t total_num_input_entries = 0, total_num_deletes = 0;
|
||||
uint64_t total_data_size = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
uint64_t total_num_range_deletes = 0;
|
||||
@@ -901,9 +903,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,
|
||||
@@ -922,7 +924,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
total_num_entries += m->NumEntries();
|
||||
total_num_input_entries += m->NumEntries();
|
||||
total_num_deletes += m->NumDeletion();
|
||||
total_data_size += m->GetDataSize();
|
||||
total_memory_usage += m->ApproximateMemoryUsage();
|
||||
@@ -934,11 +936,12 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// "Write Buffer Full", should make update flush_reason_ accordingly.
|
||||
event_logger_->Log() << "job" << job_context_->job_id << "event"
|
||||
<< "flush_started" << "num_memtables" << mems_.size()
|
||||
<< "num_entries" << total_num_entries << "num_deletes"
|
||||
<< total_num_deletes << "total_data_size"
|
||||
<< total_data_size << "memory_usage"
|
||||
<< total_memory_usage << "num_range_deletes"
|
||||
<< total_num_range_deletes << "flush_reason"
|
||||
<< "total_num_input_entries" << total_num_input_entries
|
||||
<< "num_deletes" << total_num_deletes
|
||||
<< "total_data_size" << total_data_size
|
||||
<< "memory_usage" << total_memory_usage
|
||||
<< "num_range_deletes" << total_num_range_deletes
|
||||
<< "flush_reason"
|
||||
<< GetFlushReasonString(flush_reason_);
|
||||
|
||||
{
|
||||
@@ -976,7 +979,6 @@ Status FlushJob::WriteLevel0Table() {
|
||||
meta_.oldest_ancester_time = oldest_ancester_time;
|
||||
meta_.file_creation_time = current_time;
|
||||
|
||||
uint64_t num_input_entries = 0;
|
||||
uint64_t memtable_payload_bytes = 0;
|
||||
uint64_t memtable_garbage_bytes = 0;
|
||||
IOStatus io_s;
|
||||
@@ -1010,16 +1012,38 @@ Status FlushJob::WriteLevel0Table() {
|
||||
cfd_->internal_stats(), &io_s, io_tracer_,
|
||||
BlobFileCreationReason::kFlush, seqno_to_time_mapping_.get(),
|
||||
event_logger_, job_context_->job_id, &table_properties_, write_hint,
|
||||
full_history_ts_low, blob_callback_, base_, &num_input_entries,
|
||||
&memtable_payload_bytes, &memtable_garbage_bytes);
|
||||
full_history_ts_low, blob_callback_, base_, &memtable_payload_bytes,
|
||||
&memtable_garbage_bytes, &flush_stats);
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:s", &s);
|
||||
// TODO: Cleanup io_status in BuildTable and table builders
|
||||
assert(!s.ok() || io_s.ok());
|
||||
io_s.PermitUncheckedError();
|
||||
if (num_input_entries != total_num_entries && s.ok()) {
|
||||
std::string msg = "Expected " + std::to_string(total_num_entries) +
|
||||
if (s.ok() && total_num_input_entries != flush_stats.num_input_records) {
|
||||
std::string msg = "Expected " +
|
||||
std::to_string(total_num_input_entries) +
|
||||
" entries in memtables, but read " +
|
||||
std::to_string(num_input_entries);
|
||||
std::to_string(flush_stats.num_input_records);
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "[%s] [JOB %d] Level-0 flush %s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id,
|
||||
msg.c_str());
|
||||
if (db_options_.flush_verify_memtable_count) {
|
||||
s = Status::Corruption(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Only verify on table with format collects table properties
|
||||
if (s.ok() &&
|
||||
(mutable_cf_options_.table_factory->IsInstanceOf(
|
||||
TableFactory::kBlockBasedTableName()) ||
|
||||
mutable_cf_options_.table_factory->IsInstanceOf(
|
||||
TableFactory::kPlainTableName())) &&
|
||||
flush_stats.num_output_records != table_properties_.num_entries) {
|
||||
std::string msg =
|
||||
"Number of keys in flush output SST files does not match "
|
||||
"number of keys added to the table. Expected " +
|
||||
std::to_string(flush_stats.num_output_records) + " but there are " +
|
||||
std::to_string(table_properties_.num_entries) +
|
||||
" in output SST files";
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "[%s] [JOB %d] Level-0 flush %s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id,
|
||||
msg.c_str());
|
||||
@@ -1085,12 +1109,10 @@ Status FlushJob::WriteLevel0Table() {
|
||||
// Piggyback FlushJobInfo on the first first flushed memtable.
|
||||
mems_[0]->SetFlushJobInfo(GetFlushJobInfo());
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
const uint64_t micros = clock_->NowMicros() - start_micros;
|
||||
const uint64_t cpu_micros = clock_->CPUMicros() - start_cpu_micros;
|
||||
stats.micros = micros;
|
||||
stats.cpu_micros = cpu_micros;
|
||||
flush_stats.micros = micros;
|
||||
flush_stats.cpu_micros = cpu_micros;
|
||||
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Flush lasted %" PRIu64
|
||||
@@ -1099,22 +1121,23 @@ Status FlushJob::WriteLevel0Table() {
|
||||
cpu_micros);
|
||||
|
||||
if (has_output) {
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
flush_stats.bytes_written = meta_.fd.GetFileSize();
|
||||
flush_stats.num_output_files = 1;
|
||||
}
|
||||
|
||||
const auto& blobs = edit_->GetBlobFileAdditions();
|
||||
for (const auto& blob : blobs) {
|
||||
stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
flush_stats.bytes_written_blob += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
flush_stats.num_output_files_blob = static_cast<int>(blobs.size());
|
||||
|
||||
RecordTimeToHistogram(stats_, FLUSH_TIME, stats.micros);
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, thread_pri_, stats);
|
||||
RecordTimeToHistogram(stats_, FLUSH_TIME, flush_stats.micros);
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, thread_pri_,
|
||||
flush_stats);
|
||||
cfd_->internal_stats()->AddCFStats(
|
||||
InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written + stats.bytes_written_blob);
|
||||
flush_stats.bytes_written + flush_stats.bytes_written_blob);
|
||||
RecordFlushIOStats();
|
||||
|
||||
return s;
|
||||
|
||||
@@ -42,6 +42,7 @@ using MinIterHeap =
|
||||
std::priority_queue<InternalIterator*, std::vector<InternalIterator*>,
|
||||
MinIterComparator>;
|
||||
|
||||
// TODO: name to TailingIterator
|
||||
/**
|
||||
* ForwardIterator is a special type of iterator that only supports Seek()
|
||||
* and Next(). It is expected to perform better than TailingIterator by
|
||||
|
||||
@@ -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(
|
||||
|
||||
+15
-8
@@ -45,6 +45,8 @@ const std::map<LevelStatType, LevelStat> InternalStats::compaction_level_stats =
|
||||
{LevelStatType::RN_GB, LevelStat{"RnGB", "Rn(GB)"}},
|
||||
{LevelStatType::RNP1_GB, LevelStat{"Rnp1GB", "Rnp1(GB)"}},
|
||||
{LevelStatType::WRITE_GB, LevelStat{"WriteGB", "Write(GB)"}},
|
||||
{LevelStatType::WRITE_PRE_COMP_GB,
|
||||
LevelStat{"WPreCompGB", "WPreComp(GB)"}},
|
||||
{LevelStatType::W_NEW_GB, LevelStat{"WnewGB", "Wnew(GB)"}},
|
||||
{LevelStatType::MOVED_GB, LevelStat{"MovedGB", "Moved(GB)"}},
|
||||
{LevelStatType::WRITE_AMP, LevelStat{"WriteAmp", "W-Amp"}},
|
||||
@@ -100,19 +102,20 @@ void PrintLevelStatsHeader(char* buf, size_t len, const std::string& cf_name,
|
||||
int line_size = snprintf(
|
||||
buf + written_size, len - written_size,
|
||||
"%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s "
|
||||
"%s "
|
||||
"%s\n",
|
||||
// Note that we skip COMPACTED_FILES and merge it with Files column
|
||||
group_by.c_str(), hdr(LevelStatType::NUM_FILES),
|
||||
hdr(LevelStatType::SIZE_BYTES), hdr(LevelStatType::SCORE),
|
||||
hdr(LevelStatType::READ_GB), hdr(LevelStatType::RN_GB),
|
||||
hdr(LevelStatType::RNP1_GB), hdr(LevelStatType::WRITE_GB),
|
||||
hdr(LevelStatType::W_NEW_GB), hdr(LevelStatType::MOVED_GB),
|
||||
hdr(LevelStatType::WRITE_AMP), hdr(LevelStatType::READ_MBPS),
|
||||
hdr(LevelStatType::WRITE_MBPS), hdr(LevelStatType::COMP_SEC),
|
||||
hdr(LevelStatType::COMP_CPU_SEC), hdr(LevelStatType::COMP_COUNT),
|
||||
hdr(LevelStatType::AVG_SEC), hdr(LevelStatType::KEY_IN),
|
||||
hdr(LevelStatType::KEY_DROP), hdr(LevelStatType::R_BLOB_GB),
|
||||
hdr(LevelStatType::W_BLOB_GB));
|
||||
hdr(LevelStatType::WRITE_PRE_COMP_GB), hdr(LevelStatType::W_NEW_GB),
|
||||
hdr(LevelStatType::MOVED_GB), hdr(LevelStatType::WRITE_AMP),
|
||||
hdr(LevelStatType::READ_MBPS), hdr(LevelStatType::WRITE_MBPS),
|
||||
hdr(LevelStatType::COMP_SEC), hdr(LevelStatType::COMP_CPU_SEC),
|
||||
hdr(LevelStatType::COMP_COUNT), hdr(LevelStatType::AVG_SEC),
|
||||
hdr(LevelStatType::KEY_IN), hdr(LevelStatType::KEY_DROP),
|
||||
hdr(LevelStatType::R_BLOB_GB), hdr(LevelStatType::W_BLOB_GB));
|
||||
|
||||
written_size += line_size;
|
||||
written_size = std::min(written_size, static_cast<int>(len));
|
||||
@@ -140,6 +143,8 @@ void PrepareLevelStats(std::map<LevelStatType, double>* level_stats,
|
||||
stats.bytes_read_non_output_levels / kGB;
|
||||
(*level_stats)[LevelStatType::RNP1_GB] = stats.bytes_read_output_level / kGB;
|
||||
(*level_stats)[LevelStatType::WRITE_GB] = stats.bytes_written / kGB;
|
||||
(*level_stats)[LevelStatType::WRITE_PRE_COMP_GB] =
|
||||
stats.bytes_written_pre_comp / kGB;
|
||||
(*level_stats)[LevelStatType::W_NEW_GB] = bytes_new / kGB;
|
||||
(*level_stats)[LevelStatType::MOVED_GB] = stats.bytes_moved / kGB;
|
||||
(*level_stats)[LevelStatType::WRITE_AMP] = w_amp;
|
||||
@@ -164,12 +169,13 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
buf, len,
|
||||
"%4s " /* Level */
|
||||
"%6d/%-3d " /* Files */
|
||||
"%8s " /* Size */
|
||||
"%10s " /* Size */
|
||||
"%5.1f " /* Score */
|
||||
"%8.1f " /* Read(GB) */
|
||||
"%7.1f " /* Rn(GB) */
|
||||
"%8.1f " /* Rnp1(GB) */
|
||||
"%9.1f " /* Write(GB) */
|
||||
"%9.1f " /* WPreComp(GB) */
|
||||
"%8.1f " /* Wnew(GB) */
|
||||
"%9.1f " /* Moved(GB) */
|
||||
"%5.1f " /* W-Amp */
|
||||
@@ -193,6 +199,7 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
stat_value.at(LevelStatType::RN_GB),
|
||||
stat_value.at(LevelStatType::RNP1_GB),
|
||||
stat_value.at(LevelStatType::WRITE_GB),
|
||||
stat_value.at(LevelStatType::WRITE_PRE_COMP_GB),
|
||||
stat_value.at(LevelStatType::W_NEW_GB),
|
||||
stat_value.at(LevelStatType::MOVED_GB),
|
||||
stat_value.at(LevelStatType::WRITE_AMP),
|
||||
|
||||
+15
-1
@@ -71,6 +71,7 @@ enum class LevelStatType {
|
||||
RN_GB,
|
||||
RNP1_GB,
|
||||
WRITE_GB,
|
||||
WRITE_PRE_COMP_GB,
|
||||
W_NEW_GB,
|
||||
MOVED_GB,
|
||||
WRITE_AMP,
|
||||
@@ -179,6 +180,9 @@ class InternalStats {
|
||||
// Total number of bytes written to table files during compaction
|
||||
uint64_t bytes_written;
|
||||
|
||||
// Total number of bytes written pre-compression during compaction
|
||||
uint64_t bytes_written_pre_comp;
|
||||
|
||||
// Total number of bytes written to blob files during compaction
|
||||
uint64_t bytes_written_blob;
|
||||
|
||||
@@ -231,6 +235,7 @@ class InternalStats {
|
||||
bytes_skipped_output_level(0),
|
||||
bytes_read_blob(0),
|
||||
bytes_written(0),
|
||||
bytes_written_pre_comp(0),
|
||||
bytes_written_blob(0),
|
||||
bytes_moved(0),
|
||||
num_input_files_in_non_output_levels(0),
|
||||
@@ -258,6 +263,7 @@ class InternalStats {
|
||||
bytes_skipped_output_level(0),
|
||||
bytes_read_blob(0),
|
||||
bytes_written(0),
|
||||
bytes_written_pre_comp(0),
|
||||
bytes_written_blob(0),
|
||||
bytes_moved(0),
|
||||
num_input_files_in_non_output_levels(0),
|
||||
@@ -291,6 +297,7 @@ class InternalStats {
|
||||
bytes_skipped_output_level(c.bytes_skipped_output_level),
|
||||
bytes_read_blob(c.bytes_read_blob),
|
||||
bytes_written(c.bytes_written),
|
||||
bytes_written_pre_comp(c.bytes_written_pre_comp),
|
||||
bytes_written_blob(c.bytes_written_blob),
|
||||
bytes_moved(c.bytes_moved),
|
||||
num_input_files_in_non_output_levels(
|
||||
@@ -321,6 +328,7 @@ class InternalStats {
|
||||
bytes_skipped_output_level = c.bytes_skipped_output_level;
|
||||
bytes_read_blob = c.bytes_read_blob;
|
||||
bytes_written = c.bytes_written;
|
||||
bytes_written_pre_comp = c.bytes_written_pre_comp;
|
||||
bytes_written_blob = c.bytes_written_blob;
|
||||
bytes_moved = c.bytes_moved;
|
||||
num_input_files_in_non_output_levels =
|
||||
@@ -353,6 +361,7 @@ class InternalStats {
|
||||
this->bytes_skipped_output_level = 0;
|
||||
this->bytes_read_blob = 0;
|
||||
this->bytes_written = 0;
|
||||
this->bytes_written_pre_comp = 0;
|
||||
this->bytes_written_blob = 0;
|
||||
this->bytes_moved = 0;
|
||||
this->num_input_files_in_non_output_levels = 0;
|
||||
@@ -381,6 +390,7 @@ class InternalStats {
|
||||
this->bytes_skipped_output_level += c.bytes_skipped_output_level;
|
||||
this->bytes_read_blob += c.bytes_read_blob;
|
||||
this->bytes_written += c.bytes_written;
|
||||
this->bytes_written_pre_comp += c.bytes_written_pre_comp;
|
||||
this->bytes_written_blob += c.bytes_written_blob;
|
||||
this->bytes_moved += c.bytes_moved;
|
||||
this->num_input_files_in_non_output_levels +=
|
||||
@@ -413,6 +423,7 @@ class InternalStats {
|
||||
this->bytes_skipped_output_level -= c.bytes_skipped_output_level;
|
||||
this->bytes_read_blob -= c.bytes_read_blob;
|
||||
this->bytes_written -= c.bytes_written;
|
||||
this->bytes_written_pre_comp -= c.bytes_written_pre_comp;
|
||||
this->bytes_written_blob -= c.bytes_written_blob;
|
||||
this->bytes_moved -= c.bytes_moved;
|
||||
this->num_input_files_in_non_output_levels -=
|
||||
@@ -697,7 +708,10 @@ class InternalStats {
|
||||
// a full cache, which would force a re-scan on the next GetStats.
|
||||
std::shared_ptr<CacheEntryStatsCollector<CacheEntryRoleStats>>
|
||||
cache_entry_stats_collector_;
|
||||
// Per-ColumnFamily/level compaction stats
|
||||
|
||||
// Per-column family and level compaction statistics, including flush and file
|
||||
// ingestion. These are treated as compactions to L0 or the level where the
|
||||
// file was ingested.
|
||||
std::vector<CompactionStats> comp_stats_;
|
||||
std::vector<CompactionStats> comp_stats_by_pri_;
|
||||
CompactionStats per_key_placement_comp_stats_;
|
||||
|
||||
+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);
|
||||
@@ -420,10 +418,13 @@ DECLARE_uint32(uncache_aggressiveness);
|
||||
DECLARE_int32(test_ingest_standalone_range_deletion_one_in);
|
||||
DECLARE_bool(allow_unprepared_value);
|
||||
DECLARE_string(file_temperature_age_thresholds);
|
||||
DECLARE_bool(allow_trivial_copy_when_change_temperature);
|
||||
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 "
|
||||
@@ -550,6 +536,9 @@ DEFINE_string(file_temperature_age_thresholds, "",
|
||||
"See CompactionOptionsFIFO::file_temperature_age_thresholds. "
|
||||
"empty == unset");
|
||||
|
||||
DEFINE_bool(allow_trivial_copy_when_change_temperature, true,
|
||||
"Allow kChangeTemperature to do trivial copy");
|
||||
|
||||
static const bool FLAGS_subcompactions_dummy __attribute__((__unused__)) =
|
||||
RegisterFlagValidator(&FLAGS_subcompactions, &ValidateUint32Range);
|
||||
|
||||
@@ -856,6 +845,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 +1092,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 +1477,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({});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,25 +26,50 @@ class DbStressTablePropertiesCollector : public TablePropertiesCollector {
|
||||
Status AddUserKey(const Slice& /* key */, const Slice& /* value */,
|
||||
EntryType /*type*/, SequenceNumber /*seq*/,
|
||||
uint64_t /*file_size*/) override {
|
||||
++keys_added;
|
||||
++all_calls;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Finish(UserCollectedProperties* /* properties */) override {
|
||||
void BlockAdd(uint64_t /* block_uncomp_bytes */,
|
||||
uint64_t /* block_compressed_bytes_fast */,
|
||||
uint64_t /* block_compressed_bytes_slow */) override {
|
||||
++blocks_added;
|
||||
++all_calls;
|
||||
}
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
++all_calls;
|
||||
(*properties)["db_stress_collector_property"] =
|
||||
std::to_string(keys_added) + ";" + std::to_string(blocks_added) + ";" +
|
||||
std::to_string(all_calls);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
UserCollectedProperties props;
|
||||
const_cast<DbStressTablePropertiesCollector*>(this)->Finish(&props);
|
||||
return props;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "DbStressTablePropertiesCollector";
|
||||
}
|
||||
|
||||
bool NeedCompact() const override { return need_compact_; }
|
||||
bool NeedCompact() const override {
|
||||
++all_calls;
|
||||
return need_compact_;
|
||||
}
|
||||
|
||||
private:
|
||||
const bool need_compact_;
|
||||
// These are tracked to detect race conditions that would arise from RocksDB
|
||||
// invoking TablePropertiesCollector functions in an unsynchronized way, as
|
||||
// TablePropertiesCollectors are allowed (encouraged) not to be thread safe.
|
||||
size_t keys_added = 0;
|
||||
size_t blocks_added = 0;
|
||||
// Including race between BlockAdd and AddUserKey (etc.)
|
||||
mutable size_t all_calls = 0;
|
||||
};
|
||||
|
||||
// A `DbStressTablePropertiesCollectorFactory` creates
|
||||
|
||||
@@ -427,6 +427,11 @@ bool StressTest::BuildOptionsTable() {
|
||||
std::vector<std::string>{
|
||||
"{{temperature=kWarm;age=30}:{temperature=kCold;age=300}}",
|
||||
"{{temperature=kCold;age=100}}", "{}"});
|
||||
options_tbl.emplace(
|
||||
"allow_trivial_copy_when_change_temperature",
|
||||
std::vector<std::string>{
|
||||
FLAGS_allow_trivial_copy_when_change_temperature ? "true"
|
||||
: "false"});
|
||||
}
|
||||
|
||||
// NOTE: allow -1 to mean starting disabled but dynamically changing
|
||||
@@ -838,11 +843,21 @@ Status StressTest::NewTxn(WriteOptions& write_opts, ThreadState* thread,
|
||||
FLAGS_use_only_the_last_commit_time_batch_for_recovery;
|
||||
txn_options.lock_timeout = 600000; // 10 min
|
||||
txn_options.deadlock_detect = true;
|
||||
if (FLAGS_commit_bypass_memtable_one_in > 0) {
|
||||
if (FLAGS_commit_bypass_memtable_one_in > 0 &&
|
||||
thread->rand.OneIn(FLAGS_commit_bypass_memtable_one_in)) {
|
||||
assert(FLAGS_txn_write_policy == 0);
|
||||
assert(FLAGS_user_timestamp_size == 0);
|
||||
txn_options.commit_bypass_memtable =
|
||||
thread->rand.OneIn(FLAGS_commit_bypass_memtable_one_in);
|
||||
if (thread->rand.OneIn(2)) {
|
||||
txn_options.commit_bypass_memtable = true;
|
||||
}
|
||||
if (thread->rand.OneIn(2)) {
|
||||
txn_options.large_txn_commit_optimize_threshold = 1;
|
||||
}
|
||||
if (thread->rand.OneIn(2) ||
|
||||
(!txn_options.commit_bypass_memtable &&
|
||||
txn_options.large_txn_commit_optimize_threshold != 1)) {
|
||||
txn_options.large_txn_commit_optimize_byte_threshold = 1;
|
||||
}
|
||||
if (commit_bypass_memtable) {
|
||||
*commit_bypass_memtable = txn_options.commit_bypass_memtable;
|
||||
}
|
||||
@@ -3376,8 +3391,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 +4060,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 =
|
||||
@@ -4220,10 +4231,14 @@ void InitializeOptionsFromFlags(
|
||||
StringToTemperature(FLAGS_default_temperature.c_str());
|
||||
|
||||
if (!FLAGS_file_temperature_age_thresholds.empty()) {
|
||||
const std::string allowTrivialCopyBoolStr =
|
||||
FLAGS_allow_trivial_copy_when_change_temperature ? "true" : "false";
|
||||
Status s = GetColumnFamilyOptionsFromString(
|
||||
{}, options,
|
||||
"compaction_options_fifo={file_temperature_age_thresholds=" +
|
||||
FLAGS_file_temperature_age_thresholds + "}",
|
||||
FLAGS_file_temperature_age_thresholds +
|
||||
";allow_trivial_copy_when_change_temperature=" +
|
||||
allowTrivialCopyBoolStr + "}",
|
||||
&options);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "While setting file_temperature_age_thresholds: %s\n",
|
||||
@@ -4257,7 +4272,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 +4335,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(
|
||||
|
||||
@@ -233,6 +233,13 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
}
|
||||
|
||||
Status s = secondary_db_->TryCatchUpWithPrimary();
|
||||
#ifndef NDEBUG
|
||||
uint64_t manifest_num = static_cast_with_check<DBImpl>(secondary_db_)
|
||||
->TEST_Current_Manifest_FileNo();
|
||||
#else
|
||||
uint64_t manifest_num = 0;
|
||||
#endif
|
||||
|
||||
if (!s.ok()) {
|
||||
VerificationAbort(shared,
|
||||
"Secondary failed to catch up to the primary");
|
||||
@@ -267,9 +274,11 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
assert(!pre_read_expected_values.empty() &&
|
||||
static_cast<size_t>(i - start) <
|
||||
pre_read_expected_values.size());
|
||||
VerifyValueRange(static_cast<int>(cf), i, options, shared, from_db,
|
||||
/* msg_prefix */ "Secondary get verification", s,
|
||||
pre_read_expected_values[i - start]);
|
||||
VerifyValueRange(
|
||||
static_cast<int>(cf), i, options, shared, from_db,
|
||||
/* msg_prefix */ "Secondary get verification, manifest: " +
|
||||
std::to_string(manifest_num),
|
||||
s, pre_read_expected_values[i - start]);
|
||||
}
|
||||
}
|
||||
} else if (method == VerificationMethod::kGetEntity) {
|
||||
@@ -1845,7 +1854,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 +1876,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 +1978,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 +2045,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);
|
||||
}
|
||||
|
||||
+32
-14
@@ -22,7 +22,10 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
Temperature src_temp_hint,
|
||||
std::unique_ptr<WritableFileWriter>& dest_writer,
|
||||
uint64_t size, bool use_fsync,
|
||||
const std::shared_ptr<IOTracer>& io_tracer) {
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
uint64_t max_read_buffer_size,
|
||||
const std::optional<IOOptions>& readIOOptions,
|
||||
const std::optional<IOOptions>& writeIOOptions) {
|
||||
FileOptions soptions;
|
||||
IOStatus io_s;
|
||||
std::unique_ptr<SequentialFileReader> src_reader;
|
||||
@@ -38,7 +41,8 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
|
||||
if (size == 0) {
|
||||
// default argument means copy everything
|
||||
io_s = fs->GetFileSize(source, opts, &size, nullptr);
|
||||
io_s =
|
||||
fs->GetFileSize(source, readIOOptions.value_or(opts), &size, nullptr);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
@@ -47,14 +51,23 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
new SequentialFileReader(std::move(srcfile), source, io_tracer));
|
||||
}
|
||||
|
||||
char buffer[4096];
|
||||
const size_t read_buffer_size = std::max(
|
||||
static_cast<size_t>(4096), static_cast<size_t>(max_read_buffer_size));
|
||||
std::unique_ptr<char[]> buffer;
|
||||
buffer.reset(new char[read_buffer_size]);
|
||||
|
||||
Env::IOPriority read_rate_limiter_priority = Env::IO_TOTAL;
|
||||
if (readIOOptions.has_value()) {
|
||||
read_rate_limiter_priority = readIOOptions.value().rate_limiter_priority;
|
||||
}
|
||||
Slice slice;
|
||||
while (size > 0) {
|
||||
size_t bytes_to_read = std::min(sizeof(buffer), static_cast<size_t>(size));
|
||||
size_t bytes_to_read = std::min(static_cast<size_t>(read_buffer_size),
|
||||
static_cast<size_t>(size));
|
||||
// TODO: rate limit copy file
|
||||
io_s = status_to_io_status(
|
||||
src_reader->Read(bytes_to_read, &slice, buffer,
|
||||
Env::IO_TOTAL /* rate_limiter_priority */));
|
||||
io_s = status_to_io_status(src_reader->Read(
|
||||
bytes_to_read, &slice, buffer.get(),
|
||||
read_rate_limiter_priority /* rate_limiter_priority */));
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
@@ -65,19 +78,22 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
std::to_string(dest_writer->GetFileSize()));
|
||||
}
|
||||
|
||||
io_s = dest_writer->Append(opts, slice);
|
||||
io_s = dest_writer->Append(writeIOOptions.value_or(opts), slice);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
size -= slice.size();
|
||||
}
|
||||
return dest_writer->Sync(opts, use_fsync);
|
||||
return dest_writer->Sync(writeIOOptions.value_or(opts), use_fsync);
|
||||
}
|
||||
|
||||
IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
Temperature src_temp_hint, const std::string& destination,
|
||||
Temperature dst_temp, uint64_t size, bool use_fsync,
|
||||
const std::shared_ptr<IOTracer>& io_tracer) {
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
uint64_t max_read_buffer_size,
|
||||
const std::optional<IOOptions>& readIOOptions,
|
||||
const std::optional<IOOptions>& writeIOOptions) {
|
||||
FileOptions options;
|
||||
IOStatus io_s;
|
||||
std::unique_ptr<WritableFileWriter> dest_writer;
|
||||
@@ -96,7 +112,8 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
}
|
||||
|
||||
return CopyFile(fs, source, src_temp_hint, dest_writer, size, use_fsync,
|
||||
io_tracer);
|
||||
io_tracer, max_read_buffer_size, readIOOptions,
|
||||
writeIOOptions);
|
||||
}
|
||||
|
||||
// Utility function to create a file with the provided contents
|
||||
@@ -230,15 +247,16 @@ IOStatus GenerateOneFileChecksum(
|
||||
Slice slice;
|
||||
uint64_t offset = 0;
|
||||
IOOptions opts;
|
||||
io_s = reader->PrepareIOOptions(read_options, opts);
|
||||
IODebugContext dbg;
|
||||
io_s = reader->PrepareIOOptions(read_options, opts, &dbg);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
while (size > 0) {
|
||||
size_t bytes_to_read =
|
||||
static_cast<size_t>(std::min(uint64_t{readahead_size}, size));
|
||||
io_s =
|
||||
reader->Read(opts, offset, bytes_to_read, &slice, buf.get(), nullptr);
|
||||
io_s = reader->Read(opts, offset, bytes_to_read, &slice, buf.get(), nullptr,
|
||||
&dbg);
|
||||
if (!io_s.ok()) {
|
||||
return IOStatus::Corruption("file read failed with error: " +
|
||||
io_s.ToString());
|
||||
|
||||
+22
-5
@@ -24,18 +24,28 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
Temperature src_temp_hint,
|
||||
std::unique_ptr<WritableFileWriter>& dest_writer,
|
||||
uint64_t size, bool use_fsync,
|
||||
const std::shared_ptr<IOTracer>& io_tracer);
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
uint64_t max_read_buffer_size = 4096,
|
||||
const std::optional<IOOptions>& readIOOptions = {},
|
||||
const std::optional<IOOptions>& writeIOOptions = {});
|
||||
IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
Temperature src_temp_hint, const std::string& destination,
|
||||
Temperature dst_temp, uint64_t size, bool use_fsync,
|
||||
const std::shared_ptr<IOTracer>& io_tracer);
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
uint64_t max_read_buffer_size = 4096,
|
||||
const std::optional<IOOptions>& readIOOptions = {},
|
||||
const std::optional<IOOptions>& writeIOOptions = {});
|
||||
inline IOStatus CopyFile(const std::shared_ptr<FileSystem>& fs,
|
||||
const std::string& source, Temperature src_temp_hint,
|
||||
const std::string& destination, Temperature dst_temp,
|
||||
uint64_t size, bool use_fsync,
|
||||
const std::shared_ptr<IOTracer>& io_tracer) {
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
uint64_t max_read_buffer_size = 4096,
|
||||
const std::optional<IOOptions>& readIOOptions = {},
|
||||
const std::optional<IOOptions>& writeIOOptions = {}) {
|
||||
return CopyFile(fs.get(), source, src_temp_hint, destination, dst_temp, size,
|
||||
use_fsync, io_tracer);
|
||||
use_fsync, io_tracer, max_read_buffer_size, readIOOptions,
|
||||
writeIOOptions);
|
||||
}
|
||||
IOStatus CreateFile(FileSystem* fs, const std::string& destination,
|
||||
const std::string& contents, bool use_fsync);
|
||||
@@ -76,7 +86,14 @@ IOStatus GenerateOneFileChecksum(
|
||||
const ReadOptions& read_options, Statistics* stats, SystemClock* clock);
|
||||
|
||||
inline IOStatus PrepareIOFromReadOptions(const ReadOptions& ro,
|
||||
SystemClock* clock, IOOptions& opts) {
|
||||
SystemClock* clock, IOOptions& opts,
|
||||
IODebugContext* dbg = nullptr) {
|
||||
if (ro.request_id != nullptr) {
|
||||
if (dbg != nullptr && dbg->request_id == nullptr) {
|
||||
dbg->SetRequestId(ro.request_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (ro.deadline.count()) {
|
||||
std::chrono::microseconds now =
|
||||
std::chrono::microseconds(clock->NowMicros());
|
||||
|
||||
+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(
|
||||
|
||||
@@ -106,11 +106,14 @@ IOStatus RandomAccessFileReader::Create(
|
||||
|
||||
IOStatus RandomAccessFileReader::Read(const IOOptions& opts, uint64_t offset,
|
||||
size_t n, Slice* result, char* scratch,
|
||||
AlignedBuf* aligned_buf) const {
|
||||
AlignedBuf* aligned_buf,
|
||||
IODebugContext* dbg) const {
|
||||
(void)aligned_buf;
|
||||
const Env::IOPriority rate_limiter_priority = opts.rate_limiter_priority;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("RandomAccessFileReader::Read", nullptr);
|
||||
TEST_SYNC_POINT_CALLBACK("RandomAccessFileReader::Read:IODebugContext",
|
||||
const_cast<void*>(static_cast<void*>(dbg)));
|
||||
|
||||
// To be paranoid: modify scratch a little bit, so in case underlying
|
||||
// FileSystem doesn't fill the buffer but return success and `scratch` returns
|
||||
@@ -175,7 +178,7 @@ IOStatus RandomAccessFileReader::Read(const IOOptions& opts, uint64_t offset,
|
||||
// the opts.timeout before calling file_->Read
|
||||
assert(!opts.timeout.count() || allowed == read_size);
|
||||
io_s = file_->Read(aligned_offset + buf.CurrentSize(), allowed, opts,
|
||||
&tmp, buf.Destination(), nullptr);
|
||||
&tmp, buf.Destination(), dbg);
|
||||
}
|
||||
if (ShouldNotifyListeners()) {
|
||||
auto finish_ts = FileOperationInfo::FinishNow();
|
||||
@@ -237,7 +240,7 @@ IOStatus RandomAccessFileReader::Read(const IOOptions& opts, uint64_t offset,
|
||||
// the opts.timeout before calling file_->Read
|
||||
assert(!opts.timeout.count() || allowed == n);
|
||||
io_s = file_->Read(offset + pos, allowed, opts, &tmp_result,
|
||||
scratch + pos, nullptr);
|
||||
scratch + pos, dbg);
|
||||
}
|
||||
if (ShouldNotifyListeners()) {
|
||||
auto finish_ts = FileOperationInfo::FinishNow();
|
||||
@@ -311,7 +314,8 @@ bool TryMerge(FSReadRequest* dest, const FSReadRequest& src) {
|
||||
IOStatus RandomAccessFileReader::MultiRead(const IOOptions& opts,
|
||||
FSReadRequest* read_reqs,
|
||||
size_t num_reqs,
|
||||
AlignedBuf* aligned_buf) const {
|
||||
AlignedBuf* aligned_buf,
|
||||
IODebugContext* dbg) const {
|
||||
(void)aligned_buf; // suppress warning of unused variable in LITE mode
|
||||
assert(num_reqs > 0);
|
||||
|
||||
@@ -420,8 +424,10 @@ IOStatus RandomAccessFileReader::MultiRead(const IOOptions& opts,
|
||||
remaining_bytes -= request_bytes;
|
||||
}
|
||||
}
|
||||
io_s = file_->MultiRead(fs_reqs, num_fs_reqs, opts,
|
||||
/*IODebugContext*=*/nullptr);
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"RandomAccessFileReader::MultiRead:IODebugContext",
|
||||
const_cast<void*>(static_cast<void*>(dbg)));
|
||||
io_s = file_->MultiRead(fs_reqs, num_fs_reqs, opts, dbg);
|
||||
RecordInHistogram(stats_, MULTIGET_IO_BATCH_SIZE, num_fs_reqs);
|
||||
}
|
||||
|
||||
@@ -475,18 +481,21 @@ IOStatus RandomAccessFileReader::MultiRead(const IOOptions& opts,
|
||||
}
|
||||
|
||||
IOStatus RandomAccessFileReader::PrepareIOOptions(const ReadOptions& ro,
|
||||
IOOptions& opts) const {
|
||||
IOOptions& opts,
|
||||
IODebugContext* dbg) const {
|
||||
if (clock_ != nullptr) {
|
||||
return PrepareIOFromReadOptions(ro, clock_, opts);
|
||||
return PrepareIOFromReadOptions(ro, clock_, opts, dbg);
|
||||
} else {
|
||||
return PrepareIOFromReadOptions(ro, SystemClock::Default().get(), opts);
|
||||
return PrepareIOFromReadOptions(ro, SystemClock::Default().get(), opts,
|
||||
dbg);
|
||||
}
|
||||
}
|
||||
|
||||
IOStatus RandomAccessFileReader::ReadAsync(
|
||||
FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(FSReadRequest&, void*)> cb, void* cb_arg,
|
||||
void** io_handle, IOHandleDeleter* del_fn, AlignedBuf* aligned_buf) {
|
||||
void** io_handle, IOHandleDeleter* del_fn, AlignedBuf* aligned_buf,
|
||||
IODebugContext* dbg) {
|
||||
IOStatus s;
|
||||
// Create a callback and populate info.
|
||||
auto read_async_callback =
|
||||
@@ -532,14 +541,14 @@ IOStatus RandomAccessFileReader::ReadAsync(
|
||||
(stats_ != nullptr) ? &elapsed : nullptr, true /*overwrite*/,
|
||||
true /*delay_enabled*/);
|
||||
s = file_->ReadAsync(aligned_req, opts, read_async_callback,
|
||||
read_async_info, io_handle, del_fn, nullptr /*dbg*/);
|
||||
read_async_info, io_handle, del_fn, dbg);
|
||||
} else {
|
||||
StopWatch sw(clock_, stats_, hist_type_,
|
||||
GetFileReadHistograms(stats_, opts.io_activity),
|
||||
(stats_ != nullptr) ? &elapsed : nullptr, true /*overwrite*/,
|
||||
true /*delay_enabled*/);
|
||||
s = file_->ReadAsync(req, opts, read_async_callback, read_async_info,
|
||||
io_handle, del_fn, nullptr /*dbg*/);
|
||||
io_handle, del_fn, dbg);
|
||||
}
|
||||
RecordTick(stats_, READ_ASYNC_MICROS, elapsed);
|
||||
|
||||
|
||||
@@ -164,7 +164,8 @@ class RandomAccessFileReader {
|
||||
// the internally allocated buffer on return, and the result refers to a
|
||||
// region in aligned_buf.
|
||||
IOStatus Read(const IOOptions& opts, uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch, AlignedBuf* aligned_buf) const;
|
||||
char* scratch, AlignedBuf* aligned_buf,
|
||||
IODebugContext* dbg = nullptr) const;
|
||||
|
||||
// REQUIRES:
|
||||
// num_reqs > 0, reqs do not overlap, and offsets in reqs are increasing.
|
||||
@@ -172,10 +173,12 @@ class RandomAccessFileReader {
|
||||
// In direct IO mode, aligned_buf stores the aligned buffer allocated inside
|
||||
// MultiRead, the result Slices in reqs refer to aligned_buf.
|
||||
IOStatus MultiRead(const IOOptions& opts, FSReadRequest* reqs,
|
||||
size_t num_reqs, AlignedBuf* aligned_buf) const;
|
||||
size_t num_reqs, AlignedBuf* aligned_buf,
|
||||
IODebugContext* dbg = nullptr) const;
|
||||
|
||||
IOStatus Prefetch(const IOOptions& opts, uint64_t offset, size_t n) const {
|
||||
return file_->Prefetch(offset, n, opts, nullptr);
|
||||
IOStatus Prefetch(const IOOptions& opts, uint64_t offset, size_t n,
|
||||
IODebugContext* dbg = nullptr) const {
|
||||
return file_->Prefetch(offset, n, opts, dbg);
|
||||
}
|
||||
|
||||
FSRandomAccessFile* file() { return file_.get(); }
|
||||
@@ -184,12 +187,13 @@ class RandomAccessFileReader {
|
||||
|
||||
bool use_direct_io() const { return file_->use_direct_io(); }
|
||||
|
||||
IOStatus PrepareIOOptions(const ReadOptions& ro, IOOptions& opts) const;
|
||||
IOStatus PrepareIOOptions(const ReadOptions& ro, IOOptions& opts,
|
||||
IODebugContext* dbg = nullptr) const;
|
||||
|
||||
IOStatus ReadAsync(FSReadRequest& req, const IOOptions& opts,
|
||||
std::function<void(FSReadRequest&, void*)> cb,
|
||||
void* cb_arg, void** io_handle, IOHandleDeleter* del_fn,
|
||||
AlignedBuf* aligned_buf);
|
||||
AlignedBuf* aligned_buf, IODebugContext* dbg = nullptr);
|
||||
|
||||
void ReadAsyncCallback(FSReadRequest& req, void* cb_arg);
|
||||
};
|
||||
|
||||
@@ -147,8 +147,9 @@ TEST_F(RandomAccessFileReaderTest, MultiReadDirectIO) {
|
||||
reqs.push_back(std::move(r0));
|
||||
reqs.push_back(std::move(r1));
|
||||
AlignedBuf aligned_buf;
|
||||
ASSERT_OK(
|
||||
r->MultiRead(IOOptions(), reqs.data(), reqs.size(), &aligned_buf));
|
||||
IODebugContext dbg;
|
||||
ASSERT_OK(r->MultiRead(IOOptions(), reqs.data(), reqs.size(), &aligned_buf,
|
||||
&dbg));
|
||||
|
||||
AssertResult(content, reqs);
|
||||
|
||||
@@ -192,8 +193,9 @@ TEST_F(RandomAccessFileReaderTest, MultiReadDirectIO) {
|
||||
reqs.push_back(std::move(r1));
|
||||
reqs.push_back(std::move(r2));
|
||||
AlignedBuf aligned_buf;
|
||||
ASSERT_OK(
|
||||
r->MultiRead(IOOptions(), reqs.data(), reqs.size(), &aligned_buf));
|
||||
IODebugContext dbg;
|
||||
ASSERT_OK(r->MultiRead(IOOptions(), reqs.data(), reqs.size(), &aligned_buf,
|
||||
&dbg));
|
||||
|
||||
AssertResult(content, reqs);
|
||||
|
||||
@@ -237,8 +239,9 @@ TEST_F(RandomAccessFileReaderTest, MultiReadDirectIO) {
|
||||
reqs.push_back(std::move(r1));
|
||||
reqs.push_back(std::move(r2));
|
||||
AlignedBuf aligned_buf;
|
||||
ASSERT_OK(
|
||||
r->MultiRead(IOOptions(), reqs.data(), reqs.size(), &aligned_buf));
|
||||
IODebugContext dbg;
|
||||
ASSERT_OK(r->MultiRead(IOOptions(), reqs.data(), reqs.size(), &aligned_buf,
|
||||
&dbg));
|
||||
|
||||
AssertResult(content, reqs);
|
||||
|
||||
@@ -274,8 +277,9 @@ TEST_F(RandomAccessFileReaderTest, MultiReadDirectIO) {
|
||||
reqs.push_back(std::move(r0));
|
||||
reqs.push_back(std::move(r1));
|
||||
AlignedBuf aligned_buf;
|
||||
ASSERT_OK(
|
||||
r->MultiRead(IOOptions(), reqs.data(), reqs.size(), &aligned_buf));
|
||||
IODebugContext dbg;
|
||||
ASSERT_OK(r->MultiRead(IOOptions(), reqs.data(), reqs.size(), &aligned_buf,
|
||||
&dbg));
|
||||
|
||||
AssertResult(content, reqs);
|
||||
|
||||
|
||||
@@ -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
|
||||
Loaded 100 of 221 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user