Multi-File Trivial Move in L0->L1 (#10188)

Summary:
In leveled compaction, L0->L1 trivial move will allow more than one file to be moved in one compaction. This would allow L0 files to be moved down faster when data is loaded in sequential order, making slowdown or stop condition harder to hit. Also seek L0->L1 trivial move when only some files qualify.
1. We always try to find L0->L1 trivial move from the oldest files. Keep including newer files, until adding a new file won't trigger a trivial move
2. Modify the trivial move condition so that this compaction would be tagged as trivial move.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/10188

Test Plan:
See throughput improvements with db_bench with fast fillseq benchmark and small L0 files:

./db_bench_l0_move --benchmarks=fillseq --compression_type=lz4 --write_buffer_size=5000000 --num=100000000 --value_size=1000 -level_compaction_dynamic_level_bytes

The throughput improved by about 50%. Stalling still happens though.

Reviewed By: jay-zhuang

Differential Revision: D37224743

fbshipit-source-id: 8958d97f22e12bdfc14d2e85930f6fa0070e9659
main
sdong 2 years ago committed by Facebook GitHub Bot
parent 4f51101d31
commit 4428c76181
  1. 1
      HISTORY.md
  2. 11
      db/compaction/compaction.cc
  3. 6
      db/compaction/compaction.h
  4. 10
      db/compaction/compaction_picker.cc
  5. 2
      db/compaction/compaction_picker.h
  6. 10
      db/compaction/compaction_picker_fifo.cc
  7. 134
      db/compaction/compaction_picker_level.cc
  8. 86
      db/compaction/compaction_picker_test.cc
  9. 7
      db/compaction/compaction_picker_universal.cc
  10. 40
      db/corruption_test.cc
  11. 16
      db/db_compaction_test.cc

@ -75,6 +75,7 @@
### Performance Improvements ### Performance Improvements
* Rather than doing total sort against all files in a level, SortFileByOverlappingRatio() to only find the top 50 files based on score. This can improve write throughput for the use cases where data is loaded in increasing key order and there are a lot of files in one LSM-tree, where applying compaction results is the bottleneck. * Rather than doing total sort against all files in a level, SortFileByOverlappingRatio() to only find the top 50 files based on score. This can improve write throughput for the use cases where data is loaded in increasing key order and there are a lot of files in one LSM-tree, where applying compaction results is the bottleneck.
* In leveled compaction, L0->L1 trivial move will allow more than one file to be moved in one compaction. This would allow L0 files to be moved down faster when data is loaded in sequential order, making slowdown or stop condition harder to hit. Also seek L0->L1 trivial move when only some files qualify.
## 7.3.0 (05/20/2022) ## 7.3.0 (05/20/2022)
### Bug Fixes ### Bug Fixes

@ -214,7 +214,8 @@ Compaction::Compaction(
CompressionOptions _compression_opts, Temperature _output_temperature, CompressionOptions _compression_opts, Temperature _output_temperature,
uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents, uint32_t _max_subcompactions, std::vector<FileMetaData*> _grandparents,
bool _manual_compaction, const std::string& _trim_ts, double _score, bool _manual_compaction, const std::string& _trim_ts, double _score,
bool _deletion_compaction, CompactionReason _compaction_reason, bool _deletion_compaction, bool l0_files_might_overlap,
CompactionReason _compaction_reason,
BlobGarbageCollectionPolicy _blob_garbage_collection_policy, BlobGarbageCollectionPolicy _blob_garbage_collection_policy,
double _blob_garbage_collection_age_cutoff) double _blob_garbage_collection_age_cutoff)
: input_vstorage_(vstorage), : input_vstorage_(vstorage),
@ -233,6 +234,7 @@ Compaction::Compaction(
output_compression_opts_(_compression_opts), output_compression_opts_(_compression_opts),
output_temperature_(_output_temperature), output_temperature_(_output_temperature),
deletion_compaction_(_deletion_compaction), deletion_compaction_(_deletion_compaction),
l0_files_might_overlap_(l0_files_might_overlap),
inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))), inputs_(PopulateWithAtomicBoundaries(vstorage, std::move(_inputs))),
grandparents_(std::move(_grandparents)), grandparents_(std::move(_grandparents)),
score_(_score), score_(_score),
@ -241,6 +243,7 @@ Compaction::Compaction(
is_manual_compaction_(_manual_compaction), is_manual_compaction_(_manual_compaction),
trim_ts_(_trim_ts), trim_ts_(_trim_ts),
is_trivial_move_(false), is_trivial_move_(false),
compaction_reason_(_compaction_reason), compaction_reason_(_compaction_reason),
notify_on_compaction_completion_(false), notify_on_compaction_completion_(false),
enable_blob_garbage_collection_( enable_blob_garbage_collection_(
@ -333,8 +336,10 @@ bool Compaction::IsTrivialMove() const {
// filter to be applied to that level, and thus cannot be a trivial move. // filter to be applied to that level, and thus cannot be a trivial move.
// Check if start level have files with overlapping ranges // Check if start level have files with overlapping ranges
if (start_level_ == 0 && input_vstorage_->level0_non_overlapping() == false) { if (start_level_ == 0 && input_vstorage_->level0_non_overlapping() == false &&
// We cannot move files from L0 to L1 if the files are overlapping l0_files_might_overlap_) {
// We cannot move files from L0 to L1 if the L0 files in the LSM-tree are
// overlapping, unless we are sure that files picked in L0 don't overlap.
return false; return false;
} }

@ -81,6 +81,7 @@ class Compaction {
std::vector<FileMetaData*> grandparents, std::vector<FileMetaData*> grandparents,
bool manual_compaction = false, const std::string& trim_ts = "", bool manual_compaction = false, const std::string& trim_ts = "",
double score = -1, bool deletion_compaction = false, double score = -1, bool deletion_compaction = false,
bool l0_files_might_overlap = true,
CompactionReason compaction_reason = CompactionReason::kUnknown, CompactionReason compaction_reason = CompactionReason::kUnknown,
BlobGarbageCollectionPolicy blob_garbage_collection_policy = BlobGarbageCollectionPolicy blob_garbage_collection_policy =
BlobGarbageCollectionPolicy::kUseDefault, BlobGarbageCollectionPolicy::kUseDefault,
@ -388,6 +389,11 @@ class Compaction {
// should it split the output file using the compact cursor? // should it split the output file using the compact cursor?
InternalKey output_split_key_; InternalKey output_split_key_;
// L0 files in LSM-tree might be overlapping. But the compaction picking
// logic might pick a subset of the files that aren't overlapping. if
// that is the case, set the value to false. Otherwise, set it true.
bool l0_files_might_overlap_;
// Compaction input files organized by level. Constant after construction // Compaction input files organized by level. Constant after construction
const std::vector<CompactionInputFiles> inputs_; const std::vector<CompactionInputFiles> inputs_;

@ -543,6 +543,10 @@ bool CompactionPicker::SetupOtherInputs(
output_level_inputs_size); output_level_inputs_size);
inputs->files = expanded_inputs.files; inputs->files = expanded_inputs.files;
} }
} else {
// Likely to be trivial move. Expand files if they are still trivial moves,
// but limit to mutable_cf_options.max_compaction_bytes or 8 files so that
// we don't create too much compaction pressure for the next level.
} }
return true; return true;
} }
@ -641,7 +645,8 @@ Compaction* CompactionPicker::CompactRange(
GetCompressionOptions(mutable_cf_options, vstorage, output_level), GetCompressionOptions(mutable_cf_options, vstorage, output_level),
Temperature::kUnknown, compact_range_options.max_subcompactions, Temperature::kUnknown, compact_range_options.max_subcompactions,
/* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1, /* grandparents */ {}, /* is manual */ true, trim_ts, /* score */ -1,
/* deletion_compaction */ false, CompactionReason::kUnknown, /* deletion_compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kUnknown,
compact_range_options.blob_garbage_collection_policy, compact_range_options.blob_garbage_collection_policy,
compact_range_options.blob_garbage_collection_age_cutoff); compact_range_options.blob_garbage_collection_age_cutoff);
@ -823,7 +828,8 @@ Compaction* CompactionPicker::CompactRange(
GetCompressionOptions(mutable_cf_options, vstorage, output_level), GetCompressionOptions(mutable_cf_options, vstorage, output_level),
Temperature::kUnknown, compact_range_options.max_subcompactions, Temperature::kUnknown, compact_range_options.max_subcompactions,
std::move(grandparents), /* is manual */ true, trim_ts, /* score */ -1, std::move(grandparents), /* is manual */ true, trim_ts, /* score */ -1,
/* deletion_compaction */ false, CompactionReason::kUnknown, /* deletion_compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kUnknown,
compact_range_options.blob_garbage_collection_policy, compact_range_options.blob_garbage_collection_policy,
compact_range_options.blob_garbage_collection_age_cutoff); compact_range_options.blob_garbage_collection_age_cutoff);

@ -217,6 +217,8 @@ class CompactionPicker {
return &compactions_in_progress_; return &compactions_in_progress_;
} }
const InternalKeyComparator* icmp() const { return icmp_; }
protected: protected:
const ImmutableOptions& ioptions_; const ImmutableOptions& ioptions_;

@ -116,7 +116,8 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
mutable_cf_options.compression_opts, Temperature::kUnknown, mutable_cf_options.compression_opts, Temperature::kUnknown,
/* max_subcompactions */ 0, {}, /* is manual */ false, /* max_subcompactions */ 0, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0), /* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ true, CompactionReason::kFIFOTtl); /* is deletion compaction */ true, /* l0_files_might_overlap */ true,
CompactionReason::kFIFOTtl);
return c; return c;
} }
@ -160,6 +161,7 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
0 /* max_subcompactions */, {}, /* is manual */ false, 0 /* max_subcompactions */, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0), /* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ false, /* is deletion compaction */ false,
/* l0_files_might_overlap */ true,
CompactionReason::kFIFOReduceNumFiles); CompactionReason::kFIFOReduceNumFiles);
return c; return c;
} }
@ -209,7 +211,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
mutable_cf_options.compression_opts, Temperature::kUnknown, mutable_cf_options.compression_opts, Temperature::kUnknown,
/* max_subcompactions */ 0, {}, /* is manual */ false, /* max_subcompactions */ 0, {}, /* is manual */ false,
/* trim_ts */ "", vstorage->CompactionScore(0), /* trim_ts */ "", vstorage->CompactionScore(0),
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize); /* is deletion compaction */ true,
/* l0_files_might_overlap */ true, CompactionReason::kFIFOMaxSize);
return c; return c;
} }
@ -315,7 +318,8 @@ Compaction* FIFOCompactionPicker::PickCompactionToWarm(
Temperature::kWarm, Temperature::kWarm,
/* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "", /* max_subcompactions */ 0, {}, /* is manual */ false, /* trim_ts */ "",
vstorage->CompactionScore(0), vstorage->CompactionScore(0),
/* is deletion compaction */ false, CompactionReason::kChangeTemperature); /* is deletion compaction */ false, /* l0_files_might_overlap */ true,
CompactionReason::kChangeTemperature);
return c; return c;
} }

@ -7,11 +7,13 @@
// Use of this source code is governed by a BSD-style license that can be // 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. // found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "db/compaction/compaction_picker_level.h"
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "db/compaction/compaction_picker_level.h" #include "db/version_edit.h"
#include "logging/log_buffer.h" #include "logging/log_buffer.h"
#include "test_util/sync_point.h" #include "test_util/sync_point.h"
@ -87,6 +89,9 @@ class LevelCompactionBuilder {
// function will return false. // function will return false.
bool PickFileToCompact(); bool PickFileToCompact();
// Return true if a L0 trivial move is picked up.
bool TryPickL0TrivialMove();
// For L0->L0, picks the longest span of files that aren't currently // For L0->L0, picks the longest span of files that aren't currently
// undergoing compaction for which work-per-deleted-file decreases. The span // undergoing compaction for which work-per-deleted-file decreases. The span
// always starts from the newest L0 file. // always starts from the newest L0 file.
@ -117,6 +122,7 @@ class LevelCompactionBuilder {
int base_index_ = -1; int base_index_ = -1;
double start_level_score_ = 0; double start_level_score_ = 0;
bool is_manual_ = false; bool is_manual_ = false;
bool is_l0_trivial_move_ = false;
CompactionInputFiles start_level_inputs_; CompactionInputFiles start_level_inputs_;
std::vector<CompactionInputFiles> compaction_inputs_; std::vector<CompactionInputFiles> compaction_inputs_;
CompactionInputFiles output_level_inputs_; CompactionInputFiles output_level_inputs_;
@ -261,7 +267,7 @@ void LevelCompactionBuilder::SetupInitialFiles() {
} }
bool LevelCompactionBuilder::SetupOtherL0FilesIfNeeded() { bool LevelCompactionBuilder::SetupOtherL0FilesIfNeeded() {
if (start_level_ == 0 && output_level_ != 0) { if (start_level_ == 0 && output_level_ != 0 && !is_l0_trivial_move_) {
return compaction_picker_->GetOverlappingL0Files( return compaction_picker_->GetOverlappingL0Files(
vstorage_, &start_level_inputs_, output_level_, &parent_index_); vstorage_, &start_level_inputs_, output_level_, &parent_index_);
} }
@ -274,7 +280,8 @@ bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
// need to consider other levels. // need to consider other levels.
if (output_level_ != 0) { if (output_level_ != 0) {
output_level_inputs_.level = output_level_; output_level_inputs_.level = output_level_;
if (!compaction_picker_->SetupOtherInputs( if (!is_l0_trivial_move_ &&
!compaction_picker_->SetupOtherInputs(
cf_name_, mutable_cf_options_, vstorage_, &start_level_inputs_, cf_name_, mutable_cf_options_, vstorage_, &start_level_inputs_,
&output_level_inputs_, &parent_index_, base_index_)) { &output_level_inputs_, &parent_index_, base_index_)) {
return false; return false;
@ -285,20 +292,22 @@ bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
compaction_inputs_.push_back(output_level_inputs_); compaction_inputs_.push_back(output_level_inputs_);
} }
// In some edge cases we could pick a compaction that will be compacting if (!is_l0_trivial_move_) {
// a key range that overlap with another running compaction, and both // In some edge cases we could pick a compaction that will be compacting
// of them have the same output level. This could happen if // a key range that overlap with another running compaction, and both
// (1) we are running a non-exclusive manual compaction // of them have the same output level. This could happen if
// (2) AddFile ingest a new file into the LSM tree // (1) we are running a non-exclusive manual compaction
// We need to disallow this from happening. // (2) AddFile ingest a new file into the LSM tree
if (compaction_picker_->FilesRangeOverlapWithCompaction(compaction_inputs_, // We need to disallow this from happening.
output_level_)) { if (compaction_picker_->FilesRangeOverlapWithCompaction(
// This compaction output could potentially conflict with the output compaction_inputs_, output_level_)) {
// of a currently running compaction, we cannot run it. // This compaction output could potentially conflict with the output
return false; // of a currently running compaction, we cannot run it.
return false;
}
compaction_picker_->GetGrandparents(vstorage_, start_level_inputs_,
output_level_inputs_, &grandparents_);
} }
compaction_picker_->GetGrandparents(vstorage_, start_level_inputs_,
output_level_inputs_, &grandparents_);
} else { } else {
compaction_inputs_.push_back(start_level_inputs_); compaction_inputs_.push_back(start_level_inputs_);
} }
@ -349,6 +358,7 @@ Compaction* LevelCompactionBuilder::GetCompaction() {
Temperature::kUnknown, Temperature::kUnknown,
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_, /* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
/* trim_ts */ "", start_level_score_, false /* deletion_compaction */, /* trim_ts */ "", start_level_score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ start_level_ == 0 && !is_l0_trivial_move_,
compaction_reason_); compaction_reason_);
// If it's level 0 compaction, make sure we don't execute any other level 0 // If it's level 0 compaction, make sure we don't execute any other level 0
@ -417,6 +427,75 @@ uint32_t LevelCompactionBuilder::GetPathId(
return p; return p;
} }
bool LevelCompactionBuilder::TryPickL0TrivialMove() {
if (vstorage_->base_level() <= 0) {
return false;
}
if (start_level_ == 0 && mutable_cf_options_.compression_per_level.empty() &&
!vstorage_->LevelFiles(output_level_).empty() &&
ioptions_.db_paths.size() <= 1) {
// Try to pick trivial move from L0 to L1. We start from the oldest
// file. We keep expanding to newer files if it would form a
// trivial move.
// For now we don't support it with
// mutable_cf_options_.compression_per_level to prevent the logic
// of determining whether L0 can be trivial moved to the next level.
// We skip the case where output level is empty, since in this case, at
// least the oldest file would qualify for trivial move, and this would
// be a surprising behavior with few benefits.
// We search from the oldest file from the newest. In theory, there are
// files in the middle can form trivial move too, but it is probably
// uncommon and we ignore these cases for simplicity.
const std::vector<FileMetaData*>& level_files =
vstorage_->LevelFiles(start_level_);
InternalKey my_smallest, my_largest;
for (auto it = level_files.rbegin(); it != level_files.rend(); ++it) {
CompactionInputFiles output_level_inputs;
output_level_inputs.level = output_level_;
FileMetaData* file = *it;
if (it == level_files.rbegin()) {
my_smallest = file->smallest;
my_largest = file->largest;
} else {
if (compaction_picker_->icmp()->Compare(file->largest, my_smallest) <
0) {
my_smallest = file->smallest;
} else if (compaction_picker_->icmp()->Compare(file->smallest,
my_largest) > 0) {
my_largest = file->largest;
} else {
break;
}
}
vstorage_->GetOverlappingInputs(output_level_, &my_smallest, &my_largest,
&output_level_inputs.files);
if (output_level_inputs.empty()) {
assert(!file->being_compacted);
start_level_inputs_.files.push_back(file);
} else {
break;
}
}
}
if (!start_level_inputs_.empty()) {
// Sort files by key range. Not sure it's 100% necessary but it's cleaner
// to always keep files sorted by key the key ranges don't overlap.
std::sort(start_level_inputs_.files.begin(),
start_level_inputs_.files.end(),
[icmp = compaction_picker_->icmp()](FileMetaData* f1,
FileMetaData* f2) -> bool {
return (icmp->Compare(f1->smallest, f2->smallest) < 0);
});
is_l0_trivial_move_ = true;
return true;
}
return false;
}
bool LevelCompactionBuilder::PickFileToCompact() { bool LevelCompactionBuilder::PickFileToCompact() {
// level 0 files are overlapping. So we cannot pick more // level 0 files are overlapping. So we cannot pick more
// than one concurrent compactions at this level. This // than one concurrent compactions at this level. This
@ -429,20 +508,26 @@ bool LevelCompactionBuilder::PickFileToCompact() {
} }
start_level_inputs_.clear(); start_level_inputs_.clear();
start_level_inputs_.level = start_level_;
assert(start_level_ >= 0); assert(start_level_ >= 0);
// Pick the largest file in this level that is not already if (TryPickL0TrivialMove()) {
// being compacted return true;
const std::vector<int>& file_size = }
vstorage_->FilesByCompactionPri(start_level_);
const std::vector<FileMetaData*>& level_files = const std::vector<FileMetaData*>& level_files =
vstorage_->LevelFiles(start_level_); vstorage_->LevelFiles(start_level_);
// Pick the file with the highest score in this level that is not already
// being compacted.
const std::vector<int>& file_scores =
vstorage_->FilesByCompactionPri(start_level_);
unsigned int cmp_idx; unsigned int cmp_idx;
for (cmp_idx = vstorage_->NextCompactionIndex(start_level_); for (cmp_idx = vstorage_->NextCompactionIndex(start_level_);
cmp_idx < file_size.size(); cmp_idx++) { cmp_idx < file_scores.size(); cmp_idx++) {
int index = file_size[cmp_idx]; int index = file_scores[cmp_idx];
auto* f = level_files[index]; auto* f = level_files[index];
// do not pick a file to compact if it is being compacted // do not pick a file to compact if it is being compacted
@ -460,7 +545,6 @@ bool LevelCompactionBuilder::PickFileToCompact() {
} }
start_level_inputs_.files.push_back(f); start_level_inputs_.files.push_back(f);
start_level_inputs_.level = start_level_;
if (!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_, if (!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
&start_level_inputs_) || &start_level_inputs_) ||
compaction_picker_->FilesRangeOverlapWithCompaction( compaction_picker_->FilesRangeOverlapWithCompaction(
@ -478,8 +562,8 @@ bool LevelCompactionBuilder::PickFileToCompact() {
continue; continue;
} }
// Now that input level is fully expanded, we check whether any output files // Now that input level is fully expanded, we check whether any output
// are locked due to pending compaction. // files are locked due to pending compaction.
// //
// Note we rely on ExpandInputsToCleanCut() to tell us whether any output- // Note we rely on ExpandInputsToCleanCut() to tell us whether any output-
// level files are locked, not just the extra ones pulled in for user-key // level files are locked, not just the extra ones pulled in for user-key

@ -2249,6 +2249,92 @@ TEST_F(CompactionPickerTest, IsTrivialMoveOn) {
ASSERT_TRUE(compaction->IsTrivialMove()); ASSERT_TRUE(compaction->IsTrivialMove());
} }
TEST_F(CompactionPickerTest, L0TrivialMove1) {
mutable_cf_options_.max_bytes_for_level_base = 10000000u;
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
mutable_cf_options_.max_compaction_bytes = 10000000u;
ioptions_.level_compaction_dynamic_level_bytes = false;
NewVersionStorage(6, kCompactionStyleLevel);
Add(0, 1U, "100", "150", 3000U, 0, 710, 800);
Add(0, 2U, "151", "200", 3001U, 0, 610, 700);
Add(0, 3U, "301", "350", 3000U, 0, 510, 600);
Add(0, 4U, "451", "400", 3000U, 0, 410, 500);
Add(1, 5U, "120", "130", 7000U);
Add(1, 6U, "170", "180", 7000U);
Add(1, 7U, "220", "230", 7000U);
Add(1, 8U, "270", "280", 7000U);
UpdateVersionStorageInfo();
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
&log_buffer_));
ASSERT_TRUE(compaction.get() != nullptr);
ASSERT_EQ(1, compaction->num_input_levels());
ASSERT_EQ(2, compaction->num_input_files(0));
ASSERT_EQ(3, compaction->input(0, 0)->fd.GetNumber());
ASSERT_EQ(4, compaction->input(0, 1)->fd.GetNumber());
ASSERT_TRUE(compaction->IsTrivialMove());
}
TEST_F(CompactionPickerTest, L0TrivialMoveOneFile) {
mutable_cf_options_.max_bytes_for_level_base = 10000000u;
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
mutable_cf_options_.max_compaction_bytes = 10000000u;
ioptions_.level_compaction_dynamic_level_bytes = false;
NewVersionStorage(6, kCompactionStyleLevel);
Add(0, 1U, "100", "150", 3000U, 0, 710, 800);
Add(0, 2U, "551", "600", 3001U, 0, 610, 700);
Add(0, 3U, "101", "150", 3000U, 0, 510, 600);
Add(0, 4U, "451", "400", 3000U, 0, 410, 500);
Add(1, 5U, "120", "130", 7000U);
Add(1, 6U, "170", "180", 7000U);
Add(1, 7U, "220", "230", 7000U);
Add(1, 8U, "270", "280", 7000U);
UpdateVersionStorageInfo();
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
&log_buffer_));
ASSERT_TRUE(compaction.get() != nullptr);
ASSERT_EQ(1, compaction->num_input_levels());
ASSERT_EQ(1, compaction->num_input_files(0));
ASSERT_EQ(4, compaction->input(0, 0)->fd.GetNumber());
ASSERT_TRUE(compaction->IsTrivialMove());
}
TEST_F(CompactionPickerTest, L0TrivialMoveWholeL0) {
mutable_cf_options_.max_bytes_for_level_base = 10000000u;
mutable_cf_options_.level0_file_num_compaction_trigger = 4;
mutable_cf_options_.max_compaction_bytes = 10000000u;
ioptions_.level_compaction_dynamic_level_bytes = false;
NewVersionStorage(6, kCompactionStyleLevel);
Add(0, 1U, "300", "350", 3000U, 0, 710, 800);
Add(0, 2U, "651", "600", 3001U, 0, 610, 700);
Add(0, 3U, "501", "550", 3000U, 0, 510, 600);
Add(0, 4U, "451", "400", 3000U, 0, 410, 500);
Add(1, 5U, "120", "130", 7000U);
Add(1, 6U, "970", "980", 7000U);
UpdateVersionStorageInfo();
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
cf_name_, mutable_cf_options_, mutable_db_options_, vstorage_.get(),
&log_buffer_));
ASSERT_TRUE(compaction.get() != nullptr);
ASSERT_EQ(1, compaction->num_input_levels());
ASSERT_EQ(4, compaction->num_input_files(0));
ASSERT_EQ(1, compaction->input(0, 0)->fd.GetNumber());
ASSERT_EQ(4, compaction->input(0, 1)->fd.GetNumber());
ASSERT_EQ(3, compaction->input(0, 2)->fd.GetNumber());
ASSERT_EQ(2, compaction->input(0, 3)->fd.GetNumber());
ASSERT_TRUE(compaction->IsTrivialMove());
}
TEST_F(CompactionPickerTest, IsTrivialMoveOffSstPartitioned) { TEST_F(CompactionPickerTest, IsTrivialMoveOffSstPartitioned) {
mutable_cf_options_.max_bytes_for_level_base = 10000u; mutable_cf_options_.max_bytes_for_level_base = 10000u;
mutable_cf_options_.max_compaction_bytes = 10001u; mutable_cf_options_.max_compaction_bytes = 10001u;

@ -758,7 +758,8 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSortedRuns(
Temperature::kUnknown, Temperature::kUnknown,
/* max_subcompactions */ 0, grandparents, /* max_subcompactions */ 0, grandparents,
/* is manual */ false, /* trim_ts */ "", score_, /* is manual */ false, /* trim_ts */ "", score_,
false /* deletion_compaction */, compaction_reason); false /* deletion_compaction */,
/* l0_files_might_overlap */ true, compaction_reason);
} }
// Look at overall size amplification. If size amplification // Look at overall size amplification. If size amplification
@ -1083,6 +1084,7 @@ Compaction* UniversalCompactionBuilder::PickIncrementalForReduceSizeAmp(
Temperature::kUnknown, Temperature::kUnknown,
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false, /* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */, /* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true,
CompactionReason::kUniversalSizeAmplification); CompactionReason::kUniversalSizeAmplification);
} }
@ -1225,6 +1227,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
Temperature::kUnknown, Temperature::kUnknown,
/* max_subcompactions */ 0, grandparents, /* is manual */ false, /* max_subcompactions */ 0, grandparents, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */, /* trim_ts */ "", score_, false /* deletion_compaction */,
/* l0_files_might_overlap */ true,
CompactionReason::kFilesMarkedForCompaction); CompactionReason::kFilesMarkedForCompaction);
} }
@ -1300,7 +1303,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToOldest(
Temperature::kUnknown, Temperature::kUnknown,
/* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false, /* max_subcompactions */ 0, /* grandparents */ {}, /* is manual */ false,
/* trim_ts */ "", score_, false /* deletion_compaction */, /* trim_ts */ "", score_, false /* deletion_compaction */,
compaction_reason); /* l0_files_might_overlap */ true, compaction_reason);
} }
Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() { Compaction* UniversalCompactionBuilder::PickPeriodicCompaction() {

@ -7,6 +7,7 @@
// Use of this source code is governed by a BSD-style license that can be // 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. // found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "rocksdb/options.h"
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
#include <fcntl.h> #include <fcntl.h>
@ -553,7 +554,10 @@ TEST_F(CorruptionTest, CorruptedDescriptor) {
ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
DBImpl* dbi = static_cast_with_check<DBImpl>(db_); DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
ASSERT_OK(dbi->TEST_FlushMemTable()); ASSERT_OK(dbi->TEST_FlushMemTable());
ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr)); CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr));
Corrupt(kDescriptorFile, 0, 1000); Corrupt(kDescriptorFile, 0, 1000);
Status s = TryReopen(); Status s = TryReopen();
@ -770,7 +774,9 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
DBImpl* dbi = static_cast_with_check<DBImpl>(db_); DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
ASSERT_OK(dbi->TEST_FlushMemTable()); ASSERT_OK(dbi->TEST_FlushMemTable());
mock->SetCorruptionMode(mode); mock->SetCorruptionMode(mode);
s = dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true); CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
s = dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr);
if (mode == mock::MockTableFactory::kCorruptNone) { if (mode == mock::MockTableFactory::kCorruptNone) {
ASSERT_OK(s); ASSERT_OK(s);
} else { } else {
@ -806,7 +812,10 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
} else { } else {
DBImpl* dbi = static_cast_with_check<DBImpl>(db_); DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
ASSERT_OK(dbi->TEST_FlushMemTable()); ASSERT_OK(dbi->TEST_FlushMemTable());
ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true)); CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr));
} }
db_->ReleaseSnapshot(snap); db_->ReleaseSnapshot(snap);
} }
@ -842,7 +851,10 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
} else { } else {
DBImpl* dbi = static_cast_with_check<DBImpl>(db_); DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
ASSERT_OK(dbi->TEST_FlushMemTable()); ASSERT_OK(dbi->TEST_FlushMemTable());
ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true)); CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr));
} }
db_->ReleaseSnapshot(snap); db_->ReleaseSnapshot(snap);
} }
@ -875,7 +887,10 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
} else { } else {
DBImpl* dbi = static_cast_with_check<DBImpl>(db_); DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
ASSERT_OK(dbi->TEST_FlushMemTable()); ASSERT_OK(dbi->TEST_FlushMemTable());
ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true)); CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr));
} }
db_->ReleaseSnapshot(snap); db_->ReleaseSnapshot(snap);
} }
@ -902,7 +917,10 @@ TEST_F(CorruptionTest, LogCorruptionErrorsInCompactionIterator) {
DBImpl* dbi = static_cast_with_check<DBImpl>(db_); DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
ASSERT_OK(dbi->TEST_FlushMemTable()); ASSERT_OK(dbi->TEST_FlushMemTable());
Status s = dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true); CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
Status s =
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr);
ASSERT_NOK(s); ASSERT_NOK(s);
ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(s.IsCorruption());
} }
@ -928,7 +946,10 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
mock->SetCorruptionMode(mock::MockTableFactory::kCorruptNone); mock->SetCorruptionMode(mock::MockTableFactory::kCorruptNone);
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "true"}})); ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "true"}}));
ASSERT_NOK(dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true)); CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_NOK(
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr));
} }
TEST_F(CorruptionTest, FlushKeyOrderCheck) { TEST_F(CorruptionTest, FlushKeyOrderCheck) {
@ -975,7 +996,10 @@ TEST_F(CorruptionTest, DisableKeyOrderCheck) {
ASSERT_OK(db_->Put(WriteOptions(), "foo2", "v1")); ASSERT_OK(db_->Put(WriteOptions(), "foo2", "v1"));
ASSERT_OK(db_->Put(WriteOptions(), "foo4", "v1")); ASSERT_OK(db_->Put(WriteOptions(), "foo4", "v1"));
ASSERT_OK(dbi->TEST_FlushMemTable()); ASSERT_OK(dbi->TEST_FlushMemTable());
ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr, nullptr, true)); CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(
dbi->CompactRange(cro, dbi->DefaultColumnFamily(), nullptr, nullptr));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
} }

@ -1093,16 +1093,20 @@ TEST_F(DBCompactionTest, ManualCompactionUnknownOutputSize) {
// create two files in l1 that we can compact // create two files in l1 that we can compact
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
for (int j = 0; j < options.level0_file_num_compaction_trigger; j++) { for (int j = 0; j < options.level0_file_num_compaction_trigger; j++) {
// make l0 files' ranges overlap to avoid trivial move
ASSERT_OK(Put(std::to_string(2 * i), std::string(1, 'A'))); ASSERT_OK(Put(std::to_string(2 * i), std::string(1, 'A')));
ASSERT_OK(Put(std::to_string(2 * i + 1), std::string(1, 'A'))); ASSERT_OK(Put(std::to_string(2 * i + 1), std::string(1, 'A')));
ASSERT_OK(Flush()); ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable()); ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
} }
ASSERT_OK(dbfull()->TEST_WaitForCompact()); ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0);
ASSERT_EQ(NumTableFilesAtLevel(1, 0), i + 1);
} }
ASSERT_OK(
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "2"}}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0);
ASSERT_EQ(NumTableFilesAtLevel(1, 0), 2);
ASSERT_OK(
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "3"}}));
ColumnFamilyMetaData cf_meta; ColumnFamilyMetaData cf_meta;
dbfull()->GetColumnFamilyMetaData(dbfull()->DefaultColumnFamily(), &cf_meta); dbfull()->GetColumnFamilyMetaData(dbfull()->DefaultColumnFamily(), &cf_meta);
@ -4366,7 +4370,13 @@ TEST_F(DBCompactionTest, LevelTtlBooster) {
ASSERT_OK(Flush()); ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact()); ASSERT_OK(dbfull()->TEST_WaitForCompact());
} }
// Force files to be compacted to L1
ASSERT_OK(
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "1"}}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,1,2", FilesPerLevel()); ASSERT_EQ("0,1,2", FilesPerLevel());
ASSERT_OK(
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "2"}}));
ASSERT_GT(SizeAtLevel(1), kNumKeysPerFile * 4 * kValueSize); ASSERT_GT(SizeAtLevel(1), kNumKeysPerFile * 4 * kValueSize);
} }

Loading…
Cancel
Save