summaryrefslogtreecommitdiff
path: root/db/compaction
diff options
context:
space:
mode:
authorHui Xiao <huixiao@fb.com>2024-05-13 13:12:06 -0700
committerFacebook GitHub Bot <facebook-github-bot@users.noreply.github.com>2024-05-13 13:12:06 -0700
commit20213d01a333aca20182e5aab6e4513537d9758c (patch)
treed2764f8d169fd057000a78c4bc3bc04f63d18089 /db/compaction
parent7747abdc151be4227a3885379403bfd7a268208b (diff)
Fix crash in CompactFiles() of conflict range under `preclude_last_level_data_seconds > 0` (#12628)
Summary: **Context/Summary:** Previously `CompactFiles()` used `RangeOverlapWithCompaction()` to check for conflict when sanitizing input files while later used `FilesRangeOverlapWithCompaction()` to assert for no conflict. The latter function checks for more conflict scenarios than the former does, particularly the ones arising from `preclude_last_level_data_seconds > 0` (i.e, compaction can output to second-to-the-last level). So we ran into assertion violation in `CompactFiles()` like below ``` Assertion `output_level == 0 || !FilesRangeOverlapWithCompaction( input_files, output_level, Compaction::EvaluatePenultimateLevel(vstorage, ioptions_, start_level, output_level))' failed. ``` This PR make `CompactFiles()` used `FilesRangeOverlapWithCompaction()` and return Aborted status upon range conflict instead of crashing (during debug build) or proceed incorrectly (during non-debug build). To do so cleanly, I included a refactoring to make `FilesRangeOverlapWithCompaction()` part of `SanitizeAndConvertCompactionInputFiles()`, replacing `RangeOverlapWithCompaction()`. Pull Request resolved: https://github.com/facebook/rocksdb/pull/12628 Test Plan: New UT crashed before the fix and return correct status after the fix. Reviewed By: cbi42 Differential Revision: D57123536 Pulled By: hx235 fbshipit-source-id: f963a2c9e7ba1a9927a67fcc87f0dce126d3a430
Diffstat (limited to 'db/compaction')
-rw-r--r--db/compaction/compaction_picker.cc34
-rw-r--r--db/compaction/compaction_picker.h33
2 files changed, 43 insertions, 24 deletions
diff --git a/db/compaction/compaction_picker.cc b/db/compaction/compaction_picker.cc
index 57e4457db..e049d95b2 100644
--- a/db/compaction/compaction_picker.cc
+++ b/db/compaction/compaction_picker.cc
@@ -340,8 +340,8 @@ Compaction* CompactionPicker::CompactFiles(
#ifndef NDEBUG
assert(input_files.size());
// This compaction output should not overlap with a running compaction as
- // `SanitizeCompactionInputFiles` should've checked earlier and db mutex
- // shouldn't have been released since.
+ // `SanitizeAndConvertCompactionInputFiles` should've checked earlier and db
+ // mutex shouldn't have been released since.
int start_level = Compaction::kInvalidLevel;
for (const auto& in : input_files) {
// input_files should already be sorted by level
@@ -1040,19 +1040,18 @@ Status CompactionPicker::SanitizeCompactionInputFilesForAllLevels(
}
}
}
- if (RangeOverlapWithCompaction(smallestkey, largestkey, output_level)) {
- return Status::Aborted(
- "A running compaction is writing to the same output level in an "
- "overlapping key range");
- }
return Status::OK();
}
-Status CompactionPicker::SanitizeCompactionInputFiles(
+Status CompactionPicker::SanitizeAndConvertCompactionInputFiles(
std::unordered_set<uint64_t>* input_files,
- const ColumnFamilyMetaData& cf_meta, const int output_level) const {
+ const ColumnFamilyMetaData& cf_meta, const int output_level,
+ const VersionStorageInfo* vstorage,
+ std::vector<CompactionInputFiles>* converted_input_files) const {
assert(static_cast<int>(cf_meta.levels.size()) - 1 ==
cf_meta.levels[cf_meta.levels.size() - 1].level);
+ assert(converted_input_files);
+
if (output_level >= static_cast<int>(cf_meta.levels.size())) {
return Status::InvalidArgument(
"Output level for column family " + cf_meta.name +
@@ -1078,7 +1077,6 @@ Status CompactionPicker::SanitizeCompactionInputFiles(
Status s = SanitizeCompactionInputFilesForAllLevels(input_files, cf_meta,
output_level);
-
if (!s.ok()) {
return s;
}
@@ -1119,6 +1117,22 @@ Status CompactionPicker::SanitizeCompactionInputFiles(
}
}
+ s = GetCompactionInputsFromFileNumbers(converted_input_files, input_files,
+ vstorage, CompactionOptions());
+ if (!s.ok()) {
+ return s;
+ }
+ assert(converted_input_files->size() > 0);
+ if (output_level != 0 &&
+ FilesRangeOverlapWithCompaction(
+ *converted_input_files, output_level,
+ Compaction::EvaluatePenultimateLevel(
+ vstorage, ioptions_, (*converted_input_files)[0].level,
+ output_level))) {
+ return Status::Aborted(
+ "A running compaction is writing to the same output level(s) in an "
+ "overlapping key range");
+ }
return Status::OK();
}
diff --git a/db/compaction/compaction_picker.h b/db/compaction/compaction_picker.h
index 0cec71b47..88915d459 100644
--- a/db/compaction/compaction_picker.h
+++ b/db/compaction/compaction_picker.h
@@ -86,16 +86,20 @@ class CompactionPicker {
virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const = 0;
-// Sanitize the input set of compaction input files.
-// When the input parameters do not describe a valid compaction, the
-// function will try to fix the input_files by adding necessary
-// files. If it's not possible to conver an invalid input_files
-// into a valid one by adding more files, the function will return a
-// non-ok status with specific reason.
-//
- Status SanitizeCompactionInputFiles(std::unordered_set<uint64_t>* input_files,
- const ColumnFamilyMetaData& cf_meta,
- const int output_level) const;
+ // Sanitize the input set of compaction input files and convert it to
+ // `std::vector<CompactionInputFiles>` in the output parameter
+ // `converted_input_files`.
+ // When the input parameters do not describe a valid
+ // compaction, the function will try to fix the input_files by adding
+ // necessary files. If it's not possible to convert an invalid input_files
+ // into a valid one by adding more files, the function will return a
+ // non-ok status with specific reason.
+ //
+ Status SanitizeAndConvertCompactionInputFiles(
+ std::unordered_set<uint64_t>* input_files,
+ const ColumnFamilyMetaData& cf_meta, const int output_level,
+ const VersionStorageInfo* vstorage,
+ std::vector<CompactionInputFiles>* converted_input_files) const;
// Free up the files that participated in a compaction
//
@@ -109,8 +113,8 @@ class CompactionPicker {
// object.
//
// Caller must provide a set of input files that has been passed through
- // `SanitizeCompactionInputFiles` earlier. The lock should not be released
- // between that call and this one.
+ // `SanitizeAndConvertCompactionInputFiles` earlier. The lock should not be
+ // released between that call and this one.
Compaction* CompactFiles(const CompactionOptions& compact_options,
const std::vector<CompactionInputFiles>& input_files,
int output_level, VersionStorageInfo* vstorage,
@@ -120,6 +124,7 @@ class CompactionPicker {
// Converts a set of compaction input file numbers into
// a list of CompactionInputFiles.
+ // TODO(hx235): remove the unused paramter `compact_options`
Status GetCompactionInputsFromFileNumbers(
std::vector<CompactionInputFiles>* input_files,
std::unordered_set<uint64_t>* input_set,
@@ -225,8 +230,8 @@ class CompactionPicker {
protected:
const ImmutableOptions& ioptions_;
-// A helper function to SanitizeCompactionInputFiles() that
-// sanitizes "input_files" by adding necessary files.
+ // A helper function to SanitizeAndConvertCompactionInputFiles() that
+ // sanitizes "input_files" by adding necessary files.
virtual Status SanitizeCompactionInputFilesForAllLevels(
std::unordered_set<uint64_t>* input_files,
const ColumnFamilyMetaData& cf_meta, const int output_level) const;