# Keepalive

Maintain persistent gRPC connections to NextBlock for optimal performance using Rust's Tonic client.

## Basic Keepalive Implementation

<pre class="language-rust"><code class="lang-rust"><strong>use std::time::Duration;
</strong>use tokio::time::{interval, sleep};
use tonic::Request;

<strong>// Send periodic ping to keep connection alive
</strong>async fn start_keepalive_task(
    // mut nextblock_client: YourGeneratedApiClient,
    ping_interval: Duration,
) -> Result&#x3C;(), Box&#x3C;dyn std::error::Error>> {
    let mut interval_timer = interval(ping_interval);
    
    println!("Starting keepalive task with interval: {:?}", ping_interval);
    
    loop {
        interval_timer.tick().await;
        
<strong>        // Send ping request
</strong>        /* Uncomment when you have the generated API client
        match nextblock_client.ping(Request::new(())).await {
            Ok(_) => {
                println!("Keepalive ping successful at {}", 
                        chrono::Utc::now().format("%H:%M:%S"));
            }
            Err(e) => {
                eprintln!("Keepalive ping failed: {}", e);
                // Optionally implement reconnection logic here
                break;
            }
        }
        */
        
<strong>        // Mock ping for demonstration
</strong>        println!("Mock keepalive ping sent at {}", 
                chrono::Utc::now().format("%H:%M:%S"));
    }
    
    Ok(())
}
</code></pre>

## Advanced Keepalive with Connection Management

<pre class="language-rust"><code class="lang-rust"><strong>use std::sync::Arc;
</strong>use tokio::sync::RwLock;
use tonic::transport::Channel;

<strong>// Connection health tracker
</strong>#[derive(Debug, Clone)]
pub struct ConnectionHealth {
    pub is_healthy: bool,
    pub last_successful_ping: chrono::DateTime&#x3C;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,
        }
    }
}

<strong>// Advanced keepalive manager
</strong>pub struct KeepaliveManager {
    // client: Arc&#x3C;RwLock&#x3C;YourGeneratedApiClient>>,
    health: Arc&#x3C;RwLock&#x3C;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,
        }
    }
    
<strong>    // Start keepalive task
</strong>    pub async fn start(&#x26;self) -> Result&#x3C;(), Box&#x3C;dyn std::error::Error>> {
        let health = Arc::clone(&#x26;self.health);
        // let client = Arc::clone(&#x26;self.client);
        let config = self.config.clone();
        
        tokio::spawn(async move {
            let mut interval_timer = interval(config.ping_interval);
            
            loop {
                interval_timer.tick().await;
                
<strong>                // Send ping and update health
</strong>                Self::send_ping_and_update_health(
                    // Arc::clone(&#x26;client),
                    Arc::clone(&#x26;health),
                    &#x26;config,
                ).await;
            }
        });
        
        Ok(())
    }
    
<strong>    // Send ping and update connection health
</strong>    async fn send_ping_and_update_health(
        // client: Arc&#x3C;RwLock&#x3C;YourGeneratedApiClient>>,
        health: Arc&#x3C;RwLock&#x3C;ConnectionHealth>>,
        config: &#x26;KeepaliveConfig,
    ) {
        let ping_start = std::time::Instant::now();
        
<strong>        // Update ping attempt counter
</strong>        {
            let mut health_guard = health.write().await;
            health_guard.total_pings_sent += 1;
        }
        
<strong>        // Send ping
</strong>        /* 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);
            }
        }
        */
        
<strong>        // Mock ping result for demonstration
</strong>        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);
    }
    
<strong>    // Get connection health status
</strong>    pub async fn get_health(&#x26;self) -> ConnectionHealth {
        self.health.read().await.clone()
    }
    
<strong>    // Check if connection is healthy
</strong>    pub async fn is_healthy(&#x26;self) -> bool {
        self.health.read().await.is_healthy
    }
}
</code></pre>

## Connection Recovery

<pre class="language-rust"><code class="lang-rust"><strong>// Automatic connection recovery
</strong>pub struct ConnectionManager {
    config: NextBlockConfig,
    keepalive_manager: Option&#x3C;KeepaliveManager>,
    // client: Option&#x3C;YourGeneratedApiClient>,
}

impl ConnectionManager {
    pub fn new(config: NextBlockConfig) -> Self {
        Self {
            config,
            keepalive_manager: None,
            // client: None,
        }
    }
    
<strong>    // Establish connection with automatic recovery
</strong>    pub async fn connect_with_recovery(&#x26;mut self) -> Result&#x3C;(), Box&#x3C;dyn std::error::Error>> {
        loop {
            match self.attempt_connection().await {
                Ok(()) => {
                    println!("Successfully connected to NextBlock");
                    
<strong>                    // Start keepalive
</strong>                    self.start_keepalive().await?;
                    
<strong>                    // Monitor connection health
</strong>                    self.monitor_connection_health().await?;
                    break;
                }
                Err(e) => {
                    eprintln!("Connection failed: {}. Retrying in 5 seconds...", e);
                    sleep(Duration::from_secs(5)).await;
                }
            }
        }
        
        Ok(())
    }
    
<strong>    // Attempt to establish connection
</strong>    async fn attempt_connection(&#x26;mut self) -> Result&#x3C;(), Box&#x3C;dyn std::error::Error>> {
        // let client = create_nextblock_client(self.config.clone()).await?;
        // self.client = Some(client);
        println!("Mock connection established");
        Ok(())
    }
    
<strong>    // Start keepalive task
</strong>    async fn start_keepalive(&#x26;mut self) -> Result&#x3C;(), Box&#x3C;dyn std::error::Error>> {
        let keepalive_config = KeepaliveConfig::default();
        
        // let keepalive_manager = KeepaliveManager::new(
        //     self.client.as_ref().unwrap().clone(),
        //     keepalive_config,
        // );
        
        // keepalive_manager.start().await?;
        // self.keepalive_manager = Some(keepalive_manager);
        
        println!("Keepalive task started");
        Ok(())
    }
    
<strong>    // Monitor connection health and trigger recovery
</strong>    async fn monitor_connection_health(&#x26;self) -> Result&#x3C;(), Box&#x3C;dyn std::error::Error>> {
        let mut health_check_interval = interval(Duration::from_secs(30));
        
        loop {
            health_check_interval.tick().await;
            
            if let Some(ref keepalive_manager) = self.keepalive_manager {
                if !keepalive_manager.is_healthy().await {
                    eprintln!("Connection unhealthy, attempting recovery...");
                    // Trigger reconnection logic
                    return Err("Connection lost".into());
                }
            }
        }
    }
}
</code></pre>

## Usage Example

<pre class="language-rust"><code class="lang-rust"><strong>#[tokio::main]
</strong>async fn main() -> Result&#x3C;(), Box&#x3C;dyn std::error::Error>> {
<strong>    // Basic keepalive usage
</strong>    // let config = NextBlockConfig::default();
    // let client = create_nextblock_client(config).await?;
    
<strong>    // Start simple keepalive task
</strong>    let keepalive_handle = tokio::spawn(async move {
        start_keepalive_task(
            // client,
            Duration::from_secs(60), // Ping every minute
        ).await
    });
    
<strong>    // Advanced usage with connection management
</strong>    let config = NextBlockConfig::default();
    let connection_manager = ConnectionManager::new(config);
    
    let manager_handle = tokio::spawn(async move {
        if let Err(e) = connection_manager.connect_with_recovery().await {
            eprintln!("Connection manager failed: {}", e);
        }
    });
    
<strong>    // Your main application logic here
</strong>    println!("Application running with keepalive...");
    
<strong>    // Wait for tasks
</strong>    tokio::select! {
        result = keepalive_handle => {
            if let Err(e) = result? {
                eprintln!("Keepalive task failed: {}", e);
            }
        }
        result = manager_handle => {
            if let Err(e) = result {
                eprintln!("Connection manager task failed: {}", e);
            }
        }
    }
    
    Ok(())
}
</code></pre>

## Best Practices

1. **Ping frequency**: Use 60-second intervals for most applications
2. **Health monitoring**: Track connection health and implement recovery
3. **Error handling**: Handle ping failures gracefully with exponential backoff
4. **Resource cleanup**: Properly close connections on shutdown
5. **Logging**: Log keepalive status for debugging and monitoring
6. **Timeouts**: Set appropriate timeouts for ping requests
7. **Reconnection logic**: Implement automatic reconnection on connection loss


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.nextblock.io/api/examples/rust/keepalive.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
