1use std::{
31 collections::HashMap,
32 sync::{Arc, Mutex},
33 time::{Duration, Instant},
34};
35use sysinfo::{CpuRefreshKind, MemoryRefreshKind, ProcessRefreshKind, RefreshKind, System};
36use tokio::task::JoinHandle;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub enum ResourceType {
41 Cpu,
43 Memory,
45 DiskIo,
47 NetworkBandwidth,
49}
50
51#[derive(Debug, Clone)]
53pub struct ResourceLimits {
54 pub max_cpu_percent: u32,
56 pub max_memory_bytes: u64,
58 pub max_disk_io_bps: u64,
60 pub max_network_bps: u64,
62 pub auto_throttle: bool,
64 pub throttle_threshold: f64,
66}
67
68impl Default for ResourceLimits {
69 #[inline]
70 fn default() -> Self {
71 Self {
72 max_cpu_percent: 80,
73 max_memory_bytes: 4 * 1024 * 1024 * 1024, max_disk_io_bps: 100 * 1024 * 1024, max_network_bps: 100 * 1024 * 1024, auto_throttle: true,
77 throttle_threshold: 0.8, }
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
84pub enum DegradationLevel {
85 None = 0,
87 Minor = 1,
89 Moderate = 2,
91 Severe = 3,
93 Critical = 4,
95}
96
97impl DegradationLevel {
98 #[must_use]
100 pub const fn description(&self) -> &'static str {
101 match self {
102 Self::None => "Normal operation",
103 Self::Minor => "Minor resource pressure - reducing non-critical operations",
104 Self::Moderate => "Moderate resource pressure - disabling background tasks",
105 Self::Severe => "Severe resource pressure - minimal operations only",
106 Self::Critical => "Critical resource exhaustion - emergency mode",
107 }
108 }
109
110 #[must_use]
112 #[inline]
113 pub const fn requires_action(&self) -> bool {
114 !matches!(self, Self::None)
115 }
116
117 #[must_use]
119 #[inline]
120 pub const fn cache_multiplier(&self) -> f64 {
121 match self {
122 Self::None => 1.0,
123 Self::Minor => 0.8,
124 Self::Moderate => 0.5,
125 Self::Severe => 0.2,
126 Self::Critical => 0.0,
127 }
128 }
129
130 #[must_use]
132 #[inline]
133 pub const fn concurrency_multiplier(&self) -> f64 {
134 match self {
135 Self::None => 1.0,
136 Self::Minor => 0.8,
137 Self::Moderate => 0.5,
138 Self::Severe => 0.25,
139 Self::Critical => 0.1,
140 }
141 }
142}
143
144impl Default for DegradationLevel {
145 #[inline]
146 fn default() -> Self {
147 Self::None
148 }
149}
150
151#[derive(Debug, Clone, Default)]
153pub struct ResourceStats {
154 pub used: u64,
156 pub limit: u64,
158 pub peak: u64,
160 pub allocations: u64,
162 pub deallocations: u64,
164 pub limit_exceeded_count: u64,
166}
167
168impl ResourceStats {
169 #[inline]
171 #[must_use]
172 pub fn utilization(&self) -> f64 {
173 if self.limit == 0 {
174 return 0.0;
175 }
176 (self.used as f64) / (self.limit as f64)
177 }
178
179 #[inline]
181 #[must_use]
182 pub fn exceeds_threshold(&self, threshold: f64) -> bool {
183 self.utilization() > threshold
184 }
185
186 #[inline]
188 #[must_use]
189 pub const fn is_at_limit(&self) -> bool {
190 self.used >= self.limit
191 }
192
193 #[inline]
195 #[must_use]
196 pub const fn available(&self) -> u64 {
197 self.limit.saturating_sub(self.used)
198 }
199}
200
201#[derive(Debug, Clone)]
203struct AllocationRecord {
204 amount: u64,
205 timestamp: Instant,
206}
207
208pub struct ResourceMonitor {
210 limits: ResourceLimits,
211 stats: Arc<Mutex<HashMap<ResourceType, ResourceStats>>>,
213 recent_allocations: Arc<Mutex<HashMap<ResourceType, Vec<AllocationRecord>>>>,
215 throttled: Arc<Mutex<HashMap<ResourceType, bool>>>,
217 system: Arc<Mutex<System>>,
219 degradation_level: Arc<Mutex<DegradationLevel>>,
221}
222
223impl ResourceMonitor {
224 #[must_use]
226 pub fn new(limits: ResourceLimits) -> Self {
227 let mut stats = HashMap::new();
228 stats.insert(
229 ResourceType::Cpu,
230 ResourceStats {
231 limit: u64::from(limits.max_cpu_percent),
232 ..Default::default()
233 },
234 );
235 stats.insert(
236 ResourceType::Memory,
237 ResourceStats {
238 limit: limits.max_memory_bytes,
239 ..Default::default()
240 },
241 );
242 stats.insert(
243 ResourceType::DiskIo,
244 ResourceStats {
245 limit: limits.max_disk_io_bps,
246 ..Default::default()
247 },
248 );
249 stats.insert(
250 ResourceType::NetworkBandwidth,
251 ResourceStats {
252 limit: limits.max_network_bps,
253 ..Default::default()
254 },
255 );
256
257 let system = System::new_with_specifics(
258 RefreshKind::nothing()
259 .with_memory(MemoryRefreshKind::everything())
260 .with_cpu(CpuRefreshKind::everything())
261 .with_processes(ProcessRefreshKind::everything()),
262 );
263
264 Self {
265 limits,
266 stats: Arc::new(Mutex::new(stats)),
267 recent_allocations: Arc::new(Mutex::new(HashMap::new())),
268 throttled: Arc::new(Mutex::new(HashMap::new())),
269 system: Arc::new(Mutex::new(system)),
270 degradation_level: Arc::new(Mutex::new(DegradationLevel::None)),
271 }
272 }
273
274 #[must_use]
276 #[inline]
277 pub fn can_allocate(&self, resource_type: ResourceType, amount: u64) -> bool {
278 let stats = self.stats.lock().unwrap();
279 if let Some(stat) = stats.get(&resource_type) {
280 stat.used + amount <= stat.limit
281 } else {
282 false
283 }
284 }
285
286 pub fn record_allocation(&mut self, resource_type: ResourceType, amount: u64) {
288 {
289 let mut stats = self.stats.lock().unwrap();
290 if let Some(stat) = stats.get_mut(&resource_type) {
291 stat.used += amount;
292 stat.allocations += 1;
293
294 if stat.used > stat.peak {
295 stat.peak = stat.used;
296 }
297
298 if stat.used > stat.limit {
299 stat.limit_exceeded_count += 1;
300 }
301 }
302 } {
306 let mut recent = self.recent_allocations.lock().unwrap();
307 recent
308 .entry(resource_type)
309 .or_default()
310 .push(AllocationRecord {
311 amount,
312 timestamp: Instant::now(),
313 });
314 } self.update_throttling(resource_type);
318 }
319
320 pub fn record_deallocation(&mut self, resource_type: ResourceType, amount: u64) {
322 {
323 let mut stats = self.stats.lock().unwrap();
324 if let Some(stat) = stats.get_mut(&resource_type) {
325 stat.used = stat.used.saturating_sub(amount);
326 stat.deallocations += 1;
327 }
328 } self.update_throttling(resource_type);
332 }
333
334 pub fn update_usage(&mut self, resource_type: ResourceType, current: u64) {
336 {
337 let mut stats = self.stats.lock().unwrap();
338 if let Some(stat) = stats.get_mut(&resource_type) {
339 stat.used = current;
340
341 if current > stat.peak {
342 stat.peak = current;
343 }
344
345 if current > stat.limit {
346 stat.limit_exceeded_count += 1;
347 }
348 }
349 } self.update_throttling(resource_type);
353 }
354
355 #[inline]
357 fn update_throttling(&self, resource_type: ResourceType) {
358 if !self.limits.auto_throttle {
359 return;
360 }
361
362 let stats = self.stats.lock().unwrap();
363 if let Some(stat) = stats.get(&resource_type) {
364 let should_throttle = stat.exceeds_threshold(self.limits.throttle_threshold);
365 let mut throttled = self.throttled.lock().unwrap();
366 throttled.insert(resource_type, should_throttle);
367 }
368 }
369
370 #[inline]
372 #[must_use]
373 pub fn is_throttled(&self, resource_type: ResourceType) -> bool {
374 self.throttled
375 .lock()
376 .unwrap()
377 .get(&resource_type)
378 .copied()
379 .unwrap_or(false)
380 }
381
382 #[must_use]
384 #[inline]
385 pub fn get_stats(&self, resource_type: ResourceType) -> Option<ResourceStats> {
386 self.stats.lock().unwrap().get(&resource_type).cloned()
387 }
388
389 #[must_use]
391 #[inline]
392 pub fn get_all_stats(&self) -> HashMap<ResourceType, ResourceStats> {
393 self.stats.lock().unwrap().clone()
394 }
395
396 #[must_use]
398 #[inline]
399 pub fn get_allocation_rate(&self, resource_type: ResourceType, window: Duration) -> u64 {
400 let recent = self.recent_allocations.lock().unwrap();
401 if let Some(records) = recent.get(&resource_type) {
402 let cutoff = Instant::now() - window;
403 let total: u64 = records
404 .iter()
405 .filter(|r| r.timestamp > cutoff)
406 .map(|r| r.amount)
407 .sum();
408
409 (total as f64 / window.as_secs_f64()) as u64
411 } else {
412 0
413 }
414 }
415
416 pub fn cleanup_old_records(&mut self, older_than: Duration) {
418 let mut recent = self.recent_allocations.lock().unwrap();
419 let cutoff = Instant::now() - older_than;
420
421 for records in recent.values_mut() {
422 records.retain(|r| r.timestamp > cutoff);
423 }
424 }
425
426 pub fn reset_stats(&mut self) {
428 let mut stats = self.stats.lock().unwrap();
429 for stat in stats.values_mut() {
430 stat.used = 0;
431 stat.peak = 0;
432 stat.allocations = 0;
433 stat.deallocations = 0;
434 stat.limit_exceeded_count = 0;
435 }
436 }
437
438 #[must_use]
440 #[inline]
441 pub fn health_score(&self) -> f64 {
442 let stats = self.stats.lock().unwrap();
443 let mut total_utilization = 0.0;
444 let mut count = 0;
445
446 for stat in stats.values() {
447 total_utilization += stat.utilization();
448 count += 1;
449 }
450
451 if count == 0 {
452 return 1.0;
453 }
454
455 1.0 - (total_utilization / count as f64)
457 }
458
459 #[must_use]
461 #[inline]
462 pub fn is_over_limit(&self) -> bool {
463 let stats = self.stats.lock().unwrap();
464 stats.values().any(|s| s.is_at_limit())
465 }
466
467 #[must_use]
472 #[inline]
473 pub fn calculate_degradation_level(&self) -> DegradationLevel {
474 let stats = self.stats.lock().unwrap();
475
476 let max_utilization = stats
478 .values()
479 .map(|s| s.utilization())
480 .fold(0.0f64, f64::max);
481
482 if max_utilization >= 0.95 {
484 DegradationLevel::Critical
485 } else if max_utilization >= 0.90 {
486 DegradationLevel::Severe
487 } else if max_utilization >= 0.85 {
488 DegradationLevel::Moderate
489 } else if max_utilization >= 0.80 {
490 DegradationLevel::Minor
491 } else {
492 DegradationLevel::None
493 }
494 }
495
496 pub fn update_degradation_level(&mut self) {
501 let new_level = self.calculate_degradation_level();
502 let mut current_level = self.degradation_level.lock().unwrap();
503
504 if new_level != *current_level {
505 *current_level = new_level;
506 eprintln!(
508 "Resource degradation level changed to: {:?} - {}",
509 new_level,
510 new_level.description()
511 );
512 }
513 }
514
515 #[must_use]
517 #[inline]
518 pub fn degradation_level(&self) -> DegradationLevel {
519 *self.degradation_level.lock().unwrap()
520 }
521
522 #[must_use]
524 #[inline]
525 pub fn should_accept_requests(&self) -> bool {
526 self.degradation_level() != DegradationLevel::Critical
527 }
528
529 #[must_use]
531 #[inline]
532 pub fn should_run_background_tasks(&self) -> bool {
533 matches!(
534 self.degradation_level(),
535 DegradationLevel::None | DegradationLevel::Minor
536 )
537 }
538
539 #[must_use]
541 #[inline]
542 pub fn recommended_cache_size(&self, base_size: usize) -> usize {
543 let multiplier = self.degradation_level().cache_multiplier();
544 ((base_size as f64) * multiplier) as usize
545 }
546
547 #[must_use]
549 #[inline]
550 pub fn recommended_concurrency(&self, base_concurrency: usize) -> usize {
551 let multiplier = self.degradation_level().concurrency_multiplier();
552 ((base_concurrency as f64) * multiplier).max(1.0) as usize
553 }
554
555 #[must_use]
561 pub fn sample_cpu_usage(&mut self) -> f32 {
562 let mut sys = self.system.lock().unwrap();
563 sys.refresh_cpu_usage();
564
565 let cpus = sys.cpus();
567 let cpu_usage = if cpus.is_empty() {
568 0.0
569 } else {
570 cpus.iter().map(|cpu| cpu.cpu_usage()).sum::<f32>() / cpus.len() as f32
571 };
572
573 drop(sys); self.update_usage(ResourceType::Cpu, cpu_usage as u64);
576
577 self.update_degradation_level();
579
580 cpu_usage
581 }
582
583 #[must_use]
589 pub fn sample_memory_usage(&mut self) -> u64 {
590 let mut sys = self.system.lock().unwrap();
591 sys.refresh_memory();
592
593 let used_memory = sys.used_memory();
595
596 drop(sys); self.update_usage(ResourceType::Memory, used_memory);
599
600 self.update_degradation_level();
602
603 used_memory
604 }
605
606 #[must_use]
612 pub fn sample_all_system_resources(&mut self) -> (f32, u64) {
613 let mut sys = self.system.lock().unwrap();
614 sys.refresh_cpu_usage();
615 sys.refresh_memory();
616
617 let cpus = sys.cpus();
619 let cpu_usage = if cpus.is_empty() {
620 0.0
621 } else {
622 cpus.iter().map(|cpu| cpu.cpu_usage()).sum::<f32>() / cpus.len() as f32
623 };
624 let memory_used = sys.used_memory();
625
626 drop(sys); self.update_usage(ResourceType::Cpu, cpu_usage as u64);
630 self.update_usage(ResourceType::Memory, memory_used);
631
632 self.update_degradation_level();
634
635 (cpu_usage, memory_used)
636 }
637
638 #[must_use]
640 #[inline]
641 pub fn total_system_memory(&self) -> u64 {
642 self.system.lock().unwrap().total_memory()
643 }
644
645 #[must_use]
647 #[inline]
648 pub fn cpu_count(&self) -> usize {
649 self.system.lock().unwrap().cpus().len()
650 }
651
652 #[must_use]
667 pub fn predict_usage(
668 &self,
669 resource_type: ResourceType,
670 window: Duration,
671 forecast_duration: Duration,
672 ) -> Option<u64> {
673 let recent = self.recent_allocations.lock().unwrap();
674 let records = recent.get(&resource_type)?;
675
676 if records.is_empty() {
677 return None;
678 }
679
680 let cutoff = Instant::now() - window;
681 let relevant_records: Vec<_> = records.iter().filter(|r| r.timestamp > cutoff).collect();
682
683 if relevant_records.len() < 2 {
684 return None; }
686
687 let n = relevant_records.len() as f64;
690 let base_time = relevant_records[0].timestamp;
691
692 let mut sum_x = 0.0;
693 let mut sum_y = 0.0;
694 let mut sum_xy = 0.0;
695 let mut sum_xx = 0.0;
696 let mut cumulative = 0u64;
697
698 for record in &relevant_records {
699 cumulative += record.amount;
700 let x = record.timestamp.duration_since(base_time).as_secs_f64();
701 let y = cumulative as f64;
702
703 sum_x += x;
704 sum_y += y;
705 sum_xy += x * y;
706 sum_xx += x * x;
707 }
708
709 let denominator = n * sum_xx - sum_x * sum_x;
711 if denominator.abs() < 1e-10 {
712 return None; }
714
715 let slope = (n * sum_xy - sum_x * sum_y) / denominator;
716 let intercept = (sum_y - slope * sum_x) / n;
717
718 let forecast_x = window.as_secs_f64() + forecast_duration.as_secs_f64();
720 let predicted = slope * forecast_x + intercept;
721
722 Some(predicted.max(0.0) as u64)
723 }
724
725 #[must_use]
740 pub fn should_proactive_throttle(
741 &self,
742 resource_type: ResourceType,
743 prediction_window: Duration,
744 forecast_duration: Duration,
745 ) -> bool {
746 let predicted_usage =
748 match self.predict_usage(resource_type, prediction_window, forecast_duration) {
749 Some(pred) => pred,
750 None => return false, };
752
753 let stats = self.stats.lock().unwrap();
755 let limit = match stats.get(&resource_type) {
756 Some(stat) => stat.limit,
757 None => return false,
758 };
759 drop(stats);
760
761 if limit == 0 {
763 return false;
764 }
765
766 let predicted_utilization = (predicted_usage as f64) / (limit as f64);
767
768 predicted_utilization > 0.7
771 }
772
773 #[must_use]
788 pub fn get_throttle_intensity(
789 &self,
790 resource_type: ResourceType,
791 prediction_window: Duration,
792 forecast_duration: Duration,
793 ) -> f64 {
794 let predicted_usage =
795 match self.predict_usage(resource_type, prediction_window, forecast_duration) {
796 Some(pred) => pred,
797 None => return 0.0, };
799
800 let stats = self.stats.lock().unwrap();
801 let limit = match stats.get(&resource_type) {
802 Some(stat) => stat.limit,
803 None => return 0.0,
804 };
805 drop(stats);
806
807 if limit == 0 {
808 return 0.0;
809 }
810
811 let predicted_utilization = (predicted_usage as f64) / (limit as f64);
812
813 if predicted_utilization < 0.7 {
819 0.0
820 } else if predicted_utilization < 0.85 {
821 (predicted_utilization - 0.7) / 0.15 * 0.5
822 } else if predicted_utilization < 0.95 {
823 0.5 + (predicted_utilization - 0.85) / 0.10 * 0.3
824 } else {
825 0.8 + ((predicted_utilization - 0.95) / 0.05 * 0.2).min(0.2)
826 }
827 }
828}
829
830pub struct MonitoringHandle {
834 task_handle: JoinHandle<()>,
835 stop_signal: Arc<Mutex<bool>>,
836}
837
838impl MonitoringHandle {
839 pub async fn stop(self) -> Result<(), tokio::task::JoinError> {
845 *self.stop_signal.lock().unwrap() = true;
847
848 self.task_handle.await
850 }
851
852 #[must_use]
854 pub fn is_running(&self) -> bool {
855 !self.task_handle.is_finished()
856 }
857}
858
859#[derive(Debug, Clone)]
861pub struct MonitoringConfig {
862 pub sample_interval: Duration,
864 pub auto_update_degradation: bool,
866 pub log_sampling: bool,
868}
869
870impl Default for MonitoringConfig {
871 fn default() -> Self {
872 Self {
873 sample_interval: Duration::from_secs(5),
874 auto_update_degradation: true,
875 log_sampling: false,
876 }
877 }
878}
879
880impl ResourceMonitor {
881 #[must_use]
915 pub fn start_monitoring(&self, config: MonitoringConfig) -> MonitoringHandle {
916 let stop_signal = Arc::new(Mutex::new(false));
917 let stop_signal_clone = Arc::clone(&stop_signal);
918
919 let stats = Arc::clone(&self.stats);
921 let system = Arc::clone(&self.system);
922 let degradation_level = Arc::clone(&self.degradation_level);
923
924 let task_handle = tokio::spawn(async move {
925 let mut interval = tokio::time::interval(config.sample_interval);
926
927 loop {
928 if *stop_signal_clone.lock().unwrap() {
930 break;
931 }
932
933 interval.tick().await;
935
936 let mut sys = system.lock().unwrap();
938 sys.refresh_cpu_usage();
939 sys.refresh_memory();
940
941 let cpus = sys.cpus();
943 let cpu_usage = if cpus.is_empty() {
944 0.0
945 } else {
946 cpus.iter().map(|cpu| cpu.cpu_usage()).sum::<f32>() / cpus.len() as f32
947 };
948 let memory_used = sys.used_memory();
949
950 drop(sys); {
954 let mut stats_map = stats.lock().unwrap();
955
956 if let Some(cpu_stats) = stats_map.get_mut(&ResourceType::Cpu) {
958 let cpu_value = cpu_usage as u64;
959 cpu_stats.used = cpu_value;
960 if cpu_value > cpu_stats.peak {
961 cpu_stats.peak = cpu_value;
962 }
963 if cpu_value > cpu_stats.limit {
964 cpu_stats.limit_exceeded_count += 1;
965 }
966 }
967
968 if let Some(mem_stats) = stats_map.get_mut(&ResourceType::Memory) {
970 mem_stats.used = memory_used;
971 if memory_used > mem_stats.peak {
972 mem_stats.peak = memory_used;
973 }
974 if memory_used > mem_stats.limit {
975 mem_stats.limit_exceeded_count += 1;
976 }
977 }
978 }
979
980 if config.auto_update_degradation {
982 let stats_map = stats.lock().unwrap();
983
984 let mut max_utilization = 0.0;
986 for stat in stats_map.values() {
987 let util = stat.utilization();
988 if util > max_utilization {
989 max_utilization = util;
990 }
991 }
992
993 let new_level = if max_utilization < 0.7 {
995 DegradationLevel::None
996 } else if max_utilization < 0.8 {
997 DegradationLevel::Minor
998 } else if max_utilization < 0.9 {
999 DegradationLevel::Moderate
1000 } else if max_utilization < 0.95 {
1001 DegradationLevel::Severe
1002 } else {
1003 DegradationLevel::Critical
1004 };
1005
1006 *degradation_level.lock().unwrap() = new_level;
1007 }
1008
1009 if config.log_sampling {
1011 eprintln!(
1012 "[ResourceMonitor] CPU: {:.1}%, Memory: {} bytes",
1013 cpu_usage, memory_used
1014 );
1015 }
1016 }
1017 });
1018
1019 MonitoringHandle {
1020 task_handle,
1021 stop_signal,
1022 }
1023 }
1024}
1025
1026#[cfg(test)]
1027mod tests {
1028 use super::*;
1029
1030 #[test]
1031 fn test_resource_stats_utilization() {
1032 let stats = ResourceStats {
1033 used: 50,
1034 limit: 100,
1035 ..Default::default()
1036 };
1037 assert_eq!(stats.utilization(), 0.5);
1038 }
1039
1040 #[test]
1041 fn test_resource_stats_available() {
1042 let stats = ResourceStats {
1043 used: 30,
1044 limit: 100,
1045 ..Default::default()
1046 };
1047 assert_eq!(stats.available(), 70);
1048 }
1049
1050 #[test]
1051 fn test_resource_stats_exceeds_threshold() {
1052 let stats = ResourceStats {
1053 used: 85,
1054 limit: 100,
1055 ..Default::default()
1056 };
1057 assert!(stats.exceeds_threshold(0.8));
1058 assert!(!stats.exceeds_threshold(0.9));
1059 }
1060
1061 #[test]
1062 fn test_can_allocate() {
1063 let limits = ResourceLimits {
1064 max_memory_bytes: 1000,
1065 ..Default::default()
1066 };
1067 let monitor = ResourceMonitor::new(limits);
1068
1069 assert!(monitor.can_allocate(ResourceType::Memory, 500));
1070 assert!(monitor.can_allocate(ResourceType::Memory, 1000));
1071 assert!(!monitor.can_allocate(ResourceType::Memory, 1001));
1072 }
1073
1074 #[test]
1075 fn test_record_allocation() {
1076 let limits = ResourceLimits {
1077 max_memory_bytes: 1000,
1078 ..Default::default()
1079 };
1080 let mut monitor = ResourceMonitor::new(limits);
1081
1082 monitor.record_allocation(ResourceType::Memory, 300);
1083
1084 let stats = monitor.get_stats(ResourceType::Memory).unwrap();
1085 assert_eq!(stats.used, 300);
1086 assert_eq!(stats.allocations, 1);
1087 assert_eq!(stats.peak, 300);
1088 }
1089
1090 #[test]
1091 fn test_record_deallocation() {
1092 let limits = ResourceLimits {
1093 max_memory_bytes: 1000,
1094 ..Default::default()
1095 };
1096 let mut monitor = ResourceMonitor::new(limits);
1097
1098 monitor.record_allocation(ResourceType::Memory, 500);
1099 monitor.record_deallocation(ResourceType::Memory, 200);
1100
1101 let stats = monitor.get_stats(ResourceType::Memory).unwrap();
1102 assert_eq!(stats.used, 300);
1103 assert_eq!(stats.deallocations, 1);
1104 }
1105
1106 #[test]
1107 fn test_peak_tracking() {
1108 let limits = ResourceLimits::default();
1109 let mut monitor = ResourceMonitor::new(limits);
1110
1111 monitor.record_allocation(ResourceType::Memory, 100);
1112 monitor.record_allocation(ResourceType::Memory, 200);
1113 monitor.record_deallocation(ResourceType::Memory, 150);
1114
1115 let stats = monitor.get_stats(ResourceType::Memory).unwrap();
1116 assert_eq!(stats.peak, 300);
1117 assert_eq!(stats.used, 150);
1118 }
1119
1120 #[test]
1121 fn test_limit_exceeded_count() {
1122 let limits = ResourceLimits {
1123 max_memory_bytes: 100,
1124 ..Default::default()
1125 };
1126 let mut monitor = ResourceMonitor::new(limits);
1127
1128 monitor.record_allocation(ResourceType::Memory, 120);
1129 monitor.record_allocation(ResourceType::Memory, 50);
1130
1131 let stats = monitor.get_stats(ResourceType::Memory).unwrap();
1132 assert_eq!(stats.limit_exceeded_count, 2);
1133 }
1134
1135 #[test]
1136 fn test_throttling() {
1137 let limits = ResourceLimits {
1138 max_memory_bytes: 100,
1139 auto_throttle: true,
1140 throttle_threshold: 0.8,
1141 ..Default::default()
1142 };
1143 let mut monitor = ResourceMonitor::new(limits);
1144
1145 assert!(!monitor.is_throttled(ResourceType::Memory));
1146
1147 monitor.record_allocation(ResourceType::Memory, 85);
1148 assert!(monitor.is_throttled(ResourceType::Memory));
1149
1150 monitor.record_deallocation(ResourceType::Memory, 30);
1151 assert!(!monitor.is_throttled(ResourceType::Memory));
1152 }
1153
1154 #[test]
1155 fn test_allocation_rate() {
1156 let limits = ResourceLimits::default();
1157 let mut monitor = ResourceMonitor::new(limits);
1158
1159 monitor.record_allocation(ResourceType::DiskIo, 1000);
1160 monitor.record_allocation(ResourceType::DiskIo, 2000);
1161
1162 std::thread::sleep(Duration::from_millis(100));
1163
1164 let rate = monitor.get_allocation_rate(ResourceType::DiskIo, Duration::from_secs(1));
1165 assert!(rate > 0);
1167 }
1168
1169 #[test]
1170 fn test_cleanup_old_records() {
1171 let limits = ResourceLimits::default();
1172 let mut monitor = ResourceMonitor::new(limits);
1173
1174 monitor.record_allocation(ResourceType::Memory, 100);
1175 std::thread::sleep(Duration::from_millis(150));
1176 monitor.record_allocation(ResourceType::Memory, 200);
1177
1178 monitor.cleanup_old_records(Duration::from_millis(100));
1179
1180 let rate = monitor.get_allocation_rate(ResourceType::Memory, Duration::from_secs(1));
1182 assert!(rate > 0);
1183 }
1184
1185 #[test]
1186 fn test_reset_stats() {
1187 let limits = ResourceLimits::default();
1188 let mut monitor = ResourceMonitor::new(limits);
1189
1190 monitor.record_allocation(ResourceType::Memory, 500);
1191 monitor.reset_stats();
1192
1193 let stats = monitor.get_stats(ResourceType::Memory).unwrap();
1194 assert_eq!(stats.used, 0);
1195 assert_eq!(stats.peak, 0);
1196 assert_eq!(stats.allocations, 0);
1197 }
1198
1199 #[test]
1200 fn test_health_score() {
1201 let limits = ResourceLimits {
1202 max_memory_bytes: 1000,
1203 max_cpu_percent: 100,
1204 ..Default::default()
1205 };
1206 let mut monitor = ResourceMonitor::new(limits);
1207
1208 assert!(monitor.health_score() > 0.99);
1210
1211 monitor.record_allocation(ResourceType::Memory, 500);
1215 monitor.update_usage(ResourceType::Cpu, 50);
1216
1217 let health = monitor.health_score();
1218 assert!(health > 0.7 && health < 0.8);
1221 }
1222
1223 #[test]
1224 fn test_is_over_limit() {
1225 let limits = ResourceLimits {
1226 max_memory_bytes: 100,
1227 ..Default::default()
1228 };
1229 let mut monitor = ResourceMonitor::new(limits);
1230
1231 assert!(!monitor.is_over_limit());
1232
1233 monitor.record_allocation(ResourceType::Memory, 150);
1234 assert!(monitor.is_over_limit());
1235 }
1236
1237 #[test]
1238 fn test_update_usage() {
1239 let limits = ResourceLimits::default();
1240 let mut monitor = ResourceMonitor::new(limits);
1241
1242 monitor.update_usage(ResourceType::Cpu, 75);
1243
1244 let stats = monitor.get_stats(ResourceType::Cpu).unwrap();
1245 assert_eq!(stats.used, 75);
1246 }
1247
1248 #[test]
1249 fn test_predict_usage_insufficient_data() {
1250 let limits = ResourceLimits::default();
1251 let monitor = ResourceMonitor::new(limits);
1252
1253 let prediction = monitor.predict_usage(
1255 ResourceType::Memory,
1256 Duration::from_secs(10),
1257 Duration::from_secs(5),
1258 );
1259 assert!(prediction.is_none());
1260 }
1261
1262 #[test]
1263 fn test_predict_usage_with_trend() {
1264 let limits = ResourceLimits {
1265 max_memory_bytes: 10000,
1266 ..Default::default()
1267 };
1268 let mut monitor = ResourceMonitor::new(limits);
1269
1270 for i in 0..5 {
1272 monitor.record_allocation(ResourceType::Memory, 100);
1273 if i < 4 {
1274 std::thread::sleep(Duration::from_millis(100));
1275 }
1276 }
1277
1278 let prediction = monitor.predict_usage(
1280 ResourceType::Memory,
1281 Duration::from_secs(1),
1282 Duration::from_millis(500),
1283 );
1284
1285 assert!(prediction.is_some());
1287 let predicted = prediction.unwrap();
1288 assert!(predicted > 0);
1290 }
1291
1292 #[test]
1293 fn test_should_proactive_throttle_no_data() {
1294 let limits = ResourceLimits::default();
1295 let monitor = ResourceMonitor::new(limits);
1296
1297 let should_throttle = monitor.should_proactive_throttle(
1299 ResourceType::Memory,
1300 Duration::from_secs(10),
1301 Duration::from_secs(5),
1302 );
1303 assert!(!should_throttle);
1304 }
1305
1306 #[test]
1307 fn test_should_proactive_throttle_high_prediction() {
1308 let limits = ResourceLimits {
1309 max_memory_bytes: 1000,
1310 ..Default::default()
1311 };
1312 let mut monitor = ResourceMonitor::new(limits);
1313
1314 for i in 0..5 {
1316 monitor.record_allocation(ResourceType::Memory, 200);
1317 if i < 4 {
1318 std::thread::sleep(Duration::from_millis(50));
1319 }
1320 }
1321
1322 let should_throttle = monitor.should_proactive_throttle(
1324 ResourceType::Memory,
1325 Duration::from_secs(1),
1326 Duration::from_millis(200),
1327 );
1328
1329 assert!(should_throttle);
1331 }
1332
1333 #[test]
1334 fn test_get_throttle_intensity_no_data() {
1335 let limits = ResourceLimits::default();
1336 let monitor = ResourceMonitor::new(limits);
1337
1338 let intensity = monitor.get_throttle_intensity(
1339 ResourceType::Memory,
1340 Duration::from_secs(10),
1341 Duration::from_secs(5),
1342 );
1343
1344 assert_eq!(intensity, 0.0);
1346 }
1347
1348 #[test]
1349 fn test_get_throttle_intensity_low_usage() {
1350 let limits = ResourceLimits {
1351 max_memory_bytes: 10000,
1352 ..Default::default()
1353 };
1354 let mut monitor = ResourceMonitor::new(limits);
1355
1356 for i in 0..3 {
1358 monitor.record_allocation(ResourceType::Memory, 100);
1359 if i < 2 {
1360 std::thread::sleep(Duration::from_millis(100));
1361 }
1362 }
1363
1364 let intensity = monitor.get_throttle_intensity(
1365 ResourceType::Memory,
1366 Duration::from_secs(1),
1367 Duration::from_millis(500),
1368 );
1369
1370 assert!(intensity < 0.3);
1372 }
1373
1374 #[test]
1375 fn test_degradation_level_calculation() {
1376 let limits = ResourceLimits {
1377 max_memory_bytes: 1000,
1378 ..Default::default()
1379 };
1380 let mut monitor = ResourceMonitor::new(limits);
1381
1382 monitor.record_allocation(ResourceType::Memory, 500); assert_eq!(
1385 monitor.calculate_degradation_level(),
1386 DegradationLevel::None
1387 );
1388
1389 monitor.record_allocation(ResourceType::Memory, 350); assert_eq!(
1391 monitor.calculate_degradation_level(),
1392 DegradationLevel::Moderate
1393 );
1394
1395 monitor.record_allocation(ResourceType::Memory, 60); assert_eq!(
1397 monitor.calculate_degradation_level(),
1398 DegradationLevel::Severe
1399 );
1400
1401 monitor.record_allocation(ResourceType::Memory, 50); assert_eq!(
1403 monitor.calculate_degradation_level(),
1404 DegradationLevel::Critical
1405 );
1406 }
1407
1408 #[tokio::test]
1409 async fn test_background_monitoring() {
1410 let limits = ResourceLimits::default();
1411 let monitor = ResourceMonitor::new(limits);
1412
1413 let config = MonitoringConfig {
1415 sample_interval: Duration::from_millis(100),
1416 auto_update_degradation: true,
1417 log_sampling: false,
1418 };
1419
1420 let handle = monitor.start_monitoring(config);
1421
1422 tokio::time::sleep(Duration::from_millis(300)).await;
1424
1425 assert!(handle.is_running());
1427
1428 let cpu_stats = monitor.get_stats(ResourceType::Cpu);
1430 let mem_stats = monitor.get_stats(ResourceType::Memory);
1431
1432 assert!(cpu_stats.is_some());
1433 assert!(mem_stats.is_some());
1434
1435 handle.stop().await.unwrap();
1437 }
1438
1439 #[tokio::test]
1440 async fn test_monitoring_handle_stop() {
1441 let limits = ResourceLimits::default();
1442 let monitor = ResourceMonitor::new(limits);
1443
1444 let config = MonitoringConfig {
1445 sample_interval: Duration::from_millis(100),
1446 ..Default::default()
1447 };
1448
1449 let handle = monitor.start_monitoring(config);
1450
1451 assert!(handle.is_running());
1453
1454 handle.stop().await.unwrap();
1456
1457 tokio::time::sleep(Duration::from_millis(50)).await;
1459 }
1460
1461 #[tokio::test]
1462 async fn test_sample_cpu_usage() {
1463 let limits = ResourceLimits::default();
1464 let mut monitor = ResourceMonitor::new(limits);
1465
1466 let cpu_usage = monitor.sample_cpu_usage();
1468
1469 assert!(cpu_usage >= 0.0);
1471 assert!(cpu_usage <= 100.0);
1472
1473 let stats = monitor.get_stats(ResourceType::Cpu).unwrap();
1475 assert_eq!(stats.used, cpu_usage as u64);
1476 }
1477
1478 #[tokio::test]
1479 async fn test_sample_memory_usage() {
1480 let limits = ResourceLimits::default();
1481 let mut monitor = ResourceMonitor::new(limits);
1482
1483 let memory_used = monitor.sample_memory_usage();
1485
1486 assert!(memory_used > 0);
1488
1489 let stats = monitor.get_stats(ResourceType::Memory).unwrap();
1491 assert_eq!(stats.used, memory_used);
1492 }
1493
1494 #[tokio::test]
1495 async fn test_sample_all_system_resources() {
1496 let limits = ResourceLimits::default();
1497 let mut monitor = ResourceMonitor::new(limits);
1498
1499 let (cpu_usage, memory_used) = monitor.sample_all_system_resources();
1501
1502 assert!(cpu_usage >= 0.0);
1504 assert!(cpu_usage <= 100.0);
1505 assert!(memory_used > 0);
1506
1507 let cpu_stats = monitor.get_stats(ResourceType::Cpu).unwrap();
1509 let mem_stats = monitor.get_stats(ResourceType::Memory).unwrap();
1510
1511 assert_eq!(cpu_stats.used, cpu_usage as u64);
1512 assert_eq!(mem_stats.used, memory_used);
1513 }
1514
1515 #[test]
1516 fn test_monitoring_config_default() {
1517 let config = MonitoringConfig::default();
1518
1519 assert_eq!(config.sample_interval, Duration::from_secs(5));
1520 assert!(config.auto_update_degradation);
1521 assert!(!config.log_sampling);
1522 }
1523
1524 #[tokio::test]
1525 async fn test_monitoring_updates_degradation() {
1526 let limits = ResourceLimits {
1527 max_cpu_percent: 10, max_memory_bytes: 1000,
1529 ..Default::default()
1530 };
1531 let monitor = ResourceMonitor::new(limits);
1532
1533 let config = MonitoringConfig {
1534 sample_interval: Duration::from_millis(100),
1535 auto_update_degradation: true,
1536 log_sampling: false,
1537 };
1538
1539 let handle = monitor.start_monitoring(config);
1540
1541 tokio::time::sleep(Duration::from_millis(350)).await;
1543
1544 let level = monitor.degradation_level();
1546 assert!(level >= DegradationLevel::None);
1547
1548 handle.stop().await.unwrap();
1550 }
1551
1552 #[test]
1553 fn test_total_system_memory() {
1554 let limits = ResourceLimits::default();
1555 let monitor = ResourceMonitor::new(limits);
1556
1557 let total_mem = monitor.total_system_memory();
1558
1559 assert!(total_mem > 0);
1561 }
1562
1563 #[test]
1564 fn test_cpu_count() {
1565 let limits = ResourceLimits::default();
1566 let monitor = ResourceMonitor::new(limits);
1567
1568 let cpu_count = monitor.cpu_count();
1569
1570 assert!(cpu_count > 0);
1572 }
1573}