mocra_core/common/
registry.rs1use crate::cacheable::{CacheAble, CacheService};
2use serde::{Deserialize, Serialize};
3use std::sync::Arc;
4use std::time::Duration;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct NodeInfo {
9 pub id: String,
11 pub ip: String,
13 pub hostname: String,
15 pub last_heartbeat: u64,
17 pub version: String,
19}
20
21impl CacheAble for NodeInfo {
22 fn field() -> impl AsRef<str> {
23 "nodes".to_string()
24 }
25}
26
27pub struct NodeRegistry {
31 cache: Arc<CacheService>,
32 node_id: String,
33 ttl: Duration,
34}
35
36impl NodeRegistry {
37 pub fn new(cache: Arc<CacheService>, node_id: String, ttl: Duration) -> Self {
39 Self {
40 cache,
41 node_id,
42 ttl,
43 }
44 }
45
46 pub async fn heartbeat(
50 &self,
51 ip: &str,
52 hostname: &str,
53 version: &str,
54 ) -> Result<(), crate::errors::Error> {
55 let now = std::time::SystemTime::now()
56 .duration_since(std::time::UNIX_EPOCH)
57 .unwrap()
58 .as_secs();
59 let info = NodeInfo {
60 id: self.node_id.clone(),
61 ip: ip.to_string(),
62 hostname: hostname.to_string(),
63 last_heartbeat: now,
64 version: version.to_string(),
65 };
66
67 info.send_with_ttl(&self.node_id, &self.cache, self.ttl)
69 .await
70 .map_err(crate::errors::Error::from)?;
71
72 let index_key = format!("{}:registry:nodes_index", self.cache.namespace());
75 let _ = self
76 .cache
77 .zadd(&index_key, now as f64, self.node_id.as_bytes())
78 .await
79 .map_err(crate::errors::Error::from)?;
80
81 Ok(())
82 }
83
84 pub async fn get_active_nodes(&self) -> Result<Vec<NodeInfo>, crate::errors::Error> {
86 let now = std::time::SystemTime::now()
87 .duration_since(std::time::UNIX_EPOCH)
88 .unwrap()
89 .as_secs();
90 let cutoff = now.saturating_sub(self.ttl.as_secs());
91 let index_key = format!("{}:registry:nodes_index", self.cache.namespace());
92
93 let _ = self
96 .cache
97 .zremrangebyscore(&index_key, f64::NEG_INFINITY, cutoff as f64)
98 .await;
99
100 let active_ids_bytes = self
102 .cache
103 .zrangebyscore(&index_key, cutoff as f64, f64::INFINITY)
104 .await
105 .map_err(crate::errors::Error::from)?;
106
107 if active_ids_bytes.is_empty() {
108 return Ok(Vec::new());
109 }
110
111 let mut keys = Vec::with_capacity(active_ids_bytes.len());
112 for id_bytes in active_ids_bytes {
113 let id = String::from_utf8(id_bytes).unwrap_or_default();
114 if !id.is_empty() {
115 keys.push(format!("{}:nodes:{}", self.cache.namespace(), id));
118 }
119 }
120
121 if keys.is_empty() {
122 return Ok(Vec::new());
123 }
124
125 let key_refs: Vec<&str> = keys.iter().map(|s| s.as_str()).collect();
126 let results = self
127 .cache
128 .mget(&key_refs)
129 .await
130 .map_err(crate::errors::Error::from)?;
131
132 let mut nodes = Vec::with_capacity(results.len());
133 for bytes in results.into_iter().flatten() {
134 if let Ok(node) = serde_json::from_slice::<NodeInfo>(&bytes) {
135 nodes.push(node);
136 }
137 }
138 Ok(nodes)
139 }
140
141 pub async fn deregister(&self) -> Result<(), crate::errors::Error> {
142 NodeInfo::delete(&self.node_id, &self.cache)
143 .await
144 .map_err(crate::errors::Error::from)
145 }
146}