@@ -4464,72 +4464,138 @@ TEST_P(DBMultiScanIteratorTest, RangeBetweenFiles) {
44644464 iter.reset ();
44654465}
44664466
4467- TEST_P (DBMultiScanIteratorTest, OutOfL0FileRange) {
4468- // Test that prepare does not fail scan when a scan range
4469- // is outside of a L0 file's key range.
4467+ // This test case tests multiscan in the presence of fragmented range
4468+ // tombstones in the LSM.
4469+ TEST_P (DBMultiScanIteratorTest, FragmentedRangeTombstones) {
44704470 auto options = CurrentOptions ();
4471+ // Compaction may create files 2x the target_file_size_base,
4472+ // so set this to 50KB so we atleast end up with 2 files of
4473+ // 100KB
4474+ options.target_file_size_base = 50 << 10 ; // 50KB
4475+ options.compaction_style = kCompactionStyleUniversal ;
4476+ options.num_levels = 50 ;
44714477 options.compression = kNoCompression ;
44724478 DestroyAndReopen (options);
44734479
4474- Random rnd (301 );
4475- // Create a Lmax file
4476- // key01 ~ key99
4477- for (int i = 0 ; i < 100 ; ++i) {
4478- std::stringstream ss;
4479- ss << std::setw (2 ) << std::setfill (' 0' ) << i;
4480- ASSERT_OK (Put (" k" + ss.str (), rnd.RandomString (1024 )));
4481- }
4480+ // Setup the LSM as follows -
4481+ // 1. Ingest a file with 100 keys
4482+ // 2. Ingest a file with one overlapping key
4483+ // 3. Do a Put and flush a file to L0 with one overlapping key
4484+ // 4. Ingest a standalone delete range file that covers the full key space
4485+ // and a file with the same 100 keys with new values. This will ingest
4486+ // into L0 due to the presence of an existing file in L0
4487+ // The final LSM will have an SST in Lmax with 100 keys, and 2 SST files
4488+ // in Lmax-1 with half the keys each and completely overlapping delete ranges
4489+ std::unordered_map<std::string, std::string> kvs;
4490+ auto rnd = Random::GetTLSInstance ();
4491+ auto create_ingestion_data_file_and_update_key_value =
4492+ [&](const std::string& filename, int start_key, int end_key) {
4493+ std::unique_ptr<SstFileWriter> writer;
4494+ writer.reset (new SstFileWriter (EnvOptions (), options));
4495+ ASSERT_OK (writer->Open (filename));
4496+ for (int i = start_key; i < end_key; ++i) {
4497+ auto kiter = kvs.find (Key (i));
4498+ if (kiter != kvs.end ()) {
4499+ kvs.erase (kiter);
4500+ }
4501+ auto res =
4502+ kvs.emplace (std::make_pair (Key (i), rnd->RandomString (2 << 10 )));
4503+ ASSERT_OK (writer->Put (res.first ->first , res.first ->second ));
4504+ }
4505+ ASSERT_OK (writer->Finish ());
4506+ writer.reset ();
4507+ };
4508+
4509+ CreateColumnFamilies ({" new_cf" }, options);
4510+ std::string ingest_file = dbname_ + " test.sst" ;
4511+ // Write ~200KB data
4512+ create_ingestion_data_file_and_update_key_value (ingest_file + " _0" , 0 , 100 );
4513+ create_ingestion_data_file_and_update_key_value (ingest_file + " _1" , 50 , 51 );
4514+ ColumnFamilyHandle* cfh = handles_[0 ];
4515+ IngestExternalFileOptions ifo;
4516+ Status s = dbfull ()->IngestExternalFile (
4517+ cfh, {ingest_file + " _0" , ingest_file + " _1" }, ifo);
4518+ ASSERT_OK (s);
4519+
4520+ ASSERT_OK (Put (0 , Key (50 ), rnd->RandomString (2 << 10 )));
44824521 ASSERT_OK (Flush ());
4483- CompactRangeOptions cro;
4484- cro.bottommost_level_compaction = BottommostLevelCompaction::kForce ;
4485- ASSERT_OK (db_->CompactRange (cro, nullptr , nullptr ));
44864522
4487- // Create a L0 file
4488- // key00 ~ key09
4489- for (int i = 0 ; i < 10 ; ++i) {
4490- std::stringstream ss;
4491- ss << std::setw (2 ) << std::setfill (' 0' ) << i;
4492- ASSERT_OK (Put (" k" + ss.str (), rnd.RandomString (1024 )));
4523+ {
4524+ std::unique_ptr<SstFileWriter> writer;
4525+ writer.reset (new SstFileWriter (EnvOptions (), options));
4526+ ASSERT_OK (writer->Open (ingest_file + " _2" ));
4527+ ASSERT_OK (writer->DeleteRange (" a" , " z" ));
4528+ ASSERT_OK (writer->Finish ());
4529+ writer.reset ();
44934530 }
4494- ASSERT_OK (Flush ());
4495- ASSERT_EQ (NumTableFilesAtLevel (0 ), 1 );
4531+ create_ingestion_data_file_and_update_key_value (ingest_file + " _3" , 0 , 100 );
4532+ s = dbfull ()->IngestExternalFile (
4533+ cfh, {ingest_file + " _2" , ingest_file + " _3" }, ifo);
4534+ ASSERT_OK (s);
44964535
4497- // The second range is outside of L0 file's key range
4498- std::vector<std::string> key_ranges ({" k04" , " k06" , " k12" , " k14" });
4536+ ASSERT_OK (dbfull ()->TEST_WaitForCompact ());
4537+
4538+ // The first scan range overlaps the DB key range, while the second extends
4539+ // beyond but overlaps the delete range
4540+ std::vector<std::string> key_ranges ({" key000085" , " key000090" , " l" , " n" });
44994541 ReadOptions ro;
4500- Slice ub;
4501- ro.iterate_upper_bound = &ub;
45024542 ro.fill_cache = GetParam ();
45034543 MultiScanArgs scan_options (BytewiseComparator ());
45044544 scan_options.insert (key_ranges[0 ], key_ranges[1 ]);
45054545 scan_options.insert (key_ranges[2 ], key_ranges[3 ]);
4506- ColumnFamilyHandle* cfh = dbfull ()->DefaultColumnFamily ();
4507- std::unique_ptr<Iterator> iter (dbfull ()->NewIterator (ro, cfh));
4508- ASSERT_NE (iter, nullptr );
4509- iter->Prepare (scan_options);
4510- int count = 0 ;
4511- ub = key_ranges[1 ];
4512- iter->Seek (key_ranges[0 ]);
4513- while (iter->status ().ok () && iter->Valid ()) {
4514- ASSERT_GE (iter->key ().compare (key_ranges[0 ]), 0 );
4515- ASSERT_LT (iter->key ().compare (key_ranges[1 ]), 0 );
4516- count++;
4517- iter->Next ();
4546+ std::unique_ptr<MultiScan> iter =
4547+ dbfull ()->NewMultiScan (ro, cfh, scan_options);
4548+ try {
4549+ int i = 0 ;
4550+ int count = 0 ;
4551+ for (auto range : *iter) {
4552+ for (auto it : range) {
4553+ ASSERT_GE (it.first .ToString (), key_ranges[i]);
4554+ ASSERT_LT (it.first .ToString (), key_ranges[i + 1 ]);
4555+ auto kiter = kvs.find (it.first .ToString ());
4556+ ASSERT_NE (kiter, kvs.end ());
4557+ ASSERT_EQ (kiter->second , it.second .ToString ());
4558+ count++;
4559+ }
4560+ i += 2 ;
4561+ }
4562+ ASSERT_EQ (i, 4 );
4563+ ASSERT_EQ (count, 5 );
4564+ } catch (MultiScanException& ex) {
4565+ ASSERT_OK (ex.status ());
45184566 }
4519- ASSERT_OK (iter->status ()) << iter->status ().ToString ();
4520- ASSERT_EQ (count, 2 );
4567+ iter.reset ();
45214568
4522- ub = key_ranges[3 ];
4523- count = 0 ;
4524- iter->Seek (key_ranges[2 ]);
4525- while (iter->status ().ok () && iter->Valid ()) {
4526- ASSERT_GE (iter->key ().compare (key_ranges[2 ]), 0 );
4527- ASSERT_LT (iter->key ().compare (key_ranges[3 ]), 0 );
4528- count++;
4529- iter->Next ();
4569+ // The second scan range start overlaps the delete range in the first file
4570+ // in Lmax-1, while the end overlaps the keys in the second file
4571+ (*scan_options).clear ();
4572+ key_ranges[0 ] = " key000010" ;
4573+ key_ranges[1 ] = " key000020" ;
4574+ key_ranges[2 ] = " key0000500" ;
4575+ key_ranges[3 ] = " key000060" ;
4576+ scan_options.insert (key_ranges[0 ], key_ranges[1 ]);
4577+ scan_options.insert (key_ranges[2 ], key_ranges[3 ]);
4578+ iter = dbfull ()->NewMultiScan (ro, cfh, scan_options);
4579+ try {
4580+ int i = 0 ;
4581+ int count = 0 ;
4582+ for (auto range : *iter) {
4583+ for (auto it : range) {
4584+ ASSERT_GE (it.first .ToString (), key_ranges[i]);
4585+ ASSERT_LT (it.first .ToString (), key_ranges[i + 1 ]);
4586+ auto kiter = kvs.find (it.first .ToString ());
4587+ ASSERT_NE (kiter, kvs.end ());
4588+ ASSERT_EQ (kiter->second , it.second .ToString ());
4589+ count++;
4590+ }
4591+ i += 2 ;
4592+ }
4593+ ASSERT_EQ (i, 4 );
4594+ ASSERT_EQ (count, 19 );
4595+ } catch (MultiScanException& ex) {
4596+ ASSERT_OK (ex.status ());
45304597 }
4531- ASSERT_OK (iter->status ()) << iter->status ().ToString ();
4532- ASSERT_EQ (count, 2 );
4598+ iter.reset ();
45334599}
45344600
45354601} // namespace ROCKSDB_NAMESPACE
0 commit comments