WASM Edge Computing: Routing Strategies
WASM Edge Computing: Routing Strategies
Part of: WASM Edge Computing User Guide
HeliosDB provides 5 routing strategies optimized for different use cases.
1. Round-Robin
Use Case: Uniform load distribution Latency: Variable Complexity: O(1)
let router = GeoRouter::new(RoutingStrategy::RoundRobin);
// Distributes requests evenly across all healthy nodeslet decision = router.route(client_location)?;Algorithm:
fn select_round_robin(&self, nodes: &[EdgeNode]) -> Option<EdgeNode> { let healthy_nodes: Vec<_> = nodes .iter() .filter(|n| n.has_capacity()) .collect();
if healthy_nodes.is_empty() { return None; }
let index = self.round_robin_index % healthy_nodes.len(); self.round_robin_index = (self.round_robin_index + 1) % healthy_nodes.len();
Some(healthy_nodes[index].clone())}Performance:
- Throughput: Excellent (even distribution)
- Latency: Poor (ignores geography)
- Failover: Good (automatic rotation)
2. Least-Connections
Use Case: Optimize for current load Latency: Variable Complexity: O(n)
let router = GeoRouter::new(RoutingStrategy::LeastConnections);
// Routes to node with lowest current loadlet decision = router.route(client_location)?;Algorithm:
fn select_least_connections(&self, nodes: &[EdgeNode]) -> Option<EdgeNode> { nodes .iter() .filter(|n| n.has_capacity()) .min_by_key(|n| n.current_load) .cloned()}Performance:
- Throughput: Excellent (prevents hotspots)
- Latency: Fair (considers load but not distance)
- Failover: Excellent (avoids overloaded nodes)
3. Weighted Distribution
Use Case: Capacity-based routing Latency: Variable Complexity: O(n)
let router = GeoRouter::new(RoutingStrategy::WeightedRoundRobin);
// Distributes based on available capacitylet decision = router.route(client_location)?;Algorithm:
fn select_weighted_round_robin(&self, nodes: &[EdgeNode]) -> Option<EdgeNode> { // Weight based on available capacity let weighted_nodes: Vec<_> = nodes .iter() .filter(|n| n.has_capacity()) .flat_map(|n| { let weight = n.capacity - n.current_load; vec![n.clone(); weight.max(1)] }) .collect();
if weighted_nodes.is_empty() { return None; }
let index = self.round_robin_index % weighted_nodes.len(); self.round_robin_index += 1;
Some(weighted_nodes[index].clone())}Performance:
- Throughput: Excellent (utilizes full capacity)
- Latency: Fair (ignores geography)
- Failover: Good (naturally favors healthy nodes)
4. Geographic (Nearest)
Use Case: Minimize latency Latency: Optimal Complexity: O(n)
let router = GeoRouter::new(RoutingStrategy::GeographicProximity);
let client_location = GeoLocation { latitude: 51.5074, // London longitude: -0.1278,};
// Routes to geographically nearest nodelet decision = router.route(client_location)?;
println!("Distance: {}km", decision.distance_km);println!("Estimated latency: {}ms", decision.estimated_latency_ms);Algorithm:
fn select_geographic_proximity( &self, nodes: &[EdgeNode], client_location: (f64, f64),) -> Option<EdgeNode> { let (client_lat, client_lon) = client_location;
nodes .iter() .filter(|n| n.has_capacity()) .min_by_key(|n| { let (node_lat, node_lon) = n.region.coordinates(); let distance = haversine_distance( client_lat, client_lon, node_lat, node_lon ); (distance * 1000.0) as u64 // Convert to meters }) .cloned()}
// Haversine distance calculationfn haversine_distance(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 { const EARTH_RADIUS_KM: f64 = 6371.0;
let lat1_rad = lat1.to_radians(); let lat2_rad = lat2.to_radians(); let delta_lat = (lat2 - lat1).to_radians(); let delta_lon = (lon2 - lon1).to_radians();
let a = (delta_lat / 2.0).sin().powi(2) + lat1_rad.cos() * lat2_rad.cos() * (delta_lon / 2.0).sin().powi(2); let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
EARTH_RADIUS_KM * c}Latency Estimation:
// ~1ms per 100km + base latencypub fn estimated_latency_ms(&self, other: &GeoLocation) -> f64 { const BASE_LATENCY_MS: f64 = 2.0; const MS_PER_100KM: f64 = 1.0;
let distance = self.distance_to(other); BASE_LATENCY_MS + (distance / 100.0) * MS_PER_100KM}Performance:
- Throughput: Good (may create hotspots)
- Latency: Excellent (minimizes network distance)
- Failover: Fair (requires health checks)
5. Latency-Based (Dynamic)
Use Case: Real-time performance optimization Latency: Best Complexity: O(n)
let router = GeoRouter::new(RoutingStrategy::Hybrid);
// Considers distance, latency, load, health, and cache hit ratelet decision = router.route(client_location)?;
println!("Hybrid score: {}", decision.routing_score);println!("Routing reason: {}", decision.reason);Algorithm:
fn route_hybrid( &self, nodes: &[EdgeNode], client_location: &GeoLocation,) -> RoutingDecision { let node = nodes .iter() .max_by(|a, b| { let score_a = self.calculate_hybrid_score(a, client_location); let score_b = self.calculate_hybrid_score(b, client_location); score_a.partial_cmp(&score_b).unwrap() }) .unwrap();
RoutingDecision { node_id: node.id.clone(), estimated_latency_ms: client_location.estimated_latency_ms(&node.location), routing_score: self.calculate_hybrid_score(node, client_location), reason: "Hybrid optimization".to_string(), }}
fn calculate_hybrid_score(&self, node: &EdgeNode, client: &GeoLocation) -> f64 { // Weights for different factors const DISTANCE_WEIGHT: f64 = 0.3; const LATENCY_WEIGHT: f64 = 0.3; const LOAD_WEIGHT: f64 = 0.2; const HEALTH_WEIGHT: f64 = 0.1; const CACHE_HIT_WEIGHT: f64 = 0.1;
let distance = client.distance_to(&node.location); let latency = client.estimated_latency_ms(&node.location);
// Normalize and invert scores (lower is better for distance/latency/load) let distance_score = 1.0 / (1.0 + distance / 1000.0); let latency_score = 1.0 / (1.0 + latency / 10.0); let load_score = 1.0 - node.utilization(); let health_score = node.health; let cache_hit_score = node.cache_hit_rate;
distance_score * DISTANCE_WEIGHT + latency_score * LATENCY_WEIGHT + load_score * LOAD_WEIGHT + health_score * HEALTH_WEIGHT + cache_hit_score * CACHE_HIT_WEIGHT}Performance:
- Throughput: Excellent (balances all factors)
- Latency: Excellent (optimizes for real performance)
- Failover: Excellent (comprehensive health tracking)
Tuning Weights:
// Optimize for latencyconst DISTANCE_WEIGHT: f64 = 0.4;const LATENCY_WEIGHT: f64 = 0.4;const LOAD_WEIGHT: f64 = 0.1;const HEALTH_WEIGHT: f64 = 0.05;const CACHE_HIT_WEIGHT: f64 = 0.05;
// Optimize for load balancingconst DISTANCE_WEIGHT: f64 = 0.2;const LATENCY_WEIGHT: f64 = 0.2;const LOAD_WEIGHT: f64 = 0.4;const HEALTH_WEIGHT: f64 = 0.1;const CACHE_HIT_WEIGHT: f64 = 0.1;
// Optimize for cache hitsconst DISTANCE_WEIGHT: f64 = 0.2;const LATENCY_WEIGHT: f64 = 0.2;const LOAD_WEIGHT: f64 = 0.1;const HEALTH_WEIGHT: f64 = 0.1;const CACHE_HIT_WEIGHT: f64 = 0.4;Strategy Comparison
| Strategy | Latency | Load Balance | Complexity | Best For |
|---|---|---|---|---|
| Round-Robin | Poor | Excellent | O(1) | Testing, uniform workloads |
| Least-Connections | Fair | Excellent | O(n) | Variable request costs |
| Weighted | Fair | Excellent | O(n) | Heterogeneous capacity |
| Geographic | Excellent | Fair | O(n) | Latency-sensitive apps |
| Hybrid | Excellent | Excellent | O(n) | Production (recommended) |
Navigation
- Previous: CDN Integration
- Next: State Synchronization
- Related: Edge Architecture