fix(nym-api): refactor simulation API to remove redundant data structures
- Remove old_method/new_method nested objects from NodeMethodComparison - Use direct fields for production_performance and simulated_performance - Update build_node_comparisons_from_single_dataset to create cleaner structure - Fix calculate_summary_statistics to work with new data model - Update all tests to match new structure The API now returns a single dataset with production performance values included directly, rather than separate old/new method objects. - Deduplicate routes, to control for deterministic routing
This commit is contained in:
Generated
+1
-1
@@ -4739,7 +4739,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.67"
|
||||
version = "1.1.68"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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<f64>,
|
||||
simulated_perf: f64,
|
||||
reliability_diff: Option<f64>,
|
||||
perf_delta_pct: Option<f64>,
|
||||
) -> 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,8 +181,14 @@ pub struct NodeMethodComparison {
|
||||
pub node_id: NodeId,
|
||||
pub node_type: String,
|
||||
pub identity_key: Option<String>,
|
||||
pub old_method: Option<NodePerformanceData>,
|
||||
pub new_method: Option<NodePerformanceData>,
|
||||
/// Production performance (old method) value
|
||||
pub production_performance: Option<f64>,
|
||||
/// 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<f64>,
|
||||
pub reliability_difference: Option<f64>, // new - old
|
||||
pub performance_delta_percentage: Option<f64>, // (new - old) / old * 100
|
||||
pub ranking_old_method: Option<i64>,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user