safer_ring/
config.rs

1//! Configuration options for different safer-ring use cases.
2//!
3//! This module provides comprehensive configuration options that allow users
4//! to optimize safer-ring for their specific use cases, from low-latency
5//! applications to high-throughput batch processing.
6
7use crate::advanced::{AdvancedConfig, FeatureDetector};
8use crate::error::{Result, SaferRingError};
9use crate::logging::LogLevel;
10use std::time::Duration;
11
12/// Comprehensive configuration for safer-ring operations.
13///
14/// This struct provides all configuration options needed to optimize
15/// safer-ring for different use cases and environments.
16#[derive(Debug, Clone)]
17pub struct SaferRingConfig {
18    /// Ring configuration
19    pub ring: RingConfig,
20    /// Buffer management configuration
21    pub buffer: BufferConfig,
22    /// Performance optimization configuration
23    pub performance: PerformanceConfig,
24    /// Logging and debugging configuration
25    pub logging: LoggingConfig,
26    /// Advanced io_uring features configuration
27    pub advanced: AdvancedConfig,
28    /// Error handling configuration
29    pub error_handling: ErrorHandlingConfig,
30}
31
32/// Ring-specific configuration options.
33#[derive(Debug, Clone)]
34pub struct RingConfig {
35    /// Number of submission queue entries
36    pub sq_entries: u32,
37    /// Number of completion queue entries (0 = same as sq_entries)
38    pub cq_entries: u32,
39    /// Enable submission queue polling
40    pub sq_poll: bool,
41    /// CPU for SQ polling thread
42    pub sq_thread_cpu: Option<u32>,
43    /// SQ polling idle timeout in milliseconds
44    pub sq_thread_idle: Option<u32>,
45    /// Enable completion queue overflow handling
46    pub cq_overflow: bool,
47    /// Enable kernel submission queue thread
48    pub kernel_sq_thread: bool,
49}
50
51/// Buffer management configuration.
52#[derive(Debug, Clone)]
53pub struct BufferConfig {
54    /// Default buffer size for operations
55    pub default_size: usize,
56    /// Buffer alignment (0 = system default)
57    pub alignment: usize,
58    /// Enable buffer pooling
59    pub enable_pooling: bool,
60    /// Buffer pool size per thread
61    pub pool_size: usize,
62    /// Maximum buffer size for pooling
63    pub max_pooled_size: usize,
64    /// Enable NUMA-aware allocation
65    pub numa_aware: bool,
66    /// Preferred NUMA node (-1 = no preference)
67    pub numa_node: i32,
68    /// Enable buffer pre-registration
69    pub pre_register: bool,
70}
71
72/// Performance optimization configuration.
73#[derive(Debug, Clone)]
74pub struct PerformanceConfig {
75    /// Enable batch submission optimization
76    pub batch_submission: bool,
77    /// Maximum batch size
78    pub max_batch_size: usize,
79    /// Batch timeout for partial batches
80    pub batch_timeout: Duration,
81    /// Enable zero-copy optimizations
82    pub zero_copy: bool,
83    /// Enable vectored I/O optimization
84    pub vectored_io: bool,
85    /// Enable operation coalescing
86    pub operation_coalescing: bool,
87    /// Coalescing timeout
88    pub coalescing_timeout: Duration,
89    /// Enable adaptive polling
90    pub adaptive_polling: bool,
91    /// Polling timeout
92    pub poll_timeout: Duration,
93}
94
95/// Logging and debugging configuration.
96#[derive(Debug, Clone)]
97pub struct LoggingConfig {
98    /// Enable logging
99    pub enabled: bool,
100    /// Minimum log level
101    pub level: LogLevel,
102    /// Enable performance metrics collection
103    pub metrics: bool,
104    /// Enable operation tracing
105    pub tracing: bool,
106    /// Log file path (None = console only)
107    pub log_file: Option<std::path::PathBuf>,
108    /// Use JSON format for logs
109    pub json_format: bool,
110    /// Enable debug assertions
111    pub debug_assertions: bool,
112}
113
114/// Error handling configuration.
115#[derive(Debug, Clone)]
116pub struct ErrorHandlingConfig {
117    /// Retry failed operations automatically
118    pub auto_retry: bool,
119    /// Maximum number of retries
120    pub max_retries: u32,
121    /// Retry delay
122    pub retry_delay: Duration,
123    /// Enable graceful degradation
124    pub graceful_degradation: bool,
125    /// Fallback to synchronous I/O on errors
126    pub sync_fallback: bool,
127    /// Panic on unrecoverable errors
128    pub panic_on_fatal: bool,
129}
130
131#[allow(clippy::derivable_impls)] // Custom defaults are intentional
132impl Default for SaferRingConfig {
133    fn default() -> Self {
134        Self {
135            ring: RingConfig::default(),
136            buffer: BufferConfig::default(),
137            performance: PerformanceConfig::default(),
138            logging: LoggingConfig::default(),
139            advanced: AdvancedConfig::default(),
140            error_handling: ErrorHandlingConfig::default(),
141        }
142    }
143}
144
145impl Default for RingConfig {
146    fn default() -> Self {
147        Self {
148            sq_entries: 128,
149            cq_entries: 0, // Same as sq_entries
150            sq_poll: false,
151            sq_thread_cpu: None,
152            sq_thread_idle: None,
153            cq_overflow: true,
154            kernel_sq_thread: false,
155        }
156    }
157}
158
159impl Default for BufferConfig {
160    fn default() -> Self {
161        Self {
162            default_size: 4096,
163            alignment: 0, // System default
164            enable_pooling: true,
165            pool_size: 64,
166            max_pooled_size: 1024 * 1024, // 1MB
167            numa_aware: false,
168            numa_node: -1,
169            pre_register: false,
170        }
171    }
172}
173
174impl Default for PerformanceConfig {
175    fn default() -> Self {
176        Self {
177            batch_submission: true,
178            max_batch_size: 32,
179            batch_timeout: Duration::from_millis(1),
180            zero_copy: true,
181            vectored_io: true,
182            operation_coalescing: false,
183            coalescing_timeout: Duration::from_micros(100),
184            adaptive_polling: false,
185            poll_timeout: Duration::from_micros(10),
186        }
187    }
188}
189
190impl Default for LoggingConfig {
191    fn default() -> Self {
192        Self {
193            enabled: false,
194            level: LogLevel::Info,
195            metrics: false,
196            tracing: false,
197            log_file: None,
198            json_format: false,
199            debug_assertions: cfg!(debug_assertions),
200        }
201    }
202}
203
204impl Default for ErrorHandlingConfig {
205    fn default() -> Self {
206        Self {
207            auto_retry: false,
208            max_retries: 3,
209            retry_delay: Duration::from_millis(10),
210            graceful_degradation: true,
211            sync_fallback: false,
212            panic_on_fatal: true,
213        }
214    }
215}
216
217impl SaferRingConfig {
218    /// Create a configuration optimized for low latency applications.
219    ///
220    /// This configuration prioritizes minimal latency over throughput,
221    /// suitable for real-time applications and interactive workloads.
222    pub fn low_latency() -> Self {
223        Self {
224            ring: RingConfig {
225                sq_entries: 32,
226                cq_entries: 0,
227                sq_poll: true,
228                sq_thread_cpu: Some(0),
229                sq_thread_idle: Some(1),
230                cq_overflow: true,
231                kernel_sq_thread: true,
232            },
233            buffer: BufferConfig {
234                default_size: 1024,
235                alignment: 64, // Cache line aligned
236                enable_pooling: true,
237                pool_size: 32,
238                max_pooled_size: 64 * 1024, // 64KB
239                numa_aware: true,
240                numa_node: -1,
241                pre_register: true,
242            },
243            performance: PerformanceConfig {
244                batch_submission: false, // Immediate submission
245                max_batch_size: 1,
246                batch_timeout: Duration::from_micros(1),
247                zero_copy: true,
248                vectored_io: false,
249                operation_coalescing: false,
250                coalescing_timeout: Duration::ZERO,
251                adaptive_polling: true,
252                poll_timeout: Duration::from_nanos(100),
253            },
254            logging: LoggingConfig {
255                enabled: false, // Minimal overhead
256                level: LogLevel::Error,
257                metrics: false,
258                tracing: false,
259                log_file: None,
260                json_format: false,
261                debug_assertions: false,
262            },
263            advanced: AdvancedConfig {
264                buffer_selection: true,
265                multi_shot: false,
266                provided_buffers: true,
267                fast_poll: true,
268                sq_poll: true,
269                sq_thread_cpu: Some(0),
270                coop_taskrun: true,
271                defer_taskrun: true,
272                // Default all new features to false for high throughput config
273                ..Default::default()
274            },
275            error_handling: ErrorHandlingConfig {
276                auto_retry: false,
277                max_retries: 0,
278                retry_delay: Duration::ZERO,
279                graceful_degradation: false,
280                sync_fallback: false,
281                panic_on_fatal: true,
282            },
283        }
284    }
285
286    /// Create a configuration optimized for high throughput applications.
287    ///
288    /// This configuration prioritizes maximum throughput over latency,
289    /// suitable for batch processing and data-intensive workloads.
290    pub fn high_throughput() -> Self {
291        Self {
292            ring: RingConfig {
293                sq_entries: 1024,
294                cq_entries: 2048,
295                sq_poll: false,
296                sq_thread_cpu: None,
297                sq_thread_idle: None,
298                cq_overflow: true,
299                kernel_sq_thread: false,
300            },
301            buffer: BufferConfig {
302                default_size: 64 * 1024, // 64KB
303                alignment: 4096,         // Page aligned
304                enable_pooling: true,
305                pool_size: 256,
306                max_pooled_size: 16 * 1024 * 1024, // 16MB
307                numa_aware: true,
308                numa_node: -1,
309                pre_register: true,
310            },
311            performance: PerformanceConfig {
312                batch_submission: true,
313                max_batch_size: 256,
314                batch_timeout: Duration::from_millis(10),
315                zero_copy: true,
316                vectored_io: true,
317                operation_coalescing: true,
318                coalescing_timeout: Duration::from_millis(1),
319                adaptive_polling: false,
320                poll_timeout: Duration::from_millis(1),
321            },
322            logging: LoggingConfig {
323                enabled: true,
324                level: LogLevel::Info,
325                metrics: true,
326                tracing: false,
327                log_file: None,
328                json_format: false,
329                debug_assertions: false,
330            },
331            advanced: AdvancedConfig {
332                buffer_selection: true,
333                multi_shot: true,
334                provided_buffers: true,
335                fast_poll: false,
336                sq_poll: false,
337                sq_thread_cpu: None,
338                coop_taskrun: true,
339                defer_taskrun: true,
340                // Default all new features to false for development config
341                ..Default::default()
342            },
343            error_handling: ErrorHandlingConfig {
344                auto_retry: true,
345                max_retries: 3,
346                retry_delay: Duration::from_millis(1),
347                graceful_degradation: true,
348                sync_fallback: true,
349                panic_on_fatal: false,
350            },
351        }
352    }
353
354    /// Create a configuration optimized for development and debugging.
355    ///
356    /// This configuration enables comprehensive logging and debugging features
357    /// at the cost of performance, suitable for development and testing.
358    pub fn development() -> Self {
359        Self {
360            ring: RingConfig {
361                sq_entries: 64,
362                cq_entries: 0,
363                sq_poll: false,
364                sq_thread_cpu: None,
365                sq_thread_idle: None,
366                cq_overflow: true,
367                kernel_sq_thread: false,
368            },
369            buffer: BufferConfig {
370                default_size: 4096,
371                alignment: 0,
372                enable_pooling: true,
373                pool_size: 32,
374                max_pooled_size: 1024 * 1024,
375                numa_aware: false,
376                numa_node: -1,
377                pre_register: false,
378            },
379            performance: PerformanceConfig {
380                batch_submission: true,
381                max_batch_size: 16,
382                batch_timeout: Duration::from_millis(1),
383                zero_copy: true,
384                vectored_io: true,
385                operation_coalescing: false,
386                coalescing_timeout: Duration::from_millis(1),
387                adaptive_polling: false,
388                poll_timeout: Duration::from_millis(1),
389            },
390            logging: LoggingConfig {
391                enabled: true,
392                level: LogLevel::Debug,
393                metrics: true,
394                tracing: true,
395                log_file: Some("safer-ring.log".into()),
396                json_format: false,
397                debug_assertions: true,
398            },
399            advanced: AdvancedConfig::default(),
400            error_handling: ErrorHandlingConfig {
401                auto_retry: true,
402                max_retries: 1,
403                retry_delay: Duration::from_millis(10),
404                graceful_degradation: true,
405                sync_fallback: true,
406                panic_on_fatal: false,
407            },
408        }
409    }
410
411    /// Create a configuration optimized for production environments.
412    ///
413    /// This configuration balances performance and reliability for production
414    /// deployments with appropriate logging and error handling.
415    pub fn production() -> Self {
416        Self {
417            ring: RingConfig {
418                sq_entries: 256,
419                cq_entries: 0,
420                sq_poll: false,
421                sq_thread_cpu: None,
422                sq_thread_idle: None,
423                cq_overflow: true,
424                kernel_sq_thread: false,
425            },
426            buffer: BufferConfig {
427                default_size: 8192,
428                alignment: 0,
429                enable_pooling: true,
430                pool_size: 128,
431                max_pooled_size: 4 * 1024 * 1024, // 4MB
432                numa_aware: true,
433                numa_node: -1,
434                pre_register: false,
435            },
436            performance: PerformanceConfig {
437                batch_submission: true,
438                max_batch_size: 64,
439                batch_timeout: Duration::from_millis(5),
440                zero_copy: true,
441                vectored_io: true,
442                operation_coalescing: false,
443                coalescing_timeout: Duration::from_millis(1),
444                adaptive_polling: false,
445                poll_timeout: Duration::from_millis(1),
446            },
447            logging: LoggingConfig {
448                enabled: true,
449                level: LogLevel::Warn,
450                metrics: true,
451                tracing: false,
452                log_file: Some("/var/log/safer-ring.log".into()),
453                json_format: true,
454                debug_assertions: false,
455            },
456            advanced: AdvancedConfig::default(),
457            error_handling: ErrorHandlingConfig {
458                auto_retry: true,
459                max_retries: 3,
460                retry_delay: Duration::from_millis(100),
461                graceful_degradation: true,
462                sync_fallback: true,
463                panic_on_fatal: false,
464            },
465        }
466    }
467
468    /// Create an optimal configuration based on the current system.
469    ///
470    /// This method detects system capabilities and creates a configuration
471    /// that takes advantage of available features while maintaining compatibility.
472    pub fn auto_detect() -> Result<Self> {
473        let detector = FeatureDetector::new()?;
474        let cpu_count = num_cpus::get();
475
476        // Adjust buffer pool size based on available memory
477        let (pool_size, max_pooled_size) = if let Ok(memory_info) = Self::get_memory_info() {
478            let available_mb = memory_info.available / (1024 * 1024);
479            if available_mb > 1024 {
480                (256, 16 * 1024 * 1024)
481            } else if available_mb > 512 {
482                (128, 8 * 1024 * 1024)
483            } else {
484                (64, 1024 * 1024) // Default values
485            }
486        } else {
487            (64, 1024 * 1024) // Default values
488        };
489
490        Ok(Self {
491            advanced: detector.create_optimal_config(),
492            ring: RingConfig {
493                sq_entries: (cpu_count * 32).min(1024) as u32,
494                ..Default::default()
495            },
496            buffer: BufferConfig {
497                numa_aware: cpu_count > 16,
498                pool_size,
499                max_pooled_size,
500                ..Default::default()
501            },
502            ..Default::default()
503        })
504    }
505
506    /// Validate the configuration for consistency and compatibility.
507    pub fn validate(&self) -> Result<()> {
508        // Validate ring configuration
509        if self.ring.sq_entries == 0 {
510            return Err(SaferRingError::Io(std::io::Error::new(
511                std::io::ErrorKind::InvalidInput,
512                "SQ entries must be greater than 0",
513            )));
514        }
515
516        if self.ring.sq_entries > 4096 {
517            return Err(SaferRingError::Io(std::io::Error::new(
518                std::io::ErrorKind::InvalidInput,
519                "SQ entries should not exceed 4096",
520            )));
521        }
522
523        // Validate buffer configuration
524        if self.buffer.default_size == 0 {
525            return Err(SaferRingError::Io(std::io::Error::new(
526                std::io::ErrorKind::InvalidInput,
527                "Default buffer size must be greater than 0",
528            )));
529        }
530
531        if self.buffer.pool_size == 0 && self.buffer.enable_pooling {
532            return Err(SaferRingError::Io(std::io::Error::new(
533                std::io::ErrorKind::InvalidInput,
534                "Pool size must be greater than 0 when pooling is enabled",
535            )));
536        }
537
538        // Validate performance configuration
539        if self.performance.max_batch_size == 0 && self.performance.batch_submission {
540            return Err(SaferRingError::Io(std::io::Error::new(
541                std::io::ErrorKind::InvalidInput,
542                "Max batch size must be greater than 0 when batch submission is enabled",
543            )));
544        }
545
546        // Validate error handling configuration
547        if self.error_handling.auto_retry && self.error_handling.max_retries == 0 {
548            return Err(SaferRingError::Io(std::io::Error::new(
549                std::io::ErrorKind::InvalidInput,
550                "Max retries must be greater than 0 when auto retry is enabled",
551            )));
552        }
553
554        Ok(())
555    }
556
557    /// Get system memory information.
558    fn get_memory_info() -> Result<MemoryInfo> {
559        #[cfg(target_os = "linux")]
560        {
561            let meminfo = std::fs::read_to_string("/proc/meminfo").map_err(SaferRingError::Io)?;
562
563            let mut total = 0;
564            let mut available = 0;
565
566            for line in meminfo.lines() {
567                if line.starts_with("MemTotal:") {
568                    if let Some(value) = line.split_whitespace().nth(1) {
569                        total = value.parse::<u64>().unwrap_or(0) * 1024; // Convert KB to bytes
570                    }
571                } else if line.starts_with("MemAvailable:") {
572                    if let Some(value) = line.split_whitespace().nth(1) {
573                        available = value.parse::<u64>().unwrap_or(0) * 1024; // Convert KB to bytes
574                    }
575                }
576            }
577
578            Ok(MemoryInfo { total, available })
579        }
580
581        #[cfg(not(target_os = "linux"))]
582        {
583            // Fallback for non-Linux systems
584            Ok(MemoryInfo {
585                total: 8 * 1024 * 1024 * 1024,     // 8GB default
586                available: 4 * 1024 * 1024 * 1024, // 4GB default
587            })
588        }
589    }
590}
591
592/// System memory information.
593#[derive(Debug, Clone)]
594struct MemoryInfo {
595    /// Total system memory in bytes
596    #[allow(dead_code)]
597    total: u64,
598    /// Available system memory in bytes
599    available: u64,
600}
601
602/// Configuration builder for fluent configuration creation.
603#[derive(Debug)]
604pub struct ConfigBuilder {
605    config: SaferRingConfig,
606}
607
608impl ConfigBuilder {
609    /// Create a new configuration builder.
610    pub fn new() -> Self {
611        Self {
612            config: SaferRingConfig::default(),
613        }
614    }
615
616    /// Set ring configuration.
617    pub fn ring(mut self, ring: RingConfig) -> Self {
618        self.config.ring = ring;
619        self
620    }
621
622    /// Set buffer configuration.
623    pub fn buffer(mut self, buffer: BufferConfig) -> Self {
624        self.config.buffer = buffer;
625        self
626    }
627
628    /// Set performance configuration.
629    pub fn performance(mut self, performance: PerformanceConfig) -> Self {
630        self.config.performance = performance;
631        self
632    }
633
634    /// Set logging configuration.
635    pub fn logging(mut self, logging: LoggingConfig) -> Self {
636        self.config.logging = logging;
637        self
638    }
639
640    /// Set advanced configuration.
641    pub fn advanced(mut self, advanced: AdvancedConfig) -> Self {
642        self.config.advanced = advanced;
643        self
644    }
645
646    /// Set error handling configuration.
647    pub fn error_handling(mut self, error_handling: ErrorHandlingConfig) -> Self {
648        self.config.error_handling = error_handling;
649        self
650    }
651
652    /// Build the final configuration.
653    pub fn build(self) -> Result<SaferRingConfig> {
654        self.config.validate()?;
655        Ok(self.config)
656    }
657}
658
659impl Default for ConfigBuilder {
660    fn default() -> Self {
661        Self::new()
662    }
663}
664
665#[cfg(test)]
666mod tests {
667    use super::*;
668
669    #[test]
670    fn test_default_config() {
671        let config = SaferRingConfig::default();
672        assert!(config.validate().is_ok());
673    }
674
675    #[test]
676    fn test_low_latency_config() {
677        let config = SaferRingConfig::low_latency();
678        assert!(config.validate().is_ok());
679        assert_eq!(config.ring.sq_entries, 32);
680        assert!(config.ring.sq_poll);
681        assert!(!config.performance.batch_submission);
682    }
683
684    #[test]
685    fn test_high_throughput_config() {
686        let config = SaferRingConfig::high_throughput();
687        assert!(config.validate().is_ok());
688        assert_eq!(config.ring.sq_entries, 1024);
689        assert!(config.performance.batch_submission);
690        assert_eq!(config.performance.max_batch_size, 256);
691    }
692
693    #[test]
694    fn test_development_config() {
695        let config = SaferRingConfig::development();
696        assert!(config.validate().is_ok());
697        assert!(config.logging.enabled);
698        assert_eq!(config.logging.level, LogLevel::Debug);
699        assert!(config.logging.tracing);
700    }
701
702    #[test]
703    fn test_production_config() {
704        let config = SaferRingConfig::production();
705        assert!(config.validate().is_ok());
706        assert!(config.logging.enabled);
707        assert_eq!(config.logging.level, LogLevel::Warn);
708        assert!(config.logging.json_format);
709    }
710
711    #[test]
712    fn test_config_validation() {
713        let mut config = SaferRingConfig::default();
714
715        // Valid config should pass
716        assert!(config.validate().is_ok());
717
718        // Invalid SQ entries should fail
719        config.ring.sq_entries = 0;
720        assert!(config.validate().is_err());
721
722        config.ring.sq_entries = 128;
723        config.buffer.default_size = 0;
724        assert!(config.validate().is_err());
725    }
726
727    #[test]
728    fn test_config_builder() {
729        let config = ConfigBuilder::new()
730            .ring(RingConfig {
731                sq_entries: 64,
732                ..Default::default()
733            })
734            .buffer(BufferConfig {
735                default_size: 8192,
736                ..Default::default()
737            })
738            .build()
739            .unwrap();
740
741        assert_eq!(config.ring.sq_entries, 64);
742        assert_eq!(config.buffer.default_size, 8192);
743    }
744
745    #[test]
746    fn test_auto_detect_config() {
747        // This test may fail on systems without io_uring support
748        if let Ok(config) = SaferRingConfig::auto_detect() {
749            assert!(config.validate().is_ok());
750            assert!(config.ring.sq_entries > 0);
751        }
752    }
753}