rust-mc-status 2.0.0

High-performance asynchronous Rust library for querying Minecraft server status (Java & Bedrock)
Documentation
//! Performance test example for the rust-mc-status library.
//!
//! This example measures the performance of various operations:
//! - Single server ping
//! - Batch server pings
//! - SRV lookup performance
//! - DNS caching effectiveness
//! - Connection time vs total time
//!
//! Run with: `cargo run --example performance_test --release`

use rust_mc_status::{McClient, ServerEdition, ServerInfo};
use std::time::{Duration, Instant};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== Performance Test for rust-mc-status ===\n");

    let client = McClient::new()
        .with_timeout(Duration::from_secs(10))
        .with_max_parallel(10);

    // Test servers
    let test_servers = vec![
        ("mc.hypixel.net", ServerEdition::Java),
        ("geo.hivebedrock.network:19132", ServerEdition::Bedrock),
    ];

    println!("Test Configuration:");
    println!("  Timeout: 10 seconds");
    println!("  Max parallel: 10");
    println!("  Test servers: {} servers\n", test_servers.len());

    println!("{}", "=".repeat(70));
    println!();

    // Test 1: Single server ping (cold cache)
    println!("Test 1: Single Server Ping (Cold Cache)");
    println!("{}", "-".repeat(70));
    for (address, edition) in &test_servers {
        let start = Instant::now();
        match client.ping(address, *edition).await {
            Ok(status) => {
                let elapsed = start.elapsed();
                println!(
                    "  Server: {} ({:?})",
                    address,
                    edition
                );
                println!("  Status: ✅ Online");
                println!("  Total time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
                println!("  Server latency: {:.2} ms", status.latency);
                println!("  DNS + Connection overhead: {:.2} ms", 
                    elapsed.as_secs_f64() * 1000.0 - status.latency);
            }
            Err(e) => {
                let elapsed = start.elapsed();
                println!("  Server: {} ({:?})", address, edition);
                println!("  Status: ❌ Error: {}", e);
                println!("  Time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
            }
        }
        println!();
    }

    println!("{}", "=".repeat(70));
    println!();

    // Test 2: Single server ping (warm cache)
    println!("Test 2: Single Server Ping (Warm Cache)");
    println!("{}", "-".repeat(70));
    for (address, edition) in &test_servers {
        let start = Instant::now();
        match client.ping(address, *edition).await {
            Ok(status) => {
                let elapsed = start.elapsed();
                println!(
                    "  Server: {} ({:?})",
                    address,
                    edition
                );
                println!("  Status: ✅ Online");
                println!("  Total time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
                println!("  Server latency: {:.2} ms", status.latency);
                println!("  DNS + Connection overhead: {:.2} ms", 
                    elapsed.as_secs_f64() * 1000.0 - status.latency);
                println!("  Cache hit: ✅ (DNS and SRV records cached)");
            }
            Err(e) => {
                let elapsed = start.elapsed();
                println!("  Server: {} ({:?})", address, edition);
                println!("  Status: ❌ Error: {}", e);
                println!("  Time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
            }
        }
        println!();
    }

    println!("{}", "=".repeat(70));
    println!();

    // Test 3: Batch ping performance
    println!("Test 3: Batch Ping Performance");
    println!("{}", "-".repeat(70));
    
    let servers: Vec<ServerInfo> = test_servers
        .iter()
        .map(|(address, edition)| ServerInfo {
            address: address.to_string(),
            edition: *edition,
        })
        .collect();

    println!("  Servers: {}", servers.len());
    println!("  Max parallel: {}\n", client.max_parallel());

    let start = Instant::now();
    let results = client.ping_many(&servers).await;
    let elapsed = start.elapsed();

    let successful = results.iter().filter(|(_, r)| r.is_ok()).count();
    let failed = results.len() - successful;

    println!("  Results:");
    println!("    Total time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
    println!("    Successful: {}/{}", successful, servers.len());
    println!("    Failed: {}", failed);
    println!("    Average time per server: {:.2} ms", 
        elapsed.as_secs_f64() * 1000.0 / servers.len() as f64);
    
    if successful > 0 {
        let avg_latency: f64 = results
            .iter()
            .filter_map(|(_, r)| r.as_ref().ok())
            .map(|s| s.latency)
            .sum::<f64>() / successful as f64;
        println!("    Average server latency: {:.2} ms", avg_latency);
    }

    println!();

    println!("{}", "=".repeat(70));
    println!();

    // Test 4: SRV lookup performance (Java only)
    println!("Test 4: SRV Lookup Performance");
    println!("{}", "-".repeat(70));
    
    let java_server = "mc.hypixel.net";
    
    // Test without port (SRV lookup)
    println!("  Test A: Without port (SRV lookup enabled)");
    let start = Instant::now();
    match client.ping_java(java_server).await {
        Ok(status) => {
            let elapsed = start.elapsed();
            println!("    Server: {}", java_server);
            println!("    Status: ✅ Online");
            println!("    Total time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
            println!("    Port used: {}", status.port);
        }
        Err(e) => {
            let elapsed = start.elapsed();
            println!("    Server: {}", java_server);
            println!("    Status: ❌ Error: {}", e);
            println!("    Time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
        }
    }
    println!();

    // Test with port (no SRV lookup)
    println!("  Test B: With port (SRV lookup disabled)");
    let start = Instant::now();
    match client.ping_java(&format!("{}:25565", java_server)).await {
        Ok(status) => {
            let elapsed = start.elapsed();
            println!("    Server: {}:25565", java_server);
            println!("    Status: ✅ Online");
            println!("    Total time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
            println!("    Port used: {}", status.port);
        }
        Err(e) => {
            let elapsed = start.elapsed();
            println!("    Server: {}:25565", java_server);
            println!("    Status: ❌ Error: {}", e);
            println!("    Time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
        }
    }
    println!();

    println!("{}", "=".repeat(70));
    println!();

    // Test 5: Concurrent requests performance
    println!("Test 5: Concurrent Requests Performance");
    println!("{}", "-".repeat(70));
    
    let concurrent_tests = vec![1, 5, 10, 20];
    
    for &parallel in &concurrent_tests {
        let test_client = McClient::new()
            .with_timeout(Duration::from_secs(10))
            .with_max_parallel(parallel);
        
        println!("  Max parallel: {}", parallel);
        
        let start = Instant::now();
        let results = test_client.ping_many(&servers).await;
        let elapsed = start.elapsed();
        
        let successful = results.iter().filter(|(_, r)| r.is_ok()).count();
        
        println!("    Servers: {}", servers.len());
        println!("    Successful: {}/{}", successful, servers.len());
        println!("    Total time: {:.2} ms", elapsed.as_secs_f64() * 1000.0);
        println!("    Time per server: {:.2} ms", 
            elapsed.as_secs_f64() * 1000.0 / servers.len() as f64);
        println!();
    }

    println!("{}", "=".repeat(70));
    println!();

    // Test 6: Multiple iterations (stress test)
    println!("Test 6: Stress Test (10 iterations)");
    println!("{}", "-".repeat(70));
    
    let iterations = 10;
    let mut times = Vec::new();
    let mut latencies = Vec::new();
    
    for i in 1..=iterations {
        let start = Instant::now();
        match client.ping(test_servers[0].0, test_servers[0].1).await {
            Ok(status) => {
                let elapsed = start.elapsed();
                times.push(elapsed.as_secs_f64() * 1000.0);
                latencies.push(status.latency);
                
                if i % 5 == 0 {
                    println!("  Iteration {}/{}: {:.2} ms (latency: {:.2} ms)", 
                        i, iterations, elapsed.as_secs_f64() * 1000.0, status.latency);
                }
            }
            Err(e) => {
                let elapsed = start.elapsed();
                times.push(elapsed.as_secs_f64() * 1000.0);
                println!("  Iteration {}/{}: Error - {} ({:.2} ms)", 
                    i, iterations, e, elapsed.as_secs_f64() * 1000.0);
            }
        }
        
        // Small delay to avoid rate limiting
        tokio::time::sleep(Duration::from_millis(100)).await;
    }
    
    if !times.is_empty() {
        let avg_time = times.iter().sum::<f64>() / times.len() as f64;
        let min_time = times.iter().fold(f64::INFINITY, |a, &b| a.min(b));
        let max_time = times.iter().fold(0.0_f64, |a, &b| a.max(b));
        
        println!();
        println!("  Statistics:");
        println!("    Average time: {:.2} ms", avg_time);
        println!("    Min time: {:.2} ms", min_time);
        println!("    Max time: {:.2} ms", max_time);
        
        if !latencies.is_empty() {
            let avg_latency = latencies.iter().sum::<f64>() / latencies.len() as f64;
            let min_latency = latencies.iter().fold(f64::INFINITY, |a, &b| a.min(b));
            let max_latency = latencies.iter().fold(0.0_f64, |a, &b| a.max(b));
            
            println!("    Average latency: {:.2} ms", avg_latency);
            println!("    Min latency: {:.2} ms", min_latency);
            println!("    Max latency: {:.2} ms", max_latency);
        }
    }

    println!();
    println!("{}", "=".repeat(70));
    println!();

    // Test 7: Cache statistics
    println!("Test 7: Cache Statistics");
    println!("{}", "-".repeat(70));
    
    let stats = client.cache_stats();
    println!("  DNS cache entries: {}", stats.dns_entries);
    println!("  SRV cache entries: {}", stats.srv_entries);
    println!("  Total cache entries: {}", stats.dns_entries + stats.srv_entries);
    println!();
    
    println!("  Clearing all caches...");
    client.clear_all_caches();
    let stats_after = client.cache_stats();
    println!("  After clearing:");
    println!("    DNS cache entries: {}", stats_after.dns_entries);
    println!("    SRV cache entries: {}", stats_after.srv_entries);

    println!();
    println!("{}", "=".repeat(70));
    println!();
    println!("Performance test completed!");
    println!();
    println!("Tips for better performance:");
    println!("  - Use batch queries (ping_many) for multiple servers");
    println!("  - Adjust max_parallel based on your needs");
    println!("  - DNS and SRV records are cached for 5 minutes");
    println!("  - Use explicit ports to skip SRV lookup if not needed");
    println!("  - Run with --release flag for optimal performance");
    println!("  - Use clear_all_caches() to free memory or force fresh lookups");

    Ok(())
}