1use serde::{Deserialize, Serialize};
4use std::time::Duration;
5
6#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8pub struct Config {
9 pub storage: StorageConfig,
11
12 pub memory: MemoryConfig,
14
15 pub query: QueryConfig,
17
18 pub performance: PerformanceConfig,
20
21 #[cfg(target_arch = "wasm32")]
23 pub wasm: WasmConfig,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct StorageConfig {
29 pub max_sstable_size: u64,
31
32 pub memtable_size_threshold: u64,
34
35 pub compaction: CompactionConfig,
37
38 pub block_size: u32,
40
41 pub compression: CompressionConfig,
43
44 pub enable_bloom_filters: bool,
46
47 pub bloom_filter_fp_rate: f64,
49
50 pub io_threads: usize,
52
53 pub sync_mode: SyncMode,
55
56 #[serde(default = "default_use_mmap")]
93 pub use_mmap: bool,
94
95 #[serde(default = "default_mmap_min_size_bytes")]
103 pub mmap_min_size_bytes: usize,
104}
105
106fn default_use_mmap() -> bool {
108 false
109}
110
111fn default_mmap_min_size_bytes() -> usize {
113 4096
114}
115
116impl Default for StorageConfig {
117 fn default() -> Self {
118 Self {
119 max_sstable_size: 64 * 1024 * 1024, memtable_size_threshold: 16 * 1024 * 1024, compaction: CompactionConfig::default(),
122 block_size: 64 * 1024, compression: CompressionConfig::default(),
124 enable_bloom_filters: true,
125 bloom_filter_fp_rate: 0.01,
126 io_threads: num_cpus::get().min(4),
127 sync_mode: SyncMode::Normal,
128 use_mmap: default_use_mmap(),
131 mmap_min_size_bytes: default_mmap_min_size_bytes(),
132 }
133 }
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct CompactionConfig {
139 pub strategy: CompactionStrategy,
141
142 pub max_sstables: usize,
144
145 pub size_ratio: f64,
147
148 pub max_threads: usize,
150
151 pub background_interval: Duration,
153
154 pub auto_compaction: bool,
156}
157
158impl Default for CompactionConfig {
159 fn default() -> Self {
160 Self {
161 strategy: CompactionStrategy::Leveled,
162 max_sstables: 10,
163 size_ratio: 2.0,
164 max_threads: 2,
165 background_interval: Duration::from_secs(300), auto_compaction: true,
167 }
168 }
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct MemoryConfig {
174 pub max_memory: u64,
176
177 pub block_cache: CacheConfig,
179
180 pub row_cache: CacheConfig,
182
183 pub query_cache: CacheConfig,
185
186 pub allocator: AllocatorConfig,
188}
189
190impl Default for MemoryConfig {
191 fn default() -> Self {
192 let max_memory = 1024 * 1024 * 1024; Self {
195 max_memory,
196 block_cache: CacheConfig {
197 enabled: true,
198 max_size: max_memory / 4, policy: CachePolicy::Lru,
200 },
201 row_cache: CacheConfig {
202 enabled: true,
203 max_size: max_memory / 8, policy: CachePolicy::Lru,
205 },
206 query_cache: CacheConfig {
207 enabled: true,
208 max_size: max_memory / 16, policy: CachePolicy::Lru,
210 },
211 allocator: AllocatorConfig::default(),
212 }
213 }
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct CacheConfig {
219 pub enabled: bool,
221
222 pub max_size: u64,
224
225 pub policy: CachePolicy,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231pub enum CachePolicy {
232 Lru,
234 Lfu,
236 Arc,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct AllocatorConfig {
243 pub use_custom: bool,
245
246 pub small_pool_size: u64,
248
249 pub large_pool_size: u64,
251}
252
253impl Default for AllocatorConfig {
254 fn default() -> Self {
255 Self {
256 use_custom: false, small_pool_size: 64 * 1024 * 1024, large_pool_size: 256 * 1024 * 1024, }
260 }
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct QueryConfig {
266 pub max_execution_time: Duration,
268
269 pub max_result_rows: u64,
271
272 pub plan_cache_size: usize,
274
275 pub enable_optimization: bool,
277
278 pub parallel: ParallelQueryConfig,
280
281 pub query_cache_size: Option<usize>,
283
284 pub query_parallelism: Option<usize>,
286
287 pub analyze_iterations: Option<usize>,
289}
290
291impl Default for QueryConfig {
292 fn default() -> Self {
293 Self {
294 max_execution_time: Duration::from_secs(300), max_result_rows: 1_000_000,
296 plan_cache_size: 1000,
297 enable_optimization: true,
298 parallel: ParallelQueryConfig::default(),
299 query_cache_size: Some(100),
300 query_parallelism: Some(num_cpus::get()),
301 analyze_iterations: Some(5),
302 }
303 }
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct ParallelQueryConfig {
309 pub enabled: bool,
311
312 pub max_threads: usize,
314
315 pub min_parallel_rows: u64,
317}
318
319impl Default for ParallelQueryConfig {
320 fn default() -> Self {
321 Self {
322 enabled: true,
323 max_threads: num_cpus::get(),
324 min_parallel_rows: 10_000,
325 }
326 }
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct PerformanceConfig {
332 pub enable_metrics: bool,
334
335 pub metrics_interval: Duration,
337
338 pub enable_profiling: bool,
340
341 pub background_tasks: BackgroundTaskConfig,
343}
344
345impl Default for PerformanceConfig {
346 fn default() -> Self {
347 Self {
348 enable_metrics: true,
349 metrics_interval: Duration::from_secs(60),
350 enable_profiling: false,
351 background_tasks: BackgroundTaskConfig::default(),
352 }
353 }
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct BackgroundTaskConfig {
359 pub enable_stats: bool,
361
362 pub stats_interval: Duration,
364
365 pub enable_cleanup: bool,
367
368 pub cleanup_interval: Duration,
370}
371
372impl Default for BackgroundTaskConfig {
373 fn default() -> Self {
374 Self {
375 enable_stats: true,
376 stats_interval: Duration::from_secs(300), enable_cleanup: true,
378 cleanup_interval: Duration::from_secs(3600), }
380 }
381}
382
383#[cfg(target_arch = "wasm32")]
385#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct WasmConfig {
387 pub use_indexeddb: bool,
389
390 pub max_memory: u64,
392
393 pub enable_simd: bool,
395
396 pub enable_workers: bool,
398
399 pub max_workers: usize,
401}
402
403#[cfg(target_arch = "wasm32")]
404impl Default for WasmConfig {
405 fn default() -> Self {
406 Self {
407 use_indexeddb: true,
408 max_memory: 256 * 1024 * 1024, enable_simd: true,
410 enable_workers: true,
411 max_workers: 4,
412 }
413 }
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418pub enum CompressionAlgorithm {
419 None,
421 Lz4,
423 Snappy,
425 Deflate,
427 Zstd,
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct CompressionConfig {
434 pub enabled: bool,
436
437 pub algorithm: CompressionAlgorithm,
439
440 pub level: i32,
442
443 pub min_block_size: u32,
445}
446
447impl Default for CompressionConfig {
448 fn default() -> Self {
449 Self {
450 enabled: true,
451 algorithm: CompressionAlgorithm::Lz4,
452 level: 1, min_block_size: 1024, }
455 }
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize)]
460pub enum CompactionStrategy {
461 Size,
463 Leveled,
465 Universal,
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize)]
471pub enum SyncMode {
472 None,
474 Normal,
476 Full,
478}
479
480impl Config {
481 pub fn memory_optimized() -> Self {
483 let mut config = Self::default();
484
485 config.storage.memtable_size_threshold = 4 * 1024 * 1024; config.storage.max_sstable_size = 16 * 1024 * 1024; config.memory.max_memory = 256 * 1024 * 1024; config.memory.block_cache.max_size = 64 * 1024 * 1024; config.memory.row_cache.max_size = 32 * 1024 * 1024; config.memory.query_cache.max_size = 16 * 1024 * 1024; config.storage.compression.algorithm = CompressionAlgorithm::Zstd;
495 config.storage.compression.enabled = true;
496
497 config
498 }
499
500 pub fn performance_optimized() -> Self {
502 let mut config = Self::default();
503
504 config.storage.memtable_size_threshold = 64 * 1024 * 1024; config.storage.max_sstable_size = 256 * 1024 * 1024; config.memory.max_memory = 4 * 1024 * 1024 * 1024; config.storage.compression.algorithm = CompressionAlgorithm::Lz4;
511 config.storage.compression.enabled = true;
512
513 config.memory.block_cache.max_size = 1024 * 1024 * 1024; config.memory.row_cache.max_size = 512 * 1024 * 1024; config.memory.query_cache.max_size = 256 * 1024 * 1024; config.storage.io_threads = num_cpus::get();
520
521 config
522 }
523
524 #[cfg(target_arch = "wasm32")]
526 pub fn wasm_optimized() -> Self {
527 let mut config = Self::memory_optimized();
528
529 config.wasm.max_memory = 128 * 1024 * 1024; config.wasm.enable_simd = true;
532 config.wasm.enable_workers = false; config.memory.max_memory = 128 * 1024 * 1024; config.storage.memtable_size_threshold = 2 * 1024 * 1024; config.storage.max_sstable_size = 8 * 1024 * 1024; config.storage.compaction.auto_compaction = false;
541 config.performance.background_tasks.enable_stats = false;
542 config.performance.background_tasks.enable_cleanup = false;
543
544 config
545 }
546
547 #[cfg(test)]
549 pub fn test_config() -> Self {
550 let mut config = Config::default();
551
552 config.storage.compaction.auto_compaction = false;
554 config.performance.background_tasks.enable_stats = false;
555 config.performance.background_tasks.enable_cleanup = false;
556
557 config.query.max_execution_time = std::time::Duration::from_secs(1);
559 config.storage.compaction.background_interval = std::time::Duration::from_secs(10);
560
561 config.memory.max_memory = 64 * 1024 * 1024; config.storage.memtable_size_threshold = 1024 * 1024; config.storage.max_sstable_size = 4 * 1024 * 1024; config
567 }
568
569 pub fn validate(&self) -> crate::Result<()> {
571 if self.memory.max_memory == 0 {
573 return Err(crate::Error::configuration(
574 "max_memory must be greater than 0",
575 ));
576 }
577
578 let total_cache = self.memory.block_cache.max_size
580 + self.memory.row_cache.max_size
581 + self.memory.query_cache.max_size;
582
583 if total_cache > self.memory.max_memory {
584 return Err(crate::Error::configuration(
585 "total cache size exceeds max_memory",
586 ));
587 }
588
589 if self.storage.block_size == 0 {
591 return Err(crate::Error::configuration(
592 "block_size must be greater than 0",
593 ));
594 }
595
596 if self.storage.memtable_size_threshold == 0 {
597 return Err(crate::Error::configuration(
598 "memtable_size_threshold must be greater than 0",
599 ));
600 }
601
602 if self.storage.enable_bloom_filters
604 && (self.storage.bloom_filter_fp_rate <= 0.0
605 || self.storage.bloom_filter_fp_rate >= 1.0)
606 {
607 return Err(crate::Error::configuration(
608 "bloom_filter_fp_rate must be between 0 and 1",
609 ));
610 }
611
612 Ok(())
613 }
614}
615
616#[cfg(test)]
617mod tests {
618 use super::*;
619
620 #[test]
621 fn test_default_config() {
622 let config = Config::default();
623 assert!(config.storage.compression.enabled);
624 assert!(config.storage.enable_bloom_filters);
625 assert!(config.memory.block_cache.enabled);
626 }
627
628 #[test]
629 fn test_memory_optimized_config() {
630 let config = Config::memory_optimized();
631 assert!(
632 config.storage.memtable_size_threshold
633 < Config::default().storage.memtable_size_threshold
634 );
635 assert!(config.memory.max_memory < Config::default().memory.max_memory);
636 }
637
638 #[test]
639 fn test_performance_optimized_config() {
640 let config = Config::performance_optimized();
641 assert!(
642 config.storage.memtable_size_threshold
643 > Config::default().storage.memtable_size_threshold
644 );
645 assert!(config.memory.max_memory > Config::default().memory.max_memory);
646 }
647
648 #[test]
649 fn test_config_validation() {
650 let mut config = Config::default();
651 assert!(config.validate().is_ok());
652
653 config.memory.max_memory = 0;
655 assert!(config.validate().is_err());
656
657 config = Config::default();
659 config.memory.block_cache.max_size = config.memory.max_memory + 1;
660 assert!(config.validate().is_err());
661 }
662
663 #[test]
664 fn test_storage_validation_errors() {
665 let mut config = Config::default();
666
667 config.storage.block_size = 0;
669 let result = config.validate();
670 assert!(result.is_err());
671 assert!(result
672 .unwrap_err()
673 .to_string()
674 .contains("block_size must be greater than 0"));
675
676 config = Config::default();
678 config.storage.memtable_size_threshold = 0;
679 let result = config.validate();
680 assert!(result.is_err());
681 assert!(result
682 .unwrap_err()
683 .to_string()
684 .contains("memtable_size_threshold must be greater than 0"));
685
686 config = Config::default();
688 config.storage.enable_bloom_filters = true;
689 config.storage.bloom_filter_fp_rate = 0.0; let result = config.validate();
691 assert!(result.is_err());
692 assert!(result
693 .unwrap_err()
694 .to_string()
695 .contains("bloom_filter_fp_rate must be between 0 and 1"));
696
697 config.storage.bloom_filter_fp_rate = 1.0; let result = config.validate();
700 assert!(result.is_err());
701
702 config.storage.bloom_filter_fp_rate = 1.5; let result = config.validate();
705 assert!(result.is_err());
706
707 config.storage.bloom_filter_fp_rate = -0.1; let result = config.validate();
710 assert!(result.is_err());
711 }
712
713 #[test]
714 fn test_valid_bloom_filter_config() {
715 let mut config = Config::default();
716 config.storage.enable_bloom_filters = true;
717 config.storage.bloom_filter_fp_rate = 0.01; assert!(config.validate().is_ok());
719
720 config.storage.bloom_filter_fp_rate = 0.5; assert!(config.validate().is_ok());
722
723 config.storage.bloom_filter_fp_rate = 0.99; assert!(config.validate().is_ok());
725 }
726
727 #[test]
728 fn test_storage_config_deserializes_without_mmap_fields() {
729 let mut value = serde_json::to_value(StorageConfig::default()).unwrap();
733 let obj = value.as_object_mut().unwrap();
734 obj.remove("use_mmap");
735 obj.remove("mmap_min_size_bytes");
736 assert!(!obj.contains_key("use_mmap"));
737
738 let restored: StorageConfig =
739 serde_json::from_value(value).expect("old payload must still deserialize");
740 assert!(!restored.use_mmap, "missing use_mmap must default to false");
741 assert_eq!(
742 restored.mmap_min_size_bytes, 4096,
743 "missing mmap_min_size_bytes must default to one page"
744 );
745 }
746
747 #[test]
748 fn test_full_config_deserializes_without_mmap_fields() {
749 let mut value = serde_json::to_value(Config::default()).unwrap();
752 let storage = value
753 .get_mut("storage")
754 .and_then(|s| s.as_object_mut())
755 .unwrap();
756 storage.remove("use_mmap");
757 storage.remove("mmap_min_size_bytes");
758
759 let restored: Config =
760 serde_json::from_value(value).expect("old Config payload must still deserialize");
761 assert!(!restored.storage.use_mmap);
762 assert_eq!(restored.storage.mmap_min_size_bytes, 4096);
763 restored.validate().expect("restored config must validate");
764 }
765
766 #[test]
767 fn test_mmap_fields_roundtrip_when_present() {
768 let mut config = StorageConfig::default();
770 config.use_mmap = true;
771 config.mmap_min_size_bytes = 8192;
772 let json = serde_json::to_string(&config).unwrap();
773 let restored: StorageConfig = serde_json::from_str(&json).unwrap();
774 assert!(restored.use_mmap);
775 assert_eq!(restored.mmap_min_size_bytes, 8192);
776 }
777
778 #[test]
779 fn test_bloom_filter_disabled() {
780 let mut config = Config::default();
781 config.storage.enable_bloom_filters = false;
782 config.storage.bloom_filter_fp_rate = 0.0; assert!(config.validate().is_ok());
784
785 config.storage.bloom_filter_fp_rate = 1.0; assert!(config.validate().is_ok());
787
788 config.storage.bloom_filter_fp_rate = -1.0; assert!(config.validate().is_ok());
790 }
791}