1use std::collections::HashSet;
16use std::sync::Arc;
17
18use itertools::Itertools;
19use risingwave_hummock_sdk::HummockSstableId;
20use risingwave_hummock_sdk::level::{InputLevel, Level, Levels, OverlappingLevel};
21use risingwave_hummock_sdk::sstable_info::SstableInfo;
22use risingwave_pb::hummock::LevelType;
23
24use super::{CompactionInput, CompactionPicker, LocalPickerStatistic};
25use crate::hummock::compaction::overlap_strategy::{
26 OverlapInfo, OverlapStrategy, RangeOverlapInfo,
27};
28use crate::hummock::compaction::selector::ManualCompactionOption;
29use crate::hummock::level_handler::LevelHandler;
30
31pub struct ManualCompactionPicker {
32 overlap_strategy: Arc<dyn OverlapStrategy>,
33 option: ManualCompactionOption,
34 target_level: usize,
35}
36
37impl ManualCompactionPicker {
38 pub fn new(
39 overlap_strategy: Arc<dyn OverlapStrategy>,
40 option: ManualCompactionOption,
41 target_level: usize,
42 ) -> Self {
43 Self {
44 overlap_strategy,
45 option,
46 target_level,
47 }
48 }
49
50 fn pick_l0_to_sub_level(
51 &self,
52 l0: &OverlappingLevel,
53 level_handlers: &[LevelHandler],
54 ) -> Option<CompactionInput> {
55 assert_eq!(self.option.level, 0);
56 let mut input_levels = vec![];
57 let mut sub_level_id = 0;
58 let mut start_idx = None;
59 let mut end_idx = None;
60 for (idx, level) in l0.sub_levels.iter().enumerate() {
63 if !self.filter_level_by_option(level) {
64 continue;
65 }
66 if level_handlers[0].is_level_pending_compact(level) {
67 return None;
68 }
69 if start_idx.is_none() {
71 sub_level_id = level.sub_level_id;
72 start_idx = Some(idx as u64);
73 end_idx = start_idx;
74 } else {
75 end_idx = Some(idx as u64);
76 }
77 }
78 let (start_idx, end_idx) = match (start_idx, end_idx) {
79 (Some(start_idx), Some(end_idx)) => (start_idx, end_idx),
80 _ => {
81 return None;
82 }
83 };
84 for level in l0
87 .sub_levels
88 .iter()
89 .skip(start_idx as usize)
90 .take((end_idx - start_idx + 1) as usize)
91 {
92 if level_handlers[0].is_level_pending_compact(level) {
93 return None;
94 }
95 input_levels.push(InputLevel {
96 level_idx: 0,
97 level_type: level.level_type,
98 table_infos: level.table_infos.clone(),
99 });
100 }
101 if input_levels.is_empty() {
102 return None;
103 }
104 input_levels.reverse();
105 Some(CompactionInput {
106 input_levels,
107 target_level: 0,
108 target_sub_level_id: sub_level_id,
109 ..Default::default()
110 })
111 }
112
113 fn pick_l0_to_base_level(
114 &self,
115 levels: &Levels,
116 level_handlers: &[LevelHandler],
117 ) -> Option<CompactionInput> {
118 assert!(self.option.level == 0 && self.target_level > 0);
119 for l in 1..self.target_level {
120 assert!(levels.levels[l - 1].table_infos.is_empty());
121 }
122 let l0 = &levels.l0;
123 let mut input_levels = vec![];
124 let mut max_sub_level_idx = usize::MAX;
125 let mut info = self.overlap_strategy.create_overlap_info();
126 for (idx, level) in l0.sub_levels.iter().enumerate() {
128 if !self.filter_level_by_option(level) {
129 continue;
130 }
131 if level_handlers[0].is_level_pending_compact(level) {
132 return None;
133 }
134
135 max_sub_level_idx = idx;
137 }
138 if max_sub_level_idx == usize::MAX {
139 return None;
140 }
141 for idx in 0..=max_sub_level_idx {
143 if level_handlers[0].is_level_pending_compact(&l0.sub_levels[idx]) {
145 return None;
146 }
147 for table in &l0.sub_levels[idx].table_infos {
148 info.update(&table.key_range);
149 }
150 input_levels.push(InputLevel {
151 level_idx: 0,
152 level_type: l0.sub_levels[idx].level_type,
153 table_infos: l0.sub_levels[idx].table_infos.clone(),
154 })
155 }
156 let target_input_ssts_range =
157 info.check_multiple_overlap(&levels.levels[self.target_level - 1].table_infos);
158 let target_input_ssts = if target_input_ssts_range.is_empty() {
159 vec![]
160 } else {
161 levels.levels[self.target_level - 1].table_infos[target_input_ssts_range].to_vec()
162 };
163 if target_input_ssts
164 .iter()
165 .any(|table| level_handlers[self.target_level].is_pending_compact(&table.sst_id))
166 {
167 return None;
168 }
169 if input_levels.is_empty() {
170 return None;
171 }
172 input_levels.reverse();
173 input_levels.push(InputLevel {
174 level_idx: self.target_level as u32,
175 level_type: LevelType::Nonoverlapping,
176 table_infos: target_input_ssts,
177 });
178
179 Some(CompactionInput {
180 input_levels,
181 target_level: self.target_level,
182 target_sub_level_id: 0,
183 ..Default::default()
184 })
185 }
186
187 fn filter_level_by_option(&self, level: &Level) -> bool {
190 let mut hint_sst_ids: HashSet<HummockSstableId> = HashSet::new();
191 hint_sst_ids.extend(self.option.sst_ids.iter());
192 if self
193 .overlap_strategy
194 .check_overlap_with_range(&self.option.key_range, &level.table_infos)
195 .is_empty()
196 {
197 return false;
198 }
199 if !hint_sst_ids.is_empty()
200 && !level
201 .table_infos
202 .iter()
203 .any(|t| hint_sst_ids.contains(&t.sst_id))
204 {
205 return false;
206 }
207 if !self.option.internal_table_id.is_empty()
208 && !level.table_infos.iter().any(|sst_info| {
209 sst_info
210 .table_ids
211 .iter()
212 .any(|t| self.option.internal_table_id.contains(t))
213 })
214 {
215 return false;
216 }
217 true
218 }
219}
220
221impl CompactionPicker for ManualCompactionPicker {
222 fn pick_compaction(
223 &mut self,
224 levels: &Levels,
225 level_handlers: &[LevelHandler],
226 _stats: &mut LocalPickerStatistic,
227 ) -> Option<CompactionInput> {
228 if self.option.exclusive
229 && level_handlers
230 .iter()
231 .any(|level_handler| level_handler.pending_file_count() > 0)
232 {
233 return None;
234 }
235 if self.option.level == 0 {
236 if !self.option.sst_ids.is_empty() {
237 return self.pick_l0_to_sub_level(&levels.l0, level_handlers);
238 } else if self.target_level > 0 {
239 return self.pick_l0_to_base_level(levels, level_handlers);
240 } else {
241 return None;
242 }
243 }
244 let mut hint_sst_ids: HashSet<HummockSstableId> = HashSet::new();
245 hint_sst_ids.extend(self.option.sst_ids.iter());
246 let mut range_overlap_info = RangeOverlapInfo::default();
247 range_overlap_info.update(&self.option.key_range);
248 let level = self.option.level;
249 let target_level = self.target_level;
250 assert!(
251 self.option.level == self.target_level || self.option.level + 1 == self.target_level
252 );
253 let mut select_input_ssts: Vec<SstableInfo> = levels
255 .get_level(self.option.level)
256 .table_infos
257 .iter()
258 .filter(|sst_info| hint_sst_ids.is_empty() || hint_sst_ids.contains(&sst_info.sst_id))
259 .filter(|sst_info| range_overlap_info.check_overlap(sst_info))
260 .filter(|sst_info| {
261 if self.option.internal_table_id.is_empty() {
262 return true;
263 }
264
265 for table_id in &sst_info.table_ids {
267 if self.option.internal_table_id.contains(table_id) {
268 return true;
269 }
270 }
271 false
272 })
273 .cloned()
274 .collect();
275 if select_input_ssts.is_empty() {
276 return None;
277 }
278 let target_input_ssts = if target_level == level {
279 let (left, _) = levels
281 .get_level(level)
282 .table_infos
283 .iter()
284 .find_position(|p| p.sst_id == select_input_ssts.first().unwrap().sst_id)
285 .unwrap();
286 let (right, _) = levels
287 .get_level(level)
288 .table_infos
289 .iter()
290 .find_position(|p| p.sst_id == select_input_ssts.last().unwrap().sst_id)
291 .unwrap();
292 select_input_ssts = levels.get_level(level).table_infos[left..=right].to_vec();
293 vec![]
294 } else {
295 self.overlap_strategy.check_base_level_overlap(
296 &select_input_ssts,
297 &levels.get_level(target_level).table_infos,
298 )
299 };
300 if select_input_ssts
301 .iter()
302 .any(|table| level_handlers[level].is_pending_compact(&table.sst_id))
303 {
304 return None;
305 }
306 if target_input_ssts
307 .iter()
308 .any(|table| level_handlers[target_level].is_pending_compact(&table.sst_id))
309 {
310 return None;
311 }
312
313 Some(CompactionInput {
314 select_input_size: select_input_ssts.iter().map(|sst| sst.sst_size).sum(),
315 target_input_size: target_input_ssts.iter().map(|sst| sst.sst_size).sum(),
316 total_file_count: (select_input_ssts.len() + target_input_ssts.len()) as u64,
317 input_levels: vec![
318 InputLevel {
319 level_idx: level as u32,
320 level_type: levels.levels[level - 1].level_type,
321 table_infos: select_input_ssts,
322 },
323 InputLevel {
324 level_idx: target_level as u32,
325 level_type: levels.levels[target_level - 1].level_type,
326 table_infos: target_input_ssts,
327 },
328 ],
329 target_level,
330 ..Default::default()
331 })
332 }
333}
334
335#[cfg(test)]
336pub mod tests {
337 use std::collections::{BTreeSet, HashMap};
338
339 use bytes::Bytes;
340 use risingwave_hummock_sdk::key_range::KeyRange;
341 use risingwave_hummock_sdk::version::HummockVersionStateTableInfo;
342 use risingwave_pb::hummock::compact_task;
343
344 use super::*;
345 use crate::hummock::compaction::compaction_config::CompactionConfigBuilder;
346 use crate::hummock::compaction::overlap_strategy::RangeOverlapStrategy;
347 use crate::hummock::compaction::selector::tests::{
348 assert_compaction_task, generate_l0_nonoverlapping_sublevels,
349 generate_l0_overlapping_sublevels, generate_level, generate_table,
350 };
351 use crate::hummock::compaction::selector::{CompactionSelector, ManualCompactionSelector};
352 use crate::hummock::compaction::{CompactionDeveloperConfig, LocalSelectorStatistic};
353 use crate::hummock::model::CompactionGroup;
354 use crate::hummock::test_utils::{compaction_selector_context, iterator_test_key_of_epoch};
355
356 fn clean_task_state(level_handler: &mut LevelHandler) {
357 for pending_task_id in &level_handler.pending_tasks_ids() {
358 level_handler.remove_task(*pending_task_id);
359 }
360 }
361
362 fn is_l0_to_lbase(compaction_input: &CompactionInput) -> bool {
363 compaction_input
364 .input_levels
365 .iter()
366 .take(compaction_input.input_levels.len() - 1)
367 .all(|i| i.level_idx == 0)
368 && compaction_input
369 .input_levels
370 .iter()
371 .last()
372 .unwrap()
373 .level_idx as usize
374 == compaction_input.target_level
375 && compaction_input.target_level > 0
376 }
377
378 fn is_l0_to_l0(compaction_input: &CompactionInput) -> bool {
379 compaction_input
380 .input_levels
381 .iter()
382 .all(|i| i.level_idx == 0)
383 && compaction_input.target_level == 0
384 }
385
386 #[test]
387 fn test_manual_compaction_picker() {
388 let levels = vec![
389 Level {
390 level_idx: 1,
391 level_type: LevelType::Nonoverlapping,
392 table_infos: vec![
393 generate_table(0, 1, 0, 100, 1),
394 generate_table(1, 1, 101, 200, 1),
395 generate_table(2, 1, 222, 300, 1),
396 ],
397 ..Default::default()
398 },
399 Level {
400 level_idx: 2,
401 level_type: LevelType::Nonoverlapping,
402 table_infos: vec![
403 generate_table(4, 1, 0, 100, 1),
404 generate_table(5, 1, 101, 150, 1),
405 generate_table(6, 1, 151, 201, 1),
406 generate_table(7, 1, 501, 800, 1),
407 generate_table(8, 2, 301, 400, 1),
408 ],
409 ..Default::default()
410 },
411 ];
412 let mut levels = Levels {
413 levels,
414 l0: generate_l0_nonoverlapping_sublevels(vec![]),
415 ..Default::default()
416 };
417 let mut levels_handler = vec![
418 LevelHandler::new(0),
419 LevelHandler::new(1),
420 LevelHandler::new(2),
421 ];
422 let mut local_stats = LocalPickerStatistic::default();
423
424 {
425 let option = ManualCompactionOption {
427 level: 1,
428 key_range: KeyRange {
429 left: Bytes::from(iterator_test_key_of_epoch(1, 0, 1)),
430 right: Bytes::from(iterator_test_key_of_epoch(1, 201, 1)),
431 right_exclusive: false,
432 },
433 ..Default::default()
434 };
435
436 let target_level = option.level + 1;
437 let mut picker = ManualCompactionPicker::new(
438 Arc::new(RangeOverlapStrategy::default()),
439 option,
440 target_level,
441 );
442 let result = picker
443 .pick_compaction(&levels, &levels_handler, &mut local_stats)
444 .unwrap();
445 result.add_pending_task(0, &mut levels_handler);
446
447 assert_eq!(2, result.input_levels[0].table_infos.len());
448 assert_eq!(3, result.input_levels[1].table_infos.len());
449 }
450
451 {
452 clean_task_state(&mut levels_handler[1]);
453 clean_task_state(&mut levels_handler[2]);
454
455 let option = ManualCompactionOption::default();
457 let target_level = option.level + 1;
458 let mut picker = ManualCompactionPicker::new(
459 Arc::new(RangeOverlapStrategy::default()),
460 option,
461 target_level,
462 );
463 let result = picker
464 .pick_compaction(&levels, &levels_handler, &mut local_stats)
465 .unwrap();
466 result.add_pending_task(0, &mut levels_handler);
467
468 assert_eq!(3, result.input_levels[0].table_infos.len());
469 assert_eq!(3, result.input_levels[1].table_infos.len());
470 }
471
472 {
473 clean_task_state(&mut levels_handler[1]);
474 clean_task_state(&mut levels_handler[2]);
475
476 let level_table_info = &mut levels.levels[0].table_infos;
477 let table_info_1 = &mut level_table_info[1];
478 let mut t_inner = table_info_1.get_inner();
479 t_inner.table_ids.resize(2, 0.into());
480 t_inner.table_ids[0] = 1.into();
481 t_inner.table_ids[1] = 2.into();
482 *table_info_1 = t_inner.into();
483
484 let option = ManualCompactionOption {
486 level: 1,
487 internal_table_id: HashSet::from([2.into()]),
488 ..Default::default()
489 };
490
491 let target_level = option.level + 1;
492 let mut picker = ManualCompactionPicker::new(
493 Arc::new(RangeOverlapStrategy::default()),
494 option,
495 target_level,
496 );
497
498 let result = picker
499 .pick_compaction(&levels, &levels_handler, &mut local_stats)
500 .unwrap();
501 result.add_pending_task(0, &mut levels_handler);
502
503 assert_eq!(1, result.input_levels[0].table_infos.len());
504 assert_eq!(2, result.input_levels[1].table_infos.len());
505 }
506
507 {
508 clean_task_state(&mut levels_handler[1]);
509 clean_task_state(&mut levels_handler[2]);
510
511 let level_table_info = &mut levels.levels[0].table_infos;
513 for table_info in level_table_info {
514 let mut t_inner = table_info.get_inner();
515 t_inner.table_ids.resize(2, 0.into());
516 t_inner.table_ids[0] = 1.into();
517 t_inner.table_ids[1] = 2.into();
518 *table_info = t_inner.into();
519 }
520
521 let option = ManualCompactionOption {
523 sst_ids: vec![],
524 level: 1,
525 key_range: KeyRange {
526 left: Bytes::from(iterator_test_key_of_epoch(1, 101, 1)),
527 right: Bytes::from(iterator_test_key_of_epoch(1, 199, 1)),
528 right_exclusive: false,
529 },
530 internal_table_id: HashSet::from([2.into()]),
531 target_level: None,
532 exclusive: false,
533 };
534
535 let target_level = option.level + 1;
536 let mut picker = ManualCompactionPicker::new(
537 Arc::new(RangeOverlapStrategy::default()),
538 option,
539 target_level,
540 );
541
542 let result = picker
543 .pick_compaction(&levels, &levels_handler, &mut local_stats)
544 .unwrap();
545
546 assert_eq!(1, result.input_levels[0].table_infos.len());
547 assert_eq!(2, result.input_levels[1].table_infos.len());
548 }
549 }
550
551 #[test]
552 fn test_manual_compaction_exclusive_blocked_by_pending() {
553 let (levels, mut levels_handler) = generate_test_levels();
554 let option = ManualCompactionOption {
555 exclusive: true,
556 ..Default::default()
557 };
558 let target_level = option.level + 1;
559 let mut picker = ManualCompactionPicker::new(
560 Arc::new(RangeOverlapStrategy::default()),
561 option,
562 target_level,
563 );
564
565 let pending_sst_id = levels.levels[0].table_infos[0].sst_id;
566 levels_handler[1].test_add_pending_sst(pending_sst_id, 1);
567
568 assert!(
569 picker
570 .pick_compaction(
571 &levels,
572 &levels_handler,
573 &mut LocalPickerStatistic::default()
574 )
575 .is_none()
576 );
577 }
578
579 fn generate_test_levels() -> (Levels, Vec<LevelHandler>) {
580 let mut l0 = generate_l0_overlapping_sublevels(vec![
581 vec![
582 generate_table(5, 1, 0, 500, 2),
583 generate_table(6, 2, 600, 1000, 2),
584 ],
585 vec![
586 generate_table(7, 1, 0, 500, 3),
587 generate_table(8, 2, 600, 1000, 3),
588 ],
589 vec![
590 generate_table(9, 1, 300, 500, 4),
591 generate_table(10, 2, 600, 1000, 4),
592 ],
593 ]);
594 l0.sub_levels[1].level_type = LevelType::Nonoverlapping as _;
596 assert_eq!(l0.sub_levels.len(), 3);
597 let mut levels = vec![
598 Level {
599 level_idx: 1,
600 level_type: LevelType::Nonoverlapping,
601 table_infos: vec![
602 generate_table(3, 1, 0, 100, 1),
603 generate_table(4, 2, 2000, 3000, 1),
604 ],
605 ..Default::default()
606 },
607 Level {
608 level_idx: 2,
609 level_type: LevelType::Nonoverlapping,
610 table_infos: vec![
611 generate_table(1, 1, 0, 100, 1),
612 generate_table(2, 2, 2000, 3000, 1),
613 ],
614 ..Default::default()
615 },
616 ];
617 assert_eq!(levels.len(), 2);
619 for iter in [l0.sub_levels.iter_mut(), levels.iter_mut()] {
620 for (idx, l) in iter.enumerate() {
621 for t in &mut l.table_infos {
622 let mut t_inner = t.get_inner();
623 t_inner.table_ids.clear();
624 if idx == 0 {
625 t_inner
626 .table_ids
627 .push((((t.sst_id.as_raw_id() % 2) + 1) as u32).into());
628 } else {
629 t_inner.table_ids.push(3.into());
630 }
631 *t = t_inner.into();
632 }
633 }
634 }
635 let levels = Levels {
636 levels,
637 l0,
638 ..Default::default()
639 };
640
641 let levels_handler = vec![
642 LevelHandler::new(0),
643 LevelHandler::new(1),
644 LevelHandler::new(2),
645 ];
646 (levels, levels_handler)
647 }
648
649 fn generate_intra_test_levels() -> (Levels, Vec<LevelHandler>) {
650 let l0 = generate_l0_overlapping_sublevels(vec![]);
651 let levels = vec![Level {
652 level_idx: 1,
653 level_type: LevelType::Nonoverlapping,
654 table_infos: vec![
655 generate_table(1, 1, 0, 100, 1),
656 generate_table(2, 2, 100, 200, 1),
657 generate_table(3, 2, 200, 300, 1),
658 generate_table(4, 2, 300, 400, 1),
659 ],
660 ..Default::default()
661 }];
662 let levels = Levels {
663 levels,
664 l0,
665 ..Default::default()
666 };
667
668 let levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
669 (levels, levels_handler)
670 }
671
672 #[test]
673 fn test_l0_empty() {
674 let l0 = generate_l0_nonoverlapping_sublevels(vec![]);
675 let levels = vec![Level {
676 level_idx: 1,
677 level_type: LevelType::Nonoverlapping,
678 table_infos: vec![],
679 total_file_size: 0,
680 sub_level_id: 0,
681 uncompressed_file_size: 0,
682 ..Default::default()
683 }];
684 let levels = Levels {
685 levels,
686 l0,
687 ..Default::default()
688 };
689 let levels_handler = vec![LevelHandler::new(0), LevelHandler::new(1)];
690 let option = ManualCompactionOption {
691 sst_ids: vec![1.into()],
692 level: 0,
693 key_range: KeyRange {
694 left: Bytes::default(),
695 right: Bytes::default(),
696 right_exclusive: false,
697 },
698 internal_table_id: HashSet::default(),
699 target_level: None,
700 exclusive: false,
701 };
702 let mut picker =
703 ManualCompactionPicker::new(Arc::new(RangeOverlapStrategy::default()), option, 0);
704 assert!(
705 picker
706 .pick_compaction(
707 &levels,
708 &levels_handler,
709 &mut LocalPickerStatistic::default()
710 )
711 .is_none()
712 );
713 }
714
715 #[test]
716 fn test_l0_basic() {
717 let (levels, levels_handler) = generate_test_levels();
718
719 let option = ManualCompactionOption {
721 sst_ids: vec![],
722 level: 0,
723 key_range: KeyRange {
724 left: Bytes::default(),
725 right: Bytes::default(),
726 right_exclusive: false,
727 },
728 internal_table_id: HashSet::default(),
729 target_level: None,
730 exclusive: false,
731 };
732 let mut picker = ManualCompactionPicker::new(
733 Arc::new(RangeOverlapStrategy::default()),
734 option.clone(),
735 0,
736 );
737 let mut local_stats = LocalPickerStatistic::default();
738 assert!(
739 picker
740 .pick_compaction(&levels, &levels_handler, &mut local_stats)
741 .is_none()
742 );
743
744 let mut picker =
746 ManualCompactionPicker::new(Arc::new(RangeOverlapStrategy::default()), option, 1);
747 let mut expected = [vec![5, 6], vec![7, 8], vec![9, 10]];
748 expected.reverse();
749 let result = picker
750 .pick_compaction(&levels, &levels_handler, &mut local_stats)
751 .unwrap();
752 assert_eq!(result.input_levels.len(), 4);
753 assert!(is_l0_to_lbase(&result));
754 assert_eq!(result.target_level, 1);
755 for (l, e) in expected.iter().enumerate().take(3) {
756 assert_eq!(
757 result.input_levels[l]
758 .table_infos
759 .iter()
760 .map(|s| s.sst_id)
761 .collect_vec(),
762 *e
763 );
764 }
765 assert_eq!(
766 result.input_levels[3].table_infos,
767 vec![levels.levels[0].table_infos[0].clone()]
768 );
769
770 let option = ManualCompactionOption {
772 sst_ids: vec![],
773 level: 0,
774 key_range: KeyRange {
775 left: Bytes::from(iterator_test_key_of_epoch(1, 0, 2)),
776 right: Bytes::from(iterator_test_key_of_epoch(1, 200, 2)),
777 right_exclusive: false,
778 },
779 internal_table_id: HashSet::default(),
780 target_level: None,
781 exclusive: false,
782 };
783 let mut picker =
784 ManualCompactionPicker::new(Arc::new(RangeOverlapStrategy::default()), option, 1);
785 let mut expected = [vec![5, 6], vec![7, 8]];
786 expected.reverse();
787 let result = picker
788 .pick_compaction(&levels, &levels_handler, &mut local_stats)
789 .unwrap();
790 assert_eq!(result.input_levels.len(), 3);
791 assert!(is_l0_to_lbase(&result));
792 assert_eq!(result.target_level, 1);
793 for (l, e) in expected.iter().enumerate().take(2) {
794 assert_eq!(
795 result.input_levels[l]
796 .table_infos
797 .iter()
798 .map(|s| s.sst_id)
799 .collect_vec(),
800 *e
801 );
802 }
803 assert_eq!(
804 result.input_levels[2].table_infos,
805 vec![levels.levels[0].table_infos[0].clone()]
806 );
807 }
808
809 #[test]
810 fn test_l0_to_l0_option_sst_ids() {
811 let (levels, levels_handler) = generate_test_levels();
812 let sst_id_filters = vec![
814 (0, vec![6], vec![vec![5, 6]]),
815 (0, vec![7], vec![vec![7, 8]]),
816 (0, vec![9], vec![vec![9, 10]]),
817 (0, vec![6, 9], vec![vec![5, 6], vec![7, 8], vec![9, 10]]),
818 (0, vec![8, 9], vec![vec![7, 8], vec![9, 10]]),
819 (0, vec![6, 8, 9], vec![vec![5, 6], vec![7, 8], vec![9, 10]]),
820 ];
821 let mut local_stats = LocalPickerStatistic::default();
822 for (input_level, sst_id_filter, expected) in &sst_id_filters {
823 let expected = expected.iter().rev().cloned().collect_vec();
824 let option = ManualCompactionOption {
825 sst_ids: sst_id_filter.iter().cloned().map(Into::into).collect(),
826 level: *input_level as _,
827 key_range: KeyRange {
828 left: Bytes::default(),
829 right: Bytes::default(),
830 right_exclusive: false,
831 },
832 internal_table_id: HashSet::default(),
833 target_level: None,
834 exclusive: false,
835 };
836 let mut picker = ManualCompactionPicker::new(
837 Arc::new(RangeOverlapStrategy::default()),
838 option.clone(),
839 input_level + 1,
841 );
842 let result = picker
843 .pick_compaction(&levels, &levels_handler, &mut local_stats)
844 .unwrap();
845 assert!(is_l0_to_l0(&result));
846 assert_eq!(result.input_levels.len(), expected.len());
847 for (i, e) in expected.iter().enumerate().take(result.input_levels.len()) {
848 assert_eq!(
849 result.input_levels[i]
850 .table_infos
851 .iter()
852 .map(|s| s.sst_id)
853 .collect_vec(),
854 *e
855 );
856 }
857 }
858 }
859
860 #[test]
861 fn test_l0_to_lbase_option_internal_table() {
862 let (levels, mut levels_handler) = generate_test_levels();
863 let input_level = 0;
864 let target_level = input_level + 1;
865 let mut local_stats = LocalPickerStatistic::default();
866 {
867 let option = ManualCompactionOption {
868 sst_ids: vec![],
869 level: input_level,
870 key_range: KeyRange {
871 left: Bytes::default(),
872 right: Bytes::default(),
873 right_exclusive: false,
874 },
875 internal_table_id: HashSet::from([100.into()]),
877 target_level: None,
878 exclusive: false,
879 };
880 let mut picker = ManualCompactionPicker::new(
881 Arc::new(RangeOverlapStrategy::default()),
882 option,
883 target_level,
884 );
885 assert!(
886 picker
887 .pick_compaction(&levels, &levels_handler, &mut local_stats)
888 .is_none()
889 )
890 }
891
892 {
893 let option = ManualCompactionOption {
894 sst_ids: vec![],
895 level: input_level,
896 key_range: KeyRange {
897 left: Bytes::default(),
898 right: Bytes::default(),
899 right_exclusive: false,
900 },
901 internal_table_id: HashSet::from([1.into(), 2.into(), 3.into()]),
903 target_level: None,
904 exclusive: false,
905 };
906 let mut picker = ManualCompactionPicker::new(
907 Arc::new(RangeOverlapStrategy::default()),
908 option,
909 target_level,
910 );
911 let result = picker
912 .pick_compaction(&levels, &levels_handler, &mut local_stats)
913 .unwrap();
914 assert_eq!(result.input_levels.len(), 4);
915 assert!(is_l0_to_lbase(&result));
916 assert_eq!(result.target_level, 1);
917 assert!(is_l0_to_lbase(&result));
918 assert_eq!(
919 result
920 .input_levels
921 .iter()
922 .take(3)
923 .flat_map(|s| s.table_infos.clone())
924 .map(|s| s.sst_id)
925 .collect_vec(),
926 vec![9, 10, 7, 8, 5, 6]
927 );
928 assert_eq!(
929 result.input_levels[3]
930 .table_infos
931 .iter()
932 .map(|s| s.sst_id)
933 .collect_vec(),
934 vec![3]
935 );
936 }
937
938 {
939 let option = ManualCompactionOption {
940 sst_ids: vec![],
941 level: input_level,
942 key_range: KeyRange {
943 left: Bytes::default(),
944 right: Bytes::default(),
945 right_exclusive: false,
946 },
947 internal_table_id: HashSet::from([3.into()]),
949 target_level: None,
950 exclusive: false,
951 };
952 let mut picker = ManualCompactionPicker::new(
953 Arc::new(RangeOverlapStrategy::default()),
954 option,
955 target_level,
956 );
957 let result = picker
958 .pick_compaction(&levels, &levels_handler, &mut local_stats)
959 .unwrap();
960 assert_eq!(result.input_levels.len(), 4);
961 assert!(is_l0_to_lbase(&result));
962 assert_eq!(
963 result
964 .input_levels
965 .iter()
966 .take(3)
967 .flat_map(|s| s.table_infos.clone())
968 .map(|s| s.sst_id)
969 .collect_vec(),
970 vec![9, 10, 7, 8, 5, 6]
971 );
972 assert_eq!(
973 result.input_levels[3]
974 .table_infos
975 .iter()
976 .map(|s| s.sst_id)
977 .collect_vec(),
978 vec![3]
979 );
980 assert_eq!(result.target_level, 1);
981 }
982
983 {
984 let option = ManualCompactionOption {
985 sst_ids: vec![],
986 level: input_level,
987 key_range: KeyRange {
988 left: Bytes::default(),
989 right: Bytes::default(),
990 right_exclusive: false,
991 },
992 internal_table_id: HashSet::from([1.into()]),
995 target_level: None,
996 exclusive: false,
997 };
998 let mut picker = ManualCompactionPicker::new(
999 Arc::new(RangeOverlapStrategy::default()),
1000 option,
1001 target_level,
1002 );
1003 let result = picker
1004 .pick_compaction(&levels, &levels_handler, &mut local_stats)
1005 .unwrap();
1006 result.add_pending_task(0, &mut levels_handler);
1007 assert_eq!(result.input_levels.len(), 2);
1008 assert!(is_l0_to_lbase(&result));
1009 assert_eq!(result.target_level, 1);
1010 assert_eq!(
1011 result
1012 .input_levels
1013 .iter()
1014 .take(1)
1015 .flat_map(|s| s.table_infos.clone())
1016 .map(|s| s.sst_id)
1017 .collect_vec(),
1018 vec![5, 6]
1019 );
1020 assert_eq!(
1021 result.input_levels[1]
1022 .table_infos
1023 .iter()
1024 .map(|s| s.sst_id)
1025 .collect_vec(),
1026 vec![3]
1027 );
1028
1029 let option = ManualCompactionOption {
1031 sst_ids: vec![],
1032 level: input_level,
1033 key_range: KeyRange {
1034 left: Bytes::default(),
1035 right: Bytes::default(),
1036 right_exclusive: false,
1037 },
1038 internal_table_id: HashSet::from([3.into()]),
1040 target_level: None,
1041 exclusive: false,
1042 };
1043 let mut picker = ManualCompactionPicker::new(
1044 Arc::new(RangeOverlapStrategy::default()),
1045 option,
1046 target_level,
1047 );
1048 assert!(
1050 picker
1051 .pick_compaction(&levels, &levels_handler, &mut local_stats)
1052 .is_none()
1053 );
1054
1055 clean_task_state(&mut levels_handler[0]);
1056 clean_task_state(&mut levels_handler[1]);
1057 }
1058 }
1059
1060 #[test]
1061 fn test_ln_to_lnext_option_internal_table() {
1062 let (levels, levels_handler) = generate_test_levels();
1063 let input_level = 1;
1064 let target_level = input_level + 1;
1065 let mut local_stats = LocalPickerStatistic::default();
1066 {
1067 let option = ManualCompactionOption {
1068 sst_ids: vec![],
1069 level: input_level,
1070 key_range: KeyRange {
1071 left: Bytes::default(),
1072 right: Bytes::default(),
1073 right_exclusive: false,
1074 },
1075 internal_table_id: HashSet::from([100.into()]),
1077 target_level: None,
1078 exclusive: false,
1079 };
1080 let mut picker = ManualCompactionPicker::new(
1081 Arc::new(RangeOverlapStrategy::default()),
1082 option,
1083 target_level,
1084 );
1085 assert!(
1086 picker
1087 .pick_compaction(&levels, &levels_handler, &mut local_stats)
1088 .is_none()
1089 )
1090 }
1091
1092 {
1093 let expected_input_level_sst_ids = [vec![4], vec![2]];
1094 let option = ManualCompactionOption {
1095 sst_ids: vec![],
1096 level: input_level,
1097 key_range: KeyRange {
1098 left: Bytes::default(),
1099 right: Bytes::default(),
1100 right_exclusive: false,
1101 },
1102 internal_table_id: HashSet::from([1.into()]),
1104 target_level: None,
1105 exclusive: false,
1106 };
1107 let mut picker = ManualCompactionPicker::new(
1108 Arc::new(RangeOverlapStrategy::default()),
1109 option,
1110 target_level,
1111 );
1112 let result = picker
1113 .pick_compaction(&levels, &levels_handler, &mut local_stats)
1114 .unwrap();
1115 assert_eq!(
1116 result.input_levels.len(),
1117 expected_input_level_sst_ids.len()
1118 );
1119 assert_eq!(result.target_level, target_level);
1120 for (l, e) in expected_input_level_sst_ids
1121 .iter()
1122 .enumerate()
1123 .take(result.input_levels.len())
1124 {
1125 assert_eq!(
1126 result.input_levels[l]
1127 .table_infos
1128 .iter()
1129 .map(|s| s.sst_id)
1130 .collect_vec(),
1131 *e
1132 );
1133 }
1134 }
1135 }
1136
1137 #[test]
1138 fn test_ln_to_lnext_option_sst_ids() {
1139 let (levels, levels_handler) = generate_test_levels();
1140 let sst_id_filters = vec![
1142 (1, vec![3], vec![vec![3], vec![1]]),
1143 (1, vec![4], vec![vec![4], vec![2]]),
1144 (1, vec![3, 4], vec![vec![3, 4], vec![1, 2]]),
1145 ];
1146 let mut local_stats = LocalPickerStatistic::default();
1147 for (input_level, sst_id_filter, expected) in &sst_id_filters {
1148 let option = ManualCompactionOption {
1149 sst_ids: sst_id_filter.iter().cloned().map(Into::into).collect(),
1150 level: *input_level as _,
1151 key_range: KeyRange {
1152 left: Bytes::default(),
1153 right: Bytes::default(),
1154 right_exclusive: false,
1155 },
1156 internal_table_id: HashSet::default(),
1157 target_level: None,
1158 exclusive: false,
1159 };
1160 let mut picker = ManualCompactionPicker::new(
1161 Arc::new(RangeOverlapStrategy::default()),
1162 option.clone(),
1163 input_level + 1,
1164 );
1165 let result = picker
1166 .pick_compaction(&levels, &levels_handler, &mut local_stats)
1167 .unwrap();
1168 assert_eq!(result.input_levels.len(), expected.len());
1169 for (i, e) in expected.iter().enumerate().take(result.input_levels.len()) {
1170 assert_eq!(
1171 result.input_levels[i]
1172 .table_infos
1173 .iter()
1174 .map(|s| s.sst_id)
1175 .collect_vec(),
1176 *e
1177 );
1178 }
1179 }
1180 }
1181
1182 #[test]
1183 fn test_ln_to_ln() {
1184 let (levels, levels_handler) = generate_intra_test_levels();
1185 let sst_id_filters = vec![
1187 (1, vec![1], vec![vec![1], vec![]]),
1188 (1, vec![3], vec![vec![3], vec![]]),
1189 (1, vec![4], vec![vec![4], vec![]]),
1190 (1, vec![3, 4], vec![vec![3, 4], vec![]]),
1191 (1, vec![1, 4], vec![vec![1, 2, 3, 4], vec![]]),
1192 (1, vec![2, 4], vec![vec![2, 3, 4], vec![]]),
1193 (1, vec![1, 3], vec![vec![1, 2, 3], vec![]]),
1194 ];
1195 for (input_level, sst_id_filter, expected) in &sst_id_filters {
1196 let option = ManualCompactionOption {
1197 sst_ids: sst_id_filter.iter().cloned().map(Into::into).collect(),
1198 level: *input_level as _,
1199 key_range: KeyRange {
1200 left: Bytes::default(),
1201 right: Bytes::default(),
1202 right_exclusive: false,
1203 },
1204 internal_table_id: HashSet::default(),
1205 target_level: None,
1206 exclusive: false,
1207 };
1208 let mut picker = ManualCompactionPicker::new(
1209 Arc::new(RangeOverlapStrategy::default()),
1210 option.clone(),
1211 *input_level as _,
1212 );
1213 let result = picker
1214 .pick_compaction(
1215 &levels,
1216 &levels_handler,
1217 &mut LocalPickerStatistic::default(),
1218 )
1219 .unwrap();
1220 assert_eq!(result.input_levels.len(), expected.len());
1221 for (i, e) in expected.iter().enumerate().take(result.input_levels.len()) {
1222 assert_eq!(
1223 result.input_levels[i]
1224 .table_infos
1225 .iter()
1226 .map(|s| s.sst_id)
1227 .collect_vec(),
1228 *e
1229 );
1230 }
1231 }
1232 }
1233
1234 #[test]
1235 fn test_manual_compaction_selector_l0() {
1236 let config = CompactionConfigBuilder::new().max_level(4).build();
1237 let group_config = CompactionGroup::new(1, config);
1238 let l0 = generate_l0_nonoverlapping_sublevels(vec![
1239 generate_table(0, 1, 0, 500, 1),
1240 generate_table(1, 1, 0, 500, 1),
1241 ]);
1242 assert_eq!(l0.sub_levels.len(), 2);
1243 let levels = vec![
1244 generate_level(1, vec![]),
1245 generate_level(2, vec![]),
1246 generate_level(3, vec![]),
1247 Level {
1248 level_idx: 4,
1249 level_type: LevelType::Nonoverlapping,
1250 table_infos: vec![
1251 generate_table(2, 1, 0, 100, 1),
1252 generate_table(3, 1, 101, 200, 1),
1253 generate_table(4, 1, 222, 300, 1),
1254 ],
1255 ..Default::default()
1256 },
1257 ];
1258 assert_eq!(levels.len(), 4);
1259 let levels = Levels {
1260 levels,
1261 l0,
1262 ..Default::default()
1263 };
1264 let mut levels_handler = (0..5).map(LevelHandler::new).collect_vec();
1265 let mut local_stats = LocalSelectorStatistic::default();
1266
1267 {
1269 let option = ManualCompactionOption {
1270 sst_ids: [0, 1].iter().cloned().map(Into::into).collect(),
1271 key_range: KeyRange {
1272 left: Bytes::default(),
1273 right: Bytes::default(),
1274 right_exclusive: false,
1275 },
1276 internal_table_id: HashSet::default(),
1277 level: 0,
1278 target_level: None,
1279 exclusive: false,
1280 };
1281 let mut selector = ManualCompactionSelector::new(option);
1282 let task = selector
1283 .pick_compaction(
1284 1,
1285 compaction_selector_context(
1286 &group_config,
1287 &levels,
1288 &BTreeSet::new(),
1289 &mut levels_handler,
1290 &mut local_stats,
1291 &HashMap::default(),
1292 Arc::new(CompactionDeveloperConfig::default()),
1293 &Default::default(),
1294 &HummockVersionStateTableInfo::empty(),
1295 ),
1296 )
1297 .unwrap();
1298 assert_compaction_task(&task, &levels_handler);
1299 assert_eq!(task.input.input_levels.len(), 2);
1300 assert_eq!(task.input.input_levels[0].level_idx, 0);
1301 assert_eq!(task.input.input_levels[1].level_idx, 0);
1302 assert_eq!(task.input.target_level, 0);
1303 }
1304
1305 for level_handler in &mut levels_handler {
1306 for pending_task_id in &level_handler.pending_tasks_ids() {
1307 level_handler.remove_task(*pending_task_id);
1308 }
1309 }
1310
1311 {
1313 let option = ManualCompactionOption {
1314 sst_ids: vec![],
1315 key_range: KeyRange {
1316 left: Bytes::default(),
1317 right: Bytes::default(),
1318 right_exclusive: false,
1319 },
1320 internal_table_id: HashSet::default(),
1321 level: 0,
1322 target_level: None,
1323 exclusive: false,
1324 };
1325 let mut selector = ManualCompactionSelector::new(option);
1326 let task = selector
1327 .pick_compaction(
1328 2,
1329 compaction_selector_context(
1330 &group_config,
1331 &levels,
1332 &BTreeSet::new(),
1333 &mut levels_handler,
1334 &mut local_stats,
1335 &HashMap::default(),
1336 Arc::new(CompactionDeveloperConfig::default()),
1337 &Default::default(),
1338 &HummockVersionStateTableInfo::empty(),
1339 ),
1340 )
1341 .unwrap();
1342 assert_compaction_task(&task, &levels_handler);
1343 assert_eq!(task.input.input_levels.len(), 3);
1344 assert_eq!(task.input.input_levels[0].level_idx, 0);
1345 assert_eq!(task.input.input_levels[1].level_idx, 0);
1346 assert_eq!(task.input.input_levels[2].level_idx, 4);
1347 assert_eq!(task.input.target_level, 4);
1348 }
1349 }
1350
1351 #[test]
1352 fn test_manual_compaction_selector_rejects_l0_non_base_target_level() {
1353 let config = CompactionConfigBuilder::new().max_level(4).build();
1354 let group_config = CompactionGroup::new(1, config);
1355 let l0 = generate_l0_nonoverlapping_sublevels(vec![
1356 generate_table(0, 1, 0, 500, 1),
1357 generate_table(1, 1, 0, 500, 1),
1358 ]);
1359 let levels = Levels {
1360 levels: vec![
1361 generate_level(1, vec![]),
1362 generate_level(2, vec![]),
1363 generate_level(3, vec![generate_table(2, 1, 0, 500, 1)]),
1364 generate_level(4, vec![]),
1365 ],
1366 l0,
1367 ..Default::default()
1368 };
1369 let mut levels_handler = (0..5).map(LevelHandler::new).collect_vec();
1370 let mut local_stats = LocalSelectorStatistic::default();
1371 let option = ManualCompactionOption {
1372 sst_ids: vec![],
1373 key_range: KeyRange {
1374 left: Bytes::default(),
1375 right: Bytes::default(),
1376 right_exclusive: false,
1377 },
1378 internal_table_id: HashSet::default(),
1379 level: 0,
1380 target_level: Some(4),
1381 exclusive: false,
1382 };
1383 let mut selector = ManualCompactionSelector::new(option);
1384
1385 assert!(
1386 selector
1387 .pick_compaction(
1388 1,
1389 compaction_selector_context(
1390 &group_config,
1391 &levels,
1392 &BTreeSet::new(),
1393 &mut levels_handler,
1394 &mut local_stats,
1395 &HashMap::default(),
1396 Arc::new(CompactionDeveloperConfig::default()),
1397 &Default::default(),
1398 &HummockVersionStateTableInfo::empty(),
1399 ),
1400 )
1401 .is_none()
1402 );
1403 assert_eq!(
1404 selector.validation_error(),
1405 Some("target_level for L0 must be 3, got 4")
1406 );
1407 }
1408
1409 #[test]
1411 fn test_manual_compaction_selector() {
1412 let config = CompactionConfigBuilder::new().max_level(4).build();
1413 let group_config = CompactionGroup::new(1, config);
1414 let l0 = generate_l0_nonoverlapping_sublevels(vec![]);
1415 assert_eq!(l0.sub_levels.len(), 0);
1416 let levels = vec![
1417 generate_level(1, vec![]),
1418 generate_level(2, vec![]),
1419 generate_level(
1420 3,
1421 vec![
1422 generate_table(0, 1, 150, 151, 1),
1423 generate_table(1, 1, 250, 251, 1),
1424 ],
1425 ),
1426 Level {
1427 level_idx: 4,
1428 level_type: LevelType::Nonoverlapping,
1429 table_infos: vec![
1430 generate_table(2, 1, 0, 100, 1),
1431 generate_table(3, 1, 101, 200, 1),
1432 generate_table(4, 1, 222, 300, 1),
1433 generate_table(5, 1, 333, 400, 1),
1434 generate_table(6, 1, 444, 500, 1),
1435 generate_table(7, 1, 555, 600, 1),
1436 ],
1437 ..Default::default()
1438 },
1439 ];
1440 assert_eq!(levels.len(), 4);
1441 let levels = Levels {
1442 levels,
1443 l0,
1444 ..Default::default()
1445 };
1446 let mut levels_handler = (0..5).map(LevelHandler::new).collect_vec();
1447 let mut local_stats = LocalSelectorStatistic::default();
1448
1449 {
1451 let option = ManualCompactionOption {
1452 sst_ids: [0, 1].iter().cloned().map(Into::into).collect(),
1453 key_range: KeyRange {
1454 left: Bytes::default(),
1455 right: Bytes::default(),
1456 right_exclusive: false,
1457 },
1458 internal_table_id: HashSet::default(),
1459 level: 3,
1460 target_level: None,
1461 exclusive: false,
1462 };
1463 let mut selector = ManualCompactionSelector::new(option);
1464 let task = selector
1465 .pick_compaction(
1466 1,
1467 compaction_selector_context(
1468 &group_config,
1469 &levels,
1470 &BTreeSet::new(),
1471 &mut levels_handler,
1472 &mut local_stats,
1473 &HashMap::default(),
1474 Arc::new(CompactionDeveloperConfig::default()),
1475 &Default::default(),
1476 &HummockVersionStateTableInfo::empty(),
1477 ),
1478 )
1479 .unwrap();
1480 assert_compaction_task(&task, &levels_handler);
1481 assert_eq!(task.input.input_levels.len(), 2);
1482 assert_eq!(task.input.input_levels[0].level_idx, 3);
1483 assert_eq!(task.input.input_levels[0].table_infos.len(), 2);
1484 assert_eq!(task.input.input_levels[1].level_idx, 4);
1485 assert_eq!(task.input.input_levels[1].table_infos.len(), 2);
1486 assert_eq!(task.input.target_level, 4);
1487 }
1488
1489 for level_handler in &mut levels_handler {
1490 for pending_task_id in &level_handler.pending_tasks_ids() {
1491 level_handler.remove_task(*pending_task_id);
1492 }
1493 }
1494
1495 {
1497 let option = ManualCompactionOption {
1498 sst_ids: [0, 1].iter().cloned().map(Into::into).collect(),
1499 key_range: KeyRange {
1500 left: Bytes::default(),
1501 right: Bytes::default(),
1502 right_exclusive: false,
1503 },
1504 internal_table_id: HashSet::default(),
1505 level: 3,
1506 target_level: Some(3),
1507 exclusive: false,
1508 };
1509 let mut selector = ManualCompactionSelector::new(option);
1510 let task = selector
1511 .pick_compaction(
1512 2,
1513 compaction_selector_context(
1514 &group_config,
1515 &levels,
1516 &BTreeSet::new(),
1517 &mut levels_handler,
1518 &mut local_stats,
1519 &HashMap::default(),
1520 Arc::new(CompactionDeveloperConfig::default()),
1521 &Default::default(),
1522 &HummockVersionStateTableInfo::empty(),
1523 ),
1524 )
1525 .unwrap();
1526 assert_compaction_task(&task, &levels_handler);
1527 assert_eq!(task.input.input_levels.len(), 2);
1528 assert_eq!(task.input.input_levels[0].level_idx, 3);
1529 assert_eq!(task.input.input_levels[0].table_infos.len(), 2);
1530 assert_eq!(task.input.input_levels[1].level_idx, 3);
1531 assert_eq!(task.input.input_levels[1].table_infos.len(), 0);
1532 assert_eq!(task.input.target_level, 3);
1533 }
1534
1535 for level_handler in &mut levels_handler {
1536 for pending_task_id in &level_handler.pending_tasks_ids() {
1537 level_handler.remove_task(*pending_task_id);
1538 }
1539 }
1540
1541 {
1543 let option = ManualCompactionOption {
1544 sst_ids: vec![],
1545 key_range: KeyRange {
1546 left: Bytes::default(),
1547 right: Bytes::default(),
1548 right_exclusive: false,
1549 },
1550 internal_table_id: HashSet::default(),
1551 level: 4,
1552 target_level: None,
1553 exclusive: false,
1554 };
1555 let mut selector = ManualCompactionSelector::new(option);
1556 let task = selector
1557 .pick_compaction(
1558 1,
1559 compaction_selector_context(
1560 &group_config,
1561 &levels,
1562 &BTreeSet::new(),
1563 &mut levels_handler,
1564 &mut local_stats,
1565 &HashMap::default(),
1566 Arc::new(CompactionDeveloperConfig::default()),
1567 &Default::default(),
1568 &HummockVersionStateTableInfo::empty(),
1569 ),
1570 )
1571 .unwrap();
1572 assert_compaction_task(&task, &levels_handler);
1573 assert_eq!(task.input.input_levels.len(), 2);
1574 assert_eq!(task.input.input_levels[0].level_idx, 4);
1575 assert_eq!(task.input.input_levels[0].table_infos.len(), 6);
1576 assert_eq!(task.input.input_levels[1].level_idx, 4);
1577 assert_eq!(task.input.input_levels[1].table_infos.len(), 0);
1578 assert_eq!(task.input.target_level, 4);
1579 assert!(matches!(
1580 task.compaction_task_type,
1581 compact_task::TaskType::Manual
1582 ));
1583 }
1584 }
1585}