1use std::fmt::Write;
16use std::sync::Arc;
17
18use itertools::Itertools;
19use risingwave_hummock_sdk::append_sstable_info_to_string;
20use risingwave_hummock_sdk::key_range::KeyRange;
21use risingwave_hummock_sdk::level::Level;
22use risingwave_hummock_sdk::sstable_info::SstableInfo;
23use risingwave_pb::hummock::LevelType;
24
25use crate::hummock::compaction::overlap_strategy::{OverlapInfo, OverlapStrategy};
26use crate::hummock::level_handler::LevelHandler;
27
28#[derive(Default, Debug)]
29pub struct SubLevelSstables {
30 pub total_file_size: u64,
31 pub total_file_count: usize,
32 pub sstable_infos: Vec<(u64, Vec<SstableInfo>)>,
33 pub expected: bool,
34}
35
36pub struct NonOverlapSubLevelPicker {
37 min_compaction_bytes: u64,
38 max_compaction_bytes: u64,
39 min_expected_level_count: usize,
40 max_file_count: u64,
41 overlap_strategy: Arc<dyn OverlapStrategy>,
42 enable_check_task_level_overlap: bool,
43 max_expected_level_count: usize,
44 enable_optimize_l0_interval_selection: bool,
45}
46
47impl NonOverlapSubLevelPicker {
48 fn select_intervals<'a>(
55 &self,
56 l0: &'a [Level],
57 level_handler: &LevelHandler,
58 ) -> Option<&'a [SstableInfo]> {
59 if !self.enable_optimize_l0_interval_selection {
60 return l0.first().map(|level| {
61 debug_assert_eq!(level.level_type, LevelType::Nonoverlapping);
62 level.table_infos.as_slice()
63 });
64 }
65
66 if let [level] = l0 {
67 return (level.level_type == LevelType::Nonoverlapping)
68 .then_some(level.table_infos.as_slice());
69 }
70
71 let mut candidates = l0
72 .iter()
73 .enumerate()
74 .filter(|(_, level)| level.level_type == LevelType::Nonoverlapping)
75 .peekable();
76 let first = candidates.next()?;
77 if candidates.peek().is_none() {
79 return Some(&first.1.table_infos);
80 }
81 let level_idx = std::iter::once(first)
82 .chain(candidates)
83 .map(|(idx, level)| {
84 let non_pending = level
85 .table_infos
86 .iter()
87 .filter(|sst| !level_handler.is_pending_compact(&sst.sst_id))
88 .count();
89 (idx, non_pending, level.table_infos.len())
90 })
91 .max_by_key(|&(idx, non_pending, file_count)| {
92 (non_pending, file_count, std::cmp::Reverse(idx))
93 })
94 .map(|(idx, _, _)| idx)?;
95
96 Some(&l0[level_idx].table_infos)
97 }
98
99 pub fn new(
100 min_compaction_bytes: u64,
101 max_compaction_bytes: u64,
102 min_expected_level_count: usize,
103 max_file_count: u64,
104 overlap_strategy: Arc<dyn OverlapStrategy>,
105 enable_check_task_level_overlap: bool,
106 max_expected_level_count: usize,
107 enable_optimize_l0_interval_selection: bool,
108 ) -> Self {
109 Self {
110 min_compaction_bytes,
111 max_compaction_bytes,
112 min_expected_level_count,
113 max_file_count,
114 overlap_strategy,
115 enable_check_task_level_overlap,
116 max_expected_level_count,
117 enable_optimize_l0_interval_selection,
118 }
119 }
120
121 #[cfg(test)]
122 pub fn for_test(
123 min_compaction_bytes: u64,
124 max_compaction_bytes: u64,
125 min_expected_level_count: usize,
126 max_file_count: u64,
127 overlap_strategy: Arc<dyn OverlapStrategy>,
128 enable_check_task_level_overlap: bool,
129 max_expected_level_count: usize,
130 enable_optimize_l0_interval_selection: bool,
131 ) -> Self {
132 Self {
133 min_compaction_bytes,
134 max_compaction_bytes,
135 min_expected_level_count,
136 max_file_count,
137 overlap_strategy,
138 enable_check_task_level_overlap,
139 max_expected_level_count,
140 enable_optimize_l0_interval_selection,
141 }
142 }
143
144 fn pick_sub_level(
160 &self,
161 levels: &[Level],
162 level_handler: &LevelHandler,
163 key_range: &KeyRange,
164 ) -> Option<SubLevelSstables> {
165 let mut ret = SubLevelSstables::default();
166 for sub_level in levels {
167 ret.sstable_infos.push((sub_level.sub_level_id, vec![]));
168 }
169
170 let mut pick_levels_range = Vec::default();
171 let mut max_select_level_count = 0;
172
173 'expand_new_level: for (target_index, target_level) in levels.iter().enumerate() {
176 if target_level.level_type != LevelType::Nonoverlapping {
177 break;
178 }
179
180 if ret
181 .sstable_infos
182 .iter()
183 .filter(|(_sub_level_id, ssts)| !ssts.is_empty())
184 .count()
185 > self.max_expected_level_count
186 {
187 break;
188 }
189
190 let mut basic_overlap_info = self.overlap_strategy.create_overlap_info();
192 basic_overlap_info.update(key_range);
193
194 let mut overlap_files_range =
195 basic_overlap_info.check_multiple_include(&target_level.table_infos);
196 if overlap_files_range.is_empty() {
197 overlap_files_range =
198 basic_overlap_info.check_multiple_overlap(&target_level.table_infos);
199 }
200
201 if overlap_files_range.is_empty() {
202 continue;
203 }
204
205 let mut overlap_levels = vec![];
206
207 let mut add_files_size: u64 = 0;
208 let mut add_files_count: usize = 0;
209
210 let mut select_level_count = 0;
211 for reverse_index in (0..=target_index).rev() {
212 let target_tables = &levels[reverse_index].table_infos;
213
214 overlap_files_range = if target_index == reverse_index {
215 overlap_files_range
216 } else {
217 basic_overlap_info.check_multiple_overlap(target_tables)
218 };
219
220 if overlap_files_range.is_empty() {
223 continue;
225 }
226
227 for other in &target_tables[overlap_files_range.clone()] {
228 if level_handler.is_pending_compact(&other.sst_id) {
229 break 'expand_new_level;
230 }
231 basic_overlap_info.update(&other.key_range);
232
233 add_files_size += other.sst_size;
234 add_files_count += 1;
235 }
236
237 overlap_levels.push((reverse_index, overlap_files_range.clone()));
238 select_level_count += 1;
239 }
240
241 if select_level_count > max_select_level_count {
242 max_select_level_count = select_level_count;
243 pick_levels_range = overlap_levels;
244 }
245
246 if max_select_level_count >= self.min_expected_level_count
248 && (add_files_size >= self.max_compaction_bytes
249 || add_files_count >= self.max_file_count as usize)
250 {
251 break 'expand_new_level;
252 }
253 }
254
255 if !pick_levels_range.is_empty() {
256 for (reverse_index, sst_range) in pick_levels_range {
257 let level_ssts = &levels[reverse_index].table_infos;
258 ret.sstable_infos[reverse_index].1 = level_ssts[sst_range].to_vec();
259 ret.total_file_count += ret.sstable_infos[reverse_index].1.len();
260 ret.total_file_size += ret.sstable_infos[reverse_index]
261 .1
262 .iter()
263 .map(|sst| sst.sst_size)
264 .sum::<u64>();
265 }
266
267 ret.sstable_infos
269 .iter_mut()
270 .for_each(|(_sub_level_id, level_ssts)| {
271 level_ssts.sort_by(|sst1, sst2| sst1.key_range.cmp(&sst2.key_range));
272 });
273 } else {
274 ret.total_file_count = 0;
275 ret.total_file_size = 0;
276 }
277
278 if self.enable_check_task_level_overlap {
279 self.verify_task_level_overlap(&ret, levels);
280 }
281
282 ret.sstable_infos
283 .retain(|(_sub_level_id, ssts)| !ssts.is_empty());
284
285 if ret.total_file_size > self.max_compaction_bytes
287 || ret.total_file_count as u64 > self.max_file_count
288 || ret.sstable_infos.len() > self.max_expected_level_count
289 {
290 let mut total_file_count = 0;
292 let mut total_file_size = 0;
293 let mut total_level_count = 0;
294 for (index, (_sub_level_id, sstables)) in ret.sstable_infos.iter().enumerate() {
295 total_file_count += sstables.len();
296 total_file_size += sstables.iter().map(|sst| sst.sst_size).sum::<u64>();
297 total_level_count += 1;
298
299 if total_level_count >= self.min_expected_level_count
301 && (total_file_count as u64 >= self.max_file_count
302 || total_file_size >= self.max_compaction_bytes
303 || total_level_count >= self.max_expected_level_count)
304 {
305 ret.total_file_count = total_file_count;
306 ret.total_file_size = total_file_size;
307 ret.sstable_infos.truncate(index + 1);
308 break;
309 }
310 }
311 }
312
313 if ret.sstable_infos.is_empty() {
314 return None;
315 }
316
317 Some(ret)
318 }
319
320 pub fn pick_l0_multi_non_overlap_level(
336 &self,
337 l0: &[Level],
338 level_handler: &LevelHandler,
339 ) -> Vec<SubLevelSstables> {
340 if l0.len() < self.min_expected_level_count {
341 return vec![];
342 }
343
344 let mut scores = vec![];
345 let Some(intervals) = self.select_intervals(l0, level_handler) else {
346 return vec![];
347 };
348 for sst in intervals {
349 if level_handler.is_pending_compact(&sst.sst_id) {
350 continue;
351 }
352
353 if let Some(score) = self.pick_sub_level(l0, level_handler, &sst.key_range) {
354 scores.push(score);
355 }
356 }
357
358 if scores.is_empty() {
359 return vec![];
360 }
361
362 let mut expected = Vec::with_capacity(scores.len());
363 let mut unexpected = vec![];
364
365 for mut selected_task in scores {
366 if selected_task.sstable_infos.len() > self.max_expected_level_count
367 || selected_task.sstable_infos.len() < self.min_expected_level_count
368 || selected_task.total_file_size < self.min_compaction_bytes
369 {
370 selected_task.expected = false;
371 unexpected.push(selected_task);
372 } else {
373 selected_task.expected = true;
374 expected.push(selected_task);
375 }
376 }
377
378 expected.sort_by(|a, b| {
382 b.sstable_infos
383 .len()
384 .cmp(&a.sstable_infos.len())
385 .then_with(|| a.total_file_count.cmp(&b.total_file_count))
386 .then_with(|| a.total_file_size.cmp(&b.total_file_size))
387 });
388
389 unexpected.sort_by(|a, b| {
395 let a_select_count_offset =
396 (a.sstable_infos.len() as i64 - self.max_expected_level_count as i64).abs();
397 let b_select_count_offset =
398 (b.sstable_infos.len() as i64 - self.max_expected_level_count as i64).abs();
399
400 let a_file_count_offset =
401 (a.total_file_count as i64 - self.max_file_count as i64).abs();
402 let b_file_count_offset =
403 (b.total_file_count as i64 - self.max_file_count as i64).abs();
404
405 let a_file_size_offset =
406 (a.total_file_size as i64 - self.max_compaction_bytes as i64).abs();
407 let b_file_size_offset =
408 (b.total_file_size as i64 - self.max_compaction_bytes as i64).abs();
409
410 a_select_count_offset
411 .cmp(&b_select_count_offset)
412 .then_with(|| a_file_count_offset.cmp(&b_file_count_offset))
413 .then_with(|| a_file_size_offset.cmp(&b_file_size_offset))
414 });
415
416 expected.extend(unexpected);
417
418 expected
419 }
420
421 fn verify_task_level_overlap(&self, ret: &SubLevelSstables, levels: &[Level]) {
422 let mut overlap_info: Option<Box<dyn OverlapInfo>> = None;
423 for (level_idx, (_sub_level_id, ssts)) in ret.sstable_infos.iter().enumerate().rev() {
424 if let Some(overlap_info) = overlap_info.as_mut() {
425 let level = levels.get(level_idx).unwrap();
427 let overlap_sst_range = overlap_info.check_multiple_overlap(&level.table_infos);
428 if !overlap_sst_range.is_empty() {
429 let expected_sst_ids = level.table_infos[overlap_sst_range.clone()]
430 .iter()
431 .map(|s| s.object_id)
432 .collect_vec();
433 let actual_sst_ids = ssts.iter().map(|s| s.object_id).collect_vec();
434 let start_idx = actual_sst_ids
437 .iter()
438 .position(|sst_id| sst_id == expected_sst_ids.first().unwrap());
439 if start_idx.is_none_or(|idx| {
440 actual_sst_ids[idx..idx + expected_sst_ids.len()] != expected_sst_ids
441 }) {
442 let mut actual_sst_infos = String::new();
444 ssts.iter()
445 .for_each(|s| append_sstable_info_to_string(&mut actual_sst_infos, s));
446
447 let mut expected_sst_infos = String::new();
449 level.table_infos[overlap_sst_range].iter().for_each(|s| {
450 append_sstable_info_to_string(&mut expected_sst_infos, s)
451 });
452
453 let mut ret_sst_infos = String::new();
455 ret.sstable_infos.iter().enumerate().for_each(
456 |(idx, (_sub_level_id, ssts))| {
457 writeln!(
458 ret_sst_infos,
459 "sub level {}",
460 levels.get(idx).unwrap().sub_level_id
461 )
462 .unwrap();
463 ssts.iter().for_each(|s| {
464 append_sstable_info_to_string(&mut ret_sst_infos, s)
465 });
466 },
467 );
468 panic!(
469 "Compact task overlap check fails. Actual: {} Expected: {} Ret {}",
470 actual_sst_infos, expected_sst_infos, ret_sst_infos
471 );
472 }
473 }
474 } else if !ssts.is_empty() {
475 overlap_info = Some(self.overlap_strategy.create_overlap_info());
477 }
478
479 for sst in ssts {
480 overlap_info.as_mut().unwrap().update(&sst.key_range);
481 }
482 }
483 }
484}
485
486#[cfg(test)]
487pub mod tests {
488 use std::collections::BTreeSet;
489
490 use risingwave_common::config::meta::default::compaction_config;
491
492 use super::*;
493 use crate::hummock::compaction::overlap_strategy::RangeOverlapStrategy;
494 use crate::hummock::compaction::picker::non_overlap_sub_level_picker::NonOverlapSubLevelPicker;
495 use crate::hummock::compaction::selector::tests::generate_table;
496
497 #[test]
498 fn test_select_intervals_tie_breaks() {
499 let l0 = vec![
500 Level {
501 level_type: LevelType::Nonoverlapping,
502 table_infos: vec![
503 generate_table(0, 1, 0, 9, 1),
504 generate_table(1, 1, 10, 19, 1),
505 ],
506 ..Default::default()
507 },
508 Level {
509 level_type: LevelType::Nonoverlapping,
510 table_infos: vec![
511 generate_table(2, 1, 0, 9, 1),
512 generate_table(3, 1, 10, 19, 1),
513 ],
514 ..Default::default()
515 },
516 ];
517 let picker = NonOverlapSubLevelPicker::new(
518 0,
519 10000,
520 1,
521 10000,
522 Arc::new(RangeOverlapStrategy::default()),
523 true,
524 10,
525 true,
526 );
527 let mut handler = LevelHandler::new(0);
528 assert_eq!(picker.select_intervals(&l0, &handler).unwrap()[0].sst_id, 0);
530 handler.test_add_pending_sst(0.into(), 1);
531 assert_eq!(picker.select_intervals(&l0, &handler).unwrap()[0].sst_id, 2);
532 handler.test_add_pending_sst(2.into(), 1);
533 assert_eq!(picker.select_intervals(&l0, &handler).unwrap()[0].sst_id, 0);
534 let mut l0 = l0;
536 l0[1].table_infos.push(generate_table(4, 1, 20, 29, 1));
537 handler.test_add_pending_sst(4.into(), 1);
538 assert_eq!(picker.select_intervals(&l0, &handler).unwrap()[0].sst_id, 2);
539 }
540
541 #[test]
543 #[ignore = "microbenchmark"]
544 fn bench_select_intervals() {
545 use std::hint::black_box;
546 use std::time::Duration;
547
548 let mut criterion = criterion::Criterion::default()
549 .sample_size(30)
550 .warm_up_time(Duration::from_secs(1))
551 .measurement_time(Duration::from_secs(3));
552 let picker = NonOverlapSubLevelPicker::new(
553 0,
554 10000,
555 1,
556 10000,
557 Arc::new(RangeOverlapStrategy::default()),
558 false,
559 64,
560 true,
561 );
562 for (name, counts) in [
563 ("single", vec![1024]),
564 ("small", vec![8; 4]),
565 ("balanced", vec![256; 64]),
566 (
567 "skewed",
568 std::iter::once(10000)
569 .chain(std::iter::repeat_n(1, 63))
570 .collect(),
571 ),
572 ] {
573 let mut next_id = 0;
574 let mut handler = LevelHandler::new(0);
575 let levels: Vec<_> = counts
576 .into_iter()
577 .map(|count| {
578 let table_infos = (0..count)
579 .map(|idx| {
580 let sst = generate_table(next_id, 1, idx * 10, idx * 10 + 9, 1);
581 if next_id.is_multiple_of(4) {
583 handler.test_add_pending_sst(next_id.into(), 1);
584 }
585 next_id += 1;
586 sst
587 })
588 .collect();
589 Level {
590 level_type: LevelType::Nonoverlapping,
591 table_infos,
592 ..Default::default()
593 }
594 })
595 .collect();
596 criterion.bench_function(&format!("select_intervals/{name}"), |b| {
597 b.iter(|| {
598 black_box(picker.select_intervals(black_box(&levels), black_box(&handler)))
599 })
600 });
601 }
602 criterion.final_summary();
603 }
604
605 #[test]
606 fn test_pick_l0_multi_level() {
607 let levels = vec![
608 Level {
609 level_idx: 1,
610 level_type: LevelType::Nonoverlapping,
611 table_infos: vec![
612 generate_table(0, 1, 50, 99, 2),
613 generate_table(1, 1, 100, 149, 2),
614 generate_table(2, 1, 150, 249, 2),
615 generate_table(6, 1, 250, 300, 2),
616 generate_table(7, 1, 350, 400, 2),
617 generate_table(8, 1, 450, 500, 2),
618 ],
619 total_file_size: 800,
620 ..Default::default()
621 },
622 Level {
623 level_idx: 2,
624 level_type: LevelType::Nonoverlapping,
625 table_infos: vec![
626 generate_table(4, 1, 50, 199, 1),
627 generate_table(5, 1, 200, 249, 1),
628 generate_table(9, 1, 250, 300, 2),
629 generate_table(10, 1, 350, 400, 2),
630 generate_table(11, 1, 450, 500, 2),
631 ],
632 total_file_size: 350,
633 ..Default::default()
634 },
635 Level {
636 level_idx: 3,
637 level_type: LevelType::Nonoverlapping,
638 table_infos: vec![
639 generate_table(11, 1, 250, 300, 2),
640 generate_table(12, 1, 350, 400, 2),
641 generate_table(13, 1, 450, 500, 2),
642 ],
643 total_file_size: 150,
644 ..Default::default()
645 },
646 Level {
647 level_idx: 4,
648 level_type: LevelType::Nonoverlapping,
649 table_infos: vec![
650 generate_table(14, 1, 250, 300, 2),
651 generate_table(15, 1, 350, 400, 2),
652 generate_table(16, 1, 450, 500, 2),
653 ],
654 total_file_size: 150,
655 ..Default::default()
656 },
657 ];
658
659 let levels_handlers = [
660 LevelHandler::new(0),
661 LevelHandler::new(1),
662 LevelHandler::new(2),
663 ];
664
665 {
666 let picker = NonOverlapSubLevelPicker::new(
668 0,
669 10000,
670 1,
671 10000,
672 Arc::new(RangeOverlapStrategy::default()),
673 true,
674 compaction_config::max_l0_compact_level_count() as usize,
675 true,
676 );
677 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
678 assert_eq!(6, ret.len());
679 }
680
681 {
682 let picker = NonOverlapSubLevelPicker::new(
684 0,
685 100,
686 1,
687 10000,
688 Arc::new(RangeOverlapStrategy::default()),
689 true,
690 compaction_config::max_l0_compact_level_count() as usize,
691 true,
692 );
693 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
694 assert_eq!(6, ret.len());
695 }
696
697 {
698 let picker = NonOverlapSubLevelPicker::new(
700 0,
701 10000,
702 1,
703 5,
704 Arc::new(RangeOverlapStrategy::default()),
705 true,
706 compaction_config::max_l0_compact_level_count() as usize,
707 true,
708 );
709 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
710 assert_eq!(6, ret.len());
711 }
712 }
713
714 #[test]
715 fn test_pick_l0_multi_level2() {
716 let levels = vec![
717 Level {
718 level_idx: 1,
719 level_type: LevelType::Nonoverlapping,
720 table_infos: vec![
721 generate_table(0, 1, 50, 99, 2),
722 generate_table(1, 1, 100, 149, 2),
723 generate_table(2, 1, 150, 249, 2),
724 generate_table(6, 1, 250, 300, 2),
725 generate_table(7, 1, 350, 400, 2),
726 generate_table(8, 1, 450, 500, 2),
727 ],
728 total_file_size: 800,
729 ..Default::default()
730 },
731 Level {
732 level_idx: 2,
733 level_type: LevelType::Nonoverlapping,
734 table_infos: vec![
735 generate_table(4, 1, 50, 99, 1),
736 generate_table(5, 1, 150, 200, 1),
737 generate_table(9, 1, 250, 300, 2),
738 generate_table(10, 1, 350, 400, 2),
739 generate_table(11, 1, 450, 500, 2),
740 ],
741 total_file_size: 250,
742 ..Default::default()
743 },
744 Level {
745 level_idx: 3,
746 level_type: LevelType::Nonoverlapping,
747 table_infos: vec![
748 generate_table(11, 1, 250, 300, 2),
749 generate_table(12, 1, 350, 400, 2),
750 generate_table(13, 1, 450, 500, 2),
751 ],
752 total_file_size: 150,
753 ..Default::default()
754 },
755 Level {
756 level_idx: 4,
757 level_type: LevelType::Nonoverlapping,
758 table_infos: vec![
759 generate_table(14, 1, 250, 300, 2),
760 generate_table(15, 1, 350, 400, 2),
761 generate_table(16, 1, 450, 500, 2),
762 ],
763 total_file_size: 150,
764 ..Default::default()
765 },
766 ];
767
768 let levels_handlers = [
769 LevelHandler::new(0),
770 LevelHandler::new(1),
771 LevelHandler::new(2),
772 ];
773
774 {
775 let picker = NonOverlapSubLevelPicker::new(
777 0,
778 10000,
779 1,
780 10000,
781 Arc::new(RangeOverlapStrategy::default()),
782 true,
783 compaction_config::max_l0_compact_level_count() as usize,
784 true,
785 );
786 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
787 assert_eq!(6, ret.len());
788 }
789
790 {
791 let max_compaction_bytes = 100;
793 let picker = NonOverlapSubLevelPicker::new(
794 60,
795 max_compaction_bytes,
796 1,
797 10000,
798 Arc::new(RangeOverlapStrategy::default()),
799 true,
800 compaction_config::max_l0_compact_level_count() as usize,
801 true,
802 );
803 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
804 assert_eq!(6, ret.len());
805
806 for plan in &ret {
807 if plan.total_file_size >= max_compaction_bytes {
808 assert!(plan.expected);
809 } else {
810 assert!(!plan.expected);
811 }
812 }
813 }
814
815 {
816 let max_file_count = 2;
818 let picker = NonOverlapSubLevelPicker::new(
819 0,
820 10000,
821 1,
822 max_file_count,
823 Arc::new(RangeOverlapStrategy::default()),
824 true,
825 compaction_config::max_l0_compact_level_count() as usize,
826 true,
827 );
828 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
829 assert_eq!(6, ret.len());
830
831 for plan in ret {
832 let mut sst_id_set = BTreeSet::default();
833 for sst in &plan.sstable_infos {
834 sst_id_set.insert(sst.1[0].sst_id);
835 }
836 assert!(sst_id_set.len() <= max_file_count as usize);
837 }
838 }
839
840 {
841 let min_depth = 3;
843 let picker = NonOverlapSubLevelPicker::new(
844 10,
845 10000,
846 min_depth,
847 100,
848 Arc::new(RangeOverlapStrategy::default()),
849 true,
850 compaction_config::max_l0_compact_level_count() as usize,
851 true,
852 );
853 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
854 assert_eq!(6, ret.len());
855
856 for plan in ret {
857 if plan.sstable_infos.len() >= min_depth {
858 assert!(plan.expected);
859 } else {
860 assert!(!plan.expected);
861 }
862 }
863 }
864 }
865
866 #[test]
867 fn test_pick_unexpected_task() {
868 let levels = vec![
869 Level {
870 level_idx: 1,
871 level_type: LevelType::Nonoverlapping,
872 table_infos: vec![generate_table(0, 1, 50, 100, 2)], total_file_size: 50,
874 ..Default::default()
875 },
876 Level {
877 level_idx: 2,
878 level_type: LevelType::Nonoverlapping,
879 table_infos: vec![
880 generate_table(1, 1, 101, 150, 1), ],
882 total_file_size: 50,
883 ..Default::default()
884 },
885 Level {
886 level_idx: 3,
887 level_type: LevelType::Nonoverlapping,
888 table_infos: vec![
889 generate_table(2, 1, 151, 200, 2), ],
891 total_file_size: 50,
892 ..Default::default()
893 },
894 Level {
895 level_idx: 4,
896 level_type: LevelType::Nonoverlapping,
897 table_infos: vec![
898 generate_table(3, 1, 50, 300, 2), ],
900 total_file_size: 250,
901 ..Default::default()
902 },
903 ];
904
905 let levels_handlers = [
906 LevelHandler::new(0),
907 LevelHandler::new(1),
908 LevelHandler::new(2),
909 LevelHandler::new(3),
910 LevelHandler::new(4),
911 ];
912
913 {
914 let picker = NonOverlapSubLevelPicker::new(
916 0,
917 10000,
918 1,
919 10000,
920 Arc::new(RangeOverlapStrategy::default()),
921 true,
922 compaction_config::max_l0_compact_level_count() as usize,
923 true,
924 );
925 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
926 {
927 let plan = &ret[0];
928 assert_eq!(4, plan.sstable_infos.len());
929 }
930 }
931
932 {
933 let picker = NonOverlapSubLevelPicker::new(
935 0,
936 150,
937 1,
938 10000,
939 Arc::new(RangeOverlapStrategy::default()),
940 true,
941 compaction_config::max_l0_compact_level_count() as usize,
942 true,
943 );
944 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
945 {
946 let plan = &ret[0];
947 assert_eq!(3, plan.sstable_infos.len());
948 }
949 }
950
951 {
952 let picker = NonOverlapSubLevelPicker::new(
954 0,
955 10000,
956 1,
957 3,
958 Arc::new(RangeOverlapStrategy::default()),
959 true,
960 compaction_config::max_l0_compact_level_count() as usize,
961 true,
962 );
963 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
964 {
965 let plan = &ret[0];
966 assert_eq!(3, plan.sstable_infos.len());
967 }
968 }
969
970 {
971 let max_expected_level_count = 3;
973 let picker = NonOverlapSubLevelPicker::for_test(
974 0,
975 10000,
976 1,
977 3,
978 Arc::new(RangeOverlapStrategy::default()),
979 true,
980 max_expected_level_count,
981 true,
982 );
983 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
984 {
985 let plan = &ret[0];
986 assert_eq!(max_expected_level_count, plan.sstable_infos.len());
987
988 assert_eq!(0, plan.sstable_infos[0].1[0].sst_id);
989 assert_eq!(1, plan.sstable_infos[1].1[0].sst_id);
990 assert_eq!(2, plan.sstable_infos[2].1[0].sst_id);
991 }
992 }
993
994 {
995 let max_expected_level_count = 100;
997 let picker = NonOverlapSubLevelPicker::for_test(
998 1000,
999 10000,
1000 1,
1001 100,
1002 Arc::new(RangeOverlapStrategy::default()),
1003 true,
1004 max_expected_level_count,
1005 true,
1006 );
1007 let ret = picker.pick_l0_multi_non_overlap_level(&levels, &levels_handlers[0]);
1008 {
1009 let plan = &ret[0];
1010
1011 assert_eq!(0, plan.sstable_infos[0].1[0].sst_id);
1012 assert_eq!(1, plan.sstable_infos[1].1[0].sst_id);
1013 assert_eq!(2, plan.sstable_infos[2].1[0].sst_id);
1014 assert_eq!(3, plan.sstable_infos[3].1[0].sst_id);
1015 assert!(!plan.expected);
1016 }
1017 }
1018 }
1019
1020 #[test]
1021 fn test_select_intervals_combined() {
1022 let l0 = vec![
1024 Level {
1025 level_idx: 1,
1026 level_type: LevelType::Nonoverlapping,
1027 table_infos: vec![generate_table(1, 1, 0, 10, 1)],
1028 ..Default::default()
1029 },
1030 Level {
1031 level_idx: 2,
1032 level_type: LevelType::Overlapping,
1033 table_infos: vec![generate_table(2, 1, 0, 10, 1)],
1034 ..Default::default()
1035 },
1036 ];
1037 let picker = NonOverlapSubLevelPicker::new(
1038 0,
1039 10000,
1040 1,
1041 10000,
1042 Arc::new(RangeOverlapStrategy::default()),
1043 true,
1044 compaction_config::max_l0_compact_level_count() as usize,
1045 false,
1046 );
1047 let level_handler = LevelHandler::new(0);
1048 let intervals = picker.select_intervals(&l0, &level_handler).unwrap();
1049 assert_eq!(intervals.len(), 1);
1050 assert_eq!(intervals[0].sst_id, 1);
1051
1052 let l0 = vec![
1054 Level {
1055 level_idx: 1,
1056 level_type: LevelType::Nonoverlapping,
1057 table_infos: vec![generate_table(1, 1, 0, 10, 1)], ..Default::default()
1059 },
1060 Level {
1061 level_idx: 2,
1062 level_type: LevelType::Nonoverlapping,
1063 table_infos: vec![
1064 generate_table(2, 1, 0, 10, 1),
1065 generate_table(3, 1, 20, 30, 1),
1066 ], ..Default::default()
1068 },
1069 ];
1070 let picker = NonOverlapSubLevelPicker::new(
1071 0,
1072 10000,
1073 1,
1074 10000,
1075 Arc::new(RangeOverlapStrategy::default()),
1076 true,
1077 compaction_config::max_l0_compact_level_count() as usize,
1078 true,
1079 );
1080 let intervals = picker.select_intervals(&l0, &level_handler).unwrap();
1081 assert_eq!(intervals.len(), 2);
1082 assert_eq!(
1083 intervals.iter().map(|s| s.sst_id).collect::<Vec<_>>(),
1084 vec![2, 3]
1085 );
1086
1087 let l0 = vec![Level {
1089 level_idx: 1,
1090 level_type: LevelType::Overlapping,
1091 table_infos: vec![generate_table(1, 1, 0, 10, 1)],
1092 ..Default::default()
1093 }];
1094 let picker = NonOverlapSubLevelPicker::new(
1095 0,
1096 10000,
1097 1,
1098 10000,
1099 Arc::new(RangeOverlapStrategy::default()),
1100 true,
1101 compaction_config::max_l0_compact_level_count() as usize,
1102 true,
1103 );
1104 assert!(picker.select_intervals(&l0, &level_handler).is_none());
1105 }
1106}