kestrel_timer/
config.rs

1//! Timer Configuration Module
2//!
3//! Provides hierarchical configuration structure and Builder pattern for configuring timing wheel, service, and batch processing behavior.
4//!
5//! 定时器配置模块,提供分层的配置结构和 Builder 模式,用于配置时间轮、服务和批处理行为。
6use crate::error::TimerError;
7use std::num::NonZeroUsize;
8use std::time::Duration;
9
10/// Timing Wheel Configuration
11///
12/// Used to configure parameters for hierarchical timing wheel. The system only supports hierarchical mode.
13///
14/// # 时间轮配置
15///
16/// 用于配置分层时间轮的参数。系统只支持分层模式。
17///
18/// # Examples (示例)
19/// ```no_run
20/// use kestrel_timer::config::WheelConfig;
21/// use std::time::Duration;
22///
23/// // Use default configuration (使用默认配置,分层模式)
24/// let config = WheelConfig::default();
25///
26/// // Use Builder to customize configuration (使用 Builder 自定义配置)
27/// let config = WheelConfig::builder()
28///     .l0_tick_duration(Duration::from_millis(20))
29///     .l0_slot_count(1024)
30///     .l1_tick_duration(Duration::from_secs(2))
31///     .l1_slot_count(128)
32///     .build()
33///     .unwrap();
34/// ```
35#[derive(Debug, Clone)]
36pub struct WheelConfig {
37    /// Duration of each tick in L0 layer, bottom layer
38    ///
39    /// L0 层每个 tick 的持续时间
40    pub l0_tick_duration: Duration,
41
42    /// Number of slots in L0 layer, must be power of 2
43    ///
44    /// L0 层槽位数,必须是 2 的幂
45    pub l0_slot_count: usize,
46
47    /// Duration of each tick in L1 layer, upper layer
48    ///
49    /// L1 层每个 tick 的持续时间
50    pub l1_tick_duration: Duration,
51
52    /// Number of slots in L1 layer, must be power of 2
53    ///
54    /// L1 层槽位数,必须是 2 的幂
55    pub l1_slot_count: usize,
56}
57
58impl Default for WheelConfig {
59    fn default() -> Self {
60        Self {
61            l0_tick_duration: Duration::from_millis(10),
62            l0_slot_count: 512,
63            l1_tick_duration: Duration::from_secs(1),
64            l1_slot_count: 64,
65        }
66    }
67}
68
69impl WheelConfig {
70    /// Create configuration builder (创建配置构建器)
71    pub fn builder() -> WheelConfigBuilder {
72        WheelConfigBuilder::default()
73    }
74}
75
76/// Timing Wheel Configuration Builder
77#[derive(Debug, Clone)]
78pub struct WheelConfigBuilder {
79    l0_tick_duration: Duration,
80    l0_slot_count: usize,
81    l1_tick_duration: Duration,
82    l1_slot_count: usize,
83}
84
85impl Default for WheelConfigBuilder {
86    fn default() -> Self {
87        Self {
88            l0_tick_duration: Duration::from_millis(10),
89            l0_slot_count: 512,
90            l1_tick_duration: Duration::from_secs(1),
91            l1_slot_count: 64,
92        }
93    }
94}
95
96impl WheelConfigBuilder {
97    /// Set L0 layer tick duration
98    pub fn l0_tick_duration(mut self, duration: Duration) -> Self {
99        self.l0_tick_duration = duration;
100        self
101    }
102
103    /// Set L0 layer slot count
104    pub fn l0_slot_count(mut self, count: usize) -> Self {
105        self.l0_slot_count = count;
106        self
107    }
108
109    /// Set L1 layer tick duration
110    pub fn l1_tick_duration(mut self, duration: Duration) -> Self {
111        self.l1_tick_duration = duration;
112        self
113    }
114
115    /// Set L1 layer slot count
116    pub fn l1_slot_count(mut self, count: usize) -> Self {
117        self.l1_slot_count = count;
118        self
119    }
120
121    /// Build and validate configuration
122    ///
123    /// # Returns
124    /// - `Ok(WheelConfig)`: Configuration is valid
125    /// - `Err(TimerError)`: Configuration validation failed
126    ///
127    /// # Validation Rules
128    /// - L0 tick duration must be greater than 0
129    /// - L1 tick duration must be greater than 0
130    /// - L0 slot count must be greater than 0 and power of 2
131    /// - L1 slot count must be greater than 0 and power of 2
132    /// - L1 tick must be an integer multiple of L0 tick
133    pub fn build(self) -> Result<WheelConfig, TimerError> {
134        // Validate L0 layer configuration
135        if self.l0_tick_duration.is_zero() {
136            return Err(TimerError::InvalidConfiguration {
137                field: "l0_tick_duration".to_string(),
138                reason: "L0 layer tick duration must be greater than 0".to_string(),
139            });
140        }
141
142        if self.l0_slot_count == 0 {
143            return Err(TimerError::InvalidSlotCount {
144                slot_count: self.l0_slot_count,
145                reason: "L0 layer slot count must be greater than 0",
146            });
147        }
148
149        if !self.l0_slot_count.is_power_of_two() {
150            return Err(TimerError::InvalidSlotCount {
151                slot_count: self.l0_slot_count,
152                reason: "L0 layer slot count must be power of 2",
153            });
154        }
155
156        // Validate L1 layer configuration
157        if self.l1_tick_duration.is_zero() {
158            return Err(TimerError::InvalidConfiguration {
159                field: "l1_tick_duration".to_string(),
160                reason: "L1 layer tick duration must be greater than 0".to_string(),
161            });
162        }
163
164        if self.l1_slot_count == 0 {
165            return Err(TimerError::InvalidSlotCount {
166                slot_count: self.l1_slot_count,
167                reason: "L1 layer slot count must be greater than 0",
168            });
169        }
170
171        if !self.l1_slot_count.is_power_of_two() {
172            return Err(TimerError::InvalidSlotCount {
173                slot_count: self.l1_slot_count,
174                reason: "L1 layer slot count must be power of 2",
175            });
176        }
177
178        // Validate L1 tick is an integer multiple of L0 tick
179        let l0_ms = self.l0_tick_duration.as_millis() as u64;
180        let l1_ms = self.l1_tick_duration.as_millis() as u64;
181        if !l1_ms.is_multiple_of(l0_ms) {
182            return Err(TimerError::InvalidConfiguration {
183                field: "l1_tick_duration".to_string(),
184                reason: format!(
185                    "L1 tick duration ({} ms) must be an integer multiple of L0 tick duration ({} ms)",
186                    l1_ms, l0_ms
187                ),
188            });
189        }
190
191        Ok(WheelConfig {
192            l0_tick_duration: self.l0_tick_duration,
193            l0_slot_count: self.l0_slot_count,
194            l1_tick_duration: self.l1_tick_duration,
195            l1_slot_count: self.l1_slot_count,
196        })
197    }
198}
199
200/// Service Configuration
201///
202/// Used to configure channel capacities for TimerService.
203///
204/// # 服务配置
205///
206/// 用于配置 TimerService 的通道容量。
207///
208/// # Examples (示例)
209/// ```no_run
210/// use kestrel_timer::config::ServiceConfig;
211/// use std::num::NonZeroUsize;
212///
213/// // Use default configuration (使用默认配置)
214/// let config = ServiceConfig::default();
215///
216/// // Use Builder to customize configuration (使用 Builder 自定义配置)
217/// let config = ServiceConfig::builder()
218///     .command_channel_capacity(NonZeroUsize::new(1024).unwrap())
219///     .timeout_channel_capacity(NonZeroUsize::new(2000).unwrap())
220///     .build();
221/// ```
222#[derive(Debug, Clone)]
223pub struct ServiceConfig {
224    /// Command channel capacity
225    ///
226    /// 命令通道容量
227    pub command_channel_capacity: NonZeroUsize,
228
229    /// Timeout channel capacity
230    ///
231    /// 超时通道容量
232    pub timeout_channel_capacity: NonZeroUsize,
233}
234
235impl Default for ServiceConfig {
236    fn default() -> Self {
237        Self {
238            command_channel_capacity: NonZeroUsize::new(512).unwrap(),
239            timeout_channel_capacity: NonZeroUsize::new(1000).unwrap(),
240        }
241    }
242}
243
244impl ServiceConfig {
245    /// Create configuration builder (创建配置构建器)
246    pub fn builder() -> ServiceConfigBuilder {
247        ServiceConfigBuilder::default()
248    }
249}
250
251/// Service Configuration Builder
252///
253/// 用于构建 ServiceConfig 的构建器。
254#[derive(Debug, Clone)]
255pub struct ServiceConfigBuilder {
256    /// Command channel capacity
257    ///
258    /// 命令通道容量
259    pub command_channel_capacity: NonZeroUsize,
260
261    /// Timeout channel capacity
262    ///
263    /// 超时通道容量
264    pub timeout_channel_capacity: NonZeroUsize,
265}
266
267impl Default for ServiceConfigBuilder {
268    fn default() -> Self {
269        let config = ServiceConfig::default();
270        Self {
271            command_channel_capacity: config.command_channel_capacity,
272            timeout_channel_capacity: config.timeout_channel_capacity,
273        }
274    }
275}
276
277impl ServiceConfigBuilder {
278    /// Set command channel capacity (设置命令通道容量)
279    pub fn command_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
280        self.command_channel_capacity = capacity;
281        self
282    }
283
284    /// Set timeout channel capacity (设置超时通道容量)
285    pub fn timeout_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
286        self.timeout_channel_capacity = capacity;
287        self
288    }
289
290    /// Build and validate configuration
291    ///
292    /// # Returns
293    /// - `Ok(ServiceConfig)`: Configuration is valid
294    /// - `Err(TimerError)`: Configuration validation failed
295    ///
296    /// # Validation Rules
297    /// - All channel capacities must be greater than 0
298    ///
299    /// 构建并验证配置
300    ///
301    /// # 返回值
302    /// - `Ok(ServiceConfig)`: 配置有效
303    /// - `Err(TimerError)`: 配置验证失败
304    ///
305    /// # 验证规则
306    /// - 所有通道容量必须大于 0
307    ///
308    pub fn build(self) -> ServiceConfig {
309        ServiceConfig {
310            command_channel_capacity: self.command_channel_capacity,
311            timeout_channel_capacity: self.timeout_channel_capacity,
312        }
313    }
314}
315
316/// Batch Processing Configuration
317///
318/// Used to configure optimization parameters for batch operations.
319///
320/// 用于配置批处理操作的优化参数。
321///
322/// # 批处理配置
323///
324/// 用于配置批处理操作的优化参数。
325///
326/// # Examples (示例)
327/// ```no_run
328/// use kestrel_timer::config::BatchConfig;
329///
330/// // Use default configuration (使用默认配置)
331/// let config = BatchConfig::default();
332///
333/// // Custom configuration (使用自定义配置)
334/// let config = BatchConfig {
335///     small_batch_threshold: 20,
336/// };
337/// ```
338#[derive(Debug, Clone)]
339pub struct BatchConfig {
340    /// Small batch threshold, used for batch cancellation optimization
341    /// When the number of tasks to be cancelled is less than or equal to this value, cancel individually without grouping and sorting
342    ///
343    /// 小批量阈值,用于批量取消操作的优化
344    ///
345    /// 当需要取消的任务数量小于或等于此值时,取消操作将单独进行,无需分组和排序
346    pub small_batch_threshold: usize,
347}
348
349impl Default for BatchConfig {
350    fn default() -> Self {
351        Self {
352            small_batch_threshold: 10,
353        }
354    }
355}
356
357/// Top-level Timer Configuration
358///
359/// Combines all sub-configurations to provide complete timer system configuration.
360///
361/// # 定时器配置
362///
363/// 用于组合所有子配置,提供完整的定时器系统配置。
364///
365/// # Examples (示例)
366/// ```no_run
367/// use kestrel_timer::config::TimerConfig;
368/// use std::num::NonZeroUsize;
369///
370/// // Use default configuration (使用默认配置)
371/// let config = TimerConfig::default();
372///
373/// // Use Builder to customize configuration, service parameters only
374/// // (使用 Builder 自定义配置,仅配置服务参数)
375/// let config = TimerConfig::builder()
376///     .command_channel_capacity(NonZeroUsize::new(1024).unwrap())
377///     .timeout_channel_capacity(NonZeroUsize::new(2000).unwrap())
378///     .build();
379/// ```
380#[derive(Debug, Clone, Default)]
381pub struct TimerConfig {
382    /// Timing wheel configuration
383    pub wheel: WheelConfig,
384    /// Service configuration
385    pub service: ServiceConfig,
386    /// Batch processing configuration
387    pub batch: BatchConfig,
388}
389
390impl TimerConfig {
391    /// Create configuration builder (创建配置构建器)
392    pub fn builder() -> TimerConfigBuilder {
393        TimerConfigBuilder::default()
394    }
395}
396
397/// Top-level Timer Configuration Builder (顶级定时器配置构建器)
398///
399/// 用于构建 TimerConfig 的构建器。
400#[derive(Debug, Default)]
401pub struct TimerConfigBuilder {
402    wheel_builder: WheelConfigBuilder,
403    service_builder: ServiceConfigBuilder,
404    batch_config: BatchConfig,
405}
406
407impl TimerConfigBuilder {
408    /// Set command channel capacity (设置命令通道容量)
409    pub fn command_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
410        self.service_builder = self.service_builder.command_channel_capacity(capacity);
411        self
412    }
413
414    /// Set timeout channel capacity (设置超时通道容量)
415    pub fn timeout_channel_capacity(mut self, capacity: NonZeroUsize) -> Self {
416        self.service_builder = self.service_builder.timeout_channel_capacity(capacity);
417        self
418    }
419
420    /// Set small batch threshold (设置小批量阈值)
421    pub fn small_batch_threshold(mut self, threshold: usize) -> Self {
422        self.batch_config.small_batch_threshold = threshold;
423        self
424    }
425
426    /// Build and validate configuration
427    ///
428    /// # Returns
429    /// - `Ok(TimerConfig)`: Configuration is valid
430    /// - `Err(TimerError)`: Configuration validation failed
431    ///
432    /// # 构建并验证配置
433    ///
434    /// # 返回值
435    /// - `Ok(TimerConfig)`: 配置有效
436    /// - `Err(TimerError)`: 配置验证失败
437    ///
438    pub fn build(self) -> Result<TimerConfig, TimerError> {
439        Ok(TimerConfig {
440            wheel: self.wheel_builder.build()?,
441            service: self.service_builder.build(),
442            batch: self.batch_config,
443        })
444    }
445}
446
447#[cfg(test)]
448mod tests {
449    use super::*;
450
451    #[test]
452    fn test_wheel_config_default() {
453        let config = WheelConfig::default();
454        assert_eq!(config.l0_tick_duration, Duration::from_millis(10));
455        assert_eq!(config.l0_slot_count, 512);
456        assert_eq!(config.l1_tick_duration, Duration::from_secs(1));
457        assert_eq!(config.l1_slot_count, 64);
458    }
459
460    #[test]
461    fn test_wheel_config_builder() {
462        let config = WheelConfig::builder()
463            .l0_tick_duration(Duration::from_millis(20))
464            .l0_slot_count(1024)
465            .l1_tick_duration(Duration::from_secs(2))
466            .l1_slot_count(128)
467            .build()
468            .unwrap();
469
470        assert_eq!(config.l0_tick_duration, Duration::from_millis(20));
471        assert_eq!(config.l0_slot_count, 1024);
472        assert_eq!(config.l1_tick_duration, Duration::from_secs(2));
473        assert_eq!(config.l1_slot_count, 128);
474    }
475
476    #[test]
477    fn test_wheel_config_validation_zero_tick() {
478        let result = WheelConfig::builder()
479            .l0_tick_duration(Duration::ZERO)
480            .build();
481
482        assert!(result.is_err());
483    }
484
485    #[test]
486    fn test_wheel_config_validation_invalid_slot_count() {
487        let result = WheelConfig::builder().l0_slot_count(100).build();
488
489        assert!(result.is_err());
490    }
491
492    #[test]
493    fn test_service_config_builder() {
494        let config = ServiceConfig::builder()
495            .command_channel_capacity(NonZeroUsize::new(1024).unwrap())
496            .timeout_channel_capacity(NonZeroUsize::new(2000).unwrap())
497            .build();
498
499        assert_eq!(
500            config.command_channel_capacity,
501            NonZeroUsize::new(1024).unwrap()
502        );
503        assert_eq!(
504            config.timeout_channel_capacity,
505            NonZeroUsize::new(2000).unwrap()
506        );
507    }
508
509    #[test]
510    fn test_batch_config_default() {
511        let config = BatchConfig::default();
512        assert_eq!(config.small_batch_threshold, 10);
513    }
514
515    #[test]
516    fn test_timer_config_default() {
517        let config = TimerConfig::default();
518        assert_eq!(config.wheel.l0_slot_count, 512);
519        assert_eq!(
520            config.service.command_channel_capacity,
521            NonZeroUsize::new(512).unwrap()
522        );
523        assert_eq!(config.batch.small_batch_threshold, 10);
524    }
525
526    #[test]
527    fn test_timer_config_builder() {
528        let config = TimerConfig::builder()
529            .command_channel_capacity(NonZeroUsize::new(1024).unwrap())
530            .timeout_channel_capacity(NonZeroUsize::new(2000).unwrap())
531            .small_batch_threshold(20)
532            .build()
533            .unwrap();
534
535        assert_eq!(
536            config.service.command_channel_capacity,
537            NonZeroUsize::new(1024).unwrap()
538        );
539        assert_eq!(
540            config.service.timeout_channel_capacity,
541            NonZeroUsize::new(2000).unwrap()
542        );
543        assert_eq!(config.batch.small_batch_threshold, 20);
544    }
545}