diff --git a/Cargo.lock b/Cargo.lock index c5b3fce611..efc80371af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4739,7 +4739,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "nym-api" -version = "1.1.67" +version = "1.1.68" dependencies = [ "anyhow", "async-trait", diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index 9cf5771fc0..a8bce92d02 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "nym-api" license = "GPL-3.0" -version = "1.1.68" +version = "1.1.69" authors.workspace = true edition = "2021" rust-version.workspace = true diff --git a/nym-api/src/simulation_api/handlers/mod.rs b/nym-api/src/simulation_api/handlers/mod.rs index fd7b3da675..2406ec68d4 100644 --- a/nym-api/src/simulation_api/handlers/mod.rs +++ b/nym-api/src/simulation_api/handlers/mod.rs @@ -607,39 +607,15 @@ fn build_node_comparisons_from_single_dataset( _ => None, }; - // Create comparison with old and new method data - let old_method = perf.production_performance.map(|prod_perf| NodePerformanceData { - node_id: perf.node_id, - node_type: perf.node_type.clone(), - identity_key: perf.identity_key.clone(), - reliability_score: prod_perf, - positive_samples: 0, - negative_samples: 0, - work_factor: perf.work_factor, - calculation_method: "production".to_string(), - calculated_at: perf.calculated_at, - production_performance: Some(prod_perf), - }); - - let new_method = NodePerformanceData { - node_id: perf.node_id, - node_type: perf.node_type.clone(), - identity_key: perf.identity_key.clone(), - reliability_score: perf.reliability_score, - positive_samples: perf.positive_samples, - negative_samples: perf.negative_samples, - work_factor: perf.work_factor, - calculation_method: perf.calculation_method, - calculated_at: perf.calculated_at, - production_performance: perf.production_performance, - }; - comparisons.push(NodeMethodComparison { node_id: perf.node_id, node_type: perf.node_type, identity_key: perf.identity_key, - old_method, - new_method: Some(new_method), + production_performance: perf.production_performance, + simulated_performance: perf.reliability_score, + positive_samples: perf.positive_samples, + negative_samples: perf.negative_samples, + work_factor: perf.work_factor, reliability_difference, performance_delta_percentage, ranking_old_method: ranking_old, @@ -661,12 +637,13 @@ fn calculate_summary_statistics(comparisons: &[NodeMethodComparison]) -> Compari let mut max_degradation: f64 = 0.0; for comparison in comparisons { - if let Some(old) = &comparison.old_method { - reliabilities_old.push(old.reliability_score); - } - if let Some(new) = &comparison.new_method { - reliabilities_new.push(new.reliability_score); + // Collect production (old method) reliability values + if let Some(old) = comparison.production_performance { + reliabilities_old.push(old); } + + // Collect simulated (new method) reliability values + reliabilities_new.push(comparison.simulated_performance); if let Some(diff) = comparison.reliability_difference { if diff > 0.001 { @@ -882,52 +859,35 @@ mod tests { use super::*; use crate::{simulation_api::models::{NodeMethodComparison, NodePerformanceData}}; - fn create_test_performance_data( + fn create_test_comparison( node_id: NodeId, - reliability: f64, - method: &str, - ) -> NodePerformanceData { - NodePerformanceData { + production_perf: Option, + simulated_perf: f64, + reliability_diff: Option, + perf_delta_pct: Option, + ) -> NodeMethodComparison { + NodeMethodComparison { node_id, node_type: "mixnode".to_string(), - identity_key: Some("test_key".to_string()), - reliability_score: reliability, + identity_key: Some(format!("key{}", node_id)), + production_performance: production_perf, + simulated_performance: simulated_perf, positive_samples: 100, negative_samples: 10, work_factor: Some(1.0), - calculation_method: method.to_string(), - calculated_at: 1234567890, - production_performance: None, + reliability_difference: reliability_diff, + performance_delta_percentage: perf_delta_pct, + ranking_old_method: None, + ranking_new_method: None, + ranking_delta: None, } } #[test] fn test_calculate_summary_statistics_basic() { let comparisons = vec![ - NodeMethodComparison { - node_id: 1, - node_type: "mixnode".to_string(), - identity_key: Some("key1".to_string()), - old_method: Some(create_test_performance_data(1, 80.0, "old")), - new_method: Some(create_test_performance_data(1, 90.0, "new")), - reliability_difference: Some(10.0), - performance_delta_percentage: Some(12.5), - ranking_old_method: Some(2), - ranking_new_method: Some(1), - ranking_delta: Some(-1), - }, - NodeMethodComparison { - node_id: 2, - node_type: "mixnode".to_string(), - identity_key: Some("key2".to_string()), - old_method: Some(create_test_performance_data(2, 70.0, "old")), - new_method: Some(create_test_performance_data(2, 65.0, "new")), - reliability_difference: Some(-5.0), - performance_delta_percentage: Some(-7.14), - ranking_old_method: Some(1), - ranking_new_method: Some(2), - ranking_delta: Some(1), - }, + create_test_comparison(1, Some(80.0), 90.0, Some(10.0), Some(12.5)), + create_test_comparison(2, Some(70.0), 65.0, Some(-5.0), Some(-7.14)), ]; let stats = calculate_summary_statistics(&comparisons); @@ -1056,8 +1016,8 @@ mod tests { // Find node 1 comparison let node1_comparison = comparisons.iter().find(|c| c.node_id == 1).unwrap(); - assert!(node1_comparison.old_method.is_some()); - assert!(node1_comparison.new_method.is_some()); + assert_eq!(node1_comparison.production_performance, Some(80.0)); + assert_eq!(node1_comparison.simulated_performance, 90.0); assert_eq!(node1_comparison.reliability_difference, Some(10.0)); // 90 - 80 assert_eq!(node1_comparison.performance_delta_percentage, Some(12.5)); // (90-80)/80 * 100 assert_eq!(node1_comparison.ranking_old_method, Some(1)); // 80% is best among nodes with production data @@ -1065,16 +1025,16 @@ mod tests { // Find node 2 comparison let node2_comparison = comparisons.iter().find(|c| c.node_id == 2).unwrap(); - assert!(node2_comparison.old_method.is_some()); - assert!(node2_comparison.new_method.is_some()); + assert_eq!(node2_comparison.production_performance, Some(70.0)); + assert_eq!(node2_comparison.simulated_performance, 65.0); assert_eq!(node2_comparison.reliability_difference, Some(-5.0)); // 65 - 70 assert_eq!(node2_comparison.ranking_old_method, Some(2)); // 70% is second assert_eq!(node2_comparison.ranking_new_method, Some(3)); // 65% is worst // Find node 3 comparison (no production data) let node3_comparison = comparisons.iter().find(|c| c.node_id == 3).unwrap(); - assert!(node3_comparison.old_method.is_none()); - assert!(node3_comparison.new_method.is_some()); + assert_eq!(node3_comparison.production_performance, None); + assert_eq!(node3_comparison.simulated_performance, 85.0); assert_eq!(node3_comparison.reliability_difference, None); assert_eq!(node3_comparison.ranking_old_method, None); // No production ranking assert_eq!(node3_comparison.ranking_new_method, Some(2)); // 85% is second best @@ -1096,8 +1056,18 @@ mod tests { available_methods: vec!["old".to_string(), "new".to_string()], }, node_performance: vec![ - create_test_performance_data(1, 80.0, "old"), - create_test_performance_data(1, 90.0, "new"), + NodePerformanceData { + node_id: 1, + node_type: "mixnode".to_string(), + identity_key: Some("test_key".to_string()), + reliability_score: 90.0, + positive_samples: 100, + negative_samples: 10, + work_factor: Some(1.0), + calculation_method: "new".to_string(), + calculated_at: 1234567890, + production_performance: Some(80.0), + }, ], performance_comparisons: vec![PerformanceComparisonData { node_id: 1, @@ -1120,7 +1090,7 @@ mod tests { assert!(csv.contains( "data_type,node_id,node_type,reliability_score,reward_amount,calculation_method" )); - assert!(csv.contains("performance,1,mixnode,80,")); - assert!(csv.contains("performance,1,mixnode,90,")); + assert!(csv.contains("performance,1,mixnode,90,")); // New method score + assert!(csv.contains("performance_comparison,1,mixnode,,80,")); // Performance comparison score } } diff --git a/nym-api/src/simulation_api/models.rs b/nym-api/src/simulation_api/models.rs index 8d89174b7c..43afedf405 100644 --- a/nym-api/src/simulation_api/models.rs +++ b/nym-api/src/simulation_api/models.rs @@ -181,8 +181,14 @@ pub struct NodeMethodComparison { pub node_id: NodeId, pub node_type: String, pub identity_key: Option, - pub old_method: Option, - pub new_method: Option, + /// Production performance (old method) value + pub production_performance: Option, + /// Simulated performance (new method) value + pub simulated_performance: f64, + /// Sample counts from route-based calculation + pub positive_samples: u32, + pub negative_samples: u32, + pub work_factor: Option, pub reliability_difference: Option, // new - old pub performance_delta_percentage: Option, // (new - old) / old * 100 pub ranking_old_method: Option, diff --git a/nym-api/src/support/storage/manager.rs b/nym-api/src/support/storage/manager.rs index abffacc659..3819a1b4db 100644 --- a/nym-api/src/support/storage/manager.rs +++ b/nym-api/src/support/storage/manager.rs @@ -1556,7 +1556,7 @@ impl StorageManager { let db_routes = sqlx::query_as!( RawRouteData, r#" - SELECT + SELECT DISTINCT, layer1 as "layer1", layer2 as "layer2", layer3 as "layer3", diff --git a/nym-network-monitor/src/main.rs b/nym-network-monitor/src/main.rs index 4fb7d6854a..5f744d388c 100644 --- a/nym-network-monitor/src/main.rs +++ b/nym-network-monitor/src/main.rs @@ -333,5 +333,6 @@ fn mixnet_debug_config(min_gateway_performance: u8) -> nym_client_core::config:: debug_config.cover_traffic.disable_loop_cover_traffic_stream = true; debug_config.topology.minimum_gateway_performance = min_gateway_performance; debug_config.traffic.deterministic_route_selection = true; + debug_config.traffic.average_packet_delay = Duration::from_millis(0); debug_config }