127 lines
5.9 KiB
Rust
127 lines
5.9 KiB
Rust
#![deny(warnings)]
|
|
#![allow(dead_code)]
|
|
//! [TSM.ID].[11031972] -- Platform X Ecosystem
|
|
//! xcu-orbital-router -- Mesh Routing with Latency Scoring & Circuit Breaker
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Debug)]
|
|
pub enum OrbitalError { NoRoute(String), CircuitOpen(String), AllPathsFailed(String) }
|
|
impl std::fmt::Display for OrbitalError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self { Self::NoRoute(e) => write!(f, "No route: {e}"), Self::CircuitOpen(e) => write!(f, "Circuit: {e}"), Self::AllPathsFailed(e) => write!(f, "All failed: {e}") }
|
|
}
|
|
}
|
|
impl std::error::Error for OrbitalError {}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MeshNode { pub id: String, pub region: String, pub latency_ms: u32, pub capacity: u32, pub current_load: u32 }
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MeshEdge { pub from: String, pub to: String, pub latency_ms: u32, pub bandwidth_mbps: u32 }
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct CircuitBreaker { pub failures: u32, pub threshold: u32, pub is_open: bool, pub last_failure: u64 }
|
|
impl CircuitBreaker {
|
|
pub fn new(threshold: u32) -> Self { Self { failures: 0, threshold, is_open: false, last_failure: 0 } }
|
|
pub fn record_failure(&mut self, now: u64) { self.failures += 1; self.last_failure = now; if self.failures >= self.threshold { self.is_open = true; } }
|
|
pub fn record_success(&mut self) { self.failures = 0; self.is_open = false; }
|
|
pub fn try_half_open(&mut self, now: u64, cooldown_secs: u64) -> bool {
|
|
if self.is_open && now - self.last_failure > cooldown_secs { self.is_open = false; self.failures = self.threshold - 1; return true; }
|
|
!self.is_open
|
|
}
|
|
}
|
|
|
|
pub struct OrbitalRouter {
|
|
nodes: HashMap<String, MeshNode>,
|
|
edges: Vec<MeshEdge>,
|
|
breakers: HashMap<String, CircuitBreaker>,
|
|
breaker_threshold: u32,
|
|
}
|
|
|
|
impl OrbitalRouter {
|
|
pub fn new(breaker_threshold: u32) -> Self {
|
|
Self { nodes: HashMap::new(), edges: Vec::new(), breakers: HashMap::new(), breaker_threshold }
|
|
}
|
|
pub fn add_node(&mut self, node: MeshNode) { self.nodes.insert(node.id.clone(), node); }
|
|
pub fn add_edge(&mut self, edge: MeshEdge) { self.edges.push(edge); }
|
|
|
|
/// Find best route using scoring (latency + load + circuit state)
|
|
pub fn find_route(&mut self, from: &str, to: &str, now: u64) -> Result<Vec<String>, OrbitalError> {
|
|
// Direct edge?
|
|
let direct: Vec<&MeshEdge> = self.edges.iter().filter(|e| e.from == from && e.to == to).collect();
|
|
for edge in &direct {
|
|
let key = format!("{}->{}", edge.from, edge.to);
|
|
let breaker = self.breakers.entry(key).or_insert_with(|| CircuitBreaker::new(self.breaker_threshold));
|
|
if breaker.try_half_open(now, 30) {
|
|
return Ok(vec![from.into(), to.into()]);
|
|
}
|
|
}
|
|
// Multi-hop: find via intermediate nodes
|
|
let intermediates: Vec<String> = self.edges.iter()
|
|
.filter(|e| e.from == from)
|
|
.flat_map(|e1| {
|
|
self.edges.iter()
|
|
.filter(|e2| e2.from == e1.to && e2.to == to)
|
|
.map(move |e2| (e1.clone(), e2.clone()))
|
|
})
|
|
.map(|(e1, _)| e1.to.clone())
|
|
.collect();
|
|
|
|
if intermediates.is_empty() { return Err(OrbitalError::NoRoute(format!("{from} -> {to}"))); }
|
|
|
|
// Score intermediates by latency + load
|
|
let mut best_hop = intermediates[0].clone();
|
|
let mut best_score = f64::MAX;
|
|
for hop in &intermediates {
|
|
let key = format!("{from}->{hop}");
|
|
let breaker = self.breakers.entry(key).or_insert_with(|| CircuitBreaker::new(self.breaker_threshold));
|
|
if !breaker.try_half_open(now, 30) { continue; }
|
|
if let Some(node) = self.nodes.get(hop) {
|
|
let load_ratio = if node.capacity > 0 { node.current_load as f64 / node.capacity as f64 } else { 1.0 };
|
|
let score = node.latency_ms as f64 + load_ratio * 100.0;
|
|
if score < best_score { best_score = score; best_hop = hop.clone(); }
|
|
}
|
|
}
|
|
Ok(vec![from.into(), best_hop, to.into()])
|
|
}
|
|
|
|
pub fn report_failure(&mut self, from: &str, to: &str, now: u64) {
|
|
let key = format!("{from}->{to}");
|
|
let breaker = self.breakers.entry(key).or_insert_with(|| CircuitBreaker::new(self.breaker_threshold));
|
|
breaker.record_failure(now);
|
|
}
|
|
|
|
pub fn report_success(&mut self, from: &str, to: &str) {
|
|
let key = format!("{from}->{to}");
|
|
if let Some(breaker) = self.breakers.get_mut(&key) { breaker.record_success(); }
|
|
}
|
|
|
|
pub fn node_count(&self) -> usize { self.nodes.len() }
|
|
pub fn open_circuits(&self) -> usize { self.breakers.values().filter(|b| b.is_open).count() }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
fn setup() -> OrbitalRouter {
|
|
let mut r = OrbitalRouter::new(3);
|
|
r.add_node(MeshNode { id: "a".into(), region: "sg".into(), latency_ms: 5, capacity: 100, current_load: 10 });
|
|
r.add_node(MeshNode { id: "b".into(), region: "jp".into(), latency_ms: 20, capacity: 100, current_load: 50 });
|
|
r.add_node(MeshNode { id: "c".into(), region: "de".into(), latency_ms: 10, capacity: 100, current_load: 20 });
|
|
r.add_edge(MeshEdge { from: "a".into(), to: "b".into(), latency_ms: 15, bandwidth_mbps: 100 });
|
|
r.add_edge(MeshEdge { from: "a".into(), to: "c".into(), latency_ms: 10, bandwidth_mbps: 100 });
|
|
r.add_edge(MeshEdge { from: "c".into(), to: "b".into(), latency_ms: 20, bandwidth_mbps: 50 });
|
|
r
|
|
}
|
|
#[test]
|
|
fn test_direct_route() { let mut r = setup(); let route = r.find_route("a", "b", 1000).unwrap(); assert_eq!(route, vec!["a", "b"]); }
|
|
#[test]
|
|
fn test_circuit_breaker() {
|
|
let mut r = setup();
|
|
r.report_failure("a", "b", 100);
|
|
r.report_failure("a", "b", 101);
|
|
r.report_failure("a", "b", 102);
|
|
assert_eq!(r.open_circuits(), 1);
|
|
}
|
|
}
|