1use serde::{Deserialize, Serialize};
12use std::collections::{BTreeMap, HashMap, HashSet};
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
15pub struct PeerId(pub String);
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct IndexVectorClock {
19 pub clock: BTreeMap<String, u64>,
20}
21
22impl IndexVectorClock {
23 pub fn new() -> Self {
24 IndexVectorClock {
25 clock: BTreeMap::new(),
26 }
27 }
28
29 pub fn increment(&mut self, node_id: &str) {
30 *self.clock.entry(node_id.to_string()).or_insert(0) += 1;
31 }
32
33 pub fn merge(&mut self, other: &IndexVectorClock) {
34 for (node, timestamp) in &other.clock {
35 let current = self.clock.entry(node.clone()).or_insert(0);
36 *current = (*current).max(*timestamp);
37 }
38 }
39
40 pub fn happens_before(&self, other: &IndexVectorClock) -> bool {
41 let mut less = false;
42 for (node, timestamp) in &self.clock {
43 let other_ts = other.clock.get(node).copied().unwrap_or(0);
44 if timestamp > &other_ts {
45 return false;
46 }
47 if timestamp < &other_ts {
48 less = true;
49 }
50 }
51 less
52 }
53
54 pub fn concurrent_with(&self, other: &IndexVectorClock) -> bool {
55 !self.happens_before(other) && !other.happens_before(self)
56 }
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct IndexReplicationState {
61 pub index_name: String,
62 pub local_version: IndexVectorClock,
63 pub peer_versions: HashMap<String, IndexVectorClock>,
64 pub replicated_peers: HashSet<String>,
65 pub pending_sync: Vec<IndexOperation>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub enum IndexOperation {
70 CreateIndex {
71 name: String,
72 field: String,
73 index_type: String,
74 },
75 UpdateIndex {
76 name: String,
77 record_id: String,
78 value: String,
79 },
80 DeleteIndex {
81 name: String,
82 },
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct IndexSyncMessage {
87 pub source_peer: String,
88 pub target_peer: String,
89 pub index_name: String,
90 pub version: IndexVectorClock,
91 pub operations: Vec<IndexOperation>,
92 pub timestamp: u64,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct RemoteIndexState {
97 pub peer_id: String,
98 pub indexes: HashMap<String, RemoteIndexMetadata>,
99 pub last_sync: u64,
100 pub reachable: bool,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct RemoteIndexMetadata {
105 pub name: String,
106 pub version: IndexVectorClock,
107 pub entry_count: usize,
108 pub avg_query_time_ms: f64,
109 pub last_updated: u64,
110}
111
112pub struct DistributedIndexManager {
114 local_peer_id: String,
115 replication_states: HashMap<String, IndexReplicationState>,
116 peer_states: HashMap<String, RemoteIndexState>,
117 pending_operations: Vec<IndexSyncMessage>,
118 consistency_protocol: ConsistencyProtocol,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq)]
122pub enum ConsistencyProtocol {
123 EventualConsistency,
124 StrongConsistency,
125 CausalConsistency,
126}
127
128impl DistributedIndexManager {
129 pub fn new(local_peer_id: String) -> Self {
130 DistributedIndexManager {
131 local_peer_id,
132 replication_states: HashMap::new(),
133 peer_states: HashMap::new(),
134 pending_operations: Vec::new(),
135 consistency_protocol: ConsistencyProtocol::CausalConsistency,
136 }
137 }
138
139 pub fn create_replicated_index(
141 &mut self,
142 name: String,
143 field: String,
144 index_type: String,
145 ) -> Result<(), String> {
146 let mut version = IndexVectorClock::new();
147 version.increment(&self.local_peer_id);
148
149 let replication_state = IndexReplicationState {
150 index_name: name.clone(),
151 local_version: version,
152 peer_versions: HashMap::new(),
153 replicated_peers: HashSet::new(),
154 pending_sync: vec![IndexOperation::CreateIndex {
155 name: name.clone(),
156 field,
157 index_type,
158 }],
159 };
160
161 self.replication_states.insert(name, replication_state);
162 Ok(())
163 }
164
165 pub fn register_peer(&mut self, peer_id: String) {
167 self.peer_states.insert(
168 peer_id.clone(),
169 RemoteIndexState {
170 peer_id,
171 indexes: HashMap::new(),
172 last_sync: current_time_ms(),
173 reachable: true,
174 },
175 );
176 }
177
178 pub fn update_peer_index_metadata(
180 &mut self,
181 peer_id: &str,
182 index_name: String,
183 metadata: RemoteIndexMetadata,
184 ) -> Result<(), String> {
185 if let Some(peer_state) = self.peer_states.get_mut(peer_id) {
186 peer_state.indexes.insert(index_name.clone(), metadata);
187 peer_state.last_sync = current_time_ms();
188 Ok(())
189 } else {
190 Err(format!("Peer {} not found", peer_id))
191 }
192 }
193
194 pub fn enqueue_sync(&mut self, message: IndexSyncMessage) {
196 self.pending_operations.push(message);
197 }
198
199 pub fn get_pending_for_peer(&self, peer_id: &str) -> Vec<IndexSyncMessage> {
201 self.pending_operations
202 .iter()
203 .filter(|msg| msg.target_peer == peer_id)
204 .cloned()
205 .collect()
206 }
207
208 pub fn acknowledge_sync(&mut self, peer_id: &str, index_name: &str) -> Result<(), String> {
210 if let Some(replication_state) = self.replication_states.get_mut(index_name) {
211 replication_state
212 .replicated_peers
213 .insert(peer_id.to_string());
214 Ok(())
215 } else {
216 Err(format!("Index {} not found", index_name))
217 }
218 }
219
220 pub fn get_replication_status(&self, index_name: &str) -> Option<ReplicationStatus> {
222 self.replication_states.get(index_name).map(|state| {
223 let replicated_count = state.replicated_peers.len();
224 let total_peers = self.peer_states.len();
225 let reachable_peers = self.peer_states.values().filter(|p| p.reachable).count();
226
227 ReplicationStatus {
228 index_name: index_name.to_string(),
229 replicated_to: replicated_count,
230 total_peers,
231 reachable_peers,
232 pending_syncs: state.pending_sync.len(),
233 consistency_level: self.calculate_consistency_level(index_name),
234 }
235 })
236 }
237
238 fn calculate_consistency_level(&self, index_name: &str) -> ConsistencyLevel {
240 if let Some(replication_state) = self.replication_states.get(index_name) {
241 let total = self.peer_states.len();
242 let replicated = replication_state.replicated_peers.len();
243
244 if replicated == total {
245 ConsistencyLevel::Strong
246 } else if replicated > total / 2 {
247 ConsistencyLevel::Causal
248 } else {
249 ConsistencyLevel::Eventual
250 }
251 } else {
252 ConsistencyLevel::Local
253 }
254 }
255
256 pub fn get_replication_targets(&self, index_name: &str) -> Vec<String> {
258 if let Some(replication_state) = self.replication_states.get(index_name) {
259 self.peer_states
260 .keys()
261 .filter(|peer_id| {
262 peer_id != &&self.local_peer_id
263 && !replication_state.replicated_peers.contains(*peer_id)
264 })
265 .cloned()
266 .collect()
267 } else {
268 Vec::new()
269 }
270 }
271
272 pub fn detect_conflicts(&self, index_name: &str) -> Vec<VersionConflict> {
274 let mut conflicts = Vec::new();
275
276 if let Some(replication_state) = self.replication_states.get(index_name) {
277 for (peer_id, peer_version) in &replication_state.peer_versions {
278 if replication_state
279 .local_version
280 .concurrent_with(peer_version)
281 {
282 conflicts.push(VersionConflict {
283 index_name: index_name.to_string(),
284 local_version: replication_state.local_version.clone(),
285 remote_peer: peer_id.clone(),
286 remote_version: peer_version.clone(),
287 });
288 }
289 }
290 }
291
292 conflicts
293 }
294
295 pub fn merge_versions(&mut self, index_name: &str) -> Result<(), String> {
297 if let Some(replication_state) = self.replication_states.get_mut(index_name) {
298 for peer_version in replication_state.peer_versions.values() {
299 replication_state.local_version.merge(peer_version);
300 }
301 Ok(())
302 } else {
303 Err(format!("Index {} not found", index_name))
304 }
305 }
306
307 pub fn set_peer_reachable(&mut self, peer_id: &str, reachable: bool) {
309 if let Some(peer_state) = self.peer_states.get_mut(peer_id) {
310 peer_state.reachable = reachable;
311 }
312 }
313
314 pub fn get_sync_status(&self) -> DistributedSyncStatus {
316 let total_indexes = self.replication_states.len();
317 let fully_replicated = self
318 .replication_states
319 .values()
320 .filter(|state| state.replicated_peers.len() == self.peer_states.len())
321 .count();
322
323 let pending_total: usize = self
324 .replication_states
325 .values()
326 .map(|state| state.pending_sync.len())
327 .sum();
328
329 DistributedSyncStatus {
330 total_indexes,
331 fully_replicated,
332 partially_replicated: total_indexes - fully_replicated,
333 pending_operations: pending_total,
334 unreachable_peers: self.peer_states.values().filter(|p| !p.reachable).count(),
335 }
336 }
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
340pub enum ConsistencyLevel {
341 Local,
342 Eventual,
343 Causal,
344 Strong,
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize)]
348pub struct ReplicationStatus {
349 pub index_name: String,
350 pub replicated_to: usize,
351 pub total_peers: usize,
352 pub reachable_peers: usize,
353 pub pending_syncs: usize,
354 pub consistency_level: ConsistencyLevel,
355}
356
357#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct VersionConflict {
359 pub index_name: String,
360 pub local_version: IndexVectorClock,
361 pub remote_peer: String,
362 pub remote_version: IndexVectorClock,
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct DistributedSyncStatus {
367 pub total_indexes: usize,
368 pub fully_replicated: usize,
369 pub partially_replicated: usize,
370 pub pending_operations: usize,
371 pub unreachable_peers: usize,
372}
373
374fn current_time_ms() -> u64 {
375 std::time::SystemTime::now()
376 .duration_since(std::time::UNIX_EPOCH)
377 .map(|d| d.as_millis() as u64)
378 .unwrap_or(0)
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384
385 #[test]
386 fn test_vector_clock_increment() {
387 let mut clock = IndexVectorClock::new();
388 clock.increment("node1");
389 assert_eq!(clock.clock.get("node1"), Some(&1));
390 }
391
392 #[test]
393 fn test_vector_clock_merge() {
394 let mut clock1 = IndexVectorClock::new();
395 clock1.increment("node1");
396 clock1.increment("node1");
397
398 let mut clock2 = IndexVectorClock::new();
399 clock2.increment("node2");
400 clock2.increment("node2");
401
402 clock1.merge(&clock2);
403 assert_eq!(clock1.clock.get("node1"), Some(&2));
404 assert_eq!(clock1.clock.get("node2"), Some(&2));
405 }
406
407 #[test]
408 fn test_vector_clock_happens_before() {
409 let mut clock1 = IndexVectorClock::new();
410 clock1.increment("node1");
411
412 let mut clock2 = IndexVectorClock::new();
413 clock2.increment("node1");
414 clock2.increment("node1");
415
416 assert!(clock1.happens_before(&clock2));
417 assert!(!clock2.happens_before(&clock1));
418 }
419
420 #[test]
421 fn test_concurrent_detection() {
422 let mut clock1 = IndexVectorClock::new();
423 clock1.increment("node1");
424
425 let mut clock2 = IndexVectorClock::new();
426 clock2.increment("node2");
427
428 assert!(clock1.concurrent_with(&clock2));
429 }
430
431 #[test]
432 fn test_distributed_manager_creation() {
433 let mut manager = DistributedIndexManager::new("node1".to_string());
434 manager.register_peer("node2".to_string());
435
436 assert!(manager
437 .create_replicated_index(
438 "status_idx".to_string(),
439 "status".to_string(),
440 "hash".to_string()
441 )
442 .is_ok());
443
444 let status = manager.get_replication_status("status_idx");
445 assert!(status.is_some());
446 }
447
448 #[test]
449 fn test_replication_targets() {
450 let mut manager = DistributedIndexManager::new("node1".to_string());
451 manager.register_peer("node2".to_string());
452 manager.register_peer("node3".to_string());
453
454 manager
455 .create_replicated_index("idx1".to_string(), "field".to_string(), "hash".to_string())
456 .ok();
457
458 let targets = manager.get_replication_targets("idx1");
459 assert_eq!(targets.len(), 2);
460 }
461
462 #[test]
463 fn test_sync_status() {
464 let mut manager = DistributedIndexManager::new("node1".to_string());
465 manager.register_peer("node2".to_string());
466
467 manager
468 .create_replicated_index("idx1".to_string(), "field".to_string(), "hash".to_string())
469 .ok();
470 manager
471 .create_replicated_index("idx2".to_string(), "field".to_string(), "hash".to_string())
472 .ok();
473
474 let status = manager.get_sync_status();
475 assert_eq!(status.total_indexes, 2);
476 }
477}