# Keepalive

Maintain persistent gRPC connections to NextBlock using JavaScript/TypeScript.

<pre class="language-typescript"><code class="lang-typescript"><strong>// Connection health tracking
</strong>interface ConnectionHealth {
  isHealthy: boolean;
  lastSuccessfulPing: Date;
  consecutiveFailures: number;
  totalPingsSent: number;
  totalPingsSuccessful: number;
  averagePingTime: number;
}

<strong>// Keepalive configuration
</strong>interface KeepaliveConfig {
  pingInterval: number;        // milliseconds
  maxConsecutiveFailures: number;
  reconnectDelay: number;      // milliseconds
  healthCheckEnabled: boolean;
  timeout: number;             // milliseconds
}

<strong>// Default keepalive configuration
</strong>const defaultKeepaliveConfig: KeepaliveConfig = {
  pingInterval: 60000,         // 60 seconds
  maxConsecutiveFailures: 3,
  reconnectDelay: 5000,        // 5 seconds
  healthCheckEnabled: true,
  timeout: 15000,              // 15 seconds
};

<strong>// Basic keepalive implementation
</strong>async function startKeepaliveTask(
  // nextblockClient: any, // Your generated gRPC client
  config: KeepaliveConfig = defaultKeepaliveConfig
): Promise&#x3C;void> {
  console.log(`Starting keepalive task with ${config.pingInterval}ms interval`);

  const keepaliveInterval = setInterval(async () => {
    try {
      const startTime = Date.now();

      /* Uncomment when you have the generated gRPC client
      try {
        await Promise.race([
          nextblockClient.ping({}),
          new Promise((_, reject) => 
            setTimeout(() => reject(new Error('Timeout')), config.timeout)
          )
        ]);

        const pingTime = Date.now() - startTime;
        console.log(`Keepalive ping successful (${pingTime}ms) at ${new Date().toTimeString()}`);

      } catch (error) {
        console.error('Keepalive ping failed:', error);
        // Optionally implement reconnection logic
        clearInterval(keepaliveInterval);
        return;
      }
      */

<strong>      // Mock ping for demonstration
</strong>      await new Promise(resolve => setTimeout(resolve, 50 + Math.random() * 100)); // 50-150ms delay
      const pingTime = Date.now() - startTime;
      console.log(`Mock keepalive ping successful (${pingTime}ms) at ${new Date().toTimeString()}`);

    } catch (error) {
      console.error('Keepalive task error:', error);
      clearInterval(keepaliveInterval);
    }
  }, config.pingInterval);

<strong>  // Handle graceful shutdown
</strong>  process.on('SIGINT', () => {
    clearInterval(keepaliveInterval);
    console.log('Keepalive task stopped');
    process.exit(0);
  });

<strong>  // Return a promise that never resolves (keeps running)
</strong>  return new Promise(() => {});
}

<strong>// Advanced keepalive manager
</strong>class KeepaliveManager {
  private config: KeepaliveConfig;
  private health: ConnectionHealth;
  private keepaliveInterval?: NodeJS.Timeout;
  private isRunning: boolean = false;

  constructor(
    // private nextblockClient: any, // Your generated gRPC client
    config: KeepaliveConfig = defaultKeepaliveConfig
  ) {
    this.config = config;
    this.health = {
      isHealthy: true,
      lastSuccessfulPing: new Date(),
      consecutiveFailures: 0,
      totalPingsSent: 0,
      totalPingsSuccessful: 0,
      averagePingTime: 0,
    };
  }

  async start(): Promise&#x3C;void> {
    if (this.isRunning) return;

    this.isRunning = true;
    this.keepaliveInterval = setInterval(
      () => this.sendPing(),
      this.config.pingInterval
    );
    
    console.log('Keepalive manager started');
  }

  async stop(): Promise&#x3C;void> {
    this.isRunning = false;
    
    if (this.keepaliveInterval) {
      clearInterval(this.keepaliveInterval);
      this.keepaliveInterval = undefined;
    }
    
    console.log('Keepalive manager stopped');
  }

  private async sendPing(): Promise&#x3C;void> {
    const startTime = Date.now();
    
    try {
      /* Uncomment when you have the generated gRPC client
      await Promise.race([
        this.nextblockClient.ping({}),
        new Promise((_, reject) => 
          setTimeout(() => reject(new Error('Timeout')), this.config.timeout)
        )
      ]);
      */

<strong>      // Mock ping delay
</strong>      await new Promise(resolve => 
        setTimeout(resolve, 50 + Math.random() * 100)
      );

      const pingTime = Date.now() - startTime;
      this.updateHealthSuccess(pingTime);

      console.log(
        `Keepalive ping successful (${pingTime}ms) - ` +
        `Health: ${this.getSuccessRate().toFixed(1)}%`
      );

    } catch (error) {
      this.updateHealthFailure();
      console.error(`Keepalive ping failed: ${error}`);

      if (!this.health.isHealthy) {
        await this.handleConnectionRecovery();
      }
    }
  }

  private updateHealthSuccess(pingTime: number): void {
    this.health.isHealthy = true;
    this.health.lastSuccessfulPing = new Date();
    this.health.consecutiveFailures = 0;
    this.health.totalPingsSent++;
    this.health.totalPingsSuccessful++;

<strong>    // Update average ping time
</strong>    if (this.health.totalPingsSuccessful === 1) {
      this.health.averagePingTime = pingTime;
    } else {
      this.health.averagePingTime = (
        (this.health.averagePingTime * (this.health.totalPingsSuccessful - 1) + pingTime) /
        this.health.totalPingsSuccessful
      );
    }
  }

  private updateHealthFailure(): void {
    this.health.consecutiveFailures++;
    this.health.totalPingsSent++;

    if (this.health.consecutiveFailures >= this.config.maxConsecutiveFailures) {
      this.health.isHealthy = false;
      console.warn(
        `Connection marked as unhealthy after ${this.health.consecutiveFailures} consecutive failures`
      );
    }
  }

  private async handleConnectionRecovery(): Promise&#x3C;void> {
    console.warn('Connection unhealthy, attempting recovery...');
    
<strong>    // Wait before attempting recovery
</strong>    await new Promise(resolve => setTimeout(resolve, this.config.reconnectDelay));
    
    try {
<strong>      // Implement connection recovery logic here
</strong>      console.log('Connection recovery attempted');
      
<strong>      // Reset some health metrics on successful recovery
</strong>      // this.health.consecutiveFailures = 0;
      
    } catch (error) {
      console.error('Connection recovery failed:', error);
    }
  }

  getHealth(): ConnectionHealth {
    return { ...this.health };
  }

  isHealthy(): boolean {
    return this.health.isHealthy;
  }

  private getSuccessRate(): number {
    if (this.health.totalPingsSent === 0) return 100;
    return (this.health.totalPingsSuccessful / this.health.totalPingsSent) * 100;
  }
}

<strong>// Connection manager with integrated keepalive
</strong>class ConnectionManagerWithKeepalive {
  private nextblockConfig: any;
  private keepaliveConfig: KeepaliveConfig;
  private keepaliveManager?: KeepaliveManager;
  // private client?: any;
  private isConnected: boolean = false;

  constructor(
    nextblockConfig: any,
    keepaliveConfig: KeepaliveConfig = defaultKeepaliveConfig
  ) {
    this.nextblockConfig = nextblockConfig;
    this.keepaliveConfig = keepaliveConfig;
  }

  async connect(): Promise&#x3C;boolean> {
    try {
<strong>      // Create connection (see connection.md)
</strong>      // const connection = await createNextBlockClient(this.nextblockConfig);
      // this.client = connection.client;

<strong>      // Test connection
</strong>      // await this.client.ping({});

      this.isConnected = true;
      console.log('Successfully connected to NextBlock');

<strong>      // Start keepalive
</strong>      this.keepaliveManager = new KeepaliveManager(
        // this.client,
        this.keepaliveConfig
      );
      await this.keepaliveManager.start();

      return true;

    } catch (error) {
      console.error('Failed to connect:', error);
      this.isConnected = false;
      return false;
    }
  }

  async disconnect(): Promise&#x3C;void> {
    if (this.keepaliveManager) {
      await this.keepaliveManager.stop();
    }

    this.isConnected = false;
    console.log('Disconnected from NextBlock');
  }

  getConnectionHealth(): ConnectionHealth | null {
    return this.keepaliveManager?.getHealth() || null;
  }

  get connected(): boolean {
    return this.isConnected;
  }

  // get grpcClient(): any {
  //   return this.client;
  // }
}

<strong>// Health monitoring and alerting
</strong>class HealthMonitor {
  private keepaliveManager: KeepaliveManager;
  private alertThresholds = {
    successRate: 90.0,        // Alert if success rate &#x3C; 90%
    avgPingTime: 1000,        // Alert if avg ping time > 1s
    consecutiveFailures: 2,   // Alert after 2 consecutive failures
  };
  private monitorInterval?: NodeJS.Timeout;

  constructor(keepaliveManager: KeepaliveManager) {
    this.keepaliveManager = keepaliveManager;
  }

  startMonitoring(checkInterval: number = 30000): void {
    this.monitorInterval = setInterval(() => {
      this.checkHealth();
    }, checkInterval);

    console.log(`Health monitoring started with ${checkInterval}ms interval`);
  }

  stopMonitoring(): void {
    if (this.monitorInterval) {
      clearInterval(this.monitorInterval);
      this.monitorInterval = undefined;
    }
    console.log('Health monitoring stopped');
  }

  private checkHealth(): void {
    const health = this.keepaliveManager.getHealth();

<strong>    // Check success rate
</strong>    const successRate = health.totalPingsSent > 0 
      ? (health.totalPingsSuccessful / health.totalPingsSent) * 100 
      : 100;

    if (successRate &#x3C; this.alertThresholds.successRate) {
      this.triggerAlert('Low success rate', `Success rate: ${successRate.toFixed(1)}%`);
    }

<strong>    // Check average ping time
</strong>    if (health.averagePingTime > this.alertThresholds.avgPingTime) {
      this.triggerAlert('High ping time', `Average ping time: ${health.averagePingTime.toFixed(1)}ms`);
    }

<strong>    // Check consecutive failures
</strong>    if (health.consecutiveFailures >= this.alertThresholds.consecutiveFailures) {
      this.triggerAlert('Connection issues', `Consecutive failures: ${health.consecutiveFailures}`);
    }
  }

  private triggerAlert(alertType: string, details: string): void {
    const timestamp = new Date().toISOString();
    console.warn(`🚨 HEALTH ALERT [${timestamp}] ${alertType}: ${details}`);

<strong>    // Implement additional alerting logic here
</strong>    // - Send notifications
    // - Post to monitoring systems
    // - Trigger recovery procedures
  }
}
</code></pre>

## Usage Examples

<pre class="language-typescript"><code class="lang-typescript"><strong>// Basic keepalive example
</strong>async function basicKeepaliveExample(): Promise&#x3C;void> {
  console.log('Starting basic keepalive example...');

<strong>  // Connect to NextBlock (see connection.md)
</strong>  // const config = configFromEnv();
  // const connection = await createNextBlockClient(config);

<strong>  // Start keepalive task
</strong>  const keepalivePromise = startKeepaliveTask(
    // connection.client,
    {
      pingInterval: 30000,     // Ping every 30 seconds
      maxConsecutiveFailures: 3,
      reconnectDelay: 5000,
      healthCheckEnabled: true,
      timeout: 15000,
    }
  );

  console.log('Application running with keepalive...');

<strong>  // Simulate application work
</strong>  setTimeout(() => {
    console.log('Application work completed, stopping...');
    process.exit(0);
  }, 300000); // Run for 5 minutes

  await keepalivePromise;
}

<strong>// Advanced keepalive with health monitoring
</strong>async function advancedKeepaliveExample(): Promise&#x3C;void> {
  console.log('Starting advanced keepalive example...');

<strong>  // Configuration
</strong>  const keepaliveConfig: KeepaliveConfig = {
    pingInterval: 30000,      // Ping every 30 seconds
    maxConsecutiveFailures: 3,
    reconnectDelay: 10000,    // Wait 10 seconds before reconnection
    healthCheckEnabled: true,
    timeout: 15000,
  };

<strong>  // Use connection manager with integrated keepalive
</strong>  const manager = new ConnectionManagerWithKeepalive(
    {}, // nextblockConfig placeholder
    keepaliveConfig
  );

  try {
    const connected = await manager.connect();

    if (connected) {
      console.log('Connected with keepalive enabled');

<strong>      // Start health monitoring
</strong>      const healthMonitor = new HealthMonitor(manager.keepaliveManager!);
      healthMonitor.startMonitoring(60000); // Check every minute

<strong>      // Simulate application work with periodic health checks
</strong>      for (let i = 0; i &#x3C; 10; i++) {
        await new Promise(resolve => setTimeout(resolve, 30000));

        const health = manager.getConnectionHealth();
        if (health) {
          console.log(`Connection health check ${i + 1}:`);
          console.log(`  Healthy: ${health.isHealthy}`);
          console.log(`  Success rate: ${((health.totalPingsSuccessful / Math.max(health.totalPingsSent, 1)) * 100).toFixed(1)}%`);
          console.log(`  Avg ping time: ${health.averagePingTime.toFixed(1)}ms`);
          console.log(`  Total pings: ${health.totalPingsSent}`);
        }
      }

      healthMonitor.stopMonitoring();

    } else {
      console.error('Failed to establish connection');
    }

  } catch (error) {
    console.error('Advanced keepalive example failed:', error);
  } finally {
    await manager.disconnect();
  }
}

<strong>// Connection pool with keepalive
</strong>class ConnectionPoolWithKeepalive {
  private connections: ConnectionManagerWithKeepalive[] = [];
  private currentIndex: number = 0;
  private poolSize: number;
  private nextblockConfig: any;
  private keepaliveConfig: KeepaliveConfig;

  constructor(
    nextblockConfig: any,
    keepaliveConfig: KeepaliveConfig,
    poolSize: number = 5
  ) {
    this.nextblockConfig = nextblockConfig;
    this.keepaliveConfig = keepaliveConfig;
    this.poolSize = poolSize;
  }

  async initialize(): Promise&#x3C;void> {
    console.log(`Initializing connection pool with ${this.poolSize} connections...`);

    const connectionPromises = Array.from({ length: this.poolSize }, async (_, i) => {
      const manager = new ConnectionManagerWithKeepalive(
        this.nextblockConfig,
        this.keepaliveConfig
      );

      const connected = await manager.connect();
      if (connected) {
        this.connections.push(manager);
        console.log(`Connection ${i + 1} established with keepalive`);
      } else {
        console.warn(`Failed to create connection ${i + 1}`);
      }
    });

    await Promise.all(connectionPromises);
    console.log(`Connection pool initialized with ${this.connections.length} connections`);
  }

  getConnection(): ConnectionManagerWithKeepalive {
    if (this.connections.length === 0) {
      throw new Error('No available connections in pool');
    }

    const connection = this.connections[this.currentIndex];
    this.currentIndex = (this.currentIndex + 1) % this.connections.length;
    return connection;
  }

  async closeAll(): Promise&#x3C;void> {
    await Promise.all(this.connections.map(conn => conn.disconnect()));
    this.connections = [];
    console.log('All connections closed');
  }

  getPoolHealth(): ConnectionHealth[] {
    return this.connections
      .map(conn => conn.getConnectionHealth())
      .filter((health): health is ConnectionHealth => health !== null);
  }
}

<strong>// Main example runner
</strong>async function main(): Promise&#x3C;void> {
  console.log('NextBlock Keepalive Examples');

  const choice = process.argv[2] || '1';

  switch (choice) {
    case '1':
      console.log('\n1. Basic Keepalive:');
      await basicKeepaliveExample();
      break;
    
    case '2':
      console.log('\n2. Advanced Keepalive with Monitoring:');
      await advancedKeepaliveExample();
      break;
    
    case '3':
      console.log('\n3. Connection Pool with Keepalive:');
      const pool = new ConnectionPoolWithKeepalive(
        {}, // config placeholder
        defaultKeepaliveConfig,
        3
      );
      
      await pool.initialize();
      
<strong>      // Use the pool for some time
</strong>      setTimeout(async () => {
        const poolHealth = pool.getPoolHealth();
        console.log(`Pool health summary: ${poolHealth.length} healthy connections`);
        await pool.closeAll();
      }, 60000);
      
      break;
    
    default:
      console.log('Usage: node keepalive.js [1|2|3]');
      console.log('  1: Basic keepalive');
      console.log('  2: Advanced keepalive with monitoring');
      console.log('  3: Connection pool with keepalive');
  }
}

if (require.main === module) {
  main().catch(console.error);
}

export {
  KeepaliveConfig,
  ConnectionHealth,
  KeepaliveManager,
  ConnectionManagerWithKeepalive,
  HealthMonitor,
  ConnectionPoolWithKeepalive,
  startKeepaliveTask,
};
</code></pre>

## Best Practices

1. **Appropriate intervals**: Use 30-60 second ping intervals for most applications
2. **Health monitoring**: Track connection health and implement alerting
3. **Graceful recovery**: Handle connection failures with exponential backoff
4. **Resource cleanup**: Always stop keepalive tasks when shutting down
5. **Timeout handling**: Set reasonable timeouts for ping requests
6. **Logging**: Log keepalive events for debugging and monitoring
7. **Integration**: Integrate keepalive with your connection management system
8. **Connection pooling**: Use connection pools for high-throughput applications


---

# 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/javascript/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.
