use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct Region(pub String);
impl Region {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for Region {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<&str> for Region {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for Region {
fn from(s: String) -> Self {
Self(s)
}
}
#[derive(Debug, Clone)]
pub struct WorkerRegion {
pub worker_id: Uuid,
pub region: Region,
pub latency_estimate_ms: u64,
}
impl WorkerRegion {
#[must_use]
pub fn new(worker_id: Uuid, region: Region, latency_estimate_ms: u64) -> Self {
Self {
worker_id,
region,
latency_estimate_ms,
}
}
}
#[derive(Debug, Clone)]
pub struct GeoPlacementPolicy {
pub preferred_region: Region,
pub fallback_regions: Vec<Region>,
pub max_latency_ms: u64,
}
impl GeoPlacementPolicy {
#[must_use]
pub fn preferred_only(region: Region, max_latency_ms: u64) -> Self {
Self {
preferred_region: region,
fallback_regions: Vec::new(),
max_latency_ms,
}
}
}
#[must_use]
pub fn select_worker_by_region(
workers: &[WorkerRegion],
policy: &GeoPlacementPolicy,
) -> Option<Uuid> {
if workers.is_empty() {
return None;
}
let best_in_region = |region: &Region| -> Option<&WorkerRegion> {
workers
.iter()
.filter(|w| &w.region == region && w.latency_estimate_ms <= policy.max_latency_ms)
.min_by_key(|w| w.latency_estimate_ms)
};
if let Some(w) = best_in_region(&policy.preferred_region) {
return Some(w.worker_id);
}
for fallback in &policy.fallback_regions {
if let Some(w) = best_in_region(fallback) {
return Some(w.worker_id);
}
}
if let Some(w) = workers
.iter()
.filter(|w| w.latency_estimate_ms <= policy.max_latency_ms)
.min_by_key(|w| w.latency_estimate_ms)
{
return Some(w.worker_id);
}
workers
.iter()
.min_by_key(|w| w.latency_estimate_ms)
.map(|w| w.worker_id)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_workers() -> Vec<WorkerRegion> {
vec![
WorkerRegion::new(
Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(),
Region::new("us-east-1"),
10,
),
WorkerRegion::new(
Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap(),
Region::new("eu-west-1"),
80,
),
WorkerRegion::new(
Uuid::parse_str("00000000-0000-0000-0000-000000000003").unwrap(),
Region::new("ap-southeast-1"),
150,
),
]
}
#[test]
fn test_geo_placement_prefers_primary_region() {
let workers = make_workers();
let policy = GeoPlacementPolicy {
preferred_region: Region::new("us-east-1"),
fallback_regions: vec![Region::new("eu-west-1")],
max_latency_ms: 200,
};
let selected = select_worker_by_region(&workers, &policy).expect("should select");
assert_eq!(
selected,
Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap()
);
}
#[test]
fn test_geo_placement_falls_back_correctly() {
let workers = make_workers();
let policy = GeoPlacementPolicy {
preferred_region: Region::new("us-west-2"),
fallback_regions: vec![Region::new("eu-west-1")],
max_latency_ms: 200,
};
let selected = select_worker_by_region(&workers, &policy).expect("should select");
assert_eq!(
selected,
Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap()
);
}
#[test]
fn test_geo_placement_any_within_latency_cap() {
let workers = make_workers();
let policy = GeoPlacementPolicy {
preferred_region: Region::new("unknown"),
fallback_regions: vec![],
max_latency_ms: 100,
};
let selected = select_worker_by_region(&workers, &policy).expect("should select");
assert_eq!(
selected,
Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap()
);
}
#[test]
fn test_geo_placement_last_resort_when_all_over_cap() {
let workers = vec![WorkerRegion::new(
Uuid::parse_str("00000000-0000-0000-0000-000000000004").unwrap(),
Region::new("far-away"),
999,
)];
let policy = GeoPlacementPolicy {
preferred_region: Region::new("us-east-1"),
fallback_regions: vec![],
max_latency_ms: 50, };
let selected = select_worker_by_region(&workers, &policy).expect("should select");
assert_eq!(
selected,
Uuid::parse_str("00000000-0000-0000-0000-000000000004").unwrap()
);
}
#[test]
fn test_geo_placement_empty_workers_returns_none() {
let policy = GeoPlacementPolicy {
preferred_region: Region::new("us-east-1"),
fallback_regions: vec![],
max_latency_ms: 200,
};
assert!(select_worker_by_region(&[], &policy).is_none());
}
#[test]
fn test_geo_placement_picks_lowest_latency_in_preferred() {
let id_slow = Uuid::parse_str("00000000-0000-0000-0000-000000000010").unwrap();
let id_fast = Uuid::parse_str("00000000-0000-0000-0000-000000000011").unwrap();
let workers = vec![
WorkerRegion::new(id_slow, Region::new("us-east-1"), 90),
WorkerRegion::new(id_fast, Region::new("us-east-1"), 20),
];
let policy = GeoPlacementPolicy {
preferred_region: Region::new("us-east-1"),
fallback_regions: vec![],
max_latency_ms: 200,
};
let selected = select_worker_by_region(&workers, &policy).expect("should select");
assert_eq!(selected, id_fast);
}
#[test]
fn test_region_display() {
let r = Region::new("eu-central-1");
assert_eq!(r.to_string(), "eu-central-1");
assert_eq!(r.as_str(), "eu-central-1");
}
#[test]
fn test_region_from_str() {
let r: Region = "us-west-2".into();
assert_eq!(r, Region::new("us-west-2"));
}
}