rullst-core 12.2.0

Core runtime, routing engine, and telemetry for the Rullst framework suite.
Documentation
use serde::{Deserialize, Serialize};

/// Recommendation generated by the Agentic DevOps Engine
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InfrastructureRecommendation {
    /// Name of the monitored metric (e.g. tokio_tick_latency_us)
    pub metric_name: String,
    /// Current measured value of the metric
    pub current_value: f64,
    /// Recommended target value for optimal execution
    pub target_value: f64,
    /// Actionable recommendation for developer or autoscaler
    pub recommended_action: String,
    /// Urgency level ("LOW", "MEDIUM", "HIGH", "CRITICAL")
    pub urgency: String,
}

/// Agentic DevOps Engine analyzing system telemetry and proposing infrastructure tuning (Milestone 22)
#[derive(Debug, Default)]
pub struct DevOpsAgent;

impl DevOpsAgent {
    /// Create a new instance of the Agentic DevOps Engine
    pub fn new() -> Self {
        Self
    }

    /// Analyze runtime telemetry metrics and generate autonomous recommendations
    pub fn analyze_telemetry(
        &self,
        tokio_latency_us: u64,
        memory_rss_bytes: u64,
        db_pool_active_connections: u32,
        db_pool_max_connections: u32,
    ) -> Vec<InfrastructureRecommendation> {
        let mut recommendations = Vec::new();

        // 1. Analyze Tokio Runtime Event Loop Latency
        if tokio_latency_us > 10_000 {
            recommendations.push(InfrastructureRecommendation {
                metric_name: "tokio_tick_latency_us".to_string(),
                current_value: tokio_latency_us as f64,
                target_value: 1_000.0,
                recommended_action: "Increase Tokio worker threads or offload CPU-bound blocking tasks to tokio::task::spawn_blocking".to_string(),
                urgency: "HIGH".to_string(),
            });
        }

        // 2. Analyze RSS Memory Usage (Threshold > 500MB)
        let memory_mb = memory_rss_bytes as f64 / (1024.0 * 1024.0);
        if memory_mb > 500.0 {
            recommendations.push(InfrastructureRecommendation {
                metric_name: "memory_rss_mb".to_string(),
                current_value: memory_mb,
                target_value: 256.0,
                recommended_action: "Audit response payload buffer allocations or enable Brotli asset compression level 11".to_string(),
                urgency: "MEDIUM".to_string(),
            });
        }

        // 3. Analyze SQLx Database Connection Pool Saturation
        if db_pool_max_connections > 0 {
            let pool_utilization =
                db_pool_active_connections as f64 / db_pool_max_connections as f64;
            if pool_utilization > 0.85 {
                recommendations.push(InfrastructureRecommendation {
                    metric_name: "db_pool_utilization".to_string(),
                    current_value: pool_utilization * 100.0,
                    target_value: 60.0,
                    recommended_action: format!(
                        "Scale SQLx connection pool max_connections from {} to {}",
                        db_pool_max_connections,
                        db_pool_max_connections * 2
                    ),
                    urgency: "HIGH".to_string(),
                });
            }
        }

        recommendations
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_devops_agent_recommendations() {
        let agent = DevOpsAgent::new();

        // Test below/at boundary (no recommendations)
        let recs_clean = agent.analyze_telemetry(10_000, 500 * 1024 * 1024, 17, 20);
        assert_eq!(recs_clean.len(), 0);

        // Test high Tokio latency (> 10_000)
        let recs = agent.analyze_telemetry(15_000, 100 * 1024 * 1024, 5, 20);
        assert_eq!(recs.len(), 1);
        assert_eq!(recs[0].metric_name, "tokio_tick_latency_us");

        // Test high memory (> 500 MB)
        let recs_mem = agent.analyze_telemetry(500, 600 * 1024 * 1024, 5, 20);
        assert_eq!(recs_mem.len(), 1);
        assert_eq!(recs_mem[0].metric_name, "memory_rss_mb");

        // Test saturated DB pool (> 85%)
        let recs_db = agent.analyze_telemetry(500, 100 * 1024 * 1024, 18, 20);
        assert_eq!(recs_db.len(), 1);
        assert_eq!(recs_db[0].metric_name, "db_pool_utilization");
    }
}