oxirs_vec/real_time_embedding_pipeline/
versioning.rs1use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
7use std::sync::{Arc, RwLock};
8use std::time::Duration;
9
10use crate::real_time_embedding_pipeline::{
11 config::VersionControlConfig,
12 traits::{HealthStatus, Version, VersionStorage},
13};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct VersionManagerConfig {
18 pub max_versions_per_id: usize,
19 pub cleanup_interval: Duration,
20 pub enable_compression: bool,
21 pub max_total_versions: usize,
22}
23
24impl Default for VersionManagerConfig {
25 fn default() -> Self {
26 Self {
27 max_versions_per_id: 10,
28 cleanup_interval: Duration::from_secs(300),
29 enable_compression: false,
30 max_total_versions: 100_000,
31 }
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct VersionManagerStatistics {
38 pub total_versions: u64,
39 pub total_cleanups: u64,
40 pub total_ids_tracked: usize,
41 pub is_running: bool,
42}
43
44pub struct InMemoryVersionStorage {
46 data: HashMap<String, Vec<Version>>,
47 max_versions_per_id: usize,
48}
49
50impl InMemoryVersionStorage {
51 pub fn new(max_versions_per_id: usize) -> Self {
52 Self {
53 data: HashMap::new(),
54 max_versions_per_id,
55 }
56 }
57}
58
59impl VersionStorage for InMemoryVersionStorage {
60 fn store_version(&mut self, id: &str, version: &Version) -> Result<()> {
61 let versions = self.data.entry(id.to_string()).or_default();
62 versions.push(version.clone());
63 if versions.len() > self.max_versions_per_id {
64 let excess = versions.len() - self.max_versions_per_id;
65 versions.drain(0..excess);
66 }
67 Ok(())
68 }
69
70 fn get_version(&self, id: &str, version_number: u64) -> Result<Option<Version>> {
71 Ok(self
72 .data
73 .get(id)
74 .and_then(|vs| vs.iter().find(|v| v.version == version_number).cloned()))
75 }
76
77 fn get_all_versions(&self, id: &str) -> Result<Vec<Version>> {
78 Ok(self.data.get(id).cloned().unwrap_or_default())
79 }
80
81 fn cleanup_old_versions(&mut self, id: &str, keep_count: usize) -> Result<usize> {
82 if let Some(versions) = self.data.get_mut(id) {
83 let original_len = versions.len();
84 if original_len > keep_count {
85 let to_remove = original_len - keep_count;
86 versions.drain(0..to_remove);
87 return Ok(to_remove);
88 }
89 }
90 Ok(0)
91 }
92}
93
94pub struct VersionManager {
96 config: VersionManagerConfig,
97 storage: Arc<RwLock<InMemoryVersionStorage>>,
98 is_running: AtomicBool,
99 total_versions: AtomicU64,
100 total_cleanups: AtomicU64,
101}
102
103impl VersionManager {
104 pub fn new(version_control_config: VersionControlConfig) -> Result<Self> {
105 let config = VersionManagerConfig {
106 max_versions_per_id: version_control_config.max_versions,
107 enable_compression: false,
108 ..Default::default()
109 };
110 let storage = Arc::new(RwLock::new(InMemoryVersionStorage::new(
111 config.max_versions_per_id,
112 )));
113 Ok(Self {
114 config,
115 storage,
116 is_running: AtomicBool::new(false),
117 total_versions: AtomicU64::new(0),
118 total_cleanups: AtomicU64::new(0),
119 })
120 }
121
122 pub async fn start(&self) -> Result<()> {
123 self.is_running.store(true, Ordering::Release);
124 Ok(())
125 }
126
127 pub async fn stop(&self) -> Result<()> {
128 self.is_running.store(false, Ordering::Release);
129 Ok(())
130 }
131
132 pub async fn health_check(&self) -> Result<HealthStatus> {
133 if self.is_running.load(Ordering::Acquire) {
134 Ok(HealthStatus::Healthy)
135 } else {
136 Ok(HealthStatus::Warning {
137 message: "Version manager not running".to_string(),
138 })
139 }
140 }
141
142 pub fn track_version(&self, id: &str, version: Version) -> Result<()> {
143 let mut storage = self
144 .storage
145 .write()
146 .map_err(|_| anyhow::anyhow!("Failed to acquire version storage lock"))?;
147 storage.store_version(id, &version)?;
148 self.total_versions.fetch_add(1, Ordering::Relaxed);
149 Ok(())
150 }
151
152 pub fn get_version(&self, id: &str, version_number: u64) -> Result<Option<Version>> {
153 let storage = self
154 .storage
155 .read()
156 .map_err(|_| anyhow::anyhow!("Failed to acquire version storage lock"))?;
157 storage.get_version(id, version_number)
158 }
159
160 pub fn cleanup_old(&self, id: &str) -> Result<usize> {
161 let mut storage = self
162 .storage
163 .write()
164 .map_err(|_| anyhow::anyhow!("Failed to acquire version storage lock"))?;
165 let removed = storage.cleanup_old_versions(id, self.config.max_versions_per_id)?;
166 if removed > 0 {
167 self.total_cleanups.fetch_add(1, Ordering::Relaxed);
168 }
169 Ok(removed)
170 }
171
172 pub fn get_statistics(&self) -> VersionManagerStatistics {
173 let total_ids = self.storage.read().map(|s| s.data.len()).unwrap_or(0);
174 VersionManagerStatistics {
175 total_versions: self.total_versions.load(Ordering::Acquire),
176 total_cleanups: self.total_cleanups.load(Ordering::Acquire),
177 total_ids_tracked: total_ids,
178 is_running: self.is_running.load(Ordering::Acquire),
179 }
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use crate::real_time_embedding_pipeline::config::VersionControlConfig;
187 use crate::real_time_embedding_pipeline::traits::Version;
188 use crate::Vector;
189 use std::time::SystemTime;
190
191 fn make_version(num: u64) -> Version {
192 Version {
193 version: num,
194 vector: Vector::new(vec![1.0, 2.0, 3.0]),
195 created_at: SystemTime::now(),
196 metadata: HashMap::new(),
197 checksum: format!("checksum_{}", num),
198 }
199 }
200
201 #[tokio::test]
202 async fn test_version_manager_start_stop() {
203 let config = VersionControlConfig::default();
204 let manager = VersionManager::new(config).expect("should create");
205 manager.start().await.expect("should start");
206 let health = manager.health_check().await.expect("should check");
207 assert!(matches!(health, HealthStatus::Healthy));
208 manager.stop().await.expect("should stop");
209 }
210
211 #[test]
212 fn test_track_and_get_version() {
213 let config = VersionControlConfig::default();
214 let manager = VersionManager::new(config).expect("should create");
215 let v = make_version(1);
216 manager.track_version("item1", v).expect("should track");
217 let result = manager.get_version("item1", 1).expect("should get");
218 assert!(result.is_some());
219 assert_eq!(result.expect("should have version").version, 1);
220 }
221
222 #[test]
223 fn test_cleanup_old_versions() {
224 let config = VersionControlConfig {
225 max_versions: 3,
226 ..Default::default()
227 };
228 let manager = VersionManager::new(config).expect("should create");
229 for i in 1..=5 {
230 manager
231 .track_version("item1", make_version(i))
232 .expect("should track");
233 }
234 let _removed = manager.cleanup_old("item1").expect("should cleanup");
235 let stats = manager.get_statistics();
236 assert!(stats.total_versions > 0);
237 }
238
239 #[test]
240 fn test_in_memory_version_storage() {
241 let mut storage = InMemoryVersionStorage::new(3);
242 for i in 1..=5u64 {
243 let v = Version {
244 version: i,
245 vector: Vector::new(vec![i as f32]),
246 created_at: SystemTime::now(),
247 metadata: HashMap::new(),
248 checksum: format!("c{}", i),
249 };
250 storage.store_version("key", &v).expect("should store");
251 }
252 let all = storage.get_all_versions("key").expect("should get all");
253 assert!(all.len() <= 3);
254 }
255}