use crate::{ init::lifecycle::validate_server_health, lock_w, services::session::handle_disastrous_failure, services::ws::client::establish_websocket_connection, state::FDOLL, }; use tokio::time::{self, Duration}; use tokio_util::sync::CancellationToken; use tracing::{info, warn}; /// Starts a periodic health monitor that validates server connectivity /// and attempts to recover WebSocket connection if health checks fail. pub async fn start_health_monitor() { stop_health_monitor(); let cancel_token = CancellationToken::new(); { let mut guard = lock_w!(FDOLL); guard.network.health_monitor_token = Some(cancel_token.clone()); } tokio::spawn(async move { let mut interval = time::interval(Duration::from_secs(30)); // Check every 30 seconds let mut consecutive_failures = 0u8; const MAX_FAILURES: u8 = 3; loop { tokio::select! { _ = cancel_token.cancelled() => { info!("Health monitor stopped"); break; } _ = interval.tick() => { match validate_server_health().await { Ok(_) => { consecutive_failures = 0; } Err(e) => { consecutive_failures = consecutive_failures.saturating_add(1); warn!( "Health check failed ({}/{}): {}", consecutive_failures, MAX_FAILURES, e ); if consecutive_failures >= MAX_FAILURES { info!("Server appears unreachable after {} attempts, triggering recovery", MAX_FAILURES); handle_disastrous_failure(Some(format!( "Lost connection to server: {}", e ))) .await; break; } else { // Try to re-establish WebSocket connection info!("Attempting to re-establish WebSocket connection"); establish_websocket_connection().await; } } } } } } }); } /// Stops the health monitor loop. pub fn stop_health_monitor() { let mut guard = lock_w!(FDOLL); if let Some(token) = guard.network.health_monitor_token.take() { token.cancel(); } }