Fixing Failed Assertion in Subcompaction State Diff

Summary:
In D43239 (https://reviews.facebook.net/D43239) there is an
assertion to make sure a subcompaction's output is never empty at the
end of execution. This assertion however breaks the build because some
tests lead to exactly that scenario. So instead I have altered the logic
to handle this case instead of just failing the assertion.

The reason that it is possible for a subcompaction's output to be empty is
that during a sequential execution of subcompactions, if a user aborts the
compaction job then some of the later subcompactions to be executed may
have yet to process any keys and therefore have yet to generate output files.
This becomes very rare once the subcompactions are executed in parallel,
but for now they are still sequential so the case is possible when there is an
early termination, as in some of the tests.

Test Plan: ./db_test  ./db_compaction_test

Reviewers: sdong, igor, anthony, yhchiang

Reviewed By: yhchiang

Subscribers: dhruba

Differential Revision: https://reviews.facebook.net/D44877
This commit is contained in:
Ari Ekmekji 2015-08-18 12:27:12 -07:00
parent f0da6977a3
commit 601b1aaca0

View File

@ -84,8 +84,16 @@ struct CompactionJob::SubCompactionState {
std::unique_ptr<WritableFileWriter> outfile;
std::unique_ptr<TableBuilder> builder;
Output* current_output() {
assert(!outputs.empty());
return &outputs.back();
if (outputs.empty()) {
// This subcompaction's ouptut could be empty if compaction was aborted
// before this subcompaction had a chance to generate any output files.
// When subcompactions are executed sequentially this is more likely and
// will be particulalry likely for the last subcompaction to be empty.
// Once they are run in parallel however it should be much rarer.
return nullptr;
} else {
return &outputs.back();
}
}
// State during the sub-compaction
@ -152,15 +160,26 @@ struct CompactionJob::CompactionState {
}
Slice SmallestUserKey() {
assert(!sub_compact_states.empty() &&
sub_compact_states[0].start == nullptr);
return sub_compact_states[0].outputs[0].smallest.user_key();
for (size_t i = 0; i < sub_compact_states.size(); i++) {
if (!sub_compact_states[i].outputs.empty()) {
return sub_compact_states[i].outputs[0].smallest.user_key();
}
}
// TODO(aekmekji): should we exit with an error if it reaches here?
assert(0);
return Slice(nullptr, 0);
}
Slice LargestUserKey() {
assert(!sub_compact_states.empty() &&
sub_compact_states.back().end == nullptr);
return sub_compact_states.back().current_output()->largest.user_key();
for (int i = static_cast<int>(sub_compact_states.size() - 1); i >= 0; i--) {
if (!sub_compact_states[i].outputs.empty()) {
assert(sub_compact_states[i].current_output() != nullptr);
return sub_compact_states[i].current_output()->largest.user_key();
}
}
// TODO(aekmekji): should we exit with an error if it reaches here?
assert(0);
return Slice(nullptr, 0);
}
};
@ -768,6 +787,7 @@ Status CompactionJob::WriteKeyValue(const Slice& key, const Slice& value,
}
}
assert(sub_compact->builder != nullptr);
assert(sub_compact->current_output() != nullptr);
SequenceNumber seqno = GetInternalKeySeqno(newkey);
if (sub_compact->builder->NumEntries() == 0) {
@ -825,6 +845,7 @@ Status CompactionJob::FinishCompactionOutputFile(const Status& input_status,
assert(sub_compact != nullptr);
assert(sub_compact->outfile);
assert(sub_compact->builder != nullptr);
assert(sub_compact->current_output() != nullptr);
const uint64_t output_number = sub_compact->current_output()->number;
const uint32_t output_path_id = sub_compact->current_output()->path_id;