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
54#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
56#[serde(rename_all = "snake_case")]
57pub enum ScanMemoryMode {
58 #[default]
60 Basic,
61 Extended,
63}
64
65pub(crate) const FETCH_OPTION_TIMEOUT: Duration = Duration::from_secs(3);
67
68#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
71#[serde(default)]
72pub struct MitoConfig {
73 pub num_workers: usize,
77 pub worker_channel_size: usize,
79 pub worker_request_batch_size: usize,
81
82 pub manifest_checkpoint_distance: u64,
86 pub experimental_manifest_keep_removed_file_count: usize,
91 #[serde(with = "humantime_serde")]
96 pub experimental_manifest_keep_removed_file_ttl: Duration,
97 pub compress_manifest: bool,
99
100 pub max_background_index_builds: usize,
103 pub experimental_enable_series_index: bool,
106 pub experimental_series_index_max_size: ReadableSize,
110 pub experimental_enable_range_index: bool,
113 #[serde(with = "humantime_serde")]
115 pub experimental_series_index_maintenance_interval: Duration,
116 #[serde(with = "humantime_serde")]
119 pub experimental_series_index_bucket_width: Duration,
120 pub max_background_flushes: usize,
122 pub max_background_compactions: usize,
124 pub max_background_purges: usize,
126 pub experimental_compaction_memory_limit: MemoryLimit,
130 pub experimental_compaction_on_exhausted: OnExhaustedPolicy,
132 pub experimental_compaction_output_parallelism: usize,
136
137 #[serde(with = "humantime_serde")]
140 pub auto_flush_interval: Duration,
141 pub global_write_buffer_size: ReadableSize,
143 pub global_write_buffer_reject_size: ReadableSize,
145 pub default_region_write_buffer_size: ReadableSize,
149
150 pub sst_meta_cache_size: ReadableSize,
153 pub vector_cache_size: ReadableSize,
155 pub page_cache_size: ReadableSize,
157 pub selector_result_cache_size: ReadableSize,
159 pub range_result_cache_size: ReadableSize,
161 pub prefilter_result_cache_size: ReadableSize,
163 pub enable_write_cache: bool,
165 pub write_cache_path: String,
167 pub write_cache_size: ReadableSize,
169 #[serde(with = "humantime_serde")]
171 pub write_cache_ttl: Option<Duration>,
172 pub preload_index_cache: bool,
174 pub index_cache_percent: u8,
178 pub enable_refill_cache_on_read: bool,
181 pub manifest_cache_size: ReadableSize,
183
184 pub sst_write_buffer_size: ReadableSize,
188 pub max_concurrent_scan_files: usize,
191 pub allow_stale_entries: bool,
193 pub scan_memory_limit: MemoryLimit,
197 pub scan_memory_mode: ScanMemoryMode,
201 pub scan_memory_on_exhausted: OnExhaustedPolicy,
205
206 pub index: IndexConfig,
208 pub inverted_index: InvertedIndexConfig,
210 pub fulltext_index: FulltextIndexConfig,
212 pub bloom_filter_index: BloomFilterConfig,
214
215 #[serde(with = "humantime_serde")]
218 pub min_compaction_interval: Duration,
219 pub schedule_compaction_after_edit: bool,
221
222 pub default_flat_format: bool,
225
226 pub experimental_series_scan_v2: bool,
228
229 pub gc: GcConfig,
230}
231
232impl Default for MitoConfig {
233 fn default() -> Self {
234 let mut mito_config = MitoConfig {
235 num_workers: divide_num_cpus(2),
236 worker_channel_size: 128,
237 worker_request_batch_size: 64,
238 manifest_checkpoint_distance: 10,
239 experimental_manifest_keep_removed_file_count: 256,
240 experimental_manifest_keep_removed_file_ttl: Duration::from_secs(60 * 60),
241 compress_manifest: false,
242 max_background_index_builds: divide_num_cpus(8),
243 experimental_enable_series_index: false,
244 experimental_series_index_max_size: ReadableSize::gb(5),
245 experimental_enable_range_index: false,
246 experimental_series_index_maintenance_interval:
247 DEFAULT_SERIES_INDEX_MAINTENANCE_INTERVAL,
248 experimental_series_index_bucket_width: Duration::from_secs(5 * 24 * 60 * 60),
249 max_background_flushes: divide_num_cpus(2),
250 max_background_compactions: divide_num_cpus(4),
251 max_background_purges: get_total_cpu_cores(),
252 experimental_compaction_memory_limit: MemoryLimit::Unlimited,
253 experimental_compaction_on_exhausted: OnExhaustedPolicy::default(),
254 experimental_compaction_output_parallelism: 1,
255 auto_flush_interval: Duration::from_secs(10 * 60),
256 global_write_buffer_size: ReadableSize::gb(1),
257 global_write_buffer_reject_size: ReadableSize::gb(2),
258 default_region_write_buffer_size: ReadableSize::mb(0),
259 sst_meta_cache_size: ReadableSize::mb(128),
260 vector_cache_size: ReadableSize::mb(512),
261 page_cache_size: ReadableSize::mb(512),
262 selector_result_cache_size: ReadableSize::mb(512),
263 range_result_cache_size: ReadableSize::mb(512),
264 prefilter_result_cache_size: ReadableSize::mb(128),
265 enable_write_cache: false,
266 write_cache_path: String::new(),
267 write_cache_size: ReadableSize::gb(5),
268 write_cache_ttl: None,
269 preload_index_cache: true,
270 index_cache_percent: DEFAULT_INDEX_CACHE_PERCENT,
271 enable_refill_cache_on_read: true,
272 manifest_cache_size: ReadableSize::mb(256),
273 sst_write_buffer_size: DEFAULT_WRITE_BUFFER_SIZE,
274 max_concurrent_scan_files: DEFAULT_MAX_CONCURRENT_SCAN_FILES,
275 allow_stale_entries: false,
276 scan_memory_limit: MemoryLimit::Percentage(25),
277 scan_memory_mode: ScanMemoryMode::Basic,
278 scan_memory_on_exhausted: OnExhaustedPolicy::Fail,
279 index: IndexConfig::default(),
280 inverted_index: InvertedIndexConfig::default(),
281 fulltext_index: FulltextIndexConfig::default(),
282 bloom_filter_index: BloomFilterConfig::default(),
283 min_compaction_interval: Duration::from_secs(0),
284 schedule_compaction_after_edit: true,
285 default_flat_format: true,
286 experimental_series_scan_v2: false,
288 gc: GcConfig::default(),
289 };
290
291 if let Some(sys_memory) = get_total_memory_readable() {
293 mito_config.adjust_buffer_and_cache_size(sys_memory);
294 }
295
296 mito_config
297 }
298}
299
300impl MitoConfig {
301 pub fn sanitize(&mut self, data_home: &str) -> Result<()> {
305 if matches!(
306 self.scan_memory_on_exhausted,
307 OnExhaustedPolicy::Wait { .. }
308 ) {
309 warn!(
310 "scan_memory_on_exhausted = {:?} is deprecated and is treated as fail; \
311 scan memory exhaustion now fails immediately. Set scan_memory_on_exhausted = \"fail\"",
312 self.scan_memory_on_exhausted
313 );
314 self.scan_memory_on_exhausted = OnExhaustedPolicy::Fail;
315 }
316
317 if self.experimental_enable_series_index {
318 snafu::ensure!(
319 self.experimental_series_index_max_size.as_bytes() >= 1024,
320 crate::error::InvalidConfigSnafu {
321 reason: "experimental_series_index_max_size must be at least 1KiB"
322 }
323 );
324 }
325 if self.num_workers == 0 {
327 self.num_workers = divide_num_cpus(2);
328 }
329
330 if self.worker_channel_size == 0 {
332 warn!("Sanitize channel size 0 to 1");
333 self.worker_channel_size = 1;
334 }
335
336 if self.max_background_flushes == 0 {
337 warn!(
338 "Sanitize max background flushes 0 to {}",
339 divide_num_cpus(2)
340 );
341 self.max_background_flushes = divide_num_cpus(2);
342 }
343 if self.max_background_compactions == 0 {
344 warn!(
345 "Sanitize max background compactions 0 to {}",
346 divide_num_cpus(4)
347 );
348 self.max_background_compactions = divide_num_cpus(4);
349 }
350 if self.max_background_purges == 0 {
351 let cpu_cores = get_total_cpu_cores();
352 warn!("Sanitize max background purges 0 to {}", cpu_cores);
353 self.max_background_purges = cpu_cores;
354 }
355
356 if self
357 .experimental_series_index_maintenance_interval
358 .is_zero()
359 {
360 warn!("Sanitize series-index maintenance interval 0 to 5 minutes");
361 self.experimental_series_index_maintenance_interval =
362 DEFAULT_SERIES_INDEX_MAINTENANCE_INTERVAL;
363 }
364
365 if self.global_write_buffer_reject_size <= self.global_write_buffer_size {
366 self.global_write_buffer_reject_size = self.global_write_buffer_size * 2;
367 warn!(
368 "Sanitize global write buffer reject size to {}",
369 self.global_write_buffer_reject_size
370 );
371 }
372
373 if self.sst_write_buffer_size < MULTIPART_UPLOAD_MINIMUM_SIZE {
374 self.sst_write_buffer_size = MULTIPART_UPLOAD_MINIMUM_SIZE;
375 warn!(
376 "Sanitize sst write buffer size to {}",
377 self.sst_write_buffer_size
378 );
379 }
380
381 if self.write_cache_path.trim().is_empty() {
383 self.write_cache_path = data_home.to_string();
384 }
385
386 if self.index_cache_percent == 0 || self.index_cache_percent >= 100 {
388 warn!(
389 "Invalid index_cache_percent {}, resetting to default {}",
390 self.index_cache_percent, DEFAULT_INDEX_CACHE_PERCENT
391 );
392 self.index_cache_percent = DEFAULT_INDEX_CACHE_PERCENT;
393 }
394
395 self.index.sanitize(data_home, &self.inverted_index)?;
396
397 Ok(())
398 }
399
400 fn adjust_buffer_and_cache_size(&mut self, sys_memory: ReadableSize) {
401 let global_write_buffer_size = cmp::min(
403 sys_memory / GLOBAL_WRITE_BUFFER_SIZE_FACTOR,
404 ReadableSize::gb(1),
405 );
406 let global_write_buffer_reject_size = global_write_buffer_size * 2;
408 let sst_meta_cache_size = cmp::min(
411 sys_memory / SST_META_CACHE_SIZE_FACTOR,
412 ReadableSize::mb(512),
413 );
414 let prefilter_result_cache_size = cmp::min(
415 sys_memory / PREFILTER_RESULT_CACHE_SIZE_FACTOR,
416 ReadableSize::mb(128),
417 );
418 let mem_cache_size = cmp::min(sys_memory / MEM_CACHE_SIZE_FACTOR, ReadableSize::mb(512));
420 let page_cache_size = sys_memory / PAGE_CACHE_SIZE_FACTOR;
421
422 self.global_write_buffer_size = global_write_buffer_size;
423 self.global_write_buffer_reject_size = global_write_buffer_reject_size;
424 self.sst_meta_cache_size = sst_meta_cache_size;
425 self.vector_cache_size = mem_cache_size;
426 self.page_cache_size = page_cache_size;
427 self.selector_result_cache_size = mem_cache_size;
428 self.range_result_cache_size = mem_cache_size;
429 self.prefilter_result_cache_size = prefilter_result_cache_size;
431
432 self.index.adjust_buffer_and_cache_size(sys_memory);
433 }
434
435 #[cfg(test)]
437 pub fn enable_write_cache(
438 mut self,
439 path: String,
440 size: ReadableSize,
441 ttl: Option<Duration>,
442 ) -> Self {
443 self.enable_write_cache = true;
444 self.write_cache_path = path;
445 self.write_cache_size = size;
446 self.write_cache_ttl = ttl;
447 self
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 #[test]
456 fn test_scan_memory_policy_upgrade() {
457 for policy in [
458 None,
459 Some("fail"),
460 Some("skip"),
461 Some("wait"),
462 Some("wait(30s)"),
463 ] {
464 let input = policy
465 .map(|policy| format!("scan_memory_on_exhausted = {policy:?}"))
466 .unwrap_or_default();
467 let mut config: MitoConfig = toml::from_str(&input).unwrap();
468 let compaction_policy = config.experimental_compaction_on_exhausted;
469 config.sanitize("/data").unwrap();
470 assert_eq!(OnExhaustedPolicy::Fail, config.scan_memory_on_exhausted);
471 assert_eq!(
472 compaction_policy,
473 config.experimental_compaction_on_exhausted
474 );
475 let serialized = toml::to_string(&config).unwrap();
476 assert!(serialized.contains("scan_memory_on_exhausted = \"fail\""));
477 config.sanitize("/data").unwrap();
478 assert_eq!(serialized, toml::to_string(&config).unwrap());
479 }
480 for policy in ["unknown", "wait(invalid)", "wait()"] {
481 assert!(
482 toml::from_str::<MitoConfig>(&format!("scan_memory_on_exhausted = {policy:?}"))
483 .is_err()
484 );
485 }
486 }
487
488 #[test]
489 fn test_default_auto_flush_interval() {
490 assert_eq!(
491 Duration::from_secs(10 * 60),
492 MitoConfig::default().auto_flush_interval
493 );
494 }
495
496 #[test]
497 fn test_adjust_sst_metadata_and_prefilter_cache_caps_independently() {
498 let mut config = MitoConfig::default();
499
500 config.adjust_buffer_and_cache_size(ReadableSize::gb(1));
501 assert_eq!(ReadableSize::mb(128), config.sst_meta_cache_size);
502 assert_eq!(ReadableSize::mb(32), config.prefilter_result_cache_size);
503
504 config.adjust_buffer_and_cache_size(ReadableSize::gb(64));
505 assert_eq!(ReadableSize::mb(512), config.sst_meta_cache_size);
506 assert_eq!(ReadableSize::mb(128), config.prefilter_result_cache_size);
507 }
508
509 #[test]
510 fn test_series_index_config() {
511 assert!(!MitoConfig::default().experimental_enable_series_index);
512 assert!(!MitoConfig::default().experimental_enable_range_index);
513 assert_eq!(
514 MitoConfig::default().experimental_series_index_bucket_width,
515 Duration::from_secs(5 * 24 * 60 * 60)
516 );
517 let mut config: MitoConfig = toml::from_str(
518 "experimental_enable_series_index = true
519 experimental_enable_range_index = false
520 experimental_series_index_max_size = '64MiB'
521 experimental_series_index_maintenance_interval = '30s'
522 experimental_series_index_bucket_width = '2days'",
523 )
524 .unwrap();
525 config.sanitize("/data").unwrap();
526 assert!(config.experimental_enable_series_index);
527 assert_eq!(
528 ReadableSize::mb(64),
529 config.experimental_series_index_max_size
530 );
531 assert!(!config.experimental_enable_range_index);
532 assert_eq!(
533 config.experimental_series_index_maintenance_interval,
534 Duration::from_secs(30)
535 );
536 assert_eq!(
537 config.experimental_series_index_bucket_width,
538 Duration::from_secs(2 * 24 * 60 * 60)
539 );
540 let restored: MitoConfig = toml::from_str(&toml::to_string(&config).unwrap()).unwrap();
541 assert_eq!(config, restored);
542 config.experimental_series_index_max_size = ReadableSize(1023);
543 assert!(config.sanitize("/data").is_err());
544 config.experimental_series_index_max_size = ReadableSize(1024);
545 config.sanitize("/data").unwrap();
546
547 let mut config: MitoConfig =
548 toml::from_str("experimental_series_index_maintenance_interval = '0s'").unwrap();
549 config.sanitize("/data").unwrap();
550 assert_eq!(
551 config.experimental_series_index_maintenance_interval,
552 MitoConfig::default().experimental_series_index_maintenance_interval
553 );
554 }
555
556 #[test]
557 fn test_range_index_config() {
558 let config: MitoConfig = toml::from_str("experimental_enable_series_index = true").unwrap();
559 assert!(!config.experimental_enable_range_index);
560 for enabled in [false, true] {
561 let config: MitoConfig =
562 toml::from_str(&format!("experimental_enable_range_index = {enabled}")).unwrap();
563 assert_eq!(enabled, config.experimental_enable_range_index);
564 let restored: MitoConfig = toml::from_str(&toml::to_string(&config).unwrap()).unwrap();
565 assert_eq!(config, restored);
566 }
567 }
568}
569
570#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
572#[serde(rename_all = "snake_case")]
573pub enum IndexBuildMode {
574 #[default]
576 Sync,
577 Async,
579}
580
581#[serde_as]
582#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
583#[serde(default)]
584pub struct IndexConfig {
585 pub aux_path: String,
595
596 pub staging_size: ReadableSize,
598 #[serde(with = "humantime_serde")]
602 pub staging_ttl: Option<Duration>,
603
604 pub build_mode: IndexBuildMode,
606
607 pub write_buffer_size: ReadableSize,
609
610 pub metadata_cache_size: ReadableSize,
612 pub content_cache_size: ReadableSize,
614 pub content_cache_page_size: ReadableSize,
616 pub result_cache_size: ReadableSize,
618}
619
620impl Default for IndexConfig {
621 fn default() -> Self {
622 Self {
623 aux_path: String::new(),
624 staging_size: ReadableSize::gb(2),
625 staging_ttl: Some(Duration::from_secs(7 * 24 * 60 * 60)),
626 build_mode: IndexBuildMode::default(),
627 write_buffer_size: ReadableSize::mb(8),
628 metadata_cache_size: ReadableSize::mb(64),
629 content_cache_size: ReadableSize::mb(128),
630 content_cache_page_size: ReadableSize::kb(64),
631 result_cache_size: ReadableSize::mb(128),
632 }
633 }
634}
635
636impl IndexConfig {
637 pub fn sanitize(
638 &mut self,
639 data_home: &str,
640 inverted_index: &InvertedIndexConfig,
641 ) -> Result<()> {
642 #[allow(deprecated)]
643 if self.aux_path.is_empty() && !inverted_index.intermediate_path.is_empty() {
644 self.aux_path.clone_from(&inverted_index.intermediate_path);
645 warn!(
646 "`inverted_index.intermediate_path` is deprecated, use
647 `index.aux_path` instead. Set `index.aux_path` to {}",
648 &inverted_index.intermediate_path
649 )
650 }
651 if self.aux_path.is_empty() {
652 let path = Path::new(data_home).join("index_intermediate");
653 self.aux_path = path.as_os_str().to_string_lossy().to_string();
654 }
655
656 if self.write_buffer_size < MULTIPART_UPLOAD_MINIMUM_SIZE {
657 self.write_buffer_size = MULTIPART_UPLOAD_MINIMUM_SIZE;
658 warn!(
659 "Sanitize index write buffer size to {}",
660 self.write_buffer_size
661 );
662 }
663
664 if self.staging_ttl.map(|ttl| ttl.is_zero()).unwrap_or(false) {
665 self.staging_ttl = None;
666 }
667
668 Ok(())
669 }
670
671 pub fn adjust_buffer_and_cache_size(&mut self, sys_memory: ReadableSize) {
672 let cache_size = cmp::min(sys_memory / MEM_CACHE_SIZE_FACTOR, ReadableSize::mb(128));
673 self.result_cache_size = cmp::min(self.result_cache_size, cache_size);
674 self.content_cache_size = cmp::min(self.content_cache_size, cache_size);
675
676 let metadata_cache_size = cmp::min(
677 sys_memory / INDEX_METADATA_CACHE_SIZE_FACTOR,
678 ReadableSize::mb(64),
679 );
680 self.metadata_cache_size = cmp::min(self.metadata_cache_size, metadata_cache_size);
681 }
682}
683
684#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
686#[serde(rename_all = "snake_case")]
687pub enum Mode {
688 #[default]
690 Auto,
691 Disable,
693}
694
695impl Mode {
696 pub fn disabled(&self) -> bool {
698 matches!(self, Mode::Disable)
699 }
700
701 pub fn auto(&self) -> bool {
703 matches!(self, Mode::Auto)
704 }
705}
706
707#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
709#[serde(rename_all = "snake_case")]
710pub enum MemoryThreshold {
711 #[default]
713 Auto,
714 Unlimited,
716 #[serde(untagged)]
718 Size(ReadableSize),
719}
720
721#[serde_as]
723#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
724#[serde(default)]
725pub struct InvertedIndexConfig {
726 pub create_on_flush: Mode,
728 pub create_on_compaction: Mode,
730 pub apply_on_query: Mode,
732
733 pub mem_threshold_on_create: MemoryThreshold,
735
736 #[deprecated = "use [IndexConfig::aux_path] instead"]
737 #[serde(skip_serializing)]
738 pub intermediate_path: String,
739
740 #[deprecated = "use [IndexConfig::write_buffer_size] instead"]
741 #[serde(skip_serializing)]
742 pub write_buffer_size: ReadableSize,
743}
744
745impl Default for InvertedIndexConfig {
746 #[allow(deprecated)]
747 fn default() -> Self {
748 Self {
749 create_on_flush: Mode::Auto,
750 create_on_compaction: Mode::Auto,
751 apply_on_query: Mode::Auto,
752 mem_threshold_on_create: MemoryThreshold::Auto,
753 write_buffer_size: ReadableSize::mb(8),
754 intermediate_path: String::new(),
755 }
756 }
757}
758
759impl InvertedIndexConfig {
760 pub fn mem_threshold_on_create(&self) -> Option<usize> {
761 match self.mem_threshold_on_create {
762 MemoryThreshold::Auto => {
763 if let Some(sys_memory) = get_total_memory_readable() {
764 Some((sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as usize)
765 } else {
766 Some(ReadableSize::mb(64).as_bytes() as usize)
767 }
768 }
769 MemoryThreshold::Unlimited => None,
770 MemoryThreshold::Size(size) => Some(size.as_bytes() as usize),
771 }
772 }
773}
774
775#[serde_as]
777#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
778#[serde(default)]
779pub struct FulltextIndexConfig {
780 pub create_on_flush: Mode,
782 pub create_on_compaction: Mode,
784 pub apply_on_query: Mode,
786 pub mem_threshold_on_create: MemoryThreshold,
788 pub compress: bool,
790}
791
792impl Default for FulltextIndexConfig {
793 fn default() -> Self {
794 Self {
795 create_on_flush: Mode::Auto,
796 create_on_compaction: Mode::Auto,
797 apply_on_query: Mode::Auto,
798 mem_threshold_on_create: MemoryThreshold::Auto,
799 compress: true,
800 }
801 }
802}
803
804impl FulltextIndexConfig {
805 pub fn mem_threshold_on_create(&self) -> usize {
806 match self.mem_threshold_on_create {
807 MemoryThreshold::Auto => {
808 if let Some(sys_memory) = get_total_memory_readable() {
809 (sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as _
810 } else {
811 ReadableSize::mb(64).as_bytes() as _
812 }
813 }
814 MemoryThreshold::Unlimited => usize::MAX,
815 MemoryThreshold::Size(size) => size.as_bytes() as _,
816 }
817 }
818}
819
820#[serde_as]
822#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
823#[serde(default)]
824pub struct BloomFilterConfig {
825 pub create_on_flush: Mode,
827 pub create_on_compaction: Mode,
829 pub apply_on_query: Mode,
831 pub mem_threshold_on_create: MemoryThreshold,
833}
834
835impl Default for BloomFilterConfig {
836 fn default() -> Self {
837 Self {
838 create_on_flush: Mode::Auto,
839 create_on_compaction: Mode::Auto,
840 apply_on_query: Mode::Auto,
841 mem_threshold_on_create: MemoryThreshold::Auto,
842 }
843 }
844}
845
846impl BloomFilterConfig {
847 pub fn mem_threshold_on_create(&self) -> Option<usize> {
848 match self.mem_threshold_on_create {
849 MemoryThreshold::Auto => {
850 if let Some(sys_memory) = get_total_memory_readable() {
851 Some((sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as usize)
852 } else {
853 Some(ReadableSize::mb(64).as_bytes() as usize)
854 }
855 }
856 MemoryThreshold::Unlimited => None,
857 MemoryThreshold::Size(size) => Some(size.as_bytes() as usize),
858 }
859 }
860}
861
862fn divide_num_cpus(divisor: usize) -> usize {
864 debug_assert!(divisor > 0);
865 let cores = get_total_cpu_cores();
866 debug_assert!(cores > 0);
867
868 cores.div_ceil(divisor)
869}