mockforge_performance/
bottleneck.rs

1//! Bottleneck Simulation
2//!
3//! Simulates various types of bottlenecks to observe system behavior under stress.
4
5use serde::{Deserialize, Serialize};
6use std::sync::Arc;
7use std::time::Duration;
8use tokio::sync::RwLock;
9use tokio::time::sleep;
10use tracing::debug;
11
12/// Bottleneck type
13#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
14#[serde(rename_all = "snake_case")]
15pub enum BottleneckType {
16    /// CPU bottleneck (simulated with busy-wait)
17    Cpu,
18    /// Memory bottleneck (simulated with allocation)
19    Memory,
20    /// Network bottleneck (simulated with delay)
21    Network,
22    /// I/O bottleneck (simulated with delay)
23    Io,
24    /// Database bottleneck (simulated with delay)
25    Database,
26}
27
28/// Bottleneck configuration
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct BottleneckConfig {
31    /// Bottleneck type
32    pub bottleneck_type: BottleneckType,
33    /// Severity (0.0-1.0, where 1.0 is maximum bottleneck)
34    pub severity: f64,
35    /// Affected endpoint pattern (None = all endpoints)
36    pub endpoint_pattern: Option<String>,
37    /// Duration in seconds (None = indefinite)
38    pub duration_secs: Option<u64>,
39}
40
41impl BottleneckConfig {
42    /// Create a new bottleneck configuration
43    pub fn new(bottleneck_type: BottleneckType, severity: f64) -> Self {
44        Self {
45            bottleneck_type,
46            severity,
47            endpoint_pattern: None,
48            duration_secs: None,
49        }
50    }
51
52    /// Set endpoint pattern
53    pub fn with_endpoint_pattern(mut self, pattern: String) -> Self {
54        self.endpoint_pattern = Some(pattern);
55        self
56    }
57
58    /// Set duration
59    pub fn with_duration(mut self, duration_secs: u64) -> Self {
60        self.duration_secs = Some(duration_secs);
61        self
62    }
63}
64
65/// Bottleneck simulator
66///
67/// Simulates various types of bottlenecks.
68#[derive(Debug, Clone)]
69pub struct BottleneckSimulator {
70    /// Active bottlenecks
71    bottlenecks: Arc<RwLock<Vec<BottleneckConfig>>>,
72}
73
74impl BottleneckSimulator {
75    /// Create a new bottleneck simulator
76    pub fn new() -> Self {
77        Self {
78            bottlenecks: Arc::new(RwLock::new(Vec::new())),
79        }
80    }
81
82    /// Add a bottleneck
83    pub async fn add_bottleneck(&self, config: BottleneckConfig) {
84        let bottleneck_type = config.bottleneck_type;
85        let mut bottlenecks = self.bottlenecks.write().await;
86        bottlenecks.push(config);
87        debug!("Bottleneck added: {:?}", bottleneck_type);
88    }
89
90    /// Remove all bottlenecks
91    pub async fn clear_bottlenecks(&self) {
92        let mut bottlenecks = self.bottlenecks.write().await;
93        bottlenecks.clear();
94        debug!("All bottlenecks cleared");
95    }
96
97    /// Get active bottlenecks
98    pub async fn get_bottlenecks(&self) -> Vec<BottleneckConfig> {
99        let bottlenecks = self.bottlenecks.read().await;
100        bottlenecks.clone()
101    }
102
103    /// Apply bottlenecks for a request
104    ///
105    /// Returns the total delay in milliseconds.
106    pub async fn apply_bottlenecks(&self, endpoint: &str) -> u64 {
107        let bottlenecks = self.bottlenecks.read().await;
108        let mut total_delay_ms = 0u64;
109
110        for bottleneck in bottlenecks.iter() {
111            // Check if endpoint matches pattern
112            if let Some(ref pattern) = bottleneck.endpoint_pattern {
113                if !endpoint.contains(pattern) {
114                    continue;
115                }
116            }
117
118            // Calculate delay based on bottleneck type and severity
119            let delay_ms = match bottleneck.bottleneck_type {
120                BottleneckType::Cpu => {
121                    // CPU bottleneck: busy-wait
122                    let cpu_time_ms = (bottleneck.severity * 100.0) as u64;
123                    self.simulate_cpu_bottleneck(cpu_time_ms).await;
124                    0 // CPU bottleneck doesn't add delay, it uses CPU time
125                }
126                BottleneckType::Memory => {
127                    // Memory bottleneck: allocation
128                    let memory_mb = (bottleneck.severity * 100.0) as usize;
129                    self.simulate_memory_bottleneck(memory_mb).await;
130                    0 // Memory bottleneck doesn't add delay
131                }
132                BottleneckType::Network => {
133                    // Network bottleneck: delay
134                    (bottleneck.severity * 500.0) as u64
135                }
136                BottleneckType::Io => {
137                    // I/O bottleneck: delay
138                    (bottleneck.severity * 300.0) as u64
139                }
140                BottleneckType::Database => {
141                    // Database bottleneck: delay
142                    (bottleneck.severity * 400.0) as u64
143                }
144            };
145
146            total_delay_ms += delay_ms;
147        }
148
149        if total_delay_ms > 0 {
150            sleep(Duration::from_millis(total_delay_ms)).await;
151        }
152
153        total_delay_ms
154    }
155
156    /// Simulate CPU bottleneck (busy-wait)
157    async fn simulate_cpu_bottleneck(&self, duration_ms: u64) {
158        let start = std::time::Instant::now();
159        let duration = Duration::from_millis(duration_ms);
160
161        // Busy-wait to simulate CPU load
162        while start.elapsed() < duration {
163            // Spin loop
164            std::hint::spin_loop();
165        }
166    }
167
168    /// Simulate memory bottleneck (allocation)
169    async fn simulate_memory_bottleneck(&self, size_mb: usize) {
170        // Allocate memory to simulate memory pressure
171        let _memory: Vec<u8> = vec![0; size_mb * 1024 * 1024];
172        // Memory is dropped when function returns
173    }
174}
175
176impl Default for BottleneckSimulator {
177    fn default() -> Self {
178        Self::new()
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[tokio::test]
187    async fn test_bottleneck_simulator() {
188        let simulator = BottleneckSimulator::new();
189
190        let config = BottleneckConfig::new(BottleneckType::Network, 0.5)
191            .with_endpoint_pattern("/api/users".to_string());
192
193        simulator.add_bottleneck(config).await;
194
195        let bottlenecks = simulator.get_bottlenecks().await;
196        assert_eq!(bottlenecks.len(), 1);
197    }
198
199    #[tokio::test]
200    async fn test_apply_bottlenecks() {
201        let simulator = BottleneckSimulator::new();
202
203        let config = BottleneckConfig::new(BottleneckType::Network, 0.1);
204        simulator.add_bottleneck(config).await;
205
206        let start = std::time::Instant::now();
207        simulator.apply_bottlenecks("/api/test").await;
208        let elapsed = start.elapsed();
209
210        // Should have added some delay
211        assert!(elapsed.as_millis() > 0);
212    }
213}