1use std::cell::RefCell;
16use std::sync::Arc;
17
18use itertools::Itertools;
19use risingwave_common::config::meta::default::compaction_config;
20use risingwave_hummock_sdk::level::{InputLevel, Level, Levels, OverlappingLevel};
21use risingwave_pb::hummock::{CompactionConfig, LevelType};
22
23use super::non_overlap_sub_level_picker::NonOverlapSubLevelPicker;
24use super::{
25 CompactionInput, CompactionPicker, CompactionTaskValidator, LocalPickerStatistic,
26 ValidationRuleType,
27};
28use crate::hummock::compaction::picker::TrivialMovePicker;
29use crate::hummock::compaction::{CompactionDeveloperConfig, create_overlap_strategy};
30use crate::hummock::level_handler::LevelHandler;
31
32std::thread_local! {
33 static LOG_COUNTER: RefCell<usize> = const { RefCell::new(0) };
34}
35
36pub struct LevelCompactionPicker {
37 target_level: usize,
38 config: Arc<CompactionConfig>,
39 compaction_task_validator: Arc<CompactionTaskValidator>,
40 developer_config: Arc<CompactionDeveloperConfig>,
41}
42
43impl CompactionPicker for LevelCompactionPicker {
44 fn pick_compaction(
45 &mut self,
46 levels: &Levels,
47 level_handlers: &[LevelHandler],
48 stats: &mut LocalPickerStatistic,
49 ) -> Option<CompactionInput> {
50 let l0 = &levels.l0;
51 if l0.sub_levels.is_empty() {
52 return None;
53 }
54 if l0.sub_levels[0].level_type != LevelType::Nonoverlapping
55 && l0.sub_levels[0].table_infos.len() > 1
56 {
57 stats.skip_by_overlapping += 1;
58 return None;
59 }
60
61 let is_l0_pending_compact =
62 level_handlers[0].is_level_all_pending_compact(&l0.sub_levels[0]);
63
64 if is_l0_pending_compact {
65 stats.skip_by_pending_files += 1;
66 return None;
67 }
68
69 if let Some(mut ret) = self.pick_base_trivial_move(
70 l0,
71 levels.get_level(self.target_level),
72 level_handlers,
73 stats,
74 ) {
75 ret.vnode_partition_count = self.config.split_weight_by_vnode;
76 return Some(ret);
77 }
78
79 debug_assert!(self.target_level == levels.get_level(self.target_level).level_idx as usize);
80 if let Some(ret) = self.pick_multi_level_to_base(
81 l0,
82 levels.get_level(self.target_level),
83 self.config.split_weight_by_vnode,
84 level_handlers,
85 stats,
86 ) {
87 return Some(ret);
88 }
89
90 None
91 }
92}
93
94impl LevelCompactionPicker {
95 #[cfg(test)]
96 pub fn new(
97 target_level: usize,
98 config: Arc<CompactionConfig>,
99 developer_config: Arc<CompactionDeveloperConfig>,
100 ) -> LevelCompactionPicker {
101 LevelCompactionPicker {
102 target_level,
103 compaction_task_validator: Arc::new(CompactionTaskValidator::new(config.clone())),
104 config,
105 developer_config,
106 }
107 }
108
109 pub fn new_with_validator(
110 target_level: usize,
111 config: Arc<CompactionConfig>,
112 compaction_task_validator: Arc<CompactionTaskValidator>,
113 developer_config: Arc<CompactionDeveloperConfig>,
114 ) -> LevelCompactionPicker {
115 LevelCompactionPicker {
116 target_level,
117 config,
118 compaction_task_validator,
119 developer_config,
120 }
121 }
122
123 fn pick_base_trivial_move(
124 &self,
125 l0: &OverlappingLevel,
126 target_level: &Level,
127 level_handlers: &[LevelHandler],
128 stats: &mut LocalPickerStatistic,
129 ) -> Option<CompactionInput> {
130 if !self.developer_config.enable_trivial_move {
131 return None;
132 }
133
134 let overlap_strategy = create_overlap_strategy(self.config.compaction_mode());
135 let trivial_move_picker = TrivialMovePicker::new(
136 0,
137 self.target_level,
138 overlap_strategy.clone(),
139 if self.compaction_task_validator.is_enable() {
140 self.config.sst_allowed_trivial_move_min_size.unwrap_or(0)
142 } else {
143 0
144 },
145 self.config
146 .sst_allowed_trivial_move_max_count
147 .unwrap_or(compaction_config::sst_allowed_trivial_move_max_count())
148 as usize,
149 );
150
151 trivial_move_picker.pick_trivial_move_task(
152 &l0.sub_levels[0].table_infos,
153 &target_level.table_infos,
154 level_handlers,
155 stats,
156 )
157 }
158
159 fn pick_multi_level_to_base(
160 &self,
161 l0: &OverlappingLevel,
162 target_level: &Level,
163 vnode_partition_count: u32,
164 level_handlers: &[LevelHandler],
165 stats: &mut LocalPickerStatistic,
166 ) -> Option<CompactionInput> {
167 let overlap_strategy = create_overlap_strategy(self.config.compaction_mode());
168 let min_compaction_bytes = self.config.sub_level_max_compaction_bytes;
169 let non_overlap_sub_level_picker = NonOverlapSubLevelPicker::new(
170 min_compaction_bytes,
171 std::cmp::max(
174 self.config.max_bytes_for_level_base,
175 self.config.max_compaction_bytes / 2,
176 ),
177 1,
178 self.config.level0_max_compact_file_number,
180 overlap_strategy.clone(),
181 self.developer_config.enable_check_task_level_overlap,
182 self.config
183 .max_l0_compact_level_count
184 .unwrap_or(compaction_config::max_l0_compact_level_count()) as usize,
185 self.config
186 .enable_optimize_l0_interval_selection
187 .unwrap_or(compaction_config::enable_optimize_l0_interval_selection()),
188 );
189
190 let mut max_vnode_partition_idx = 0;
191 for (idx, level) in l0.sub_levels.iter().enumerate() {
192 if level.vnode_partition_count < vnode_partition_count {
193 break;
194 }
195 max_vnode_partition_idx = idx;
196 }
197
198 let candidate_l0_plans = non_overlap_sub_level_picker.pick_l0_multi_non_overlap_level(
199 &l0.sub_levels[..=max_vnode_partition_idx],
200 &level_handlers[0],
201 );
202 if candidate_l0_plans.is_empty() {
203 stats.skip_by_pending_files += 1;
204 return None;
205 }
206
207 let mut all_pending = true;
208 for input in candidate_l0_plans {
209 let mut overlap_info = overlap_strategy.create_overlap_info();
210 for (_, tables) in &input.sstable_infos {
211 for sst in tables {
212 overlap_info.update(&sst.key_range);
213 }
214 }
215 let target_range = overlap_info.check_multiple_overlap(&target_level.table_infos);
216 let target_level_files = if target_range.is_empty() {
217 &[][..]
218 } else {
219 &target_level.table_infos[target_range]
220 };
221
222 let mut target_file_size = 0;
223 let mut pending_compact = false;
224 for sst in target_level_files {
225 if level_handlers[target_level.level_idx as usize].is_pending_compact(&sst.sst_id) {
226 pending_compact = true;
227 break;
228 }
229 target_file_size += sst.sst_size;
230 }
231 if pending_compact {
232 continue;
233 }
234 all_pending = false;
235
236 let mut select_level_inputs = input
237 .sstable_infos
238 .into_iter()
239 .map(|(_, table_infos)| InputLevel {
240 level_idx: 0,
241 level_type: LevelType::Nonoverlapping,
242 table_infos,
243 })
244 .collect_vec();
245 select_level_inputs.reverse();
246 let target_file_count = target_level_files.len();
247 select_level_inputs.push(InputLevel {
248 level_idx: target_level.level_idx,
249 level_type: target_level.level_type,
250 table_infos: target_level_files.to_vec(),
251 });
252
253 let result = CompactionInput {
254 input_levels: select_level_inputs,
255 target_level: self.target_level,
256 select_input_size: input.total_file_size,
257 target_input_size: target_file_size,
258 total_file_count: (input.total_file_count + target_file_count) as u64,
259 vnode_partition_count,
260 ..Default::default()
261 };
262
263 if !self.compaction_task_validator.valid_compact_task(
264 &result,
265 ValidationRuleType::ToBase,
266 stats,
267 ) {
268 if l0.total_file_size > target_level.total_file_size * 8 {
269 let log_counter = LOG_COUNTER.with_borrow_mut(|counter| {
270 *counter += 1;
271 *counter
272 });
273
274 if log_counter.is_multiple_of(100) {
276 tracing::warn!(
277 "skip task with level count: {}, file count: {}, select size: {}, target size: {}, target level size: {}",
278 result.input_levels.len(),
279 result.total_file_count,
280 result.select_input_size,
281 result.target_input_size,
282 target_level.total_file_size,
283 );
284 }
285 }
286 continue;
287 }
288
289 return Some(result);
290 }
291 if all_pending {
293 stats.skip_by_pending_files += 1;
294 }
295 None
296 }
297}
298
299#[cfg(test)]
300pub mod tests {
301
302 use super::*;
303 use crate::hummock::compaction::compaction_config::CompactionConfigBuilder;
304 use crate::hummock::compaction::selector::tests::*;
305 use crate::hummock::compaction::{CompactionMode, TierCompactionPicker};
306
307 fn candidate_fixture(count: usize, target_size: u64) -> (OverlappingLevel, Level) {
308 let sub_levels = (0..2)
309 .map(|level_idx| {
310 let table_infos = (0..count)
311 .map(|idx| {
312 generate_table(
313 (level_idx * count + idx) as u64,
314 1,
315 idx * 10,
316 idx * 10 + 9,
317 1,
318 )
319 })
320 .collect();
321 Level {
322 sub_level_id: level_idx as u64,
323 level_type: LevelType::Nonoverlapping,
324 table_infos,
325 total_file_size: count as u64 * 10,
326 ..Default::default()
327 }
328 })
329 .collect();
330 let table_infos = (0..count)
331 .map(|idx| {
332 let mut sst =
333 generate_table_impl((2 * count + idx) as u64, 1, idx * 10, idx * 10 + 9, 1);
334 sst.sst_size = target_size;
335 sst.into()
336 })
337 .collect();
338 (
339 OverlappingLevel {
340 sub_levels,
341 total_file_size: count as u64 * 20,
342 ..Default::default()
343 },
344 Level {
345 level_idx: 1,
346 level_type: LevelType::Nonoverlapping,
347 table_infos,
348 total_file_size: count as u64 * target_size,
349 ..Default::default()
350 },
351 )
352 }
353
354 #[test]
355 fn test_candidate_pending_and_validation_statistics() {
356 let picker = create_compaction_picker_for_test();
357 for (target_size, pending_count, succeeds, write_amp_skips, pending_skips) in [
358 (10, 0, true, 0, 0),
359 (10, 7, true, 0, 0),
360 (10, 8, false, 0, 1),
361 (100, 0, false, 8, 0),
362 (100, 7, false, 1, 0),
363 (100, 8, false, 0, 1),
364 ] {
365 let (l0, target) = candidate_fixture(8, target_size);
366 let mut handlers = vec![LevelHandler::new(0), LevelHandler::new(1)];
367 for sst in target.table_infos.iter().take(pending_count) {
368 handlers[1].test_add_pending_sst(sst.sst_id, 1);
369 }
370 let mut stats = LocalPickerStatistic::default();
371 let result = picker.pick_multi_level_to_base(&l0, &target, 0, &handlers, &mut stats);
372 assert_eq!(
373 result.is_some(),
374 succeeds,
375 "target_size={target_size}, pending={pending_count}"
376 );
377 assert_eq!(stats.skip_by_write_amp_limit, write_amp_skips);
378 assert_eq!(stats.skip_by_pending_files, pending_skips);
379 assert_eq!(stats.skip_by_count_limit, 0);
380 assert_eq!(stats.skip_by_overlapping, 0);
381 if let Some(result) = result {
382 assert_eq!(
383 result.input_levels[0].table_infos[0].sst_id,
384 (8 + pending_count) as u64
385 );
386 assert_eq!(
387 result.input_levels[1].table_infos[0].sst_id,
388 pending_count as u64
389 );
390 assert_eq!(
391 result.input_levels[2].table_infos[0].sst_id,
392 (16 + pending_count) as u64
393 );
394 assert_eq!(result.select_input_size, 20);
395 assert_eq!(result.target_input_size, 10);
396 assert_eq!(result.total_file_count, 3);
397 }
398 }
399 }
400
401 #[test]
403 #[ignore = "microbenchmark"]
404 fn bench_level_to_base() {
405 use std::hint::black_box;
406 use std::time::Duration;
407
408 let mut criterion = criterion::Criterion::default()
409 .sample_size(30)
410 .warm_up_time(Duration::from_secs(1))
411 .measurement_time(Duration::from_secs(3));
412 let picker = create_compaction_picker_for_test();
413 for (name, count, target_size, pending_count) in [
414 ("small", 8, 10, 0),
415 ("first_valid", 256, 10, 0),
416 ("late_valid", 256, 10, 255),
417 ("all_pending", 256, 10, 256),
418 ("all_rejected", 256, 100, 0),
419 ] {
420 let (l0, target) = candidate_fixture(count, target_size);
421 let mut handlers = vec![LevelHandler::new(0), LevelHandler::new(1)];
422 for sst in target.table_infos.iter().take(pending_count) {
423 handlers[1].test_add_pending_sst(sst.sst_id, 1);
424 }
425 criterion.bench_function(&format!("level_to_base/{name}"), |b| {
426 b.iter(|| {
427 let mut stats = LocalPickerStatistic::default();
428 let result = picker.pick_multi_level_to_base(
429 black_box(&l0),
430 black_box(&target),
431 0,
432 black_box(&handlers),
433 &mut stats,
434 );
435 black_box((result, stats))
436 })
437 });
438 }
439 criterion.final_summary();
440 }
441
442 fn create_compaction_picker_for_test() -> LevelCompactionPicker {
443 let config = Arc::new(
444 CompactionConfigBuilder::new()
445 .level0_tier_compact_file_number(2)
446 .level0_sub_level_compact_level_count(1)
447 .build(),
448 );
449 LevelCompactionPicker::new(1, config, Arc::new(CompactionDeveloperConfig::default()))
450 }
451
452 #[test]
453 fn test_compact_l0_to_l1() {
454 let mut picker = create_compaction_picker_for_test();
455 let l0 = generate_level(
456 0,
457 vec![
458 generate_table(5, 1, 100, 200, 2),
459 generate_table(4, 1, 201, 300, 2),
460 ],
461 );
462 let mut levels = Levels {
463 l0: OverlappingLevel {
464 total_file_size: l0.total_file_size,
465 uncompressed_file_size: l0.total_file_size,
466 sub_levels: vec![l0],
467 },
468 levels: vec![generate_level(
469 1,
470 vec![
471 generate_table(3, 1, 1, 100, 1),
472 generate_table(2, 1, 101, 150, 1),
473 generate_table(1, 1, 201, 210, 1),
474 ],
475 )],
476 ..Default::default()
477 };
478 let mut local_stats = LocalPickerStatistic::default();
479 let mut levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
480
481 let ret = picker
482 .pick_compaction(&levels, &levels_handler, &mut local_stats)
483 .unwrap();
484 assert_eq!(ret.input_levels[0].table_infos.len(), 1);
485 assert_eq!(ret.input_levels[0].table_infos[0].sst_id, 4);
486 assert_eq!(ret.input_levels[1].table_infos[0].sst_id, 1);
487
488 ret.add_pending_task(0, &mut levels_handler);
489 {
490 push_table_level0_nonoverlapping(&mut levels, generate_table(6, 1, 100, 200, 2));
491 push_table_level0_nonoverlapping(&mut levels, generate_table(7, 1, 301, 333, 4));
492 let ret2 = picker
493 .pick_compaction(&levels, &levels_handler, &mut local_stats)
494 .unwrap();
495
496 assert_eq!(ret2.input_levels[0].table_infos.len(), 1);
497 assert_eq!(ret2.input_levels[0].table_infos[0].sst_id, 6);
498 assert_eq!(ret2.input_levels[1].table_infos[0].sst_id, 5);
499 }
500
501 levels.l0.sub_levels[0]
502 .table_infos
503 .retain(|table| table.sst_id != 4);
504 levels.l0.total_file_size -= ret.input_levels[0].table_infos[0].file_size;
505
506 levels_handler[0].remove_task(0);
507 levels_handler[1].remove_task(0);
508
509 let ret = picker
510 .pick_compaction(&levels, &levels_handler, &mut local_stats)
511 .unwrap();
512 assert_eq!(ret.input_levels.len(), 3);
513 assert_eq!(ret.input_levels[0].table_infos[0].sst_id, 6);
514 assert_eq!(ret.input_levels[1].table_infos[0].sst_id, 5);
515 assert_eq!(ret.input_levels[2].table_infos.len(), 2);
516 assert_eq!(ret.input_levels[2].table_infos[0].sst_id, 3);
517 assert_eq!(ret.input_levels[2].table_infos[1].sst_id, 2);
518 ret.add_pending_task(1, &mut levels_handler);
519
520 let mut local_stats = LocalPickerStatistic::default();
521 push_table_level0_overlapping(&mut levels, generate_table(8, 1, 199, 233, 3));
524 let ret = picker.pick_compaction(&levels, &levels_handler, &mut local_stats);
525 assert!(ret.is_none());
526
527 levels_handler[0].remove_task(1);
529 levels_handler[1].remove_task(1);
530 let ret = picker
531 .pick_compaction(&levels, &levels_handler, &mut local_stats)
532 .unwrap();
533 assert_eq!(ret.input_levels.len(), 3);
534 assert_eq!(ret.input_levels[0].table_infos[0].sst_id, 6);
535 assert_eq!(ret.input_levels[1].table_infos[0].sst_id, 5);
536 assert_eq!(ret.input_levels[2].table_infos.len(), 2);
537 }
538
539 #[test]
540 fn test_selecting_key_range_overlap() {
541 let config = Arc::new(
543 CompactionConfigBuilder::new()
544 .level0_tier_compact_file_number(2)
545 .compaction_mode(CompactionMode::Range as i32)
546 .level0_sub_level_compact_level_count(1)
547 .enable_optimize_l0_interval_selection(Some(false))
548 .build(),
549 );
550
551 let config_enable_optimize_l0_interval_selection = Arc::new(
552 CompactionConfigBuilder::new()
553 .level0_tier_compact_file_number(2)
554 .compaction_mode(CompactionMode::Range as i32)
555 .level0_sub_level_compact_level_count(1)
556 .enable_optimize_l0_interval_selection(Some(true))
557 .build(),
558 );
559
560 let mut picker =
561 LevelCompactionPicker::new(1, config, Arc::new(CompactionDeveloperConfig::default()));
562
563 let mut picker_enable_optimize_l0_interval_selection = LevelCompactionPicker::new(
564 1,
565 config_enable_optimize_l0_interval_selection,
566 Arc::new(CompactionDeveloperConfig::default()),
567 );
568
569 let levels = vec![Level {
570 level_idx: 1,
571 level_type: LevelType::Nonoverlapping,
572 table_infos: vec![
573 generate_table(3, 1, 0, 50, 1),
574 generate_table(4, 1, 150, 180, 1),
575 generate_table(5, 1, 250, 300, 1),
576 ],
577 ..Default::default()
578 }];
579 let mut levels = Levels {
580 levels,
581 l0: OverlappingLevel {
582 sub_levels: vec![],
583 total_file_size: 0,
584 uncompressed_file_size: 0,
585 },
586 ..Default::default()
587 };
588 push_tables_level0_nonoverlapping(&mut levels, vec![generate_table(1, 1, 50, 140, 2)]);
589 push_tables_level0_nonoverlapping(
590 &mut levels,
591 vec![
592 generate_table(7, 1, 200, 250, 2),
593 generate_table(8, 1, 400, 500, 2),
594 ],
595 );
596
597 {
598 let mut levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
599
600 let mut local_stats = LocalPickerStatistic::default();
601 let ret = picker_enable_optimize_l0_interval_selection
602 .pick_compaction(&levels, &levels_handler, &mut local_stats)
603 .unwrap();
604
605 assert_eq!(ret.input_levels.len(), 2);
608 assert_eq!(
609 ret.input_levels[0]
610 .table_infos
611 .iter()
612 .map(|t| t.sst_id)
613 .collect_vec(),
614 vec![8]
615 );
616 assert!(ret.input_levels[1].table_infos.is_empty());
618
619 ret.add_pending_task(0, &mut levels_handler);
620
621 let ret = picker_enable_optimize_l0_interval_selection
622 .pick_compaction(&levels, &levels_handler, &mut local_stats)
623 .unwrap();
624
625 assert_eq!(ret.input_levels.len(), 2);
626 assert_eq!(
627 ret.input_levels[0]
628 .table_infos
629 .iter()
630 .map(|t| t.sst_id)
631 .collect_vec(),
632 vec![7]
633 );
634
635 assert_eq!(
636 ret.input_levels[1]
637 .table_infos
638 .iter()
639 .map(|t| t.sst_id)
640 .collect_vec(),
641 vec![5]
642 );
643 }
644
645 {
646 let levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
647
648 let mut local_stats = LocalPickerStatistic::default();
649 let ret = picker
650 .pick_compaction(&levels, &levels_handler, &mut local_stats)
651 .unwrap();
652
653 assert_eq!(ret.input_levels.len(), 2);
655 assert_eq!(
656 ret.input_levels[0]
657 .table_infos
658 .iter()
659 .map(|t| t.sst_id.as_raw_id())
660 .collect_vec(),
661 vec![1]
662 );
663
664 assert_eq!(
665 ret.input_levels[1]
666 .table_infos
667 .iter()
668 .map(|t| t.sst_id.as_raw_id())
669 .collect_vec(),
670 vec![3]
671 );
672 }
673 }
674
675 #[test]
676 fn test_l0_to_l1_compact_conflict() {
677 let mut picker = create_compaction_picker_for_test();
680 let levels = vec![Level {
681 level_idx: 1,
682 level_type: LevelType::Nonoverlapping,
683 table_infos: vec![],
684 total_file_size: 0,
685 sub_level_id: 0,
686 uncompressed_file_size: 0,
687 ..Default::default()
688 }];
689 let mut levels = Levels {
690 levels,
691 l0: OverlappingLevel {
692 sub_levels: vec![],
693 total_file_size: 0,
694 uncompressed_file_size: 0,
695 },
696 ..Default::default()
697 };
698 push_tables_level0_nonoverlapping(
699 &mut levels,
700 vec![
701 generate_table(1, 1, 100, 300, 2),
702 generate_table(2, 1, 350, 500, 2),
703 ],
704 );
705 let mut levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
706
707 let mut local_stats = LocalPickerStatistic::default();
708 let ret = picker
709 .pick_compaction(&levels, &levels_handler, &mut local_stats)
710 .unwrap();
711 ret.add_pending_task(0, &mut levels_handler); push_tables_level0_nonoverlapping(&mut levels, vec![generate_table(3, 1, 250, 300, 3)]);
714 let config: CompactionConfig = CompactionConfigBuilder::new()
715 .level0_tier_compact_file_number(2)
716 .max_compaction_bytes(1000)
717 .sub_level_max_compaction_bytes(150)
718 .max_bytes_for_level_multiplier(1)
719 .level0_sub_level_compact_level_count(3)
720 .build();
721 let mut picker = TierCompactionPicker::new(Arc::new(config));
722
723 let ret: Option<CompactionInput> =
724 picker.pick_compaction(&levels, &levels_handler, &mut local_stats);
725 assert!(ret.is_none());
726 }
727
728 #[test]
729 fn test_skip_compact_write_amplification_limit() {
730 let config: CompactionConfig = CompactionConfigBuilder::new()
731 .level0_tier_compact_file_number(2)
732 .max_compaction_bytes(1000)
733 .sub_level_max_compaction_bytes(150)
734 .max_bytes_for_level_multiplier(1)
735 .level0_sub_level_compact_level_count(2)
736 .build();
737 let mut picker = LevelCompactionPicker::new(
738 1,
739 Arc::new(config),
740 Arc::new(CompactionDeveloperConfig::default()),
741 );
742
743 let mut levels = Levels {
744 levels: vec![Level {
745 level_idx: 1,
746 level_type: LevelType::Nonoverlapping,
747 table_infos: vec![
748 generate_table(1, 1, 100, 399, 2),
749 generate_table(2, 1, 400, 699, 2),
750 generate_table(3, 1, 700, 999, 2),
751 ],
752 total_file_size: 900,
753 sub_level_id: 0,
754 uncompressed_file_size: 900,
755 ..Default::default()
756 }],
757 l0: generate_l0_nonoverlapping_sublevels(vec![]),
758 ..Default::default()
759 };
760 push_tables_level0_nonoverlapping(
761 &mut levels,
762 vec![
763 generate_table(4, 1, 100, 180, 2),
764 generate_table(5, 1, 400, 450, 2),
765 generate_table(6, 1, 600, 700, 2),
766 ],
767 );
768
769 let mut levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
770 let mut local_stats = LocalPickerStatistic::default();
771 levels_handler[0].add_pending_task(1, 4, &levels.l0.sub_levels[0].table_infos);
772 let ret = picker.pick_compaction(&levels, &levels_handler, &mut local_stats);
773 assert!(ret.is_none());
775 }
776
777 #[test]
778 fn test_l0_to_l1_break_on_exceed_compaction_size() {
779 let mut local_stats = LocalPickerStatistic::default();
780 let mut l0 = generate_l0_overlapping_sublevels(vec![
781 vec![
782 generate_table(4, 1, 10, 90, 1),
783 generate_table(5, 1, 210, 220, 1),
784 ],
785 vec![generate_table(6, 1, 0, 100000, 1)],
786 vec![generate_table(7, 1, 0, 100000, 1)],
787 ]);
788 for s in &mut l0.sub_levels {
790 s.level_type = LevelType::Nonoverlapping;
791 }
792 let levels = Levels {
793 l0,
794 levels: vec![generate_level(1, vec![generate_table(3, 1, 0, 100000, 1)])],
795 ..Default::default()
796 };
797 let levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
798
799 let config = Arc::new(
801 CompactionConfigBuilder::new()
802 .max_compaction_bytes(500000)
803 .sub_level_max_compaction_bytes(50000)
804 .max_bytes_for_level_base(500000)
805 .level0_sub_level_compact_level_count(1)
806 .build(),
807 );
808 let mut picker =
811 LevelCompactionPicker::new(1, config, Arc::new(CompactionDeveloperConfig::default()));
812 let ret = picker
813 .pick_compaction(&levels, &levels_handler, &mut local_stats)
814 .unwrap();
815 assert_eq!(ret.input_levels[0].table_infos[0].sst_id, 7);
816 assert_eq!(
817 3,
818 ret.input_levels.iter().filter(|l| l.level_idx == 0).count()
819 );
820 assert_eq!(
821 4,
822 ret.input_levels
823 .iter()
824 .filter(|l| l.level_idx == 0)
825 .map(|l| l.table_infos.len())
826 .sum::<usize>()
827 );
828
829 let config = Arc::new(
831 CompactionConfigBuilder::new()
832 .max_compaction_bytes(100010)
833 .max_bytes_for_level_base(512)
834 .level0_sub_level_compact_level_count(1)
835 .build(),
836 );
837 let mut picker =
838 LevelCompactionPicker::new(1, config, Arc::new(CompactionDeveloperConfig::default()));
839
840 let ret = picker
841 .pick_compaction(&levels, &levels_handler, &mut local_stats)
842 .unwrap();
843 assert_eq!(ret.input_levels[0].table_infos[0].sst_id, 6);
844 assert_eq!(
845 2,
846 ret.input_levels.iter().filter(|l| l.level_idx == 0).count()
847 );
848 assert_eq!(
849 3,
850 ret.input_levels
851 .iter()
852 .filter(|l| l.level_idx == 0)
853 .map(|l| l.table_infos.len())
854 .sum::<usize>()
855 );
856 }
857
858 #[test]
859 fn test_l0_to_l1_break_on_pending_sub_level() {
860 let l0 = generate_l0_nonoverlapping_multi_sublevels(vec![
861 vec![
862 generate_table(4, 1, 10, 90, 1),
863 generate_table(5, 1, 210, 220, 1),
864 ],
865 vec![generate_table(6, 1, 0, 100000, 1)],
866 vec![generate_table(7, 1, 0, 100000, 1)],
867 ]);
868
869 let levels = Levels {
870 l0,
871 levels: vec![generate_level(1, vec![generate_table(3, 1, 0, 100000, 1)])],
872 ..Default::default()
873 };
874 let mut levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
875 let mut local_stats = LocalPickerStatistic::default();
876
877 let pending_level = levels.l0.sub_levels[1].clone();
879 assert_eq!(pending_level.sub_level_id, 1);
880 let tier_task_input = CompactionInput {
881 input_levels: vec![InputLevel {
882 level_idx: 0,
883 level_type: pending_level.level_type,
884 table_infos: pending_level.table_infos.clone(),
885 }],
886 target_level: 1,
887 target_sub_level_id: pending_level.sub_level_id,
888 ..Default::default()
889 };
890 assert!(!levels_handler[0].is_level_pending_compact(&pending_level));
891 tier_task_input.add_pending_task(1, &mut levels_handler);
892 assert!(levels_handler[0].is_level_pending_compact(&pending_level));
893
894 let config = Arc::new(
896 CompactionConfigBuilder::new()
897 .max_compaction_bytes(500000)
898 .level0_sub_level_compact_level_count(2)
899 .build(),
900 );
901
902 let mut picker = LevelCompactionPicker::new(
905 1,
906 config.clone(),
907 Arc::new(CompactionDeveloperConfig::default()),
908 );
909 let ret = picker.pick_compaction(&levels, &levels_handler, &mut local_stats);
910 assert!(ret.is_none());
911
912 for pending_task_id in &levels_handler[0].pending_tasks_ids() {
914 levels_handler[0].remove_task(*pending_task_id);
915 }
916
917 let mut picker =
919 LevelCompactionPicker::new(1, config, Arc::new(CompactionDeveloperConfig::default()));
920 picker
921 .pick_compaction(&levels, &levels_handler, &mut local_stats)
922 .unwrap();
923 }
924
925 #[test]
926 fn test_l0_to_base_when_all_base_pending() {
927 let l0 = generate_l0_nonoverlapping_multi_sublevels(vec![
928 vec![
929 generate_table(4, 1, 10, 90, 1),
930 generate_table(5, 1, 1000, 2000, 1),
931 ],
932 vec![generate_table(6, 1, 10, 90, 1)],
933 ]);
934
935 let levels = Levels {
936 l0,
937 levels: vec![generate_level(1, vec![generate_table(3, 1, 1, 100, 1)])],
938 ..Default::default()
939 };
940 let mut levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
941 let mut local_stats = LocalPickerStatistic::default();
942
943 let config = Arc::new(
944 CompactionConfigBuilder::new()
945 .max_compaction_bytes(500000)
946 .level0_sub_level_compact_level_count(2)
947 .sub_level_max_compaction_bytes(1000)
948 .build(),
949 );
950
951 let mut picker =
952 LevelCompactionPicker::new(1, config, Arc::new(CompactionDeveloperConfig::default()));
953 let ret = picker
954 .pick_compaction(&levels, &levels_handler, &mut local_stats)
955 .unwrap();
956 assert_eq!(2, ret.input_levels.len());
958 assert!(ret.input_levels[1].table_infos.is_empty());
959 assert_eq!(5, ret.input_levels[0].table_infos[0].sst_id);
960 ret.add_pending_task(0, &mut levels_handler);
961
962 let ret = picker
963 .pick_compaction(&levels, &levels_handler, &mut local_stats)
964 .unwrap();
965 assert_eq!(3, ret.input_levels.len());
966 assert_eq!(6, ret.input_levels[0].table_infos[0].sst_id);
967 }
968}