Skip to main content

rustfs_io_core/
config.rs

1// Copyright 2024 RustFS Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! I/O scheduler configuration types.
16//!
17//! This module provides configuration types for the I/O scheduler,
18//! including priority thresholds, queue capacities, and load thresholds.
19
20use std::time::Duration;
21
22/// I/O scheduler configuration.
23#[derive(Debug, Clone, PartialEq)]
24pub struct IoSchedulerConfig {
25    /// Maximum concurrent disk reads.
26    pub max_concurrent_reads: usize,
27    /// High priority size threshold in bytes.
28    pub high_priority_size_threshold: usize,
29    /// Low priority size threshold in bytes.
30    pub low_priority_size_threshold: usize,
31    /// High priority queue capacity.
32    pub queue_high_capacity: usize,
33    /// Normal priority queue capacity.
34    pub queue_normal_capacity: usize,
35    /// Low priority queue capacity.
36    pub queue_low_capacity: usize,
37    /// Starvation prevention check interval in milliseconds.
38    pub starvation_prevention_interval_ms: u64,
39    /// Starvation threshold in seconds.
40    pub starvation_threshold_secs: u64,
41    /// Load sampling window size.
42    pub load_sample_window: usize,
43    /// High load wait time threshold in milliseconds.
44    pub load_high_threshold_ms: u64,
45    /// Low load wait time threshold in milliseconds.
46    pub load_low_threshold_ms: u64,
47    /// Whether priority scheduling is enabled.
48    pub enable_priority: bool,
49
50    // Enhanced scheduling configuration fields
51    /// Storage media detection enabled.
52    pub storage_detection_enabled: bool,
53    /// Sequential detection enabled.
54    pub sequential_detection_enabled: bool,
55    /// Bandwidth monitoring enabled.
56    pub bandwidth_monitoring_enabled: bool,
57    /// Adaptive buffer sizing enabled.
58    pub adaptive_buffer_enabled: bool,
59    /// Base buffer size for I/O operations.
60    pub base_buffer_size: usize,
61    /// Maximum buffer size.
62    pub max_buffer_size: usize,
63    /// Minimum buffer size.
64    pub min_buffer_size: usize,
65}
66
67impl Default for IoSchedulerConfig {
68    fn default() -> Self {
69        Self {
70            max_concurrent_reads: 32,
71            high_priority_size_threshold: 64 * 1024,      // 64KB
72            low_priority_size_threshold: 4 * 1024 * 1024, // 4MB
73            queue_high_capacity: 100,
74            queue_normal_capacity: 500,
75            queue_low_capacity: 200,
76            starvation_prevention_interval_ms: 100,
77            starvation_threshold_secs: 5,
78            load_sample_window: 10,
79            load_high_threshold_ms: 50,
80            load_low_threshold_ms: 5,
81            enable_priority: true,
82            storage_detection_enabled: true,
83            sequential_detection_enabled: true,
84            bandwidth_monitoring_enabled: true,
85            adaptive_buffer_enabled: true,
86            base_buffer_size: 128 * 1024, // 128KB
87            max_buffer_size: 1024 * 1024, // 1MB
88            min_buffer_size: 4 * 1024,    // 4KB
89        }
90    }
91}
92
93impl IoSchedulerConfig {
94    /// Create a new configuration with default values.
95    pub fn new() -> Self {
96        Self::default()
97    }
98
99    /// Validate the configuration.
100    ///
101    /// # Errors
102    ///
103    /// Returns an error if any configuration value is invalid.
104    pub fn validate(&self) -> Result<(), ConfigError> {
105        if self.max_concurrent_reads == 0 {
106            return Err(ConfigError::InvalidValue("max_concurrent_reads must be > 0".to_string()));
107        }
108        if self.high_priority_size_threshold >= self.low_priority_size_threshold {
109            return Err(ConfigError::InvalidValue(
110                "high_priority_size_threshold must be < low_priority_size_threshold".to_string(),
111            ));
112        }
113        if self.min_buffer_size > self.max_buffer_size {
114            return Err(ConfigError::InvalidValue("min_buffer_size must be <= max_buffer_size".to_string()));
115        }
116        if self.base_buffer_size < self.min_buffer_size || self.base_buffer_size > self.max_buffer_size {
117            return Err(ConfigError::InvalidValue(
118                "base_buffer_size must be between min_buffer_size and max_buffer_size".to_string(),
119            ));
120        }
121        Ok(())
122    }
123
124    /// Get the starvation prevention interval as a Duration.
125    pub fn starvation_prevention_interval(&self) -> Duration {
126        Duration::from_millis(self.starvation_prevention_interval_ms)
127    }
128
129    /// Get the starvation threshold as a Duration.
130    pub fn starvation_threshold(&self) -> Duration {
131        Duration::from_secs(self.starvation_threshold_secs)
132    }
133
134    /// Get the high load threshold as a Duration.
135    pub fn load_high_threshold(&self) -> Duration {
136        Duration::from_millis(self.load_high_threshold_ms)
137    }
138
139    /// Get the low load threshold as a Duration.
140    pub fn load_low_threshold(&self) -> Duration {
141        Duration::from_millis(self.load_low_threshold_ms)
142    }
143
144    /// Builder pattern: set max concurrent reads.
145    pub fn with_max_concurrent_reads(mut self, value: usize) -> Self {
146        self.max_concurrent_reads = value;
147        self
148    }
149
150    /// Builder pattern: set priority thresholds.
151    pub fn with_priority_thresholds(mut self, high: usize, low: usize) -> Self {
152        self.high_priority_size_threshold = high;
153        self.low_priority_size_threshold = low;
154        self
155    }
156
157    /// Builder pattern: set buffer sizes.
158    pub fn with_buffer_sizes(mut self, base: usize, min: usize, max: usize) -> Self {
159        self.base_buffer_size = base;
160        self.min_buffer_size = min;
161        self.max_buffer_size = max;
162        self
163    }
164
165    /// Builder pattern: enable/disable priority scheduling.
166    pub fn with_priority_enabled(mut self, enabled: bool) -> Self {
167        self.enable_priority = enabled;
168        self
169    }
170}
171
172/// Configuration error type.
173#[derive(Debug, Clone, thiserror::Error)]
174pub enum ConfigError {
175    /// Invalid configuration value.
176    #[error("Invalid configuration: {0}")]
177    InvalidValue(String),
178}
179
180/// I/O priority queue configuration.
181#[derive(Debug, Clone, PartialEq)]
182pub struct IoPriorityQueueConfig {
183    /// High priority queue capacity.
184    pub high_capacity: usize,
185    /// Normal priority queue capacity.
186    pub normal_capacity: usize,
187    /// Low priority queue capacity.
188    pub low_capacity: usize,
189    /// Starvation prevention interval.
190    pub starvation_interval: Duration,
191    /// Starvation threshold.
192    pub starvation_threshold: Duration,
193}
194
195impl Default for IoPriorityQueueConfig {
196    fn default() -> Self {
197        Self {
198            high_capacity: 100,
199            normal_capacity: 500,
200            low_capacity: 200,
201            starvation_interval: Duration::from_millis(100),
202            starvation_threshold: Duration::from_secs(5),
203        }
204    }
205}
206
207impl IoPriorityQueueConfig {
208    /// Create from IoSchedulerConfig.
209    pub fn from_scheduler_config(config: &IoSchedulerConfig) -> Self {
210        Self {
211            high_capacity: config.queue_high_capacity,
212            normal_capacity: config.queue_normal_capacity,
213            low_capacity: config.queue_low_capacity,
214            starvation_interval: config.starvation_prevention_interval(),
215            starvation_threshold: config.starvation_threshold(),
216        }
217    }
218
219    /// Get total capacity across all queues.
220    pub fn total_capacity(&self) -> usize {
221        self.high_capacity + self.normal_capacity + self.low_capacity
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228
229    #[test]
230    fn test_default_config() {
231        let config = IoSchedulerConfig::default();
232        assert!(config.validate().is_ok());
233        assert!(config.enable_priority);
234        assert!(config.adaptive_buffer_enabled);
235    }
236
237    #[test]
238    fn test_config_validation() {
239        let config = IoSchedulerConfig::new().with_max_concurrent_reads(0);
240        assert!(config.validate().is_err());
241
242        let config = IoSchedulerConfig::new().with_priority_thresholds(1024 * 1024, 1024);
243        assert!(config.validate().is_err());
244
245        let config = IoSchedulerConfig::new().with_buffer_sizes(1024, 4096, 512);
246        assert!(config.validate().is_err());
247    }
248
249    #[test]
250    fn test_builder_pattern() {
251        let config = IoSchedulerConfig::new()
252            .with_max_concurrent_reads(64)
253            .with_priority_thresholds(32 * 1024, 8 * 1024 * 1024)
254            .with_buffer_sizes(256 * 1024, 8 * 1024, 2 * 1024 * 1024)
255            .with_priority_enabled(false);
256
257        assert_eq!(config.max_concurrent_reads, 64);
258        assert_eq!(config.high_priority_size_threshold, 32 * 1024);
259        assert!(!config.enable_priority);
260        assert!(config.validate().is_ok());
261    }
262
263    #[test]
264    fn test_priority_queue_config() {
265        let config = IoSchedulerConfig::default();
266        let pq_config = IoPriorityQueueConfig::from_scheduler_config(&config);
267
268        assert_eq!(pq_config.high_capacity, config.queue_high_capacity);
269        assert_eq!(pq_config.normal_capacity, config.queue_normal_capacity);
270        assert_eq!(pq_config.low_capacity, config.queue_low_capacity);
271        assert!(pq_config.total_capacity() > 0);
272    }
273
274    #[test]
275    fn test_duration_helpers() {
276        let config = IoSchedulerConfig::default();
277
278        assert_eq!(config.starvation_prevention_interval(), Duration::from_millis(100));
279        assert_eq!(config.starvation_threshold(), Duration::from_secs(5));
280        assert_eq!(config.load_high_threshold(), Duration::from_millis(50));
281        assert_eq!(config.load_low_threshold(), Duration::from_millis(5));
282    }
283}