oxirs_vec/compaction/
metrics.rs1use super::types::{CompactionResult, CompactionState, CompactionStatistics};
4use serde::{Deserialize, Serialize};
5use std::collections::VecDeque;
6use std::sync::{Arc, Mutex};
7use std::time::Duration;
8
9#[derive(Debug, Clone)]
11pub struct CompactionMetrics {
12 state: Arc<Mutex<CompactionState>>,
14 statistics: Arc<Mutex<CompactionStatistics>>,
16 history: Arc<Mutex<VecDeque<CompactionResult>>>,
18 max_history_size: usize,
20}
21
22impl Default for CompactionMetrics {
23 fn default() -> Self {
24 Self::new(100)
25 }
26}
27
28impl CompactionMetrics {
29 pub fn new(max_history_size: usize) -> Self {
31 Self {
32 state: Arc::new(Mutex::new(CompactionState::Idle)),
33 statistics: Arc::new(Mutex::new(CompactionStatistics::default())),
34 history: Arc::new(Mutex::new(VecDeque::new())),
35 max_history_size,
36 }
37 }
38
39 pub fn update_state(&self, state: CompactionState) {
41 let mut s = self.state.lock().unwrap();
42 *s = state;
43 }
44
45 pub fn get_state(&self) -> CompactionState {
47 *self.state.lock().unwrap()
48 }
49
50 pub fn record_compaction(&self, result: CompactionResult) {
52 let mut stats = self.statistics.lock().unwrap();
53 let mut history = self.history.lock().unwrap();
54
55 stats.total_compactions += 1;
57 if result.success {
58 stats.successful_compactions += 1;
59 } else {
60 stats.failed_compactions += 1;
61 }
62
63 stats.total_vectors_processed += result.vectors_processed;
64 stats.total_vectors_removed += result.vectors_removed;
65 stats.total_bytes_reclaimed += result.bytes_reclaimed;
66 stats.current_fragmentation = result.fragmentation_after;
67 stats.last_compaction_time = Some(result.end_time);
68 stats.last_compaction_result = Some(result.clone());
69
70 if stats.total_compactions > 0 {
72 let total_duration = stats.avg_compaction_duration.as_secs_f64()
73 * (stats.total_compactions - 1) as f64
74 + result.duration.as_secs_f64();
75 stats.avg_compaction_duration =
76 Duration::from_secs_f64(total_duration / stats.total_compactions as f64);
77 } else {
78 stats.avg_compaction_duration = result.duration;
79 }
80
81 history.push_back(result);
83 while history.len() > self.max_history_size {
84 history.pop_front();
85 }
86 }
87
88 pub fn update_fragmentation(&self, fragmentation: f64) {
90 let mut stats = self.statistics.lock().unwrap();
91 stats.current_fragmentation = fragmentation;
92 }
93
94 pub fn get_statistics(&self) -> CompactionStatistics {
96 self.statistics.lock().unwrap().clone()
97 }
98
99 pub fn get_history(&self, limit: Option<usize>) -> Vec<CompactionResult> {
101 let history = self.history.lock().unwrap();
102 if let Some(lim) = limit {
103 history.iter().rev().take(lim).cloned().collect()
104 } else {
105 history.iter().cloned().collect()
106 }
107 }
108
109 pub fn calculate_efficiency(&self) -> CompactionEfficiency {
111 let stats = self.statistics.lock().unwrap();
112
113 let success_rate = if stats.total_compactions > 0 {
114 stats.successful_compactions as f64 / stats.total_compactions as f64
115 } else {
116 0.0
117 };
118
119 let avg_space_reclaimed = if stats.successful_compactions > 0 {
120 stats.total_bytes_reclaimed as f64 / stats.successful_compactions as f64
121 } else {
122 0.0
123 };
124
125 let avg_vectors_removed = if stats.successful_compactions > 0 {
126 stats.total_vectors_removed as f64 / stats.successful_compactions as f64
127 } else {
128 0.0
129 };
130
131 CompactionEfficiency {
132 success_rate,
133 avg_space_reclaimed_bytes: avg_space_reclaimed as u64,
134 avg_vectors_removed: avg_vectors_removed as usize,
135 avg_duration: stats.avg_compaction_duration,
136 current_fragmentation: stats.current_fragmentation,
137 }
138 }
139
140 pub fn reset(&self) {
142 let mut stats = self.statistics.lock().unwrap();
143 *stats = CompactionStatistics::default();
144
145 let mut history = self.history.lock().unwrap();
146 history.clear();
147
148 let mut state = self.state.lock().unwrap();
149 *state = CompactionState::Idle;
150 }
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct CompactionEfficiency {
156 pub success_rate: f64,
158 pub avg_space_reclaimed_bytes: u64,
160 pub avg_vectors_removed: usize,
162 pub avg_duration: Duration,
164 pub current_fragmentation: f64,
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use crate::compaction::types::CompactionResult;
172 use std::time::SystemTime;
173
174 fn create_test_result(success: bool, bytes_reclaimed: u64) -> CompactionResult {
175 CompactionResult {
176 start_time: SystemTime::now(),
177 end_time: SystemTime::now(),
178 duration: Duration::from_secs(10),
179 vectors_processed: 1000,
180 vectors_removed: 100,
181 bytes_reclaimed,
182 fragmentation_before: 0.4,
183 fragmentation_after: 0.1,
184 success,
185 error: None,
186 }
187 }
188
189 #[test]
190 fn test_metrics_recording() {
191 let metrics = CompactionMetrics::new(10);
192
193 let result = create_test_result(true, 1_000_000);
194 metrics.record_compaction(result);
195
196 let stats = metrics.get_statistics();
197 assert_eq!(stats.total_compactions, 1);
198 assert_eq!(stats.successful_compactions, 1);
199 assert_eq!(stats.total_bytes_reclaimed, 1_000_000);
200 }
201
202 #[test]
203 fn test_efficiency_calculation() {
204 let metrics = CompactionMetrics::new(10);
205
206 metrics.record_compaction(create_test_result(true, 1_000_000));
207 metrics.record_compaction(create_test_result(true, 2_000_000));
208 metrics.record_compaction(create_test_result(false, 0));
209
210 let efficiency = metrics.calculate_efficiency();
211 assert!((efficiency.success_rate - 0.666).abs() < 0.01);
212 assert_eq!(efficiency.avg_space_reclaimed_bytes, 1_500_000);
213 }
214
215 #[test]
216 fn test_history_limit() {
217 let metrics = CompactionMetrics::new(5);
218
219 for i in 0..10 {
220 metrics.record_compaction(create_test_result(true, i * 1000));
221 }
222
223 let history = metrics.get_history(None);
224 assert_eq!(history.len(), 5);
225 }
226
227 #[test]
228 fn test_state_updates() {
229 let metrics = CompactionMetrics::new(10);
230
231 assert_eq!(metrics.get_state(), CompactionState::Idle);
232
233 metrics.update_state(CompactionState::Running);
234 assert_eq!(metrics.get_state(), CompactionState::Running);
235
236 metrics.update_state(CompactionState::Completed);
237 assert_eq!(metrics.get_state(), CompactionState::Completed);
238 }
239}