amadeus_node/utils/
system_metrics.rs

1/// System metrics for CPU and memory usage
2#[derive(Debug, Clone)]
3pub struct SystemStats {
4    pub cpu_usage: f32,
5    pub memory_usage: u64,
6    pub total_memory: u64,
7    pub cores_available: usize,
8}
9
10impl Default for SystemStats {
11    fn default() -> Self {
12        Self {
13            cpu_usage: 0.0,
14            memory_usage: 0,
15            total_memory: 16 * 1024 * 1024 * 1024, // Default to 16GB
16            cores_available: std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1),
17        }
18    }
19}
20
21#[cfg(feature = "system-metrics")]
22mod inner {
23    use std::sync::LazyLock;
24    use std::sync::Mutex;
25    use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
26    use sysinfo::{ProcessesToUpdate, System};
27    use tokio::time::{Duration, interval};
28
29    use super::*;
30
31    /// Global system monitor instance that persists between calls
32    static SYSTEM_MONITOR: LazyLock<Mutex<System>> = LazyLock::new(|| {
33        let mut system = System::new_all();
34        system.refresh_all();
35        Mutex::new(system)
36    });
37
38    /// Cached system stats - updated periodically by background task
39    static CACHED_CPU_USAGE: AtomicU32 = AtomicU32::new(0); // Store as u32 (percentage * 100 for precision)
40    static CACHED_MEMORY_USAGE: AtomicU64 = AtomicU64::new(0);
41    static CACHED_TOTAL_MEMORY: AtomicU64 = AtomicU64::new(0);
42    static STATS_UPDATER_STARTED: std::sync::Once = std::sync::Once::new();
43
44    /// Start the background system stats updater
45    fn ensure_stats_updater_started() {
46        STATS_UPDATER_STARTED.call_once(|| {
47            tokio::spawn(async {
48                let mut interval = interval(Duration::from_secs(1));
49
50                loop {
51                    interval.tick().await;
52
53                    if let Ok(current_pid) = sysinfo::get_current_pid() {
54                        if let Ok(mut system) = SYSTEM_MONITOR.lock() {
55                            system.refresh_processes(ProcessesToUpdate::Some(&[current_pid]), true);
56                            system.refresh_memory(); // Refresh system memory info
57
58                            if let Some(process) = system.process(current_pid) {
59                                let cpu_usage = process.cpu_usage();
60                                let memory_usage = process.memory();
61
62                                // Store CPU usage with precision (multiply by 100)
63                                CACHED_CPU_USAGE.store((cpu_usage * 100.0) as u32, Ordering::Relaxed);
64                                CACHED_MEMORY_USAGE.store(memory_usage, Ordering::Relaxed);
65                            }
66
67                            // Cache total system memory
68                            let total_memory = system.total_memory();
69                            CACHED_TOTAL_MEMORY.store(total_memory, Ordering::Relaxed);
70                        }
71                    }
72                }
73            });
74        });
75    }
76
77    /// Get current process CPU and memory usage from cache
78    pub fn get_system_stats() -> SystemStats {
79        ensure_stats_updater_started();
80
81        let cpu_usage = CACHED_CPU_USAGE.load(Ordering::Relaxed) as f32 / 100.0;
82        let memory_usage = CACHED_MEMORY_USAGE.load(Ordering::Relaxed);
83        let total_memory = CACHED_TOTAL_MEMORY.load(Ordering::Relaxed);
84        let cores_available = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
85
86        SystemStats { cpu_usage, memory_usage, total_memory, cores_available }
87    }
88}
89
90/// Get current system statistics (CPU, memory usage and available cores)
91/// Returns default values when system-metrics feature is disabled
92pub fn get_system_stats() -> SystemStats {
93    #[cfg(feature = "system-metrics")]
94    {
95        inner::get_system_stats()
96    }
97    #[cfg(not(feature = "system-metrics"))]
98    {
99        SystemStats::default()
100    }
101}