1use std::cmp;
18use std::path::Path;
19use std::time::Duration;
20
21use common_base::memory_limit::MemoryLimit;
22use common_base::readable_size::ReadableSize;
23use common_memory_manager::OnExhaustedPolicy;
24use common_stat::{get_total_cpu_cores, get_total_memory_readable};
25use common_telemetry::warn;
26use serde::{Deserialize, Serialize};
27use serde_with::serde_as;
28
29use crate::cache::file_cache::DEFAULT_INDEX_CACHE_PERCENT;
30use crate::error::Result;
31use crate::gc::GcConfig;
32use crate::sst::DEFAULT_WRITE_BUFFER_SIZE;
33
34const MULTIPART_UPLOAD_MINIMUM_SIZE: ReadableSize = ReadableSize::mb(5);
35const DEFAULT_SERIES_INDEX_MAINTENANCE_INTERVAL: Duration = Duration::from_secs(5 * 60);
36pub(crate) const DEFAULT_MAX_CONCURRENT_SCAN_FILES: usize = 384;
38
39const GLOBAL_WRITE_BUFFER_SIZE_FACTOR: u64 = 8;
41const SST_META_CACHE_SIZE_FACTOR: u64 = 8;
43const PREFILTER_RESULT_CACHE_SIZE_FACTOR: u64 = 32;
45const INDEX_METADATA_CACHE_SIZE_FACTOR: u64 = 32;
47const MEM_CACHE_SIZE_FACTOR: u64 = 16;
49const PAGE_CACHE_SIZE_FACTOR: u64 = 8;
51const INDEX_CREATE_MEM_THRESHOLD_FACTOR: u64 = 16;
53
54pub(crate) const FETCH_OPTION_TIMEOUT: Duration = Duration::from_secs(3);
56
57#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
60#[serde(default)]
61pub struct MitoConfig {
62 pub num_workers: usize,
66 pub worker_channel_size: usize,
68 pub worker_request_batch_size: usize,
70
71 pub manifest_checkpoint_distance: u64,
75 pub experimental_manifest_keep_removed_file_count: usize,
80 #[serde(with = "humantime_serde")]
85 pub experimental_manifest_keep_removed_file_ttl: Duration,
86 pub compress_manifest: bool,
88
89 pub max_background_index_builds: usize,
92 pub experimental_enable_series_index: bool,
95 pub experimental_series_index_max_size: ReadableSize,
99 pub experimental_enable_range_index: bool,
102 #[serde(with = "humantime_serde")]
104 pub experimental_series_index_maintenance_interval: Duration,
105 #[serde(with = "humantime_serde")]
108 pub experimental_series_index_bucket_width: Duration,
109 pub max_background_flushes: usize,
111 pub max_background_compactions: usize,
113 pub max_background_purges: usize,
115 pub experimental_compaction_memory_limit: MemoryLimit,
119 pub experimental_compaction_on_exhausted: OnExhaustedPolicy,
121
122 #[serde(with = "humantime_serde")]
125 pub auto_flush_interval: Duration,
126 pub global_write_buffer_size: ReadableSize,
128 pub global_write_buffer_reject_size: ReadableSize,
130 pub default_region_write_buffer_size: ReadableSize,
134
135 pub sst_meta_cache_size: ReadableSize,
138 pub vector_cache_size: ReadableSize,
140 pub page_cache_size: ReadableSize,
142 pub selector_result_cache_size: ReadableSize,
144 pub range_result_cache_size: ReadableSize,
146 pub prefilter_result_cache_size: ReadableSize,
148 pub enable_write_cache: bool,
150 pub write_cache_path: String,
152 pub write_cache_size: ReadableSize,
154 #[serde(with = "humantime_serde")]
156 pub write_cache_ttl: Option<Duration>,
157 pub preload_index_cache: bool,
159 pub index_cache_percent: u8,
163 pub enable_refill_cache_on_read: bool,
166 pub manifest_cache_size: ReadableSize,
168
169 pub sst_write_buffer_size: ReadableSize,
173 pub max_concurrent_scan_files: usize,
175 pub allow_stale_entries: bool,
177 pub scan_memory_limit: MemoryLimit,
181 pub scan_memory_on_exhausted: OnExhaustedPolicy,
186
187 pub index: IndexConfig,
189 pub inverted_index: InvertedIndexConfig,
191 pub fulltext_index: FulltextIndexConfig,
193 pub bloom_filter_index: BloomFilterConfig,
195
196 #[serde(with = "humantime_serde")]
199 pub min_compaction_interval: Duration,
200 pub schedule_compaction_after_edit: bool,
202
203 pub default_flat_format: bool,
206
207 pub experimental_series_scan_v2: bool,
209
210 pub gc: GcConfig,
211}
212
213impl Default for MitoConfig {
214 fn default() -> Self {
215 let mut mito_config = MitoConfig {
216 num_workers: divide_num_cpus(2),
217 worker_channel_size: 128,
218 worker_request_batch_size: 64,
219 manifest_checkpoint_distance: 10,
220 experimental_manifest_keep_removed_file_count: 256,
221 experimental_manifest_keep_removed_file_ttl: Duration::from_secs(60 * 60),
222 compress_manifest: false,
223 max_background_index_builds: divide_num_cpus(8),
224 experimental_enable_series_index: false,
225 experimental_series_index_max_size: ReadableSize::gb(5),
226 experimental_enable_range_index: false,
227 experimental_series_index_maintenance_interval:
228 DEFAULT_SERIES_INDEX_MAINTENANCE_INTERVAL,
229 experimental_series_index_bucket_width: Duration::from_secs(5 * 24 * 60 * 60),
230 max_background_flushes: divide_num_cpus(2),
231 max_background_compactions: divide_num_cpus(4),
232 max_background_purges: get_total_cpu_cores(),
233 experimental_compaction_memory_limit: MemoryLimit::Unlimited,
234 experimental_compaction_on_exhausted: OnExhaustedPolicy::default(),
235 auto_flush_interval: Duration::from_secs(10 * 60),
236 global_write_buffer_size: ReadableSize::gb(1),
237 global_write_buffer_reject_size: ReadableSize::gb(2),
238 default_region_write_buffer_size: ReadableSize::mb(0),
239 sst_meta_cache_size: ReadableSize::mb(128),
240 vector_cache_size: ReadableSize::mb(512),
241 page_cache_size: ReadableSize::mb(512),
242 selector_result_cache_size: ReadableSize::mb(512),
243 range_result_cache_size: ReadableSize::mb(512),
244 prefilter_result_cache_size: ReadableSize::mb(128),
245 enable_write_cache: false,
246 write_cache_path: String::new(),
247 write_cache_size: ReadableSize::gb(5),
248 write_cache_ttl: None,
249 preload_index_cache: true,
250 index_cache_percent: DEFAULT_INDEX_CACHE_PERCENT,
251 enable_refill_cache_on_read: true,
252 manifest_cache_size: ReadableSize::mb(256),
253 sst_write_buffer_size: DEFAULT_WRITE_BUFFER_SIZE,
254 max_concurrent_scan_files: DEFAULT_MAX_CONCURRENT_SCAN_FILES,
255 allow_stale_entries: false,
256 scan_memory_limit: MemoryLimit::default(),
257 scan_memory_on_exhausted: OnExhaustedPolicy::Fail,
258 index: IndexConfig::default(),
259 inverted_index: InvertedIndexConfig::default(),
260 fulltext_index: FulltextIndexConfig::default(),
261 bloom_filter_index: BloomFilterConfig::default(),
262 min_compaction_interval: Duration::from_secs(0),
263 schedule_compaction_after_edit: true,
264 default_flat_format: true,
265 experimental_series_scan_v2: false,
267 gc: GcConfig::default(),
268 };
269
270 if let Some(sys_memory) = get_total_memory_readable() {
272 mito_config.adjust_buffer_and_cache_size(sys_memory);
273 }
274
275 mito_config
276 }
277}
278
279impl MitoConfig {
280 pub fn sanitize(&mut self, data_home: &str) -> Result<()> {
284 if self.experimental_enable_series_index {
285 snafu::ensure!(
286 self.experimental_series_index_max_size.as_bytes() >= 1024,
287 crate::error::InvalidConfigSnafu {
288 reason: "experimental_series_index_max_size must be at least 1KiB"
289 }
290 );
291 }
292 if self.num_workers == 0 {
294 self.num_workers = divide_num_cpus(2);
295 }
296
297 if self.worker_channel_size == 0 {
299 warn!("Sanitize channel size 0 to 1");
300 self.worker_channel_size = 1;
301 }
302
303 if self.max_background_flushes == 0 {
304 warn!(
305 "Sanitize max background flushes 0 to {}",
306 divide_num_cpus(2)
307 );
308 self.max_background_flushes = divide_num_cpus(2);
309 }
310 if self.max_background_compactions == 0 {
311 warn!(
312 "Sanitize max background compactions 0 to {}",
313 divide_num_cpus(4)
314 );
315 self.max_background_compactions = divide_num_cpus(4);
316 }
317 if self.max_background_purges == 0 {
318 let cpu_cores = get_total_cpu_cores();
319 warn!("Sanitize max background purges 0 to {}", cpu_cores);
320 self.max_background_purges = cpu_cores;
321 }
322
323 if self
324 .experimental_series_index_maintenance_interval
325 .is_zero()
326 {
327 warn!("Sanitize series-index maintenance interval 0 to 5 minutes");
328 self.experimental_series_index_maintenance_interval =
329 DEFAULT_SERIES_INDEX_MAINTENANCE_INTERVAL;
330 }
331
332 if self.global_write_buffer_reject_size <= self.global_write_buffer_size {
333 self.global_write_buffer_reject_size = self.global_write_buffer_size * 2;
334 warn!(
335 "Sanitize global write buffer reject size to {}",
336 self.global_write_buffer_reject_size
337 );
338 }
339
340 if self.sst_write_buffer_size < MULTIPART_UPLOAD_MINIMUM_SIZE {
341 self.sst_write_buffer_size = MULTIPART_UPLOAD_MINIMUM_SIZE;
342 warn!(
343 "Sanitize sst write buffer size to {}",
344 self.sst_write_buffer_size
345 );
346 }
347
348 if self.write_cache_path.trim().is_empty() {
350 self.write_cache_path = data_home.to_string();
351 }
352
353 if self.index_cache_percent == 0 || self.index_cache_percent >= 100 {
355 warn!(
356 "Invalid index_cache_percent {}, resetting to default {}",
357 self.index_cache_percent, DEFAULT_INDEX_CACHE_PERCENT
358 );
359 self.index_cache_percent = DEFAULT_INDEX_CACHE_PERCENT;
360 }
361
362 self.index.sanitize(data_home, &self.inverted_index)?;
363
364 Ok(())
365 }
366
367 fn adjust_buffer_and_cache_size(&mut self, sys_memory: ReadableSize) {
368 let global_write_buffer_size = cmp::min(
370 sys_memory / GLOBAL_WRITE_BUFFER_SIZE_FACTOR,
371 ReadableSize::gb(1),
372 );
373 let global_write_buffer_reject_size = global_write_buffer_size * 2;
375 let sst_meta_cache_size = cmp::min(
378 sys_memory / SST_META_CACHE_SIZE_FACTOR,
379 ReadableSize::mb(512),
380 );
381 let prefilter_result_cache_size = cmp::min(
382 sys_memory / PREFILTER_RESULT_CACHE_SIZE_FACTOR,
383 ReadableSize::mb(128),
384 );
385 let mem_cache_size = cmp::min(sys_memory / MEM_CACHE_SIZE_FACTOR, ReadableSize::mb(512));
387 let page_cache_size = sys_memory / PAGE_CACHE_SIZE_FACTOR;
388
389 self.global_write_buffer_size = global_write_buffer_size;
390 self.global_write_buffer_reject_size = global_write_buffer_reject_size;
391 self.sst_meta_cache_size = sst_meta_cache_size;
392 self.vector_cache_size = mem_cache_size;
393 self.page_cache_size = page_cache_size;
394 self.selector_result_cache_size = mem_cache_size;
395 self.range_result_cache_size = mem_cache_size;
396 self.prefilter_result_cache_size = prefilter_result_cache_size;
398
399 self.index.adjust_buffer_and_cache_size(sys_memory);
400 }
401
402 #[cfg(test)]
404 pub fn enable_write_cache(
405 mut self,
406 path: String,
407 size: ReadableSize,
408 ttl: Option<Duration>,
409 ) -> Self {
410 self.enable_write_cache = true;
411 self.write_cache_path = path;
412 self.write_cache_size = size;
413 self.write_cache_ttl = ttl;
414 self
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use super::*;
421
422 #[test]
423 fn test_default_auto_flush_interval() {
424 assert_eq!(
425 Duration::from_secs(10 * 60),
426 MitoConfig::default().auto_flush_interval
427 );
428 }
429
430 #[test]
431 fn test_adjust_sst_metadata_and_prefilter_cache_caps_independently() {
432 let mut config = MitoConfig::default();
433
434 config.adjust_buffer_and_cache_size(ReadableSize::gb(1));
435 assert_eq!(ReadableSize::mb(128), config.sst_meta_cache_size);
436 assert_eq!(ReadableSize::mb(32), config.prefilter_result_cache_size);
437
438 config.adjust_buffer_and_cache_size(ReadableSize::gb(64));
439 assert_eq!(ReadableSize::mb(512), config.sst_meta_cache_size);
440 assert_eq!(ReadableSize::mb(128), config.prefilter_result_cache_size);
441 }
442
443 #[test]
444 fn test_series_index_config() {
445 assert!(!MitoConfig::default().experimental_enable_series_index);
446 assert!(!MitoConfig::default().experimental_enable_range_index);
447 assert_eq!(
448 MitoConfig::default().experimental_series_index_bucket_width,
449 Duration::from_secs(5 * 24 * 60 * 60)
450 );
451 let mut config: MitoConfig = toml::from_str(
452 "experimental_enable_series_index = true
453 experimental_enable_range_index = false
454 experimental_series_index_max_size = '64MiB'
455 experimental_series_index_maintenance_interval = '30s'
456 experimental_series_index_bucket_width = '2days'",
457 )
458 .unwrap();
459 config.sanitize("/data").unwrap();
460 assert!(config.experimental_enable_series_index);
461 assert_eq!(
462 ReadableSize::mb(64),
463 config.experimental_series_index_max_size
464 );
465 assert!(!config.experimental_enable_range_index);
466 assert_eq!(
467 config.experimental_series_index_maintenance_interval,
468 Duration::from_secs(30)
469 );
470 assert_eq!(
471 config.experimental_series_index_bucket_width,
472 Duration::from_secs(2 * 24 * 60 * 60)
473 );
474 let restored: MitoConfig = toml::from_str(&toml::to_string(&config).unwrap()).unwrap();
475 assert_eq!(config, restored);
476 config.experimental_series_index_max_size = ReadableSize(1023);
477 assert!(config.sanitize("/data").is_err());
478 config.experimental_series_index_max_size = ReadableSize(1024);
479 config.sanitize("/data").unwrap();
480
481 let mut config: MitoConfig =
482 toml::from_str("experimental_series_index_maintenance_interval = '0s'").unwrap();
483 config.sanitize("/data").unwrap();
484 assert_eq!(
485 config.experimental_series_index_maintenance_interval,
486 MitoConfig::default().experimental_series_index_maintenance_interval
487 );
488 }
489
490 #[test]
491 fn test_range_index_config() {
492 let config: MitoConfig = toml::from_str("experimental_enable_series_index = true").unwrap();
493 assert!(!config.experimental_enable_range_index);
494 for enabled in [false, true] {
495 let config: MitoConfig =
496 toml::from_str(&format!("experimental_enable_range_index = {enabled}")).unwrap();
497 assert_eq!(enabled, config.experimental_enable_range_index);
498 let restored: MitoConfig = toml::from_str(&toml::to_string(&config).unwrap()).unwrap();
499 assert_eq!(config, restored);
500 }
501 }
502}
503
504#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
506#[serde(rename_all = "snake_case")]
507pub enum IndexBuildMode {
508 #[default]
510 Sync,
511 Async,
513}
514
515#[serde_as]
516#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
517#[serde(default)]
518pub struct IndexConfig {
519 pub aux_path: String,
529
530 pub staging_size: ReadableSize,
532 #[serde(with = "humantime_serde")]
536 pub staging_ttl: Option<Duration>,
537
538 pub build_mode: IndexBuildMode,
540
541 pub write_buffer_size: ReadableSize,
543
544 pub metadata_cache_size: ReadableSize,
546 pub content_cache_size: ReadableSize,
548 pub content_cache_page_size: ReadableSize,
550 pub result_cache_size: ReadableSize,
552}
553
554impl Default for IndexConfig {
555 fn default() -> Self {
556 Self {
557 aux_path: String::new(),
558 staging_size: ReadableSize::gb(2),
559 staging_ttl: Some(Duration::from_secs(7 * 24 * 60 * 60)),
560 build_mode: IndexBuildMode::default(),
561 write_buffer_size: ReadableSize::mb(8),
562 metadata_cache_size: ReadableSize::mb(64),
563 content_cache_size: ReadableSize::mb(128),
564 content_cache_page_size: ReadableSize::kb(64),
565 result_cache_size: ReadableSize::mb(128),
566 }
567 }
568}
569
570impl IndexConfig {
571 pub fn sanitize(
572 &mut self,
573 data_home: &str,
574 inverted_index: &InvertedIndexConfig,
575 ) -> Result<()> {
576 #[allow(deprecated)]
577 if self.aux_path.is_empty() && !inverted_index.intermediate_path.is_empty() {
578 self.aux_path.clone_from(&inverted_index.intermediate_path);
579 warn!(
580 "`inverted_index.intermediate_path` is deprecated, use
581 `index.aux_path` instead. Set `index.aux_path` to {}",
582 &inverted_index.intermediate_path
583 )
584 }
585 if self.aux_path.is_empty() {
586 let path = Path::new(data_home).join("index_intermediate");
587 self.aux_path = path.as_os_str().to_string_lossy().to_string();
588 }
589
590 if self.write_buffer_size < MULTIPART_UPLOAD_MINIMUM_SIZE {
591 self.write_buffer_size = MULTIPART_UPLOAD_MINIMUM_SIZE;
592 warn!(
593 "Sanitize index write buffer size to {}",
594 self.write_buffer_size
595 );
596 }
597
598 if self.staging_ttl.map(|ttl| ttl.is_zero()).unwrap_or(false) {
599 self.staging_ttl = None;
600 }
601
602 Ok(())
603 }
604
605 pub fn adjust_buffer_and_cache_size(&mut self, sys_memory: ReadableSize) {
606 let cache_size = cmp::min(sys_memory / MEM_CACHE_SIZE_FACTOR, ReadableSize::mb(128));
607 self.result_cache_size = cmp::min(self.result_cache_size, cache_size);
608 self.content_cache_size = cmp::min(self.content_cache_size, cache_size);
609
610 let metadata_cache_size = cmp::min(
611 sys_memory / INDEX_METADATA_CACHE_SIZE_FACTOR,
612 ReadableSize::mb(64),
613 );
614 self.metadata_cache_size = cmp::min(self.metadata_cache_size, metadata_cache_size);
615 }
616}
617
618#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
620#[serde(rename_all = "snake_case")]
621pub enum Mode {
622 #[default]
624 Auto,
625 Disable,
627}
628
629impl Mode {
630 pub fn disabled(&self) -> bool {
632 matches!(self, Mode::Disable)
633 }
634
635 pub fn auto(&self) -> bool {
637 matches!(self, Mode::Auto)
638 }
639}
640
641#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
643#[serde(rename_all = "snake_case")]
644pub enum MemoryThreshold {
645 #[default]
647 Auto,
648 Unlimited,
650 #[serde(untagged)]
652 Size(ReadableSize),
653}
654
655#[serde_as]
657#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
658#[serde(default)]
659pub struct InvertedIndexConfig {
660 pub create_on_flush: Mode,
662 pub create_on_compaction: Mode,
664 pub apply_on_query: Mode,
666
667 pub mem_threshold_on_create: MemoryThreshold,
669
670 #[deprecated = "use [IndexConfig::aux_path] instead"]
671 #[serde(skip_serializing)]
672 pub intermediate_path: String,
673
674 #[deprecated = "use [IndexConfig::write_buffer_size] instead"]
675 #[serde(skip_serializing)]
676 pub write_buffer_size: ReadableSize,
677}
678
679impl Default for InvertedIndexConfig {
680 #[allow(deprecated)]
681 fn default() -> Self {
682 Self {
683 create_on_flush: Mode::Auto,
684 create_on_compaction: Mode::Auto,
685 apply_on_query: Mode::Auto,
686 mem_threshold_on_create: MemoryThreshold::Auto,
687 write_buffer_size: ReadableSize::mb(8),
688 intermediate_path: String::new(),
689 }
690 }
691}
692
693impl InvertedIndexConfig {
694 pub fn mem_threshold_on_create(&self) -> Option<usize> {
695 match self.mem_threshold_on_create {
696 MemoryThreshold::Auto => {
697 if let Some(sys_memory) = get_total_memory_readable() {
698 Some((sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as usize)
699 } else {
700 Some(ReadableSize::mb(64).as_bytes() as usize)
701 }
702 }
703 MemoryThreshold::Unlimited => None,
704 MemoryThreshold::Size(size) => Some(size.as_bytes() as usize),
705 }
706 }
707}
708
709#[serde_as]
711#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
712#[serde(default)]
713pub struct FulltextIndexConfig {
714 pub create_on_flush: Mode,
716 pub create_on_compaction: Mode,
718 pub apply_on_query: Mode,
720 pub mem_threshold_on_create: MemoryThreshold,
722 pub compress: bool,
724}
725
726impl Default for FulltextIndexConfig {
727 fn default() -> Self {
728 Self {
729 create_on_flush: Mode::Auto,
730 create_on_compaction: Mode::Auto,
731 apply_on_query: Mode::Auto,
732 mem_threshold_on_create: MemoryThreshold::Auto,
733 compress: true,
734 }
735 }
736}
737
738impl FulltextIndexConfig {
739 pub fn mem_threshold_on_create(&self) -> usize {
740 match self.mem_threshold_on_create {
741 MemoryThreshold::Auto => {
742 if let Some(sys_memory) = get_total_memory_readable() {
743 (sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as _
744 } else {
745 ReadableSize::mb(64).as_bytes() as _
746 }
747 }
748 MemoryThreshold::Unlimited => usize::MAX,
749 MemoryThreshold::Size(size) => size.as_bytes() as _,
750 }
751 }
752}
753
754#[serde_as]
756#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
757#[serde(default)]
758pub struct BloomFilterConfig {
759 pub create_on_flush: Mode,
761 pub create_on_compaction: Mode,
763 pub apply_on_query: Mode,
765 pub mem_threshold_on_create: MemoryThreshold,
767}
768
769impl Default for BloomFilterConfig {
770 fn default() -> Self {
771 Self {
772 create_on_flush: Mode::Auto,
773 create_on_compaction: Mode::Auto,
774 apply_on_query: Mode::Auto,
775 mem_threshold_on_create: MemoryThreshold::Auto,
776 }
777 }
778}
779
780impl BloomFilterConfig {
781 pub fn mem_threshold_on_create(&self) -> Option<usize> {
782 match self.mem_threshold_on_create {
783 MemoryThreshold::Auto => {
784 if let Some(sys_memory) = get_total_memory_readable() {
785 Some((sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as usize)
786 } else {
787 Some(ReadableSize::mb(64).as_bytes() as usize)
788 }
789 }
790 MemoryThreshold::Unlimited => None,
791 MemoryThreshold::Size(size) => Some(size.as_bytes() as usize),
792 }
793 }
794}
795
796fn divide_num_cpus(divisor: usize) -> usize {
798 debug_assert!(divisor > 0);
799 let cores = get_total_cpu_cores();
800 debug_assert!(cores > 0);
801
802 cores.div_ceil(divisor)
803}