mockforge_performance/
bottleneck.rs1use serde::{Deserialize, Serialize};
6use std::sync::Arc;
7use std::time::Duration;
8use tokio::sync::RwLock;
9use tokio::time::sleep;
10use tracing::debug;
11
12#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
14#[serde(rename_all = "snake_case")]
15pub enum BottleneckType {
16 Cpu,
18 Memory,
20 Network,
22 Io,
24 Database,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct BottleneckConfig {
31 pub bottleneck_type: BottleneckType,
33 pub severity: f64,
35 pub endpoint_pattern: Option<String>,
37 pub duration_secs: Option<u64>,
39}
40
41impl BottleneckConfig {
42 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 pub fn with_endpoint_pattern(mut self, pattern: String) -> Self {
54 self.endpoint_pattern = Some(pattern);
55 self
56 }
57
58 pub fn with_duration(mut self, duration_secs: u64) -> Self {
60 self.duration_secs = Some(duration_secs);
61 self
62 }
63}
64
65#[derive(Debug, Clone)]
69pub struct BottleneckSimulator {
70 bottlenecks: Arc<RwLock<Vec<BottleneckConfig>>>,
72}
73
74impl BottleneckSimulator {
75 pub fn new() -> Self {
77 Self {
78 bottlenecks: Arc::new(RwLock::new(Vec::new())),
79 }
80 }
81
82 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 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 pub async fn get_bottlenecks(&self) -> Vec<BottleneckConfig> {
99 let bottlenecks = self.bottlenecks.read().await;
100 bottlenecks.clone()
101 }
102
103 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 if let Some(ref pattern) = bottleneck.endpoint_pattern {
113 if !endpoint.contains(pattern) {
114 continue;
115 }
116 }
117
118 let delay_ms = match bottleneck.bottleneck_type {
120 BottleneckType::Cpu => {
121 let cpu_time_ms = (bottleneck.severity * 100.0) as u64;
123 self.simulate_cpu_bottleneck(cpu_time_ms).await;
124 0 }
126 BottleneckType::Memory => {
127 let memory_mb = (bottleneck.severity * 100.0) as usize;
129 self.simulate_memory_bottleneck(memory_mb).await;
130 0 }
132 BottleneckType::Network => {
133 (bottleneck.severity * 500.0) as u64
135 }
136 BottleneckType::Io => {
137 (bottleneck.severity * 300.0) as u64
139 }
140 BottleneckType::Database => {
141 (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 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 while start.elapsed() < duration {
163 std::hint::spin_loop();
165 }
166 }
167
168 async fn simulate_memory_bottleneck(&self, size_mb: usize) {
170 let _memory: Vec<u8> = vec![0; size_mb * 1024 * 1024];
172 }
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 assert!(elapsed.as_millis() > 0);
212 }
213}