1use crate::{TuningConfig, UnifiedCacheStats};
7use serde::{Deserialize, Serialize};
8use std::{
9 collections::VecDeque,
10 sync::{Arc, RwLock},
11 time::SystemTime,
12};
13use tokio::time::Interval;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct PerformanceSnapshot {
18 pub timestamp: SystemTime,
20 pub hit_rate: f64,
22 pub l1_hit_rate: f64,
24 pub l2_hit_rate: f64,
26 pub memory_usage_percent: f64,
28 pub disk_usage_percent: f64,
30 pub avg_get_latency_us: f64,
32 pub avg_put_latency_us: f64,
34 pub ops_per_second: f64,
36 pub eviction_rate: f64,
38 pub promotion_rate: f64,
40}
41
42impl Default for PerformanceSnapshot {
43 fn default() -> Self {
44 Self {
45 timestamp: SystemTime::now(),
46 hit_rate: 0.0,
47 l1_hit_rate: 0.0,
48 l2_hit_rate: 0.0,
49 memory_usage_percent: 0.0,
50 disk_usage_percent: 0.0,
51 avg_get_latency_us: 0.0,
52 avg_put_latency_us: 0.0,
53 ops_per_second: 0.0,
54 eviction_rate: 0.0,
55 promotion_rate: 0.0,
56 }
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct TuningRecommendation {
63 pub parameter: String,
65 pub current_value: f64,
67 pub recommended_value: f64,
69 pub confidence: f64,
71 pub expected_improvement: f64,
73 pub reason: String,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct AdaptiveTuningStats {
80 pub tuning_operations: u64,
82 pub successful_tunings: u64,
84 pub failed_tunings: u64,
86 pub success_rate: f64,
88 pub avg_improvement: f64,
90 pub current_confidence: f64,
92 pub last_tuning: SystemTime,
94}
95
96impl Default for AdaptiveTuningStats {
97 fn default() -> Self {
98 Self {
99 tuning_operations: 0,
100 successful_tunings: 0,
101 failed_tunings: 0,
102 success_rate: 0.0,
103 avg_improvement: 0.0,
104 current_confidence: 0.5,
105 last_tuning: SystemTime::UNIX_EPOCH,
106 }
107 }
108}
109
110#[derive(Debug)]
115pub struct AdaptiveTuner {
116 config: TuningConfig,
118 performance_history: Arc<RwLock<VecDeque<PerformanceSnapshot>>>,
120 current_params: Arc<RwLock<TuningParameters>>,
122 stats: Arc<RwLock<AdaptiveTuningStats>>,
124 _tuning_interval: Option<Interval>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct TuningParameters {
131 pub l1_size_multiplier: f64,
133 pub l2_size_multiplier: f64,
135 pub promotion_threshold: u64,
137 pub eviction_aggressiveness: f64,
139 pub ttl_multiplier: f64,
141 pub preheating_aggressiveness: f64,
143}
144
145impl Default for TuningParameters {
146 fn default() -> Self {
147 Self {
148 l1_size_multiplier: 1.0,
149 l2_size_multiplier: 1.0,
150 promotion_threshold: 3,
151 eviction_aggressiveness: 0.5,
152 ttl_multiplier: 1.0,
153 preheating_aggressiveness: 0.3,
154 }
155 }
156}
157
158impl AdaptiveTuner {
159 pub fn new(config: TuningConfig) -> Self {
161 Self {
162 config,
163 performance_history: Arc::new(RwLock::new(VecDeque::new())),
164 current_params: Arc::new(RwLock::new(TuningParameters::default())),
165 stats: Arc::new(RwLock::new(AdaptiveTuningStats::default())),
166 _tuning_interval: None,
167 }
168 }
169
170 pub async fn record_performance(&self, stats: &UnifiedCacheStats) {
172 let snapshot = PerformanceSnapshot {
173 timestamp: SystemTime::now(),
174 hit_rate: stats.overall_stats.overall_hit_rate,
175 l1_hit_rate: if stats.l1_stats.hits + stats.l1_stats.misses > 0 {
176 stats.l1_stats.hits as f64 / (stats.l1_stats.hits + stats.l1_stats.misses) as f64
177 } else {
178 0.0
179 },
180 l2_hit_rate: if stats.l2_stats.hits + stats.l2_stats.misses > 0 {
181 stats.l2_stats.hits as f64 / (stats.l2_stats.hits + stats.l2_stats.misses) as f64
182 } else {
183 0.0
184 },
185 memory_usage_percent: if stats.l1_stats.max_usage_bytes > 0 {
186 stats.l1_stats.usage_bytes as f64 / stats.l1_stats.max_usage_bytes as f64 * 100.0
187 } else {
188 0.0
189 },
190 disk_usage_percent: if stats.l2_stats.max_usage_bytes > 0 {
191 stats.l2_stats.usage_bytes as f64 / stats.l2_stats.max_usage_bytes as f64 * 100.0
192 } else {
193 0.0
194 },
195 avg_get_latency_us: stats.performance_metrics.avg_get_latency_us,
196 avg_put_latency_us: stats.performance_metrics.avg_put_latency_us,
197 ops_per_second: stats.performance_metrics.ops_per_second,
198 eviction_rate: stats.l1_stats.evictions as f64 + stats.l2_stats.evictions as f64,
199 promotion_rate: stats.overall_stats.promotions as f64,
200 };
201
202 let mut history = self.performance_history.write().unwrap();
203 history.push_back(snapshot);
204
205 let max_history = self.config.performance_window_size;
207 while history.len() > max_history {
208 history.pop_front();
209 }
210 }
211
212 pub async fn analyze_and_tune(&self) -> Vec<TuningRecommendation> {
214 if !self.config.enable_adaptive_tuning {
215 return Vec::new();
216 }
217
218 let history = {
220 let history_guard = self.performance_history.read().unwrap();
221 if history_guard.len() < self.config.min_samples_for_tuning {
222 return Vec::new();
223 }
224 history_guard.clone()
225 };
226
227 let mut recommendations = Vec::new();
228
229 if let Some(hit_rate_rec) = self.analyze_hit_rate(&history) {
231 recommendations.push(hit_rate_rec);
232 }
233
234 if let Some(memory_rec) = self.analyze_memory_usage(&history) {
236 recommendations.push(memory_rec);
237 }
238
239 if let Some(latency_rec) = self.analyze_latency(&history) {
241 recommendations.push(latency_rec);
242 }
243
244 if let Some(eviction_rec) = self.analyze_eviction_patterns(&history) {
246 recommendations.push(eviction_rec);
247 }
248
249 self.apply_recommendations(&recommendations).await;
251
252 recommendations
253 }
254
255 fn analyze_hit_rate(
257 &self,
258 history: &VecDeque<PerformanceSnapshot>,
259 ) -> Option<TuningRecommendation> {
260 if history.len() < 3 {
261 return None;
262 }
263
264 let recent_hit_rate = history
265 .iter()
266 .rev()
267 .take(3)
268 .map(|s| s.hit_rate)
269 .sum::<f64>()
270 / 3.0;
271 let older_hit_rate = history.iter().take(3).map(|s| s.hit_rate).sum::<f64>() / 3.0;
272
273 if recent_hit_rate < older_hit_rate - 0.05 && recent_hit_rate < self.config.target_hit_rate
275 {
276 let current_params = self.current_params.read().unwrap();
277 let new_multiplier = (current_params.l1_size_multiplier * 1.2).min(2.0);
278
279 return Some(TuningRecommendation {
280 parameter: "l1_size_multiplier".to_string(),
281 current_value: current_params.l1_size_multiplier,
282 recommended_value: new_multiplier,
283 confidence: 0.8,
284 expected_improvement: 0.1,
285 reason: "Hit rate declining, increasing L1 cache size".to_string(),
286 });
287 }
288
289 None
290 }
291
292 fn analyze_memory_usage(
294 &self,
295 history: &VecDeque<PerformanceSnapshot>,
296 ) -> Option<TuningRecommendation> {
297 let avg_memory_usage =
298 history.iter().map(|s| s.memory_usage_percent).sum::<f64>() / history.len() as f64;
299
300 if avg_memory_usage > 90.0 {
302 let current_params = self.current_params.read().unwrap();
303 let new_aggressiveness = (current_params.eviction_aggressiveness + 0.1).min(1.0);
304
305 return Some(TuningRecommendation {
306 parameter: "eviction_aggressiveness".to_string(),
307 current_value: current_params.eviction_aggressiveness,
308 recommended_value: new_aggressiveness,
309 confidence: 0.9,
310 expected_improvement: 0.05,
311 reason: "High memory usage, increasing eviction aggressiveness".to_string(),
312 });
313 }
314
315 if avg_memory_usage < 50.0 {
317 let current_params = self.current_params.read().unwrap();
318 let new_aggressiveness = (current_params.eviction_aggressiveness - 0.1).max(0.1);
319
320 return Some(TuningRecommendation {
321 parameter: "eviction_aggressiveness".to_string(),
322 current_value: current_params.eviction_aggressiveness,
323 recommended_value: new_aggressiveness,
324 confidence: 0.7,
325 expected_improvement: 0.03,
326 reason: "Low memory usage, reducing eviction aggressiveness".to_string(),
327 });
328 }
329
330 None
331 }
332
333 fn analyze_latency(
335 &self,
336 history: &VecDeque<PerformanceSnapshot>,
337 ) -> Option<TuningRecommendation> {
338 if history.len() < 5 {
339 return None;
340 }
341
342 let recent_latency = history
343 .iter()
344 .rev()
345 .take(3)
346 .map(|s| s.avg_get_latency_us)
347 .sum::<f64>()
348 / 3.0;
349 let baseline_latency = history
350 .iter()
351 .take(3)
352 .map(|s| s.avg_get_latency_us)
353 .sum::<f64>()
354 / 3.0;
355
356 if recent_latency > baseline_latency * 1.5 && recent_latency > 1000.0 {
358 let current_params = self.current_params.read().unwrap();
359 let new_ttl_multiplier = (current_params.ttl_multiplier * 0.8).max(0.5);
360
361 return Some(TuningRecommendation {
362 parameter: "ttl_multiplier".to_string(),
363 current_value: current_params.ttl_multiplier,
364 recommended_value: new_ttl_multiplier,
365 confidence: 0.7,
366 expected_improvement: 0.2,
367 reason: "High latency detected, reducing TTL to improve cache freshness"
368 .to_string(),
369 });
370 }
371
372 None
373 }
374
375 fn analyze_eviction_patterns(
377 &self,
378 history: &VecDeque<PerformanceSnapshot>,
379 ) -> Option<TuningRecommendation> {
380 let avg_eviction_rate =
381 history.iter().map(|s| s.eviction_rate).sum::<f64>() / history.len() as f64;
382
383 if avg_eviction_rate > 100.0 {
385 let current_params = self.current_params.read().unwrap();
386 let new_multiplier = (current_params.l2_size_multiplier * 1.3).min(3.0);
387
388 return Some(TuningRecommendation {
389 parameter: "l2_size_multiplier".to_string(),
390 current_value: current_params.l2_size_multiplier,
391 recommended_value: new_multiplier,
392 confidence: 0.8,
393 expected_improvement: 0.15,
394 reason: "High eviction rate, increasing L2 cache size".to_string(),
395 });
396 }
397
398 None
399 }
400
401 async fn apply_recommendations(&self, recommendations: &[TuningRecommendation]) {
403 let mut params = self.current_params.write().unwrap();
404 let mut stats = self.stats.write().unwrap();
405
406 for rec in recommendations {
407 if rec.confidence >= self.config.min_confidence_for_auto_tuning {
408 match rec.parameter.as_str() {
409 "l1_size_multiplier" => params.l1_size_multiplier = rec.recommended_value,
410 "l2_size_multiplier" => params.l2_size_multiplier = rec.recommended_value,
411 "promotion_threshold" => {
412 params.promotion_threshold = rec.recommended_value as u64
413 }
414 "eviction_aggressiveness" => {
415 params.eviction_aggressiveness = rec.recommended_value
416 }
417 "ttl_multiplier" => params.ttl_multiplier = rec.recommended_value,
418 "preheating_aggressiveness" => {
419 params.preheating_aggressiveness = rec.recommended_value
420 }
421 _ => continue,
422 }
423
424 stats.tuning_operations += 1;
425 stats.last_tuning = SystemTime::now();
426 }
427 }
428 }
429
430 pub fn get_current_parameters(&self) -> TuningParameters {
432 self.current_params.read().unwrap().clone()
433 }
434
435 pub fn get_stats(&self) -> AdaptiveTuningStats {
437 let mut stats = self.stats.read().unwrap().clone();
438
439 if stats.tuning_operations > 0 {
441 stats.success_rate = stats.successful_tunings as f64 / stats.tuning_operations as f64;
442 }
443
444 stats
445 }
446
447 pub async fn record_tuning_outcome(&self, improved: bool, improvement: f64) {
449 let mut stats = self.stats.write().unwrap();
450
451 if improved {
452 stats.successful_tunings += 1;
453 stats.avg_improvement = (stats.avg_improvement * (stats.successful_tunings - 1) as f64
454 + improvement)
455 / stats.successful_tunings as f64;
456 } else {
457 stats.failed_tunings += 1;
458 }
459
460 if stats.tuning_operations > 0 {
462 stats.current_confidence =
463 stats.successful_tunings as f64 / stats.tuning_operations as f64;
464 }
465 }
466}