@@ -982,6 +982,150 @@ TEST_F(CompactionPickerTest, UniversalIncrementalSpace1) {
982982 ASSERT_EQ (14U , compaction->input (1 , 3 )->fd .GetNumber ());
983983}
984984
985+ TEST_F (CompactionPickerTest, ReadTriggeredCompactionDisabled) {
986+ NewVersionStorage (6 , kCompactionStyleLevel );
987+ // threshold=0 means disabled, no files should be marked
988+ mutable_cf_options_.read_triggered_compaction_threshold = 0.0 ;
989+ Add (0 , 1U , " 100" , " 200" , 1000U );
990+ file_map_[1U ].first ->stats .num_reads_sampled .store (999999 );
991+ UpdateVersionStorageInfo ();
992+ ASSERT_TRUE (vstorage_->ReadTriggeredCompactionFiles ().empty ());
993+ }
994+
995+ TEST_F (CompactionPickerTest, ReadTriggeredCompactionBelowThreshold) {
996+ NewVersionStorage (6 , kCompactionStyleLevel );
997+ mutable_cf_options_.read_triggered_compaction_threshold = 1.0 ;
998+ // file_size=1000, reads=500 => reads_per_byte=0.5 < 1.0
999+ Add (0 , 1U , " 100" , " 200" , 1000U );
1000+ file_map_[1U ].first ->stats .num_reads_sampled .store (500 );
1001+ UpdateVersionStorageInfo ();
1002+ ASSERT_TRUE (vstorage_->ReadTriggeredCompactionFiles ().empty ());
1003+ }
1004+
1005+ TEST_F (CompactionPickerTest, ReadTriggeredCompactionAboveThreshold) {
1006+ NewVersionStorage (6 , kCompactionStyleLevel );
1007+ mutable_cf_options_.read_triggered_compaction_threshold = 0.5 ;
1008+ // file_size=1000, reads=600 => reads_per_byte=0.6 > 0.5
1009+ Add (0 , 1U , " 100" , " 200" , 1000U );
1010+ file_map_[1U ].first ->stats .num_reads_sampled .store (600 );
1011+ // file_size=1000, reads=300 => reads_per_byte=0.3 < 0.5 (not marked)
1012+ Add (1 , 2U , " 300" , " 400" , 1000U );
1013+ file_map_[2U ].first ->stats .num_reads_sampled .store (300 );
1014+ // file_size=1000, reads=800 => reads_per_byte=0.8 > 0.5 (hottest)
1015+ Add (2 , 3U , " 500" , " 600" , 1000U );
1016+ file_map_[3U ].first ->stats .num_reads_sampled .store (800 );
1017+ // Add a file at the bottom so L2 is not the last non-empty level
1018+ Add (4 , 4U , " 700" , " 800" , 1000U );
1019+ UpdateVersionStorageInfo ();
1020+
1021+ const auto & marked = vstorage_->ReadTriggeredCompactionFiles ();
1022+ ASSERT_EQ (marked.size (), 2 );
1023+ // Sorted by reads_per_byte descending: file 3 (0.8) then file 1 (0.6)
1024+ ASSERT_EQ (marked[0 ].second ->fd .GetNumber (), 3U );
1025+ ASSERT_EQ (marked[1 ].second ->fd .GetNumber (), 1U );
1026+ }
1027+
1028+ TEST_F (CompactionPickerTest, NeedsCompactionReadTriggered) {
1029+ NewVersionStorage (6 , kCompactionStyleLevel );
1030+ mutable_cf_options_.read_triggered_compaction_threshold = 0.1 ;
1031+ Add (1 , 1U , " 100" , " 200" , 1000U );
1032+ file_map_[1U ].first ->stats .num_reads_sampled .store (500 );
1033+ Add (3 , 2U , " 300" , " 400" , 1000U );
1034+ UpdateVersionStorageInfo ();
1035+
1036+ ASSERT_FALSE (vstorage_->ReadTriggeredCompactionFiles ().empty ());
1037+ ASSERT_TRUE (level_compaction_picker.NeedsCompaction (vstorage_.get ()));
1038+ }
1039+
1040+ TEST_F (CompactionPickerTest, ReadTriggeredPicksFile) {
1041+ NewVersionStorage (6 , kCompactionStyleLevel );
1042+ mutable_cf_options_.read_triggered_compaction_threshold = 0.1 ;
1043+ Add (1 , 1U , " 100" , " 200" , 1000U );
1044+ file_map_[1U ].first ->stats .num_reads_sampled .store (500 );
1045+ Add (3 , 2U , " 300" , " 400" , 1000U );
1046+ UpdateVersionStorageInfo ();
1047+
1048+ std::unique_ptr<Compaction> compaction (level_compaction_picker.PickCompaction (
1049+ cf_name_, mutable_cf_options_, mutable_db_options_,
1050+ /* existing_snapshots=*/ {}, /* snapshot_checker=*/ nullptr , vstorage_.get (),
1051+ &log_buffer_, /* full_history_ts_low=*/ " " ));
1052+ ASSERT_TRUE (compaction.get () != nullptr );
1053+ ASSERT_EQ (compaction->compaction_reason (), CompactionReason::kReadTriggered );
1054+ }
1055+
1056+ TEST_F (CompactionPickerTest, UniversalReadTriggeredCompaction) {
1057+ const uint64_t kFileSize = 100000 ;
1058+
1059+ mutable_cf_options_.read_triggered_compaction_threshold = 0.001 ;
1060+ // Set trigger high so size amp / sorted run pickers don't fire
1061+ mutable_cf_options_.level0_file_num_compaction_trigger = 10 ;
1062+ UniversalCompactionPicker universal_compaction_picker (ioptions_, &icmp_);
1063+
1064+ NewVersionStorage (5 , kCompactionStyleUniversal );
1065+
1066+ // Hot file at L2 with data at L4 below it
1067+ Add (0 , 1U , " 150" , " 200" , kFileSize , 0 , 500 , 550 );
1068+ Add (2 , 2U , " 301" , " 350" , kFileSize , 0 , 201 , 250 );
1069+ Add (4 , 3U , " 301" , " 400" , kFileSize , 0 , 101 , 150 );
1070+
1071+ // Mark file 2 (L2) as having high reads
1072+ file_map_[2U ].first ->stats .num_reads_sampled .store (kFileSize );
1073+ UpdateVersionStorageInfo ();
1074+
1075+ ASSERT_TRUE (universal_compaction_picker.NeedsCompaction (vstorage_.get ()));
1076+
1077+ std::unique_ptr<Compaction> compaction (
1078+ universal_compaction_picker.PickCompaction (
1079+ cf_name_, mutable_cf_options_, mutable_db_options_,
1080+ /* existing_snapshots=*/ {}, /* snapshot_checker=*/ nullptr ,
1081+ vstorage_.get (), &log_buffer_, /* full_history_ts_low=*/ " " ));
1082+
1083+ ASSERT_TRUE (compaction);
1084+ ASSERT_EQ (compaction->compaction_reason (), CompactionReason::kReadTriggered );
1085+ ASSERT_EQ (compaction->start_level (), 2 );
1086+ ASSERT_EQ (compaction->output_level (), 4 );
1087+ }
1088+
1089+ TEST_F (CompactionPickerTest, ReadTriggeredSkipsLastLevel) {
1090+ const uint64_t kFileSize = 100000 ;
1091+
1092+ mutable_cf_options_.read_triggered_compaction_threshold = 0.001 ;
1093+ mutable_cf_options_.level0_file_num_compaction_trigger = 10 ;
1094+ UniversalCompactionPicker universal_compaction_picker (ioptions_, &icmp_);
1095+
1096+ NewVersionStorage (5 , kCompactionStyleUniversal );
1097+
1098+ Add (0 , 1U , " 150" , " 200" , kFileSize , 0 , 500 , 550 );
1099+ Add (4 , 3U , " 301" , " 350" , kFileSize , 0 , 101 , 150 );
1100+
1101+ // File 3 is at the last non-empty level — should NOT be marked for
1102+ // read-triggered compaction. Bottommost file cleanup is handled
1103+ // separately by ComputeBottommostFilesMarkedForCompaction().
1104+ file_map_[3U ].first ->stats .num_reads_sampled .store (kFileSize );
1105+ UpdateVersionStorageInfo ();
1106+
1107+ ASSERT_TRUE (vstorage_->ReadTriggeredCompactionFiles ().empty ());
1108+ }
1109+
1110+ TEST_F (CompactionPickerTest, UniversalReadTriggeredNoPickWhenNotMarked) {
1111+ const uint64_t kFileSize = 100000 ;
1112+
1113+ mutable_cf_options_.read_triggered_compaction_threshold = 0.001 ;
1114+ UniversalCompactionPicker universal_compaction_picker (ioptions_, &icmp_);
1115+
1116+ NewVersionStorage (5 , kCompactionStyleUniversal );
1117+
1118+ Add (0 , 1U , " 150" , " 200" , kFileSize , 0 , 500 , 550 );
1119+ Add (4 , 3U , " 301" , " 350" , kFileSize , 0 , 101 , 150 );
1120+
1121+ // No reads on any file
1122+ UpdateVersionStorageInfo ();
1123+
1124+ ASSERT_TRUE (vstorage_->ReadTriggeredCompactionFiles ().empty ());
1125+ // Not enough sorted runs to trigger compaction either
1126+ ASSERT_FALSE (universal_compaction_picker.NeedsCompaction (vstorage_.get ()));
1127+ }
1128+
9851129TEST_F (CompactionPickerTest, UniversalIncrementalSpace2) {
9861130 const uint64_t kFileSize = 100000 ;
9871131
0 commit comments