diff --git a/db/db_compaction_test.cc b/db/db_compaction_test.cc index 994b0bc569..940cdeaa5a 100644 --- a/db/db_compaction_test.cc +++ b/db/db_compaction_test.cc @@ -74,6 +74,43 @@ class CompactionStatsCollector : public EventListener { std::vector> compaction_completed_; }; +class DeletionTriggeredCompactionWithMinFileSizeTestListener + : public EventListener { + public: + explicit DeletionTriggeredCompactionWithMinFileSizeTestListener( + uint64_t min_file_size) + : min_file_size_(min_file_size) {} + + void OnCompactionBegin(DB* db, const CompactionJobInfo& ci) override { + ASSERT_EQ(ci.compaction_reason, + CompactionReason::kFilesMarkedForCompaction); + + auto env = db->GetEnv(); + const std::vector& db_paths = db->GetOptions().db_paths; + for (const auto& file : ci.input_file_infos) { + uint64_t file_size = GetSstFileSize(env, db_paths, file.file_number); + + // Assert that the file size respects the minimum threshold + ASSERT_GE(file_size, min_file_size_); + } + } + + private: + static uint64_t GetSstFileSize(Env* env, const std::vector& db_paths, + uint64_t file_number) { + uint32_t path_id = 0; // since only one path + std::string sst_file_name = TableFileName(db_paths, file_number, path_id); + uint64_t file_size = 0; + Status s = env->GetFileSize(sst_file_name, &file_size); + if (!s.ok()) { + return 0; + } + return file_size; + } + + uint64_t min_file_size_; +}; + class DBCompactionTest : public DBTestBase { public: DBCompactionTest() @@ -1371,6 +1408,89 @@ TEST_F(DBCompactionTest, RecoverDuringMemtableCompaction) { } while (ChangeOptions()); } +TEST_F(DBCompactionTest, CompactionWithDeletionsAndMinFileSize) { + const uint64_t kMinFileSize = 32 * 1024; // 32KB + const int kDeletionTriggerCount = 50; + const int kInitialKeyCount = 100; + const int kAdditionalKeyCount = 50; + const int kValueSize = 1024; + const int kSmallValueSize = 512; + const int kSeed = 301; + + Options options = CurrentOptions(); + options.compaction_style = kCompactionStyleLevel; + options.write_buffer_size = 1024 * 1024; // 1MB + options.level0_file_num_compaction_trigger = 100; + + options.table_properties_collector_factories = { + NewCompactOnDeletionCollectorFactory( + kInitialKeyCount /* sliding window size */, kDeletionTriggerCount, + 0.5 /* deletion ratio */, kMinFileSize)}; + auto listener = + new DeletionTriggeredCompactionWithMinFileSizeTestListener(kMinFileSize); + options.listeners.emplace_back(listener); + + DestroyAndReopen(options); + Random rnd(kSeed); + + // Create a large file that will be subject to DTC later + for (int i = 0; i < kInitialKeyCount; i++) { + ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize))); + } + ASSERT_OK(Flush()); + + std::vector initial_metadata; + db_->GetLiveFilesMetaData(&initial_metadata); + ASSERT_EQ(initial_metadata.size(), 1); + + // Create small files that should not trigger compaction + ASSERT_OK(Put("small_file_key1", rnd.RandomString(kSmallValueSize))); + ASSERT_OK(Put("small_file_key2", rnd.RandomString(kSmallValueSize))); + ASSERT_OK(Flush()); + ASSERT_OK(Delete("small_file_key1")); + ASSERT_OK(Flush()); + + // Create a file with enough deletions and size to trigger DTC + // Delete keys from the large file to reach deletion threshold + for (int i = 0; i < kDeletionTriggerCount; i++) { + ASSERT_OK(Delete(Key(i))); + } + + // Add new keys to ensure the deletion file meets the min_file_size threshold + for (int i = kInitialKeyCount; i < kInitialKeyCount + kAdditionalKeyCount; + i++) { + ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize))); + } + ASSERT_OK(Flush()); + + ASSERT_OK(dbfull()->TEST_WaitForCompact()); + + // Verify file count after compaction + ASSERT_EQ(NumTableFilesAtLevel(0), 2); // Small file and deletion file + ASSERT_EQ(NumTableFilesAtLevel(1), 1); // Compacted large file + + // Verify deleted keys are gone + for (int i = 0; i < kDeletionTriggerCount; i++) { + std::string value; + ASSERT_TRUE(db_->Get(ReadOptions(), Key(i), &value).IsNotFound()); + } + + // Verify non-deleted keys from large file are still accessible + for (int i = kDeletionTriggerCount; i < kInitialKeyCount; i++) { + std::string value; + ASSERT_OK(db_->Get(ReadOptions(), Key(i), &value)); + ASSERT_EQ(value.size(), kValueSize); + } + + // Verify new keys are accessible + for (int i = kInitialKeyCount; i < kInitialKeyCount + kAdditionalKeyCount; + i++) { + std::string value; + ASSERT_OK(db_->Get(ReadOptions(), Key(i), &value)); + ASSERT_EQ(value.size(), kValueSize); + } +} + TEST_P(DBCompactionTestWithParam, TrivialMoveOneFile) { int32_t trivial_move = 0; ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(