Skip to content

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 nodes
let 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 load
let 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 capacity
let 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 node
let 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 calculation
fn 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 latency
pub 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 rate
let 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 latency
const 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 balancing
const 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 hits
const 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

StrategyLatencyLoad BalanceComplexityBest For
Round-RobinPoorExcellentO(1)Testing, uniform workloads
Least-ConnectionsFairExcellentO(n)Variable request costs
WeightedFairExcellentO(n)Heterogeneous capacity
GeographicExcellentFairO(n)Latency-sensitive apps
HybridExcellentExcellentO(n)Production (recommended)