Skip to content

Commit 48005a9

Browse files
committed
refactor(storage): seperate validator logic from picker (#11984)
1 parent c272d3f commit 48005a9

12 files changed

+376
-116
lines changed

src/meta/src/hummock/compaction/level_selector.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use risingwave_pb::hummock::hummock_version::Levels;
2626
use risingwave_pb::hummock::{compact_task, CompactionConfig, LevelType};
2727

2828
use super::picker::{
29-
SpaceReclaimCompactionPicker, SpaceReclaimPickerState, TtlPickerState,
29+
CompactionTaskValidator, SpaceReclaimCompactionPicker, SpaceReclaimPickerState, TtlPickerState,
3030
TtlReclaimCompactionPicker,
3131
};
3232
use super::{
@@ -95,14 +95,19 @@ impl DynamicLevelSelectorCore {
9595
select_level: usize,
9696
target_level: usize,
9797
overlap_strategy: Arc<dyn OverlapStrategy>,
98+
compaction_task_validator: Arc<CompactionTaskValidator>,
9899
) -> Box<dyn CompactionPicker> {
99100
if select_level == 0 {
100101
if target_level == 0 {
101-
Box::new(TierCompactionPicker::new(self.config.clone()))
102+
Box::new(TierCompactionPicker::new_with_validator(
103+
self.config.clone(),
104+
compaction_task_validator,
105+
))
102106
} else {
103-
Box::new(LevelCompactionPicker::new(
107+
Box::new(LevelCompactionPicker::new_with_validator(
104108
target_level,
105109
self.config.clone(),
110+
compaction_task_validator,
106111
))
107112
}
108113
} else {
@@ -374,6 +379,11 @@ impl LevelSelector for DynamicLevelSelector {
374379
let overlap_strategy =
375380
create_overlap_strategy(compaction_group.compaction_config.compaction_mode());
376381
let ctx = dynamic_level_core.get_priority_levels(levels, level_handlers);
382+
383+
// TODO: Determine which rule to enable by write limit
384+
let compaction_task_validator = Arc::new(CompactionTaskValidator::new(
385+
compaction_group.compaction_config.clone(),
386+
));
377387
for (score, select_level, target_level) in ctx.score_levels {
378388
if score <= SCORE_BASE {
379389
return None;
@@ -382,6 +392,7 @@ impl LevelSelector for DynamicLevelSelector {
382392
select_level,
383393
target_level,
384394
overlap_strategy.clone(),
395+
compaction_task_validator.clone(),
385396
);
386397
let mut stats = LocalPickerStatistic::default();
387398
if let Some(ret) = picker.pick_compaction(levels, level_handlers, &mut stats) {

src/meta/src/hummock/compaction/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,25 +247,25 @@ impl LocalSelectorStatistic {
247247
metrics
248248
.compact_skip_frequency
249249
.with_label_values(&[level_label.as_str(), "write-amp"])
250-
.inc_by(stats.skip_by_write_amp_limit);
250+
.inc();
251251
}
252252
if stats.skip_by_count_limit > 0 {
253253
metrics
254254
.compact_skip_frequency
255255
.with_label_values(&[level_label.as_str(), "count"])
256-
.inc_by(stats.skip_by_count_limit);
256+
.inc();
257257
}
258258
if stats.skip_by_pending_files > 0 {
259259
metrics
260260
.compact_skip_frequency
261261
.with_label_values(&[level_label.as_str(), "pending-files"])
262-
.inc_by(stats.skip_by_pending_files);
262+
.inc();
263263
}
264264
if stats.skip_by_overlapping > 0 {
265265
metrics
266266
.compact_skip_frequency
267267
.with_label_values(&[level_label.as_str(), "overlapping"])
268-
.inc_by(stats.skip_by_overlapping);
268+
.inc();
269269
}
270270
metrics
271271
.compact_skip_frequency

src/meta/src/hummock/compaction/picker/base_level_compaction_picker.rs

Lines changed: 63 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@ use risingwave_pb::hummock::hummock_version::Levels;
2020
use risingwave_pb::hummock::{CompactionConfig, InputLevel, Level, LevelType, OverlappingLevel};
2121

2222
use super::min_overlap_compaction_picker::NonOverlapSubLevelPicker;
23-
use super::{CompactionInput, CompactionPicker, LocalPickerStatistic};
23+
use super::{
24+
CompactionInput, CompactionPicker, CompactionTaskValidator, LocalPickerStatistic,
25+
ValidationRuleType,
26+
};
2427
use crate::hummock::compaction::create_overlap_strategy;
2528
use crate::hummock::compaction::picker::TrivialMovePicker;
2629
use crate::hummock::level_handler::LevelHandler;
2730

2831
pub struct LevelCompactionPicker {
2932
target_level: usize,
3033
config: Arc<CompactionConfig>,
34+
compaction_task_validator: Arc<CompactionTaskValidator>,
3135
}
3236

3337
impl CompactionPicker for LevelCompactionPicker {
@@ -84,13 +88,27 @@ impl CompactionPicker for LevelCompactionPicker {
8488
}
8589

8690
impl LevelCompactionPicker {
91+
#[cfg(test)]
8792
pub fn new(target_level: usize, config: Arc<CompactionConfig>) -> LevelCompactionPicker {
8893
LevelCompactionPicker {
8994
target_level,
95+
compaction_task_validator: Arc::new(CompactionTaskValidator::new(config.clone())),
9096
config,
9197
}
9298
}
9399

100+
pub fn new_with_validator(
101+
target_level: usize,
102+
config: Arc<CompactionConfig>,
103+
compaction_task_validator: Arc<CompactionTaskValidator>,
104+
) -> LevelCompactionPicker {
105+
LevelCompactionPicker {
106+
target_level,
107+
config,
108+
compaction_task_validator,
109+
}
110+
}
111+
94112
fn pick_base_trivial_move(
95113
&self,
96114
l0: &OverlappingLevel,
@@ -117,10 +135,10 @@ impl LevelCompactionPicker {
117135
level_handlers: &[LevelHandler],
118136
stats: &mut LocalPickerStatistic,
119137
) -> Option<CompactionInput> {
138+
// TODO: remove this
120139
let l0_size = l0.total_file_size - level_handlers[0].get_pending_file_size();
121140
let base_level_size = target_level.total_file_size
122141
- level_handlers[target_level.level_idx as usize].get_pending_file_size();
123-
124142
if l0_size < base_level_size {
125143
stats.skip_by_write_amp_limit += 1;
126144
return None;
@@ -157,7 +175,7 @@ impl LevelCompactionPicker {
157175

158176
let mut skip_by_pending = false;
159177
let mut input_levels = vec![];
160-
let mut min_write_amp_meet = false;
178+
161179
for input in l0_select_tables_vec {
162180
let l0_select_tables = input
163181
.sstable_infos
@@ -184,16 +202,6 @@ impl LevelCompactionPicker {
184202
continue;
185203
}
186204

187-
// The size of target level may be too large, we shall skip this compact task and wait
188-
// the data in base level compact to lower level.
189-
if target_level_size > self.config.max_compaction_bytes && strict_check {
190-
continue;
191-
}
192-
193-
if input.total_file_size >= target_level_size {
194-
min_write_amp_meet = true;
195-
}
196-
197205
input_levels.push((input, target_level_size, target_level_ssts));
198206
}
199207

@@ -204,20 +212,7 @@ impl LevelCompactionPicker {
204212
return None;
205213
}
206214

207-
if !min_write_amp_meet && strict_check {
208-
// If the write-amplification of all candidate task are large, we may hope to wait base
209-
// level compact more data to lower level. But if we skip all task, I'm
210-
// afraid the data will be blocked in level0 and will be never compacted to base level.
211-
// So we only allow one task exceed write-amplification-limit running in
212-
// level0 to base-level.
213-
return None;
214-
}
215-
216215
for (input, target_file_size, target_level_files) in input_levels {
217-
if min_write_amp_meet && input.total_file_size < target_file_size {
218-
continue;
219-
}
220-
221216
let mut select_level_inputs = input
222217
.sstable_infos
223218
.into_iter()
@@ -228,18 +223,33 @@ impl LevelCompactionPicker {
228223
})
229224
.collect_vec();
230225
select_level_inputs.reverse();
226+
let target_file_count = target_level_files.len();
231227
select_level_inputs.push(InputLevel {
232228
level_idx: target_level.level_idx,
233229
level_type: target_level.level_type,
234230
table_infos: target_level_files,
235231
});
236-
return Some(CompactionInput {
232+
233+
let result = CompactionInput {
237234
input_levels: select_level_inputs,
238235
target_level: self.target_level,
239-
target_sub_level_id: 0,
240-
});
236+
select_input_size: input.total_file_size,
237+
target_input_size: target_file_size,
238+
total_file_count: (input.total_file_count + target_file_count) as u64,
239+
..Default::default()
240+
};
241+
242+
if !self.compaction_task_validator.valid_compact_task(
243+
&result,
244+
ValidationRuleType::ToBase,
245+
stats,
246+
) && strict_check
247+
{
248+
continue;
249+
}
250+
251+
return Some(result);
241252
}
242-
stats.skip_by_write_amp_limit += 1;
243253
None
244254
}
245255

@@ -267,8 +277,6 @@ impl LevelCompactionPicker {
267277
self.config.sub_level_max_compaction_bytes,
268278
);
269279

270-
let tier_sub_level_compact_level_count =
271-
self.config.level0_sub_level_compact_level_count as usize;
272280
let non_overlap_sub_level_picker = NonOverlapSubLevelPicker::new(
273281
self.config.sub_level_max_compaction_bytes / 2,
274282
max_compaction_bytes,
@@ -284,18 +292,10 @@ impl LevelCompactionPicker {
284292
continue;
285293
}
286294

287-
let mut skip_by_write_amp = false;
288-
// Limit the number of selection levels for the non-overlapping
289-
// sub_level at least level0_sub_level_compact_level_count
290-
for (plan_index, input) in l0_select_tables_vec.into_iter().enumerate() {
291-
if plan_index == 0
292-
&& input.sstable_infos.len()
293-
< self.config.level0_sub_level_compact_level_count as usize
294-
{
295-
// first plan level count smaller than limit
296-
break;
297-
}
298-
295+
let validator = CompactionTaskValidator::new(self.config.clone());
296+
let mut select_input_size = 0;
297+
let mut total_file_count = 0;
298+
for input in l0_select_tables_vec {
299299
let mut max_level_size = 0;
300300
for level_select_table in &input.sstable_infos {
301301
let level_select_size = level_select_table
@@ -306,22 +306,6 @@ impl LevelCompactionPicker {
306306
max_level_size = std::cmp::max(max_level_size, level_select_size);
307307
}
308308

309-
// This limitation would keep our write-amplification no more than
310-
// ln(max_compaction_bytes/flush_level_bytes) /
311-
// ln(self.config.level0_sub_level_compact_level_count/2) Here we only use half
312-
// of level0_sub_level_compact_level_count just for convenient.
313-
let is_write_amp_large =
314-
max_level_size * self.config.level0_sub_level_compact_level_count as u64 / 2
315-
>= input.total_file_size;
316-
317-
if (is_write_amp_large
318-
|| input.sstable_infos.len() < tier_sub_level_compact_level_count)
319-
&& input.total_file_count < self.config.level0_max_compact_file_number as usize
320-
{
321-
skip_by_write_amp = true;
322-
continue;
323-
}
324-
325309
let mut select_level_inputs = Vec::with_capacity(input.sstable_infos.len());
326310
for level_select_sst in input.sstable_infos {
327311
if level_select_sst.is_empty() {
@@ -332,17 +316,25 @@ impl LevelCompactionPicker {
332316
level_type: LevelType::Nonoverlapping as i32,
333317
table_infos: level_select_sst,
334318
});
319+
320+
select_input_size += input.total_file_size;
321+
total_file_count += input.total_file_count;
335322
}
336323
select_level_inputs.reverse();
337-
return Some(CompactionInput {
324+
325+
let result = CompactionInput {
338326
input_levels: select_level_inputs,
339-
target_level: 0,
340327
target_sub_level_id: level.sub_level_id,
341-
});
342-
}
328+
select_input_size,
329+
total_file_count: total_file_count as u64,
330+
..Default::default()
331+
};
343332

344-
if skip_by_write_amp {
345-
stats.skip_by_write_amp_limit += 1;
333+
if !validator.valid_compact_task(&result, ValidationRuleType::Intra, stats) {
334+
continue;
335+
}
336+
337+
return Some(result);
346338
}
347339
}
348340

@@ -402,6 +394,7 @@ impl LevelCompactionPicker {
402394
target_level_idx -= 1;
403395
}
404396

397+
let select_input_size = select_sst.file_size;
405398
let input_levels = vec![
406399
InputLevel {
407400
level_idx: 0,
@@ -418,6 +411,9 @@ impl LevelCompactionPicker {
418411
input_levels,
419412
target_level: 0,
420413
target_sub_level_id: l0.sub_levels[target_level_idx].sub_level_id,
414+
select_input_size,
415+
total_file_count: 1,
416+
..Default::default()
421417
});
422418
}
423419
None
@@ -901,6 +897,7 @@ pub mod tests {
901897
}],
902898
target_level: 1,
903899
target_sub_level_id: pending_level.sub_level_id,
900+
..Default::default()
904901
};
905902
assert!(!levels_handler[0].is_level_pending_compact(&pending_level));
906903
tier_task_input.add_pending_task(1, &mut levels_handler);
@@ -918,7 +915,6 @@ pub mod tests {
918915
// But stopped by pending sub-level when trying to include more sub-levels.
919916
let mut picker = LevelCompactionPicker::new(1, config.clone());
920917
let ret = picker.pick_compaction(&levels, &levels_handler, &mut local_stats);
921-
922918
assert!(ret.is_none());
923919

924920
// Free the pending sub-level.
@@ -964,7 +960,6 @@ pub mod tests {
964960
let ret = picker
965961
.pick_compaction(&levels, &levels_handler, &mut local_stats)
966962
.unwrap();
967-
// println!("ret.input_levels: {:?}", ret.input_levels);
968963
// 1. trivial_move
969964
assert_eq!(2, ret.input_levels.len());
970965
assert!(ret.input_levels[1].table_infos.is_empty());
@@ -974,7 +969,6 @@ pub mod tests {
974969
let ret = picker
975970
.pick_compaction(&levels, &levels_handler, &mut local_stats)
976971
.unwrap();
977-
println!("ret.input_levels: {:?}", ret.input_levels);
978972
assert_eq!(3, ret.input_levels.len());
979973
assert_eq!(6, ret.input_levels[0].table_infos[0].sst_id);
980974
}

0 commit comments

Comments
 (0)