use std::sync::Arc;
use tokio::sync::RwLock;
use tonic::transport::Channel;
// Connection health tracker
#[derive(Debug, Clone)]
pub struct ConnectionHealth {
pub is_healthy: bool,
pub last_successful_ping: chrono::DateTime<chrono::Utc>,
pub consecutive_failures: u32,
pub total_pings_sent: u64,
pub total_pings_successful: u64,
}
impl Default for ConnectionHealth {
fn default() -> Self {
Self {
is_healthy: true,
last_successful_ping: chrono::Utc::now(),
consecutive_failures: 0,
total_pings_sent: 0,
total_pings_successful: 0,
}
}
}
// Advanced keepalive manager
pub struct KeepaliveManager {
// client: Arc<RwLock<YourGeneratedApiClient>>,
health: Arc<RwLock<ConnectionHealth>>,
config: KeepaliveConfig,
}
#[derive(Debug, Clone)]
pub struct KeepaliveConfig {
pub ping_interval: Duration,
pub max_consecutive_failures: u32,
pub reconnect_delay: Duration,
pub health_check_enabled: bool,
}
impl Default for KeepaliveConfig {
fn default() -> Self {
Self {
ping_interval: Duration::from_secs(60),
max_consecutive_failures: 3,
reconnect_delay: Duration::from_secs(5),
health_check_enabled: true,
}
}
}
impl KeepaliveManager {
pub fn new(
// client: YourGeneratedApiClient,
config: KeepaliveConfig,
) -> Self {
Self {
// client: Arc::new(RwLock::new(client)),
health: Arc::new(RwLock::new(ConnectionHealth::default())),
config,
}
}
// Start keepalive task
pub async fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
let health = Arc::clone(&self.health);
// let client = Arc::clone(&self.client);
let config = self.config.clone();
tokio::spawn(async move {
let mut interval_timer = interval(config.ping_interval);
loop {
interval_timer.tick().await;
// Send ping and update health
Self::send_ping_and_update_health(
// Arc::clone(&client),
Arc::clone(&health),
&config,
).await;
}
});
Ok(())
}
// Send ping and update connection health
async fn send_ping_and_update_health(
// client: Arc<RwLock<YourGeneratedApiClient>>,
health: Arc<RwLock<ConnectionHealth>>,
config: &KeepaliveConfig,
) {
let ping_start = std::time::Instant::now();
// Update ping attempt counter
{
let mut health_guard = health.write().await;
health_guard.total_pings_sent += 1;
}
// Send ping
/* Uncomment when you have the generated API client
let ping_result = {
let mut client_guard = client.write().await;
client_guard.ping(Request::new(())).await
};
match ping_result {
Ok(_) => {
let ping_duration = ping_start.elapsed();
let mut health_guard = health.write().await;
health_guard.is_healthy = true;
health_guard.last_successful_ping = chrono::Utc::now();
health_guard.consecutive_failures = 0;
health_guard.total_pings_successful += 1;
println!("Keepalive ping successful ({}ms) - Health: {:.1}%",
ping_duration.as_millis(),
(health_guard.total_pings_successful as f64 /
health_guard.total_pings_sent as f64) * 100.0);
}
Err(e) => {
let mut health_guard = health.write().await;
health_guard.consecutive_failures += 1;
if health_guard.consecutive_failures >= config.max_consecutive_failures {
health_guard.is_healthy = false;
eprintln!("Connection marked as unhealthy after {} consecutive failures",
health_guard.consecutive_failures);
}
eprintln!("Keepalive ping failed (attempt {}): {}",
health_guard.consecutive_failures, e);
}
}
*/
// Mock ping result for demonstration
let ping_duration = ping_start.elapsed();
let mut health_guard = health.write().await;
health_guard.is_healthy = true;
health_guard.last_successful_ping = chrono::Utc::now();
health_guard.consecutive_failures = 0;
health_guard.total_pings_successful += 1;
println!("Mock keepalive ping successful ({}ms) - Health: {:.1}%",
ping_duration.as_millis(),
(health_guard.total_pings_successful as f64 /
health_guard.total_pings_sent as f64) * 100.0);
}
// Get connection health status
pub async fn get_health(&self) -> ConnectionHealth {
self.health.read().await.clone()
}
// Check if connection is healthy
pub async fn is_healthy(&self) -> bool {
self.health.read().await.is_healthy
}
}