Skip to content

Commit 1061f74

Browse files
committed
UnitTest for Remote Compaction Empty Result (facebook#13812)
Summary: Unit Test for a repro for the fix that was reported by facebook#13743 There's potential dataloss when Remote Compaction entries are all removed due to various reasons (CompactionFilter, DeleteRange covering all keys of the SST file, etc) Pull Request resolved: facebook#13812 Test Plan: ``` ./compaction_service_test --gtest_filter="*CompactionServiceTest.EmptyResult*" ``` Failed before merging facebook#13743, now passing Reviewed By: cbi42 Differential Revision: D79192829 Pulled By: jaykorean fbshipit-source-id: e200300c4a7993de21c63cd92bda65b692921b89
1 parent 6a83adc commit 1061f74

3 files changed

Lines changed: 78 additions & 1 deletion

File tree

db/compaction/compaction_job.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1736,7 +1736,6 @@ Status CompactionJob::FinishCompactionOutputFile(
17361736
if (s.ok()) {
17371737
tp = outputs.GetTableProperties();
17381738
}
1739-
17401739
if (s.ok() && current_entries == 0 && tp.num_range_deletions == 0) {
17411740
// If there is nothing to output, no necessary to generate a sst file.
17421741
// This happens when the output level is bottom level, at the same time
@@ -1940,6 +1939,10 @@ Status CompactionJob::OpenCompactionOutputFile(SubcompactionState* sub_compact,
19401939

19411940
// no need to lock because VersionSet::next_file_number_ is atomic
19421941
uint64_t file_number = versions_->NewFileNumber();
1942+
#ifndef NDEBUG
1943+
TEST_SYNC_POINT_CALLBACK(
1944+
"CompactionJob::OpenCompactionOutputFile::NewFileNumber", &file_number);
1945+
#endif
19431946
std::string fname = GetTableFileName(file_number);
19441947
// Fire events.
19451948
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();

db/compaction/compaction_service_test.cc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,79 @@ TEST_F(CompactionServiceTest, VerifyInputRecordCount) {
793793
SyncPoint::GetInstance()->ClearAllCallBacks();
794794
}
795795

796+
TEST_F(CompactionServiceTest, EmptyResult) {
797+
Options options = CurrentOptions();
798+
options.disable_auto_compactions = true;
799+
ReopenWithCompactionService(&options);
800+
GenerateTestData();
801+
802+
auto my_cs = GetCompactionService();
803+
804+
uint64_t comp_num = my_cs->GetCompactionNum();
805+
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
806+
ASSERT_GE(my_cs->GetCompactionNum(), comp_num + 1);
807+
808+
// Delete range to cover entire range
809+
ASSERT_OK(db_->DeleteRange(WriteOptions(), "key", "keyz"));
810+
ASSERT_OK(Flush());
811+
812+
// In this unit test, both remote compaction and primary db instance are
813+
// running in the same process, so NewFileNumber will never have a collision.
814+
// In the real-world remote compactions, when the compaction is indeed running
815+
// in another process, this is not going to be the case.
816+
// To simulate the SST file with the same name created in the tmp directory,
817+
// override the file number in remote compaction to re-use old SST file
818+
// number.
819+
bool need_to_override_file_number = false;
820+
SyncPoint::GetInstance()->SetCallBack(
821+
"DBImplSecondary::OpenAndCompact::BeforeLoadingOptions:0",
822+
[&](void*) { need_to_override_file_number = true; });
823+
824+
SyncPoint::GetInstance()->SetCallBack(
825+
"CompactionJob::OpenCompactionOutputFile::NewFileNumber",
826+
[&](void* file_number) {
827+
if (need_to_override_file_number) {
828+
auto n = static_cast<uint64_t*>(file_number);
829+
ColumnFamilyMetaData cf_meta;
830+
db_->GetColumnFamilyMetaData(&cf_meta);
831+
for (const auto& level : cf_meta.levels) {
832+
for (const auto& file : level.files) {
833+
// Use one of the existing file name
834+
*n = test::GetFileNumber(file.name);
835+
need_to_override_file_number = false;
836+
return;
837+
}
838+
}
839+
}
840+
});
841+
842+
// Inject failure, so that the remote compaction fails after
843+
// ProcessKeyValueCompaction()
844+
SyncPoint::GetInstance()->SetCallBack(
845+
"DBImplSecondary::CompactWithoutInstallation::End", [&](void* status) {
846+
// override job status
847+
auto s = static_cast<Status*>(status);
848+
*s = Status::Aborted("MyTestCompactionService failed to compact!");
849+
});
850+
SyncPoint::GetInstance()->EnableProcessing();
851+
852+
// Compaction should fail and SST files in the primary db should exist
853+
{
854+
ASSERT_NOK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
855+
ColumnFamilyMetaData meta;
856+
db_->GetColumnFamilyMetaData(&meta);
857+
for (const auto& level : meta.levels) {
858+
for (const auto& file : level.files) {
859+
std::string fname = file.db_path + "/" + file.name;
860+
ASSERT_OK(db_->GetEnv()->FileExists(fname));
861+
}
862+
}
863+
}
864+
Close();
865+
SyncPoint::GetInstance()->DisableProcessing();
866+
SyncPoint::GetInstance()->ClearAllCallBacks();
867+
}
868+
796869
TEST_F(CompactionServiceTest, CorruptedOutput) {
797870
Options options = CurrentOptions();
798871
options.disable_auto_compactions = true;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a bug in remote compaction that may mistakenly delete live SST file(s) during the cleanup phase when no keys survive the compaction (all expired)

0 commit comments

Comments
 (0)