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 #[serde(default)]
125 pub disk_access_mode: DiskAccessMode,
126
127 #[serde(default = "default_direct_io_memory_fraction")]
133 pub direct_io_memory_fraction: f64,
134
135 #[serde(default)]
144 pub prefetch: PrefetchMode,
145
146 #[serde(default = "default_direct_io_prefetch_bytes")]
151 pub direct_io_prefetch_bytes: usize,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
159#[serde(rename_all = "lowercase")]
160pub enum DiskAccessMode {
161 #[default]
163 Auto,
164 Buffered,
166 Mmap,
172 Direct,
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
183#[serde(rename_all = "lowercase")]
184pub enum PrefetchMode {
185 Off,
187 Sequential,
189 WillNeed,
191 #[default]
194 Auto,
195}
196
197fn default_use_mmap() -> bool {
199 false
200}
201
202fn default_mmap_min_size_bytes() -> usize {
204 4096
205}
206
207fn default_direct_io_memory_fraction() -> f64 {
209 0.5
210}
211
212fn default_direct_io_prefetch_bytes() -> usize {
214 1024 * 1024
215}
216
217impl Default for StorageConfig {
218 fn default() -> Self {
219 Self {
220 max_sstable_size: 64 * 1024 * 1024, memtable_size_threshold: 16 * 1024 * 1024, compaction: CompactionConfig::default(),
223 block_size: 64 * 1024, compression: CompressionConfig::default(),
225 enable_bloom_filters: true,
226 bloom_filter_fp_rate: 0.01,
227 io_threads: num_cpus::get().min(4),
228 sync_mode: SyncMode::Normal,
229 use_mmap: default_use_mmap(),
232 mmap_min_size_bytes: default_mmap_min_size_bytes(),
233 disk_access_mode: DiskAccessMode::default(),
234 direct_io_memory_fraction: default_direct_io_memory_fraction(),
235 prefetch: PrefetchMode::default(),
236 direct_io_prefetch_bytes: default_direct_io_prefetch_bytes(),
237 }
238 }
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct CompactionConfig {
244 pub strategy: CompactionStrategy,
246
247 pub max_sstables: usize,
249
250 pub size_ratio: f64,
252
253 pub max_threads: usize,
255
256 pub background_interval: Duration,
258
259 pub auto_compaction: bool,
261}
262
263impl Default for CompactionConfig {
264 fn default() -> Self {
265 Self {
266 strategy: CompactionStrategy::Leveled,
267 max_sstables: 10,
268 size_ratio: 2.0,
269 max_threads: 2,
270 background_interval: Duration::from_secs(300), auto_compaction: true,
272 }
273 }
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct MemoryConfig {
279 pub max_memory: u64,
281
282 pub block_cache: CacheConfig,
284
285 pub row_cache: CacheConfig,
287
288 pub query_cache: CacheConfig,
290
291 pub allocator: AllocatorConfig,
293}
294
295impl Default for MemoryConfig {
296 fn default() -> Self {
297 let max_memory = 1024 * 1024 * 1024; Self {
300 max_memory,
301 block_cache: CacheConfig {
302 enabled: true,
303 max_size: max_memory / 4, policy: CachePolicy::Lru,
305 },
306 row_cache: CacheConfig {
307 enabled: true,
308 max_size: max_memory / 8, policy: CachePolicy::Lru,
310 },
311 query_cache: CacheConfig {
312 enabled: true,
313 max_size: max_memory / 16, policy: CachePolicy::Lru,
315 },
316 allocator: AllocatorConfig::default(),
317 }
318 }
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct CacheConfig {
324 pub enabled: bool,
326
327 pub max_size: u64,
329
330 pub policy: CachePolicy,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
336pub enum CachePolicy {
337 Lru,
339 Lfu,
341 Arc,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct AllocatorConfig {
348 pub use_custom: bool,
350
351 pub small_pool_size: u64,
353
354 pub large_pool_size: u64,
356}
357
358impl Default for AllocatorConfig {
359 fn default() -> Self {
360 Self {
361 use_custom: false, small_pool_size: 64 * 1024 * 1024, large_pool_size: 256 * 1024 * 1024, }
365 }
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct QueryConfig {
371 pub max_execution_time: Duration,
373
374 pub max_result_rows: u64,
376
377 pub plan_cache_size: usize,
379
380 pub enable_optimization: bool,
382
383 pub parallel: ParallelQueryConfig,
385
386 pub query_cache_size: Option<usize>,
388
389 pub query_parallelism: Option<usize>,
391
392 pub analyze_iterations: Option<usize>,
394}
395
396impl Default for QueryConfig {
397 fn default() -> Self {
398 Self {
399 max_execution_time: Duration::from_secs(300), max_result_rows: 1_000_000,
401 plan_cache_size: 1000,
402 enable_optimization: true,
403 parallel: ParallelQueryConfig::default(),
404 query_cache_size: Some(100),
405 query_parallelism: Some(num_cpus::get()),
406 analyze_iterations: Some(5),
407 }
408 }
409}
410
411#[derive(Debug, Clone, Serialize, Deserialize)]
413pub struct ParallelQueryConfig {
414 pub enabled: bool,
416
417 pub max_threads: usize,
419
420 pub min_parallel_rows: u64,
422}
423
424impl Default for ParallelQueryConfig {
425 fn default() -> Self {
426 Self {
427 enabled: true,
428 max_threads: num_cpus::get(),
429 min_parallel_rows: 10_000,
430 }
431 }
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
436pub struct PerformanceConfig {
437 pub enable_metrics: bool,
439
440 pub metrics_interval: Duration,
442
443 pub enable_profiling: bool,
445
446 pub background_tasks: BackgroundTaskConfig,
448}
449
450impl Default for PerformanceConfig {
451 fn default() -> Self {
452 Self {
453 enable_metrics: true,
454 metrics_interval: Duration::from_secs(60),
455 enable_profiling: false,
456 background_tasks: BackgroundTaskConfig::default(),
457 }
458 }
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
463pub struct BackgroundTaskConfig {
464 pub enable_stats: bool,
466
467 pub stats_interval: Duration,
469
470 pub enable_cleanup: bool,
472
473 pub cleanup_interval: Duration,
475}
476
477impl Default for BackgroundTaskConfig {
478 fn default() -> Self {
479 Self {
480 enable_stats: true,
481 stats_interval: Duration::from_secs(300), enable_cleanup: true,
483 cleanup_interval: Duration::from_secs(3600), }
485 }
486}
487
488#[cfg(target_arch = "wasm32")]
490#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct WasmConfig {
492 pub use_indexeddb: bool,
494
495 pub max_memory: u64,
497
498 pub enable_simd: bool,
500
501 pub enable_workers: bool,
503
504 pub max_workers: usize,
506}
507
508#[cfg(target_arch = "wasm32")]
509impl Default for WasmConfig {
510 fn default() -> Self {
511 Self {
512 use_indexeddb: true,
513 max_memory: 256 * 1024 * 1024, enable_simd: true,
515 enable_workers: true,
516 max_workers: 4,
517 }
518 }
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize)]
523pub enum CompressionAlgorithm {
524 None,
526 Lz4,
528 Snappy,
530 Deflate,
532 Zstd,
534}
535
536#[derive(Debug, Clone, Serialize, Deserialize)]
538pub struct CompressionConfig {
539 pub enabled: bool,
541
542 pub algorithm: CompressionAlgorithm,
544
545 pub level: i32,
547
548 pub min_block_size: u32,
550}
551
552impl Default for CompressionConfig {
553 fn default() -> Self {
554 Self {
555 enabled: true,
556 algorithm: CompressionAlgorithm::Lz4,
557 level: 1, min_block_size: 1024, }
560 }
561}
562
563#[derive(Debug, Clone, Serialize, Deserialize)]
565pub enum CompactionStrategy {
566 Size,
568 Leveled,
570 Universal,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize)]
576pub enum SyncMode {
577 None,
579 Normal,
581 Full,
583}
584
585impl Config {
586 pub fn memory_optimized() -> Self {
588 let mut config = Self::default();
589
590 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;
600 config.storage.compression.enabled = true;
601
602 config
603 }
604
605 pub fn performance_optimized() -> Self {
607 let mut config = Self::default();
608
609 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;
616 config.storage.compression.enabled = true;
617
618 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();
625
626 config
627 }
628
629 #[cfg(target_arch = "wasm32")]
631 pub fn wasm_optimized() -> Self {
632 let mut config = Self::memory_optimized();
633
634 config.wasm.max_memory = 128 * 1024 * 1024; config.wasm.enable_simd = true;
637 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;
646 config.performance.background_tasks.enable_stats = false;
647 config.performance.background_tasks.enable_cleanup = false;
648
649 config
650 }
651
652 #[cfg(test)]
654 pub fn test_config() -> Self {
655 let mut config = Config::default();
656
657 config.storage.compaction.auto_compaction = false;
659 config.performance.background_tasks.enable_stats = false;
660 config.performance.background_tasks.enable_cleanup = false;
661
662 config.query.max_execution_time = std::time::Duration::from_secs(1);
664 config.storage.compaction.background_interval = std::time::Duration::from_secs(10);
665
666 config.memory.max_memory = 64 * 1024 * 1024; config.storage.memtable_size_threshold = 1024 * 1024; config.storage.max_sstable_size = 4 * 1024 * 1024; config
672 }
673
674 pub fn validate(&self) -> crate::Result<()> {
676 if self.memory.max_memory == 0 {
678 return Err(crate::Error::configuration(
679 "max_memory must be greater than 0",
680 ));
681 }
682
683 let total_cache = self.memory.block_cache.max_size
685 + self.memory.row_cache.max_size
686 + self.memory.query_cache.max_size;
687
688 if total_cache > self.memory.max_memory {
689 return Err(crate::Error::configuration(
690 "total cache size exceeds max_memory",
691 ));
692 }
693
694 if self.storage.block_size == 0 {
696 return Err(crate::Error::configuration(
697 "block_size must be greater than 0",
698 ));
699 }
700
701 if self.storage.memtable_size_threshold == 0 {
702 return Err(crate::Error::configuration(
703 "memtable_size_threshold must be greater than 0",
704 ));
705 }
706
707 if self.storage.enable_bloom_filters
709 && (self.storage.bloom_filter_fp_rate <= 0.0
710 || self.storage.bloom_filter_fp_rate >= 1.0)
711 {
712 return Err(crate::Error::configuration(
713 "bloom_filter_fp_rate must be between 0 and 1",
714 ));
715 }
716
717 Ok(())
718 }
719}
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724
725 #[test]
726 fn test_default_config() {
727 let config = Config::default();
728 assert!(config.storage.compression.enabled);
729 assert!(config.storage.enable_bloom_filters);
730 assert!(config.memory.block_cache.enabled);
731 }
732
733 #[test]
734 fn test_memory_optimized_config() {
735 let config = Config::memory_optimized();
736 assert!(
737 config.storage.memtable_size_threshold
738 < Config::default().storage.memtable_size_threshold
739 );
740 assert!(config.memory.max_memory < Config::default().memory.max_memory);
741 }
742
743 #[test]
744 fn test_performance_optimized_config() {
745 let config = Config::performance_optimized();
746 assert!(
747 config.storage.memtable_size_threshold
748 > Config::default().storage.memtable_size_threshold
749 );
750 assert!(config.memory.max_memory > Config::default().memory.max_memory);
751 }
752
753 #[test]
754 fn test_config_validation() {
755 let mut config = Config::default();
756 assert!(config.validate().is_ok());
757
758 config.memory.max_memory = 0;
760 assert!(config.validate().is_err());
761
762 config = Config::default();
764 config.memory.block_cache.max_size = config.memory.max_memory + 1;
765 assert!(config.validate().is_err());
766 }
767
768 #[test]
769 fn test_storage_validation_errors() {
770 let mut config = Config::default();
771
772 config.storage.block_size = 0;
774 let result = config.validate();
775 assert!(result.is_err());
776 assert!(result
777 .unwrap_err()
778 .to_string()
779 .contains("block_size must be greater than 0"));
780
781 config = Config::default();
783 config.storage.memtable_size_threshold = 0;
784 let result = config.validate();
785 assert!(result.is_err());
786 assert!(result
787 .unwrap_err()
788 .to_string()
789 .contains("memtable_size_threshold must be greater than 0"));
790
791 config = Config::default();
793 config.storage.enable_bloom_filters = true;
794 config.storage.bloom_filter_fp_rate = 0.0; let result = config.validate();
796 assert!(result.is_err());
797 assert!(result
798 .unwrap_err()
799 .to_string()
800 .contains("bloom_filter_fp_rate must be between 0 and 1"));
801
802 config.storage.bloom_filter_fp_rate = 1.0; let result = config.validate();
805 assert!(result.is_err());
806
807 config.storage.bloom_filter_fp_rate = 1.5; let result = config.validate();
810 assert!(result.is_err());
811
812 config.storage.bloom_filter_fp_rate = -0.1; let result = config.validate();
815 assert!(result.is_err());
816 }
817
818 #[test]
819 fn test_valid_bloom_filter_config() {
820 let mut config = Config::default();
821 config.storage.enable_bloom_filters = true;
822 config.storage.bloom_filter_fp_rate = 0.01; assert!(config.validate().is_ok());
824
825 config.storage.bloom_filter_fp_rate = 0.5; assert!(config.validate().is_ok());
827
828 config.storage.bloom_filter_fp_rate = 0.99; assert!(config.validate().is_ok());
830 }
831
832 #[test]
833 fn test_storage_config_deserializes_without_mmap_fields() {
834 let mut value = serde_json::to_value(StorageConfig::default()).unwrap();
838 let obj = value.as_object_mut().unwrap();
839 obj.remove("use_mmap");
840 obj.remove("mmap_min_size_bytes");
841 assert!(!obj.contains_key("use_mmap"));
842
843 let restored: StorageConfig =
844 serde_json::from_value(value).expect("old payload must still deserialize");
845 assert!(!restored.use_mmap, "missing use_mmap must default to false");
846 assert_eq!(
847 restored.mmap_min_size_bytes, 4096,
848 "missing mmap_min_size_bytes must default to one page"
849 );
850 }
851
852 #[test]
853 fn test_full_config_deserializes_without_mmap_fields() {
854 let mut value = serde_json::to_value(Config::default()).unwrap();
857 let storage = value
858 .get_mut("storage")
859 .and_then(|s| s.as_object_mut())
860 .unwrap();
861 storage.remove("use_mmap");
862 storage.remove("mmap_min_size_bytes");
863
864 let restored: Config =
865 serde_json::from_value(value).expect("old Config payload must still deserialize");
866 assert!(!restored.storage.use_mmap);
867 assert_eq!(restored.storage.mmap_min_size_bytes, 4096);
868 restored.validate().expect("restored config must validate");
869 }
870
871 #[test]
872 fn test_mmap_fields_roundtrip_when_present() {
873 let mut config = StorageConfig::default();
875 config.use_mmap = true;
876 config.mmap_min_size_bytes = 8192;
877 let json = serde_json::to_string(&config).unwrap();
878 let restored: StorageConfig = serde_json::from_str(&json).unwrap();
879 assert!(restored.use_mmap);
880 assert_eq!(restored.mmap_min_size_bytes, 8192);
881 }
882
883 #[test]
884 fn test_disk_access_defaults() {
885 let config = StorageConfig::default();
888 assert_eq!(config.disk_access_mode, DiskAccessMode::Auto);
889 assert_eq!(config.prefetch, PrefetchMode::Auto);
890 assert_eq!(config.direct_io_memory_fraction, 0.5);
891 assert_eq!(config.direct_io_prefetch_bytes, 1024 * 1024);
892 }
893
894 #[test]
895 fn test_storage_config_deserializes_without_disk_access_fields() {
896 let mut value = serde_json::to_value(StorageConfig::default()).unwrap();
899 let obj = value.as_object_mut().unwrap();
900 for key in [
901 "disk_access_mode",
902 "direct_io_memory_fraction",
903 "prefetch",
904 "direct_io_prefetch_bytes",
905 ] {
906 obj.remove(key);
907 }
908
909 let restored: StorageConfig =
910 serde_json::from_value(value).expect("old payload must still deserialize");
911 assert_eq!(restored.disk_access_mode, DiskAccessMode::Auto);
912 assert_eq!(restored.prefetch, PrefetchMode::Auto);
913 assert_eq!(restored.direct_io_memory_fraction, 0.5);
914 assert_eq!(restored.direct_io_prefetch_bytes, 1024 * 1024);
915 }
916
917 #[test]
918 fn test_disk_access_fields_roundtrip() {
919 let mut config = StorageConfig::default();
921 config.disk_access_mode = DiskAccessMode::Direct;
922 config.prefetch = PrefetchMode::WillNeed;
923 config.direct_io_memory_fraction = 0.25;
924 config.direct_io_prefetch_bytes = 2 * 1024 * 1024;
925 let json = serde_json::to_string(&config).unwrap();
926 assert!(json.contains("\"direct\""), "enum must serialize lowercase");
927 assert!(json.contains("\"willneed\""));
928 let restored: StorageConfig = serde_json::from_str(&json).unwrap();
929 assert_eq!(restored.disk_access_mode, DiskAccessMode::Direct);
930 assert_eq!(restored.prefetch, PrefetchMode::WillNeed);
931 assert_eq!(restored.direct_io_memory_fraction, 0.25);
932 assert_eq!(restored.direct_io_prefetch_bytes, 2 * 1024 * 1024);
933 }
934
935 #[test]
936 fn test_bloom_filter_disabled() {
937 let mut config = Config::default();
938 config.storage.enable_bloom_filters = false;
939 config.storage.bloom_filter_fp_rate = 0.0; assert!(config.validate().is_ok());
941
942 config.storage.bloom_filter_fp_rate = 1.0; assert!(config.validate().is_ok());
944
945 config.storage.bloom_filter_fp_rate = -1.0; assert!(config.validate().is_ok());
947 }
948}