@@ -321,7 +321,8 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
321321 InternalIterator* input = iter_.get ();
322322 if (repair_enabled_) {
323323 repair_iter_ = std::make_unique<MissingBlobRepairIterator>(
324- input, repair_storage_.get (), cmp_->timestamp_size ());
324+ input, repair_storage_.get (), cmp_->timestamp_size (),
325+ repair_only_dropped_blob_files_);
325326 input = repair_iter_.get ();
326327 }
327328 c_iter_.reset (new CompactionIterator (
@@ -338,6 +339,15 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
338339 c_iter_->SetMissingBlobRepairIterator (repair_iter_.get ());
339340 }
340341
342+ // Repairs only references to `dropped_files`, which are recorded as dropped
343+ // in a Version that holds `healthy_files`.
344+ void EnableDroppedBlobRepair (const std::vector<uint64_t >& healthy_files,
345+ std::vector<uint64_t > dropped_files) {
346+ EnableMissingBlobRepair (healthy_files);
347+ repair_storage_->SetDroppedBlobFiles (std::move (dropped_files));
348+ repair_only_dropped_blob_files_ = true ;
349+ }
350+
341351 void EnableMissingBlobRepair (const std::vector<uint64_t >& healthy_files) {
342352 repair_enabled_ = true ;
343353 repair_storage_ = std::make_unique<VersionStorageInfo>(
@@ -415,6 +425,7 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
415425 const std::atomic<bool > kManualCompactionCanceledFalse_ {false };
416426 FakeCompaction* compaction_proxy_;
417427 bool repair_enabled_ = false ;
428+ bool repair_only_dropped_blob_files_ = false ;
418429};
419430
420431TEST_P (CompactionIteratorTest, MergeBeforeFilterFullOrder) {
@@ -1088,7 +1099,8 @@ TEST_P(CompactionIteratorTest, MissingBlobRepairPreservesInputErrors) {
10881099 std::unique_ptr<InternalIterator> failed_input (
10891100 NewErrorInternalIterator (Status::IOError (" Failed SST read" )));
10901101 MissingBlobRepairIterator failed_repair (failed_input.get (),
1091- repair_storage_.get (), 0 );
1102+ repair_storage_.get (), 0 ,
1103+ /* only_dropped_blob_files=*/ false );
10921104 failed_repair.SeekToFirst ();
10931105 ASSERT_FALSE (failed_repair.Valid ());
10941106 ASSERT_EQ (failed_repair.status ().code (), Status::Code::kIOError );
@@ -1129,7 +1141,8 @@ TEST_P(CompactionIteratorTest, MissingBlobRepairPreservesInputErrors) {
11291141
11301142 FailingNextIterator input ({test::KeyStr (" a" , 5 , kTypeBlobIndex )},
11311143 {BlobIndexForFile (20 )}, &icmp_);
1132- MissingBlobRepairIterator repair (&input, repair_storage_.get (), 0 );
1144+ MissingBlobRepairIterator repair (&input, repair_storage_.get (), 0 ,
1145+ /* only_dropped_blob_files=*/ false );
11331146 repair.SeekToFirst ();
11341147 ASSERT_OK (repair.status ());
11351148 ASSERT_TRUE (repair.Valid ());
@@ -1141,12 +1154,84 @@ TEST_P(CompactionIteratorTest, MissingBlobRepairPreservesInputErrors) {
11411154 ASSERT_FALSE (repair.IsRepairDeletion ());
11421155}
11431156
1157+ TEST_P (CompactionIteratorTest, MissingBlobRepairOnlyDroppedBlobFiles) {
1158+ // Blob file 10 is in the Version, 20 is recorded as dropped and 30 is
1159+ // missing without a record.
1160+ EnableMissingBlobRepair ({10 });
1161+ repair_storage_->SetDroppedBlobFiles ({20 });
1162+ VectorIterator input (
1163+ {test::KeyStr (" a" , 5 , kTypeBlobIndex ),
1164+ test::KeyStr (" b" , 4 , kTypeBlobIndex ),
1165+ test::KeyStr (" c" , 3 , kTypeBlobIndex )},
1166+ {BlobIndexForFile (10 ), BlobIndexForFile (20 ), BlobIndexForFile (30 )},
1167+ &icmp_);
1168+ MissingBlobRepairIterator repair (&input, repair_storage_.get (), 0 ,
1169+ /* only_dropped_blob_files=*/ true );
1170+ repair.SeekToFirst ();
1171+ ASSERT_TRUE (repair.Valid ());
1172+ ASSERT_EQ (repair.key (), test::KeyStr (" a" , 5 , kTypeBlobIndex ));
1173+ ASSERT_FALSE (repair.IsRepairDeletion ());
1174+ repair.Next ();
1175+ ASSERT_TRUE (repair.Valid ());
1176+ ASSERT_EQ (repair.key (), test::KeyStr (" b" , 4 , kTypeDeletion ));
1177+ ASSERT_TRUE (repair.IsRepairDeletion ());
1178+ repair.Next ();
1179+ ASSERT_TRUE (repair.Valid ());
1180+ ASSERT_EQ (repair.key (), test::KeyStr (" c" , 3 , kTypeBlobIndex ));
1181+ ASSERT_EQ (repair.value (), BlobIndexForFile (30 ));
1182+ ASSERT_FALSE (repair.IsRepairDeletion ());
1183+ repair.Next ();
1184+ ASSERT_FALSE (repair.Valid ());
1185+ ASSERT_OK (repair.status ());
1186+ }
1187+
1188+ TEST_P (CompactionIteratorTest, MissingBlobRepairOnlyDroppedWideColumns) {
1189+ // Blob file 20 is in the Version, 30 is recorded as dropped and 40 is
1190+ // missing without a record.
1191+ EnableDroppedBlobRepair ({20 }, {30 });
1192+ BlobIndex healthy;
1193+ BlobIndex dropped;
1194+ BlobIndex unrecorded;
1195+ ASSERT_OK (healthy.DecodeFrom (BlobIndexForFile (20 )));
1196+ ASSERT_OK (dropped.DecodeFrom (BlobIndexForFile (30 )));
1197+ ASSERT_OK (unrecorded.DecodeFrom (BlobIndexForFile (40 )));
1198+
1199+ // Only the column in the dropped blob file is removed.
1200+ std::string mixed;
1201+ ASSERT_OK (WideColumnSerialization::SerializeV2 (
1202+ WideColumns{{" " , " x" }, {" c1" , " " }, {" c2" , " " }, {" c3" , " " }},
1203+ {{1 , dropped}, {2 , healthy}, {3 , unrecorded}}, mixed));
1204+ std::string mixed_repaired;
1205+ ASSERT_OK (WideColumnSerialization::SerializeV2 (
1206+ WideColumns{{" " , " x" }, {" c2" , " " }, {" c3" , " " }},
1207+ {{1 , healthy}, {2 , unrecorded}}, mixed_repaired));
1208+ // An entity without dropped columns is left as is.
1209+ std::string healthy_only;
1210+ ASSERT_OK (WideColumnSerialization::SerializeV2 (WideColumns{{" c2" , " " }},
1211+ {{0 , healthy}}, healthy_only));
1212+ // An entity that loses every column becomes a deletion.
1213+ std::string dropped_only;
1214+ ASSERT_OK (WideColumnSerialization::SerializeV2 (WideColumns{{" c1" , " " }},
1215+ {{0 , dropped}}, dropped_only));
1216+
1217+ RunTest ({test::KeyStr (" a" , 5 , kTypeWideColumnEntity ),
1218+ test::KeyStr (" b" , 5 , kTypeWideColumnEntity ),
1219+ test::KeyStr (" c" , 5 , kTypeWideColumnEntity ),
1220+ test::KeyStr (" c" , 3 , kTypeValue )},
1221+ {mixed, healthy_only, dropped_only, " older" },
1222+ {test::KeyStr (" a" , 5 , kTypeWideColumnEntity ),
1223+ test::KeyStr (" b" , 5 , kTypeWideColumnEntity ),
1224+ test::KeyStr (" c" , 5 , kTypeDeletion )},
1225+ {mixed_repaired, healthy_only, " " });
1226+ }
1227+
11441228TEST_P (CompactionIteratorTest, MissingBlobRepairSeekAndNextUseRewrittenKeys) {
11451229 EnableMissingBlobRepair ({});
11461230 VectorIterator input (
11471231 {test::KeyStr (" a" , 5 , kTypeBlobIndex ), test::KeyStr (" b" , 4 , kTypeValue )},
11481232 {BlobIndexForFile (10 ), " healthy" }, &icmp_);
1149- MissingBlobRepairIterator repair (&input, repair_storage_.get (), 0 );
1233+ MissingBlobRepairIterator repair (&input, repair_storage_.get (), 0 ,
1234+ /* only_dropped_blob_files=*/ false );
11501235 const std::string deletion = test::KeyStr (" a" , 5 , kTypeDeletion );
11511236 repair.Seek (deletion);
11521237 ASSERT_OK (repair.status ());
@@ -1173,14 +1258,16 @@ TEST_P(CompactionIteratorTest,
11731258 {
11741259 std::unique_ptr<InternalIterator> input (
11751260 NewErrorInternalIterator (Status::IOError (" Failed SST read" )));
1176- MissingBlobRepairIterator repair (input.get (), repair_storage_.get (), 0 );
1261+ MissingBlobRepairIterator repair (input.get (), repair_storage_.get (), 0 ,
1262+ /* only_dropped_blob_files=*/ false );
11771263 ASSERT_FALSE (repair.Valid ());
11781264 }
11791265 VectorIterator input ({test::KeyStr (" a" , 5 , kTypeWideColumnEntity )},
11801266 {" invalid entity" });
11811267 input.SeekToFirst ();
11821268 ASSERT_TRUE (input.Valid ());
1183- MissingBlobRepairIterator repair (&input, repair_storage_.get (), 0 );
1269+ MissingBlobRepairIterator repair (&input, repair_storage_.get (), 0 ,
1270+ /* only_dropped_blob_files=*/ false );
11841271}
11851272
11861273// It is possible that the output of the compaction iterator is empty even if
@@ -2321,7 +2408,8 @@ TEST_P(CompactionIteratorTsGcTest,
23212408 input.SeekToFirst ();
23222409 ASSERT_TRUE (input.Valid ());
23232410 MissingBlobRepairIterator repair (&input, repair_storage_.get (),
2324- cmp_->timestamp_size ());
2411+ cmp_->timestamp_size (),
2412+ /* only_dropped_blob_files=*/ false );
23252413}
23262414
23272415TEST_P (CompactionIteratorTsGcTest, MissingBlobRepairRespectsHistoryTrimming) {
@@ -2336,7 +2424,8 @@ TEST_P(CompactionIteratorTsGcTest, MissingBlobRepairRespectsHistoryTrimming) {
23362424 PutFixed64 (&trim_timestamp, 103 );
23372425 HistoryTrimmingIterator trimmed (&input, cmp_, trim_timestamp);
23382426 MissingBlobRepairIterator repair (&trimmed, repair_storage_.get (),
2339- cmp_->timestamp_size ());
2427+ cmp_->timestamp_size (),
2428+ /* only_dropped_blob_files=*/ false );
23402429 repair.SeekToFirst ();
23412430 ASSERT_OK (repair.status ());
23422431 ASSERT_TRUE (repair.Valid ());
0 commit comments