use std::collections::HashMap;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
pub struct InstanceRegistry {
pub(super) entries: HashMap<String, RegistryEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct RegistryEntry {
pub(super) instance_id: String,
pub(super) service: String,
pub(super) endpoint: String,
pub(super) host_id: String,
pub(super) metadata: HashMap<String, String>,
pub(super) registered_at: DateTime<Utc>,
pub(super) last_heartbeat: DateTime<Utc>,
}
impl Default for InstanceRegistry {
fn default() -> Self {
Self::new()
}
}
impl InstanceRegistry {
pub fn new() -> Self {
Self {
entries: HashMap::new(),
}
}
pub fn register(
&mut self,
instance_id: &str,
service: &str,
endpoint: &str,
host_id: &str,
metadata: HashMap<String, String>,
) {
let now = Utc::now();
self.entries.insert(
instance_id.to_string(),
RegistryEntry {
instance_id: instance_id.to_string(),
service: service.to_string(),
endpoint: endpoint.to_string(),
host_id: host_id.to_string(),
metadata,
registered_at: now,
last_heartbeat: now,
},
);
}
pub fn deregister(&mut self, instance_id: &str) -> bool {
self.entries.remove(instance_id).is_some()
}
pub fn heartbeat(&mut self, instance_id: &str) -> bool {
if let Some(entry) = self.entries.get_mut(instance_id) {
entry.last_heartbeat = Utc::now();
true
} else {
false
}
}
pub fn endpoints(&self, service: &str) -> Vec<String> {
self.entries
.values()
.filter(|e| e.service == service)
.map(|e| e.endpoint.clone())
.collect()
}
pub fn instances_for_service(&self, service: &str) -> Vec<&str> {
self.entries
.values()
.filter(|e| e.service == service)
.map(|e| e.instance_id.as_str())
.collect()
}
pub fn instances_on_host(&self, host_id: &str) -> Vec<&str> {
self.entries
.values()
.filter(|e| e.host_id == host_id)
.map(|e| e.instance_id.as_str())
.collect()
}
pub fn evict_stale(&mut self, max_age: chrono::Duration) -> Vec<String> {
let cutoff = Utc::now() - max_age;
let stale: Vec<String> = self
.entries
.iter()
.filter(|(_, e)| e.last_heartbeat < cutoff)
.map(|(id, _)| id.clone())
.collect();
for id in &stale {
self.entries.remove(id);
}
stale
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn services(&self) -> Vec<String> {
let mut svcs: Vec<String> = self.entries.values().map(|e| e.service.clone()).collect();
svcs.sort();
svcs.dedup();
svcs
}
}