@@ -87,6 +87,7 @@ namespace ROCKSDB_NAMESPACE {
8787namespace {
8888
8989const std::string kDummyValue (10000 , ' o' );
90+ constexpr auto kVerbose = false ;
9091
9192// DummyPropertiesCollector used to test BlockBasedTableProperties
9293class DummyPropertiesCollector : public TablePropertiesCollector {
@@ -934,7 +935,6 @@ class HarnessTest : public testing::Test {
934935
935936 void TestRandomAccess (Random* rnd, const std::vector<std::string>& keys,
936937 const stl_wrappers::KVMap& data) {
937- static const bool kVerbose = false ;
938938 InternalIterator* iter = constructor_->NewIterator ();
939939 ASSERT_TRUE (!iter->Valid ());
940940 stl_wrappers::KVMap::const_iterator model_iter = data.begin ();
@@ -7827,7 +7827,6 @@ class UserDefinedIndexTestBase : public BlockBasedTableTestBase {
78277827 read_opts.iterate_upper_bound = &ub;
78287828 std::unique_ptr<Iterator> iter (db->NewIterator (read_opts, cfh));
78297829 iter->Prepare (scan_opts);
7830- static const bool kVerbose = false ;
78317830 for (auto opt : opts) {
78327831 ub = opt.range .limit .value ();
78337832 iter->Seek (opt.range .start .value ());
@@ -8979,8 +8978,6 @@ std::ostream& operator<<(std::ostream& os,
89798978 << param.enable_compaction_with_sst_partitioner << " }" ;
89808979}
89818980
8982- constexpr auto kVerbose = false ;
8983-
89848981struct DataRange {
89858982 size_t start; // inclusive
89868983 size_t end; // exclusive
@@ -9131,23 +9128,47 @@ class UserDefinedIndexStressTest
91319128 }
91329129
91339130 void CreateSstFileWithRanges (const std::string& ingest_file,
9134- const DataRange& range) {
9135- std::unique_ptr<SstFileWriter> writer =
9136- std::make_unique <SstFileWriter>( EnvOptions (), options_) ;
9137- ASSERT_OK (writer-> Open (ingest_file)) ;
9131+ const std::vector< DataRange>& ranges,
9132+ bool & data_added) {
9133+ std::unique_ptr <SstFileWriter> writer ;
9134+ data_added = false ;
91389135
9139- assert (range. start != range. end ) ;
9136+ std::vector<DataRange> ranges_in_file ;
91409137
9141- if (range.is_range_delete ) {
9142- ASSERT_OK (writer->DeleteRange (range.start_key , range.end_key ));
9143- } else {
9144- for (size_t i = range.start ; i != range.end ;) {
9145- auto key = FormatKey (i);
9146- range.start < range.end ? i++ : i--;
9147- ASSERT_OK (writer->Put (key, range.value ));
9138+ for (auto const & range : ranges) {
9139+ assert (range.start != range.end );
9140+ if (range.skipped ) {
9141+ continue ;
9142+ }
9143+
9144+ if (writer == nullptr ) {
9145+ writer = std::make_unique<SstFileWriter>(EnvOptions (), options_);
9146+ ASSERT_OK (writer->Open (ingest_file));
9147+ }
9148+ ranges_in_file.push_back (range);
9149+
9150+ data_added = true ;
9151+
9152+ if (range.is_range_delete ) {
9153+ ASSERT_OK (writer->DeleteRange (range.start_key , range.end_key ));
9154+ } else {
9155+ for (size_t i = range.start ; i != range.end ;) {
9156+ auto key = FormatKey (i);
9157+ range.start < range.end ? i++ : i--;
9158+ ASSERT_OK (writer->Put (key, range.value ));
9159+ }
9160+ }
9161+ }
9162+ if (kVerbose ) {
9163+ std::cout << " Ingested file: " + ingest_file + " ; Range: {" << std::endl;
9164+ for (const auto & range : ranges_in_file) {
9165+ std::cout << " " << range.ToString () << " ," << std::endl;
91489166 }
9167+ std::cout << " }" << std::endl;
9168+ }
9169+ if (data_added) {
9170+ ASSERT_OK (writer->Finish ());
91499171 }
9150- ASSERT_OK (writer->Finish ()) << range.ToString ();
91519172 }
91529173
91539174 void RangeScan (std::unique_ptr<Iterator>& iter,
@@ -9267,17 +9288,42 @@ class UserDefinedIndexStressTest
92679288 void IngestFilesInOneLevel (const std::vector<DataRange>& ranges_in_level,
92689289 const std::string& ingest_file_name_prefix,
92699290 size_t & ingest_file_count,
9270- const IngestExternalFileOptions& ifo) {
9291+ const IngestExternalFileOptions& ifo,
9292+ bool combine_ranges = false ) {
92719293 std::vector<std::string> ingest_files;
92729294 // Generate SST file and bulk load them one level at a time
9273- for (auto const & range : ranges_in_level) {
9274- if (!range.skipped ) {
9295+ if (combine_ranges) {
9296+ size_t i = 0 ;
9297+ while (i < ranges_in_level.size ()) {
9298+ // if combine ranges, generate 1 SST file that combines muliple ranges
9299+ // together
9300+ size_t batch_end_idx =
9301+ std::min (i + rnd.Uniform (3 ) + 2 , ranges_in_level.size ());
9302+ bool data_added = false ;
92759303 ASSERT_NO_FATAL_FAILURE (CreateSstFileWithRanges (
92769304 ingest_file_name_prefix + std::to_string (ingest_file_count),
9277- range));
9278- ingest_files.push_back (ingest_file_name_prefix +
9279- std::to_string (ingest_file_count));
9280- ingest_file_count++;
9305+ {ranges_in_level.begin () + i,
9306+ ranges_in_level.begin () + batch_end_idx},
9307+ data_added));
9308+ if (data_added) {
9309+ ingest_files.push_back (ingest_file_name_prefix +
9310+ std::to_string (ingest_file_count));
9311+ ingest_file_count++;
9312+ }
9313+ i = batch_end_idx;
9314+ }
9315+ } else {
9316+ for (auto const & range : ranges_in_level) {
9317+ if (!range.skipped ) {
9318+ bool data_added = false ;
9319+ ASSERT_NO_FATAL_FAILURE (CreateSstFileWithRanges (
9320+ ingest_file_name_prefix + std::to_string (ingest_file_count),
9321+ {range}, data_added));
9322+ ASSERT_TRUE (data_added);
9323+ ingest_files.push_back (ingest_file_name_prefix +
9324+ std::to_string (ingest_file_count));
9325+ ingest_file_count++;
9326+ }
92819327 }
92829328 }
92839329
@@ -9314,9 +9360,9 @@ class UserDefinedIndexStressTest
93149360// Becuase query count == 2, level n+1 would only prepare 3-5. but since 4-6
93159361// got deleted in the upper level, they are not returned, so only 3 is
93169362// returned. Meantime the query should have return [3, 6]
9317- // One way to fix this is by preparing more data blocks once prepared blocks are
9318- // exhausted, but upper bound is not reached yet.
9319- // This requires following changes:
9363+ // One way to fix this is by preparing more data blocks once prepared blocks
9364+ // are exhausted, but upper bound is not reached yet. This requires following
9365+ // changes:
93209366// 1. Fix out of bound flag in block table iterator. Only set it if the key is
93219367// larger than the upper bound.
93229368// 2. Refactor the prepared block single dimension vector into 2 dimension of
@@ -9349,12 +9395,126 @@ TEST_P(UserDefinedIndexStressTest, DISABLED_PartialDeleteRange) {
93499395 ASSERT_NO_FATAL_FAILURE (ValidateQueryResult ());
93509396}
93519397
9398+ TEST_P (UserDefinedIndexStressTest, DeleteRangeMixedWithDataFile) {
9399+ // Create 2 column families. One use normal put/del, the other uses sst
9400+ // ingest.
9401+ // Test the case where there are 3 levels, the middle level is a delete
9402+ // range file that span across the entire key space. The top level file have
9403+ // multiple files and each one has both data and delete range Scan same
9404+ // range between the 2 CF and validate the result is same
9405+ SCOPED_TRACE (" Start with random seed: " + std::to_string (rand_seed_));
9406+ dbname_ = test::PerThreadDBPath (
9407+ " UserDefinedIndexStressTest_DeleteRangeMixedWithDataFile" );
9408+ SCOPED_TRACE (" dbname: " + dbname_);
9409+ ASSERT_NO_FATAL_FAILURE (SetupDB (dbname_));
9410+
9411+ // Test 3 levels.
9412+ // bottom level is normal data files.
9413+ ranges_in_levels_.push_back (GenerateKeyRanges (rnd.Uniform (3 ) + 4 , 2 , " L6" ));
9414+ // middle level delete range between each level
9415+ if (is_reverse_comparator_) {
9416+ ranges_in_levels_.push_back ({{.start = 100 ,
9417+ .end = 0 ,
9418+ .is_range_delete = true ,
9419+ .skipped = false ,
9420+ .start_key = " keyz" ,
9421+ .end_key = " key" }});
9422+ } else {
9423+ ranges_in_levels_.push_back ({{.start = 0 ,
9424+ .end = 100 ,
9425+ .is_range_delete = true ,
9426+ .skipped = false ,
9427+ .start_key = " key" ,
9428+ .end_key = " keyz" }});
9429+ }
9430+
9431+ // Top level is normal data files
9432+ ranges_in_levels_.push_back (GenerateKeyRanges (rnd.Uniform (3 ) + 4 , 2 , " L4" ));
9433+
9434+ IngestExternalFileOptions ifo;
9435+ ifo.snapshot_consistency = false ;
9436+ auto ingest_file_name_prefix = dbname_ + " ingest_file_" ;
9437+ size_t ingest_file_count = 0 ;
9438+ auto first_level = true ;
9439+ for (auto const & ranges_in_level : ranges_in_levels_) {
9440+ ASSERT_NO_FATAL_FAILURE (
9441+ IngestFilesInOneLevel (ranges_in_level, ingest_file_name_prefix,
9442+ ingest_file_count, ifo, true ));
9443+ if (first_level) {
9444+ first_level = false ;
9445+ if (enable_compaction_with_sst_partitioner_) {
9446+ // When compaction is enabled, do a compaction at the first level
9447+ ASSERT_NO_FATAL_FAILURE (CompactIngestedCF ());
9448+ }
9449+ }
9450+ }
9451+
9452+ ASSERT_NO_FATAL_FAILURE (AddDataToRegularCF ());
9453+
9454+ ASSERT_NO_FATAL_FAILURE (ValidateQueryResult ());
9455+ }
9456+
93529457TEST_P (UserDefinedIndexStressTest, DeleteRange) {
93539458 // Create 2 column families. One use normal put/del, the other uses sst
93549459 // ingest.
9355- // Test the case where there are 3 levels, the middle level is a delete range
9356- // file that span across the entire key space.
9357- // Range scan same range between the 2 CF and validate the result is same
9460+ // Test the case where there are 3 levels, the middle level is a delete
9461+ // range file that span across the entire key space. Range scan same range
9462+ // between the 2 CF and validate the result is same
9463+ SCOPED_TRACE (" Start with random seed: " + std::to_string (rand_seed_));
9464+ dbname_ = test::PerThreadDBPath (" UserDefinedIndexStressTest_DeleteRange" );
9465+ SCOPED_TRACE (" dbname: " + dbname_);
9466+ ASSERT_NO_FATAL_FAILURE (SetupDB (dbname_));
9467+
9468+ // Test 3 levels.
9469+ // bottom level is normal data files.
9470+ ranges_in_levels_.push_back (GenerateKeyRanges (rnd.Uniform (3 ) + 4 , 2 , " L6" ));
9471+ // middle level delete range between each level
9472+ if (is_reverse_comparator_) {
9473+ ranges_in_levels_.push_back ({{.start = 100 ,
9474+ .end = 0 ,
9475+ .is_range_delete = true ,
9476+ .skipped = false ,
9477+ .start_key = " keyz" ,
9478+ .end_key = " key" }});
9479+ } else {
9480+ ranges_in_levels_.push_back ({{.start = 0 ,
9481+ .end = 100 ,
9482+ .is_range_delete = true ,
9483+ .skipped = false ,
9484+ .start_key = " key" ,
9485+ .end_key = " keyz" }});
9486+ }
9487+ // Top level is normal data files
9488+ ranges_in_levels_.push_back (GenerateKeyRanges (rnd.Uniform (3 ) + 4 , 2 , " L4" ));
9489+
9490+ IngestExternalFileOptions ifo;
9491+ ifo.snapshot_consistency = false ;
9492+ auto ingest_file_name_prefix = dbname_ + " ingest_file_" ;
9493+ size_t ingest_file_count = 0 ;
9494+ auto first_level = true ;
9495+ for (auto const & ranges_in_level : ranges_in_levels_) {
9496+ ASSERT_NO_FATAL_FAILURE (IngestFilesInOneLevel (
9497+ ranges_in_level, ingest_file_name_prefix, ingest_file_count, ifo));
9498+ if (first_level) {
9499+ first_level = false ;
9500+ if (enable_compaction_with_sst_partitioner_) {
9501+ // When compaction is enabled, do a compaction at the first level
9502+ ASSERT_NO_FATAL_FAILURE (CompactIngestedCF ());
9503+ }
9504+ }
9505+ }
9506+
9507+ ASSERT_NO_FATAL_FAILURE (AddDataToRegularCF ());
9508+
9509+ ASSERT_NO_FATAL_FAILURE (ValidateQueryResult ());
9510+ }
9511+
9512+ TEST_P (UserDefinedIndexStressTest, AtomicReplaceBulkLoad) {
9513+ // Create 2 column families. One use normal put/del, the other uses sst
9514+ // ingest.
9515+ // Test the case where there are 3 levels, the middle level is a delete
9516+ // range file that span across the entire key space. Range scan same range
9517+ // between the 2 CF and validate the result is same
93589518 SCOPED_TRACE (" Start with random seed: " + std::to_string (rand_seed_));
93599519 dbname_ = test::PerThreadDBPath (" UserDefinedIndexStressTest_DeleteRange" );
93609520 SCOPED_TRACE (" dbname: " + dbname_);
@@ -9399,6 +9559,23 @@ TEST_P(UserDefinedIndexStressTest, DeleteRange) {
93999559 }
94009560 }
94019561
9562+ // Ingest the a new file with atomic replace with full key space, this layer
9563+ // is exactly same as the one at Level 4
9564+ bool data_added;
9565+ ASSERT_NO_FATAL_FAILURE (CreateSstFileWithRanges (
9566+ ingest_file_name_prefix + std::to_string (++ingest_file_count),
9567+ ranges_in_levels_[2 ], data_added));
9568+
9569+ IngestExternalFileArg ingest_arg;
9570+ ingest_arg.column_family = ingest_cfh_;
9571+ ingest_arg.options = ifo;
9572+ ingest_arg.external_files .push_back (ingest_file_name_prefix +
9573+ std::to_string (ingest_file_count));
9574+ ingest_arg.atomic_replace_range = RangeOpt (nullptr , nullptr );
9575+
9576+ ASSERT_OK (db_->IngestExternalFiles (
9577+ std::vector<IngestExternalFileArg>({ingest_arg})));
9578+
94029579 ASSERT_NO_FATAL_FAILURE (AddDataToRegularCF ());
94039580
94049581 ASSERT_NO_FATAL_FAILURE (ValidateQueryResult ());
0 commit comments